PageSourceSearch

https://members.iehp.org/bundles/MemberPortalApp?v=Ht5q7yt3XYjf3T3P2MUgZCEqlUANLUds_yKlyMdHTl41

js iehp.org collected 2026-09-24 09:24:29 UTC 1,335,257 bytes, 39,044 lines download raw bytes

1!function () { "use strict"; angular.module( "ngMask", [] ) }(), function () { "use strict"; angular.module( "ngMask" ).directive( "mask", ["$log", "$timeout", "MaskService", function ( a, b, c ) { return { restrict: "A", require: "ngModel", compile: function ( d, e ) { function f( a ) { "number" == typeof a && ( b.cancel( g ), g = b( function () { var b = a + 1, c = d[0]; if ( c.setSelectionRange ) c.focus(), c.setSelectionRange( a, b ); else if ( c.createTextRange ) { var e = c.createTextRange(); e.collapse( !0 ), e.moveEnd( "character", b ), e.moveStart( "character", a ), e.select() } } ) ) } if ( !e.mask || !e.ngModel ) return void a.info( "Mask and ng-model attributes are required!" ); var g, h, i = c.create(); return { pre: function ( a, b, c ) { h = i.generateRegex( { mask: c.mask, repeat: c.repeat || c.maskRepeat, clean: "true" === ( c.clean || c.maskClean ), limit: "true" === ( c.limit || c.maskLimit || "true" ), restrict: c.restrict || c.maskRestrict || "select", validate: "true" === ( c.validate || c.maskValidate || "true" ), model: c.ngModel, value: c.ngValue } ) }, post: function ( c, d, e, g ) { h.then( function () { function h( b ) { var c = b; b = b || ""; var d = i.getViewValue( b ), e = k.maskWithoutOptionals || "", h = d.withDivisors( !0 ), j = d.withoutDivisors( !0 ); try { var l = i.getRegex( h.length - 1 ), m = i.getRegex( e.length - 1 ), n = l.test( h ) || m.test( h ), o = b.length - h.length === 1, p = e.length - h.length > 0; if ( "accept" !== k.restrict ) if ( "select" !== k.restrict || n && !o ) "reject" !== k.restrict || n || ( d = i.removeWrongPositions( h ), h = d.withDivisors( !0 ), j = d.withoutDivisors( !0 ) ); else { var q = b[b.length - 1], r = h[h.length - 1]; q !== r && p && ( h += q ); var s = i.getFirstWrongPosition( h ); angular.isDefined( s ) && f( s ) } k.limit || ( h = d.withDivisors( !1 ), j = d.withoutDivisors( !1 ) ), k.validate && g.$dirty && ( m.test( h ) || g.$isEmpty( c ) ? g.$setValidity( "mask", !0 ) : g.$setValidity( "mask", !1 ) ), b !== h && ( g.$setViewValue( angular.copy( h ), "input" ), g.$render() ) } catch ( t ) { throw a.error( "[mask - parseViewValue]" ), t } return k.clean ? j : h } var j, k = i.getOptions(); g.$parsers.push( h ), d.on( "click input paste keyup", function () { j = b( function () { b.cancel( j ), h( d.val() ), c.$apply() }, 100 ) } ); var l = c.$watch( e.ngModel, function ( a ) { angular.isDefined( a ) && ( h( a ), l() ) } ); k.value && c.$evalAsync( function () { g.$setViewValue( angular.copy( k.value ), "input" ), g.$render() } ) } ) } } } } }] ) }(), function () { "use strict"; angular.module( "ngMask" ).factory( "MaskService", ["$q", "OptionalService", "UtilService", function ( a, b, c ) { function d() { function d( a, b ) { var c; try { var d = t[a], e = C[d], f = h( a ); e ? c = "(" + e.source + ")" : ( i( a ) || ( z.push( a ), A[a] = d ), c = "(\\" + d + ")" ) } catch ( g ) { throw g } return ( f || b ) && ( c += "?" ), new RegExp( c ) } function e( a, b ) { var c, f; try { var g = d( a, b ); c = g; var i = h( a ), j = g.source; if ( i && u > a + 1 ) { var k = e( a + 1, !0 ).elementOptionalRegex(); j += k.source } f = new RegExp( j ) } catch ( l ) { throw l } return { elementRegex: function () { return c }, elementOptionalRegex: function () { return f } } } function f( c ) { var d = a.defer(); s = c; try { var f = c.mask, g = c.repeat; if ( !f ) return; g && ( f = Array( parseInt( g ) + 1 ).join( f ) ), w = b.getOptionals( f ).fromMaskWithoutOptionals(), s.maskWithoutOptionals = t = b.removeOptionals( f ), u = t.length; for ( var h, i = 0; u > i; i++ ) { var l = e( i ), m = l.elementRegex(), n = l.elementOptionalRegex(), o = h ? h.source + n.source : n.source; o = new RegExp( o ), h = h ? h.source + m.source : m.source, h = new RegExp( h ), B.push( o ) } j(), v = k( t ).length, d.resolve( { options: s, divisors: z, divisorElements: A, optionalIndexes: w, optionalDivisors: x, optionalDivisorsCombinations: y } ) } catch ( p ) { throw d.reject( p ), p } return d.promise } function g( a ) { var b; try { b = B[a] ? B[a].source : "" } catch ( c ) { throw c } return new RegExp( "^" + b + "$" ) } function h( a ) { return c.inArray( a, w ) } function i( a ) { return c.inArray( a, z ) } function j() { function a( a, b ) { return a - b } for ( var b = z.sort( a ), c = w.sort( a ), d = 0; d < b.length; d++ ) for ( var e = b[d], f = 1; f <= c.length; f++ ) { var g = c[f - 1]; if ( g >= e ) break; x[e] = x[e] ? x[e].concat( e - f ) : [e - f], A[e - f] = A[e] } } function k( a ) { a = a.toString(); try { if ( z.length > 0 && a ) { for ( var b = Object.keys( A ), d = [], e = b.length - 1; e >= 0; e-- ) { var f = A[b[e]]; f && d.push( f ) } d = c.uniqueArray( d ); var g = new RegExp( "[\\" + d.join( "\\" ) + "]", "g" ); return a.replace( g, "" ) } return a } catch ( h ) { throw h } } function l( a, b ) { function d( a, b ) { for ( var c = b, d = 0; d < a.length; d++ ) { var e = a[d]; e < c.length && c.splice( e, 0, A[e] ) } return c } var e = a, f = z.filter( function ( a ) { var d = Object.keys( x ).map( function ( a ) { return parseInt( a ) } ); return !c.inArray( a, b ) && !c.inArray( a, d ) } ); return angular.isArray( a ) && angular.isArray( b ) ? ( e = d( f, e ), e = d( b, e ) ) : e } function m( a ) { var b = a.split( "" ), d = !0; if ( w.length > 0 ) { for ( var e = [], f = Object.keys( x ), h = 0; h < f.length; h++ ) { var i = x[f[h]]; e.push( i ) } 0 === y.length && c.lazyProduct( e, function () { y.push( Array.prototype.slice.call( arguments ) ) } ); for ( var h = y.length - 1; h >= 0; h-- ) { var j = angular.copy( b ); j = l( j, y[h] ); var k = j.join( "" ), m = g( t.length - 1 ); if ( m.test( k ) ) { d = !1, b = j; break } } } return d && ( b = l( b, z ) ), b.join( "" ) } function n() { return s } function o( a ) { try { var b = k( a ), c = m( b ); return { withDivisors: function ( a ) { return a ? c.substr( 0, u ) : c }, withoutDivisors: function ( a ) { return a ? b.substr( 0, v ) : b } } } catch ( d ) { throw d } } function p( a, b ) { var c = []; if ( !a ) return 0; for ( var d = 0; d < a.length; d++ ) { var e = g( d ), f = a.substr( 0, d + 1 ); if ( e && !e.test( f ) && ( c.push( d ), b ) ) break } return c } function q( a ) { return p( a, !0 )[0] } function r( a ) { for ( var b = p( a, !1 ), c = a, d = 0; d < b.length; d++ ) { var e = b[d], f = a.split( "" ); f.splice( e, 1 ), c = f.join( "" ) } return o( c ) } var s, t, u = 0, v = 0, w = [], x = {}, y = [], z = [], A = {}, B = [], C = { 9: /[0-9]/, 8: /[0-8]/, 7: /[0-7]/, 6: /[0-6]/, 5: /[0-5]/, 4: /[0-4]/, 3: /[0-3]/, 2: /[0-2]/, 1: /[0-1]/, 0: /[0]/, "*": /./, w: /\w/, W: /\W/, d: /\d/, D: /\D/, s: /\s/, S: /\S/, b: /\b/, A: /[A-Z]/, a: /[a-z]/, Z: /[A-ZÇÀÁÂÃÈÉÊẼÌÍÎĨÒÓÔÕÙÚÛŨ]/, z: /[a-zçáàãâéèêẽíìĩîóòôõúùũüû]/, "@": /[a-zA-Z]/, "#": /[a-zA-ZçáàãâéèêẽíìĩîóòôõúùũüûÇÀÁÂÃÈÉÊẼÌÍÎĨÒÓÔÕÙÚÛŨ]/, "%": /[0-9a-zA-ZçáàãâéèêẽíìĩîóòôõúùũüûÇÀÁÂÃÈÉÊẼÌÍÎĨÒÓÔÕÙÚÛŨ]/ }; return { getViewValue: o, generateRegex: f, getRegex: g, getOptions: n, removeDivisors: k, getFirstWrongPosition: q, removeWrongPositions: r } } return { create: d } }] ) }(), function () { "use strict"; angular.module( "ngMask" ).factory( "OptionalService", [function () { function a( a ) { var c = []; try { for ( var d = /\?/g, e = []; null != ( e = d.exec( a ) ) ; ) c.push( e.index - 1 ) } catch ( f ) { throw f } return { fromMask: function () { return c }, fromMaskWithoutOptionals: function () { return b( c ) } } } function b( a ) { for ( var b = [], c = 0; c < a.length; c++ ) b.push( a[c] - c ); return b } function c( a ) { var b; try { b = a.replace( /\?/g, "" ) } catch ( c ) { throw c } return b } return { removeOptionals: c, getOptionals: a } }] ) }(), function () { "use strict"; angular.module( "ngMask" ).factory( "UtilService", [function () { function a( a, b, c ) { function d( h ) { var i = a[h], j = g[h]; if ( h === f ) for ( var k = 0; j > k; ++k ) e[h] = i[k], b.apply( c, e ); else for ( var k = 0; j > k; ++k ) e[h] = i[k], d( h + 1 ); e.pop() } c || ( c = this ); for ( var e = [], f = a.length - 1, g = [], h = a.length; h--; ) g[h] = a[h].length; d( 0 ) } function b( a, b ) { var c; try { c = b.indexOf( a ) > -1 } catch ( d ) { throw d } return c } function c( a ) { for ( var b = {}, c = [], d = 0, e = a.length; e > d; ++d ) b.hasOwnProperty( a[d] ) || ( c.push( a[d] ), b[a[d]] = 1 ); return c } return { lazyProduct: a, inArray: b, uniqueArray: c } }] ) }();
2
3/**
4* @name InfoBox
5* @version 1.1.5 [March 1, 2011]
6* @author Gary Little (inspired by proof-of-concept code from Pamela Fox of Google)
7* @copyright Copyright 2010 Gary Little [gary at luxcentral.com]
8* @fileoverview InfoBox extends the Google Maps JavaScript API V3 <tt>OverlayView</tt> class.
9*  <p>
10*  An InfoBox behaves like a <tt>google.maps.InfoWindow</tt>, but it supports several
11*  additional properties for advanced styling. An InfoBox can also be used as a map label.
12*  <p>
13*  An InfoBox also fires the same events as a <tt>google.maps.InfoWindow</tt>.
14*  <p>
15*  Browsers tested:
16*  <p>
17*  Mac -- Safari (4.0.4), Firefox (3.6), Opera (10.10), Chrome (4.0.249.43), OmniWeb (5.10.1)
18*  <br>
19*  Win -- Safari, Firefox, Opera, Chrome (3.0.195.38), Internet Explorer (8.0.6001.18702)
20*  <br>
21*  iPod Touch/iPhone -- Safari (3.1.2)
22*/
23
24/*!
25*
26* Licensed under the Apache License, Version 2.0 (the "License");
27* you may not use this file except in compliance with the License.
28* You may obtain a copy of the License at
29*
30*       http://www.apache.org/licenses/LICENSE-2.0
31*
32* Unless required by applicable law or agreed to in writing, software
33* distributed under the License is distributed on an "AS IS" BASIS,
34* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35* See the License for the specific language governing permissions and
36* limitations under the License.
37*/
38
39/*jslint browser:true */
40/*global google */
41
42/**
43* @name InfoBoxOptions
44* @class This class represents the optional parameter passed to the {@link InfoBox} constructor.
45* @property {string|Node} content The content of the InfoBox (plain text or an HTML DOM node).
46* @property {boolean} disableAutoPan Disable auto-pan on <tt>open</tt> (default is <tt>false</tt>).
47* @property {number} maxWidth The maximum width (in pixels) of the InfoBox. Set to 0 if no maximum.
48* @property {Size} pixelOffset The offset (in pixels) from the top left corner of the InfoBox
49*  (or the bottom left corner if the <code>alignBottom</code> property is <code>true</code>)
50*  to the map pixel corresponding to <tt>position</tt>.
51* @property {LatLng} position The geographic location at which to display the InfoBox.
52* @property {number} zIndex The CSS z-index style value for the InfoBox.
53*  Note: This value overrides a zIndex setting specified in the <tt>boxStyle</tt> property.
54* @property {string} boxClass The name of the CSS class defining the styles for the InfoBox container.
55*  The default name is <code>infoBox</code>.
56* @property {Object} [boxStyle] An object literal whose properties define specific CSS
57*  style values to be applied to the InfoBox. Style values defined here override those that may
58*  be defined in the <code>boxClass</code> style sheet. If this property is changed after the
59*  InfoBox has been created, all previously set styles (except those defined in the style sheet)
60*  are removed from the InfoBox before the new style values are applied.
61* @property {string} closeBoxMargin The CSS margin style value for the close box.
62*  The default is "2px" (a 2-pixel margin on all sides).
63* @property {string} closeBoxURL The URL of the image representing the close box.
64*  Note: The default is the URL for Google's standard close box.
65*  Set this property to "" if no close box is required.
66* @property {Size} infoBoxClearance Minimum offset (in pixels) from the InfoBox to the
67*  map edge after an auto-pan.
68* @property {boolean} isHidden Hide the InfoBox on <tt>open</tt> (default is <tt>false</tt>).
69* @property {boolean} alignBottom Align the bottom left corner of the InfoBox to the <code>position</code>
70*  location (default is <tt>false</tt> which means that the top left corner of the InfoBox is aligned).
71* @property {string} pane The pane where the InfoBox is to appear (default is "floatPane").
72*  Set the pane to "mapPane" if the InfoBox is being used as a map label.
73*  Valid pane names are the property names for the <tt>google.maps.MapPanes</tt> object.
74* @property {boolean} enableEventPropagation Propagate mousedown, click, dblclick,
75*  and contextmenu events in the InfoBox (default is <tt>false</tt> to mimic the behavior
76*  of a <tt>google.maps.InfoWindow</tt>). Set this property to <tt>true</tt> if the InfoBox
77*  is being used as a map label. iPhone note: This property setting has no effect; events are
78*  always propagated.
79*/
80
81/**
82* Creates an InfoBox with the options specified in {@link InfoBoxOptions}.
83*  Call <tt>InfoBox.open</tt> to add the box to the map.
84* @constructor
85* @param {InfoBoxOptions} [opt_opts]
86*/
87function InfoBox(opt_opts) {
88
89    opt_opts = opt_opts || {};
90
91    google.maps.OverlayView.apply(this, arguments);
92
93    // Standard options (in common with google.maps.InfoWindow):
94    //
95    this.content_ = opt_opts.content || "";
96    this.disableAutoPan_ = opt_opts.disableAutoPan || false;
97    this.maxWidth_ = opt_opts.maxWidth || 0;
98    this.pixelOffset_ = opt_opts.pixelOffset || new google.maps.Size(0, 0);
99    this.position_ = opt_opts.position || new google.maps.LatLng(0, 0);
100    this.zIndex_ = opt_opts.zIndex || null;
101
102    // Additional options (unique to InfoBox):
103    //
104    this.boxClass_ = opt_opts.boxClass || "infoBox";
105    this.boxStyle_ = opt_opts.boxStyle || {};
106    this.closeBoxMargin_ = opt_opts.closeBoxMargin || "2px";
107    this.closeBoxURL_ = opt_opts.closeBoxURL || "https://www.google.com/intl/en_us/mapfiles/close.gif";
108    if (opt_opts.closeBoxURL === "") {
109        this.closeBoxURL_ = "";
110    }
111    this.infoBoxClearance_ = opt_opts.infoBoxClearance || new google.maps.Size(1, 1);
112    this.isHidden_ = opt_opts.isHidden || false;
113    this.alignBottom_ = opt_opts.alignBottom || false;
114    this.pane_ = opt_opts.pane || "floatPane";
115    this.enableEventPropagation_ = opt_opts.enableEventPropagation || false;
116
117    this.div_ = null;
118    this.closeListener_ = null;
119    this.eventListener1_ = null;
120    this.eventListener2_ = null;
121    this.eventListener3_ = null;
122    this.moveListener_ = null;
123    this.contextListener_ = null;
124    this.fixedWidthSet_ = null;
125}
126
127/* InfoBox extends OverlayView in the Google Maps API v3.
128*/
129InfoBox.prototype = new google.maps.OverlayView();
130
131/**
132* Creates the DIV representing the InfoBox.
133* @private
134*/
135InfoBox.prototype.createInfoBoxDiv_ = function () {
136
137    var bw;
138    var me = this;
139
140    // This handler prevents an event in the InfoBox from being passed on to the map.
141    //
142    var cancelHandler = function (e) {
143        e.cancelBubble = true;
144
145        if (e.stopPropagation) {
146
147            e.stopPropagation();
148        }
149    };
150
151    // This handler ignores the current event in the InfoBox and conditionally prevents
152    // the event from being passed on to the map. It is used for the contextmenu event.
153    //
154    var ignoreHandler = function (e) {
155
156        e.returnValue = false;
157
158        if (e.preventDefault) {
159
160            e.preventDefault();
161        }
162
163        if (!me.enableEventPropagation_) {
164
165            cancelHandler(e);
166        }
167    };
168
169    if (!this.div_) {
170
171        this.div_ = document.createElement("div");
172
173        this.setBoxStyle_();
174
175        if (typeof this.content_.nodeType === "undefined") {
176            this.div_.innerHTML = this.getCloseBoxImg_() + this.content_;
177        } else {
178            this.div_.innerHTML = this.getCloseBoxImg_();
179            this.div_.appendChild(this.content_);
180        }
181
182        // Add the InfoBox DIV to the DOM
183        this.getPanes()[this.pane_].appendChild(this.div_);
184
185        this.addClickHandler_();
186
187        if (this.div_.style.width) {
188
189            this.fixedWidthSet_ = true;
190
191        } else {
192
193            if (this.maxWidth_ !== 0 && this.div_.offsetWidth > this.maxWidth_) {
194
195                this.div_.style.width = this.maxWidth_;
196                this.div_.style.overflow = "auto";
197                this.fixedWidthSet_ = true;
198
199            } else { // The following code is needed to overcome problems with MSIE
200
201                bw = this.getBoxWidths_();
202
203                this.div_.style.width = (this.div_.offsetWidth - bw.left - bw.right) + "px";
204                this.fixedWidthSet_ = false;
205            }
206        }
207
208        this.panBox_(this.disableAutoPan_);
209
210        if (!this.enableEventPropagation_) {
211
212            // Cancel event propagation.
213            //
214            this.eventListener1_ = google.maps.event.addDomListener(this.div_, "mousedown", cancelHandler);
215            this.eventListener2_ = google.maps.event.addDomListener(this.div_, "click", cancelHandler);
216            this.eventListener3_ = google.maps.event.addDomListener(this.div_, "dblclick", cancelHandler);
217        }
218
219        this.contextListener_ = google.maps.event.addDomListener(this.div_, "contextmenu", ignoreHandler);
220
221        /**
222        * This event is fired when the DIV containing the InfoBox's content is attached to the DOM.
223        * @name InfoBox#domready
224        * @event
225        */
226        google.maps.event.trigger(this, "domready");
227    }
228};
229
230/**
231* Returns the HTML <IMG> tag for the close box.
232* @private
233*/
234InfoBox.prototype.getCloseBoxImg_ = function () {
235
236    var img = "";
237
238    if (this.closeBoxURL_ !== "") {
239
240        img = "<img";
241        img += " src='" + this.closeBoxURL_ + "'";
242        img += " align=right"; // Do this because Opera chokes on style='float: right;'
243        img += " style='";
244        img += " position: relative;"; // Required by MSIE
245        img += " cursor: pointer;";
246        img += " margin: " + this.closeBoxMargin_ + ";";
247        img += "'>";
248    }
249
250    return img;
251};
252
253/**
254* Adds the click handler to the InfoBox close box.
255* @private
256*/
257InfoBox.prototype.addClickHandler_ = function () {
258
259    var closeBox;
260
261    if (this.closeBoxURL_ !== "") {
262
263        closeBox = this.div_.firstChild;
264        this.closeListener_ = google.maps.event.addDomListener(closeBox, 'click', this.getCloseClickHandler_());
265
266    } else {
267
268        this.closeListener_ = null;
269    }
270};
271
272/**
273* Returns the function to call when the user clicks the close box of an InfoBox.
274* @private
275*/
276InfoBox.prototype.getCloseClickHandler_ = function () {
277
278    var me = this;
279
280    return function (e) {
281
282        // 1.0.3 fix: Always prevent propagation of a close box click to the map:
283        e.cancelBubble = true;
284
285        if (e.stopPropagation) {
286
287            e.stopPropagation();
288        }
289
290        me.close();
291
292        /**
293        * This event is fired when the InfoBox's close box is clicked.
294        * @name InfoBox#closeclick
295        * @event
296        */
297        google.maps.event.trigger(me, "closeclick");
298    };
299};
300
301/**
302* Pans the map so that the InfoBox appears entirely within the map's visible area.
303* @private
304*/
305InfoBox.prototype.panBox_ = function (disablePan) {
306
307    var map;
308    var bounds;
309    var xOffset = 0, yOffset = 0;
310
311    if (!disablePan) {
312
313        map = this.getMap();
314
315        if (map instanceof google.maps.Map) { // Only pan if attached to map, not panorama
316
317            if (!map.getBounds().contains(this.position_)) {
318                // Marker not in visible area of map, so set center
319                // of map to the marker position first.
320                map.setCenter(this.position_);
321            }
322
323            bounds = map.getBounds();
324
325            var mapDiv = map.getDiv();
326            var mapWidth = mapDiv.offsetWidth;
327            var mapHeight = mapDiv.offsetHeight;
328            var iwOffsetX = this.pixelOffset_.width;
329            var iwOffsetY = this.pixelOffset_.height;
330            var iwWidth = this.div_.offsetWidth;
331            var iwHeight = this.div_.offsetHeight;
332            var padX = this.infoBoxClearance_.width;
333            var padY = this.infoBoxClearance_.height;
334            var pixPosition = this.getProjection().fromLatLngToContainerPixel(this.position_);
335
336            if (pixPosition.x < (-iwOffsetX + padX)) {
337                xOffset = pixPosition.x + iwOffsetX - padX;
338            } else if ((pixPosition.x + iwWidth + iwOffsetX + padX) > mapWidth) {
339                xOffset = pixPosition.x + iwWidth + iwOffsetX + padX - mapWidth;
340            }
341            if (this.alignBottom_) {
342                if (pixPosition.y < (-iwOffsetY + padY + iwHeight)) {
343                    yOffset = pixPosition.y + iwOffsetY - padY - iwHeight;
344                } else if ((pixPosition.y + iwOffsetY + padY) > mapHeight) {
345                    yOffset = pixPosition.y + iwOffsetY + padY - mapHeight;
346                }
347            } else {
348                if (pixPosition.y < (-iwOffsetY + padY)) {
349                    yOffset = pixPosition.y + iwOffsetY - padY;
350                } else if ((pixPosition.y + iwHeight + iwOffsetY + padY) > mapHeight) {
351                    yOffset = pixPosition.y + iwHeight + iwOffsetY + padY - mapHeight;
352                }
353            }
354
355            if (!(xOffset === 0 && yOffset === 0)) {
356
357                // Move the map to the shifted center.
358                //
359                var c = map.getCenter();
360                map.panBy(xOffset, yOffset);
361            }
362        }
363    }
364};
365
366/**
367* Sets the style of the InfoBox by setting the style sheet and applying
368* other specific styles requested.
369* @private
370*/
371InfoBox.prototype.setBoxStyle_ = function () {
372
373    var i, boxStyle;
374
375    if (this.div_) {
376
377        // Apply style values from the style sheet defined in the boxClass parameter:
378        this.div_.className = this.boxClass_;
379
380        // Clear existing inline style values:
381        this.div_.style.cssText = "";
382
383        // Apply style values defined in the boxStyle parameter:
384        boxStyle = this.boxStyle_;
385        for (i in boxStyle) {
386
387            if (boxStyle.hasOwnProperty(i)) {
388
389                this.div_.style[i] = boxStyle[i];
390            }
391        }
392
393        // Fix up opacity style for benefit of MSIE:
394        //
395        if (typeof this.div_.style.opacity !== "undefined" && this.div_.style.opacity !== "") {
396
397            this.div_.style.filter = "alpha(opacity=" + (this.div_.style.opacity * 100) + ")";
398        }
399
400        // Apply required styles:
401        //
402        this.div_.style.position = "absolute";
403        this.div_.style.visibility = 'hidden';
404        if (this.zIndex_ !== null) {
405
406            this.div_.style.zIndex = this.zIndex_;
407        }
408    }
409};
410
411/**
412* Get the widths of the borders of the InfoBox.
413* @private
414* @return {Object} widths object (top, bottom left, right)
415*/
416InfoBox.prototype.getBoxWidths_ = function () {
417
418    var computedStyle;
419    var bw = { top: 0, bottom: 0, left: 0, right: 0 };
420    var box = this.div_;
421
422    if (document.defaultView && document.defaultView.getComputedStyle) {
423
424        computedStyle = box.ownerDocument.defaultView.getComputedStyle(box, "");
425
426        if (computedStyle) {
427
428            // The computed styles are always in pixel units (good!)
429            bw.top = parseInt(computedStyle.borderTopWidth, 10) || 0;
430            bw.bottom = parseInt(computedStyle.borderBottomWidth, 10) || 0;
431            bw.left = parseInt(computedStyle.borderLeftWidth, 10) || 0;
432            bw.right = parseInt(computedStyle.borderRightWidth, 10) || 0;
433        }
434
435    } else if (document.documentElement.currentStyle) { // MSIE
436
437        if (box.currentStyle) {
438
439            // The current styles may not be in pixel units, but assume they are (bad!)
440            bw.top = parseInt(box.currentStyle.borderTopWidth, 10) || 0;
441            bw.bottom = parseInt(box.currentStyle.borderBottomWidth, 10) || 0;
442            bw.left = parseInt(box.currentStyle.borderLeftWidth, 10) || 0;
443            bw.right = parseInt(box.currentStyle.borderRightWidth, 10) || 0;
444        }
445    }
446
447    return bw;
448};
449
450/**
451* Invoked when <tt>close</tt> is called. Do not call it directly.
452*/
453InfoBox.prototype.onRemove = function () {
454
455    if (this.div_) {
456
457        this.div_.parentNode.removeChild(this.div_);
458        this.div_ = null;
459    }
460};
461
462/**
463* Draws the InfoBox based on the current map projection and zoom level.
464*/
465InfoBox.prototype.draw = function () {
466
467    this.createInfoBoxDiv_();
468
469    var pixPosition = this.getProjection().fromLatLngToDivPixel(this.position_);
470
471    this.div_.style.left = (pixPosition.x + this.pixelOffset_.width) + "px";
472
473    if (this.alignBottom_) {
474        this.div_.style.bottom = -(pixPosition.y + this.pixelOffset_.height) + "px";
475    } else {
476        this.div_.style.top = (pixPosition.y + this.pixelOffset_.height) + "px";
477    }
478
479    if (this.isHidden_) {
480
481        this.div_.style.visibility = 'hidden';
482
483    } else {
484
485        this.div_.style.visibility = "visible";
486    }
487};
488
489/**
490* Sets the options for the InfoBox. Note that changes to the <tt>maxWidth</tt>,
491*  <tt>closeBoxMargin</tt>, <tt>closeBoxURL</tt>, and <tt>enableEventPropagation</tt>
492*  properties have no affect until the current InfoBox is <tt>close</tt>d and a new one
493*  is <tt>open</tt>ed.
494* @param {InfoBoxOptions} opt_opts
495*/
496InfoBox.prototype.setOptions = function (opt_opts) {
497    if (typeof opt_opts.boxClass !== "undefined") { // Must be first
498
499        this.boxClass_ = opt_opts.boxClass;
500        this.setBoxStyle_();
501    }
502    if (typeof opt_opts.boxStyle !== "undefined") { // Must be second
503
504        this.boxStyle_ = opt_opts.boxStyle;
505        this.setBoxStyle_();
506    }
507    if (typeof opt_opts.content !== "undefined") {
508
509        this.setContent(opt_opts.content);
510    }
511    if (typeof opt_opts.disableAutoPan !== "undefined") {
512
513        this.disableAutoPan_ = opt_opts.disableAutoPan;
514    }
515    if (typeof opt_opts.maxWidth !== "undefined") {
516
517        this.maxWidth_ = opt_opts.maxWidth;
518    }
519    if (typeof opt_opts.pixelOffset !== "undefined") {
520
521        this.pixelOffset_ = opt_opts.pixelOffset;
522    }
523    if (typeof opt_opts.position !== "undefined") {
524
525        this.setPosition(opt_opts.position);
526    }
527    if (typeof opt_opts.zIndex !== "undefined") {
528
529        this.setZIndex(opt_opts.zIndex);
530    }
531    if (typeof opt_opts.closeBoxMargin !== "undefined") {
532
533        this.closeBoxMargin_ = opt_opts.closeBoxMargin;
534    }
535    if (typeof opt_opts.closeBoxURL !== "undefined") {
536
537        this.closeBoxURL_ = opt_opts.closeBoxURL;
538    }
539    if (typeof opt_opts.infoBoxClearance !== "undefined") {
540
541        this.infoBoxClearance_ = opt_opts.infoBoxClearance;
542    }
543    if (typeof opt_opts.isHidden !== "undefined") {
544
545        this.isHidden_ = opt_opts.isHidden;
546    }
547    if (typeof opt_opts.enableEventPropagation !== "undefined") {
548
549        this.enableEventPropagation_ = opt_opts.enableEventPropagation;
550    }
551
552    if (this.div_) {
553
554        this.draw();
555    }
556};
557
558/**
559* Sets the content of the InfoBox.
560*  The content can be plain text or an HTML DOM node.
561* @param {string|Node} content
562*/
563InfoBox.prototype.setContent = function (content) {
564    this.content_ = content;
565
566    if (this.div_) {
567
568        if (this.closeListener_) {
569
570            google.maps.event.removeListener(this.closeListener_);
571            this.closeListener_ = null;
572        }
573
574        // Odd code required to make things work with MSIE.
575        //
576        if (!this.fixedWidthSet_) {
577
578            this.div_.style.width = "";
579        }
580
581        if (typeof content.nodeType === "undefined") {
582            this.div_.innerHTML = this.getCloseBoxImg_() + content;
583        } else {
584            this.div_.innerHTML = this.getCloseBoxImg_();
585            this.div_.appendChild(content);
586        }
587
588        // Perverse code required to make things work with MSIE.
589        // (Ensures the close box does, in fact, float to the right.)
590        //
591        if (!this.fixedWidthSet_) {
592
593            this.div_.style.width = this.div_.offsetWidth + "px";
594            this.div_.innerHTML = this.getCloseBoxImg_() + content;
595        }
596
597        this.addClickHandler_();
598    }
599
600    /**
601    * This event is fired when the content of the InfoBox changes.
602    * @name InfoBox#content_changed
603    * @event
604    */
605    google.maps.event.trigger(this, "content_changed");
606};
607
608/**
609* Sets the geographic location of the InfoBox.
610* @param {LatLng} latlng
611*/
612InfoBox.prototype.setPosition = function (latlng) {
613
614    this.position_ = latlng;
615
616    if (this.div_) {
617
618        this.draw();
619    }
620
621    /**
622    * This event is fired when the position of the InfoBox changes.
623    * @name InfoBox#position_changed
624    * @event
625    */
626    google.maps.event.trigger(this, "position_changed");
627};
628
629/**
630* Sets the zIndex style for the InfoBox.
631* @param {number} index
632*/
633InfoBox.prototype.setZIndex = function (index) {
634
635    this.zIndex_ = index;
636
637    if (this.div_) {
638
639        this.div_.style.zIndex = index;
640    }
641
642    /**
643    * This event is fired when the zIndex of the InfoBox changes.
644    * @name InfoBox#zindex_changed
645    * @event
646    */
647    google.maps.event.trigger(this, "zindex_changed");
648};
649
650/**
651* Returns the content of the InfoBox.
652* @returns {string}
653*/
654InfoBox.prototype.getContent = function () {
655
656    return this.content_;
657};
658
659/**
660* Returns the geographic location of the InfoBox.
661* @returns {LatLng}
662*/
663InfoBox.prototype.getPosition = function () {
664
665    return this.position_;
666};
667
668/**
669* Returns the zIndex for the InfoBox.
670* @returns {number}
671*/
672InfoBox.prototype.getZIndex = function () {
673
674    return this.zIndex_;
675};
676
677/**
678* Shows the InfoBox.
679*/
680InfoBox.prototype.show = function () {
681
682    this.isHidden_ = false;
683    if (this.div_) {
684        this.div_.style.visibility = "visible";
685    }
686};
687
688/**
689* Hides the InfoBox.
690*/
691InfoBox.prototype.hide = function () {
692
693    this.isHidden_ = true;
694    if (this.div_) {
695        this.div_.style.visibility = "hidden";
696    }
697};
698
699/**
700* Adds the InfoBox to the specified map or Street View panorama. If <tt>anchor</tt>
701*  (usually a <tt>google.maps.Marker</tt>) is specified, the position
702*  of the InfoBox is set to the position of the <tt>anchor</tt>. If the
703*  anchor is dragged to a new location, the InfoBox moves as well.
704* @param {Map|StreetViewPanorama} map
705* @param {MVCObject} [anchor]
706*/
707InfoBox.prototype.open = function (map, anchor) {
708
709    var me = this;
710
711    if (anchor) {
712
713        this.position_ = anchor.getPosition();
714        this.moveListener_ = google.maps.event.addListener(anchor, "position_changed", function () {
715            me.setPosition(this.getPosition());
716        });
717    }
718
719    this.setMap(map);
720
721    if (this.div_) {
722
723        this.panBox_();
724    }
725};
726
727/**
728* Removes the InfoBox from the map.
729*/
730InfoBox.prototype.close = function () {
731
732    if (this.closeListener_) {
733
734        google.maps.event.removeListener(this.closeListener_);
735        this.closeListener_ = null;
736    }
737
738    if (this.eventListener1_) {
739
740        google.maps.event.removeListener(this.eventListener1_);
741        google.maps.event.removeListener(this.eventListener2_);
742        google.maps.event.removeListener(this.eventListener3_);
743        this.eventListener1_ = null;
744        this.eventListener2_ = null;
745        this.eventListener3_ = null;
746    }
747
748    if (this.moveListener_) {
749
750        google.maps.event.removeListener(this.moveListener_);
751        this.moveListener_ = null;
752    }
753
754    if (this.contextListener_) {
755
756        google.maps.event.removeListener(this.contextListener_);
757        this.contextListener_ = null;
758    }
759
760    this.setMap(null);
761};
762
763(function () {
764    'use strict';
765
766    const app = angular.module('customFilters', []);
767
768    app.filter('ifTextReturnNumber', ['$filter', function ($filter) {
769        return function (input, fractionSize) {
770            if (isNaN(input)) {
771                return '0';//input;
772            } else {
773                return $filter('number')(input, fractionSize);
774            }
775        };
776    }]);
777
778    app.filter('na',
779        [
780            '$filter', function($filter) {
781                return function(input) {
782                    return input === '' || input === undefined || input === null || input.length === 0 ? $filter('translate')('not_available') : input;
783                };
784            }
785        ]);
786
787    app.filter('tel', function() {
788        return function(tel) {
789            if (!tel) {
790                return $filter('translate')('not_available');
791            }
792
793            const value = tel.toString().trim().replace(/^\+/, '');
794
795            if (value.match(/[^0-9]/)) {
796                return tel;
797            }
798
799            var country, city, number;
800
801            switch (value.length) {
802            case 10: // +1PPP####### -> C (PPP) ###-####
803                country = 1;
804                city = value.slice(0, 3);
805                number = value.slice(3);
806                break;
807
808            case 11: // +CPPP####### -> CCC (PP) ###-####
809                country = value[0];
810                city = value.slice(1, 4);
811                number = value.slice(4);
812                break;
813
814            case 12: // +CCCPP####### -> CCC (PP) ###-####
815                country = value.slice(0, 3);
816                city = value.slice(3, 5);
817                number = value.slice(5);
818                break;
819
820            default:
821                return tel;
822            }
823
824            if (country == 1) {
825                country = '';
826            }
827
828            number = number.slice(0, 3) + '-' + number.slice(3);
829
830            return (country + ' (' + city + ') ' + number).trim();
831        };
832
833    });
834
835    app.filter('obfuscateValue', ['$rootScope', function ($rootScope) {
836            return function (item) {
837
838                if ($rootScope.TestUserMode) {
839                    return String(item).substring(0, 1) + 'xxxxxx' + String(item).substring(1, 2);
840                }
841                else {
842                    return item;
843                }
844            };
845        }
846    ]);
847
848    app.filter('excludeFrom', [function () {
849        return function (inputArray, filterCriteria) {
850            return inputArray.filter(function (item) {
851                // if the value of filterCriteria is "falsy", retain the inputArray as it is
852                // then check if the currently checked item in the inputArray is different from the filterCriteria,
853                // if so, keep it in the filtered results
854                return !filterCriteria || !angular.equals(item, filterCriteria);
855            });
856        };
857    }]);
858
859    app.filter('containsHtml', ['utilitiesService', function (utilitiesService) {
860        return function (input) {
861            return utilitiesService.containsHtml(input);
862        };
863    }]);
864
865    app.filter('trustedHtml', ['$sce', function ($sce) {
866        return function (html) {
867              return $sce.trustAsHtml(html);
868          };
869      }]);
870
871})();
872/**
873 * Minified by jsDelivr using Terser v5.19.2.
874 * Original file: /npm/[email protected]/src/toastify.js
875 *
876 * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
877 */
878/*!
879 * Toastify js 1.12.0
880 * https://github.com/apvarun/toastify-js
881 * @license MIT licensed
882 *
883 * Copyright (C) 2018 Varun A P
884 */
885!function (t, o) { "object" == typeof module && module.exports ? module.exports = o() : t.Toastify = o() }(this, (function (t) { var o = function (t) { return new o.lib.init(t) }; function i(t, o) { return o.offset[t] ? isNaN(o.offset[t]) ? o.offset[t] : o.offset[t] + "px" : "0px" } function s(t, o) { return !(!t || "string" != typeof o) && !!(t.className && t.className.trim().split(/\s+/gi).indexOf(o) > -1) } return o.defaults = { oldestFirst: !0, text: "Toastify is awesome!", node: void 0, duration: 3e3, selector: void 0, callback: function () { }, destination: void 0, newWindow: !1, close: !1, gravity: "toastify-top", positionLeft: !1, position: "", backgroundColor: "", avatar: "", className: "", stopOnFocus: !0, onClick: function () { }, offset: { x: 0, y: 0 }, escapeMarkup: !0, ariaLive: "polite", style: { background: "" } }, o.lib = o.prototype = { toastify: "1.12.0", constructor: o, init: function (t) { return t || (t = {}), this.options = {}, this.toastElement = null, this.options.text = t.text || o.defaults.text, this.options.node = t.node || o.defaults.node, this.options.duration = 0 === t.duration ? 0 : t.duration || o.defaults.duration, this.options.selector = t.selector || o.defaults.selector, this.options.callback = t.callback || o.defaults.callback, this.options.destination = t.destination || o.defaults.destination, this.options.newWindow = t.newWindow || o.defaults.newWindow, this.options.close = t.close || o.defaults.close, this.options.gravity = "bottom" === t.gravity ? "toastify-bottom" : o.defaults.gravity, this.options.positionLeft = t.positionLeft || o.defaults.positionLeft, this.options.position = t.position || o.defaults.position, this.options.backgroundColor = t.backgroundColor || o.defaults.backgroundColor, this.options.avatar = t.avatar || o.defaults.avatar, this.options.className = t.className || o.defaults.className, this.options.stopOnFocus = void 0 === t.stopOnFocus ? o.defaults.stopOnFocus : t.stopOnFocus, this.options.onClick = t.onClick || o.defaults.onClick, this.options.offset = t.offset || o.defaults.offset, this.options.escapeMarkup = void 0 !== t.escapeMarkup ? t.escapeMarkup : o.defaults.escapeMarkup, this.options.ariaLive = t.ariaLive || o.defaults.ariaLive, this.options.style = t.style || o.defaults.style, t.backgroundColor && (this.options.style.background = t.backgroundColor), this }, buildToast: function () { if (!this.options) throw "Toastify is not initialized"; var t = document.createElement("div"); for (var o in t.className = "toastify on " + this.options.className, this.options.position ? t.className += " toastify-" + this.options.position : !0 === this.options.positionLeft ? (t.className += " toastify-left", console.warn("Property `positionLeft` will be depreciated in further versions. Please use `position` instead.")) : t.className += " toastify-right", t.className += " " + this.options.gravity, this.options.backgroundColor && console.warn('DEPRECATION NOTICE: "backgroundColor" is being deprecated. Please use the "style.background" property.'), this.options.style) t.style[o] = this.options.style[o]; if (this.options.ariaLive && t.setAttribute("aria-live", this.options.ariaLive), this.options.node && this.options.node.nodeType === Node.ELEMENT_NODE) t.appendChild(this.options.node); else if (this.options.escapeMarkup ? t.innerText = this.options.text : t.innerHTML = this.options.text, "" !== this.options.avatar) { var s = document.createElement("img"); s.src = this.options.avatar, s.className = "toastify-avatar", "left" == this.options.position || !0 === this.options.positionLeft ? t.appendChild(s) : t.insertAdjacentElement("afterbegin", s) } if (!0 === this.options.close) { var e = document.createElement("button"); e.type = "button", e.setAttribute("aria-label", "Close"), e.className = "toast-close", e.innerHTML = "&#10006;", e.addEventListener("click", function (t) { t.stopPropagation(), this.removeElement(this.toastElement), window.clearTimeout(this.toastElement.timeOutValue) }.bind(this)); var n = window.innerWidth > 0 ? window.innerWidth : screen.width; ("left" == this.options.position || !0 === this.options.positionLeft) && n > 360 ? t.insertAdjacentElement("afterbegin", e) : t.appendChild(e) } if (this.options.stopOnFocus && this.options.duration > 0) { var a = this; t.addEventListener("mouseover", (function (o) { window.clearTimeout(t.timeOutValue) })), t.addEventListener("mouseleave", (function () { t.timeOutValue = window.setTimeout((function () { a.removeElement(t) }), a.options.duration) })) } if (void 0 !== this.options.destination && t.addEventListener("click", function (t) { t.stopPropagation(), !0 === this.options.newWindow ? window.open(this.options.destination, "_blank") : window.location = this.options.destination }.bind(this)), "function" == typeof this.options.onClick && void 0 === this.options.destination && t.addEventListener("click", function (t) { t.stopPropagation(), this.options.onClick() }.bind(this)), "object" == typeof this.options.offset) { var l = i("x", this.options), r = i("y", this.options), p = "left" == this.options.position ? l : "-" + l, d = "toastify-top" == this.options.gravity ? r : "-" + r; t.style.transform = "translate(" + p + "," + d + ")" } return t }, showToast: function () { var t; if (this.toastElement = this.buildToast(), !(t = "string" == typeof this.options.selector ? document.getElementById(this.options.selector) : this.options.selector instanceof HTMLElement || "undefined" != typeof ShadowRoot && this.options.selector instanceof ShadowRoot ? this.options.selector : document.body)) throw "Root element is not defined";
885 var i = o.defaults.oldestFirst ? t.firstChild : t.lastChild; return t.insertBefore(this.toastElement, i), o.reposition(), this.options.duration > 0 && (this.toastElement.timeOutValue = window.setTimeout(function () { this.removeElement(this.toastElement) }.bind(this), this.options.duration)), this }, hideToast: function () { this.toastElement.timeOutValue && clearTimeout(this.toastElement.timeOutValue), this.removeElement(this.toastElement) }, removeElement: function (t) { t.className = t.className.replace(" on", ""), window.setTimeout(function () { this.options.node && this.options.node.parentNode && this.options.node.parentNode.removeChild(this.options.node), t.parentNode && t.parentNode.removeChild(t), this.options.callback.call(t), o.reposition() }.bind(this), 400) } }, o.reposition = function () { for (var t, o = { top: 15, bottom: 15 }, i = { top: 15, bottom: 15 }, e = { top: 15, bottom: 15 }, n = document.getElementsByClassName("toastify"), a = 0; a < n.length; a++) { t = !0 === s(n[a], "toastify-top") ? "toastify-top" : "toastify-bottom"; var l = n[a].offsetHeight; t = t.substr(9, t.length - 1); (window.innerWidth > 0 ? window.innerWidth : screen.width) <= 360 ? (n[a].style[t] = e[t] + "px", e[t] += l + 15) : !0 === s(n[a], "toastify-left") ? (n[a].style[t] = o[t] + "px", o[t] += l + 15) : (n[a].style[t] = i[t] + "px", i[t] += l + 15) } return this }, o.lib.init.prototype = o.lib, o }));
886//# sourceMappingURL=/sm/e1ebbfe1bf0b0061f0726ebc83434e1c2f8308e6354c415fd05ecccdaad47617.map
887/**
888 * mOxie - multi-runtime File API & XMLHttpRequest L2 Polyfill
889 * v1.2.1
890 *
891 * Copyright 2013, Moxiecode Systems AB
892 * Released under GPL License.
893 *
894 * License: http://www.plupload.com/license
895 * Contributing: http://www.plupload.com/contributing
896 *
897 * Date: 2014-05-14
898 */
899/**
900 * Compiled inline version. (Library mode)
901 */
902
903/*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
904/*globals $code */
905
906(function(exports, undefined) {
907	"use strict";
908
909	var modules = {};
910
911	function require(ids, callback) {
912		var module, defs = [];
913
914		for (var i = 0; i < ids.length; ++i) {
915			module = modules[ids[i]] || resolve(ids[i]);
916			if (!module) {
917				throw 'module definition dependecy not found: ' + ids[i];
918			}
919
920			defs.push(module);
921		}
922
923		callback.apply(null, defs);
924	}
925
926	function define(id, dependencies, definition) {
927		if (typeof id !== 'string') {
928			throw 'invalid module definition, module id must be defined and be a string';
929		}
930
931		if (dependencies === undefined) {
932			throw 'invalid module definition, dependencies must be specified';
933		}
934
935		if (definition === undefined) {
936			throw 'invalid module definition, definition function must be specified';
937		}
938
939		require(dependencies, function() {
940			modules[id] = definition.apply(null, arguments);
941		});
942	}
943
944	function defined(id) {
945		return !!modules[id];
946	}
947
948	function resolve(id) {
949		var target = exports;
950		var fragments = id.split(/[.\/]/);
951
952		for (var fi = 0; fi < fragments.length; ++fi) {
953			if (!target[fragments[fi]]) {
954				return;
955			}
956
957			target = target[fragments[fi]];
958		}
959
960		return target;
961	}
962
963	function expose(ids) {
964		for (var i = 0; i < ids.length; i++) {
965			var target = exports;
966			var id = ids[i];
967			var fragments = id.split(/[.\/]/);
968
969			for (var fi = 0; fi < fragments.length - 1; ++fi) {
970				if (target[fragments[fi]] === undefined) {
971					target[fragments[fi]] = {};
972				}
973
974				target = target[fragments[fi]];
975			}
976
977			target[fragments[fragments.length - 1]] = modules[id];
978		}
979	}
980
981// Included from: src/javascript/core/utils/Basic.js
982
983/**
984 * Basic.js
985 *
986 * Copyright 2013, Moxiecode Systems AB
987 * Released under GPL License.
988 *
989 * License: http://www.plupload.com/license
990 * Contributing: http://www.plupload.com/contributing
991 */
992
993define('moxie/core/utils/Basic', [], function() {
994	/**
995	Gets the true type of the built-in object (better version of typeof).
996	@author Angus Croll (http://javascriptweblog.wordpress.com/)
997
998	@method typeOf
999	@for Utils
1000	@static
1001	@param {Object} o Object to check.
1002	@return {String} Object [[Class]]
1003	*/
1004	var typeOf = function(o) {
1005		var undef;
1006
1007		if (o === undef) {
1008			return 'undefined';
1009		} else if (o === null) {
1010			return 'null';
1011		} else if (o.nodeType) {
1012			return 'node';
1013		}
1014
1015		// the snippet below is awesome, however it fails to detect null, undefined and arguments types in IE lte 8
1016		return ({}).toString.call(o).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
1017	};
1018		
1019	/**
1020	Extends the specified object with another object.
1021
1022	@method extend
1023	@static
1024	@param {Object} target Object to extend.
1025	@param {Object} [obj]* Multiple objects to extend with.
1026	@return {Object} Same as target, the extended object.
1027	*/
1028	var extend = function(target) {
1029		var undef;
1030
1031		each(arguments, function(arg, i) {
1032			if (i > 0) {
1033				each(arg, function(value, key) {
1034					if (value !== undef) {
1035						if (typeOf(target[key]) === typeOf(value) && !!~inArray(typeOf(value), ['array', 'object'])) {
1036							extend(target[key], value);
1037						} else {
1038							target[key] = value;
1039						}
1040					}
1041				});
1042			}
1043		});
1044		return target;
1045	};
1046		
1047	/**
1048	Executes the callback function for each item in array/object. If you return false in the
1049	callback it will break the loop.
1050
1051	@method each
1052	@static
1053	@param {Object} obj Object to iterate.
1054	@param {function} callback Callback function to execute for each item.
1055	*/
1056	var each = function(obj, callback) {
1057		var length, key, i, undef;
1058
1059		if (obj) {
1060			try {
1061				length = obj.length;
1062			} catch(ex) {
1063				length = undef;
1064			}
1065
1066			if (length === undef) {
1067				// Loop object items
1068				for (key in obj) {
1069					if (obj.hasOwnProperty(key)) {
1070						if (callback(obj[key], key) === false) {
1071							return;
1072						}
1073					}
1074				}
1075			} else {
1076				// Loop array items
1077				for (i = 0; i < length; i++) {
1078					if (callback(obj[i], i) === false) {
1079						return;
1080					}
1081				}
1082			}
1083		}
1084	};
1085
1086	/**
1087	Checks if object is empty.
1088	
1089	@method isEmptyObj
1090	@static
1091	@param {Object} o Object to check.
1092	@return {Boolean}
1093	*/
1094	var isEmptyObj = function(obj) {
1095		var prop;
1096
1097		if (!obj || typeOf(obj) !== 'object') {
1098			return true;
1099		}
1100
1101		for (prop in obj) {
1102			return false;
1103		}
1104
1105		return true;
1106	};
1107
1108	/**
1109	Recieve an array of functions (usually async) to call in sequence, each  function
1110	receives a callback as first argument that it should call, when it completes. Finally,
1111	after everything is complete, main callback is called. Passing truthy value to the
1112	callback as a first argument will interrupt the sequence and invoke main callback
1113	immediately.
1114
1115	@method inSeries
1116	@static
1117	@param {Array} queue Array of functions to call in sequence
1118	@param {Function}
1118 cb Main callback that is called in the end, or in case of error
1119	*/
1120	var inSeries = function(queue, cb) {
1121		var i = 0, length = queue.length;
1122
1123		if (typeOf(cb) !== 'function') {
1124			cb = function() {};
1125		}
1126
1127		if (!queue || !queue.length) {
1128			cb();
1129		}
1130
1131		function callNext(i) {
1132			if (typeOf(queue[i]) === 'function') {
1133				queue[i](function(error) {
1134					/*jshint expr:true */
1135					++i < length && !error ? callNext(i) : cb(error);
1136				});
1137			}
1138		}
1139		callNext(i);
1140	};
1141
1142
1143	/**
1144	Recieve an array of functions (usually async) to call in parallel, each  function
1145	receives a callback as first argument that it should call, when it completes. After 
1146	everything is complete, main callback is called. Passing truthy value to the
1147	callback as a first argument will interrupt the process and invoke main callback
1148	immediately.
1149
1150	@method inParallel
1151	@static
1152	@param {Array} queue Array of functions to call in sequence
1153	@param {Function} cb Main callback that is called in the end, or in case of erro
1154	*/
1155	var inParallel = function(queue, cb) {
1156		var count = 0, num = queue.length, cbArgs = new Array(num);
1157
1158		each(queue, function(fn, i) {
1159			fn(function(error) {
1160				if (error) {
1161					return cb(error);
1162				}
1163				
1164				var args = [].slice.call(arguments);
1165				args.shift(); // strip error - undefined or not
1166
1167				cbArgs[i] = args;
1168				count++;
1169
1170				if (count === num) {
1171					cbArgs.unshift(null);
1172					cb.apply(this, cbArgs);
1173				} 
1174			});
1175		});
1176	};
1177	
1178	
1179	/**
1180	Find an element in array and return it's index if present, otherwise return -1.
1181	
1182	@method inArray
1183	@static
1184	@param {Mixed} needle Element to find
1185	@param {Array} array
1186	@return {Int} Index of the element, or -1 if not found
1187	*/
1188	var inArray = function(needle, array) {
1189		if (array) {
1190			if (Array.prototype.indexOf) {
1191				return Array.prototype.indexOf.call(array, needle);
1192			}
1193		
1194			for (var i = 0, length = array.length; i < length; i++) {
1195				if (array[i] === needle) {
1196					return i;
1197				}
1198			}
1199		}
1200		return -1;
1201	};
1202
1203
1204	/**
1205	Returns elements of first array if they are not present in second. And false - otherwise.
1206
1207	@private
1208	@method arrayDiff
1209	@param {Array} needles
1210	@param {Array} array
1211	@return {Array|Boolean}
1212	*/
1213	var arrayDiff = function(needles, array) {
1214		var diff = [];
1215
1216		if (typeOf(needles) !== 'array') {
1217			needles = [needles];
1218		}
1219
1220		if (typeOf(array) !== 'array') {
1221			array = [array];
1222		}
1223
1224		for (var i in needles) {
1225			if (inArray(needles[i], array) === -1) {
1226				diff.push(needles[i]);
1227			}	
1228		}
1229		return diff.length ? diff : false;
1230	};
1231
1232
1233	/**
1234	Find intersection of two arrays.
1235
1236	@private
1237	@method arrayIntersect
1238	@param {Array} array1
1239	@param {Array} array2
1240	@return {Array} Intersection of two arrays or null if there is none
1241	*/
1242	var arrayIntersect = function(array1, array2) {
1243		var result = [];
1244		each(array1, function(item) {
1245			if (inArray(item, array2) !== -1) {
1246				result.push(item);
1247			}
1248		});
1249		return result.length ? result : null;
1250	};
1251	
1252	
1253	/**
1254	Forces anything into an array.
1255	
1256	@method toArray
1257	@static
1258	@param {Object} obj Object with length field.
1259	@return {Array} Array object containing all items.
1260	*/
1261	var toArray = function(obj) {
1262		var i, arr = [];
1263
1264		for (i = 0; i < obj.length; i++) {
1265			arr[i] = obj[i];
1266		}
1267
1268		return arr;
1269	};
1270	
1271			
1272	/**
1273	Generates an unique ID. This is 99.99% unique since it takes the current time and 5 random numbers.
1274	The only way a user would be able to get the same ID is if the two persons at the same exact milisecond manages
1275	to get 5 the same random numbers between 0-65535 it also uses a counter so each call will be guaranteed to be page unique.
1276	It's more probable for the earth to be hit with an ansteriod. Y
1277	
1278	@method guid
1279	@static
1280	@param {String} prefix to prepend (by default 'o' will be prepended).
1281	@method guid
1282	@return {String} Virtually unique id.
1283	*/
1284	var guid = (function() {
1285		var counter = 0;
1286		
1287		return function(prefix) {
1288			var guid = new Date().getTime().toString(32), i;
1289
1290			for (i = 0; i < 5; i++) {
1291				guid += Math.floor(Math.random() * 65535).toString(32);
1292			}
1293			
1294			return (prefix || 'o_') + guid + (counter++).toString(32);
1295		};
1296	}());
1297	
1298
1299	/**
1300	Trims white spaces around the string
1301	
1302	@method trim
1303	@static
1304	@param {String} str
1305	@return {String}
1306	*/
1307	var trim = function(str) {
1308		if (!str) {
1309			return str;
1310		}
1311		return String.prototype.trim ? String.prototype.trim.call(str) : str.toString().replace(/^\s*/, '').replace(/\s*$/, '');
1312	};
1313
1314
1315	/**
1316	Parses the specified size string into a byte value. For example 10kb becomes 10240.
1317	
1318	@method parseSizeStr
1319	@static
1320	@param {String/Number} size String to parse or number to just pass through.
1321	@return {Number} Size in bytes.
1322	*/
1323	var parseSizeStr = function(size) {
1324		if (typeof(size) !== 'string') {
1325			return size;
1326		}
1327		
1328		var muls = {
1329				t: 1099511627776,
1330				g: 1073741824,
1331				m: 1048576,
1332				k: 1024
1333			},
1334			mul;
1335
1336		size = /^([0-9]+)([mgk]?)$/.exec(size.toLowerCase().replace(/[^0-9mkg]/g, ''));
1337		mul = size[2];
1338		size = +size[1];
1339		
1340		if (muls.hasOwnProperty(mul)) {
1341			size *= muls[mul];
1342		}
1343		return size;
1344	};
1345	
1346
1347	return {
1348		guid: guid,
1349		typeOf: typeOf,
1350		extend: extend,
1351		each: each,
1352		isEmptyObj: isEmptyObj,
1353		inSeries: inSeries,
1354		inParallel: inParallel,
1355		inArray: inArray,
1356		arrayDiff: arrayDiff,
1357		arrayIntersect: arrayIntersect,
1358		toArray: toArray,
1359		trim: trim,
1360		parseSizeStr: parseSizeStr
1361	};
1362});
1363
1364// Included from: src/javascript/core/I18n.js
1365
1366/**
1367 * I18n.js
1368 *
1369 * Copyright 2013, Moxiecode Systems AB
1370 * Released under GPL License.
1371 *
1372 * License: http://www.plupload.com/license
1373 * Contributing: http://www.plupload.com/contributing
1374 */
1375
1376define("moxie/core/I18n", [
1377	"moxie/core/utils/Basic"
1378], function(Basic) {
1379	var i18n = {};
1380
1381	return {
1382		/**
1383		 * Extends the language pack object with new items.
1384		 *
1385		 * @param {Object} pack Language pack items to add.
1386		 * @return {Object} Extended language pack object.
1387		 */
1388		addI18n: function(pack) {
1389			return Basic.extend(i18n, pack);
1390		},
1391
1392		/**
1393		 * Translates the specified string by checking for the english string in the language pack lookup.
1394		 *
1395		 * @param {String} str String to look for.
1396		 * @return {String} Translated string or the input string if it wasn't found.
1397		 */
1398		translate: function(str) {
1399			return i18n[str] || str;
1400		},
1401
1402		/**
1403		 * Shortcut for translate function
1404		 *
1405		 * @param {String} str String to look for.
1406		 * @return {String} Translated string or the input string if it wasn't found.
1407		 */
1408		_: function(str) {
1409			return this.translate(str);
1410		},
1411
1412		/**
1413		 * Pseudo sprintf implementation - simple way to replace tokens with specified values.
1414		 *
1415		 * @param {String} str String with tokens
1416		 * @return {String} String with replaced tokens
1417		 */
1418		sprintf: function(str) {
1419			var args = [].slice.call(arguments, 1);
1420
1421			return str.replace(/%[a-z]/g, function() {
1422				var value = args.shift();
1423				return Basic.typeOf(value) !== 'undefined' ? value : '';
1424			});
1425		}
1426	};
1427});
1428
1429// Included from: src/javascript/core/utils/Mime.js
1430
1431/**
1432 * Mime.js
1433 *
1434 * Copyright 2013, Moxiecode Systems AB
1435 * Released under GPL License.
1436 *
1437 * License: http://www.plupload.com/license
1438 * Contributing: http://www.plupload.com/contributing
1439 */
1440
1441define("moxie/core/utils/Mime", [
1442	"moxie/core/utils/Basic",
1443	"moxie/core/I18n"
1444], function(Basic, I18n) {
1445	
1446	var mimeData = "" +
1447		"application/msword,doc dot," +
1448		"application/pdf,pdf," +
1449		"application/pgp-signature,pgp," +
1450		"application/postscript,ps ai eps," +
1451		"application/rtf,rtf," +
1452		"application/vnd.ms-excel,xls xlb," +
1453		"application/vnd.ms-powerpoint,ppt pps pot," +
1454		"application/zip,zip," +
1455		"application/x-shockwave-flash,swf swfl," +
1456		"application/vnd.openxmlformats-officedocument.wordprocessingml.document,docx," +
1457		"application/vnd.openxmlformats-officedocument.wordprocessingml.template,dotx," +
1458		"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,xlsx," +
1459		"application/vnd.openxmlformats-officedocument.presentationml.presentation,pptx," +
1460		"application/vnd.openxmlformats-officedocument.presentationml.template,potx," +
1461		"application/vnd.openxmlformats-officedocument.presentationml.slideshow,ppsx," +
1462		"application/x-javascript,js," +
1463		"application/json,json," +
1464		"audio/mpeg,mp3 mpga mpega mp2," +
1465		"audio/x-wav,wav," +
1466		"audio/x-m4a,m4a," +
1467		"audio/ogg,oga ogg," +
1468		"audio/aiff,aiff aif," +
1469		"audio/flac,flac," +
1470		"audio/aac,aac," +
1471		"audio/ac3,ac3," +
1472		"audio/x-ms-wma,wma," +
1473		"image/bmp,bmp," +
1474		"image/gif,gif," +
1475		"image/jpeg,jpg jpeg jpe," +
1476		"image/photoshop,psd," +
1477		"image/png,png," +
1478		"image/svg+xml,svg svgz," +
1479		"image/tiff,tiff tif," +
1480		"text/plain,asc txt text diff log," +
1481		"text/html,htm html xhtml," +
1482		"text/css,css," +
1483		"text/csv,csv," +
1484		"text/rtf,rtf," +
1485		"video/mpeg,mpeg mpg mpe m2v," +
1486		"video/quicktime,qt mov," +
1487		"video/mp4,mp4," +
1488		"video/x-m4v,m4v," +
1489		"video/x-flv,flv," +
1490		"video/x-ms-wmv,wmv," +
1491		"video/avi,avi," +
1492		"video/webm,webm," +
1493		"video/3gpp,3gpp 3gp," +
1494		"video/3gpp2,3g2," +
1495		"video/vnd.rn-realvideo,rv," +
1496		"video/ogg,ogv," + 
1497		"video/x-matroska,mkv," +
1498		"application/vnd.oasis.opendocument.formula-template,otf," +
1499		"application/octet-stream,exe";
1500	
1501	
1502	var Mime = {
1503
1504		mimes: {},
1505
1506		extensions: {},
1507
1508		// Parses the default mime types string into a mimes and extensions lookup maps
1509		addMimeType: function (mimeData) {
1510			var items = mimeData.split(/,/), i, ii, ext;
1511			
1512			for (i = 0; i < items.length; i += 2) {
1513				ext = items[i + 1].split(/ /);
1514
1515				// extension to mime lookup
1516				for (ii = 0; ii < ext.length; ii++) {
1517					this.mimes[ext[ii]] = items[i];
1518				}
1519				// mime to extension lookup
1520				this.extensions[items[i]] = ext;
1521			}
1522		},
1523
1524
1525		extList2mimes: function (filters, addMissingExtensions) {
1526			var self = this, ext, i, ii, type, mimes = [];
1527			
1528			// convert extensions to mime types list
1529			for (i = 0; i < filters.length; i++) {
1530				ext = filters[i].extensions.split(/\s*,\s*/);
1531
1532				for (ii = 0; ii < ext.length; ii++) {
1533					
1534					// if there's an asterisk in the list, then accept attribute is not required
1535					if (ext[ii] === '*') {
1536						return [];
1537					}
1538					
1539					type = self.mimes[ext[ii]];
1540					if (!type) {
1541						if (addMissingExtensions && /^\w+$/.test(ext[ii])) {
1542							mimes.push('.' + ext[ii]);
1543						} else {
1544							return []; // accept all
1545						}
1546					} else if (Basic.inArray(type, mimes) === -1) {
1547						mimes.push(type);
1548					}
1549				}
1550			}
1551			return mimes;
1552		},
1553
1554
1555		mimes2exts: function(mimes) {
1556			var self = this, exts = [];
1557			
1558			Basic.each(mimes, function(mime) {
1559				if (mime === '*') {
1560					exts = [];
1561					return false;
1562				}
1563
1564				// check if this thing looks like mime type
1565				var m = mime.match(/^(\w+)\/(\*|\w+)$/);
1566				if (m) {
1567					if (m[2] === '*') { 
1568						// wildcard mime type detected
1569						Basic.each(self.extensions, function(arr, mime) {
1570							if ((new RegExp('^' + m[1] + '/')).test(mime)) {
1571								[].push.apply(exts, self.extensions[mime]);
1572							}
1573						});
1574					} else if (self.extensions[mime]) {
1575						[].push.apply(exts, self.extensions[mime]);
1576					}
1577				}
1578			});
1579			return exts;
1580		},
1581
1582
1583		mimes2extList: function(mimes) {
1584			var accept = [], exts = [];
1585
1586			if (Basic.typeOf(mimes) === 'string') {
1587				mimes = Basic.trim(mimes).split(/\s*,\s*/);
1588			}
1589
1590			exts = this.mimes2exts(mimes);
1591			
1592			accept.push({
1593				title: I18n.translate('Files'),
1594				extensions: exts.length ? exts.join(',') : '*'
1595			});
1596			
1597			// save original mimes string
1598			accept.mimes = mimes;
1599
1600			return accept;
1601		},
1602
1603
1604		getFileExtension: function(fileName) {
1605			var matches = fileName && fileName.match(/\.([^.]+)$/);
1606			if (matches) {
1607				return matches[1].toLowerCase();
1608			}
1609			return '';
1610		},
1611
1612		getFileMime: function(fileName) {
1613			return this.mimes[this.getFileExtension(fileName)] || '';
1614		}
1615	};
1616
1617	Mime.addMimeType(mimeData);
1618
1619	return Mime;
1620});
1621
1622// Included from: src/javascript/core/utils/Env.js
1623
1624/**
1625 * Env.js
1626 *
1627 * Copyright 2013, Moxiecode Systems AB
1628 * Released under GPL License.
1629 *
1630 * License: http://www.plupload.com/license
1631 * Contributing: http://www.plupload.com/contributing
1632 */
1633
1634define("moxie/core/utils/Env", [
1635	"moxie/core/utils/Basic"
1636], function(Basic) {
1637	
1638	// UAParser.js v0.6.2
1639	// Lightweight JavaScript-based User-Agent string parser
1640	// https://github.com/faisalman/ua-parser-js
1641	//
1642	// Copyright © 2012-2013 Faisalman <[email protected]>
1643	// Dual licensed under GPLv2 & MIT
1644
1645	var UAParser = (function (undefined) {
1646
1647	    //////////////
1648	    // Constants
1649	    /////////////
1650
1651
1652	    var EMPTY       = '',
1653	        UNKNOWN     = '?',
1654	        FUNC_TYPE   = 'function',
1655	        UNDEF_TYPE  = 'undefined',
1656	        OBJ_TYPE    = 'object',
1657	        MAJOR       = 'major',
1658	        MODEL       = 'model',
1659	        NAME        = 'name',
1660	        TYPE        = 'type',
1661	        VENDOR      = 'vendor',
1662	        VERSION     = 'version',
1663	        ARCHITECTURE= 'architecture',
1664	        CONSOLE     = 'console',
1665	        MOBILE      = 'mobile',
1666	        TABLET      = 'tablet';
1667
1668
1669	    ///////////
1670	    // Helper
1671	    //////////
1672
1673
1674	    var util = {
1675	        has : function (str1, str2) {
1676	            return str2.toLowerCase().indexOf(str1.toLowerCase()) !== -1;
1677	        },
1678	        lowerize : function (str) {
1679	            return str.toLowerCase();
1680	        }
1681	    };
1682
1683
1684	    ///////////////
1685	    // Map helper
1686	    //////////////
1687
1688
1689	    var mapper = {
1690
1691	        rgx : function () {
1692
1693	            // loop through all regexes maps
1694	            for (var result, i = 0, j, k, p, q, matches, match, args = arguments; i < args.length; i += 2) {
1695
1696	                var regex = args[i],       // even sequence (0,2,4,..)
1697	                    props = args[i + 1];   // odd sequence (1,3,5,..)
1698
1699	                // construct object barebones
1700	                if (typeof(result) === UNDEF_TYPE) {
1701	                    result = {};
1702	                    for (p in props) {
1703	                        q = props[p];
1704	                        if (typeof(q) === OBJ_TYPE) {
1705	                            result[q[0]] = undefined;
1706	                        } else {
1707	                            result[q] = undefined;
1708	                        }
1709	                    }
1710	                }
1711
1712	                // try matching uastring with regexes
1713	                for (j = k = 0; j < regex.length; j++) {
1714	                    matches = regex[j].exec(this.getUA());
1715	                    if (!!matches) {
1716	                        for (p = 0; p < props.length; p++) {
1717	                            match = matches[++k];
1718	                            q = props[p];
1719	                            // check if given property is actually array
1720	                            if (typeof(q) === OBJ_TYPE && q.length > 0) {
1721	                                if (q.length == 2) {
1722	                                    if (typeof(q[1]) == FUNC_TYPE) {
1723	                                        // assign modified match
1724	                                        result[q[0]] = q[1].call(this, match);
1725	                                    } else {
1726	                                        // assign given value, ignore regex match
1727	                                        result[q[0]] = q[1];
1728	                                    }
1729	                                } else if (q.length == 3) {
1730	                                    // check whether function or regex
1731	                                    if (typeof(q[1]) === FUNC_TYPE && !(q[1].exec && q[1].test)) {
1732	                                        // call function (usually string mapper)
1733	                                        result[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;
1734	                                    } else {
1735	                                        // sanitize match using given regex
1736	                                        result[q[0]] = match ? match.replace(q[1], q[2]) : undefined;
1737	                                    }
1738	                                } else if (q.length == 4) {
1739	                                        result[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined;
1740	                                }
1741	                            } else {
1742	                                result[q] = match ? match : undefined;
1743	                            }
1744	                        }
1745	                        break;
1746	                    }
1747	                }
1748
1749	                if(!!matches) break; // break the loop immediately if match found
1750	            }
1751	            return result;
1752	        },
1753
1754	        str : function (str, map) {
1755
1756	            for (var i in map) {
1757	                // check if array
1758	                if (typeof(map[i]) === OBJ_TYPE && map[i].length > 0) {
1759	                    for (var j = 0; j < map[i].length; j++) {
1760	                        if (util.has(map[i][j], str)) {
1761	                            return (i === UNKNOWN) ? undefined : i;
1762	                        }
1763	                    }
1764	                } else if (util.has(map[i], str)) {
1765	                    return (i === UNKNOWN) ? undefined : i;
1766	                }
1767	            }
1768	            return str;
1769	        }
1770	    };
1771
1772
1773	    ///////////////
1774	    // String map
1775	    //////////////
1776
1777
1778	    var maps = {
1779
1780	        browser : {
1781	            oldsafari : {
1782	                major : {
1783	                    '1' : ['/8', '/1', '/3'],
1784	                    '2' : '/4',
1785	                    '?' : '/'
1786	                },
1787	                version : {
1788	                    '1.0'   : '/8',
1789	                    '1.2'   : '/1',
1790	                    '1.3'   : '/3',
1791	                    '2.0'   : '/412',
1792	                    '2.0.2' : '/416',
1793	                    '2.0.3' : '/417',
1794	                    '2.0.4' : '/419',
1795	                    '?'     : '/'
1796	                }
1797	            }
1798	        },
1799
1800	        device : {
1801	            sprint : {
1802	                model : {
1803	                    'Evo Shift 4G' : '7373KT'
1804	                },
1805	                vendor : {
1806	                    'HTC'       : 'APA',
1807	                    'Sprint'    : 'Sprint'
1808	                }
1809	            }
1810	        },
1811
1812	        os : {
1813	            windows : {
1814	                version : {
1815	                    'ME'        : '4.90',
1816	                    'NT 3.11'   : 'NT3.51',
1817	                    'NT 4.0'    : 'NT4.0',
1818	                    '2000'      : 'NT 5.0',
1819	                    'XP'        : ['NT 5.1', 'NT 5.2'],
1820	                    'Vista'     : 'NT 6.0',
1821	                    '7'         : 'NT 6.1',
1822	                    '8'         : 'NT 6.2',
1823	                    '8.1'       : 'NT 6.3',
1824	                    'RT'        : 'ARM'
1825	                }
1826	            }
1827	        }
1828	    };
1829
1830
1831	    //////////////
1832	    // Regex map
1833	    /////////////
1834
1835
1836	    var regexes = {
1837
1838	        browser : [[
1839
1840	            // Presto based
1841	            /(opera\smini)\/((\d+)?[\w\.-]+)/i,                                 // Opera Mini
1842	            /(opera\s[mobiletab]+).+version\/((\d+)?[\w\.-]+)/i,                // Opera Mobi/Tablet
1843	            /(opera).+version\/((\d+)?[\w\.]+)/i,                               // Opera > 9.80
1844	            /(opera)[\/\s]+((\d+)?[\w\.]+)/i                                    // Opera < 9.80
1845	            
1846	            ], [NAME, VERSION, MAJOR], [
1847
1848	            /\s(opr)\/((\d+)?[\w\.]+)/i                                         // Opera Webkit
1849	            ], [[NAME, 'Opera'], VERSION, MAJOR], [
1850
1851	            // Mixed
1852	            /(kindle)\/((\d+)?[\w\.]+)/i,                                       // Kindle
1853	            /(lunascape|maxthon|netfront|jasmine|blazer)[\/\s]?((\d+)?[\w\.]+)*/i,
1854	                                                                                // Lunascape/Maxthon/Netfront/Jasmine/Blazer
1855
1856	            // Trident based
1857	            /(avant\s|iemobile|slim|baidu)(?:browser)?[\/\s]?((\d+)?[\w\.]*)/i,
1858	                                                                                // Avant/IEMobile/SlimBrowser/Baidu
1859	            /(?:ms|\()(ie)\s((\d+)?[\w\.]+)/i,                                  // Internet Explorer
1860
1861	            // Webkit/KHTML based
1862	            /(rekonq)((?:\/)[\w\.]+)*/i,                                        // Rekonq
1863	            /(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron)\/((\d+)?[\w\.-]+)/i
1864	                                                                                // Chromium/Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron
1865	            ], [NAME, VERSION, MAJOR], [
1866
1867	            /(trident).+rv[:\s]((\d+)?[\w\.]+).+like\sgecko/i                   // IE11
1868	            ], [[NAME, 'IE'], VERSION, MAJOR], [
1869
1870	            /(yabrowser)\/((\d+)?[\w\.]+)/i                                     // Yandex
1871	            ], [[NAME, 'Yandex'], VERSION, MAJOR], [
1872
1873	            /(comodo_dragon)\/((\d+)?[\w\.]+)/i                                 // Comodo Dragon
1874	            ], [[NAME, /_/g, ' '], VERSION, MAJOR], [
1875
1876	            /(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?((\d+)?[\w\.]+)/i
1877	                                                                                // Chrome/OmniWeb/Arora/Tizen/Nokia
1878	            ], [NAME, VERSION, MAJOR], [
1879
1880	            /(dolfin)\/((\d+)?[\w\.]+)/i                                        // Dolphin
1881	            ], [[NAME, 'Dolphin'], VERSION, MAJOR], [
1882
1883	            /((?:android.+)crmo|crios)\/((\d+)?[\w\.]+)/i                       // Chrome for Android/iOS
1884	            ], [[NAME, 'Chrome'], VERSION, MAJOR], [
1885
1886	            /((?:android.+))version\/((\d+)?[\w\.]+)\smobile\ssafari/i          // Android Browser
1887	            ], [[NAME, 'Android Browser'], VERSION, MAJOR], [
1888
1889	            /version\/((\d+)?[\w\.]+).+?mobile\/\w+\s(safari)/i                 // Mobile Safari
1890	            ], [VERSION, MAJOR, [NAME, 'Mobile Safari']], [
1891
1892	            /version\/((\d+)?[\w\.]+).+?(mobile\s?safari|safari)/i              // Safari & Safari Mobile
1893	            ], [VERSION, MAJOR, NAME], [
1894
1895	            /webkit.+?(mobile\s?safari|safari)((\/[\w\.]+))/i                   // Safari < 3.0
1896	            ], [NAME, [MAJOR, mapper.str, maps.browser.oldsafari.major], [VERSION, mapper.str, maps.browser.oldsafari.version]], [
1897
1898	            /(konqueror)\/((\d+)?[\w\.]+)/i,                                    // Konqueror
1899	            /(webkit|khtml)\/((\d+)?[\w\.]+)/i
1900	            ], [NAME, VERSION, MAJOR], [
1901
1902	            // Gecko based
1903	            /(navigator|netscape)\/((\d+)?[\w\.-]+)/i                           // Netscape
1904	            ], [[NAME, 'Netscape'], VERSION, MAJOR], [
1905	            /(swiftfox)/i,                                                      // Swiftfox
1906	            /(icedragon|iceweasel|camino|chimera|fennec|maemo\sbrowser|minimo|conkeror)[\/\s]?((\d+)?[\w\.\+]+)/i,
1907	                                                                                // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror
1908	            /(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix)\/((\d+)?[\w\.-]+)/i,
1909	                                                                                // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
1910	            /(mozilla)\/((\d+)?[\w\.]+).+rv\:.+gecko\/\d+/i,                    // Mozilla
1911
1912	            // Other
1913	            /(uc\s?browser|polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|qqbrowser)[\/\s]?((\d+)?[\w\.]+)/i,
1914	                                                                                // UCBrowser/Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/QQBrowser
1915	            /(links)\s\(((\d+)?[\w\.]+)/i,                                      // Links
1916	            /(gobrowser)\/?((\d+)?[\w\.]+)*/i,                                  // GoBrowser
1917	            /(ice\s?browser)\/v?((\d+)?[\w\._]+)/i,                             // ICE Browser
1918	            /(mosaic)[\/\s]((\d+)?[\w\.]+)/i                                    // Mosaic
1919	            ], [NAME, VERSION, MAJOR]
1920	        ],
1921
1922	        engine : [[
1923
1924	            /(presto)\/([\w\.]+)/i,                                             // Presto
1925	            /(webkit|trident|netfront|netsurf|amaya|lynx|w3m)\/([\w\.]+)/i,     // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m
1926	            /(khtml|tasman|links)[\/\s]\(?([\w\.]+)/i,                          // KHTML/Tasman/Links
1927	            /(icab)[\/\s]([23]\.[\d\.]+)/i                                      // iCab
1928	            ], [NAME, VERSION], [
1929
1930	            /rv\:([\w\.]+).*(gecko)/i                                           // Gecko
1931	            ], [VERSION, NAME]
1932	        ],
1933
1934	        os : [[
1935
1936	            // Windows based
1937	            /(windows)\snt\s6\.2;\s(arm)/i,                                     // Windows RT
1938	            /(windows\sphone(?:\sos)*|windows\smobile|windows)[\s\/]?([ntce\d\.\s]+\w)/i
1939	            ], [NAME, [VERSION, mapper.str, maps.os.windows.version]], [
1940	            /(win(?=3|9|n)|win\s9x\s)([nt\d\.]+)/i
1941	            ], [[NAME, 'Windows'], [VERSION, mapper.str, maps.os.windows.version]], [
1942
1943	            // Mobile/Embedded OS
1944	            /\((bb)(10);/i                                                      // BlackBerry 10
1945	            ], [[NAME, 'BlackBerry'], VERSION], [
1946	            /(blackberry)\w*\/?([\w\.]+)*/i,                                    // Blackberry
1947	            /(tizen)\/([\w\.]+)/i,                                              // Tizen
1948	            /(android|webos|palm\os|qnx|bada|rim\stablet\sos|meego)[\/\s-]?([\w\.]+)*/i
1949	                                                                                // Android/WebOS/Palm/QNX/Bada/RIM/MeeGo
1950	            ], [NAME, VERSION], [
1951	            /(symbian\s?os|symbos|s60(?=;))[\/\s-]?([\w\.]+)*/i                 // Symbian
1952	            ], [[NAME, 'Symbian'], VERSION],[
1953	            /mozilla.+\(mobile;.+gecko.+firefox/i                               // Firefox OS
1954	            ], [[NAME, 'Firefox OS'], VERSION], [
1955
1956	            // Console
1957	            /(nintendo|playstation)\s([wids3portablevu]+)/i,                    // Nintendo
1957/Playstation
1958
1959	            // GNU/Linux based
1960	            /(mint)[\/\s\(]?(\w+)*/i,                                           // Mint
1961	            /(joli|[kxln]?ubuntu|debian|[open]*suse|gentoo|arch|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk)[\/\s-]?([\w\.-]+)*/i,
1962	                                                                                // Joli/Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware
1963	                                                                                // Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk
1964	            /(hurd|linux)\s?([\w\.]+)*/i,                                       // Hurd/Linux
1965	            /(gnu)\s?([\w\.]+)*/i                                               // GNU
1966	            ], [NAME, VERSION], [
1967
1968	            /(cros)\s[\w]+\s([\w\.]+\w)/i                                       // Chromium OS
1969	            ], [[NAME, 'Chromium OS'], VERSION],[
1970
1971	            // Solaris
1972	            /(sunos)\s?([\w\.]+\d)*/i                                           // Solaris
1973	            ], [[NAME, 'Solaris'], VERSION], [
1974
1975	            // BSD based
1976	            /\s([frentopc-]{0,4}bsd|dragonfly)\s?([\w\.]+)*/i                   // FreeBSD/NetBSD/OpenBSD/PC-BSD/DragonFly
1977	            ], [NAME, VERSION],[
1978
1979	            /(ip[honead]+)(?:.*os\s*([\w]+)*\slike\smac|;\sopera)/i             // iOS
1980	            ], [[NAME, 'iOS'], [VERSION, /_/g, '.']], [
1981
1982	            /(mac\sos\sx)\s?([\w\s\.]+\w)*/i                                    // Mac OS
1983	            ], [NAME, [VERSION, /_/g, '.']], [
1984
1985	            // Other
1986	            /(haiku)\s(\w+)/i,                                                  // Haiku
1987	            /(aix)\s((\d)(?=\.|\)|\s)[\w\.]*)*/i,                               // AIX
1988	            /(macintosh|mac(?=_powerpc)|plan\s9|minix|beos|os\/2|amigaos|morphos|risc\sos)/i,
1989	                                                                                // Plan9/Minix/BeOS/OS2/AmigaOS/MorphOS/RISCOS
1990	            /(unix)\s?([\w\.]+)*/i                                              // UNIX
1991	            ], [NAME, VERSION]
1992	        ]
1993	    };
1994
1995
1996	    /////////////////
1997	    // Constructor
1998	    ////////////////
1999
2000
2001	    var UAParser = function (uastring) {
2002
2003	        var ua = uastring || ((window && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY);
2004
2005	        this.getBrowser = function () {
2006	            return mapper.rgx.apply(this, regexes.browser);
2007	        };
2008	        this.getEngine = function () {
2009	            return mapper.rgx.apply(this, regexes.engine);
2010	        };
2011	        this.getOS = function () {
2012	            return mapper.rgx.apply(this, regexes.os);
2013	        };
2014	        this.getResult = function() {
2015	            return {
2016	                ua      : this.getUA(),
2017	                browser : this.getBrowser(),
2018	                engine  : this.getEngine(),
2019	                os      : this.getOS()
2020	            };
2021	        };
2022	        this.getUA = function () {
2023	            return ua;
2024	        };
2025	        this.setUA = function (uastring) {
2026	            ua = uastring;
2027	            return this;
2028	        };
2029	        this.setUA(ua);
2030	    };
2031
2032	    return new UAParser().getResult();
2033	})();
2034
2035
2036	function version_compare(v1, v2, operator) {
2037	  // From: http://phpjs.org/functions
2038	  // +      original by: Philippe Jausions (http://pear.php.net/user/jausions)
2039	  // +      original by: Aidan Lister (http://aidanlister.com/)
2040	  // + reimplemented by: Kankrelune (http://www.webfaktory.info/)
2041	  // +      improved by: Brett Zamir (http://brett-zamir.me)
2042	  // +      improved by: Scott Baker
2043	  // +      improved by: Theriault
2044	  // *        example 1: version_compare('8.2.5rc', '8.2.5a');
2045	  // *        returns 1: 1
2046	  // *        example 2: version_compare('8.2.50', '8.2.52', '<');
2047	  // *        returns 2: true
2048	  // *        example 3: version_compare('5.3.0-dev', '5.3.0');
2049	  // *        returns 3: -1
2050	  // *        example 4: version_compare('4.1.0.52','4.01.0.51');
2051	  // *        returns 4: 1
2052
2053	  // Important: compare must be initialized at 0.
2054	  var i = 0,
2055	    x = 0,
2056	    compare = 0,
2057	    // vm maps textual PHP versions to negatives so they're less than 0.
2058	    // PHP currently defines these as CASE-SENSITIVE. It is important to
2059	    // leave these as negatives so that they can come before numerical versions
2060	    // and as if no letters were there to begin with.
2061	    // (1alpha is < 1 and < 1.1 but > 1dev1)
2062	    // If a non-numerical value can't be mapped to this table, it receives
2063	    // -7 as its value.
2064	    vm = {
2065	      'dev': -6,
2066	      'alpha': -5,
2067	      'a': -5,
2068	      'beta': -4,
2069	      'b': -4,
2070	      'RC': -3,
2071	      'rc': -3,
2072	      '#': -2,
2073	      'p': 1,
2074	      'pl': 1
2075	    },
2076	    // This function will be called to prepare each version argument.
2077	    // It replaces every _, -, and + with a dot.
2078	    // It surrounds any nonsequence of numbers/dots with dots.
2079	    // It replaces sequences of dots with a single dot.
2080	    //    version_compare('4..0', '4.0') == 0
2081	    // Important: A string of 0 length needs to be converted into a value
2082	    // even less than an unexisting value in vm (-7), hence [-8].
2083	    // It's also important to not strip spaces because of this.
2084	    //   version_compare('', ' ') == 1
2085	    prepVersion = function (v) {
2086	      v = ('' + v).replace(/[_\-+]/g, '.');
2087	      v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.');
2088	      return (!v.length ? [-8] : v.split('.'));
2089	    },
2090	    // This converts a version component to a number.
2091	    // Empty component becomes 0.
2092	    // Non-numerical component becomes a negative number.
2093	    // Numerical component becomes itself as an integer.
2094	    numVersion = function (v) {
2095	      return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10));
2096	    };
2097
2098	  v1 = prepVersion(v1);
2099	  v2 = prepVersion(v2);
2100	  x = Math.max(v1.length, v2.length);
2101	  for (i = 0; i < x; i++) {
2102	    if (v1[i] == v2[i]) {
2103	      continue;
2104	    }
2105	    v1[i] = numVersion(v1[i]);
2106	    v2[i] = numVersion(v2[i]);
2107	    if (v1[i] < v2[i]) {
2108	      compare = -1;
2109	      break;
2110	    } else if (v1[i] > v2[i]) {
2111	      compare = 1;
2112	      break;
2113	    }
2114	  }
2115	  if (!operator) {
2116	    return compare;
2117	  }
2118
2119	  // Important: operator is CASE-SENSITIVE.
2120	  // "No operator" seems to be treated as "<."
2121	  // Any other values seem to make the function return null.
2122	  switch (operator) {
2123	  case '>':
2124	  case 'gt':
2125	    return (compare > 0);
2126	  case '>=':
2127	  case 'ge':
2128	    return (compare >= 0);
2129	  case '<=':
2130	  case 'le':
2131	    return (compare <= 0);
2132	  case '==':
2133	  case '=':
2134	  case 'eq':
2135	    return (compare === 0);
2136	  case '<>':
2137	  case '!=':
2138	  case 'ne':
2139	    return (compare !== 0);
2140	  case '':
2141	  case '<':
2142	  case 'lt':
2143	    return (compare < 0);
2144	  default:
2145	    return null;
2146	  }
2147	}
2148
2149
2150	var can = (function() {
2151		var caps = {
2152				define_property: (function() {
2153					/* // currently too much extra code required, not exactly worth it
2154					try { // as of IE8, getters/setters are supported only on DOM elements
2155						var obj = {};
2156						if (Object.defineProperty) {
2157							Object.defineProperty(obj, 'prop', {
2158								enumerable: true,
2159								configurable: true
2160							});
2161							return true;
2162						}
2163					} catch(ex) {}
2164
2165					if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) {
2166						return true;
2167					}*/
2168					return false;
2169				}()),
2170
2171				create_canvas: (function() {
2172					// On the S60 and BB Storm, getContext exists, but always returns undefined
2173					// so we actually have to call getContext() to verify
2174					// github.com/Modernizr/Modernizr/issues/issue/97/
2175					var el = document.createElement('canvas');
2176					return !!(el.getContext && el.getContext('2d'));
2177				}()),
2178
2179				return_response_type: function(responseType) {
2180					try {
2181						if (Basic.inArray(responseType, ['', 'text', 'document']) !== -1) {
2182							return true;
2183						} else if (window.XMLHttpRequest) {
2184							var xhr = new XMLHttpRequest();
2185							xhr.open('get', '/'); // otherwise Gecko throws an exception
2186							if ('responseType' in xhr) {
2187								xhr.responseType = responseType;
2188								// as of 23.0.1271.64, Chrome switched from throwing exception to merely logging it to the console (why? o why?)
2189								if (xhr.responseType !== responseType) {
2190									return false;
2191								}
2192								return true;
2193							}
2194						}
2195					} catch (ex) {}
2196					return false;
2197				},
2198
2199				// ideas for this heavily come from Modernizr (http://modernizr.com/)
2200				use_data_uri: (function() {
2201					var du = new Image();
2202
2203					du.onload = function() {
2204						caps.use_data_uri = (du.width === 1 && du.height === 1);
2205					};
2206					
2207					setTimeout(function() {
2208						du.src = "data:image/gif;base64,R0lGODlhAQABAIAAAP8AAAAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==";
2209					}, 1);
2210					return false;
2211				}()),
2212
2213				use_data_uri_over32kb: function() { // IE8
2214					return caps.use_data_uri && (Env.browser !== 'IE' || Env.version >= 9);
2215				},
2216
2217				use_data_uri_of: function(bytes) {
2218					return (caps.use_data_uri && bytes < 33000 || caps.use_data_uri_over32kb());
2219				},
2220
2221				use_fileinput: function() {
2222					var el = document.createElement('input');
2223					el.setAttribute('type', 'file');
2224					return !el.disabled;
2225				}
2226			};
2227
2228		return function(cap) {
2229			var args = [].slice.call(arguments);
2230			args.shift(); // shift of cap
2231			return Basic.typeOf(caps[cap]) === 'function' ? caps[cap].apply(this, args) : !!caps[cap];
2232		};
2233	}());
2234
2235
2236	var Env = {
2237		can: can,
2238		
2239		browser: UAParser.browser.name,
2240		version: parseFloat(UAParser.browser.major),
2241		os: UAParser.os.name, // everybody intuitively types it in a lowercase for some reason
2242		osVersion: UAParser.os.version,
2243
2244		verComp: version_compare,
2245		
2246		swf_url: "../flash/Moxie.swf",
2247		xap_url: "../silverlight/Moxie.xap",
2248		global_event_dispatcher: "moxie.core.EventTarget.instance.dispatchEvent"
2249	};
2250
2251	// for backward compatibility
2252	// @deprecated Use `Env.os` instead
2253	Env.OS = Env.os;
2254
2255	return Env;
2256});
2257
2258// Included from: src/javascript/core/utils/Dom.js
2259
2260/**
2261 * Dom.js
2262 *
2263 * Copyright 2013, Moxiecode Systems AB
2264 * Released under GPL License.
2265 *
2266 * License: http://www.plupload.com/license
2267 * Contributing: http://www.plupload.com/contributing
2268 */
2269
2270define('moxie/core/utils/Dom', ['moxie/core/utils/Env'], function(Env) {
2271
2272	/**
2273	Get DOM Element by it's id.
2274
2275	@method get
2276	@for Utils
2277	@param {String} id Identifier of the DOM Element
2278	@return {DOMElement}
2279	*/
2280	var get = function(id) {
2281		if (typeof id !== 'string') {
2282			return id;
2283		}
2284		return document.getElementById(id);
2285	};
2286
2287	/**
2288	Checks if specified DOM element has specified class.
2289
2290	@method hasClass
2291	@static
2292	@param {Object} obj DOM element like object to add handler to.
2293	@param {String} name Class name
2294	*/
2295	var hasClass = function(obj, name) {
2296		if (!obj.className) {
2297			return false;
2298		}
2299
2300		var regExp = new RegExp("(^|\\s+)"+name+"(\\s+|$)");
2301		return regExp.test(obj.className);
2302	};
2303
2304	/**
2305	Adds specified className to specified DOM element.
2306
2307	@method addClass
2308	@static
2309	@param {Object} obj DOM element like object to add handler to.
2310	@param {String} name Class name
2311	*/
2312	var addClass = function(obj, name) {
2313		if (!hasClass(obj, name)) {
2314			obj.className = !obj.className ? name : obj.className.replace(/\s+$/, '') + ' ' + name;
2315		}
2316	};
2317
2318	/**
2319	Removes specified className from specified DOM element.
2320
2321	@method removeClass
2322	@static
2323	@param {Object} obj DOM element like object to add handler to.
2324	@param {String} name Class name
2325	*/
2326	var removeClass = function(obj, name) {
2327		if (obj.className) {
2328			var regExp = new RegExp("(^|\\s+)"+name+"(\\s+|$)");
2329			obj.className = obj.className.replace(regExp, function($0, $1, $2) {
2330				return $1 === ' ' && $2 === ' ' ? ' ' : '';
2331			});
2332		}
2333	};
2334
2335	/**
2336	Returns a given computed style of a DOM element.
2337
2338	@method getStyle
2339	@static
2340	@param {Object} obj DOM element like object.
2341	@param {String} name Style you want to get from the DOM element
2342	*/
2343	var getStyle = function(obj, name) {
2344		if (obj.currentStyle) {
2345			return obj.currentStyle[name];
2346		} else if (window.getComputedStyle) {
2347			return window.getComputedStyle(obj, null)[name];
2348		}
2349	};
2350
2351
2352	/**
2353	Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields.
2354
2355	@method getPos
2356	@static
2357	@param {Element} node HTML element or element id to get x, y position from.
2358	@param {Element} root Optional root element to stop calculations at.
2359	@return {object} Absolute position of the specified element object with x, y fields.
2360	*/
2361	var getPos = function(node, root) {
2362		var x = 0, y = 0, parent, doc = document, nodeRect, rootRect;
2363
2364		node = node;
2365		root = root || doc.body;
2366
2367		// Returns the x, y cordinate for an element on IE 6 and IE 7
2368		function getIEPos(node) {
2369			var bodyElm, rect, x = 0, y = 0;
2370
2371			if (node) {
2372				rect = node.getBoundingClientRect();
2373				bodyElm = doc.compatMode === "CSS1Compat" ? doc.documentElement : doc.body;
2374				x = rect.left + bodyElm.scrollLeft;
2375				y = rect.top + bodyElm.scrollTop;
2376			}
2377
2378			return {
2379				x : x,
2380				y : y
2381			};
2382		}
2383
2384		// Use getBoundingClientRect on IE 6 and IE 7 but not on IE 8 in standards mode
2385		if (node && node.getBoundingClientRect && Env.browser === 'IE' && (!doc.documentMode || doc.documentMode < 8)) {
2386			nodeRect = getIEPos(node);
2387			rootRect = getIEPos(root);
2388
2389			return {
2390				x : nodeRect.x - rootRect.x,
2391				y : nodeRect.y - rootRect.y
2392			};
2393		}
2394
2395		parent = node;
2396		while (parent && parent != root && parent.nodeType) {
2397			x += parent.offsetLeft || 0;
2398			y += parent.offsetTop || 0;
2399			parent = parent.offsetParent;
2400		}
2401
2402		parent = node.parentNode;
2403		while (parent && parent != root && parent.nodeType) {
2404			x -= parent.scrollLeft || 0;
2405			y -= parent.scrollTop || 0;
2406			parent = parent.parentNode;
2407		}
2408
2409		return {
2410			x : x,
2411			y : y
2412		};
2413	};
2414
2415	/**
2416	Returns the size of the specified node in pixels.
2417
2418	@method getSize
2419	@static
2420	@param {Node} node Node to get the size of.
2421	@return {Object} Object with a w and h property.
2422	*/
2423	var getSize = function(node) {
2424		return {
2425			w : node.offsetWidth || node.clientWidth,
2426			h : node.offsetHeight || node.clientHeight
2427		};
2428	};
2429
2430	return {
2431		get: get,
2432		hasClass: hasClass,
2433		addClass: addClass,
2434		removeClass: removeClass,
2435		getStyle: getStyle,
2436		getPos: getPos,
2437		getSize: getSize
2438	};
2439});
2440
2441// Included from: src/javascript/core/Exceptions.js
2442
2443/**
2444 * Exceptions.js
2445 *
2446 * Copyright 2013, Moxiecode Systems AB
2447 * Released under GPL License.
2448 *
2449 * License: http://www.plupload.com/license
2450 * Contributing: http://www.plupload.com/contributing
2451 */
2452
2453define('moxie/core/Exceptions', [
2454	'moxie/core/utils/Basic'
2455], function(Basic) {
2456	function _findKey(obj, value) {
2457		var key;
2458		for (key in obj) {
2459			if (obj[key] === value) {
2460				return key;
2461			}
2462		}
2463		return null;
2464	}
2465
2466	return {
2467		RuntimeError: (function() {
2468			var namecodes = {
2469				NOT_INIT_ERR: 1,
2470				NOT_SUPPORTED_ERR: 9,
2471				JS_ERR: 4
2472			};
2473
2474			function RuntimeError(code) {
2475				this.code = code;
2476				this.name = _findKey(namecodes, code);
2477				this.message = this.name + ": RuntimeError " + this.code;
2478			}
2479			
2480			Basic.extend(RuntimeError, namecodes);
2481			RuntimeError.prototype = Error.prototype;
2482			return RuntimeError;
2483		}()),
2484		
2485		OperationNotAllowedException: (function() {
2486			
2487			function OperationNotAllowedException(code) {
2488				this.code = code;
2489				this.name = 'OperationNotAllowedException';
2490			}
2491			
2492			Basic.extend(OperationNotAllowedException, {
2493				NOT_ALLOWED_ERR: 1
2494			});
2495			
2496			OperationNotAllowedException.prototype = Error.prototype;
2497			
2498			return OperationNotAllowedException;
2499		}()),
2500
2501		ImageError: (function() {
2502			var namecodes = {
2503				WRONG_FORMAT: 1,
2504				MAX_RESOLUTION_ERR: 2
2505			};
2506
2507			function ImageError(code) {
2508				this.code = code;
2509				this.name = _findKey(namecodes, code);
2510				this.message = this.name + ": ImageError " + this.code;
2511			}
2512			
2513			Basic.extend(ImageError, namecodes);
2514			ImageError.prototype = Error.prototype;
2515
2516			return ImageError;
2517		}()),
2518
2519		FileException: (function() {
2520			var namecodes = {
2521				NOT_FOUND_ERR: 1,
2522				SECURITY_ERR: 2,
2523				ABORT_ERR: 3,
2524				NOT_READABLE_ERR: 4,
2525				ENCODING_ERR: 5,
2526				NO_MODIFICATION_ALLOWED_ERR: 6,
2527				INVALID_STATE_ERR: 7,
2528				SYNTAX_ERR: 8
2529			};
2530
2531			function FileException(code) {
2532				this.code = code;
2533				this.name = _findKey(namecodes, code);
2534				this.message = this.name + ": FileException " + this.code;
2535			}
2536			
2537			Basic.extend(FileException, namecodes);
2538			FileException.prototype = Error.prototype;
2539			return FileException;
2540		}()),
2541		
2542		DOMException: (function() {
2543			var namecodes = {
2544				INDEX_SIZE_ERR: 1,
2545				DOMSTRING_SIZE_ERR: 2,
2546				HIERARCHY_REQUEST_ERR: 3,
2547				WRONG_DOCUMENT_ERR: 4,
2548				INVALID_CHARACTER_ERR: 5,
2549				NO_DATA_ALLOWED_ERR: 6,
2550				NO_MODIFICATION_ALLOWED_ERR: 7,
2551				NOT_FOUND_ERR: 8,
2552				NOT_SUPPORTED_ERR: 9,
2553				INUSE_ATTRIBUTE_ERR: 10,
2554				INVALID_STATE_ERR: 11,
2555				SYNTAX_ERR: 12,
2556				INVALID_MODIFICATION_ERR: 13,
2557				NAMESPACE_ERR: 14,
2558				INVALID_ACCESS_ERR: 15,
2559				VALIDATION_ERR: 16,
2560				TYPE_MISMATCH_ERR: 17,
2561				SECURITY_ERR: 18,
2562				NETWORK_ERR: 19,
2563				ABORT_ERR: 20,
2564				URL_MISMATCH_ERR: 21,
2565				QUOTA_EXCEEDED_ERR: 22,
2566				TIMEOUT_ERR: 23,
2567				INVALID_NODE_TYPE_ERR: 24,
2568				DATA_CLONE_ERR: 25
2569			};
2570
2571			function DOMException(code) {
2572				this.code = code;
2573				this.name = _findKey(namecodes, code);
2574				this.message = this.name + ": DOMException " + this.code;
2575			}
2576			
2577			Basic.extend(DOMException, namecodes);
2578			DOMException.prototype = Error.prototype;
2579			return DOMException;
2580		}()),
2581		
2582		EventException: (function() {
2583			function EventException(code) {
2584				this.code = code;
2585				this.name = 'EventException';
2586			}
2587			
2588			Basic.extend(EventException, {
2589				UNSPECIFIED_EVENT_TYPE_ERR: 0
2590			});
2591			
2592			EventException.prototype = Error.prototype;
2593			
2594			return EventException;
2595		}())
2596	};
2597});
2598
2599// Included from: src/javascript/core/EventTarget.js
2600
2601/**
2602 * EventTarget.js
2603 *
2604 * Copyright 2013, Moxiecode Systems AB
2605 * Released under GPL License.
2606 *
2607 * License: http://www.plupload.com/license
2608 * Contributing: http://www.plupload.com/contributing
2609 */
2610
2611define('moxie/core/EventTarget', [
2612	'moxie/core/Exceptions',
2613	'moxie/core/utils/Basic'
2614], function(x, Basic) {
2615	/**
2616	Parent object for all event dispatching components and objects
2617
2618	@class EventTarget
2619	@constructor EventTarget
2620	*/
2621	function EventTarget() {
2622		// hash of event listeners by object uid
2623		var eventpool = {};
2624				
2625		Basic.extend(this, {
2626			
2627			/**
2628			Unique id of the event dispatcher, usually overriden by children
2629
2630			@property uid
2631			@type String
2632			*/
2633			uid: null,
2634			
2635			/**
2636			Can be called from within a child  in order to acquire uniqie id in automated manner
2637
2638			@method init
2639			*/
2640			init: function() {
2641				if (!this.uid) {
2642					this.uid = Basic.guid('uid_');
2643				}
2644			},
2645
2646			/**
2647			Register a handler to a specific event dispatched by the object
2648
2649			@method addEventListener
2650			@param {String} type Type or basically a name of the event to subscribe to
2651			@param {Function} fn Callback function that will be called when event happens
2652			@param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first
2653			@param {Object} [scope=this] A scope to invoke event handler in
2654			*/
2655			addEventListener: function(type, fn, priority, scope) {
2656				var self = this, list;
2657				
2658				type = Basic.trim(type);
2659				
2660				if (/\s/.test(type)) {
2661					// multiple event types were passed for one handler
2662					Basic.each(type.split(/\s+/), function(type) {
2663						self.addEventListener(type, fn, priority, scope);
2664					});
2665					return;
2666				}
2667				
2668				type = type.toLowerCase();
2669				priority = parseInt(priority, 10) || 0;
2670				
2671				list = eventpool[this.uid] && eventpool[this.uid][type] || [];
2672				list.push({fn : fn, priority : priority, scope : scope || this});
2673				
2674				if (!eventpool[this.uid]) {
2675					eventpool[this.uid] = {};
2676				}
2677				eventpool[this.uid][type] = list;
2678			},
2679			
2680			/**
2681			Check if any handlers were registered to the specified event
2682
2683			@method hasEventListener
2684			@param {String} type Type or basically a name of the event to check
2685			@return {Mixed} Returns a handler if it was found and false, if - not
2686			*/
2687			hasEventListener: function(type) {
2688				return type ? !!(eventpool[this.uid] && eventpool[this.uid][type]) : !!eventpool[this.uid];
2689			},
2690			
2691			/**
2692			Unregister the handler from the event, or if former was not specified - unregister all handlers
2693
2694			@method removeEventListener
2695			@param {String} type Type or basically a name of the event
2696			@param {Function} [fn] Handler to unregister
2697			*/
2698			removeEventListener: function(type, fn) {
2699				type = type.toLowerCase();
2700	
2701				var list = eventpool[this.uid] && eventpool[this.uid][type], i;
2702	
2703				if (list) {
2704					if (fn) {
2705						for (i = list.length - 1; i >= 0; i--) {
2706							if (list[i].fn === fn) {
2707								list.splice(i, 1);
2708								break;
2709							}
2710						}
2711					} else {
2712						list = [];
2713					}
2714	
2715					// delete event list if it has become empty
2716					if (!list.length) {
2717						delete eventpool[this.uid][type];
2718						
2719						// and object specific entry in a hash if it has no more listeners attached
2720						if (Basic.isEmptyObj(eventpool[this.uid])) {
2721							delete eventpool[this.uid];
2722						}
2723					}
2724				}
2725			},
2726			
2727			/**
2728			Remove all event handlers from the object
2729
2730			@method removeAllEventListeners
2731			*/
2732			removeAllEventListeners: function() {
2733				if (eventpool[this.uid]) {
2734					delete eventpool[this.uid];
2735				}
2736			},
2737			
2738			/**
2739			Dispatch the event
2740
2741			@method dispatchEvent
2742			@param {String/Object} Type of event or event object to dispatch
2743			@param {Mixed} [...] Variable number of arguments to be passed to a handlers
2744			@return {Boolean} true by default and false if any handler returned false
2745			*/
2746			dispatchEvent: function(type) {
2747				var uid, list, args, tmpEvt, evt = {}, result = true, undef;
2748				
2749				if (Basic.typeOf(type) !== 'string') {
2750					// we can't use original object directly (because of Silverlight)
2751					tmpEvt = type;
2752
2753					if (Basic.typeOf(tmpEvt.type) === 'string') {
2754						type = tmpEvt.type;
2755
2756						if (tmpEvt.total !== undef && tmpEvt.loaded !== undef) { // progress event
2757							evt.total = tmpEvt.total;
2758							evt.loaded = tmpEvt.loaded;
2759						}
2760						evt.async = tmpEvt.async || false;
2761					} else {
2762						throw new x.EventException(x.EventException.UNSPECIFIED_EVENT_TYPE_ERR);
2763					}
2764				}
2765				
2766				// check if event is meant to be dispatched on an object having specific uid
2767				if (type.indexOf('::') !== -1) {
2768					(function(arr) {
2769						uid = arr[0];
2770						type = arr[1];
2771					}(type.split('::')));
2772				} else {
2773					uid = this.uid;
2774				}
2775				
2776				type = type.toLowerCase();
2777								
2778				list = eventpool[uid] && eventpool[uid][type];
2779
2780				if (list) {
2781					// sort event list by prority
2782					list.sort(function(a, b) { return b.priority - a.priority; });
2783					
2784					args = [].slice.call(arguments);
2785					
2786					// first argument will be pseudo-event object
2787					args.shift();
2788					evt.type = type;
2789					args.unshift(evt);
2790
2791					// Dispatch event to all listeners
2792					var queue = [];
2793					Basic.each(list, function(handler) {
2794						// explicitly set the target, otherwise events fired from shims do not get it
2795						args[0].target = handler.scope;
2796						// if event is marked as async, detach the handler
2797						if (evt.async) {
2798							queue.push(function(cb) {
2799								setTimeout(function() {
2800									cb(handler.fn.apply(handler.scope, args) === false);
2801								}, 1);
2802							});
2803						} else {
2804							queue.push(function(cb) {
2805								cb(handler.fn.apply(handler.scope, args) === false); // if handler returns false stop propagation
2806							});
2807						}
2808					});
2809					if (queue.length) {
2810						Basic.inSeries(queue, function(err) {
2811							result = !err;
2812						});
2813					}
2814				}
2815				return result;
2816			},
2817			
2818			/**
2819			Alias for addEventListener
2820
2821			@method bind
2822			@protected
2823			*/
2824			bind: function() {
2825				this.addEventListener.apply(this, arguments);
2826			},
2827			
2828			/**
2829			Alias for removeEventListener
2830
2831			@method unbind
2832			@protected
2833			*/
2834			unbind: function() {
2835				this.removeEventListener.apply(this, arguments);
2836			},
2837			
2838			/**
2839			Alias for removeAllEventListeners
2840
2841			@method unbindAll
2842			@protected
2843			*/
2844			unbindAll: function() {
2845				this.removeAllEventListeners.apply(this, arguments);
2846			},
2847			
2848			/**
2849			Alias for dispatchEvent
2850
2851			@method trigger
2852			@protected
2853			*/
2854			trigger: function() {
2855				return this.dispatchEvent.apply(this, arguments);
2856			},
2857			
2858			
2859			/**
2860			Converts properties of on[event] type to corresponding event handlers,
2861			is used to avoid extra hassle around the process of calling them back
2862
2863			@method convertEventPropsToHandlers
2864			@private
2865			*/
2866			convertEventPropsToHandlers: function(handlers) {
2867				var h;
2868						
2869				if (Basic.typeOf(handlers) !== 'array') {
2870					handlers = [handlers];
2871				}
2872
2873				for (var i = 0; i < handlers.length; i++) {
2874					h = 'on' + handlers[i];
2875					
2876					if (Basic.typeOf(this[h]) === 'function') {
2877						this.addEventListener(handlers[i], this[h]);
2878					} else if (Basic.typeOf(this[h]) === 'undefined') {
2879						this[h] = null; // object must have defined event properties, even if it doesn't make use of them
2880					}
2881				}
2882			}
2883			
2884		});
2885	}
2886
2887	EventTarget.instance = new EventTarget(); 
2888
2889	return EventTarget;
2890});
2891
2892// Included from: src/javascript/core/utils/Encode.js
2893
2894/**
2895 * Encode.js
2896 *
2897 * Copyright 2013, Moxiecode Systems AB
2898 * Released under GPL License.
2899 *
2900 * License: http://www.plupload.com/license
2901 * Contributing: http://www.plupload.com/contributing
2902 */
2903
2904define('moxie/core/utils/Encode', [], function() {
2905
2906	/**
2907	Encode string with UTF-8
2908
2909	@method utf8_encode
2910	@for Utils
2911	@static
2912	@param {String} str String to encode
2913	@return {String} UTF-8 encoded string
2914	*/
2915	var utf8_encode = function(str) {
2916		return unescape(encodeURIComponent(str));
2917	};
2918	
2919	/**
2920	Decode UTF-8 encoded string
2921
2922	@method utf8_decode
2923	@static
2924	@param {String} str String to decode
2925	@return {String} Decoded string
2926	*/
2927	var utf8_decode = function(str_data) {
2928		return decodeURIComponent(escape(str_data));
2929	};
2930	
2931	/**
2932	Decode Base64 encoded string (uses browser's default method if available),
2933	from: https://raw.github.com/kvz/phpjs/master/functions/url/base64_decode.js
2934
2935	@method atob
2936	@static
2937	@param {String} data String to decode
2938	@return {String} Decoded string
2939	*/
2940	var atob = function(data, utf8) {
2941		if (typeof(window.atob) === 'function') {
2942			return utf8 ? utf8_decode(window.atob(data)) : window.atob(data);
2943		}
2944
2945		// http://kevin.vanzonneveld.net
2946		// +   original by: Tyler Akins (http://rumkin.com)
2947		// +   improved by: Thunder.m
2948		// +      input by: Aman Gupta
2949		// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2950		// +   bugfixed by: Onno Marsman
2951		// +   bugfixed by: Pellentesque Malesuada
2952		// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2953		// +      input by: Brett Zamir (http://brett-zamir.me)
2954		// +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2955		// *     example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
2956		// *     returns 1: 'Kevin van Zonneveld'
2957		// mozilla has this native
2958		// - but breaks in 2.0.0.12!
2959		//if (typeof this.window.atob == 'function') {
2960		//    return atob(data);
2961		//}
2962		var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
2963		var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
2964			ac = 0,
2965			dec = "",
2966			tmp_arr = [];
2967
2968		if (!data) {
2969			return data;
2970		}
2971
2972		data += '';
2973
2974		do { // unpack four hexets into three octets using index points in b64
2975			h1 = b64.indexOf(data.charAt(i++));
2976			h2 = b64.indexOf(data.charAt(i++));
2977			h3 = b64.indexOf(data.charAt(i++));
2978			h4 = b64.indexOf(data.charAt(i++));
2979
2980			bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
2981
2982			o1 = bits >> 16 & 0xff;
2983			o2 = bits >> 8 & 0xff;
2984			o3 = bits & 0xff;
2985
2986			if (h3 == 64) {
2987				tmp_arr[ac++] = String.fromCharCode(o1);
2988			} else if (h4 == 64) {
2989				tmp_arr[ac++] = String.fromCharCode(o1, o2);
2990			} else {
2991				tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
2992			}
2993		} while (i < data.length);
2994
2995		dec = tmp_arr.join('');
2996
2997		return utf8 ? utf8_decode(dec) : dec;
2998	};
2999	
3000	/**
3001	Base64 encode string (uses browser's default method if available),
3002	from: https://raw.github.com/kvz/phpjs/master/functions/url/base64_encode.js
3003
3004	@method btoa
3005	@static
3006	@param {String} data String to encode
3007	@return {String} Base64 encoded string
3008	*/
3009	var btoa = function(data, utf8) {
3010		if (utf8) {
3011			utf8_encode(data);
3012		}
3013
3014		if (typeof(window.btoa) === 'function') {
3015			return window.btoa(data);
3016		}
3017
3018		// http://kevin.vanzonneveld.net
3019		// +   original by: Tyler Akins (http://rumkin.com)
3020		// +   improved by: Bayron Guevara
3021		// +   improved by: Thunder.m
3022		// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3023		// +   bugfixed by: Pellentesque Malesuada
3024		// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3025		// +   improved by: Rafał Kukawski (http://kukawski.pl)
3026		// *     example 1: base64_encode('Kevin van Zonneveld');
3027		// *     returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
3028		// mozilla has this native
3029		// - but breaks in 2.0.0.12!
3030		var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
3031		var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
3032			ac = 0,
3033			enc = "",
3034			tmp_arr = [];
3035
3036		if (!data) {
3037			return data;
3038		}
3039
3040		do { // pack three octets into four hexets
3041			o1 = data.charCodeAt(i++);
3042			o2 = data.charCodeAt(i++);
3043			o3 = data.charCodeAt(i++);
3044
3045			bits = o1 << 16 | o2 << 8 | o3;
3046
3047			h1 = bits >> 18 & 0x3f;
3048			h2 = bits >> 12 & 0x3f;
3049			h3 = bits >> 6 & 0x3f;
3050			h4 = bits & 0x3f;
3051
3052			// use hexets to index into b64, and append result to encoded string
3053			tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
3054		} while (i < data.length);
3055
3056		enc = tmp_arr.join('');
3057
3058		var r = data.length % 3;
3059
3060		return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
3061	};
3062
3063
3064	return {
3065		utf8_encode: utf8_encode,
3066		utf8_decode: utf8_decode,
3067		atob: atob,
3068		btoa: btoa
3069	};
3070});
3071
3072// Included from: src/javascript/runtime/Runtime.js
3073
3074/**
3075 * Runtime.js
3076 *
3077 * Copyright 2013, Moxiecode Systems AB
3078 * Released under GPL License.
3079 *
3080 * License: http://www.plupload.com/license
3081 * Contributing: http://www.plupload.com/contributing
3082 */
3083
3084define('moxie/runtime/Runtime', [
3085	"moxie/core/utils/Basic",
3086	"moxie/core/utils/Dom",
3087	"moxie/core/EventTarget"
3088], function(Basic, Dom, EventTarget) {
3089	var runtimeConstructors = {}, runtimes = {};
3090
3091	/**
3092	Common set of methods and properties for every runtime instance
3093
3094	@class Runtime
3095
3096	@param {Object} options
3097	@param {String} type Sanitized name of the runtime
3098	@param {Object} [caps] Set of capabilities that differentiate specified runtime
3099	@param {Object} [modeCaps] Set of capabilities that do require specific operational mode
3100	@param {String} [preferredMode='browser'] Preferred operational mode to choose if no required capabilities were requested
3101	*/
3102	function Runtime(options, type, caps, modeCaps, preferredMode) {
3103		/**
3104		Dispatched when runtime is initialized and ready.
3105		Results in RuntimeInit on a connected component.
3106
3107		@event Init
3108		*/
3109
3110		/**
3111		Dispatched when runtime fails to initialize.
3112		Results in RuntimeError on a connected component.
3113
3114		@event Error
3115		*/
3116
3117		var self = this
3118		, _shim
3119		, _uid = Basic.guid(type + '_')
3120		, defaultMode = preferredMode || 'browser'
3121		;
3122
3123		options = options || {};
3124
3125		// register runtime in private hash
3126		runtimes[_uid] = this;
3127
3128		/**
3129		Default set of capabilities, which can be redifined later by specific runtime
3130
3131		@private
3132		@property caps
3133		@type Object
3134		*/
3135		caps = Basic.extend({
3136			// Runtime can: 
3137			// provide access to raw binary data of the file
3138			access_binary: false,
3139			// provide access to raw binary data of the image (image extension is optional) 
3140			access_image_binary: false,
3141			// display binary data as thumbs for example
3142			display_media: false,
3143			// make cross-domain requests
3144			do_cors: false,
3145			// accept files dragged and dropped from the desktop
3146			drag_and_drop: false,
3147			// filter files in selection dialog by their extensions
3148			filter_by_extension: true,
3149			// resize image (and manipulate it raw data of any file in general)
3150			resize_image: false,
3151			// periodically report how many bytes of total in the file were uploaded (loaded)
3152			report_upload_progress: false,
3153			// provide access to the headers of http response 
3154			return_response_headers: false,
3155			// support response of specific type, which should be passed as an argument
3156			// e.g. runtime.can('return_response_type', 'blob')
3157			return_response_type: false,
3158			// return http status code of the response
3159			return_status_code: true,
3160			// send custom http header with the request
3161			send_custom_headers: false,
3162			// pick up the files from a dialog
3163			select_file: false,
3164			// select whole folder in file browse dialog
3165			select_folder: false,
3166			// select multiple files at once in file browse dialog
3167			select_multiple: true,
3168			// send raw binary data, that is generated after image resizing or manipulation of other kind
3169			send_binary_string: false,
3170			// send cookies with http request and therefore retain session
3171			send_browser_cookies: true,
3172			// send data formatted as multipart/form-data
3173			send_multipart: true,
3174			// slice the file or blob to smaller parts
3175			slice_blob: false,
3176			// upload file without preloading it to memory, stream it out directly from disk
3177			stream_upload: false,
3178			// programmatically trigger file browse dialog
3179			summon_file_dialog: false,
3180			// upload file of specific size, size should be passed as argument
3181			// e.g. runtime.can('upload_filesize', '500mb')
3182			upload_filesize: true,
3183			// initiate http request with specific http method, method should be passed as argument
3184			// e.g. runtime.can('use_http_method', 'put')
3185			use_http_method: true
3186		}, caps);
3187			
3188	
3189		// default to the mode that is compatible with preferred caps
3190		if (options.preferred_caps) {
3191			defaultMode = Runtime.getMode(modeCaps, options.preferred_caps, defaultMode);
3192		}
3193		
3194		// small extension factory here (is meant to be extended with actual extensions constructors)
3195		_shim = (function() {
3196			var objpool = {};
3197			return {
3198				exec: function(uid, comp, fn, args) {
3199					if (_shim[comp]) {
3200						if (!objpool[uid]) {
3201							objpool[uid] = {
3202								context: this,
3203								instance: new _shim[comp]()
3204							};
3205						}
3206						if (objpool[uid].instance[fn]) {
3207							return objpool[uid].instance[fn].apply(this, args);
3208						}
3209					}
3210				},
3211
3212				removeInstance: function(uid) {
3213					delete objpool[uid];
3214				},
3215
3216				removeAllInstances: function() {
3217					var self = this;
3218					Basic.each(objpool, function(obj, uid) {
3219						if (Basic.typeOf(obj.instance.destroy) === 'function') {
3220							obj.instance.destroy.call(obj.context);
3221						}
3222						self.removeInstance(uid);
3223					});
3224				}
3225			};
3226		}());
3227
3228
3229		// public methods
3230		Basic.extend(this, {
3231			/**
3232			Specifies whether runtime instance was initialized or not
3233
3234			@property initialized
3235			@type {Boolean}
3236			@default false
3237			*/
3238			initialized: false, // shims require this flag to stop initialization retries
3239
3240			/**
3241			Unique ID of the runtime
3242
3243			@property uid
3244			@type {String}
3245			*/
3246			uid: _uid,
3247
3248			/**
3249			Runtime type (e.g. flash, html5, etc)
3250
3251			@property type
3252			@type {String}
3253			*/
3254			type: type,
3255
3256			/**
3257			Runtime (not native one) may operate in browser or client mode.
3258
3259			@property mode
3260			@private
3261			@type {String|Boolean} current mode or false, if none possible
3262			*/
3263			mode: Runtime.getMode(modeCaps, (options.required_caps), defaultMode),
3264
3265			/**
3266			id of the DOM container for the runtime (if available)
3267
3268			@property shimid
3269			@type {String}
3270			*/
3271			shimid: _uid + '_container',
3272
3273			/**
3274			Number of connected clients. If equal to zero, runtime can be destroyed
3275
3276			@property clients
3277			@type {Number}
3278			*/
3279			clients: 0,
3280
3281			/**
3282			Runtime initialization options
3283
3284			@property options
3285			@type {Object}
3286			*/
3287			options: options,
3288
3289			/**
3290			Checks if the runtime has specific capability
3291
3292			@method can
3293			@param {String} cap Name of capability to check
3294			@param {Mixed} [value] If passed, capability should somehow correlate to the value
3295			@param {Object} [refCaps] Set of capabilities to check the specified cap against (defaults to internal set)
3296			@return {Boolean} true if runtime has such capability and false, if - not
3297			*/
3298			can: function(cap, value) {
3299				var refCaps = arguments[2] || caps;
3300
3301				// if cap var is a comma-separated list of caps, convert it to object (key/value)
3302				if (Basic.typeOf(cap) === 'string' && Basic.typeOf(value) === 'undefined') {
3303					cap = Runtime.parseCaps(cap);
3304				}
3305
3306				if (Basic.typeOf(cap) === 'object') {
3307					for (var key in cap) {
3308						if (!this.can(key, cap[key], refCaps)) {
3309							return false;
3310						}
3311					}
3312					return true;
3313				}
3314
3315				// check the individual cap
3316				if (Basic.typeOf(refCaps[cap]) === 'function') {
3317					return refCaps[cap].call(this, value);
3318				} else {
3319					return (value === refCaps[cap]);
3320				}
3321			},
3322
3323			/**
3324			Returns container for the runtime as DOM element
3325
3326			@method getShimContainer
3327			@return {DOMElement}
3328			*/
3329			getShimContainer: function() {
3330				var container, shimContainer = Dom.get(this.shimid);
3331
3332				// if no container for shim, create one
3333				if (!shimContainer) {
3334					container = this.options.container ? Dom.get(this.options.container) : document.body;
3335
3336					// create shim container and insert it at an absolute position into the outer container
3337					shimContainer = document.createElement('div');
3338					shimContainer.id = this.shimid;
3339					shimContainer.className = 'moxie-shim moxie-shim-' + this.type;
3340
3341					Basic.extend(shimContainer.style, {
3342						position: 'absolute',
3343						top: '0px',
3344						left: '0px',
3345						width: '1px',
3346						height: '1px',
3347						overflow: 'hidden'
3348					});
3349
3350					container.appendChild(shimContainer);
3351					container = null;
3352				}
3353
3354				return shimContainer;
3355			},
3356
3357			/**
3358			Returns runtime as DOM element (if appropriate)
3359
3360			@method getShim
3361			@return {DOMElement}
3362			*/
3363			getShim: function() {
3364				return _shim;
3365			},
3366
3367			/**
3368			Invokes a method within the runtime itself (might differ across the runtimes)
3369
3370			@method shimExec
3371			@param {Mixed} []
3372			@protected
3373			@return {Mixed} Depends on the action and component
3374			*/
3375			shimExec: function(component, action) {
3376				var args = [].slice.call(arguments, 2);
3377				return self.getShim().exec.call(this, this.uid, component, action, args);
3378			},
3379
3380			/**
3381			Operaional interface that is used by components to invoke specific actions on the runtime
3382			(is invoked in the scope of component)
3383
3384			@method exec
3385			@param {Mixed} []*
3386			@protected
3387			@return {Mixed} Depends on the action and component
3388			*/
3389			exec: function(component, action) { // this is called in the context of component, not runtime
3390				var args = [].slice.call(arguments, 2);
3391
3392				if (self[component] && self[component][action]) {
3393					return self[component][action].apply(this, args);
3394				}
3395				return self.shimExec.apply(this, arguments);
3396			},
3397
3398			/**
3399			Destroys the runtime (removes all events and deletes DOM structures)
3400
3401			@method destroy
3402			*/
3403			destroy: function() {
3404				if (!self) {
3405					return; // obviously already destroyed
3406				}
3407
3408				var shimContainer = Dom.get(this.shimid);
3409				if (shimContainer) {
3410					shimContainer.parentNode.removeChild(shimContainer);
3411				}
3412
3413				if (_shim) {
3414					_shim.removeAllInstances();
3415				}
3416
3417				this.unbindAll();
3418				delete runtimes[this.uid];
3419				this.uid = null; // mark this runtime as destroyed
3420				_uid = self = _shim = shimContainer = null;
3421			}
3422		});
3423
3424		// once we got the mode, test against all caps
3425		if (this.mode && options.required_caps && !this.can(options.required_caps)) {
3426			this.mode = false;
3427		}	
3428	}
3429
3430
3431	/**
3432	Default order to try different runtime types
3433
3434	@property order
3435	@type String
3436	@static
3437	*/
3438	Runtime.order = 'html5,flash,silverlight,html4';
3439
3440
3441	/**
3442	Retrieves runtime from private hash by it's uid
3443
3444	@method getRuntime
3445	@private
3446	@static
3447	@param {String} uid Unique identifier of the runtime
3448	@return {Runtime|Boolean} Returns runtime, if it exists and false, if - not
3449	*/
3450	Runtime.getRuntime = function(uid) {
3451		return runtimes[uid] ? runtimes[uid] : false;
3452	};
3453
3454
3455	/**
3456	Register constructor for the Runtime of new (or perhaps modified) type
3457
3458	@method addConstructor
3459	@static
3460	@param {String} type Runtime type (e.g. flash, html5, etc)
3461	@param {Function} construct Constructor for the Runtime type
3462	*/
3463	Runtime.addConstructor = function(type, constructor) {
3464		constructor.prototype = EventTarget.instance;
3465		runtimeConstructors[type] = constructor;
3466	};
3467
3468
3469	/**
3470	Get the constructor for the specified type.
3471
3472	method getConstructor
3473	@static
3474	@param {String} type Runtime type (e.g. flash, html5, etc)
3475	@return {Function} Constructor for the Runtime type
3476	*/
3477	Runtime.getConstructor = function(type) {
3478		return runtimeConstructors[type] || null;
3479	};
3480
3481
3482	/**
3483	Get info about the runtime (uid, type, capabilities)
3484
3485	@method getInfo
3486	@static
3487	@param {String} uid Unique identifier of the runtime
3488	@return {Mixed} Info object or null if runtime doesn't exist
3489	*/
3490	Runtime.getInfo = function(uid) {
3491		var runtime = Runtime.getRuntime(uid);
3492
3493		if (runtime) {
3494			return {
3495				uid: runtime.uid,
3496				type: runtime.type,
3497				mode: runtime.mode,
3498				can: function() {
3499					return runtime.can.apply(runtime, arguments);
3500				}
3501			};
3502		}
3503		return null;
3504	};
3505
3506
3507	/**
3508	Convert caps represented by a comma-separated string to the object representation.
3509
3510	@method parseCaps
3511	@static
3512	@param {String} capStr Comma-separated list of capabilities
3513	@return {Object}
3514	*/
3515	Runtime.parseCaps = function(capStr) {
3516		var capObj = {};
3517
3518		if (Basic.typeOf(capStr) !== 'string') {
3519			return capStr || {};
3520		}
3521
3522		Basic.each(capStr.split(','), function(key) {
3523			capObj[key] = true; // we assume it to be - true
3524		});
3525
3526		return capObj;
3527	};
3528
3529	/**
3530	Test the specified runtime for specific capabilities.
3531
3532	@method can
3533	@static
3534	@param {String} type Runtime type (e.g. flash, html5, etc)
3535	@param {String|Object} caps Set of capabilities to check
3536	@return {Boolean} Result of the test
3537	*/
3538	Runtime.can = function(type, caps) {
3539		var runtime
3540		, constructor = Runtime.getConstructor(type)
3541		, mode
3542		;
3543		if (constructor) {
3544			runtime = new constructor({
3545				required_caps: caps
3546			});
3547			mode = runtime.mode;
3548			runtime.destroy();
3549			return !!mode;
3550		}
3551		return false;
3552	};
3553
3554
3555	/**
3556	Figure out a runtime that supports specified capabilities.
3557
3558	@method thatCan
3559	@static
3560	@param {String|Object} caps Set of capabilities to check
3561	@param {String} [runtimeOrder] Comma-separated list of runtimes to check against
3562	@return {String} Usable runtime identifier or null
3563	*/
3564	Runtime.thatCan = function(caps, runtimeOrder) {
3565		var types = (runtimeOrder || Runtime.order).split(/\s*,\s*/);
3566		for (var i in types) {
3567			if (Runtime.can(types[i], caps)) {
3568				return types[i];
3569			}
3570		}
3571		return null;
3572	};
3573
3574
3575	/**
3576	Figure out an operational mode for the specified set of capabilities.
3577
3578	@method getMode
3579	@static
3580	@param {Object} modeCaps Set of capabilities that depend on particular runtime mode
3581	@param {Object} [requiredCaps] Supplied set of capabilities to find operational mode for
3582	@param {String|Boolean} [defaultMode='browser'] Default mode to use 
3583	@return {String|Boolean} Compatible operational mode
3584	*/
3585	Runtime.getMode = function(modeCaps, requiredCaps, defaultMode) {
3586		var mode = null;
3587
3588		if (Basic.typeOf(defaultMode) === 'undefined') { // only if not specified
3589			defaultMode = 'browser';
3590		}
3591
3592		if (requiredCaps && !Basic.isEmptyObj(modeCaps)) {
3593			// loop over required caps and check if they do require the same mode
3594			Basic.each(requiredCaps, function(value, cap) {
3595				if (modeCaps.hasOwnProperty(cap)) {
3596					var capMode = modeCaps[cap](value);
3597
3598					// make sure we always have an array
3599					if (typeof(capMode) === 'string') {
3600						capMode = [capMode];
3601					}
3602					
3603					if (!mode) {
3604						mode = capMode;
3605					} else if (!(mode = Basic.arrayIntersect(mode, capMode))) {
3606						// if cap requires conflicting mode - runtime cannot fulfill required caps
3607						return (mode = false);
3608					}
3609				}
3610			});
3611
3612			if (mode) {
3613				return Basic.inArray(defaultMode, mode) !== -1 ? defaultMode : mode[0];
3614			} else if (mode === false) {
3615				return false;
3616			}
3617		}
3618		return defaultMode; 
3619	};
3620
3621
3622	/**
3623	Capability check that always returns true
3624
3625	@private
3626	@static
3627	@return {True}
3628	*/
3629	Runtime.capTrue = function() {
3630		return true;
3631	};
3632
3633	/**
3634	Capability check that always returns false
3635
3636	@private
3637	@static
3638	@return {False}
3639	*/
3640	Runtime.capFalse = function() {
3641		return false;
3642	};
3643
3644	/**
3645	Evaluate the expression to boolean value and create a function that always returns it.
3646
3647	@private
3648	@static
3649	@param {Mixed} expr Expression to evaluate
3650	@return {Function} Function returning the result of evaluation
3651	*/
3652	Runtime.capTest = function(expr) {
3653		return function() {
3654			return !!expr;
3655		};
3656	};
3657
3658	return Runtime;
3659});
3660
3661// Included from: src/javascript/runtime/RuntimeClient.js
3662
3663/**
3664 * RuntimeClient.js
3665 *
3666 * Copyright 2013, Moxiecode Systems AB
3667 * Released under GPL License.
3668 *
3669 * License: http://www.plupload.com/license
3670 * Contributing: http://www.plupload.com/contributing
3671 */
3672
3673define('moxie/runtime/RuntimeClient', [
3674	'moxie/core/Exceptions',
3675	'moxie/core/utils/Basic',
3676	'moxie/runtime/Runtime'
3677], function(x, Basic, Runtime) {
3678	/**
3679	Set of methods and properties, required by a component to acquire ability to connect to a runtime
3680
3681	@class RuntimeClient
3682	*/
3683	return function RuntimeClient() {
3684		var runtime;
3685
3686		Basic.extend(this, {
3687			/**
3688			Connects to the runtime specified by the options. Will either connect to existing runtime or create a new one.
3689			Increments number of clients connected to the specified runtime.
3690
3691			@method connectRuntime
3692			@param {Mixed} options Can be a runtme uid or a set of key-value pairs defining requirements and pre-requisites
3693			*/
3694			connectRuntime: function(options) {
3695				var comp = this, ruid;
3696
3697				function initialize(items) {
3698					var type, constructor;
3699
3700					// if we ran out of runtimes
3701					if (!items.length) {
3702						comp.trigger('RuntimeError', new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
3703						runtime = null;
3704						return;
3705					}
3706
3707					type = items.shift();
3708					constructor = Runtime.getConstructor(type);
3709					if (!constructor) {
3710						initialize(items);
3711						return;
3712					}
3713
3714					// try initializing the runtime
3715					runtime = new constructor(options);
3716
3717					runtime.bind('Init', function() {
3718						// mark runtime as initialized
3719						runtime.initialized = true;
3720
3721						// jailbreak ...
3722						setTimeout(function() {
3723							runtime.clients++;
3724							// this will be triggered on component
3725							comp.trigger('RuntimeInit', runtime);
3726						}, 1);
3727					});
3728
3729					runtime.bind('Error', function() {
3730						runtime.destroy(); // runtime cannot destroy itself from inside at a right moment, thus we do it here
3731						initialize(items);
3732					});
3733
3734					/*runtime.bind('Exception', function() { });*/
3735
3736					// check if runtime managed to pick-up operational mode
3737					if (!runtime.mode) {
3738						runtime.trigger('Error');
3739						return;
3740					}
3741
3742					runtime.init();
3743				}
3744
3745				// check if a particular runtime was requested
3746				if (Basic.typeOf(options) === 'string') {
3747					ruid = options;
3748				} else if (Basic.typeOf(options.ruid) === 'string') {
3749					ruid = options.ruid;
3750				}
3751
3752				if (ruid) {
3753					runtime = Runtime.getRuntime(ruid);
3754					if (runtime) {
3755						runtime.clients++;
3756						return runtime;
3757					} else {
3758						// there should be a runtime and there's none - weird case
3759						throw new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR);
3760					}
3761				}
3762
3763				// initialize a fresh one, that fits runtime list and required features best
3764				initialize((options.runtime_order || Runtime.order).split(/\s*,\s*/));
3765			},
3766
3767			/**
3768			Returns the runtime to which the client is currently connected.
3769
3770			@method getRuntime
3771			@return {Runtime} Runtime or null if client is not connected
3772			*/
3773			getRuntime: function() {
3774				if (runtime && runtime.uid) {
3775					return runtime;
3776				}
3777				runtime = null; // make sure we do not leave zombies rambling around
3778				return null;
3779			},
3780
3781			/**
3782			Disconnects from the runtime. Decrements number of clients connected to the specified runtime.
3783
3784			@method disconnectRuntime
3785			*/
3786			disconnectRuntime: function() {
3787				if (runtime && --runtime.clients <= 0) {
3788					runtime.destroy();
3789					runtime = null;
3790				}
3791			}
3792
3793		});
3794	};
3795
3796
3797});
3798
3799// Included from: src/javascript/file/Blob.js
3800
3801/**
3802 * Blob.js
3803 *
3804 * Copyright 2013, Moxiecode Systems AB
3805 * Released under GPL License.
3806 *
3807 * License: http://www.plupload.com/license
3808 * Contributing: http://www.plupload.com/contributing
3809 */
3810
3811define('moxie/file/Blob', [
3812	'moxie/core/utils/Basic',
3813	'moxie/core/utils/Encode',
3814	'moxie/runtime/RuntimeClient'
3815], function(Basic, Encode, RuntimeClient) {
3816	
3817	var blobpool = {};
3818
3819	/**
3820	@class Blob
3821	@constructor
3822	@param {String} ruid Unique id of the runtime, to which this blob belongs to
3823	@param {Object} blob Object "Native" blob object, as it is represented in the runtime
3824	*/
3825	function Blob(ruid, blob) {
3826
3827		function _sliceDetached(start, end, type) {
3828			var blob, data = blobpool[this.uid];
3829
3830			if (Basic.typeOf(data) !== 'string' || !data.length) {
3831				return null; // or throw exception
3832			}
3833
3834			blob = new Blob(null, {
3835				type: type,
3836				size: end - start
3837			});
3838			blob.detach(data.substr(start, blob.size));
3839
3840			return blob;
3841		}
3842
3843		RuntimeClient.call(this);
3844
3845		if (ruid) {	
3846			this.connectRuntime(ruid);
3847		}
3848
3849		if (!blob) {
3850			blob = {};
3851		} else if (Basic.typeOf(blob) === 'string') { // dataUrl or binary string
3852			blob = { data: blob };
3853		}
3854
3855		Basic.extend(this, {
3856			
3857			/**
3858			Unique id of the component
3859
3860			@property uid
3861			@type {String}
3862			*/
3863			uid: blob.uid || Basic.guid('uid_'),
3864			
3865			/**
3866			Unique id of the connected runtime, if falsy, then runtime will have to be initialized 
3867			before this Blob can be used, modified or sent
3868
3869			@property ruid
3870			@type {String}
3871			*/
3872			ruid: ruid,
3873	
3874			/**
3875			Size of blob
3876
3877			@property size
3878			@type {Number}
3879			@default 0
3880			*/
3881			size: blob.size || 0,
3882			
3883			/**
3884			Mime type of blob
3885
3886			@property type
3887			@type {String}
3888			@default ''
3889			*/
3890			type: blob.type || '',
3891			
3892			/**
3893			@method slice
3894			@param {Number} [start=0]
3895			*/
3896			slice: function(start, end, type) {		
3897				if (this.isDetached()) {
3898					return _sliceDetached.apply(this, arguments);
3899				}
3900				return this.getRuntime().exec.call(this, 'Blob', 'slice', this.getSource(), start, end, type);
3901			},
3902
3903			/**
3904			Returns "native" blob object (as it is represented in connected runtime) or null if not found
3905
3906			@method getSource
3907			@return {Blob} Returns "native" blob object or null if not found
3908			*/
3909			getSource: function() {
3910				if (!blobpool[this.uid]) {
3911					return null;	
3912				}
3913				return blobpool[this.uid];
3914			},
3915
3916			/** 
3917			Detaches blob from any runtime that it depends on and initialize with standalone value
3918
3919			@method detach
3920			@protected
3921			@param {DOMString} [data=''] Standalone value
3922			*/
3923			detach: function(data) {
3924				if (this.ruid) {
3925					this.getRuntime().exec.call(this, 'Blob', 'destroy');
3926					this.disconnectRuntime();
3927					this.ruid = null;
3928				}
3929
3930				data = data || '';
3931
3932				// if dataUrl, convert to binary string
3933				var matches = data.match(/^data:([^;]*);base64,/);
3934				if (matches) {
3935					this.type = matches[1];
3936					data = Encode.atob(data.substring(data.indexOf('base64,') + 7));
3937				}
3938
3939				this.size = data.length;
3940
3941				blobpool[this.uid] = data;
3942			},
3943
3944			/**
3945			Checks if blob is standalone (detached of any runtime)
3946			
3947			@method isDetached
3948			@protected
3949			@return {Boolean}
3950			*/
3951			isDetached: function() {
3952				return !this.ruid && Basic.typeOf(blobpool[this.uid]) === 'string';
3953			},
3954			
3955			/** 
3956			Destroy Blob and free any resources it was using
3957
3958			@method destroy
3959			*/
3960			destroy: function() {
3961				this.detach();
3962				delete blobpool[this.uid];
3963			}
3964		});
3965
3966		
3967		if (blob.data) {
3968			this.detach(blob.data); // auto-detach if payload has been passed
3969		} else {
3970			blobpool[this.uid] = blob;	
3971		}
3972	}
3973	
3974	return Blob;
3975});
3976
3977// Included from: src/javascript/file/File.js
3978
3979/**
3980 * File.js
3981 *
3982 * Copyright 2013, Moxiecode Systems AB
3983 * Released under GPL License.
3984 *
3985 * License: http://www.plupload.com/license
3986 * Contributing: http://www.plupload.com/contributing
3987 */
3988
3989define('moxie/file/File', [
3990	'moxie/core/utils/Basic',
3991	'moxie/core/utils/Mime',
3992	'moxie/file/Blob'
3993], function(Basic, Mime, Blob) {
3994	/**
3995	@class File
3996	@extends Blob
3997	@constructor
3998	@param {String} ruid Unique id of the runtime, to which this blob belongs to
3999	@param {Object} file Object "Native" file object, as it is represented in the runtime
4000	*/
4001	function File(ruid, file) {
4002		var name, type;
4003
4004		if (!file) { // avoid extra errors in case we overlooked something
4005			file = {};
4006		}
4007
4008		// figure out the type
4009		if (file.type && file.type !== '') {
4010			type = file.type;
4011		} else {
4012			type = Mime.getFileMime(file.name);
4013		}
4014
4015		// sanitize file name or generate new one
4016		if (file.name) {
4017			name = file.name.replace(/\\/g, '/');
4018			name = name.substr(name.lastIndexOf('/') + 1);
4019		} else {
4020			var prefix = type.split('/')[0];
4021			name = Basic.guid((prefix !== '' ? prefix : 'file') + '_');
4022			
4023			if (Mime.extensions[type]) {
4024				name += '.' + Mime.extensions[type][0]; // append proper extension if possible
4025			}
4026		}
4027
4028		Blob.apply(this, arguments);
4029		
4030		Basic.extend(this, {
4031			/**
4032			File mime type
4033
4034			@property type
4035			@type {String}
4036			@default ''
4037			*/
4038			type: type || '',
4039
4040			/**
4041			File name
4042
4043			@property name
4044			@type {String}
4045			@default UID
4046			*/
4047			name: name || Basic.guid('file_'),
4048			
4049			/**
4050			Date of last modification
4051
4052			@property lastModifiedDate
4053			@type {String}
4054			@default now
4055			*/
4056			lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString() // Thu Aug 23 2012 19:40:00 GMT+0400 (GET)
4057		});
4058	}
4059
4060	File.prototype = Blob.prototype;
4061
4062	return File;
4063});
4064
4065// Included from: src/javascript/file/FileInput.js
4066
4067/**
4068 * FileInput.js
4069 *
4070 * Copyright 2013, Moxiecode Systems AB
4071 * Released under GPL License.
4072 *
4073 * License: http://www.plupload.com/license
4074 * Contributing: http://www.plupload.com/contributing
4075 */
4076
4077define('moxie/file/FileInput', [
4078	'moxie/core/utils/Basic',
4079	'moxie/core/utils/Mime',
4080	'moxie/core/utils/Dom',
4081	'moxie/core/Exceptions',
4082	'moxie/core/EventTarget',
4083	'moxie/core/I18n',
4084	'moxie/file/File',
4085	'moxie/runtime/Runtime',
4086	'moxie/runtime/RuntimeClient'
4087], function(Basic, Mime, Dom, x, EventTarget, I18n, File, Runtime, RuntimeClient) {
4088	/**
4089	Provides a convenient way to create cross-browser file-picker. Generates file selection dialog on click,
4090	converts selected files to _File_ objects, to be used in conjunction with _Image_, preloaded in memory
4091	with _FileReader_ or uploaded to a server through _XMLHttpRequest_.
4092
4093	@class FileInput
4094	@constructor
4095	@extends EventTarget
4096	@uses RuntimeClient
4097	@param {Object|String|DOMElement} options If options is string or node, argument is considered as _browse\_button_.
4098		@param {String|DOMElement} options.browse_button DOM Element to turn into file picker.
4099		@param {Array} [options.accept] Array of mime types to accept. By default accepts all.
4100		@param {String} [options.file='file'] Name of the file field (not the filename).
4101		@param {Boolean} [options.multiple=false] Enable selection of multiple files.
4102		@param {Boolean} [options.directory=false] Turn file input into the folder input (cannot be both at the same time).
4103		@param {String|DOMElement} [options.container] DOM Element to use as a container for file-picker. Defaults to parentNode 
4104		for _browse\_button_.
4105		@param {Object|String} [options.required_caps] Set of required capabilities, that chosen runtime must support.
4106
4107	@example
4108		<div id="container">
4109			<a id="file-picker" href="javascript:;">Browse...</a>
4110		</div>
4111
4112		<script>
4113			var fileInput = new mOxie.FileInput({
4114				browse_button: 'file-picker', // or document.getElementById('file-picker')
4115				container: 'container',
4116				accept: [
4117					{title: "Image files", extensions: "jpg,gif,png"} // accept only images
4118				],
4119				multiple: true // allow multiple file selection
4120			});
4121
4122			fileInput.onchange = function(e) {
4123				// do something to files array
4124				console.info(e.target.files); // or this.files or fileInput.files
4125			};
4126
4127			fileInput.init(); // initialize
4128		</script>
4129	*/
4130	var dispatches = [
4131		/**
4132		Dispatched when runtime is connected and file-picker is ready to be used.
4133
4134		@event ready
4135		@param {Object} event
4136		*/
4137		'ready',
4138
4139		/**
4140		Dispatched right after [ready](#event_ready) event, and whenever [refresh()](#method_refresh) is invoked. 
4141		Check [corresponding documentation entry](#method_refresh) for more info.
4142
4143		@event refresh
4144		@param {Object} event
4145		*/
4146
4147		/**
4148		Dispatched when selection of files in the dialog is complete.
4149
4150		@event change
4151		@param {Object} event
4152		*/
4153		'change',
4154
4155		'cancel', // TODO: might be useful
4156
4157		/**
4158		Dispatched when mouse cursor enters file-picker area. Can be used to style element
4159		accordingly.
4160
4161		@event mouseenter
4162		@param {Object} event
4163		*/
4164		'mouseenter',
4165
4166		/**
4167		Dispatched when mouse cursor leaves file-picker area. Can be used to style element
4168		accordingly.
4169
4170		@event mouseleave
4171		@param {Object} event
4172		*/
4173		'mouseleave',
4174
4175		/**
4176		Dispatched when functional mouse button is pressed on top of file-picker area.
4177
4178		@event mousedown
4179		@param {Object} event
4180		*/
4181		'mousedown',
4182
4183		/**
4184		Dispatched when functional mouse button is released on top of file-picker area.
4185
4186		@event mouseup
4187		@param {Object} event
4188		*/
4189		'mouseup'
4190	];
4191
4192	function FileInput(options) {
4193		var self = this,
4194			container, browseButton, defaults;
4195
4196		// if flat argument passed it should be browse_button id
4197		if (Basic.inArray(Basic.typeOf(options), ['string', 'node']) !== -1) {
4198			options = { browse_button : options };
4199		}
4200
4201		// this will help us to find proper default container
4202		browseButton = Dom.get(options.browse_button);
4203		if (!browseButton) {
4204			// browse button is required
4205			throw new x.DOMException(x.DOMException.NOT_FOUND_ERR);
4206		}
4207
4208		// figure out the options
4209		defaults = {
4210			accept: [{
4211				title: I18n.translate('All Files'),
4212				extensions: '*'
4213			}],
4214			name: 'file',
4215			multiple: false,
4216			required_caps: false,
4217			container: browseButton.parentNode || document.body
4218		};
4219		
4220		options = Basic.extend({}, defaults, options);
4221
4222		// convert to object representation
4223		if (typeof(options.required_caps) === 'string') {
4224			options.required_caps = Runtime.parseCaps(options.required_caps);
4225		}
4226					
4227		// normalize accept option (could be list of mime types or array of title/extensions pairs)
4228		if (typeof(options.accept) === 'string') {
4229			options.accept = Mime.mimes2extList(options.accept);
4230		}
4231
4232		container = Dom.get(options.container);
4233		// make sure we have container
4234		if (!container) {
4235			container = document.body;
4236		}
4237
4238		// make container relative, if it's not
4239		if (Dom.getStyle(container, 'position') === 'static') {
4240			container.style.position = 'relative';
4241		}
4242
4243		container = browseButton = null; // IE
4244						
4245		RuntimeClient.call(self);
4246		
4247		Basic.extend(self, {
4248			/**
4249			Unique id of the component
4250
4251			@property uid
4252			@protected
4253			@readOnly
4254			@type {String}
4255			@default UID
4256			*/
4257			uid: Basic.guid('uid_'),
4258			
4259			/**
4260			Unique id of the connected runtime, if any.
4261
4262			@property ruid
4263			@protected
4264			@type {String}
4265			*/
4266			ruid: null,
4267
4268			/**
4269			Unique id of the runtime container. Useful to get hold of it for various manipulations.
4270
4271			@property shimid
4272			@protected
4273			@type {String}
4274			*/
4275			shimid: null,
4276			
4277			/**
4278			Array of selected mOxie.File objects
4279
4280			@property files
4281			@type {Array}
4282			@default null
4283			*/
4284			files: null,
4285
4286			/**
4287			Initializes the file-picker, connects it to runtime and dispatches event ready when done.
4288
4289			@method init
4290			*/
4291			init: function() {
4292				self.convertEventPropsToHandlers(dispatches);
4293
4294				self.bind('RuntimeInit', function(e, runtime) {
4295					self.ruid = runtime.uid;
4296					self.shimid = runtime.shimid;
4297
4298					self.bind("Ready", function() {
4299						self.trigger("Refresh");
4300					}, 999);
4301
4302					self.bind("Change", function() {
4303						var files = runtime.exec.call(self, 'FileInput', 'getFiles');
4304
4305						self.files = [];
4306
4307						Basic.each(files, function(file) {
4308							// ignore empty files (IE10 for example hangs if you try to send them via XHR)
4309							if (file.size === 0) {
4310								return true; 
4311							}
4312							self.files.push(new File(self.ruid, file));
4313						});
4314					}, 999);
4315
4316					// re-position and resize shim container
4317					self.bind('Refresh', function() {
4318						var pos, size, browseButton, shimContainer;
4319						
4320						browseButton = Dom.get(options.browse_button);
4321						shimContainer = Dom.get(runtime.shimid); // do not use runtime.getShimContainer(), since it will create container if it doesn't exist
4322
4323						if (browseButton) {
4324							pos = Dom.getPos(browseButton, Dom.get(options.container));
4325							size = Dom.getSize(browseButton);
4326
4327							if (shimContainer) {
4328								Basic.extend(shimContainer.style, {
4329									top     : pos.y + 'px',
4330									left    : pos.x + 'px',
4331									width   : size.w + 'px',
4332									height  : size.h + 'px'
4333								});
4334							}
4335						}
4336						shimContainer = browseButton = null;
4337					});
4338					
4339					runtime.exec.call(self, 'FileInput', 'init', options);
4340				});
4341
4342				// runtime needs: options.required_features, options.runtime_order and options.container
4343				self.connectRuntime(Basic.extend({}, options, {
4344					required_caps: {
4345						select_file: true
4346					}
4347				}));
4348			},
4349
4350			/**
4351			Disables file-picker element, so that it doesn't react to mouse clicks.
4352
4353			@method disable
4354			@param {Boolean} [state=true] Disable component if - true, enable if - false
4355			*/
4356			disable: function(state) {
4357				var runtime = this.getRuntime();
4358				if (runtime) {
4359					runtime.exec.call(this, 'FileInput', 'disable', Basic.typeOf(state) === 'undefined' ? true : state);
4360				}
4361			},
4362
4363
4364			/**
4365			Reposition and resize dialog trigger to match the position and size of browse_button element.
4366
4367			@method refresh
4368			*/
4369			refresh: function() {
4370				self.trigger("Refresh");
4371			},
4372
4373
4374			/**
4375			Destroy component.
4376
4377			@method destroy
4378			*/
4379			destroy: function() {
4380				var runtime = this.getRuntime();
4381				if (runtime) {
4382					runtime.exec.call(this, 'FileInput', 'destroy');
4383					this.disconnectRuntime();
4384				}
4385
4386				if (Basic.typeOf(this.files) === 'array') {
4387					// no sense in leaving associated files behind
4388					Basic.each(this.files, function(file) {
4389						file.destroy();
4390					});
4391				} 
4392				this.files = null;
4393			}
4394		});
4395	}
4396
4397	FileInput.prototype = EventTarget.instance;
4398
4399	return FileInput;
4400});
4401
4402// Included from: src/javascript/file/FileDrop.js
4403
4404/**
4405 * FileDrop.js
4406 *
4407 * Copyright 2013, Moxiecode Systems AB
4408 * Released under GPL License.
4409 *
4410 * License: http://www.plupload.com/license
4411 * Contributing: http://www.plupload.com/contributing
4412 */
4413
4414define('moxie/file/FileDrop', [
4415	'moxie/core/I18n',
4416	'moxie/core/utils/Dom',
4417	'moxie/core/Exceptions',
4418	'moxie/core/utils/Basic',
4419	'moxie/file/File',
4420	'moxie/runtime/RuntimeClient',
4421	'moxie/core/EventTarget',
4422	'moxie/core/utils/Mime'
4423], function(I18n, Dom, x, Basic, File, RuntimeClient, EventTarget, Mime) {
4424	/**
4425	Turn arbitrary DOM element to a drop zone accepting files. Converts selected files to _File_ objects, to be used 
4426	in conjunction with _Image_, preloaded in memory with _FileReader_ or uploaded to a server through 
4427	_XMLHttpRequest_.
4428
4429	@example
4430		<div id="drop_zone">
4431			Drop files here
4432		</div>
4433		<br />
4434		<div id="filelist"></div>
4435
4436		<script type="text/javascript">
4437			var fileDrop = new mOxie.FileDrop('drop_zone'), fileList = mOxie.get('filelist');
4438
4439			fileDrop.ondrop = function() {
4440				mOxie.each(this.files, function(file) {
4441					fileList.innerHTML += '<div>' + file.name + '</div>';
4442				});
4443			};
4444
4445			fileDrop.init();
4446		</script>
4447
4448	@class FileDrop
4449	@constructor
4450	@extends EventTarget
4451	@uses RuntimeClient
4452	@param {Object|String} options If options has typeof string, argument is considered as options.drop_zone
4453		@param {String|DOMElement} options.drop_zone DOM Element to turn into a drop zone
4454		@param {Array} [options.accept] Array of mime types to accept. By default accepts all
4455		@param {Object|String} [options.required_caps] Set of required capabilities, that chosen runtime must support
4456	*/
4457	var dispatches = [
4458		/**
4459		Dispatched when runtime is connected and drop zone is ready to accept files.
4460
4461		@event ready
4462		@param {Object} event
4463		*/
4464		'ready', 
4465
4466		/**
4467		Dispatched when dragging cursor enters the drop zone.
4468
4469		@event dragenter
4470		@param {Object} event
4471		*/
4472		'dragenter',
4473
4474		/**
4475		Dispatched when dragging cursor leaves the drop zone.
4476
4477		@event dragleave
4478		@param {Object} event
4479		*/
4480		'dragleave', 
4481
4482		/**
4483		Dispatched when file is dropped onto the drop zone.
4484
4485		@event drop
4486		@param {Object} event
4487		*/
4488		'drop', 
4489
4490		/**
4491		Dispatched if error occurs.
4492
4493		@event error
4494		@param {Object} event
4495		*/
4496		'error'
4497	];
4498
4499	function FileDrop(options) {
4500		var self = this, defaults;
4501
4502		// if flat argument passed it should be drop_zone id
4503		if (typeof(options) === 'string') {
4504			options = { drop_zone : options };
4505		}
4506
4507		// figure out the options
4508		defaults = {
4509			accept: [{
4510				title: I18n.translate('All Files'),
4511				extensions: '*'
4512			}],
4513			required_caps: {
4514				drag_and_drop: true
4515			}
4516		};
4517		
4518		options = typeof(options) === 'object' ? Basic.extend({}, defaults, options) : defaults;
4519
4520		// this will help us to find proper default container
4521		options.container = Dom.get(options.drop_zone) || document.body;
4522
4523		// make container relative, if it is not
4524		if (Dom.getStyle(options.container, 'position') === 'static') {
4525			options.container.style.position = 'relative';
4526		}
4527					
4528		// normalize accept option (could be list of mime types or array of title/extensions pairs)
4529		if (typeof(options.accept) === 'string') {
4530			options.accept = Mime.mimes2extList(options.accept);
4531		}
4532
4533		RuntimeClient.call(self);
4534
4535		Basic.extend(self, {
4536			uid: Basic.guid('uid_'),
4537
4538			ruid: null,
4539
4540			files: null,
4541
4542			init: function() {
4543	
4544				self.convertEventPropsToHandlers(dispatches);
4545		
4546				self.bind('RuntimeInit', function(e, runtime) {
4547					self.ruid = runtime.uid;
4548
4549					self.bind("Drop", function() {
4550						var files = runtime.exec.call(self, 'FileDrop', 'getFiles');
4551
4552						self.files = [];
4553
4554						Basic.each(files, function(file) {
4555							self.files.push(new File(self.ruid, file));
4556						});
4557					}, 999);
4558
4559					runtime.exec.call(self, 'FileDrop', 'init', options);
4560
4561					self.dispatchEvent('ready');
4562				});
4563							
4564				// runtime needs: options.required_features, options.runtime_order and options.container
4565				self.connectRuntime(options); // throws RuntimeError
4566			},
4567
4568			destroy: function() {
4569				var runtime = this.getRuntime();
4570				if (runtime) {
4571					runtime.exec.call(this, 'FileDrop', 'destroy');
4572					this.disconnectRuntime();
4573				}
4574				this.files = null;
4575			}
4576		});
4577	}
4578
4579	FileDrop.prototype = EventTarget.instance;
4580
4581	return FileDrop;
4582});
4583
4584// Included from: src/javascript/runtime/RuntimeTarget.js
4585
4586/**
4587 * RuntimeTarget.js
4588 *
4589 * Copyright 2013, Moxiecode Systems AB
4590 * Released under GPL License.
4591 *
4592 * License: http://www.plupload.com/license
4593 * Contributing: http://www.plupload.com/contributing
4594 */
4595
4596define('moxie/runtime/RuntimeTarget', [
4597	'moxie/core/utils/Basic',
4598	'moxie/runtime/RuntimeClient',
4599	"moxie/core/EventTarget"
4600], function(Basic, RuntimeClient, EventTarget) {
4601	/**
4602	Instance of this class can be used as a target for the events dispatched by shims,
4603	when allowing them onto components is for either reason inappropriate
4604
4605	@class RuntimeTarget
4606	@constructor
4607	@protected
4608	@extends EventTarget
4609	*/
4610	function RuntimeTarget() {
4611		this.uid = Basic.guid('uid_');
4612		
4613		RuntimeClient.call(this);
4614
4615		this.destroy = function() {
4616			this.disconnectRuntime();
4617			this.unbindAll();
4618		};
4619	}
4620
4621	RuntimeTarget.prototype = EventTarget.instance;
4622
4623	return RuntimeTarget;
4624});
4625
4626// Included from: src/javascript/file/FileReader.js
4627
4628/**
4629 * FileReader.js
4630 *
4631 * Copyright 2013, Moxiecode Systems AB
4632 * Released under GPL License.
4633 *
4634 * License: http://www.plupload.com/license
4635 * Contributing: http://www.plupload.com/contributing
4636 */
4637
4638define('moxie/file/FileReader', [
4639	'moxie/core/utils/Basic',
4640	'moxie/core/utils/Encode',
4641	'moxie/core/Exceptions',
4642	'moxie/core/EventTarget',
4643	'moxie/file/Blob',
4644	'moxie/file/File',
4645	'moxie/runtime/RuntimeTarget'
4646], function(Basic, Encode, x, EventTarget, Blob, File, RuntimeTarget) {
4647	/**
4648	Utility for preloading o.Blob/o.File objects in memory. By design closely follows [W3C FileReader](http://www.w3.org/TR/FileAPI/#dfn-filereader)
4649	interface. Where possible uses native FileReader, where - not falls back to shims.
4650
4651	@class FileReader
4652	@constructor FileReader
4653	@extends EventTarget
4654	@uses RuntimeClient
4655	*/
4656	var dispatches = [
4657
4658		/** 
4659		Dispatched when the read starts.
4660
4661		@event loadstart
4662		@param {Object} event
4663		*/
4664		'loadstart', 
4665
4666		/** 
4667		Dispatched while reading (and decoding) blob, and reporting partial Blob data (progess.loaded/progress.total).
4668
4669		@event progress
4670		@param {Object} event
4671		*/
4672		'progress', 
4673
4674		/** 
4675		Dispatched when the read has successfully completed.
4676
4677		@event load
4678		@param {Object} event
4679		*/
4680		'load', 
4681
4682		/** 
4683		Dispatched when the read has been aborted. For instance, by invoking the abort() method.
4684
4685		@event abort
4686		@param {Object} event
4687		*/
4688		'abort', 
4689
4690		/** 
4691		Dispatched when the read has failed.
4692
4693		@event error
4694		@param {Object} event
4695		*/
4696		'error', 
4697
4698		/** 
4699		Dispatched when the request has completed (either in success or failure).
4700
4701		@event loadend
4702		@param {Object} event
4703		*/
4704		'loadend'
4705	];
4706	
4707	function FileReader() {
4708		var self = this, _fr;
4709				
4710		Basic.extend(this, {
4711			/**
4712			UID of the component instance.
4713
4714			@property uid
4715			@type {String}
4716			*/
4717			uid: Basic.guid('uid_'),
4718
4719			/**
4720			Contains current state of FileReader object. Can take values of FileReader.EMPTY, FileReader.LOADING
4721			and FileReader.DONE.
4722
4723			@property readyState
4724			@type {Number}
4725			@default FileReader.EMPTY
4726			*/
4727			readyState: FileReader.EMPTY,
4728			
4729			/**
4730			Result of the successful read operation.
4731
4732			@property result
4733			@type {String}
4734			*/
4735			result: null,
4736			
4737			/**
4738			Stores the error of failed asynchronous read operation.
4739
4740			@property error
4741			@type {DOMError}
4742			*/
4743			error: null,
4744			
4745			/**
4746			Initiates reading of File/Blob object contents to binary string.
4747
4748			@method readAsBinaryString
4749			@param {Blob|File} blob Object to preload
4750			*/
4751			readAsBinaryString: function(blob) {
4752				_read.call(this, 'readAsBinaryString', blob);
4753			},
4754			
4755			/**
4756			Initiates reading of File/Blob object contents to dataURL string.
4757
4758			@method readAsDataURL
4759			@param {Blob|File} blob Object to preload
4760			*/
4761			readAsDataURL: function(blob) {
4762				_read.call(this, 'readAsDataURL', blob);
4763			},
4764			
4765			/**
4766			Initiates reading of File/Blob object contents to string.
4767
4768			@method readAsText
4769			@param {Blob|File} blob Object to preload
4770			*/
4771			readAsText: function(blob) {
4772				_read.call(this, 'readAsText', blob);
4773			},
4774			
4775			/**
4776			Aborts preloading process.
4777
4778			@method abort
4779			*/
4780			abort: function() {
4781				this.result = null;
4782				
4783				if (Basic.inArray(this.readyState, [FileReader.EMPTY, FileReader.DONE]) !== -1) {
4784					return;
4785				} else if (this.readyState === FileReader.LOADING) {
4786					this.readyState = FileReader.DONE;
4787				}
4788
4789				if (_fr) {
4790					_fr.getRuntime().exec.call(this, 'FileReader', 'abort');
4791				}
4792				
4793				this.trigger('abort');
4794				this.trigger('loadend');
4795			},
4796
4797			/**
4798			Destroy component and release resources.
4799
4800			@method destroy
4801			*/
4802			destroy: function() {
4803				this.abort();
4804
4805				if (_fr) {
4806					_fr.getRuntime().exec.call(this, 'FileReader', 'destroy');
4807					_fr.disconnectRuntime();
4808				}
4809
4810				self = _fr = null;
4811			}
4812		});
4813		
4814		
4815		function _read(op, blob) {
4816			_fr = new RuntimeTarget();
4817
4818			function error(err) {
4819				self.readyState = FileReader.DONE;
4820				self.error = err;
4821				self.trigger('error');
4822				loadEnd();
4823			}
4824
4825			function loadEnd() {
4826				_fr.destroy();
4827				_fr = null;
4828				self.trigger('loadend');
4829			}
4830
4831			function exec(runtime) {
4832				_fr.bind('Error', function(e, err) {
4833					error(err);
4834				});
4835
4836				_fr.bind('Progress', function(e) {
4837					self.result = runtime.exec.call(_fr, 'FileReader', 'getResult');
4838					self.trigger(e);
4839				});
4840				
4841				_fr.bind('Load', function(e) {
4842					self.readyState = FileReader.DONE;
4843					self.result = runtime.exec.call(_fr, 'FileReader', 'getResult');
4844					self.trigger(e);
4845					loadEnd();
4846				});
4847
4848				runtime.exec.call(_fr, 'FileReader', 'read', op, blob);
4849			}
4850
4851			this.convertEventPropsToHandlers(dispatches);
4852
4853			if (this.readyState === FileReader.LOADING) {
4854				return error(new x.DOMException(x.DOMException.INVALID_STATE_ERR));
4855			}
4856
4857			this.readyState = FileReader.LOADING;
4858			this.trigger('loadstart');
4859
4860			// if source is o.Blob/o.File
4861			if (blob instanceof Blob) {
4862				if (blob.isDetached()) {
4863					var src = blob.getSource();
4864					switch (op) {
4865						case 'readAsText':
4866						case 'readAsBinaryString':
4867							this.result = src;
4868							break;
4869						case 'readAsDataURL':
4870							this.result = 'data:' + blob.type + ';base64,' + Encode.btoa(src);
4871							break;
4872					}
4873					this.readyState = FileReader.DONE;
4874					this.trigger('load');
4875					loadEnd();
4876				} else {
4877					exec(_fr.connectRuntime(blob.ruid));
4878				}
4879			} else {
4880				error(new x.DOMException(x.DOMException.NOT_FOUND_ERR));
4881			}
4882		}
4883	}
4884	
4885	/**
4886	Initial FileReader state
4887
4888	@property EMPTY
4889	@type {Number}
4890	@final
4891	@static
4892	@default 0
4893	*/
4894	FileReader.EMPTY = 0;
4895
4896	/**
4897	FileReader switches to this state when it is preloading the source
4898
4899	@property LOADING
4900	@type {Number}
4901	@final
4902	@static
4903	@default 1
4904	*/
4905	FileReader.LOADING = 1;
4906
4907	/**
4908	Preloading is complete, this is a final state
4909
4910	@property DONE
4911	@type {Number}
4912	@final
4913	@static
4914	@default 2
4915	*/
4916	FileReader.DONE = 2;
4917
4918	FileReader.prototype = EventTarget.instance;
4919
4920	return FileReader;
4921});
4922
4923// Included from: src/javascript/core/utils/Url.js
4924
4925/**
4926 * Url.js
4927 *
4928 * Copyright 2013, Moxiecode Systems AB
4929 * Released under GPL License.
4930 *
4931 * License: http://www.plupload.com/license
4932 * Contributing: http://www.plupload.com/contributing
4933 */
4934
4935define('moxie/core/utils/Url', [], function() {
4936	/**
4937	Parse url into separate components and fill in absent parts with parts from current url,
4938	based on https://raw.github.com/kvz/phpjs/master/functions/url/parse_url.js
4939
4940	@method parseUrl
4941	@for Utils
4942	@static
4943	@param {String} url Url to parse (defaults to empty string if undefined)
4944	@return {Object} Hash containing extracted uri components
4945	*/
4946	var parseUrl = function(url, currentUrl) {
4947		var key = ['source', 'scheme', 'authority', 'userInfo', 'user', 'pass', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'fragment']
4948		, i = key.length
4949		, ports = {
4950			http: 80,
4951			https: 443
4952		}
4953		, uri = {}
4954		, regex = /^(?:([^:\/?#]+):)?(?:\/\/()(?:(?:()(?:([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?()(?:(()(?:(?:[^?#\/]*\/)*)()(?:[^?#]*))(?:\\?([^#]*))?(?:#(.*))?)/
4955		, m = regex.exec(url || '')
4956		;
4957					
4958		while (i--) {
4959			if (m[i]) {
4960				uri[key[i]] = m[i];
4961			}
4962		}
4963
4964		// when url is relative, we set the origin and the path ourselves
4965		if (!uri.scheme) {
4966			// come up with defaults
4967			if (!currentUrl || typeof(currentUrl) === 'string') {
4968				currentUrl = parseUrl(currentUrl || document.location.href);
4969			}
4970
4971			uri.scheme = currentUrl.scheme;
4972			uri.host = currentUrl.host;
4973			uri.port = currentUrl.port;
4974
4975			var path = '';
4976			// for urls without trailing slash we need to figure out the path
4977			if (/^[^\/]/.test(uri.path)) {
4978				path = currentUrl.path;
4979				// if path ends with a filename, strip it
4980				if (!/(\/|\/[^\.]+)$/.test(path)) {
4981					path = path.replace(/\/[^\/]+$/, '/');
4982				} else {
4983					path += '/';
4984				}
4985			}
4986			uri.path = path + (uri.path || ''); // site may reside at domain.com or domain.com/subdir
4987		}
4988
4989		if (!uri.port) {
4990			uri.port = ports[uri.scheme] || 80;
4991		} 
4992		
4993		uri.port = parseInt(uri.port, 10);
4994
4995		if (!uri.path) {
4996			uri.path = "/";
4997		}
4998
4999		delete uri.source;
5000
5001		return uri;
5002	};
5003
5004	/**
5005	Resolve url - among other things will turn relative url to absolute
5006
5007	@method resolveUrl
5008	@static
5009	@param {String} url Either absolute or relative
5010	@return {String} Resolved, absolute url
5011	*/
5012	var resolveUrl = function(url) {
5013		var ports = { // we ignore default ports
5014			http: 80,
5015			https: 443
5016		}
5017		, urlp = parseUrl(url)
5018		;
5019
5020		return urlp.scheme + '://' + urlp.host + (urlp.port !== ports[urlp.scheme] ? ':' + urlp.port : '') + urlp.path + (urlp.query ? urlp.query : '');
5021	};
5022
5023	/**
5024	Check if specified url has the same origin as the current document
5025
5026	@method hasSameOrigin
5027	@param {String|Object} url
5028	@return {Boolean}
5029	*/
5030	var hasSameOrigin = function(url) {
5031		function origin(url) {
5032			return [url.scheme, url.host, url.port].join('/');
5033		}
5034			
5035		if (typeof url === 'string') {
5036			url = parseUrl(url);
5037		}	
5038		
5039		return origin(parseUrl()) === origin(url);
5040	};
5041
5042	return {
5043		parseUrl: parseUrl,
5044		resolveUrl: resolveUrl,
5045		hasSameOrigin: hasSameOrigin
5046	};
5047});
5048
5049// Included from: src/javascript/file/FileReaderSync.js
5050
5051/**
5052 * FileReaderSync.js
5053 *
5054 * Copyright 2013, Moxiecode Systems AB
5055 * Released under GPL License.
5056 *
5057 * License: http://www.plupload.com/license
5058 * Contributing: http://www.plupload.com/contributing
5059 */
5060
5061define('moxie/file/FileReaderSync', [
5062	'moxie/core/utils/Basic',
5063	'moxie/runtime/RuntimeClient',
5064	'moxie/core/utils/Encode'
5065], function(Basic, RuntimeClient, Encode) {
5066	/**
5067	Synchronous FileReader implementation. Something like this is available in WebWorkers environment, here
5068	it can be used to read only preloaded blobs/files and only below certain size (not yet sure what that'd be,
5069	but probably < 1mb). Not meant to be used directly by user.
5070
5071	@class FileReaderSync
5072	@private
5073	@constructor
5074	*/
5075	return function() {
5076		RuntimeClient.call(this);
5077
5078		Basic.extend(this, {
5079			uid: Basic.guid('uid_'),
5080
5081			readAsBinaryString: function(blob) {
5082				return _read.call(this, 'readAsBinaryString', blob);
5083			},
5084			
5085			readAsDataURL: function(blob) {
5086				return _read.call(this, 'readAsDataURL', blob);
5087			},
5088			
5089			/*readAsArrayBuffer: function(blob) {
5090				return _read.call(this, 'readAsArrayBuffer', blob);
5091			},*/
5092			
5093			readAsText: function(blob) {
5094				return _read.call(this, 'readAsText', blob);
5095			}
5096		});
5097
5098		function _read(op, blob) {
5099			if (blob.isDetached()) {
5100				var src = blob.getSource();
5101				switch (op) {
5102					case 'readAsBinaryString':
5103						return src;
5104					case 'readAsDataURL':
5105						return 'data:' + blob.type + ';base64,' + Encode.btoa(src);
5106					case 'readAsText':
5107						var txt = '';
5108						for (var i = 0, length = src.length; i < length; i++) {
5109							txt += String.fromCharCode(src[i]);
5110						}
5111						return txt;
5112				}
5113			} else {
5114				var result = this.connectRuntime(blob.ruid).exec.call(this, 'FileReaderSync', 'read', op, blob);
5115				this.disconnectRuntime();
5116				return result;
5117			}
5118		}
5119	};
5120});
5121
5122// Included from: src/javascript/xhr/FormData.js
5123
5124/**
5125 * FormData.js
5126 *
5127 * Copyright 2013, Moxiecode Systems AB
5128 * Released under GPL License.
5129 *
5130 * License: http://www.plupload.com/license
5131 * Contributing: http://www.plupload.com/contributing
5132 */
5133
5134define("moxie/xhr/FormData", [
5135	"moxie/core/Exceptions",
5136	"moxie/core/utils/Basic",
5137	"moxie/file/Blob"
5138], function(x, Basic, Blob) {
5139	/**
5140	FormData
5141
5142	@class FormData
5143	@constructor
5144	*/
5145	function FormData() {
5146		var _blob, _fields = [];
5147
5148		Basic.extend(this, {
5149			/**
5150			Append another key-value pair to the FormData object
5151
5152			@method append
5153			@param {String} name Name for the new field
5154			@param {String|Blob|Array|Object} value Value for the field
5155			*/
5156			append: function(name, value) {
5157				var self = this, valueType = Basic.typeOf(value);
5158
5159				// according to specs value might be either Blob or String
5160				if (value instanceof Blob) {
5161					_blob = {
5162						name: name,
5163						value: value // unfortunately we can only send single Blob in one FormData
5164					};
5165				} else if ('array' === valueType) {
5166					name += '[]';
5167
5168					Basic.each(value, function(value) {
5169						self.append(name, value);
5170					});
5171				} else if ('object' === valueType) {
5172					Basic.each(value, function(value, key) {
5173						self.append(name + '[' + key + ']', value);
5174					});
5175				} else if ('null' === valueType || 'undefined' === valueType || 'number' === valueType && isNaN(value)) {
5176					self.append(name, "false");
5177				} else {
5178					_fields.push({
5179						name: name,
5180						value: value.toString()
5181					});
5182				}
5183			},
5184
5185			/**
5186			Checks if FormData contains Blob.
5187
5188			@method hasBlob
5189			@return {Boolean}
5190			*/
5191			hasBlob: function() {
5192				return !!this.getBlob();
5193			},
5194
5195			/**
5196			Retrieves blob.
5197
5198			@method getBlob
5199			@return {Object} Either Blob if found or null
5200			*/
5201			getBlob: function() {
5202				return _blob && _blob.value || null;
5203			},
5204
5205			/**
5206			Retrieves blob field name.
5207
5208			@method getBlobName
5209			@return {String} Either Blob field name or null
5210			*/
5211			getBlobName: function() {
5212				return _blob && _blob.name || null;
5213			},
5214
5215			/**
5216			Loop over the fields in FormData and invoke the callback for each of them.
5217
5218			@method each
5219			@param {Function} cb Callback to call for each field
5220			*/
5221			each: function(cb) {
5222				Basic.each(_fields, function(field) {
5223					cb(field.value, field.name);
5224				});
5225
5226				if (_blob) {
5227					cb(_blob.value, _blob.name);
5228				}
5229			},
5230
5231			destroy: function() {
5232				_blob = null;
5233				_fields = [];
5234			}
5235		});
5236	}
5237
5238	return FormData;
5239});
5240
5241// Included from: src/javascript/xhr/XMLHttpRequest.js
5242
5243/**
5244 * XMLHttpRequest.js
5245 *
5246 * Copyright 2013, Moxiecode Systems AB
5247 * Released under GPL License.
5248 *
5249 * License: http://www.plupload.com/license
5250 * Contributing: http://www.plupload.com/contributing
5251 */
5252
5253define("moxie/xhr/XMLHttpRequest", [
5254	"moxie/core/utils/Basic",
5255	"moxie/core/Exceptions",
5256	"moxie/core/EventTarget",
5257	"moxie/core/utils/Encode",
5258	"moxie/core/utils/Url",
5259	"moxie/runtime/Runtime",
5260	"moxie/runtime/RuntimeTarget",
5261	"moxie/file/Blob",
5262	"moxie/file/FileReaderSync",
5263	"moxie/xhr/FormData",
5264	"moxie/core/utils/Env",
5265	"moxie/core/utils/Mime"
5266], function(Basic, x, EventTarget, Encode, Url, Runtime, RuntimeTarget, Blob, FileReaderSync, FormData, Env, Mime) {
5267
5268	var httpCode = {
5269		100: 'Continue',
5270		101: 'Switching Protocols',
5271		102: 'Processing',
5272
5273		200: 'OK',
5274		201: 'Created',
5275		202: 'Accepted',
5276		203: 'Non-Authoritative Information',
5277		204: 'No Content',
5278		205: 'Reset Content',
5279		206: 'Partial Content',
5280		207: 'Multi-Status',
5281		226: 'IM Used',
5282
5283		300: 'Multiple Choices',
5284		301: 'Moved Permanently',
5285		302: 'Found',
5286		303: 'See Other',
5287		304: 'Not Modified',
5288		305: 'Use Proxy',
5289		306: 'Reserved',
5290		307: 'Temporary Redirect',
5291
5292		400: 'Bad Request',
5293		401: 'Unauthorized',
5294		402: 'Payment Required',
5295		403: 'Forbidden',
5296		404: 'Not Found',
5297		405: 'Method Not Allowed',
5298		406: 'Not Acceptable',
5299		407: 'Proxy Authentication Required',
5300		408: 'Request Timeout',
5301		409: 'Conflict',
5302		410: 'Gone',
5303		411: 'Length Required',
5304		412: 'Precondition Failed',
5305		413: 'Request Entity Too Large',
5306		414: 'Request-URI Too Long',
5307		415: 'Unsupported Media Type',
5308		416: 'Requested Range Not Satisfiable',
5309		417: 'Expectation Failed',
5310		422: 'Unprocessable Entity',
5311		423: 'Locked',
5312		424: 'Failed Dependency',
5313		426: 'Upgrade Required',
5314
5315		500: 'Internal Server Error',
5316		501: 'Not Implemented',
5317		502: 'Bad Gateway',
5318		503: 'Service Unavailable',
5319		504: 'Gateway Timeout',
5320		505: 'HTTP Version Not Supported',
5321		506: 'Variant Also Negotiates',
5322		507: 'Insufficient Storage',
5323		510: 'Not Extended'
5324	};
5325
5326	function XMLHttpRequestUpload() {
5327		this.uid = Basic.guid('uid_');
5328	}
5329	
5330	XMLHttpRequestUpload.prototype = EventTarget.instance;
5331
5332	/**
5333	Implementation of XMLHttpRequest
5334
5335	@class XMLHttpRequest
5336	@constructor
5337	@uses RuntimeClient
5338	@extends EventTarget
5339	*/
5340	var dispatches = ['loadstart', 'progress', 'abort', 'error', 'load', 'timeout', 'loadend']; // & readystatechange (for historical reasons)
5341	
5342	var NATIVE = 1, RUNTIME = 2;
5343					
5344	function XMLHttpRequest() {
5345		var self = this,
5346			// this (together with _p() @see below) is here to gracefully upgrade to setter/getter syntax where possible
5347			props = {
5348				/**
5349				The amount of milliseconds a request can take before being terminated. Initially zero. Zero means there is no timeout.
5350
5351				@property timeout
5352				@type Number
5353				@default 0
5354				*/
5355				timeout: 0,
5356
5357				/**
5358				Current state, can take following values:
5359				UNSENT (numeric value 0)
5360				The object has been constructed.
5361
5362				OPENED (numeric value 1)
5363				The open() method has been successfully invoked. During this state request headers can be set using setRequestHeader() and the request can be made using the send() method.
5364
5365				HEADERS_RECEIVED (numeric value 2)
5366				All redirects (if any) have been followed and all HTTP headers of the final response have been received. Several response members of the object are now available.
5367
5368				LOADING (numeric value 3)
5369				The response entity body is being received.
5370
5371				DONE (numeric value 4)
5372
5373				@property readyState
5374				@type Number
5375				@default 0 (UNSENT)
5376				*/
5377				readyState: XMLHttpRequest.UNSENT,
5378
5379				/**
5380				True when user credentials are to be included in a cross-origin request. False when they are to be excluded
5381				in a cross-origin request and when cookies are to be ignored in its response. Initially false.
5382
5383				@property withCredentials
5384				@type Boolean
5385				@default false
5386				*/
5387				withCredentials: false,
5388
5389				/**
5390				Returns the HTTP status code.
5391
5392				@property status
5393				@type Number
5394				@default 0
5395				*/
5396				status: 0,
5397
5398				/**
5399				Returns the HTTP status text.
5400
5401				@property statusText
5402				@type String
5403				*/
5404				statusText: "",
5405
5406				/**
5407				Returns the response type. Can be set to change the response type. Values are:
5408				the empty string (default), "arraybuffer", "blob", "document", "json", and "text".
5409				
5410				@property responseType
5411				@type String
5412				*/
5413				responseType: "",
5414
5415				/**
5416				Returns the document response entity body.
5417				
5418				Throws an "InvalidStateError" exception if responseType is not the empty string or "document".
5419
5420				@property responseXML
5421				@type Document
5422				*/
5423				responseXML: null,
5424
5425				/**
5426				Returns the text response entity body.
5427				
5428				Throws an "InvalidStateError" exception if responseType is not the empty string or "text".
5429
5430				@property responseText
5431				@type String
5432				*/
5433				responseText: null,
5434
5435				/**
5436				Returns the response entity body (http://www.w3.org/TR/XMLHttpRequest/#response-entity-body).
5437				Can become: ArrayBuffer, Blob, Document, JSON, Text
5438				
5439				@property response
5440				@type Mixed
5441				*/
5442				response: null
5443			},
5444
5445			_async = true,
5446			_url,
5447			_method,
5448			_headers = {},
5449			_user,
5450			_password,
5451			_encoding = null,
5452			_mimeType = null,
5453
5454			// flags
5455			_sync_flag = false,
5456			_send_flag = false,
5457			_upload_events_flag = false,
5458			_upload_complete_flag = false,
5459			_error_flag = false,
5460			_same_origin_flag = false,
5461
5462			// times
5463			_start_time,
5464			_timeoutset_time,
5465
5466			_finalMime = null,
5467			_finalCharset = null,
5468
5469			_options = {},
5470			_xhr,
5471			_responseHeaders = '',
5472			_responseHeadersBag
5473			;
5474
5475		
5476		Basic.extend(this, props, {
5477			/**
5478			Unique id of the component
5479
5480			@property uid
5481			@type String
5482			*/
5483			uid: Basic.guid('uid_'),
5484			
5485			/**
5486			Target for Upload events
5487
5488			@property upload
5489			@type XMLHttpRequestUpload
5490			*/
5491			upload: new XMLHttpRequestUpload(),
5492			
5493
5494			/**
5495			Sets the request method, request URL, synchronous flag, request username, and request password.
5496
5497			Throws a "SyntaxError" exception if one of the following is true:
5498
5499			method is not a valid HTTP method.
5500			url cannot be resolved.
5501			url contains the "user:password" format in the userinfo production.
5502			Throws a "SecurityError" exception if method is a case-insensitive match for CONNECT, TRACE or TRACK.
5503
5504			Throws an "InvalidAccessError" exception if one of the following is true:
5505
5506			Either user or password is passed as argument and the origin of url does not match the XMLHttpRequest origin.
5507			There is an associated XMLHttpRequest document and either the timeout attribute is not zero,
5508			the withCredentials attribute is true, or the responseType attribute is not the empty string.
5509
5510
5511			@method open
5512			@param {String} method HTTP method to use on request
5513			@param {String} url URL to request
5514			@param {Boolean} [async=true] If false request will be done in synchronous manner. Asynchronous by default.
5515			@param {String} [user] Username to use in HTTP authentication process on server-side
5516			@param {String} [password] Password to use in HTTP authentication process on server-side
5517			*/
5518			open: function(method, url, async, user, password) {
5519				var urlp;
5520				
5521				// first two arguments are required
5522				if (!method || !url) {
5523					throw new x.DOMException(x.DOMException.SYNTAX_ERR);
5524				}
5525				
5526				// 2 - check if any code point in method is higher than U+00FF or after deflating method it does not match the method
5527				if (/[\u0100-\uffff]/.test(method) || Encode.utf8_encode(method) !== method) {
5528					throw new x.DOMException(x.DOMException.SYNTAX_ERR);
5529				}
5530
5531				// 3
5532				if (!!~Basic.inArray(method.toUpperCase(), ['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'TRACE', 'TRACK'])) {
5533					_method = method.toUpperCase();
5534				}
5535				
5536				
5537				// 4 - allowing these methods poses a security risk
5538				if (!!~Basic.inArray(_method, ['CONNECT', 'TRACE', 'TRACK'])) {
5539					throw new x.DOMException(x.DOMException.SECURITY_ERR);
5540				}
5541
5542				// 5
5543				url = Encode.utf8_encode(url);
5544				
5545				// 6 - Resolve url relative to the XMLHttpRequest base URL. If the algorithm returns an error, throw a "SyntaxError".
5546				urlp = Url.parseUrl(url);
5547
5548				_same_origin_flag = Url.hasSameOrigin(urlp);
5549																
5550				// 7 - manually build up absolute url
5551				_url = Url.resolveUrl(url);
5552		
5553				// 9-10, 12-13
5554				if ((user || password) && !_same_origin_flag) {
5555					throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
5556				}
5557
5558				_user = user || urlp.user;
5559				_password = password || urlp.pass;
5560				
5561				// 11
5562				_async = async || true;
5563				
5564				if (_async === false && (_p('timeout') || _p('withCredentials') || _p('responseType') !== "")) {
5565					throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
5566				}
5567				
5568				// 14 - terminate abort()
5569				
5570				// 15 - terminate send()
5571
5572				// 18
5573				_sync_flag = !_async;
5574				_send_flag = false;
5575				_headers = {};
5576				_reset.call(this);
5577
5578				// 19
5579				_p('readyState', XMLHttpRequest.OPENED);
5580				
5581				// 20
5582				this.convertEventPropsToHandlers(['readystatechange']); // unify event handlers
5583				this.dispatchEvent('readystatechange');
5584			},
5585			
5586			/**
5587			Appends an header to the list of author request headers, or if header is already
5588			in the list of author request headers, combines its value with value.
5589
5590			Throws an "InvalidStateError" exception if the state is not OPENED or if the send() flag is set.
5591			Throws a "SyntaxError" exception if header is not a valid HTTP header field name or if value
5592			is not a valid HTTP header field value.
5593			
5594			@method setRequestHeader
5595			@param {String} header
5596			@param {String|Number} value
5597			*/
5598			setRequestHeader: function(header, value) {
5599				var uaHeaders = [ // these headers are controlled by the user agent
5600						"accept-charset",
5601						"accept-encoding",
5602						"access-control-request-headers",
5603						"access-control-request-method",
5604						"connection",
5605						"content-length",
5606						"cookie",
5607						"cookie2",
5608						"content-transfer-encoding",
5609						"date",
5610						"expect",
5611						"host",
5612						"keep-alive",
5613						"origin",
5614						"referer",
5615						"te",
5616						"trailer",
5617						"transfer-encoding",
5618						"upgrade",
5619						"user-agent",
5620						"via"
5621					];
5622				
5623				// 1-2
5624				if (_p('readyState') !== XMLHttpRequest.OPENED || _send_flag) {
5625					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
5626				}
5627
5628				// 3
5629				if (/[\u0100-\uffff]/.test(header) || Encode.utf8_encode(header) !== header) {
5630					throw new x.DOMException(x.DOMException.SYNTAX_ERR);
5631				}
5632
5633				// 4
5634				/* this step is seemingly bypassed in browsers, probably to allow various unicode characters in header values
5635				if (/[\u0100-\uffff]/.test(value) || Encode.utf8_encode(value) !== value) {
5636					throw new x.DOMException(x.DOMException.SYNTAX_ERR);
5637				}*/
5638
5639				header = Basic.trim(header).toLowerCase();
5640				
5641				// setting of proxy-* and sec-* headers is prohibited by spec
5642				if (!!~Basic.inArray(header, uaHeaders) || /^(proxy\-|sec\-)/.test(header)) {
5643					return false;
5644				}
5645
5646				// camelize
5647				// browsers lowercase header names (at least for custom ones)
5648				// header = header.replace(/\b\w/g, function($1) { return $1.toUpperCase(); });
5649				
5650				if (!_headers[header]) {
5651					_headers[header] = value;
5652				} else {
5653					// http://tools.ietf.org/html/rfc2616#section-4.2 (last paragraph)
5654					_headers[header] += ', ' + value;
5655				}
5656				return true;
5657			},
5658
5659			/**
5660			Returns all headers from the response, with the exception of those whose field name is Set-Cookie or Set-Cookie2.
5661
5662			@method getAllResponseHeaders
5663			@return {String} reponse headers or empty string
5664			*/
5665			getAllResponseHeaders: function() {
5666				return _responseHeaders || '';
5667			},
5668
5669			/**
5670			Returns the header field value from the response of which the field name matches header, 
5671			unless the field name is Set-Cookie or Set-Cookie2.
5672
5673			@method getResponseHeader
5674			@param {String} header
5675			@return {String} value(s) for the specified header or null
5676			*/
5677			getResponseHeader: function(header) {
5678				header = header.toLowerCase();
5679
5680				if (_error_flag || !!~Basic.inArray(header, ['set-cookie', 'set-cookie2'])) {
5681					return null;
5682				}
5683
5684				if (_responseHeaders && _responseHeaders !== '') {
5685					// if we didn't parse response headers until now, do it and keep for later
5686					if (!_responseHeadersBag) {
5687						_responseHeadersBag = {};
5688						Basic.each(_responseHeaders.split(/\r\n/), function(line) {
5689							var pair = line.split(/:\s+/);
5690							if (pair.length === 2) { // last line might be empty, omit
5691								pair[0] = Basic.trim(pair[0]); // just in case
5692								_responseHeadersBag[pair[0].toLowerCase()] = { // simply to retain header name in original form
5693									header: pair[0],
5694									value: Basic.trim(pair[1])
5695								};
5696							}
5697						});
5698					}
5699					if (_responseHeadersBag.hasOwnProperty(header)) {
5700						return _responseHeadersBag[header].header + ': ' + _responseHeadersBag[header].value;
5701					}
5702				}
5703				return null;
5704			},
5705			
5706			/**
5707			Sets the Content-Type header for the response to mime.
5708			Throws an "InvalidStateError" exception if the state is LOADING or DONE.
5709			Throws a "SyntaxError" exception if mime is not a valid media type.
5710
5711			@method overrideMimeType
5712			@param String mime Mime type to set
5713			*/
5714			overrideMimeType: function(mime) {
5715				var matches, charset;
5716			
5717				// 1
5718				if (!!~Basic.inArray(_p('readyState'), [XMLHttpRequest.LOADING, XMLHttpRequest.DONE])) {
5719					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
5720				}
5721
5722				// 2
5723				mime = Basic.trim(mime.toLowerCase());
5724
5725				if (/;/.test(mime) && (matches = mime.match(/^([^;]+)(?:;\scharset\=)?(.*)$/))) {
5726					mime = matches[1];
5727					if (matches[2]) {
5728						charset = matches[2];
5729					}
5730				}
5731
5732				if (!Mime.mimes[mime]) {
5733					throw new x.DOMException(x.DOMException.SYNTAX_ERR);
5734				}
5735
5736				// 3-4
5737				_finalMime = mime;
5738				_finalCharset = charset;
5739			},
5740			
5741			/**
5742			Initiates the request. The optional argument provides the request entity body.
5743			The argument is ignored if request method is GET or HEAD.
5744
5745			Throws an "InvalidStateError" exception if the state is not OPENED or if the send() flag is set.
5746
5747			@method send
5748			@param {Blob|Document|String|FormData} [data] Request entity body
5749			@param {Object} [options] Set of requirements and pre-requisities for runtime initialization
5750			*/
5751			send: function(data, options) {					
5752				if (Basic.typeOf(options) === 'string') {
5753					_options = { ruid: options };
5754				} else if (!options) {
5755					_options = {};
5756				} else {
5757					_options = options;
5758				}
5759													
5760				this.convertEventPropsToHandlers(dispatches);
5761				this.upload.convertEventPropsToHandlers(dispatches);
5762															
5763				// 1-2
5764				if (this.readyState !== XMLHttpRequest.OPENED || _send_flag) {
5765					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
5766				}
5767				
5768				// 3					
5769				// sending Blob
5770				if (data instanceof Blob) {
5771					_options.ruid = data.ruid;
5772					_mimeType = data.type || 'application/octet-stream';
5773				}
5774				
5775				// FormData
5776				else if (data instanceof FormData) {
5777					if (data.hasBlob()) {
5778						var blob = data.getBlob();
5779						_options.ruid = blob.ruid;
5780						_mimeType = blob.type || 'application/octet-stream';
5781					}
5782				}
5783				
5784				// DOMString
5785				else if (typeof data === 'string') {
5786					_encoding = 'UTF-8';
5787					_mimeType = 'text/plain;charset=UTF-8';
5788					
5789					// data should be converted to Unicode and encoded as UTF-8
5790					data = Encode.utf8_encode(data);
5791				}
5792
5793				// if withCredentials not set, but requested, set it automatically
5794				if (!this.withCredentials) {
5795					this.withCredentials = (_options.required_caps && _options.required_caps.send_browser_cookies) && !_same_origin_flag;
5796				}
5797
5798				// 4 - storage mutex
5799				// 5
5800				_upload_events_flag = (!_sync_flag && this.upload.hasEventListener()); // DSAP
5801				// 6
5802				_error_flag = false;
5803				// 7
5804				_upload_complete_flag = !data;
5805				// 8 - Asynchronous steps
5806				if (!_sync_flag) {
5807					// 8.1
5808					_send_flag = true;
5809					// 8.2
5810					// this.dispatchEvent('loadstart'); // will be dispatched either by native or runtime xhr
5811					// 8.3
5812					//if (!_upload_complete_flag) {
5813						// this.upload.dispatchEvent('loadstart');	// will be dispatched either by native or runtime xhr
5814					//}
5815				}
5816				// 8.5 - Return the send() method call, but continue running the steps in this algorithm.
5817				_doXHR.call(this, data);
5818			},
5819			
5820			/**
5821			Cancels any network activity.
5822			
5823			@method abort
5824			*/
5825			abort: function() {
5826				_error_flag = true;
5827				_sync_flag = false;
5828
5829				if (!~Basic.inArray(_p('readyState'), [XMLHttpRequest.UNSENT, XMLHttpRequest.OPENED, XMLHttpRequest.DONE])) {
5830					_p('readyState', XMLHttpRequest.DONE);
5831					_send_flag = false;
5832
5833					if (_xhr) {
5834						_xhr.getRuntime().exec.call(_xhr, 'XMLHttpRequest', 'abort', _upload_complete_flag);
5835					} else {
5836						throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
5837					}
5838
5839					_upload_complete_flag = true;
5840				} else {
5841					_p('readyState', XMLHttpRequest.UNSENT);
5842				}
5843			},
5844
5845			destroy: function() {
5846				if (_xhr) {
5847					if (Basic.typeOf(_xhr.destroy) === 'function') {
5848						_xhr.destroy();
5849					}
5850					_xhr = null;
5851				}
5852
5853				this.unbindAll();
5854
5855				if (this.upload) {
5856					this.upload.unbindAll();
5857					this.upload = null;
5858				}
5859			}
5860		});
5861
5862		/* this is nice, but maybe too lengthy
5863
5864		// if supported by JS version, set getters/setters for specific properties
5865		o.defineProperty(this, 'readyState', {
5866			configurable: false,
5867
5868			get: function() {
5869				return _p('readyState');
5870			}
5871		});
5872
5873		o.defineProperty(this, 'timeout', {
5874			configurable: false,
5875
5876			get: function() {
5877				return _p('timeout');
5878			},
5879
5880			set: function(value) {
5881
5882				if (_sync_flag) {
5883					throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
5884				}
5885
5886				// timeout still should be measured relative to the start time of request
5887				_timeoutset_time = (new Date).getTime();
5888
5889				_p('timeout', value);
5890			}
5891		});
5892
5893		// the withCredentials attribute has no effect when fetching same-origin resources
5894		o.defineProperty(this, 'withCredentials', {
5895			configurable: false,
5896
5897			get: function() {
5898				return _p('withCredentials');
5899			},
5900
5901			set: function(value) {
5902				// 1-2
5903				if (!~o.inArray(_p('readyState'), [XMLHttpRequest.UNSENT, XMLHttpRequest.OPENED]) || _send_flag) {
5904					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
5905				}
5906
5907				// 3-4
5908				if (_anonymous_flag || _sync_flag) {
5909					throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
5910				}
5911
5912				// 5
5913				_p('withCredentials', value);
5914			}
5915		});
5916
5917		o.defineProperty(this, 'status', {
5918			configurable: false,
5919
5920			get: function() {
5921				return _p('status');
5922			}
5923		});
5924
5925		o.defineProperty(this, 'statusText', {
5926			configurable: false,
5927
5928			get: function() {
5929				return _p('statusText');
5930			}
5931		});
5932
5933		o.defineProperty(this, 'responseType', {
5934			configurable: false,
5935
5936			get: function() {
5937				return _p('responseType');
5938			},
5939
5940			set: function(value) {
5941				// 1
5942				if (!!~o.inArray(_p('readyState'), [XMLHttpRequest.LOADING, XMLHttpRequest.DONE])) {
5943					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
5944				}
5945
5946				// 2
5947				if (_sync_flag) {
5948					throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
5949				}
5950
5951				// 3
5952				_p('responseType', value.toLowerCase());
5953			}
5954		});
5955
5956		o.defineProperty(this, 'responseText', {
5957			configurable: false,
5958
5959			get: function() {
5960				// 1
5961				if (!~o.inArray(_p('responseType'), ['', 'text'])) {
5962					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
5963				}
5964
5965				// 2-3
5966				if (_p('readyState') !== XMLHttpRequest.DONE && _p('readyState') !== XMLHttpRequest.LOADING || _error_flag) {
5967					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
5968				}
5969
5970				return _p('responseText');
5971			}
5972		});
5973
5974		o.defineProperty(this, 'responseXML', {
5975			configurable: false,
5976
5977			get: function() {
5978				// 1
5979				if (!~o.inArray(_p('responseType'), ['', 'document'])) {
5980					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
5981				}
5982
5983				// 2-3
5984				if (_p('readyState') !== XMLHttpRequest.DONE || _error_flag) {
5985					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
5986				}
5987
5988				return _p('responseXML');
5989			}
5990		});
5991
5992		o.defineProperty(this, 'response', {
5993			configurable: false,
5994
5995			get: function() {
5996				if (!!~o.inArray(_p('responseType'), ['', 'text'])) {
5997					if (_p('readyState') !== XMLHttpRequest.DONE && _p('readyState') !== XMLHttpRequest.LOADING || _error_flag) {
5998						return '';
5999					}
6000				}
6001
6002				if (_p('readyState') !== XMLHttpRequest.DONE || _error_flag) {
6003					return null;
6004				}
6005
6006				return _p('response');
6007			}
6008		});
6009
6010		*/
6011
6012		function _p(prop, value) {
6013			if (!props.hasOwnProperty(prop)) {
6014				return;
6015			}
6016			if (arguments.length === 1) { // get
6017				return Env.can('define_property') ? props[prop] : self[prop];
6018			} else { // set
6019				if (Env.can('define_property')) {
6020					props[prop] = value;
6021				} else {
6022					self[prop] = value;
6023				}
6024			}
6025		}
6026		
6027		/*
6028		function _toASCII(str, AllowUnassigned, UseSTD3ASCIIRules) {
6029			// TODO: http://tools.ietf.org/html/rfc3490#section-4.1
6030			return str.toLowerCase();
6031		}
6032		*/
6033		
6034		
6035		function _doXHR(data) {
6036			var self = this;
6037			
6038			_start_time = new Date().getTime();
6039
6040			_xhr = new RuntimeTarget();
6041
6042			function loadEnd() {
6043				if (_xhr) { // it could have been destroyed by now
6044					_xhr.destroy();
6045					_xhr = null;
6046				}
6047				self.dispatchEvent('loadend');
6048				self = null;
6049			}
6050
6051			function exec(runtime) {
6052				_xhr.bind('LoadStart', function(e) {
6053					_p('readyState', XMLHttpRequest.LOADING);
6054					self.dispatchEvent('readystatechange');
6055
6056					self.dispatchEvent(e);
6057					
6058					if (_upload_events_flag) {
6059						self.upload.dispatchEvent(e);
6060					}
6061				});
6062				
6063				_xhr.bind('Progress', function(e) {
6064					if (_p('readyState') !== XMLHttpRequest.LOADING) {
6065						_p('readyState', XMLHttpRequest.LOADING); // LoadStart unreliable (in Flash for example)
6066						self.dispatchEvent('readystatechange');
6067					}
6068					self.dispatchEvent(e);
6069				});
6070				
6071				_xhr.bind('UploadProgress', function(e) {
6072					if (_upload_events_flag) {
6073						self.upload.dispatchEvent({
6074							type: 'progress',
6075							lengthComputable: false,
6076							total: e.total,
6077							loaded: e.loaded
6078						});
6079					}
6080				});
6081				
6082				_xhr.bind('Load', function(e) {
6083					_p('readyState', XMLHttpRequest.DONE);
6084					_p('status', Number(runtime.exec.call(_xhr, 'XMLHttpRequest', 'getStatus') || 0));
6085					_p('statusText', httpCode[_p('status')] || "");
6086					
6087					_p('response', runtime.exec.call(_xhr, 'XMLHttpRequest', 'getResponse', _p('responseType')));
6088
6089					if (!!~Basic.inArray(_p('responseType'), ['text', ''])) {
6090						_p('responseText', _p('response'));
6091					} else if (_p('responseType') === 'document') {
6092						_p('responseXML', _p('response'));
6093					}
6094
6095					_responseHeaders = runtime.exec.call(_xhr, 'XMLHttpRequest', 'getAllResponseHeaders');
6096
6097					self.dispatchEvent('readystatechange');
6098					
6099					if (_p('status') > 0) { // status 0 usually means that server is unreachable
6100						if (_upload_events_flag) {
6101							self.upload.dispatchEvent(e);
6102						}
6103						self.dispatchEvent(e);
6104					} else {
6105						_error_flag = true;
6106						self.dispatchEvent('error');
6107					}
6108					loadEnd();
6109				});
6110
6111				_xhr.bind('Abort', function(e) {
6112					self.dispatchEvent(e);
6113					loadEnd();
6114				});
6115				
6116				_xhr.bind('Error', function(e) {
6117					_error_flag = true;
6118					_p('readyState', XMLHttpRequest.DONE);
6119					self.dispatchEvent('readystatechange');
6120					_upload_complete_flag = true;
6121					self.dispatchEvent(e);
6122					loadEnd();
6123				});
6124
6125				runtime.exec.call(_xhr, 'XMLHttpRequest', 'send', {
6126					url: _url,
6127					method: _method,
6128					async: _async,
6129					user: _user,
6130					password: _password,
6131					headers: _headers,
6132					mimeType: _mimeType,
6133					encoding: _encoding,
6134					responseType: self.responseType,
6135					withCredentials: self.withCredentials,
6136					options: _options
6137				}, data);
6138			}
6139
6140			// clarify our requirements
6141			if (typeof(_options.required_caps) === 'string') {
6142				_options.required_caps = Runtime.parseCaps(_options.required_caps);
6143			}
6144
6145			_options.required_caps = Basic.extend({}, _options.required_caps, {
6146				return_response_type: self.responseType
6147			});
6148
6149			if (data instanceof FormData) {
6150				_options.required_caps.send_multipart = true;
6151			}
6152
6153			if (!_same_origin_flag) {
6154				_options.required_caps.do_cors = true;
6155			}
6156			
6157
6158			if (_options.ruid) { // we do not need to wait if we can connect directly
6159				exec(_xhr.connectRuntime(_options));
6160			} else {
6161				_xhr.bind('RuntimeInit', function(e, runtime) {
6162					exec(runtime);
6163				});
6164				_xhr.bind('RuntimeError', function(e, err) {
6165					self.dispatchEvent('RuntimeError', err);
6166				});
6167				_xhr.connectRuntime(_options);
6168			}
6169		}
6170	
6171		
6172		function _reset() {
6173			_p('responseText', "");
6174			_p('responseXML', null);
6175			_p('response', null);
6176			_p('status', 0);
6177			_p('statusText', "");
6178			_start_time = _timeoutset_time = null;
6179		}
6180	}
6181
6182	XMLHttpRequest.UNSENT = 0;
6183	XMLHttpRequest.OPENED = 1;
6184	XMLHttpRequest.HEADERS_RECEIVED = 2;
6185	XMLHttpRequest.LOADING = 3;
6186	XMLHttpRequest.DONE = 4;
6187	
6188	XMLHttpRequest.prototype = EventTarget.instance;
6189
6190	return XMLHttpRequest;
6191});
6192
6193// Included from: src/javascript/runtime/Transporter.js
6194
6195/**
6196 * Transporter.js
6197 *
6198 * Copyright 2013, Moxiecode Systems AB
6199 * Released under GPL License.
6200 *
6201 * License: http://www.plupload.com/license
6202 * Contributing: http://www.plupload.com/contributing
6203 */
6204
6205define("moxie/runtime/Transporter", [
6206	"moxie/core/utils/Basic",
6207	"moxie/core/utils/Encode",
6208	"moxie/runtime/RuntimeClient",
6209	"moxie/core/EventTarget"
6210], function(Basic, Encode, RuntimeClient, EventTarget) {
6211	function Transporter() {
6212		var mod, _runtime, _data, _size, _pos, _chunk_size;
6213
6214		RuntimeClient.call(this);
6215
6216		Basic.extend(this, {
6217			uid: Basic.guid('uid_'),
6218
6219			state: Transporter.IDLE,
6220
6221			result: null,
6222
6223			transport: function(data, type, options) {
6224				var self = this;
6225
6226				options = Basic.extend({
6227					chunk_size: 204798
6228				}, options);
6229
6230				// should divide by three, base64 requires this
6231				if ((mod = options.chunk_size % 3)) {
6232					options.chunk_size += 3 - mod;
6233				}
6234
6235				_chunk_size = options.chunk_size;
6236
6237				_reset.call(this);
6238				_data = data;
6239				_size = data.length;
6240
6241				if (Basic.typeOf(options) === 'string' || options.ruid) {
6242					_run.call(self, type, this.connectRuntime(options));
6243				} else {
6244					// we require this to run only once
6245					var cb = function(e, runtime) {
6246						self.unbind("RuntimeInit", cb);
6247						_run.call(self, type, runtime);
6248					};
6249					this.bind("RuntimeInit", cb);
6250					this.connectRuntime(options);
6251				}
6252			},
6253
6254			abort: function() {
6255				var self = this;
6256
6257				self.state = Transporter.IDLE;
6258				if (_runtime) {
6259					_runtime.exec.call(self, 'Transporter', 'clear');
6260					self.trigger("TransportingAborted");
6261				}
6262
6263				_reset.call(self);
6264			},
6265
6266
6267			destroy: function() {
6268				this.unbindAll();
6269				_runtime = null;
6270				this.disconnectRuntime();
6271				_reset.call(this);
6272			}
6273		});
6274
6275		function _reset() {
6276			_size = _pos = 0;
6277			_data = this.result = null;
6278		}
6279
6280		function _run(type, runtime) {
6281			var self = this;
6282
6283			_runtime = runtime;
6284
6285			//self.unbind("RuntimeInit");
6286
6287			self.bind("TransportingProgress", function(e) {
6288				_pos = e.loaded;
6289
6290				if (_pos < _size && Basic.inArray(self.state, [Transporter.IDLE, Transporter.DONE]) === -1) {
6291					_transport.call(self);
6292				}
6293			}, 999);
6294
6295			self.bind("TransportingComplete", function() {
6296				_pos = _size;
6297				self.state = Transporter.DONE;
6298				_data = null; // clean a bit
6299				self.result = _runtime.exec.call(self, 'Transporter', 'getAsBlob', type || '');
6300			}, 999);
6301
6302			self.state = Transporter.BUSY;
6303			self.trigger("TransportingStarted");
6304			_transport.call(self);
6305		}
6306
6307		function _transport() {
6308			var self = this,
6309				chunk,
6310				bytesLeft = _size - _pos;
6311
6312			if (_chunk_size > bytesLeft) {
6313				_chunk_size = bytesLeft;
6314			}
6315
6316			chunk = Encode.btoa(_data.substr(_pos, _chunk_size));
6317			_runtime.exec.call(self, 'Transporter', 'receive', chunk, _size);
6318		}
6319	}
6320
6321	Transporter.IDLE = 0;
6322	Transporter.BUSY = 1;
6323	Transporter.DONE = 2;
6324
6325	Transporter.prototype = EventTarget.instance;
6326
6327	return Transporter;
6328});
6329
6330// Included from: src/javascript/image/Image.js
6331
6332/**
6333 * Image.js
6334 *
6335 * Copyright 2013, Moxiecode Systems AB
6336 * Released under GPL License.
6337 *
6338 * License: http://www.plupload.com/license
6339 * Contributing: http://www.plupload.com/contributing
6340 */
6341
6342define("moxie/image/Image", [
6343	"moxie/core/utils/Basic",
6344	"moxie/core/utils/Dom",
6345	"moxie/core/Exceptions",
6346	"moxie/file/FileReaderSync",
6347	"moxie/xhr/XMLHttpRequest",
6348	"moxie/runtime/Runtime",
6349	"moxie/runtime/RuntimeClient",
6350	"moxie/runtime/Transporter",
6351	"moxie/core/utils/Env",
6352	"moxie/core/EventTarget",
6353	"moxie/file/Blob",
6354	"moxie/file/File",
6355	"moxie/core/utils/Encode"
6356], function(Basic, Dom, x, FileReaderSync, XMLHttpRequest, Runtime, RuntimeClient, Transporter, Env, EventTarget, Blob, File, Encode) {
6357	/**
6358	Image preloading and manipulation utility. Additionally it provides access to image meta info (Exif, GPS) and raw binary data.
6359
6360	@class Image
6361	@constructor
6362	@extends EventTarget
6363	*/
6364	var dispatches = [
6365		'progress',
6366
6367		/**
6368		Dispatched when loading is complete.
6369
6370		@event load
6371		@param {Object} event
6372		*/
6373		'load',
6374
6375		'error',
6376
6377		/**
6378		Dispatched when resize operation is complete.
6379		
6380		@event resize
6381		@param {Object} event
6382		*/
6383		'resize',
6384
6385		/**
6386		Dispatched when visual representation of the image is successfully embedded
6387		into the corresponsing container.
6388
6389		@event embedded
6390		@param {Object} event
6391		*/
6392		'embedded'
6393	];
6394
6395	function Image() {
6396		RuntimeClient.call(this);
6397
6398		Basic.extend(this, {
6399			/**
6400			Unique id of the component
6401
6402			@property uid
6403			@type {String}
6404			*/
6405			uid: Basic.guid('uid_'),
6406
6407			/**
6408			Unique id of the connected runtime, if any.
6409
6410			@property ruid
6411			@type {String}
6412			*/
6413			ruid: null,
6414
6415			/**
6416			Name of the file, that was used to create an image, if available. If not equals to empty string.
6417
6418			@property name
6419			@type {String}
6420			@default ""
6421			*/
6422			name: "",
6423
6424			/**
6425			Size of the image in bytes. Actual value is set only after image is preloaded.
6426
6427			@property size
6428			@type {Number}
6429			@default 0
6430			*/
6431			size: 0,
6432
6433			/**
6434			Width of the image. Actual value is set only after image is preloaded.
6435
6436			@property width
6437			@type {Number}
6438			@default 0
6439			*/
6440			width: 0,
6441
6442			/**
6443			Height of the image. Actual value is set only after image is preloaded.
6444
6445			@property height
6446			@type {Number}
6447			@default 0
6448			*/
6449			height: 0,
6450
6451			/**
6452			Mime type of the image. Currently only image/jpeg and image/png are supported. Actual value is set only after image is preloaded.
6453
6454			@property type
6455			@type {String}
6456			@default ""
6457			*/
6458			type: "",
6459
6460			/**
6461			Holds meta info (Exif, GPS). Is populated only for image/jpeg. Actual value is set only after image is preloaded.
6462
6463			@property meta
6464			@type {Object}
6465			@default {}
6466			*/
6467			meta: {},
6468
6469			/**
6470			Alias for load method, that takes another mOxie.Image object as a source (see load).
6471
6472			@method clone
6473			@param {Image} src Source for the image
6474			@param {Boolean} [exact=false] Whether to activate in-depth clone mode
6475			*/
6476			clone: function() {
6477				this.load.apply(this, arguments);
6478			},
6479
6480			/**
6481			Loads image from various sources. Currently the source for new image can be: mOxie.Image, mOxie.Blob/mOxie.File, 
6482			native Blob/File, dataUrl or URL. Depending on the type of the source, arguments - differ. When source is URL, 
6483			Image will be downloaded from remote destination and loaded in memory.
6484
6485			@example
6486				var img = new mOxie.Image();
6487				img.onload = function() {
6488					var blob = img.getAsBlob();
6489					
6490					var formData = new mOxie.FormData();
6491					formData.append('file', blob);
6492
6493					var xhr = new mOxie.XMLHttpRequest();
6494					xhr.onload = function() {
6495						// upload complete
6496					};
6497					xhr.open('post', 'upload.php');
6498					xhr.send(formData);
6499				};
6500				img.load("http://www.moxiecode.com/images/mox-logo.jpg"); // notice file extension (.jpg)
6501			
6502
6503			@method load
6504			@param {Image|Blob|File|String} src Source for the image
6505			@param {Boolean|Object} [mixed]
6506			*/
6507			load: function() {
6508				// this is here because to bind properly we need an uid first, which is created above
6509				this.bind('Load Resize', function() {
6510					_updateInfo.call(this);
6511				}, 999);
6512
6513				this.convertEventPropsToHandlers(dispatches);
6514
6515				_load.apply(this, arguments);
6516			},
6517
6518			/**
6519			Downsizes the image to fit the specified width/height. If crop is supplied, image will be cropped to exact dimensions.
6520
6521			@method downsize
6522			@param {Number} width Resulting width
6523			@param {Number} [height=width] Resulting height (optional, if not supplied will default to width)
6524			@param {Boolean} [crop=false] Whether to crop the image to exact dimensions
6525			@param {Boolean} [preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize)
6526			*/
6527			downsize: function(opts) {
6528				var defaults = {
6529					width: this.width,
6530					height: this.height,
6531					crop: false,
6532					preserveHeaders: true
6533				};
6534
6535				if (typeof(opts) === 'object') {
6536					opts = Basic.extend(defaults, opts);
6537				} else {
6538					opts = Basic.extend(defaults, {
6539						width: arguments[0],
6540						height: arguments[1],
6541						crop: arguments[2],
6542						preserveHeaders: arguments[3]
6543					});
6544				}
6545
6546				try {
6547					if (!this.size) { // only preloaded image objects can be used as source
6548						throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
6549					}
6550
6551					// no way to reliably intercept the crash due to high resolution, so we simply avoid it
6552					if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) {
6553						throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR);
6554					}
6555
6556					this.getRuntime().exec.call(this, 'Image', 'downsize', opts.width, opts.height, opts.crop, opts.preserveHeaders);
6557				} catch(ex) {
6558					// for now simply trigger error event
6559					this.trigger('error', ex.code);
6560				}
6561			},
6562
6563			/**
6564			Alias for downsize(width, height, true). (see downsize)
6565			
6566			@method crop
6567			@param {Number} width Resulting width
6568			@param {Number} [height=width] Resulting height (optional, if not supplied will default to width)
6569			@param {Boolean} [preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize)
6570			*/
6571			crop: function(width, height, preserveHeaders) {
6572				this.downsize(width, height, true, preserveHeaders);
6573			},
6574
6575			getAsCanvas: function() {
6576				if (!Env.can('create_canvas')) {
6577					throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
6578				}
6579
6580				var runtime = this.connectRuntime(this.ruid);
6581				return runtime.exec.call(this, 'Image', 'getAsCanvas');
6582			},
6583
6584			/**
6585			Retrieves image in it's current state as mOxie.Blob object. Cannot be run on empty or image in progress (throws
6586			DOMException.INVALID_STATE_ERR).
6587
6588			@method getAsBlob
6589			@param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png
6590			@param {Number} [quality=90] Applicable only together with mime type image/jpeg
6591			@return {Blob} Image as Blob
6592			*/
6593			getAsBlob: function(type, quality) {
6594				if (!this.size) {
6595					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
6596				}
6597
6598				if (!type) {
6599					type = 'image/jpeg';
6600				}
6601
6602				if (type === 'image/jpeg' && !quality) {
6603					quality = 90;
6604				}
6605
6606				return this.getRuntime().exec.call(this, 'Image', 'getAsBlob', type, quality);
6607			},
6608
6609			/**
6610			Retrieves image in it's current state as dataURL string. Cannot be run on empty or image in progress (throws
6611			DOMException.INVALID_STATE_ERR).
6612
6613			@method getAsDataURL
6614			@param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png
6615			@param {Number} [quality=90] Applicable only together with mime type image/jpeg
6616			@return {String} Image as dataURL string
6617			*/
6618			getAsDataURL: function(type, quality) {
6619				if (!this.size) {
6620					throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
6621				}
6622				return this.getRuntime().exec.call(this, 'Image', 'getAsDataURL', type, quality);
6623			},
6624
6625			/**
6626			Retrieves image in it's current state as binary string. Cannot be run on empty or image in progress (throws
6627			DOMException.INVALID_STATE_ERR).
6628
6629			@method getAsBinaryString
6630			@param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png
6631			@param {Number} [quality=90] Applicable only together with mime type image/jpeg
6632			@return {String} Image as binary string
6633			*/
6634			getAsBinaryString: function(type, quality) {
6635				var dataUrl = this.getAsDataURL(type, quality);
6636				return Encode.atob(dataUrl.substring(dataUrl.indexOf('base64,') + 7));
6637			},
6638
6639			/**
6640			Embeds a visual representation of the image into the specified node. Depending on the runtime, 
6641			it might be a canvas, an img node or a thrid party shim object (Flash or SilverLight - very rare, 
6642			can be used in legacy browsers that do not have canvas or proper dataURI support).
6643
6644			@method embed
6645			@param {DOMElement} el DOM element to insert the image object into
6646			@param {Object} [options]
6647				@param {Number} [options.width] The width of an embed (defaults to the image width)
6648				@param {Number} [options.height] The height of an embed (defaults to the image height)
6649				@param {String} [type="image/jpeg"] Mime type
6650				@param {Number} [quality=90] Quality of an embed, if mime type is image/jpeg
6651				@param {Boolean} [crop=false] Whether to crop an embed to the specified dimensions
6652			*/
6653			embed: function(el) {
6654				var self = this
6655				, imgCopy
6656				, type, quality, crop
6657				, options = arguments[1] || {}
6658				, width = this.width
6659				, height = this.height
6660				, runtime // this has to be outside of all the closures to contain proper runtime
6661				;
6662
6663				function onResize() {
6664					// if possible, embed a canvas element directly
6665					if (Env.can('create_canvas')) {
6666						var canvas = imgCopy.getAsCanvas();
6667						if (canvas) {
6668							el.appendChild(canvas);
6669							canvas = null;
6670							imgCopy.destroy();
6671							self.trigger('embedded');
6672							return;
6673						}
6674					}
6675
6676					var dataUrl = imgCopy.getAsDataURL(type, quality);
6677					if (!dataUrl) {
6678						throw new x.ImageError(x.ImageError.WRONG_FORMAT);
6679					}
6680
6681					if (Env.can('use_data_uri_of', dataUrl.length)) {
6682						el.innerHTML = '<img src="' + dataUrl + '" width="' + imgCopy.width + '" height="' + imgCopy.height + '" />';
6683						imgCopy.destroy();
6684						self.trigger('embedded');
6685					} else {
6686						var tr = new Transporter();
6687
6688						tr.bind("TransportingComplete", function() {
6689							runtime = self.connectRuntime(this.result.ruid);
6690
6691							self.bind("Embedded", function() {
6692								// position and size properly
6693								Basic.extend(runtime.getShimContainer().style, {
6694									//position: 'relative',
6695									top: '0px',
6696									left: '0px',
6697									width: imgCopy.width + 'px',
6698									height: imgCopy.height + 'px'
6699								});
6700
6701								// some shims (Flash/SilverLight) reinitialize, if parent element is hidden, reordered or it's
6702								// position type changes (in Gecko), but since we basically need this only in IEs 6/7 and
6703								// sometimes 8 and they do not have this problem, we can comment this for now
6704								/*tr.bind("RuntimeInit", function(e, runtime) {
6705									tr.destroy();
6706									runtime.destroy();
6707									onResize.call(self); // re-feed our image data
6708								});*/
6709
6710								runtime = null;
6711							}, 999);
6712
6713							runtime.exec.call(self, "ImageView", "display", this.result.uid, width, height);
6714							imgCopy.destroy();
6715						});
6716
6717						tr.transport(Encode.atob(dataUrl.substring(dataUrl.indexOf('base64,') + 7)), type, Basic.extend({}, options, {
6718							required_caps: {
6719								display_media: true
6720							},
6721							runtime_order: 'flash,silverlight',
6722							container: el
6723						}));
6724					}
6725				}
6726
6727				try {
6728					if (!(el = Dom.get(el))) {
6729						throw new x.DOMException(x.DOMException.INVALID_NODE_TYPE_ER
6729R);
6730					}
6731
6732					if (!this.size) { // only preloaded image objects can be used as source
6733						throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
6734					}
6735
6736					if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) {
6737						throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR);
6738					}
6739
6740					type = options.type || this.type || 'image/jpeg';
6741					quality = options.quality || 90;
6742					crop = Basic.typeOf(options.crop) !== 'undefined' ? options.crop : false;
6743
6744					// figure out dimensions for the thumb
6745					if (options.width) {
6746						width = options.width;
6747						height = options.height || width;
6748					} else {
6749						// if container element has measurable dimensions, use them
6750						var dimensions = Dom.getSize(el);
6751						if (dimensions.w && dimensions.h) { // both should be > 0
6752							width = dimensions.w;
6753							height = dimensions.h;
6754						}
6755					}
6756
6757					imgCopy = new Image();
6758
6759					imgCopy.bind("Resize", function() {
6760						onResize.call(self);
6761					});
6762
6763					imgCopy.bind("Load", function() {
6764						imgCopy.downsize(width, height, crop, false);
6765					});
6766
6767					imgCopy.clone(this, false);
6768
6769					return imgCopy;
6770				} catch(ex) {
6771					// for now simply trigger error event
6772					this.trigger('error', ex.code);
6773				}
6774			},
6775
6776			/**
6777			Properly destroys the image and frees resources in use. If any. Recommended way to dispose mOxie.Image object.
6778
6779			@method destroy
6780			*/
6781			destroy: function() {
6782				if (this.ruid) {
6783					this.getRuntime().exec.call(this, 'Image', 'destroy');
6784					this.disconnectRuntime();
6785				}
6786				this.unbindAll();
6787			}
6788		});
6789
6790
6791		function _updateInfo(info) {
6792			if (!info) {
6793				info = this.getRuntime().exec.call(this, 'Image', 'getInfo');
6794			}
6795
6796			this.size = info.size;
6797			this.width = info.width;
6798			this.height = info.height;
6799			this.type = info.type;
6800			this.meta = info.meta;
6801
6802			// update file name, only if empty
6803			if (this.name === '') {
6804				this.name = info.name;
6805			}
6806		}
6807		
6808
6809		function _load(src) {
6810			var srcType = Basic.typeOf(src);
6811
6812			try {
6813				// if source is Image
6814				if (src instanceof Image) {
6815					if (!src.size) { // only preloaded image objects can be used as source
6816						throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
6817					}
6818					_loadFromImage.apply(this, arguments);
6819				}
6820				// if source is o.Blob/o.File
6821				else if (src instanceof Blob) {
6822					if (!~Basic.inArray(src.type, ['image/jpeg', 'image/png'])) {
6823						throw new x.ImageError(x.ImageError.WRONG_FORMAT);
6824					}
6825					_loadFromBlob.apply(this, arguments);
6826				}
6827				// if native blob/file
6828				else if (Basic.inArray(srcType, ['blob', 'file']) !== -1) {
6829					_load.call(this, new File(null, src), arguments[1]);
6830				}
6831				// if String
6832				else if (srcType === 'string') {
6833					// if dataUrl String
6834					if (/^data:[^;]*;base64,/.test(src)) {
6835						_load.call(this, new Blob(null, { data: src }), arguments[1]);
6836					}
6837					// else assume Url, either relative or absolute
6838					else {
6839						_loadFromUrl.apply(this, arguments);
6840					}
6841				}
6842				// if source seems to be an img node
6843				else if (srcType === 'node' && src.nodeName.toLowerCase() === 'img') {
6844					_load.call(this, src.src, arguments[1]);
6845				}
6846				else {
6847					throw new x.DOMException(x.DOMException.TYPE_MISMATCH_ERR);
6848				}
6849			} catch(ex) {
6850				// for now simply trigger error event
6851				this.trigger('error', ex.code);
6852			}
6853		}
6854
6855
6856		function _loadFromImage(img, exact) {
6857			var runtime = this.connectRuntime(img.ruid);
6858			this.ruid = runtime.uid;
6859			runtime.exec.call(this, 'Image', 'loadFromImage', img, (Basic.typeOf(exact) === 'undefined' ? true : exact));
6860		}
6861
6862
6863		function _loadFromBlob(blob, options) {
6864			var self = this;
6865
6866			self.name = blob.name || '';
6867
6868			function exec(runtime) {
6869				self.ruid = runtime.uid;
6870				runtime.exec.call(self, 'Image', 'loadFromBlob', blob);
6871			}
6872
6873			if (blob.isDetached()) {
6874				this.bind('RuntimeInit', function(e, runtime) {
6875					exec(runtime);
6876				});
6877
6878				// convert to object representation
6879				if (options && typeof(options.required_caps) === 'string') {
6880					options.required_caps = Runtime.parseCaps(options.required_caps);
6881				}
6882
6883				this.connectRuntime(Basic.extend({
6884					required_caps: {
6885						access_image_binary: true,
6886						resize_image: true
6887					}
6888				}, options));
6889			} else {
6890				exec(this.connectRuntime(blob.ruid));
6891			}
6892		}
6893
6894
6895		function _loadFromUrl(url, options) {
6896			var self = this, xhr;
6897
6898			xhr = new XMLHttpRequest();
6899
6900			xhr.open('get', url);
6901			xhr.responseType = 'blob';
6902
6903			xhr.onprogress = function(e) {
6904				self.trigger(e);
6905			};
6906
6907			xhr.onload = function() {
6908				_loadFromBlob.call(self, xhr.response, true);
6909			};
6910
6911			xhr.onerror = function(e) {
6912				self.trigger(e);
6913			};
6914
6915			xhr.onloadend = function() {
6916				xhr.destroy();
6917			};
6918
6919			xhr.bind('RuntimeError', function(e, err) {
6920				self.trigger('RuntimeError', err);
6921			});
6922
6923			xhr.send(null, options);
6924		}
6925	}
6926
6927	// virtual world will crash on you if image has a resolution higher than this:
6928	Image.MAX_RESIZE_WIDTH = 6500;
6929	Image.MAX_RESIZE_HEIGHT = 6500; 
6930
6931	Image.prototype = EventTarget.instance;
6932
6933	return Image;
6934});
6935
6936// Included from: src/javascript/runtime/html5/Runtime.js
6937
6938/**
6939 * Runtime.js
6940 *
6941 * Copyright 2013, Moxiecode Systems AB
6942 * Released under GPL License.
6943 *
6944 * License: http://www.plupload.com/license
6945 * Contributing: http://www.plupload.com/contributing
6946 */
6947
6948/*global File:true */
6949
6950/**
6951Defines constructor for HTML5 runtime.
6952
6953@class moxie/runtime/html5/Runtime
6954@private
6955*/
6956define("moxie/runtime/html5/Runtime", [
6957	"moxie/core/utils/Basic",
6958	"moxie/core/Exceptions",
6959	"moxie/runtime/Runtime",
6960	"moxie/core/utils/Env"
6961], function(Basic, x, Runtime, Env) {
6962	
6963	var type = "html5", extensions = {};
6964	
6965	function Html5Runtime(options) {
6966		var I = this
6967		, Test = Runtime.capTest
6968		, True = Runtime.capTrue
6969		;
6970
6971		var caps = Basic.extend({
6972				access_binary: Test(window.FileReader || window.File && window.File.getAsDataURL),
6973				access_image_binary: function() {
6974					return I.can('access_binary') && !!extensions.Image;
6975				},
6976				display_media: Test(Env.can('create_canvas') || Env.can('use_data_uri_over32kb')),
6977				do_cors: Test(window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()),
6978				drag_and_drop: Test(function() {
6979					// this comes directly from Modernizr: http://www.modernizr.com/
6980					var div = document.createElement('div');
6981					// IE has support for drag and drop since version 5, but doesn't support dropping files from desktop
6982					return (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && (Env.browser !== 'IE' || Env.version > 9);
6983				}()),
6984				filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest
6985					return (Env.browser === 'Chrome' && Env.version >= 28) || (Env.browser === 'IE' && Env.version >= 10);
6986				}()),
6987				return_response_headers: True,
6988				return_response_type: function(responseType) {
6989					if (responseType === 'json' && !!window.JSON) { // we can fake this one even if it's not supported
6990						return true;
6991					} 
6992					return Env.can('return_response_type', responseType);
6993				},
6994				return_status_code: True,
6995				report_upload_progress: Test(window.XMLHttpRequest && new XMLHttpRequest().upload),
6996				resize_image: function() {
6997					return I.can('access_binary') && Env.can('create_canvas');
6998				},
6999				select_file: function() {
7000					return Env.can('use_fileinput') && window.File;
7001				},
7002				select_folder: function() {
7003					return I.can('select_file') && Env.browser === 'Chrome' && Env.version >= 21;
7004				},
7005				select_multiple: function() {
7006					// it is buggy on Safari Windows and iOS
7007					return I.can('select_file') && 
7008						!(Env.browser === 'Safari' && Env.os === 'Windows') && 
7009						!(Env.os === 'iOS' && Env.verComp(Env.osVersion, "7.0.4", '<'));
7010				},
7011				send_binary_string: Test(window.XMLHttpRequest && (new XMLHttpRequest().sendAsBinary || (window.Uint8Array && window.ArrayBuffer))),
7012				send_custom_headers: Test(window.XMLHttpRequest),
7013				send_multipart: function() {
7014					return !!(window.XMLHttpRequest && new XMLHttpRequest().upload && window.FormData) || I.can('send_binary_string');
7015				},
7016				slice_blob: Test(window.File && (File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice)),
7017				stream_upload: function(){
7018					return I.can('slice_blob') && I.can('send_multipart');
7019				},
7020				summon_file_dialog: Test(function() { // yeah... some dirty sniffing here...
7021					return (Env.browser === 'Firefox' && Env.version >= 4) ||
7022						(Env.browser === 'Opera' && Env.version >= 12) ||
7023						(Env.browser === 'IE' && Env.version >= 10) ||
7024						!!~Basic.inArray(Env.browser, ['Chrome', 'Safari']);
7025				}()),
7026				upload_filesize: True
7027			}, 
7028			arguments[2]
7029		);
7030
7031		Runtime.call(this, options, (arguments[1] || type), caps);
7032
7033
7034		Basic.extend(this, {
7035
7036			init : function() {
7037				this.trigger("Init");
7038			},
7039
7040			destroy: (function(destroy) { // extend default destroy method
7041				return function() {
7042					destroy.call(I);
7043					destroy = I = null;
7044				};
7045			}(this.destroy))
7046		});
7047
7048		Basic.extend(this.getShim(), extensions);
7049	}
7050
7051	Runtime.addConstructor(type, Html5Runtime);
7052
7053	return extensions;
7054});
7055
7056// Included from: src/javascript/runtime/html5/file/Blob.js
7057
7058/**
7059 * Blob.js
7060 *
7061 * Copyright 2013, Moxiecode Systems AB
7062 * Released under GPL License.
7063 *
7064 * License: http://www.plupload.com/license
7065 * Contributing: http://www.plupload.com/contributing
7066 */
7067
7068/**
7069@class moxie/runtime/html5/file/Blob
7070@private
7071*/
7072define("moxie/runtime/html5/file/Blob", [
7073	"moxie/runtime/html5/Runtime",
7074	"moxie/file/Blob"
7075], function(extensions, Blob) {
7076
7077	function HTML5Blob() {
7078		function w3cBlobSlice(blob, start, end) {
7079			var blobSlice;
7080
7081			if (window.File.prototype.slice) {
7082				try {
7083					blob.slice();	// depricated version will throw WRONG_ARGUMENTS_ERR exception
7084					return blob.slice(start, end);
7085				} catch (e) {
7086					// depricated slice method
7087					return blob.slice(start, end - start);
7088				}
7089			// slice method got prefixed: https://bugzilla.mozilla.org/show_bug.cgi?id=649672
7090			} else if ((blobSlice = window.File.prototype.webkitSlice || window.File.prototype.mozSlice)) {
7091				return blobSlice.call(blob, start, end);
7092			} else {
7093				return null; // or throw some exception
7094			}
7095		}
7096
7097		this.slice = function() {
7098			return new Blob(this.getRuntime().uid, w3cBlobSlice.apply(this, arguments));
7099		};
7100	}
7101
7102	return (extensions.Blob = HTML5Blob);
7103});
7104
7105// Included from: src/javascript/core/utils/Events.js
7106
7107/**
7108 * Events.js
7109 *
7110 * Copyright 2013, Moxiecode Systems AB
7111 * Released under GPL License.
7112 *
7113 * License: http://www.plupload.com/license
7114 * Contributing: http://www.plupload.com/contributing
7115 */
7116
7117define('moxie/core/utils/Events', [
7118	'moxie/core/utils/Basic'
7119], function(Basic) {
7120	var eventhash = {}, uid = 'moxie_' + Basic.guid();
7121	
7122	// IE W3C like event funcs
7123	function preventDefault() {
7124		this.returnValue = false;
7125	}
7126
7127	function stopPropagation() {
7128		this.cancelBubble = true;
7129	}
7130
7131	/**
7132	Adds an event handler to the specified object and store reference to the handler
7133	in objects internal Plupload registry (@see removeEvent).
7134	
7135	@method addEvent
7136	@for Utils
7137	@static
7138	@param {Object} obj DOM element like object to add handler to.
7139	@param {String} name Name to add event listener to.
7140	@param {Function} callback Function to call when event occurs.
7141	@param {String} [key] that might be used to add specifity to the event record.
7142	*/
7143	var addEvent = function(obj, name, callback, key) {
7144		var func, events;
7145					
7146		name = name.toLowerCase();
7147
7148		// Add event listener
7149		if (obj.addEventListener) {
7150			func = callback;
7151			
7152			obj.addEventListener(name, func, false);
7153		} else if (obj.attachEvent) {
7154			func = function() {
7155				var evt = window.event;
7156
7157				if (!evt.target) {
7158					evt.target = evt.srcElement;
7159				}
7160
7161				evt.preventDefault = preventDefault;
7162				evt.stopPropagation = stopPropagation;
7163
7164				callback(evt);
7165			};
7166
7167			obj.attachEvent('on' + name, func);
7168		}
7169		
7170		// Log event handler to objects internal mOxie registry
7171		if (!obj[uid]) {
7172			obj[uid] = Basic.guid();
7173		}
7174		
7175		if (!eventhash.hasOwnProperty(obj[uid])) {
7176			eventhash[obj[uid]] = {};
7177		}
7178		
7179		events = eventhash[obj[uid]];
7180		
7181		if (!events.hasOwnProperty(name)) {
7182			events[name] = [];
7183		}
7184				
7185		events[name].push({
7186			func: func,
7187			orig: callback, // store original callback for IE
7188			key: key
7189		});
7190	};
7191	
7192	
7193	/**
7194	Remove event handler from the specified object. If third argument (callback)
7195	is not specified remove all events with the specified name.
7196	
7197	@method removeEvent
7198	@static
7199	@param {Object} obj DOM element to remove event listener(s) from.
7200	@param {String} name Name of event listener to remove.
7201	@param {Function|String} [callback] might be a callback or unique key to match.
7202	*/
7203	var removeEvent = function(obj, name, callback) {
7204		var type, undef;
7205		
7206		name = name.toLowerCase();
7207		
7208		if (obj[uid] && eventhash[obj[uid]] && eventhash[obj[uid]][name]) {
7209			type = eventhash[obj[uid]][name];
7210		} else {
7211			return;
7212		}
7213			
7214		for (var i = type.length - 1; i >= 0; i--) {
7215			// undefined or not, key should match
7216			if (type[i].orig === callback || type[i].key === callback) {
7217				if (obj.removeEventListener) {
7218					obj.removeEventListener(name, type[i].func, false);
7219				} else if (obj.detachEvent) {
7220					obj.detachEvent('on'+name, type[i].func);
7221				}
7222				
7223				type[i].orig = null;
7224				type[i].func = null;
7225				type.splice(i, 1);
7226				
7227				// If callback was passed we are done here, otherwise proceed
7228				if (callback !== undef) {
7229					break;
7230				}
7231			}
7232		}
7233		
7234		// If event array got empty, remove it
7235		if (!type.length) {
7236			delete eventhash[obj[uid]][name];
7237		}
7238		
7239		// If mOxie registry has become empty, remove it
7240		if (Basic.isEmptyObj(eventhash[obj[uid]])) {
7241			delete eventhash[obj[uid]];
7242			
7243			// IE doesn't let you remove DOM object property with - delete
7244			try {
7245				delete obj[uid];
7246			} catch(e) {
7247				obj[uid] = undef;
7248			}
7249		}
7250	};
7251	
7252	
7253	/**
7254	Remove all kind of events from the specified object
7255	
7256	@method removeAllEvents
7257	@static
7258	@param {Object} obj DOM element to remove event listeners from.
7259	@param {String} [key] unique key to match, when removing events.
7260	*/
7261	var removeAllEvents = function(obj, key) {		
7262		if (!obj || !obj[uid]) {
7263			return;
7264		}
7265		
7266		Basic.each(eventhash[obj[uid]], function(events, name) {
7267			removeEvent(obj, name, key);
7268		});
7269	};
7270
7271	return {
7272		addEvent: addEvent,
7273		removeEvent: removeEvent,
7274		removeAllEvents: removeAllEvents
7275	};
7276});
7277
7278// Included from: src/javascript/runtime/html5/file/FileInput.js
7279
7280/**
7281 * FileInput.js
7282 *
7283 * Copyright 2013, Moxiecode Systems AB
7284 * Released under GPL License.
7285 *
7286 * License: http://www.plupload.com/license
7287 * Contributing: http://www.plupload.com/contributing
7288 */
7289
7290/**
7291@class moxie/runtime/html5/file/FileInput
7292@private
7293*/
7294define("moxie/runtime/html5/file/FileInput", [
7295	"moxie/runtime/html5/Runtime",
7296	"moxie/core/utils/Basic",
7297	"moxie/core/utils/Dom",
7298	"moxie/core/utils/Events",
7299	"moxie/core/utils/Mime",
7300	"moxie/core/utils/Env"
7301], function(extensions, Basic, Dom, Events, Mime, Env) {
7302	
7303	function FileInput() {
7304		var _files = [], _options;
7305
7306		Basic.extend(this, {
7307			init: function(options) {
7308				var comp = this, I = comp.getRuntime(), input, shimContainer, mimes, browseButton, zIndex, top;
7309
7310				_options = options;
7311				_files = [];
7312
7313				// figure out accept string
7314				mimes = _options.accept.mimes || Mime.extList2mimes(_options.accept, I.can('filter_by_extension'));
7315
7316				shimContainer = I.getShimContainer();
7317
7318				shimContainer.innerHTML = '<input id="' + I.uid +'" type="file" style="font-size:999px;opacity:0;"' +
7319					(_options.multiple && I.can('select_multiple') ? 'multiple' : '') + 
7320					(_options.directory && I.can('select_folder') ? 'webkitdirectory directory' : '') + // Chrome 11+
7321					(mimes ? ' accept="' + mimes.join(',') + '"' : '') + ' />';
7322
7323				input = Dom.get(I.uid);
7324
7325				// prepare file input to be placed underneath the browse_button element
7326				Basic.extend(input.style, {
7327					position: 'absolute',
7328					top: 0,
7329					left: 0,
7330					width: '100%',
7331					height: '100%'
7332				});
7333
7334
7335				browseButton = Dom.get(_options.browse_button);
7336
7337				// Route click event to the input[type=file] element for browsers that support such behavior
7338				if (I.can('summon_file_dialog')) {
7339					if (Dom.getStyle(browseButton, 'position') === 'static') {
7340						browseButton.style.position = 'relative';
7341					}
7342
7343					zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 1;
7344
7345					browseButton.style.zIndex = zIndex;
7346					shimContainer.style.zIndex = zIndex - 1;
7347
7348					Events.addEvent(browseButton, 'click', function(e) {
7349						var input = Dom.get(I.uid);
7350						if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file]
7351							input.click();
7352						}
7353						e.preventDefault();
7354					}, comp.uid);
7355				}
7356
7357				/* Since we have to place input[type=file] on top of the browse_button for some browsers,
7358				browse_button loses interactivity, so we restore it here */
7359				top = I.can('summon_file_dialog') ? browseButton : shimContainer;
7360
7361				Events.addEvent(top, 'mouseover', function() {
7362					comp.trigger('mouseenter');
7363				}, comp.uid);
7364
7365				Events.addEvent(top, 'mouseout', function() {
7366					comp.trigger('mouseleave');
7367				}, comp.uid);
7368
7369				Events.addEvent(top, 'mousedown', function() {
7370					comp.trigger('mousedown');
7371				}, comp.uid);
7372
7373				Events.addEvent(Dom.get(_options.container), 'mouseup', function() {
7374					comp.trigger('mouseup');
7375				}, comp.uid);
7376
7377
7378				input.onchange = function onChange() { // there should be only one handler for this
7379					_files = [];
7380
7381					if (_options.directory) {
7382						// folders are represented by dots, filter them out (Chrome 11+)
7383						Basic.each(this.files, function(file) {
7384							if (file.name !== ".") { // if it doesn't looks like a folder
7385								_files.push(file);
7386							}
7387						});
7388					} else {
7389						_files = [].slice.call(this.files);
7390					}
7391
7392					// clearing the value enables the user to select the same file again if they want to
7393					if (Env.browser !== 'IE' && Env.browser !== 'IEMobile') {
7394						this.value = '';
7395					} else {
7396						// in IE input[type="file"] is read-only so the only way to reset it is to re-insert it
7397						var clone = this.cloneNode(true);
7398						this.parentNode.replaceChild(clone, this);
7399						clone.onchange = onChange;
7400					}
7401					comp.trigger('change');
7402				};
7403
7404				// ready event is perfectly asynchronous
7405				comp.trigger({
7406					type: 'ready',
7407					async: true
7408				});
7409
7410				shimContainer = null;
7411			},
7412
7413			getFiles: function() {
7414				return _files;
7415			},
7416
7417			disable: function(state) {
7418				var I = this.getRuntime(), input;
7419
7420				if ((input = Dom.get(I.uid))) {
7421					input.disabled = !!state;
7422				}
7423			},
7424
7425			destroy: function() {
7426				var I = this.getRuntime()
7427				, shim = I.getShim()
7428				, shimContainer = I.getShimContainer()
7429				;
7430				
7431				Events.removeAllEvents(shimContainer, this.uid);
7432				Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
7433				Events.removeAllEvents(_options && Dom.get(_options.browse_button), this.uid);
7434				
7435				if (shimContainer) {
7436					shimContainer.innerHTML = '';
7437				}
7438
7439				shim.removeInstance(this.uid);
7440
7441				_files = _options = shimContainer = shim = null;
7442			}
7443		});
7444	}
7445
7446	return (extensions.FileInput = FileInput);
7447});
7448
7449// Included from: src/javascript/runtime/html5/file/FileDrop.js
7450
7451/**
7452 * FileDrop.js
7453 *
7454 * Copyright 2013, Moxiecode Systems AB
7455 * Released under GPL License.
7456 *
7457 * License: http://www.plupload.com/license
7458 * Contributing: http://www.plupload.com/contributing
7459 */
7460
7461/**
7462@class moxie/runtime/html5/file/FileDrop
7463@private
7464*/
7465define("moxie/runtime/html5/file/FileDrop", [
7466	"moxie/runtime/html5/Runtime",
7467	"moxie/core/utils/Basic",
7468	"moxie/core/utils/Dom",
7469	"moxie/core/utils/Events",
7470	"moxie/core/utils/Mime"
7471], function(extensions, Basic, Dom, Events, Mime) {
7472	
7473	function FileDrop() {
7474		var _files = [], _allowedExts = [], _options;
7475
7476		Basic.extend(this, {
7477			init: function(options) {
7478				var comp = this, dropZone;
7479
7480				_options = options;
7481				_allowedExts = _extractExts(_options.accept);
7482				dropZone = _options.container;
7483
7484				Events.addEvent(dropZone, 'dragover', function(e) {
7485					if (!_hasFiles(e)) {
7486						return;
7487					}
7488					e.preventDefault();
7489					e.dataTransfer.dropEffect = 'copy';
7490				}, comp.uid);
7491
7492				Events.addEvent(dropZone, 'drop', function(e) {
7493					if (!_hasFiles(e)) {
7494						return;
7495					}
7496					e.preventDefault();
7497
7498					_files = [];
7499
7500					// Chrome 21+ accepts folders via Drag'n'Drop
7501					if (e.dataTransfer.items && e.dataTransfer.items[0].webkitGetAsEntry) {
7502						_readItems(e.dataTransfer.items, function() {
7503							comp.trigger("drop");
7504						});
7505					} else {
7506						Basic.each(e.dataTransfer.files, function(file) {
7507							if (_isAcceptable(file)) {
7508								_files.push(file);
7509							}
7510						});
7511						comp.trigger("drop");
7512					}
7513				}, comp.uid);
7514
7515				Events.addEvent(dropZone, 'dragenter', function(e) {
7516					comp.trigger("dragenter");
7517				}, comp.uid);
7518
7519				Events.addEvent(dropZone, 'dragleave', function(e) {
7520					comp.trigger("dragleave");
7521				}, comp.uid);
7522			},
7523
7524			getFiles: function() {
7525				return _files;
7526			},
7527
7528			destroy: function() {
7529				Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
7530				_files = _allowedExts = _options = null;
7531			}
7532		});
7533
7534
7535		function _hasFiles(e) {
7536			if (!e.dataTransfer || !e.dataTransfer.types) { // e.dataTransfer.files is not available in Gecko during dragover
7537				return false;
7538			}
7539
7540			var types = Basic.toArray(e.dataTransfer.types || []);
7541
7542			return Basic.inArray("Files", types) !== -1 ||
7543				Basic.inArray("public.file-url", types) !== -1 || // Safari < 5
7544				Basic.inArray("application/x-moz-file", types) !== -1 // Gecko < 1.9.2 (< Firefox 3.6)
7545				;
7546		}
7547
7548		
7549		function _extractExts(accept) {
7550			var exts = [];
7551			for (var i = 0; i < accept.length; i++) {
7552				[].push.apply(exts, accept[i].extensions.split(/\s*,\s*/));
7553			}
7554			return Basic.inArray('*', exts) === -1 ? exts : [];
7555		}
7556
7557
7558		function _isAcceptable(file) {
7559			if (!_allowedExts.length) {
7560				return true;
7561			}
7562			var ext = Mime.getFileExtension(file.name);
7563			return !ext || Basic.inArray(ext, _allowedExts) !== -1;
7564		}
7565
7566
7567		function _readItems(items, cb) {
7568			var entries = [];
7569			Basic.each(items, function(item) {
7570				var entry = item.webkitGetAsEntry();
7571				// Address #998 (https://code.google.com/p/chromium/issues/detail?id=332579)
7572				if (entry) {
7573					// file() fails on OSX when the filename contains a special character (e.g. umlaut): see #61
7574					if (entry.isFile) {
7575						var file = item.getAsFile();
7576						if (_isAcceptable(file)) {
7577							_files.push(file);
7578						}
7579					} else {
7580						entries.push(entry);
7581					}
7582				}
7583			});
7584
7585			if (entries.length) {
7586				_readEntries(entries, cb);
7587			} else {
7588				cb();
7589			}
7590		}
7591
7592
7593		function _readEntries(entries, cb) {
7594			var queue = [];
7595			Basic.each(entries, function(entry) {
7596				queue.push(function(cbcb) {
7597					_readEntry(entry, cbcb);
7598				});
7599			});
7600			Basic.inSeries(queue, function() {
7601				cb();
7602			});
7603		}
7604
7605
7606		function _readEntry(entry, cb) {
7607			if (entry.isFile) {
7608				entry.file(function(file) {
7609					if (_isAcceptable(file)) {
7610						_files.push(file);
7611					}
7612					cb();
7613				}, function() {
7614					// fire an error event maybe
7615					cb();
7616				});
7617			} else if (entry.isDirectory) {
7618				_readDirEntry(entry, cb);
7619			} else {
7620				cb(); // not file, not directory? what then?..
7621			}
7622		}
7623
7624
7625		function _readDirEntry(dirEntry, cb) {
7626			var entries = [], dirReader = dirEntry.createReader();
7627
7628			// keep quering recursively till no more entries
7629			function getEntries(cbcb) {
7630				dirReader.readEntries(function(moreEntries) {
7631					if (moreEntries.length) {
7632						[].push.apply(entries, moreEntries);
7633						getEntries(cbcb);
7634					} else {
7635						cbcb();
7636					}
7637				}, cbcb);
7638			}
7639
7640			// ...and you thought FileReader was crazy...
7641			getEntries(function() {
7642				_readEntries(entries, cb);
7643			}); 
7644		}
7645	}
7646
7647	return (extensions.FileDrop = FileDrop);
7648});
7649
7650// Included from: src/javascript/runtime/html5/file/FileReader.js
7651
7652/**
7653 * FileReader.js
7654 *
7655 * Copyright 2013, Moxiecode Systems AB
7656 * Released under GPL License.
7657 *
7658 * License: http://www.plupload.com/license
7659 * Contributing: http://www.plupload.com/contributing
7660 */
7661
7662/**
7663@class moxie/runtime/html5/file/FileReader
7664@private
7665*/
7666define("moxie/runtime/html5/file/FileReader", [
7667	"moxie/runtime/html5/Runtime",
7668	"moxie/core/utils/Encode",
7669	"moxie/core/utils/Basic"
7670], function(extensions, Encode, Basic) {
7671	
7672	function FileReader() {
7673		var _fr, _convertToBinary = false;
7674
7675		Basic.extend(this, {
7676
7677			read: function(op, blob) {
7678				var target = this;
7679
7680				_fr = new window.FileReader();
7681
7682				_fr.addEventListener('progress', function(e) {
7683					target.trigger(e);
7684				});
7685
7686				_fr.addEventListener('load', function(e) {
7687					target.trigger(e);
7688				});
7689
7690				_fr.addEventListener('error', function(e) {
7691					target.trigger(e, _fr.error);
7692				});
7693
7694				_fr.addEventListener('loadend', function() {
7695					_fr = null;
7696				});
7697
7698				if (Basic.typeOf(_fr[op]) === 'function') {
7699					_convertToBinary = false;
7700					_fr[op](blob.getSource());
7701				} else if (op === 'readAsBinaryString') { // readAsBinaryString is depricated in general and never existed in IE10+
7702					_convertToBinary = true;
7703					_fr.readAsDataURL(blob.getSource());
7704				}
7705			},
7706
7707			getResult: function() {
7708				return _fr && _fr.result ? (_convertToBinary ? _toBinary(_fr.result) : _fr.result) : null;
7709			},
7710
7711			abort: function() {
7712				if (_fr) {
7713					_fr.abort();
7714				}
7715			},
7716
7717			destroy: function() {
7718				_fr = null;
7719			}
7720		});
7721
7722		function _toBinary(str) {
7723			return Encode.atob(str.substring(str.indexOf('base64,') + 7));
7724		}
7725	}
7726
7727	return (extensions.FileReader = FileReader);
7728});
7729
7730// Included from: src/javascript/runtime/html5/xhr/XMLHttpRequest.js
7731
7732/**
7733 * XMLHttpRequest.js
7734 *
7735 * Copyright 2013, Moxiecode Systems AB
7736 * Released under GPL License.
7737 *
7738 * License: http://www.plupload.com/license
7739 * Contributing: http://www.plupload.com/contributing
7740 */
7741
7742/*global ActiveXObject:true */
7743
7744/**
7745@class moxie/runtime/html5/xhr/XMLHttpRequest
7746@private
7747*/
7748define("moxie/runtime/html5/xhr/XMLHttpRequest", [
7749	"moxie/runtime/html5/Runtime",
7750	"moxie/core/utils/Basic",
7751	"moxie/core/utils/Mime",
7752	"moxie/core/utils/Url",
7753	"moxie/file/File",
7754	"moxie/file/Blob",
7755	"moxie/xhr/FormData",
7756	"moxie/core/Exceptions",
7757	"moxie/core/utils/Env"
7758], function(extensions, Basic, Mime, Url, File, Blob, FormData, x, Env) {
7759	
7760	function XMLHttpRequest() {
7761		var self = this
7762		, _xhr
7763		, _filename
7764		;
7765
7766		Basic.extend(this, {
7767			send: function(meta, data) {
7768				var target = this
7769				, isGecko2_5_6 = (Env.browser === 'Mozilla' && Env.version >= 4 && Env.version < 7)
7770				, isAndroidBrowser = Env.browser === 'Android Browser'
7771				, mustSendAsBinary = false
7772				;
7773
7774				// extract file name
7775				_filename = meta.url.replace(/^.+?\/([\w\-\.]+)$/, '$1').toLowerCase();
7776
7777				_xhr = _getNativeXHR();
7778				_xhr.open(meta.method, meta.url, meta.async, meta.user, meta.password);
7779
7780
7781				// prepare data to be sent
7782				if (data instanceof Blob) {
7783					if (data.isDetached()) {
7784						mustSendAsBinary = true;
7785					}
7786					data = data.getSource();
7787				} else if (data instanceof FormData) {
7788
7789					if (data.hasBlob()) {
7790						if (data.getBlob().isDetached()) {
7791							data = _prepareMultipart.call(target, data); // _xhr must be instantiated and be in OPENED state
7792							mustSendAsBinary = true;
7793						} else if ((isGecko2_5_6 || isAndroidBrowser) && Basic.typeOf(data.getBlob().getSource()) === 'blob' && window.FileReader) {
7794							// Gecko 2/5/6 can't send blob in FormData: https://bugzilla.mozilla.org/show_bug.cgi?id=649150
7795							// Android browsers (default one and Dolphin) seem to have the same issue, see: #613
7796							_preloadAndSend.call(target, meta, data);
7797							return; // _preloadAndSend will reinvoke send() with transmutated FormData =%D
7798						}	
7799					}
7800
7801					// transfer fields to real FormData
7802					if (data instanceof FormData) { // if still a FormData, e.g. not mangled by _prepareMultipart()
7803						var fd = new window.FormData();
7804						data.each(function(value, name) {
7805							if (value instanceof Blob) {
7806								fd.append(name, value.getSource());
7807							} else {
7808								fd.append(name, value);
7809							}
7810						});
7811						data = fd;
7812					}
7813				}
7814
7815
7816				// if XHR L2
7817				if (_xhr.upload) {
7818					if (meta.withCredentials) {
7819						_xhr.withCredentials = true;
7820					}
7821
7822					_xhr.addEventListener('load', function(e) {
7823						target.trigger(e);
7824					});
7825
7826					_xhr.addEventListener('error', function(e) {
7827						target.trigger(e);
7828					});
7829
7830					// additionally listen to progress events
7831					_xhr.addEventListener('progress', function(e) {
7832						target.trigger(e);
7833					});
7834
7835					_xhr.upload.addEventListener('progress', function(e) {
7836						target.trigger({
7837							type: 'UploadProgress',
7838							loaded: e.loaded,
7839							total: e.total
7840						});
7841					});
7842				// ... otherwise simulate XHR L2
7843				} else {
7844					_xhr.onreadystatechange = function onReadyStateChange() {
7845						
7846						// fake Level 2 events
7847						switch (_xhr.readyState) {
7848							
7849							case 1: // XMLHttpRequest.OPENED
7850								// readystatechanged is fired twice for OPENED state (in IE and Mozilla) - neu
7851								break;
7852							
7853							// looks like HEADERS_RECEIVED (state 2) is not reported in Opera (or it's old versions) - neu
7854							case 2: // XMLHttpRequest.HEADERS_RECEIVED
7855								break;
7856								
7857							case 3: // XMLHttpRequest.LOADING 
7858								// try to fire progress event for not XHR L2
7859								var total, loaded;
7860								
7861								try {
7862									if (Url.hasSameOrigin(meta.url)) { // Content-Length not accessible for cross-domain on some browsers
7863										total = _xhr.getResponseHeader('Content-Length') || 0; // old Safari throws an exception here
7864									}
7865
7866									if (_xhr.responseText) { // responseText was introduced in IE7
7867										loaded = _xhr.responseText.length;
7868									}
7869								} catch(ex) {
7870									total = loaded = 0;
7871								}
7872
7873								target.trigger({
7874									type: 'progress',
7875									lengthComputable: !!total,
7876									total: parseInt(total, 10),
7877									loaded: loaded
7878								});
7879								break;
7880								
7881							case 4: // XMLHttpRequest.DONE
7882								// release readystatechange handler (mostly for IE)
7883								_xhr.onreadystatechange = function() {};
7884
7885								// usually status 0 is returned when server is unreachable, but FF also fails to status 0 for 408 timeout
7886								if (_xhr.status === 0) {
7887									target.trigger('error');
7888								} else {
7889									target.trigger('load');
7890								}							
7891								break;
7892						}
7893					};
7894				}
7895				
7896
7897				// set request headers
7898				if (!Basic.isEmptyObj(meta.headers)) {
7899					Basic.each(meta.headers, function(value, header) {
7900						_xhr.setRequestHeader(header, value);
7901					});
7902				}
7903
7904				// request response type
7905				if ("" !== meta.responseType && 'responseType' in _xhr) {
7906					if ('json' === meta.responseType && !Env.can('return_response_type', 'json')) { // we can fake this one
7907						_xhr.responseType = 'text';
7908					} else {
7909						_xhr.responseType = meta.responseType;
7910					}
7911				}
7912
7913				// send ...
7914				if (!mustSendAsBinary) {
7915					_xhr.send(data);
7916				} else {
7917					if (_xhr.sendAsBinary) { // Gecko
7918						_xhr.sendAsBinary(data);
7919					} else { // other browsers having support for typed arrays
7920						(function() {
7921							// mimic Gecko's sendAsBinary
7922							var ui8a = new Uint8Array(data.length);
7923							for (var i = 0; i < data.length; i++) {
7924								ui8a[i] = (data.charCodeAt(i) & 0xff);
7925							}
7926							_xhr.send(ui8a.buffer);
7927						}());
7928					}
7929				}
7930
7931				target.trigger('loadstart');
7932			},
7933
7934			getStatus: function() {
7935				// according to W3C spec it should return 0 for readyState < 3, but instead it throws an exception
7936				try {
7937					if (_xhr) {
7938						return _xhr.status;
7939					}
7940				} catch(ex) {}
7941				return 0;
7942			},
7943
7944			getResponse: function(responseType) {
7945				var I = this.getRuntime();
7946
7947				try {
7948					switch (responseType) {
7949						case 'blob':
7950							var file = new File(I.uid, _xhr.response);
7951							
7952							// try to extract file name from content-disposition if possible (might be - not, if CORS for example)	
7953							var disposition = _xhr.getResponseHeader('Content-Disposition');
7954							if (disposition) {
7955								// extract filename from response header if available
7956								var match = disposition.match(/filename=([\'\"'])([^\1]+)\1/);
7957								if (match) {
7958									_filename = match[2];
7959								}
7960							}
7961							file.name = _filename;
7962
7963							// pre-webkit Opera doesn't set type property on the blob response
7964							if (!file.type) {
7965								file.type = Mime.getFileMime(_filename);
7966							}
7967							return file;
7968
7969						case 'json':
7970							if (!Env.can('return_response_type', 'json')) {
7971								return _xhr.status === 200 && !!window.JSON ? JSON.parse(_xhr.responseText) : null;
7972							}
7973							return _xhr.response;
7974
7975						case 'document':
7976							return _getDocument(_xhr);
7977
7978						default:
7979							return _xhr.responseText !== '' ? _xhr.responseText : null; // against the specs, but for consistency across the runtimes
7980					}
7981				} catch(ex) {
7982					return null;
7983				}				
7984			},
7985
7986			getAllResponseHeaders: function() {
7987				try {
7988					return _xhr.getAllResponseHeaders();
7989				} catch(ex) {}
7990				return '';
7991			},
7992
7993			abort: function() {
7994				if (_xhr) {
7995					_xhr.abort();
7996				}
7997			},
7998
7999			destroy: function() {
8000				self = _filename = null;
8001			}
8002		});
8003
8004
8005		// here we go... ugly fix for ugly bug
8006		function _preloadAndSend(meta, data) {
8007			var target = this, blob, fr;
8008				
8009			// get original blob
8010			blob = data.getBlob().getSource();
8011			
8012			// preload blob in memory to be sent as binary string
8013			fr = new window.FileReader();
8014			fr.onload = function() {
8015				// overwrite original blob
8016				data.append(data.getBlobName(), new Blob(null, {
8017					type: blob.type,
8018					data: fr.result
8019				}));
8020				// invoke send operation again
8021				self.send.call(target, meta, data);
8022			};
8023			fr.readAsBinaryString(blob);
8024		}
8025
8026		
8027		function _getNativeXHR() {
8028			if (window.XMLHttpRequest && !(Env.browser === 'IE' && Env.version < 8)) { // IE7 has native XHR but it's buggy
8029				return new window.XMLHttpRequest();
8030			} else {
8031				return (function() {
8032					var progIDs = ['Msxml2.XMLHTTP.6.0', 'Microsoft.XMLHTTP']; // if 6.0 available, use it, otherwise failback to default 3.0
8033					for (var i = 0; i < progIDs.length; i++) {
8034						try {
8035							return new ActiveXObject(progIDs[i]);
8036						} catch (ex) {}
8037					}
8038				})();
8039			}
8040		}
8041		
8042		// @credits Sergey Ilinsky	(http://www.ilinsky.com/)
8043		function _getDocument(xhr) {
8044			var rXML = xhr.responseXML;
8045			var rText = xhr.responseText;
8046			
8047			// Try parsing responseText (@see: http://www.ilinsky.com/articles/XMLHttpRequest/#bugs-ie-responseXML-content-type)
8048			if (Env.browser === 'IE' && rText && rXML && !rXML.documentElement && /[^\/]+\/[^\+]+\+xml/.test(xhr.getResponseHeader("Content-Type"))) {
8049				rXML = new window.ActiveXObject("Microsoft.XMLDOM");
8050				rXML.async = false;
8051				rXML.validateOnParse = false;
8052				rXML.loadXML(rText);
8053			}
8054	
8055			// Check if there is no error in document
8056			if (rXML) {
8057				if ((Env.browser === 'IE' && rXML.parseError !== 0) || !rXML.documentElement || rXML.documentElement.tagName === "parsererror") {
8058					return null;
8059				}
8060			}
8061			return rXML;
8062		}
8063
8064
8065		function _prepareMultipart(fd) {
8066			var boundary = '----moxieboundary' + new Date().getTime()
8067			, dashdash = '--'
8068			, crlf = '\r\n'
8069			, multipart = ''
8070			, I = this.getRuntime()
8071			;
8072
8073			if (!I.can('send_binary_string')) {
8074				throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
8075			}
8076
8077			_xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
8078
8079			// append multipart parameters
8080			fd.each(function(value, name) {
8081				// Firefox 3.6 failed to convert multibyte characters to UTF-8 in sendAsBinary(), 
8082				// so we try it here ourselves with: unescape(encodeURIComponent(value))
8083				if (value instanceof Blob) {
8084					// Build RFC2388 blob
8085					multipart += dashdash + boundary + crlf +
8086						'Content-Disposition: form-data; name="' + name + '"; filename="' + unescape(encodeURIComponent(value.name || 'blob')) + '"' + crlf +
8087						'Content-Type: ' + (value.type || 'application/octet-stream') + crlf + crlf +
8088						value.getSource() + crlf;
8089				} else {
8090					multipart += dashdash + boundary + crlf +
8091						'Content-Disposition: form-data; name="' + name + '"' + crlf + crlf +
8092						unescape(encodeURIComponent(value)) + crlf;
8093				}
8094			});
8095
8096			multipart += dashdash + boundary + dashdash + crlf;
8097
8098			return multipart;
8099		}
8100	}
8101
8102	return (extensions.XMLHttpRequest = XMLHttpRequest);
8103});
8104
8105// Included from: src/javascript/runtime/html5/utils/BinaryReader.js
8106
8107/**
8108 * BinaryReader.js
8109 *
8110 * Copyright 2013, Moxiecode Systems AB
8111 * Released under GPL License.
8112 *
8113 * License: http://www.plupload.com/license
8114 * Contributing: http://www.plupload.com/contributing
8115 */
8116
8117/**
8118@class moxie/runtime/html5/utils/BinaryReader
8119@private
8120*/
8121define("moxie/runtime/html5/utils/BinaryReader", [], function() {
8122	return function() {
8123		var II = false, bin;
8124
8125		// Private functions
8126		function read(idx, size) {
8127			var mv = II ? 0 : -8 * (size - 1), sum = 0, i;
8128
8129			for (i = 0; i < size; i++) {
8130				sum |= (bin.charCodeAt(idx + i) << Math.abs(mv + i*8));
8131			}
8132
8133			return sum;
8134		}
8135
8136		function putstr(segment, idx, length) {
8137			length = arguments.length === 3 ? length : bin.length - idx - 1;
8138			bin = bin.substr(0, idx) + segment + bin.substr(length + idx);
8139		}
8140
8141		function write(idx, num, size) {
8142			var str = '', mv = II ? 0 : -8 * (size - 1), i;
8143
8144			for (i = 0; i < size; i++) {
8145				str += String.fromCharCode((num >> Math.abs(mv + i*8)) & 255);
8146			}
8147
8148			putstr(str, idx, size);
8149		}
8150
8151		// Public functions
8152		return {
8153			II: function(order) {
8154				if (order === undefined) {
8155					return II;
8156				} else {
8157					II = order;
8158				}
8159			},
8160
8161			init: function(binData) {
8162				II = false;
8163				bin = binData;
8164			},
8165
8166			SEGMENT: function(idx, length, segment) {
8167				switch (arguments.length) {
8168					case 1:
8169						return bin.substr(idx, bin.length - idx - 1);
8170					case 2:
8171						return bin.substr(idx, length);
8172					case 3:
8173						putstr(segment, idx, length);
8174						break;
8175					default: return bin;
8176				}
8177			},
8178
8179			BYTE: function(idx) {
8180				return read(idx, 1);
8181			},
8182
8183			SHORT: function(idx) {
8184				return read(idx, 2);
8185			},
8186
8187			LONG: function(idx, num) {
8188				if (num === undefined) {
8189					return read(idx, 4);
8190				} else {
8191					write(idx, num, 4);
8192				}
8193			},
8194
8195			SLONG: function(idx) { // 2's complement notation
8196				var num = read(idx, 4);
8197
8198				return (num > 2147483647 ? num - 4294967296 : num);
8199			},
8200
8201			STRING: function(idx, size) {
8202				var str = '';
8203
8204				for (size += idx; idx < size; idx++) {
8205					str += String.fromCharCode(read(idx, 1));
8206				}
8207
8208				return str;
8209			}
8210		};
8211	};
8212});
8213
8214// Included from: src/javascript/runtime/html5/image/JPEGHeaders.js
8215
8216/**
8217 * JPEGHeaders.js
8218 *
8219 * Copyright 2013, Moxiecode Systems AB
8220 * Released under GPL License.
8221 *
8222 * License: http://www.plupload.com/license
8223 * Contributing: http://www.plupload.com/contributing
8224 */
8225 
8226/**
8227@class moxie/runtime/html5/image/JPEGHeaders
8228@private
8229*/
8230define("moxie/runtime/html5/image/JPEGHeaders", [
8231	"moxie/runtime/html5/utils/BinaryReader"
8232], function(BinaryReader) {
8233	
8234	return function JPEGHeaders(data) {
8235		var headers = [], read, idx, marker, length = 0;
8236
8237		read = new BinaryReader();
8238		read.init(data);
8239
8240		// Check if data is jpeg
8241		if (read.SHORT(0) !== 0xFFD8) {
8242			return;
8243		}
8244
8245		idx = 2;
8246
8247		while (idx <= data.length) {
8248			marker = read.SHORT(idx);
8249
8250			// omit RST (restart) markers
8251			if (marker >= 0xFFD0 && marker <= 0xFFD7) {
8252				idx += 2;
8253				continue;
8254			}
8255
8256			// no headers allowed after SOS marker
8257			if (marker === 0xFFDA || marker === 0xFFD9) {
8258				break;
8259			}
8260
8261			length = read.SHORT(idx + 2) + 2;
8262
8263			// APPn marker detected
8264			if (marker >= 0xFFE1 && marker <= 0xFFEF) {
8265				headers.push({
8266					hex: marker,
8267					name: 'APP' + (marker & 0x000F),
8268					start: idx,
8269					length: length,
8270					segment: read.SEGMENT(idx, length)
8271				});
8272			}
8273
8274			idx += length;
8275		}
8276
8277		read.init(null); // free memory
8278
8279		return {
8280			headers: headers,
8281
8282			restore: function(data) {
8283				var max, i;
8284
8285				read.init(data);
8286
8287				idx = read.SHORT(2) == 0xFFE0 ? 4 + read.SHORT(4) : 2;
8288
8289				for (i = 0, max = headers.length; i < max; i++) {
8290					read.SEGMENT(idx, 0, headers[i].segment);
8291					idx += headers[i].length;
8292				}
8293
8294				data = read.SEGMENT();
8295				read.init(null);
8296				return data;
8297			},
8298
8299			strip: function(data) {
8300				var headers, jpegHeaders, i;
8301
8302				jpegHeaders = new JPEGHeaders(data);
8303				headers = jpegHeaders.headers;
8304				jpegHeaders.purge();
8305
8306				read.init(data);
8307
8308				i = headers.length;
8309				while (i--) {
8310					read.SEGMENT(headers[i].start, headers[i].length, '');
8311				}
8312				
8313				data = read.SEGMENT();
8314				read.init(null);
8315				return data;
8316			},
8317
8318			get: function(name) {
8319				var array = [];
8320
8321				for (var i = 0, max = headers.length; i < max; i++) {
8322					if (headers[i].name === name.toUpperCase()) {
8323						array.push(headers[i].segment);
8324					}
8325				}
8326				return array;
8327			},
8328
8329			set: function(name, segment) {
8330				var array = [], i, ii, max;
8331
8332				if (typeof(segment) === 'string') {
8333					array.push(segment);
8334				} else {
8335					array = segment;
8336				}
8337
8338				for (i = ii = 0, max = headers.length; i < max; i++) {
8339					if (headers[i].name === name.toUpperCase()) {
8340						headers[i].segment = array[ii];
8341						headers[i].length = array[ii].length;
8342						ii++;
8343					}
8344					if (ii >= array.length) {
8345						break;
8346					}
8347				}
8348			},
8349
8350			purge: function() {
8351				headers = [];
8352				read.init(null);
8353				read = null;
8354			}
8355		};
8356	};
8357});
8358
8359// Included from: src/javascript/runtime/html5/image/ExifParser.js
8360
8361/**
8362 * ExifParser.js
8363 *
8364 * Copyright 2013, Moxiecode Systems AB
8365 * Released under GPL License.
8366 *
8367 * License: http://www.plupload.com/license
8368 * Contributing: http://www.plupload.com/contributing
8369 */
8370
8371/**
8372@class moxie/runtime/html5/image/ExifParser
8373@private
8374*/
8375define("moxie/runtime/html5/image/ExifParser", [
8376	"moxie/core/utils/Basic",
8377	"moxie/runtime/html5/utils/BinaryReader"
8378], function(Basic, BinaryReader) {
8379	
8380	return function ExifParser() {
8381		// Private ExifParser fields
8382		var data, tags, Tiff, offsets = {}, tagDescs;
8383
8384		data = new BinaryReader();
8385
8386		tags = {
8387			tiff : {
8388				/*
8389				The image orientation viewed in terms of rows and columns.
8390
8391				1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.
8392				2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.
8393				3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.
8394				4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
8395				5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.
8396				6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.
8397				7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
8398				8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
8399				*/
8400				0x0112: 'Orientation',
8401				0x010E: 'ImageDescription',
8402				0x010F: 'Make',
8403				0x0110: 'Model',
8404				0x0131: 'Software',
8405				0x8769: 'ExifIFDPointer',
8406				0x8825:	'GPSInfoIFDPointer'
8407			},
8408			exif : {
8409				0x9000: 'ExifVersion',
8410				0xA001: 'ColorSpace',
8411				0xA002: 'PixelXDimension',
8412				0xA003: 'PixelYDimension',
8413				0x9003: 'DateTimeOriginal',
8414				0x829A: 'ExposureTime',
8415				0x829D: 'FNumber',
8416				0x8827: 'ISOSpeedRatings',
8417				0x9201: 'ShutterSpeedValue',
8418				0x9202: 'ApertureValue'	,
8419				0x9207: 'MeteringMode',
8420				0x9208: 'LightSource',
8421				0x9209: 'Flash',
8422				0x920A: 'FocalLength',
8423				0xA402: 'ExposureMode',
8424				0xA403: 'WhiteBalance',
8425				0xA406: 'SceneCaptureType',
8426				0xA404: 'DigitalZoomRatio',
8427				0xA408: 'Contrast',
8428				0xA409: 'Saturation',
8429				0xA40A: 'Sharpness'
8430			},
8431			gps : {
8432				0x0000: 'GPSVersionID',
8433				0x0001: 'GPSLatitudeRef',
8434				0x0002: 'GPSLatitude',
8435				0x0003: 'GPSLongitudeRef',
8436				0x0004: 'GPSLongitude'
8437			}
8438		};
8439
8440		tagDescs = {
8441			'ColorSpace': {
8442				1: 'sRGB',
8443				0: 'Uncalibrated'
8444			},
8445
8446			'MeteringMode': {
8447				0: 'Unknown',
8448				1: 'Average',
8449				2: 'CenterWeightedAverage',
8450				3: 'Spot',
8451				4: 'MultiSpot',
8452				5: 'Pattern',
8453				6: 'Partial',
8454				255: 'Other'
8455			},
8456
8457			'LightSource': {
8458				1: 'Daylight',
8459				2: 'Fliorescent',
8460				3: 'Tungsten',
8461				4: 'Flash',
8462				9: 'Fine weather',
8463				10: 'Cloudy weather',
8464				11: 'Shade',
8465				12: 'Daylight fluorescent (D 5700 - 7100K)',
8466				13: 'Day white fluorescent (N 4600 -5400K)',
8467				14: 'Cool white fluorescent (W 3900 - 4500K)',
8468				15: 'White fluorescent (WW 3200 - 3700K)',
8469				17: 'Standard light A',
8470				18: 'Standard light B',
8471				19: 'Standard light C',
8472				20: 'D55',
8473				21: 'D65',
8474				22: 'D75',
8475				23: 'D50',
8476				24: 'ISO studio tungsten',
8477				255: 'Other'
8478			},
8479
8480			'Flash': {
8481				0x0000: 'Flash did not fire.',
8482				0x0001: 'Flash fired.',
8483				0x0005: 'Strobe return light not detected.',
8484				0x0007: 'Strobe return light detected.',
8485				0x0009: 'Flash fired, compulsory flash mode',
8486				0x000D: 'Flash fired, compulsory flash mode, return light not detected',
8487				0x000F: 'Flash fired, compulsory flash mode, return light detected',
8488				0x0010: 'Flash did not fire, compulsory flash mode',
8489				0x0018: 'Flash did not fire, auto mode',
8490				0x0019: 'Flash fired, auto mode',
8491				0x001D: 'Flash fired, auto mode, return light not detected',
8492				0x001F: 'Flash fired, auto mode, return light detected',
8493				0x0020: 'No flash function',
8494				0x0041: 'Flash fired, red-eye reduction mode',
8495				0x0045: 'Flash fired, red-eye reduction mode, return light not detected',
8496				0x0047: 'Flash fired, red-eye reduction mode, return light detected',
8497				0x0049: 'Flash fired, compulsory flash mode, red-eye reduction mode',
8498				0x004D: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',
8499				0x004F: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',
8500				0x0059: 'Flash fired, auto mode, red-eye reduction mode',
8501				0x005D: 'Flash fired, auto mode, return light not detected, red-eye reduction mode',
8502				0x005F: 'Flash fired, auto mode, return light detected, red-eye reduction mode'
8503			},
8504
8505			'ExposureMode': {
8506				0: 'Auto exposure',
8507				1: 'Manual exposure',
8508				2: 'Auto bracket'
8509			},
8510
8511			'WhiteBalance': {
8512				0: 'Auto white balance',
8513				1: 'Manual white balance'
8514			},
8515
8516			'SceneCaptureType': {
8517				0: 'Standard',
8518				1: 'Landscape',
8519				2: 'Portrait',
8520				3: 'Night scene'
8521			},
8522
8523			'Contrast': {
8524				0: 'Normal',
8525				1: 'Soft',
8526				2: 'Hard'
8527			},
8528
8529			'Saturation': {
8530				0: 'Normal',
8531				1: 'Low saturation',
8532				2: 'High saturation'
8533			},
8534
8535			'Sharpness': {
8536				0: 'Normal',
8537				1: 'Soft',
8538				2: 'Hard'
8539			},
8540
8541			// GPS related
8542			'GPSLatitudeRef': {
8543				N: 'North latitude',
8544				S: 'South latitude'
8545			},
8546
8547			'GPSLongitudeRef': {
8548				E: 'East longitude',
8549				W: 'West longitude'
8550			}
8551		};
8552
8553		function extractTags(IFD_offset, tags2extract) {
8554			var length = data.SHORT(IFD_offset), i, ii,
8555				tag, type, count, tagOffset, offset, value, values = [], hash = {};
8556
8557			for (i = 0; i < length; i++) {
8558				// Set binary reader pointer to beginning of the next tag
8559				offset = tagOffset = IFD_offset + 12 * i + 2;
8560
8561				tag = tags2extract[data.SHORT(offset)];
8562
8563				if (tag === undefined) {
8564					continue; // Not the tag we requested
8565				}
8566
8567				type = data.SHORT(offset+=2);
8568				count = data.LONG(offset+=2);
8569
8570				offset += 4;
8571				values = [];
8572
8573				switch (type) {
8574					case 1: // BYTE
8575					case 7: // UNDEFINED
8576						if (count > 4) {
8577							offset = data.LONG(offset) + offsets.tiffHeader;
8578						}
8579
8580						for (ii = 0; ii < count; ii++) {
8581							values[ii] = data.BYTE(offset + ii);
8582						}
8583
8584						break;
8585
8586					case 2: // STRING
8587						if (count > 4) {
8588							offset = data.LONG(offset) + offsets.tiffHeader;
8589						}
8590
8591						hash[tag] = data.STRING(offset, count - 1);
8592
8593						continue;
8594
8595					case 3: // SHORT
8596						if (count > 2) {
8597							offset = data.LONG(offset) + offsets.tiffHeader;
8598						}
8599
8600						for (ii = 0; ii < count; ii++) {
8601							values[ii] = data.SHORT(offset + ii*2);
8602						}
8603
8604						break;
8605
8606					case 4: // LONG
8607						if (count > 1) {
8608							offset = data.LONG(offset) + offsets.tiffHeader;
8609						}
8610
8611						for (ii = 0; ii < count; ii++) {
8612							values[ii] = data.LONG(offset + ii*4);
8613						}
8614
8615						break;
8616
8617					case 5: // RATIONAL
8618						offset = data.LONG(offset) + offsets.tiffHeader;
8619
8620						for (ii = 0; ii < count; ii++) {
8621							values[ii] = data.LONG(offset + ii*4) / data.LONG(offset + ii*4 + 4);
8622						}
8623
8624						break;
8625
8626					case 9: // SLONG
8627						offset = data.LONG(offset) + offsets.tiffHeader;
8628
8629						for (ii = 0; ii < count; ii++) {
8630							values[ii] = data.SLONG(offset + ii*4);
8631						}
8632
8633						break;
8634
8635					case 10: // SRATIONAL
8636						offset = data.LONG(offset) + offsets.tiffHeader;
8637
8638						for (ii = 0; ii < count; ii++) {
8639							values[ii] = data.SLONG(offset + ii*4) / data.SLONG(offset + ii*4 + 4);
8640						}
8641
8642						break;
8643
8644					default:
8645						continue;
8646				}
8647
8648				value = (count == 1 ? values[0] : values);
8649
8650				if (tagDescs.hasOwnProperty(tag) && typeof value != 'object') {
8651					hash[tag] = tagDescs[tag][value];
8652				} else {
8653					hash[tag] = value;
8654				}
8655			}
8656
8657			return hash;
8658		}
8659
8660		function getIFDOffsets() {
8661			var idx = offsets.tiffHeader;
8662
8663			// Set read order of multi-byte data
8664			data.II(data.SHORT(idx) == 0x4949);
8665
8666			// Check if always present bytes are indeed present
8667			if (data.SHORT(idx+=2) !== 0x002A) {
8668				return false;
8669			}
8670
8671			offsets.IFD0 = offsets.tiffHeader + data.LONG(idx += 2);
8672			Tiff = extractTags(offsets.IFD0, tags.tiff);
8673
8674			if ('ExifIFDPointer' in Tiff) {
8675				offsets.exifIFD = offsets.tiffHeader + Tiff.ExifIFDPointer;
8676				delete Tiff.ExifIFDPointer;
8677			}
8678
8679			if ('GPSInfoIFDPointer' in Tiff) {
8680				offsets.gpsIFD = offsets.tiffHeader + Tiff.GPSInfoIFDPointer;
8681				delete Tiff.GPSInfoIFDPointer;
8682			}
8683			return true;
8684		}
8685
8686		// At the moment only setting of simple (LONG) values, that do not require offset recalculation, is supported
8687		function setTag(ifd, tag, value) {
8688			var offset, length, tagOffset, valueOffset = 0;
8689
8690			// If tag name passed translate into hex key
8691			if (typeof(tag) === 'string') {
8692				var tmpTags = tags[ifd.toLowerCase()];
8693				for (var hex in tmpTags) {
8694					if (tmpTags[hex] === tag) {
8695						tag = hex;
8696						break;
8697					}
8698				}
8699			}
8700			offset = offsets[ifd.toLowerCase() + 'IFD'];
8701			length = data.SHORT(offset);
8702
8703			for (var i = 0; i < length; i++) {
8704				tagOffset = offset + 12 * i + 2;
8705
8706				if (data.SHORT(tagOffset) == tag) {
8707					valueOffset = tagOffset + 8;
8708					break;
8709				}
8710			}
8711
8712			if (!valueOffset) {
8713				return false;
8714			}
8715
8716			data.LONG(valueOffset, value);
8717			return true;
8718		}
8719
8720
8721		// Public functions
8722		return {
8723			init: function(segment) {
8724				// Reset internal data
8725				offsets = {
8726					tiffHeader: 10
8727				};
8728
8729				if (segment === undefined || !segment.length) {
8730					return false;
8731				}
8732
8733				data.init(segment);
8734
8735				// Check if that's APP1 and that it has EXIF
8736				if (data.SHORT(0) === 0xFFE1 && data.STRING(4, 5).toUpperCase() === "EXIF\0") {
8737					return getIFDOffsets();
8738				}
8739				return false;
8740			},
8741
8742			TIFF: function() {
8743				return Tiff;
8744			},
8745
8746			EXIF: function() {
8747				var Exif;
8748
8749				// Populate EXIF hash
8750				Exif = extractTags(offsets.exifIFD, tags.exif);
8751
8752				// Fix formatting of some tags
8753				if (Exif.ExifVersion && Basic.typeOf(Exif.ExifVersion) === 'array') {
8754					for (var i = 0, exifVersion = ''; i < Exif.ExifVersion.length; i++) {
8755						exifVersion += String.fromCharCode(Exif.ExifVersion[i]);
8756					}
8757					Exif.ExifVersion = exifVersion;
8758				}
8759
8760				return Exif;
8761			},
8762
8763			GPS: function() {
8764				var GPS;
8765
8766				GPS = extractTags(offsets.gpsIFD, tags.gps);
8767
8768				// iOS devices (and probably some others) do not put in GPSVersionID tag (why?..)
8769				if (GPS.GPSVersionID && Basic.typeOf(GPS.GPSVersionID) === 'array') {
8770					GPS.GPSVersionID = GPS.GPSVersionID.join('.');
8771				}
8772
8773				return GPS;
8774			},
8775
8776			setExif: function(tag, value) {
8777				// Right now only setting of width/height is possible
8778				if (tag !== 'PixelXDimension' && tag !== 'PixelYDimension') {return false;}
8779
8780				return setTag('exif', tag, value);
8781			},
8782
8783
8784			getBinary: function() {
8785				return data.SEGMENT();
8786			},
8787
8788			purge: function() {
8789				data.init(null);
8790				data = Tiff = null;
8791				offsets = {};
8792			}
8793		};
8794	};
8795});
8796
8797// Included from: src/javascript/runtime/html5/image/JPEG.js
8798
8799/**
8800 * JPEG.js
8801 *
8802 * Copyright 2013, Moxiecode Systems AB
8803 * Released under GPL License.
8804 *
8805 * License: http://www.plupload.com/license
8806 * Contributing: http://www.plupload.com/contributing
8807 */
8808
8809/**
8810@class moxie/runtime/html5/image/JPEG
8811@private
8812*/
8813define("moxie/runtime/html5/image/JPEG", [
8814	"moxie/core/utils/Basic",
8815	"moxie/core/Exceptions",
8816	"moxie/runtime/html5/image/JPEGHeaders",
8817	"moxie/runtime/html5/utils/BinaryReader",
8818	"moxie/runtime/html5/image/ExifParser"
8819], function(Basic, x, JPEGHeaders, BinaryReader, ExifParser) {
8820	
8821	function JPEG(binstr) {
8822		var _binstr, _br, _hm, _ep, _info, hasExif;
8823
8824		function _getDimensions() {
8825			var idx = 0, marker, length;
8826
8827			// examine all through the end, since some images might have very large APP segments
8828			while (idx <= _binstr.length) {
8829				marker = _br.SHORT(idx += 2);
8830
8831				if (marker >= 0xFFC0 && marker <= 0xFFC3) { // SOFn
8832					idx += 5; // marker (2 bytes) + length (2 bytes) + Sample precision (1 byte)
8833					return {
8834						height: _br.SHORT(idx),
8835						width: _br.SHORT(idx += 2)
8836					};
8837				}
8838				length = _br.SHORT(idx += 2);
8839				idx += length - 2;
8840			}
8841			return null;
8842		}
8843
8844		_binstr = binstr;
8845
8846		_br = new BinaryReader();
8847		_br.init(_binstr);
8848
8849		// check if it is jpeg
8850		if (_br.SHORT(0) !== 0xFFD8) {
8851			throw new x.ImageError(x.ImageError.WRONG_FORMAT);
8852		}
8853
8854		// backup headers
8855		_hm = new JPEGHeaders(binstr);
8856
8857		// extract exif info
8858		_ep = new ExifParser();
8859		hasExif = !!_ep.init(_hm.get('app1')[0]);
8860
8861		// get dimensions
8862		_info = _getDimensions.call(this);
8863
8864		Basic.extend(this, {
8865			type: 'image/jpeg',
8866
8867			size: _binstr.length,
8868
8869			width: _info && _info.width || 0,
8870
8871			height: _info && _info.height || 0,
8872
8873			setExif: function(tag, value) {
8874				if (!hasExif) {
8875					return false; // or throw an exception
8876				}
8877
8878				if (Basic.typeOf(tag) === 'object') {
8879					Basic.each(tag, function(value, tag) {
8880						_ep.setExif(tag, value);
8881					});
8882				} else {
8883					_ep.setExif(tag, value);
8884				}
8885
8886				// update internal headers
8887				_hm.set('app1', _ep.getBinary());
8888			},
8889
8890			writeHeaders: function() {
8891				if (!arguments.length) {
8892					// if no arguments passed, update headers internally
8893					return (_binstr = _hm.restore(_binstr));
8894				}
8895				return _hm.restore(arguments[0]);
8896			},
8897
8898			stripHeaders: function(binstr) {
8899				return _hm.strip(binstr);
8900			},
8901
8902			purge: function() {
8903				_purge.call(this);
8904			}
8905		});
8906
8907		if (hasExif) {
8908			this.meta = {
8909				tiff: _ep.TIFF(),
8910				exif: _ep.EXIF(),
8911				gps: _ep.GPS()
8912			};
8913		}
8914
8915		function _purge() {
8916			if (!_ep || !_hm || !_br) { 
8917				return; // ignore any repeating purge requests
8918			}
8919			_ep.purge();
8920			_hm.purge();
8921			_br.init(null);
8922			_binstr = _info = _hm = _ep = _br = null;
8923		}
8924	}
8925
8926	return JPEG;
8927});
8928
8929// Included from: src/javascript/runtime/html5/image/PNG.js
8930
8931/**
8932 * PNG.js
8933 *
8934 * Copyright 2013, Moxiecode Systems AB
8935 * Released under GPL License.
8936 *
8937 * License: http://www.plupload.com/license
8938 * Contributing: http://www.plupload.com/contributing
8939 */
8940
8941/**
8942@class moxie/runtime/html5/image/PNG
8943@private
8944*/
8945define("moxie/runtime/html5/image/PNG", [
8946	"moxie/core/Exceptions",
8947	"moxie/core/utils/Basic",
8948	"moxie/runtime/html5/utils/BinaryReader"
8949], function(x, Basic, BinaryReader) {
8950	
8951	function PNG(binstr) {
8952		var _binstr, _br, _hm, _ep, _info;
8953
8954		_binstr = binstr;
8955
8956		_br = new BinaryReader();
8957		_br.init(_binstr);
8958
8959		// check if it's png
8960		(function() {
8961			var idx = 0, i = 0
8962			, signature = [0x8950, 0x4E47, 0x0D0A, 0x1A0A]
8963			;
8964
8965			for (i = 0; i < signature.length; i++, idx += 2) {
8966				if (signature[i] != _br.SHORT(idx)) {
8967					throw new x.ImageError(x.ImageError.WRONG_FORMAT);
8968				}
8969			}
8970		}());
8971
8972		function _getDimensions() {
8973			var chunk, idx;
8974
8975			chunk = _getChunkAt.call(this, 8);
8976
8977			if (chunk.type == 'IHDR') {
8978				idx = chunk.start;
8979				return {
8980					width: _br.LONG(idx),
8981					height: _br.LONG(idx += 4)
8982				};
8983			}
8984			return null;
8985		}
8986
8987		function _purge() {
8988			if (!_br) {
8989				return; // ignore any repeating purge requests
8990			}
8991			_br.init(null);
8992			_binstr = _info = _hm = _ep = _br = null;
8993		}
8994
8995		_info = _getDimensions.call(this);
8996
8997		Basic.extend(this, {
8998			type: 'image/png',
8999
9000			size: _binstr.length,
9001
9002			width: _info.width,
9003
9004			height: _info.height,
9005
9006			purge: function() {
9007				_purge.call(this);
9008			}
9009		});
9010
9011		// for PNG we can safely trigger purge automatically, as we do not keep any data for later
9012		_purge.call(this);
9013
9014		function _getChunkAt(idx) {
9015			var length, type, start, CRC;
9016
9017			length = _br.LONG(idx);
9018			type = _br.STRING(idx += 4, 4);
9019			start = idx += 4;
9020			CRC = _br.LONG(idx + length);
9021
9022			return {
9023				length: length,
9024				type: type,
9025				start: start,
9026				CRC: CRC
9027			};
9028		}
9029	}
9030
9031	return PNG;
9032});
9033
9034// Included from: src/javascript/runtime/html5/image/ImageInfo.js
9035
9036/**
9037 * ImageInfo.js
9038 *
9039 * Copyright 2013, Moxiecode Systems AB
9040 * Released under GPL License.
9041 *
9042 * License: http://www.plupload.com/license
9043 * Contributing: http://www.plupload.com/contributing
9044 */
9045
9046/**
9047@class moxie/runtime/html5/image/ImageInfo
9048@private
9049*/
9050define("moxie/runtime/html5/image/ImageInfo", [
9051	"moxie/core/utils/Basic",
9052	"moxie/core/Exceptions",
9053	"moxie/runtime/html5/image/JPEG",
9054	"moxie/runtime/html5/image/PNG"
9055], function(Basic, x, JPEG, PNG) {
9056	/**
9057	Optional image investigation tool for HTML5 runtime. Provides the following features:
9058	- ability to distinguish image type (JPEG or PNG) by signature
9059	- ability to extract image width/height directly from it's internals, without preloading in memory (fast)
9060	- ability to extract APP headers from JPEGs (Exif, GPS, etc)
9061	- ability to replace width/height tags in extracted JPEG headers
9062	- ability to restore APP headers, that were for example stripped during image manipulation
9063
9064	@class ImageInfo
9065	@constructor
9066	@param {String} binstr Image source as binary string
9067	*/
9068	return function(binstr) {
9069		var _cs = [JPEG, PNG], _img;
9070
9071		// figure out the format, throw: ImageError.WRONG_FORMAT if not supported
9072		_img = (function() {
9073			for (var i = 0; i < _cs.length; i++) {
9074				try {
9075					return new _cs[i](binstr);
9076				} catch (ex) {
9077					// console.info(ex);
9078				}
9079			}
9080			throw new x.ImageError(x.ImageError.WRONG_FORMAT);
9081		}());
9082
9083		Basic.extend(this, {
9084			/**
9085			Image Mime Type extracted from it's depths
9086
9087			@property type
9088			@type {String}
9089			@default ''
9090			*/
9091			type: '',
9092
9093			/**
9094			Image size in bytes
9095
9096			@property size
9097			@type {Number}
9098			@default 0
9099			*/
9100			size: 0,
9101
9102			/**
9103			Image width extracted from image source
9104
9105			@property width
9106			@type {Number}
9107			@default 0
9108			*/
9109			width: 0,
9110
9111			/**
9112			Image height extracted from image source
9113
9114			@property height
9115			@type {Number}
9116			@default 0
9117			*/
9118			height: 0,
9119
9120			/**
9121			Sets Exif tag. Currently applicable only for width and height tags. Obviously works only with JPEGs.
9122
9123			@method setExif
9124			@param {String} tag Tag to set
9125			@param {Mixed} value Value to assign to the tag
9126			*/
9127			setExif: function() {},
9128
9129			/**
9130			Restores headers to the source.
9131
9132			@method writeHeaders
9133			@param {String} data Image source as binary string
9134			@return {String} Updated binary string
9135			*/
9136			writeHeaders: function(data) {
9137				return data;
9138			},
9139
9140			/**
9141			Strip all headers from the source.
9142
9143			@method stripHeaders
9144			@param {String} data Image source as binary string
9145			@return {String} Updated binary string
9146			*/
9147			stripHeaders: function(data) {
9148				return data;
9149			},
9150
9151			/**
9152			Dispose resources.
9153
9154			@method purge
9155			*/
9156			purge: function() {}
9157		});
9158
9159		Basic.extend(this, _img);
9160
9161		this.purge = function() {
9162			_img.purge();
9163			_img = null;
9164		};
9165	};
9166});
9167
9168// Included from: src/javascript/runtime/html5/image/MegaPixel.js
9169
9170/**
9171(The MIT License)
9172
9173Copyright (c) 2012 Shinichi Tomita <[email protected]>;
9174
9175Permission is hereby granted, free of charge, to any person obtaining
9176a copy of this software and associated documentation files (the
9177'Software'), to deal in the Software without restriction, including
9178without limitation the rights to use, copy, modify, merge, publish,
9179distribute, sublicense, and/or sell copies of the Software, and to
9180permit persons to whom the Software is furnished to do so, subject to
9181the following conditions:
9182
9183The above copyright notice and this permission notice shall be
9184included in all copies or substantial portions of the Software.
9185
9186THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
9187EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
9188MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
9189IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
9190CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
9191TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
9192SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9193*/
9194
9195/**
9196 * Mega pixel image rendering library for iOS6 Safari
9197 *
9198 * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel),
9199 * which causes unexpected subsampling when drawing it in canvas.
9200 * By using this library, you can safely render the image with proper stretching.
9201 *
9202 * Copyright (c) 2012 Shinichi Tomita <[email protected]>
9203 * Released under the MIT license
9204 */
9205
9206/**
9207@class moxie/runtime/html5/image/MegaPixel
9208@private
9209*/
9210define("moxie/runtime/html5/image/MegaPixel", [], function() {
9211
9212	/**
9213	 * Rendering image element (with resizing) into the canvas element
9214	 */
9215	function renderImageToCanvas(img, canvas, options) {
9216		var iw = img.naturalWidth, ih = img.naturalHeight;
9217		var width = options.width, height = options.height;
9218		var x = options.x || 0, y = options.y || 0;
9219		var ctx = canvas.getContext('2d');
9220		if (detectSubsampling(img)) {
9221			iw /= 2;
9222			ih /= 2;
9223		}
9224		var d = 1024; // size of tiling canvas
9225		var tmpCanvas = document.createElement('canvas');
9226		tmpCanvas.width = tmpCanvas.height = d;
9227		var tmpCtx = tmpCanvas.getContext('2d');
9228		var vertSquashRatio = detectVerticalSquash(img, iw, ih);
9229		var sy = 0;
9230		while (sy < ih) {
9231			var sh = sy + d > ih ? ih - sy : d;
9232			var sx = 0;
9233			while (sx < iw) {
9234				var sw = sx + d > iw ? iw - sx : d;
9235				tmpCtx.clearRect(0, 0, d, d);
9236				tmpCtx.drawImage(img, -sx, -sy);
9237				var dx = (sx * width / iw + x) << 0;
9238				var dw = Math.ceil(sw * width / iw);
9239				var dy = (sy * height / ih / vertSquashRatio + y) << 0;
9240				var dh = Math.ceil(sh * height / ih / vertSquashRatio);
9241				ctx.drawImage(tmpCanvas, 0, 0, sw, sh, dx, dy, dw, dh);
9242				sx += d;
9243			}
9244			sy += d;
9245		}
9246		tmpCanvas = tmpCtx = null;
9247	}
9248
9249	/**
9250	 * Detect subsampling in loaded image.
9251	 * In iOS, larger images than 2M pixels may be subsampled in rendering.
9252	 */
9253	function detectSubsampling(img) {
9254		var iw = img.naturalWidth, ih = img.naturalHeight;
9255		if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image
9256			var canvas = document.createElement('canvas');
9257			canvas.width = canvas.height = 1;
9258			var ctx = canvas.getContext('2d');
9259			ctx.drawImage(img, -iw + 1, 0);
9260			// subsampled image becomes half smaller in rendering size.
9261			// check alpha channel value to confirm image is covering edge pixel or not.
9262			// if alpha value is 0 image is not covering, hence subsampled.
9263			return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
9264		} else {
9265			return false;
9266		}
9267	}
9268
9269
9270	/**
9271	 * Detecting vertical squash in loaded image.
9272	 * Fixes a bug which squash image vertically while drawing into canvas for some images.
9273	 */
9274	function detectVerticalSquash(img, iw, ih) {
9275		var canvas = document.createElement('canvas');
9276		canvas.width = 1;
9277		canvas.height = ih;
9278		var ctx = canvas.getContext('2d');
9279		ctx.drawImage(img, 0, 0);
9280		var data = ctx.getImageData(0, 0, 1, ih).data;
9281		// search image edge pixel position in case it is squashed vertically.
9282		var sy = 0;
9283		var ey = ih;
9284		var py = ih;
9285		while (py > sy) {
9286			var alpha = data[(py - 1) * 4 + 3];
9287			if (alpha === 0) {
9288				ey = py;
9289			} else {
9290			sy = py;
9291			}
9292			py = (ey + sy) >> 1;
9293		}
9294		canvas = null;
9295		var ratio = (py / ih);
9296		return (ratio === 0) ? 1 : ratio;
9297	}
9298
9299	return {
9300		isSubsampled: detectSubsampling,
9301		renderTo: renderImageToCanvas
9302	};
9303});
9304
9305// Included from: src/javascript/runtime/html5/image/Image.js
9306
9307/**
9308 * Image.js
9309 *
9310 * Copyright 2013, Moxiecode Systems AB
9311 * Released under GPL License.
9312 *
9313 * License: http://www.plupload.com/license
9314 * Contributing: http://www.plupload.com/contributing
9315 */
9316
9317/**
9318@class moxie/runtime/html5/image/Image
9319@private
9320*/
9321define("moxie/runtime/html5/image/Image", [
9322	"moxie/runtime/html5/Runtime",
9323	"moxie/core/utils/Basic",
9324	"moxie/core/Exceptions",
9325	"moxie/core/utils/Encode",
9326	"moxie/file/File",
9327	"moxie/runtime/html5/image/ImageInfo",
9328	"moxie/runtime/html5/image/MegaPixel",
9329	"moxie/core/utils/Mime",
9330	"moxie/core/utils/Env"
9331], function(extensions, Basic, x, Encode, File, ImageInfo, MegaPixel, Mime, Env) {
9332	
9333	function HTML5Image() {
9334		var me = this
9335		, _img, _imgInfo, _canvas, _binStr, _blob
9336		, _modified = false // is set true whenever image is modified
9337		, _preserveHeaders = true
9338		;
9339
9340		Basic.extend(this, {
9341			loadFromBlob: function(blob) {
9342				var comp = this, I = comp.getRuntime()
9343				, asBinary = arguments.length > 1 ? arguments[1] : true
9344				;
9345
9346				if (!I.can('access_binary')) {
9347					throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
9348				}
9349
9350				_blob = blob;
9351
9352				if (blob.isDetached()) {
9353					_binStr = blob.getSource();
9354					_preload.call(this, _binStr);
9355					return;
9356				} else {
9357					_readAsDataUrl.call(this, blob.getSource(), function(dataUrl) {
9358						if (asBinary) {
9359							_binStr = _toBinary(dataUrl);
9360						}
9361						_preload.call(comp, dataUrl);
9362					});
9363				}
9364			},
9365
9366			loadFromImage: function(img, exact) {
9367				this.meta = img.meta;
9368
9369				_blob = new File(null, {
9370					name: img.name,
9371					size: img.size,
9372					type: img.type
9373				});
9374
9375				_preload.call(this, exact ? (_binStr = img.getAsBinaryString()) : img.getAsDataURL());
9376			},
9377
9378			getInfo: function() {
9379				var I = this.getRuntime(), info;
9380
9381				if (!_imgInfo && _binStr && I.can('access_image_binary')) {
9382					_imgInfo = new ImageInfo(_binStr);
9383				}
9384
9385				info = {
9386					width: _getImg().width || 0,
9387					height: _getImg().height || 0,
9388					type: _blob.type || Mime.getFileMime(_blob.name),
9389					size: _binStr && _binStr.length || _blob.size || 0,
9390					name: _blob.name || '',
9391					meta: _imgInfo && _imgInfo.meta || this.meta || {}
9392				};
9393
9394				return info;
9395			},
9396
9397			downsize: function() {
9398				_downsize.apply(this, arguments);
9399			},
9400
9401			getAsCanvas: function() {
9402				if (_canvas) {
9403					_canvas.id = this.uid + '_canvas';
9404				}
9405				return _canvas;
9406			},
9407
9408			getAsBlob: function(type, quality) {
9409				if (type !== this.type) {
9410					// if different mime type requested prepare image for conversion
9411					_downsize.call(this, this.width, this.height, false);
9412				}
9413				return new File(null, {
9414					name: _blob.name || '',
9415					type: type,
9416					data: me.getAsBinaryString.call(this, type, quality)
9417				});
9418			},
9419
9420			getAsDataURL: function(type) {
9421				var quality = arguments[1] || 90;
9422
9423				// if image has not been modified, return the source right away
9424				if (!_modified) {
9425					return _img.src;
9426				}
9427
9428				if ('image/jpeg' !== type) {
9429					return _canvas.toDataURL('image/png');
9430				} else {
9431					try {
9432						// older Geckos used to result in an exception on quality argument
9433						return _canvas.toDataURL('image/jpeg', quality/100);
9434					} catch (ex) {
9435						return _canvas.toDataURL('image/jpeg');
9436					}
9437				}
9438			},
9439
9440			getAsBinaryString: function(type, quality) {
9441				// if image has not been modified, return the source right away
9442				if (!_modified) {
9443					// if image was not loaded from binary string
9444					if (!_binStr) {
9445						_binStr = _toBinary(me.getAsDataURL(type, quality));
9446					}
9447					return _binStr;
9448				}
9449
9450				if ('image/jpeg' !== type) {
9451					_binStr = _toBinary(me.getAsDataURL(type, quality));
9452				} else {
9453					var dataUrl;
9454
9455					// if jpeg
9456					if (!quality) {
9457						quality = 90;
9458					}
9459
9460					try {
9461						// older Geckos used to result in an exception on quality argument
9462						dataUrl = _canvas.toDataURL('image/jpeg', quality/100);
9463					} catch (ex) {
9464						dataUrl = _canvas.toDataURL('image/jpeg');
9465					}
9466
9467					_binStr = _toBinary(dataUrl);
9468
9469					if (_imgInfo) {
9470						_binStr = _imgInfo.stripHeaders(_binStr);
9471
9472						if (_preserveHeaders) {
9473							// update dimensions info in exif
9474							if (_imgInfo.meta && _imgInfo.meta.exif) {
9475								_imgInfo.setExif({
9476									PixelXDimension: this.width,
9477									PixelYDimension: this.height
9478								});
9479							}
9480
9481							// re-inject the headers
9482							_binStr = _imgInfo.writeHeaders(_binStr);
9483						}
9484
9485						// will be re-created from fresh on next getInfo call
9486						_imgInfo.purge();
9487						_imgInfo = null;
9488					}
9489				}
9490
9491				_modified = false;
9492
9493				return _binStr;
9494			},
9495
9496			destroy: function() {
9497				me = null;
9498				_purge.call(this);
9499				this.getRuntime().getShim().removeInstance(this.uid);
9500			}
9501		});
9502
9503
9504		function _getImg() {
9505			if (!_canvas && !_img) {
9506				throw new x.ImageError(x.DOMException.INVALID_STATE_ERR);
9507			}
9508			return _canvas || _img;
9509		}
9510
9511
9512		function _toBinary(str) {
9513			return Encode.atob(str.substring(str.indexOf('base64,') + 7));
9514		}
9515
9516
9517		function _toDataUrl(str, type) {
9518			return 'data:' + (type || '') + ';base64,' + Encode.btoa(str);
9519		}
9520
9521
9522		function _preload(str) {
9523			var comp = this;
9524
9525			_img = new Image();
9526			_img.onerror = function() {
9527				_purge.call(this);
9528				comp.trigger('error', x.ImageError.WRONG_FORMAT);
9529			};
9530			_img.onload = function() {
9531				comp.trigger('load');
9532			};
9533
9534			_img.src = /^data:[^;]*;base64,/.test(str) ? str : _toDataUrl(str, _blob.type);
9535		}
9536
9537
9538		function _readAsDataUrl(file, callback) {
9539			var comp = this, fr;
9540
9541			// use FileReader if it's available
9542			if (window.FileReader) {
9543				fr = new FileReader();
9544				fr.onload = function() {
9545					callback(this.result);
9546				};
9547				fr.onerror = function() {
9548					comp.trigger('error', x.ImageError.WRONG_FORMAT);
9549				};
9550				fr.readAsDataURL(file);
9551			} else {
9552				return callback(file.getAsDataURL());
9553			}
9554		}
9555
9556		function _downsize(width, height, crop, preserveHeaders) {
9557			var self = this
9558			, scale
9559			, mathFn
9560			, x = 0
9561			, y = 0
9562			, img
9563			, destWidth
9564			, destHeight
9565			, orientation
9566			;
9567
9568			_preserveHeaders = preserveHeaders; // we will need to check this on export (see getAsBinaryString())
9569
9570			// take into account orientation tag
9571			orientation = (this.meta && this.meta.tiff && this.meta.tiff.Orientation) || 1;
9572
9573			if (Basic.inArray(orientation, [5,6,7,8]) !== -1) { // values that require 90 degree rotation
9574				// swap dimensions
9575				var tmp = width;
9576				width = height;
9577				height = tmp;
9578			}
9579
9580			img = _getImg();
9581
9582			// unify dimensions
9583			if (!crop) {
9584				scale = Math.min(width/img.width, height/img.height);
9585			} else {
9586				// one of the dimensions may exceed the actual image dimensions - we need to take the smallest value
9587				width = Math.min(width, img.width);
9588				height = Math.min(height, img.height);
9589
9590				scale = Math.max(width/img.width, height/img.height);
9591			}
9592		
9593			// we only downsize here
9594			if (scale > 1 && !crop && preserveHeaders) {
9595				this.trigger('Resize');
9596				return;
9597			}
9598
9599			// prepare canvas if necessary
9600			if (!_canvas) {
9601				_canvas = document.createElement("canvas");
9602			}
9603
9604			// calculate dimensions of proportionally resized image
9605			destWidth = Math.round(img.width * scale);	
9606			destHeight = Math.round(img.height * scale);
9607
9608			// scale image and canvas
9609			if (crop) {
9610				_canvas.width = width;
9611				_canvas.height = height;
9612
9613				// if dimensions of the resulting image still larger than canvas, center it
9614				if (destWidth > width) {
9615					x = Math.round((destWidth - width) / 2);
9616				}
9617
9618				if (destHeight > height) {
9619					y = Math.round((destHeight - height) / 2);
9620				}
9621			} else {
9622				_canvas.width = destWidth;
9623				_canvas.height = destHeight;
9624			}
9625
9626			// rotate if required, according to orientation tag
9627			if (!_preserveHeaders) {
9628				_rotateToOrientaion(_canvas.width, _canvas.height, orientation);
9629			}
9630
9631			_drawToCanvas.call(this, img, _canvas, -x, -y, destWidth, destHeight);
9632
9633			this.width = _canvas.width;
9634			this.height = _canvas.height;
9635
9636			_modified = true;
9637			self.trigger('Resize');
9638		}
9639
9640
9641		function _drawToCanvas(img, canvas, x, y, w, h) {
9642			if (Env.OS === 'iOS') { 
9643				// avoid squish bug in iOS6
9644				MegaPixel.renderTo(img, canvas, { width: w, height: h, x: x, y: y });
9645			} else {
9646				var ctx = canvas.getContext('2d');
9647				ctx.drawImage(img, x, y, w, h);
9648			}
9649		}
9650
9651
9652		/**
9653		* Transform canvas coordination according to specified frame size and orientation
9654		* Orientation value is from EXIF tag
9655		* @author Shinichi Tomita <[email protected]>
9656		*/
9657		function _rotateToOrientaion(width, height, orientation) {
9658			switch (orientation) {
9659				case 5:
9660				case 6:
9661				case 7:
9662				case 8:
9663					_canvas.width = height;
9664					_canvas.height = width;
9665					break;
9666				default:
9667					_canvas.width = width;
9668					_canvas.height = height;
9669			}
9670
9671			/**
9672			1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.
9673			2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.
9674			3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.
9675			4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
9676			5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.
9677			6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.
9678			7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
9679			8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
9680			*/
9681
9682			var ctx = _canvas.getContext('2d');
9683			switch (orientation) {
9684				case 2:
9685					// horizontal flip
9686					ctx.translate(width, 0);
9687					ctx.scale(-1, 1);
9688					break;
9689				case 3:
9690					// 180 rotate left
9691					ctx.translate(width, height);
9692					ctx.rotate(Math.PI);
9693					break;
9694				case 4:
9695					// vertical flip
9696					ctx.translate(0, height);
9697					ctx.scale(1, -1);
9698					break;
9699				case 5:
9700					// vertical flip + 90 rotate right
9701					ctx.rotate(0.5 * Math.PI);
9702					ctx.scale(1, -1);
9703					break;
9704				case 6:
9705					// 90 rotate right
9706					ctx.rotate(0.5 * Math.PI);
9707					ctx.translate(0, -height);
9708					break;
9709				case 7:
9710					// horizontal flip + 90 rotate right
9711					ctx.rotate(0.5 * Math.PI);
9712					ctx.translate(width, -height);
9713					ctx.scale(-1, 1);
9714					break;
9715				case 8:
9716					// 90 rotate left
9717					ctx.rotate(-0.5 * Math.PI);
9718					ctx.translate(-width, 0);
9719					break;
9720			}
9721		}
9722
9723
9724		function _purge() {
9725			if (_imgInfo) {
9726				_imgInfo.purge();
9727				_imgInfo = null;
9728			}
9729			_binStr = _img = _canvas = _blob = null;
9730			_modified = false;
9731		}
9732	}
9733
9734	return (extensions.Image = HTML5Image);
9735});
9736
9737// Included from: src/javascript/runtime/flash/Runtime.js
9738
9739/**
9740 * Runtime.js
9741 *
9742 * Copyright 2013, Moxiecode Systems AB
9743 * Released under GPL License.
9744 *
9745 * License: http://www.plupload.com/license
9746 * Contributing: http://www.plupload.com/contributing
9747 */
9748
9749/*global ActiveXObject:true */
9750
9751/**
9752Defines constructor for Flash runtime.
9753
9754@class moxie/runtime/flash/Runtime
9755@private
9756*/
9757define("moxie/runtime/flash/Runtime", [
9758	"moxie/core/utils/Basic",
9759	"moxie/core/utils/Env",
9760	"moxie/core/utils/Dom",
9761	"moxie/core/Exceptions",
9762	"moxie/runtime/Runtime"
9763], function(Basic, Env, Dom, x, Runtime) {
9764	
9765	var type = 'flash', extensions = {};
9766
9767	/**
9768	Get the version of the Flash Player
9769
9770	@method getShimVersion
9771	@private
9772	@return {Number} Flash Player version
9773	*/
9774	function getShimVersion() {
9775		var version;
9776
9777		try {
9778			version = navigator.plugins['Shockwave Flash'];
9779			version = version.description;
9780		} catch (e1) {
9781			try {
9782				version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
9783			} catch (e2) {
9784				version = '0.0';
9785			}
9786		}
9787		version = version.match(/\d+/g);
9788		return parseFloat(version[0] + '.' + version[1]);
9789	}
9790
9791	/**
9792	Constructor for the Flash Runtime
9793
9794	@class FlashRuntime
9795	@extends Runtime
9796	*/
9797	function FlashRuntime(options) {
9798		var I = this, initTimer;
9799
9800		options = Basic.extend({ swf_url: Env.swf_url }, options);
9801
9802		Runtime.call(this, options, type, {
9803			access_binary: function(value) {
9804				return value && I.mode === 'browser';
9805			},
9806			access_image_binary: function(value) {
9807				return value && I.mode === 'browser';
9808			},
9809			display_media: Runtime.capTrue,
9810			do_cors: Runtime.capTrue,
9811			drag_and_drop: false,
9812			report_upload_progress: function() {
9813				return I.mode === 'client';
9814			},
9815			resize_image: Runtime.capTrue,
9816			return_response_headers: false,
9817			return_response_type: function(responseType) {
9818				if (responseType === 'json' && !!window.JSON) {
9819					return true;
9820				} 
9821				return !Basic.arrayDiff(responseType, ['', 'text', 'document']) || I.mode === 'browser';
9822			},
9823			return_status_code: function(code) {
9824				return I.mode === 'browser' || !Basic.arrayDiff(code, [200, 404]);
9825			},
9826			select_file: Runtime.capTrue,
9827			select_multiple: Runtime.capTrue,
9828			send_binary_string: function(value) {
9829				return value && I.mode === 'browser';
9830			},
9831			send_browser_cookies: function(value) {
9832				return value && I.mode === 'browser';
9833			},
9834			send_custom_headers: function(value) {
9835				return value && I.mode === 'browser';
9836			},
9837			send_multipart: Runtime.capTrue,
9838			slice_blob: function(value) {
9839				return value && I.mode === 'browser';
9840			},
9841			stream_upload: function(value) {
9842				return value && I.mode === 'browser';
9843			},
9844			summon_file_dialog: false,
9845			upload_filesize: function(size) {
9846				return Basic.parseSizeStr(size) <= 2097152 || I.mode === 'client';
9847			},
9848			use_http_method: function(methods) {
9849				return !Basic.arrayDiff(methods, ['GET', 'POST']);
9850			}
9851		}, { 
9852			// capabilities that require specific mode
9853			access_binary: function(value) {
9854				return value ? 'browser' : 'client';
9855			},
9856			access_image_binary: function(value) {
9857				return value ? 'browser' : 'client';
9858			},
9859			report_upload_progress: function(value) {
9860				return value ? 'browser' : 'client';
9861			},
9862			return_response_type: function(responseType) {
9863				return Basic.arrayDiff(responseType, ['', 'text', 'json', 'document']) ? 'browser' : ['client', 'browser'];
9864			},
9865			return_status_code: function(code) {
9866				return Basic.arrayDiff(code, [200, 404]) ? 'browser' : ['client', 'browser'];
9867			},
9868			send_binary_string: function(value) {
9869				return value ? 'browser' : 'client';
9870			},
9871			send_browser_cookies: function(value) {
9872				return value ? 'browser' : 'client';
9873			},
9874			send_custom_headers: function(value) {
9875				return value ? 'browser' : 'client';
9876			},
9877			stream_upload: function(value) {
9878				return value ? 'client' : 'browser';
9879			},
9880			upload_filesize: function(size) {
9881				return Basic.parseSizeStr(size) >= 2097152 ? 'client' : 'browser';
9882			}
9883		}, 'client');
9884
9885
9886		// minimal requirement for Flash Player version
9887		if (getShimVersion() < 10) {
9888			this.mode = false;
9888 // with falsy mode, runtime won't operable, no matter what the mode was before
9889		}
9890
9891
9892		Basic.extend(this, {
9893
9894			getShim: function() {
9895				return Dom.get(this.uid);
9896			},
9897
9898			shimExec: function(component, action) {
9899				var args = [].slice.call(arguments, 2);
9900				return I.getShim().exec(this.uid, component, action, args);
9901			},
9902
9903			init: function() {
9904				var html, el, container;
9905
9906				container = this.getShimContainer();
9907
9908				// if not the minimal height, shims are not initialized in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)
9909				Basic.extend(container.style, {
9910					position: 'absolute',
9911					top: '-8px',
9912					left: '-8px',
9913					width: '9px',
9914					height: '9px',
9915					overflow: 'hidden'
9916				});
9917
9918				// insert flash object
9919				html = '<object id="' + this.uid + '" type="application/x-shockwave-flash" data="' +  options.swf_url + '" ';
9920
9921				if (Env.browser === 'IE') {
9922					html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
9923				}
9924
9925				html += 'width="100%" height="100%" style="outline:0">'  +
9926					'<param name="movie" value="' + options.swf_url + '" />' +
9927					'<param name="flashvars" value="uid=' + escape(this.uid) + '&target=' + Env.global_event_dispatcher + '" />' +
9928					'<param name="wmode" value="transparent" />' +
9929					'<param name="allowscriptaccess" value="always" />' +
9930				'</object>';
9931
9932				if (Env.browser === 'IE') {
9933					el = document.createElement('div');
9934					container.appendChild(el);
9935					el.outerHTML = html;
9936					el = container = null; // just in case
9937				} else {
9938					container.innerHTML = html;
9939				}
9940
9941				// Init is dispatched by the shim
9942				initTimer = setTimeout(function() {
9943					if (I && !I.initialized) { // runtime might be already destroyed by this moment
9944						I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
9945					}
9946				}, 5000);
9947			},
9948
9949			destroy: (function(destroy) { // extend default destroy method
9950				return function() {
9951					destroy.call(I);
9952					clearTimeout(initTimer); // initialization check might be still onwait
9953					options = initTimer = destroy = I = null;
9954				};
9955			}(this.destroy))
9956
9957		}, extensions);
9958	}
9959
9960	Runtime.addConstructor(type, FlashRuntime);
9961
9962	return extensions;
9963});
9964
9965// Included from: src/javascript/runtime/flash/file/Blob.js
9966
9967/**
9968 * Blob.js
9969 *
9970 * Copyright 2013, Moxiecode Systems AB
9971 * Released under GPL License.
9972 *
9973 * License: http://www.plupload.com/license
9974 * Contributing: http://www.plupload.com/contributing
9975 */
9976
9977/**
9978@class moxie/runtime/flash/file/Blob
9979@private
9980*/
9981define("moxie/runtime/flash/file/Blob", [
9982	"moxie/runtime/flash/Runtime",
9983	"moxie/file/Blob"
9984], function(extensions, Blob) {
9985
9986	var FlashBlob = {
9987		slice: function(blob, start, end, type) {
9988			var self = this.getRuntime();
9989
9990			if (start < 0) {
9991				start = Math.max(blob.size + start, 0);
9992			} else if (start > 0) {
9993				start = Math.min(start, blob.size);
9994			}
9995
9996			if (end < 0) {
9997				end = Math.max(blob.size + end, 0);
9998			} else if (end > 0) {
9999				end = Math.min(end, blob.size);
10000			}
10001
10002			blob = self.shimExec.call(this, 'Blob', 'slice', start, end, type || '');
10003
10004			if (blob) {
10005				blob = new Blob(self.uid, blob);
10006			}
10007			return blob;
10008		}
10009	};
10010
10011	return (extensions.Blob = FlashBlob);
10012});
10013
10014// Included from: src/javascript/runtime/flash/file/FileInput.js
10015
10016/**
10017 * FileInput.js
10018 *
10019 * Copyright 2013, Moxiecode Systems AB
10020 * Released under GPL License.
10021 *
10022 * License: http://www.plupload.com/license
10023 * Contributing: http://www.plupload.com/contributing
10024 */
10025
10026/**
10027@class moxie/runtime/flash/file/FileInput
10028@private
10029*/
10030define("moxie/runtime/flash/file/FileInput", [
10031	"moxie/runtime/flash/Runtime"
10032], function(extensions) {
10033	
10034	var FileInput = {		
10035		init: function(options) {
10036			this.getRuntime().shimExec.call(this, 'FileInput', 'init', {
10037				name: options.name,
10038				accept: options.accept,
10039				multiple: options.multiple
10040			});
10041			this.trigger('ready');
10042		}
10043	};
10044
10045	return (extensions.FileInput = FileInput);
10046});
10047
10048// Included from: src/javascript/runtime/flash/file/FileReader.js
10049
10050/**
10051 * FileReader.js
10052 *
10053 * Copyright 2013, Moxiecode Systems AB
10054 * Released under GPL License.
10055 *
10056 * License: http://www.plupload.com/license
10057 * Contributing: http://www.plupload.com/contributing
10058 */
10059
10060/**
10061@class moxie/runtime/flash/file/FileReader
10062@private
10063*/
10064define("moxie/runtime/flash/file/FileReader", [
10065	"moxie/runtime/flash/Runtime",
10066	"moxie/core/utils/Encode"
10067], function(extensions, Encode) {
10068
10069	var _result = '';
10070
10071	function _formatData(data, op) {
10072		switch (op) {
10073			case 'readAsText':
10074				return Encode.atob(data, 'utf8');
10075			case 'readAsBinaryString':
10076				return Encode.atob(data);
10077			case 'readAsDataURL':
10078				return data;
10079		}
10080		return null;
10081	}
10082
10083	var FileReader = {
10084		read: function(op, blob) {
10085			var target = this, self = target.getRuntime();
10086
10087			// special prefix for DataURL read mode
10088			if (op === 'readAsDataURL') {
10089				_result = 'data:' + (blob.type || '') + ';base64,';
10090			}
10091
10092			target.bind('Progress', function(e, data) {
10093				if (data) {
10094					_result += _formatData(data, op);
10095				}
10096			});
10097
10098			return self.shimExec.call(this, 'FileReader', 'readAsBase64', blob.uid);
10099		},
10100
10101		getResult: function() {
10102			return _result;
10103		},
10104
10105		destroy: function() {
10106			_result = null;
10107		}
10108	};
10109
10110	return (extensions.FileReader = FileReader);
10111});
10112
10113// Included from: src/javascript/runtime/flash/file/FileReaderSync.js
10114
10115/**
10116 * FileReaderSync.js
10117 *
10118 * Copyright 2013, Moxiecode Systems AB
10119 * Released under GPL License.
10120 *
10121 * License: http://www.plupload.com/license
10122 * Contributing: http://www.plupload.com/contributing
10123 */
10124
10125/**
10126@class moxie/runtime/flash/file/FileReaderSync
10127@private
10128*/
10129define("moxie/runtime/flash/file/FileReaderSync", [
10130	"moxie/runtime/flash/Runtime",
10131	"moxie/core/utils/Encode"
10132], function(extensions, Encode) {
10133	
10134	function _formatData(data, op) {
10135		switch (op) {
10136			case 'readAsText':
10137				return Encode.atob(data, 'utf8');
10138			case 'readAsBinaryString':
10139				return Encode.atob(data);
10140			case 'readAsDataURL':
10141				return data;
10142		}
10143		return null;
10144	}
10145
10146	var FileReaderSync = {
10147		read: function(op, blob) {
10148			var result, self = this.getRuntime();
10149
10150			result = self.shimExec.call(this, 'FileReaderSync', 'readAsBase64', blob.uid);
10151			if (!result) {
10152				return null; // or throw ex
10153			}
10154
10155			// special prefix for DataURL read mode
10156			if (op === 'readAsDataURL') {
10157				result = 'data:' + (blob.type || '') + ';base64,' + result;
10158			}
10159
10160			return _formatData(result, op, blob.type);
10161		}
10162	};
10163
10164	return (extensions.FileReaderSync = FileReaderSync);
10165});
10166
10167// Included from: src/javascript/runtime/flash/xhr/XMLHttpRequest.js
10168
10169/**
10170 * XMLHttpRequest.js
10171 *
10172 * Copyright 2013, Moxiecode Systems AB
10173 * Released under GPL License.
10174 *
10175 * License: http://www.plupload.com/license
10176 * Contributing: http://www.plupload.com/contributing
10177 */
10178
10179/**
10180@class moxie/runtime/flash/xhr/XMLHttpRequest
10181@private
10182*/
10183define("moxie/runtime/flash/xhr/XMLHttpRequest", [
10184	"moxie/runtime/flash/Runtime",
10185	"moxie/core/utils/Basic",
10186	"moxie/file/Blob",
10187	"moxie/file/File",
10188	"moxie/file/FileReaderSync",
10189	"moxie/xhr/FormData",
10190	"moxie/runtime/Transporter"
10191], function(extensions, Basic, Blob, File, FileReaderSync, FormData, Transporter) {
10192	
10193	var XMLHttpRequest = {
10194
10195		send: function(meta, data) {
10196			var target = this, self = target.getRuntime();
10197
10198			function send() {
10199				meta.transport = self.mode;
10200				self.shimExec.call(target, 'XMLHttpRequest', 'send', meta, data);
10201			}
10202
10203
10204			function appendBlob(name, blob) {
10205				self.shimExec.call(target, 'XMLHttpRequest', 'appendBlob', name, blob.uid);
10206				data = null;
10207				send();
10208			}
10209
10210
10211			function attachBlob(blob, cb) {
10212				var tr = new Transporter();
10213
10214				tr.bind("TransportingComplete", function() {
10215					cb(this.result);
10216				});
10217
10218				tr.transport(blob.getSource(), blob.type, {
10219					ruid: self.uid
10220				});
10221			}
10222
10223			// copy over the headers if any
10224			if (!Basic.isEmptyObj(meta.headers)) {
10225				Basic.each(meta.headers, function(value, header) {
10226					self.shimExec.call(target, 'XMLHttpRequest', 'setRequestHeader', header, value.toString()); // Silverlight doesn't accept integers into the arguments of type object
10227				});
10228			}
10229
10230			// transfer over multipart params and blob itself
10231			if (data instanceof FormData) {
10232				var blobField;
10233				data.each(function(value, name) {
10234					if (value instanceof Blob) {
10235						blobField = name;
10236					} else {
10237						self.shimExec.call(target, 'XMLHttpRequest', 'append', name, value);
10238					}
10239				});
10240
10241				if (!data.hasBlob()) {
10242					data = null;
10243					send();
10244				} else {
10245					var blob = data.getBlob();
10246					if (blob.isDetached()) {
10247						attachBlob(blob, function(attachedBlob) {
10248							blob.destroy();
10249							appendBlob(blobField, attachedBlob);		
10250						});
10251					} else {
10252						appendBlob(blobField, blob);
10253					}
10254				}
10255			} else if (data instanceof Blob) {
10256				if (data.isDetached()) {
10257					attachBlob(data, function(attachedBlob) {
10258						data.destroy();
10259						data = attachedBlob.uid;
10260						send();
10261					});
10262				} else {
10263					data = data.uid;
10264					send();
10265				}
10266			} else {
10267				send();
10268			}
10269		},
10270
10271		getResponse: function(responseType) {
10272			var frs, blob, self = this.getRuntime();
10273
10274			blob = self.shimExec.call(this, 'XMLHttpRequest', 'getResponseAsBlob');
10275
10276			if (blob) {
10277				blob = new File(self.uid, blob);
10278
10279				if ('blob' === responseType) {
10280					return blob;
10281				}
10282
10283				try { 
10284					frs = new FileReaderSync();
10285
10286					if (!!~Basic.inArray(responseType, ["", "text"])) {
10287						return frs.readAsText(blob);
10288					} else if ('json' === responseType && !!window.JSON) {
10289						return JSON.parse(frs.readAsText(blob));
10290					}
10291				} finally {
10292					blob.destroy();
10293				}
10294			}
10295			return null;
10296		},
10297
10298		abort: function(upload_complete_flag) {
10299			var self = this.getRuntime();
10300
10301			self.shimExec.call(this, 'XMLHttpRequest', 'abort');
10302
10303			this.dispatchEvent('readystatechange');
10304			// this.dispatchEvent('progress');
10305			this.dispatchEvent('abort');
10306
10307			//if (!upload_complete_flag) {
10308				// this.dispatchEvent('uploadprogress');
10309			//}
10310		}
10311	};
10312
10313	return (extensions.XMLHttpRequest = XMLHttpRequest);
10314});
10315
10316// Included from: src/javascript/runtime/flash/runtime/Transporter.js
10317
10318/**
10319 * Transporter.js
10320 *
10321 * Copyright 2013, Moxiecode Systems AB
10322 * Released under GPL License.
10323 *
10324 * License: http://www.plupload.com/license
10325 * Contributing: http://www.plupload.com/contributing
10326 */
10327
10328/**
10329@class moxie/runtime/flash/runtime/Transporter
10330@private
10331*/
10332define("moxie/runtime/flash/runtime/Transporter", [
10333	"moxie/runtime/flash/Runtime",
10334	"moxie/file/Blob"
10335], function(extensions, Blob) {
10336
10337	var Transporter = {
10338		getAsBlob: function(type) {
10339			var self = this.getRuntime()
10340			, blob = self.shimExec.call(this, 'Transporter', 'getAsBlob', type)
10341			;
10342			if (blob) {
10343				return new Blob(self.uid, blob);
10344			}
10345			return null;
10346		}
10347	};
10348
10349	return (extensions.Transporter = Transporter);
10350});
10351
10352// Included from: src/javascript/runtime/flash/image/Image.js
10353
10354/**
10355 * Image.js
10356 *
10357 * Copyright 2013, Moxiecode Systems AB
10358 * Released under GPL License.
10359 *
10360 * License: http://www.plupload.com/license
10361 * Contributing: http://www.plupload.com/contributing
10362 */
10363
10364/**
10365@class moxie/runtime/flash/image/Image
10366@private
10367*/
10368define("moxie/runtime/flash/image/Image", [
10369	"moxie/runtime/flash/Runtime",
10370	"moxie/core/utils/Basic",
10371	"moxie/runtime/Transporter",
10372	"moxie/file/Blob",
10373	"moxie/file/FileReaderSync"
10374], function(extensions, Basic, Transporter, Blob, FileReaderSync) {
10375	
10376	var Image = {
10377		loadFromBlob: function(blob) {
10378			var comp = this, self = comp.getRuntime();
10379
10380			function exec(srcBlob) {
10381				self.shimExec.call(comp, 'Image', 'loadFromBlob', srcBlob.uid);
10382				comp = self = null;
10383			}
10384
10385			if (blob.isDetached()) { // binary string
10386				var tr = new Transporter();
10387				tr.bind("TransportingComplete", function() {
10388					exec(tr.result.getSource());
10389				});
10390				tr.transport(blob.getSource(), blob.type, { ruid: self.uid });
10391			} else {
10392				exec(blob.getSource());
10393			}
10394		},
10395
10396		loadFromImage: function(img) {
10397			var self = this.getRuntime();
10398			return self.shimExec.call(this, 'Image', 'loadFromImage', img.uid);
10399		},
10400
10401		getAsBlob: function(type, quality) {
10402			var self = this.getRuntime()
10403			, blob = self.shimExec.call(this, 'Image', 'getAsBlob', type, quality)
10404			;
10405			if (blob) {
10406				return new Blob(self.uid, blob);
10407			}
10408			return null;
10409		},
10410
10411		getAsDataURL: function() {
10412			var self = this.getRuntime()
10413			, blob = self.Image.getAsBlob.apply(this, arguments)
10414			, frs
10415			;
10416			if (!blob) {
10417				return null;
10418			}
10419			frs = new FileReaderSync();
10420			return frs.readAsDataURL(blob);
10421		}
10422	};
10423
10424	return (extensions.Image = Image);
10425});
10426
10427// Included from: src/javascript/runtime/silverlight/Runtime.js
10428
10429/**
10430 * RunTime.js
10431 *
10432 * Copyright 2013, Moxiecode Systems AB
10433 * Released under GPL License.
10434 *
10435 * License: http://www.plupload.com/license
10436 * Contributing: http://www.plupload.com/contributing
10437 */
10438
10439/*global ActiveXObject:true */
10440
10441/**
10442Defines constructor for Silverlight runtime.
10443
10444@class moxie/runtime/silverlight/Runtime
10445@private
10446*/
10447define("moxie/runtime/silverlight/Runtime", [
10448	"moxie/core/utils/Basic",
10449	"moxie/core/utils/Env",
10450	"moxie/core/utils/Dom",
10451	"moxie/core/Exceptions",
10452	"moxie/runtime/Runtime"
10453], function(Basic, Env, Dom, x, Runtime) {
10454	
10455	var type = "silverlight", extensions = {};
10456
10457	function isInstalled(version) {
10458		var isVersionSupported = false, control = null, actualVer,
10459			actualVerArray, reqVerArray, requiredVersionPart, actualVersionPart, index = 0;
10460
10461		try {
10462			try {
10463				control = new ActiveXObject('AgControl.AgControl');
10464
10465				if (control.IsVersionSupported(version)) {
10466					isVersionSupported = true;
10467				}
10468
10469				control = null;
10470			} catch (e) {
10471				var plugin = navigator.plugins["Silverlight Plug-In"];
10472
10473				if (plugin) {
10474					actualVer = plugin.description;
10475
10476					if (actualVer === "1.0.30226.2") {
10477						actualVer = "2.0.30226.2";
10478					}
10479
10480					actualVerArray = actualVer.split(".");
10481
10482					while (actualVerArray.length > 3) {
10483						actualVerArray.pop();
10484					}
10485
10486					while ( actualVerArray.length < 4) {
10487						actualVerArray.push(0);
10488					}
10489
10490					reqVerArray = version.split(".");
10491
10492					while (reqVerArray.length > 4) {
10493						reqVerArray.pop();
10494					}
10495
10496					do {
10497						requiredVersionPart = parseInt(reqVerArray[index], 10);
10498						actualVersionPart = parseInt(actualVerArray[index], 10);
10499						index++;
10500					} while (index < reqVerArray.length && requiredVersionPart === actualVersionPart);
10501
10502					if (requiredVersionPart <= actualVersionPart && !isNaN(requiredVersionPart)) {
10503						isVersionSupported = true;
10504					}
10505				}
10506			}
10507		} catch (e2) {
10508			isVersionSupported = false;
10509		}
10510
10511		return isVersionSupported;
10512	}
10513
10514	/**
10515	Constructor for the Silverlight Runtime
10516
10517	@class SilverlightRuntime
10518	@extends Runtime
10519	*/
10520	function SilverlightRuntime(options) {
10521		var I = this, initTimer;
10522
10523		options = Basic.extend({ xap_url: Env.xap_url }, options);
10524
10525		Runtime.call(this, options, type, {
10526			access_binary: Runtime.capTrue,
10527			access_image_binary: Runtime.capTrue,
10528			display_media: Runtime.capTrue,
10529			do_cors: Runtime.capTrue,
10530			drag_and_drop: false,
10531			report_upload_progress: Runtime.capTrue,
10532			resize_image: Runtime.capTrue,
10533			return_response_headers: function(value) {
10534				return value && I.mode === 'client';
10535			},
10536			return_response_type: function(responseType) {
10537				if (responseType !== 'json') {
10538					return true;
10539				} else {
10540					return !!window.JSON;
10541				}
10542			},
10543			return_status_code: function(code) {
10544				return I.mode === 'client' || !Basic.arrayDiff(code, [200, 404]);
10545			},
10546			select_file: Runtime.capTrue,
10547			select_multiple: Runtime.capTrue,
10548			send_binary_string: Runtime.capTrue,
10549			send_browser_cookies: function(value) {
10550				return value && I.mode === 'browser';
10551			},
10552			send_custom_headers: function(value) {
10553				return value && I.mode === 'client';
10554			},
10555			send_multipart: Runtime.capTrue,
10556			slice_blob: Runtime.capTrue,
10557			stream_upload: true,
10558			summon_file_dialog: false,
10559			upload_filesize: Runtime.capTrue,
10560			use_http_method: function(methods) {
10561				return I.mode === 'client' || !Basic.arrayDiff(methods, ['GET', 'POST']);
10562			}
10563		}, { 
10564			// capabilities that require specific mode
10565			return_response_headers: function(value) {
10566				return value ? 'client' : 'browser';
10567			},
10568			return_status_code: function(code) {
10569				return Basic.arrayDiff(code, [200, 404]) ? 'client' : ['client', 'browser'];
10570			},
10571			send_browser_cookies: function(value) {
10572				return value ? 'browser' : 'client';
10573			},
10574			send_custom_headers: function(value) {
10575				return value ? 'client' : 'browser';
10576			},
10577			use_http_method: function(methods) {
10578				return Basic.arrayDiff(methods, ['GET', 'POST']) ? 'client' : ['client', 'browser'];
10579			}
10580		});
10581
10582
10583		// minimal requirement
10584		if (!isInstalled('2.0.31005.0') || Env.browser === 'Opera') {
10585			this.mode = false;
10586		}
10587
10588
10589		Basic.extend(this, {
10590			getShim: function() {
10591				return Dom.get(this.uid).content.Moxie;
10592			},
10593
10594			shimExec: function(component, action) {
10595				var args = [].slice.call(arguments, 2);
10596				return I.getShim().exec(this.uid, component, action, args);
10597			},
10598
10599			init : function() {
10600				var container;
10601
10602				container = this.getShimContainer();
10603
10604				container.innerHTML = '<object id="' + this.uid + '" data="data:application/x-silverlight," type="application/x-silverlight-2" width="100%" height="100%" style="outline:none;">' +
10605					'<param name="source" value="' + options.xap_url + '"/>' +
10606					'<param name="background" value="Transparent"/>' +
10607					'<param name="windowless" value="true"/>' +
10608					'<param name="enablehtmlaccess" value="true"/>' +
10609					'<param name="initParams" value="uid=' + this.uid + ',target=' + Env.global_event_dispatcher + '"/>' +
10610				'</object>';
10611
10612				// Init is dispatched by the shim
10613				initTimer = setTimeout(function() {
10614					if (I && !I.initialized) { // runtime might be already destroyed by this moment
10615						I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
10616					}
10617				}, Env.OS !== 'Windows'? 10000 : 5000); // give it more time to initialize in non Windows OS (like Mac)
10618			},
10619
10620			destroy: (function(destroy) { // extend default destroy method
10621				return function() {
10622					destroy.call(I);
10623					clearTimeout(initTimer); // initialization check might be still onwait
10624					options = initTimer = destroy = I = null;
10625				};
10626			}(this.destroy))
10627
10628		}, extensions);
10629	}
10630
10631	Runtime.addConstructor(type, SilverlightRuntime); 
10632
10633	return extensions;
10634});
10635
10636// Included from: src/javascript/runtime/silverlight/file/Blob.js
10637
10638/**
10639 * Blob.js
10640 *
10641 * Copyright 2013, Moxiecode Systems AB
10642 * Released under GPL License.
10643 *
10644 * License: http://www.plupload.com/license
10645 * Contributing: http://www.plupload.com/contributing
10646 */
10647
10648/**
10649@class moxie/runtime/silverlight/file/Blob
10650@private
10651*/
10652define("moxie/runtime/silverlight/file/Blob", [
10653	"moxie/runtime/silverlight/Runtime",
10654	"moxie/core/utils/Basic",
10655	"moxie/runtime/flash/file/Blob"
10656], function(extensions, Basic, Blob) {
10657	return (extensions.Blob = Basic.extend({}, Blob));
10658});
10659
10660// Included from: src/javascript/runtime/silverlight/file/FileInput.js
10661
10662/**
10663 * FileInput.js
10664 *
10665 * Copyright 2013, Moxiecode Systems AB
10666 * Released under GPL License.
10667 *
10668 * License: http://www.plupload.com/license
10669 * Contributing: http://www.plupload.com/contributing
10670 */
10671
10672/**
10673@class moxie/runtime/silverlight/file/FileInput
10674@private
10675*/
10676define("moxie/runtime/silverlight/file/FileInput", [
10677	"moxie/runtime/silverlight/Runtime"
10678], function(extensions) {
10679	
10680	var FileInput = {
10681		init: function(options) {
10682
10683			function toFilters(accept) {
10684				var filter = '';
10685				for (var i = 0; i < accept.length; i++) {
10686					filter += (filter !== '' ? '|' : '') + accept[i].title + " | *." + accept[i].extensions.replace(/,/g, ';*.');
10687				}
10688				return filter;
10689			}
10690			
10691			this.getRuntime().shimExec.call(this, 'FileInput', 'init', toFilters(options.accept), options.name, options.multiple);
10692			this.trigger('ready');
10693		}
10694	};
10695
10696	return (extensions.FileInput = FileInput);
10697});
10698
10699// Included from: src/javascript/runtime/silverlight/file/FileDrop.js
10700
10701/**
10702 * FileDrop.js
10703 *
10704 * Copyright 2013, Moxiecode Systems AB
10705 * Released under GPL License.
10706 *
10707 * License: http://www.plupload.com/license
10708 * Contributing: http://www.plupload.com/contributing
10709 */
10710
10711/**
10712@class moxie/runtime/silverlight/file/FileDrop
10713@private
10714*/
10715define("moxie/runtime/silverlight/file/FileDrop", [
10716	"moxie/runtime/silverlight/Runtime",
10717	"moxie/core/utils/Dom", 
10718	"moxie/core/utils/Events"
10719], function(extensions, Dom, Events) {
10720
10721	// not exactly useful, since works only in safari (...crickets...)
10722	var FileDrop = {
10723		init: function() {
10724			var comp = this, self = comp.getRuntime(), dropZone;
10725
10726			dropZone = self.getShimContainer();
10727
10728			Events.addEvent(dropZone, 'dragover', function(e) {
10729				e.preventDefault();
10730				e.stopPropagation();
10731				e.dataTransfer.dropEffect = 'copy';
10732			}, comp.uid);
10733
10734			Events.addEvent(dropZone, 'dragenter', function(e) {
10735				e.preventDefault();
10736				var flag = Dom.get(self.uid).dragEnter(e);
10737				// If handled, then stop propagation of event in DOM
10738				if (flag) {
10739					e.stopPropagation();
10740				}
10741			}, comp.uid);
10742
10743			Events.addEvent(dropZone, 'drop', function(e) {
10744				e.preventDefault();
10745				var flag = Dom.get(self.uid).dragDrop(e);
10746				// If handled, then stop propagation of event in DOM
10747				if (flag) {
10748					e.stopPropagation();
10749				}
10750			}, comp.uid);
10751
10752			return self.shimExec.call(this, 'FileDrop', 'init');
10753		}
10754	};
10755
10756	return (extensions.FileDrop = FileDrop);
10757});
10758
10759// Included from: src/javascript/runtime/silverlight/file/FileReader.js
10760
10761/**
10762 * FileReader.js
10763 *
10764 * Copyright 2013, Moxiecode Systems AB
10765 * Released under GPL License.
10766 *
10767 * License: http://www.plupload.com/license
10768 * Contributing: http://www.plupload.com/contributing
10769 */
10770
10771/**
10772@class moxie/runtime/silverlight/file/FileReader
10773@private
10774*/
10775define("moxie/runtime/silverlight/file/FileReader", [
10776	"moxie/runtime/silverlight/Runtime",
10777	"moxie/core/utils/Basic",
10778	"moxie/runtime/flash/file/FileReader"
10779], function(extensions, Basic, FileReader) {
10780	return (extensions.FileReader = Basic.extend({}, FileReader));
10781});
10782
10783// Included from: src/javascript/runtime/silverlight/file/FileReaderSync.js
10784
10785/**
10786 * FileReaderSync.js
10787 *
10788 * Copyright 2013, Moxiecode Systems AB
10789 * Released under GPL License.
10790 *
10791 * License: http://www.plupload.com/license
10792 * Contributing: http://www.plupload.com/contributing
10793 */
10794
10795/**
10796@class moxie/runtime/silverlight/file/FileReaderSync
10797@private
10798*/
10799define("moxie/runtime/silverlight/file/FileReaderSync", [
10800	"moxie/runtime/silverlight/Runtime",
10801	"moxie/core/utils/Basic",
10802	"moxie/runtime/flash/file/FileReaderSync"
10803], function(extensions, Basic, FileReaderSync) {
10804	return (extensions.FileReaderSync = Basic.extend({}, FileReaderSync));
10805});
10806
10807// Included from: src/javascript/runtime/silverlight/xhr/XMLHttpRequest.js
10808
10809/**
10810 * XMLHttpRequest.js
10811 *
10812 * Copyright 2013, Moxiecode Systems AB
10813 * Released under GPL License.
10814 *
10815 * License: http://www.plupload.com/license
10816 * Contributing: http://www.plupload.com/contributing
10817 */
10818
10819/**
10820@class moxie/runtime/silverlight/xhr/XMLHttpRequest
10821@private
10822*/
10823define("moxie/runtime/silverlight/xhr/XMLHttpRequest", [
10824	"moxie/runtime/silverlight/Runtime",
10825	"moxie/core/utils/Basic",
10826	"moxie/runtime/flash/xhr/XMLHttpRequest"
10827], function(extensions, Basic, XMLHttpRequest) {
10828	return (extensions.XMLHttpRequest = Basic.extend({}, XMLHttpRequest));
10829});
10830
10831// Included from: src/javascript/runtime/silverlight/runtime/Transporter.js
10832
10833/**
10834 * Transporter.js
10835 *
10836 * Copyright 2013, Moxiecode Systems AB
10837 * Released under GPL License.
10838 *
10839 * License: http://www.plupload.com/license
10840 * Contributing: http://www.plupload.com/contributing
10841 */
10842
10843/**
10844@class moxie/runtime/silverlight/runtime/Transporter
10845@private
10846*/
10847define("moxie/runtime/silverlight/runtime/Transporter", [
10848	"moxie/runtime/silverlight/Runtime",
10849	"moxie/core/utils/Basic",
10850	"moxie/runtime/flash/runtime/Transporter"
10851], function(extensions, Basic, Transporter) {
10852	return (extensions.Transporter = Basic.extend({}, Transporter));
10853});
10854
10855// Included from: src/javascript/runtime/silverlight/image/Image.js
10856
10857/**
10858 * Image.js
10859 *
10860 * Copyright 2013, Moxiecode Systems AB
10861 * Released under GPL License.
10862 *
10863 * License: http://www.plupload.com/license
10864 * Contributing: http://www.plupload.com/contributing
10865 */
10866 
10867/**
10868@class moxie/runtime/silverlight/image/Image
10869@private
10870*/
10871define("moxie/runtime/silverlight/image/Image", [
10872	"moxie/runtime/silverlight/Runtime",
10873	"moxie/core/utils/Basic",
10874	"moxie/runtime/flash/image/Image"
10875], function(extensions, Basic, Image) {
10876	return (extensions.Image = Basic.extend({}, Image, {
10877
10878		getInfo: function() {
10879			var self = this.getRuntime()
10880			, grps = ['tiff', 'exif', 'gps']
10881			, info = { meta: {} }
10882			, rawInfo = self.shimExec.call(this, 'Image', 'getInfo')
10883			;
10884
10885			if (rawInfo.meta) {
10886				Basic.each(grps, function(grp) {
10887					var meta = rawInfo.meta[grp]
10888					, tag
10889					, i
10890					, length
10891					, value
10892					;
10893					if (meta && meta.keys) {
10894						info.meta[grp] = {};
10895						for (i = 0, length = meta.keys.length; i < length; i++) {
10896							tag = meta.keys[i];
10897							value = meta[tag];
10898							if (value) {
10899								// convert numbers
10900								if (/^(\d|[1-9]\d+)$/.test(value)) { // integer (make sure doesn't start with zero)
10901									value = parseInt(value, 10);
10902								} else if (/^\d*\.\d+$/.test(value)) { // double
10903									value = parseFloat(value);
10904								}
10905								info.meta[grp][tag] = value;
10906							}
10907						}
10908					}
10909				});
10910			}
10911
10912			info.width = parseInt(rawInfo.width, 10);
10913			info.height = parseInt(rawInfo.height, 10);
10914			info.size = parseInt(rawInfo.size, 10);
10915			info.type = rawInfo.type;
10916			info.name = rawInfo.name;
10917
10918			return info;
10919		}
10920	}));
10921});
10922
10923// Included from: src/javascript/runtime/html4/Runtime.js
10924
10925/**
10926 * Runtime.js
10927 *
10928 * Copyright 2013, Moxiecode Systems AB
10929 * Released under GPL License.
10930 *
10931 * License: http://www.plupload.com/license
10932 * Contributing: http://www.plupload.com/contributing
10933 */
10934
10935/*global File:true */
10936
10937/**
10938Defines constructor for HTML4 runtime.
10939
10940@class moxie/runtime/html4/Runtime
10941@private
10942*/
10943define("moxie/runtime/html4/Runtime", [
10944	"moxie/core/utils/Basic",
10945	"moxie/core/Exceptions",
10946	"moxie/runtime/Runtime",
10947	"moxie/core/utils/Env"
10948], function(Basic, x, Runtime, Env) {
10949	
10950	var type = 'html4', extensions = {};
10951
10952	function Html4Runtime(options) {
10953		var I = this
10954		, Test = Runtime.capTest
10955		, True = Runtime.capTrue
10956		;
10957
10958		Runtime.call(this, options, type, {
10959			access_binary: Test(window.FileReader || window.File && File.getAsDataURL),
10960			access_image_binary: false,
10961			display_media: Test(extensions.Image && (Env.can('create_canvas') || Env.can('use_data_uri_over32kb'))),
10962			do_cors: false,
10963			drag_and_drop: false,
10964			filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest
10965				return (Env.browser === 'Chrome' && Env.version >= 28) || (Env.browser === 'IE' && Env.version >= 10);
10966			}()),
10967			resize_image: function() {
10968				return extensions.Image && I.can('access_binary') && Env.can('create_canvas');
10969			},
10970			report_upload_progress: false,
10971			return_response_headers: false,
10972			return_response_type: function(responseType) {
10973				if (responseType === 'json' && !!window.JSON) {
10974					return true;
10975				} 
10976				return !!~Basic.inArray(responseType, ['text', 'document', '']);
10977			},
10978			return_status_code: function(code) {
10979				return !Basic.arrayDiff(code, [200, 404]);
10980			},
10981			select_file: function() {
10982				return Env.can('use_fileinput');
10983			},
10984			select_multiple: false,
10985			send_binary_string: false,
10986			send_custom_headers: false,
10987			send_multipart: true,
10988			slice_blob: false,
10989			stream_upload: function() {
10990				return I.can('select_file');
10991			},
10992			summon_file_dialog: Test(function() { // yeah... some dirty sniffing here...
10993				return (Env.browser === 'Firefox' && Env.version >= 4) ||
10994					(Env.browser === 'Opera' && Env.version >= 12) ||
10995					!!~Basic.inArray(Env.browser, ['Chrome', 'Safari']);
10996			}()),
10997			upload_filesize: True,
10998			use_http_method: function(methods) {
10999				return !Basic.arrayDiff(methods, ['GET', 'POST']);
11000			}
11001		});
11002
11003
11004		Basic.extend(this, {
11005			init : function() {
11006				this.trigger("Init");
11007			},
11008
11009			destroy: (function(destroy) { // extend default destroy method
11010				return function() {
11011					destroy.call(I);
11012					destroy = I = null;
11013				};
11014			}(this.destroy))
11015		});
11016
11017		Basic.extend(this.getShim(), extensions);
11018	}
11019
11020	Runtime.addConstructor(type, Html4Runtime);
11021
11022	return extensions;
11023});
11024
11025// Included from: src/javascript/runtime/html4/file/FileInput.js
11026
11027/**
11028 * FileInput.js
11029 *
11030 * Copyright 2013, Moxiecode Systems AB
11031 * Released under GPL License.
11032 *
11033 * License: http://www.plupload.com/license
11034 * Contributing: http://www.plupload.com/contributing
11035 */
11036
11037/**
11038@class moxie/runtime/html4/file/FileInput
11039@private
11040*/
11041define("moxie/runtime/html4/file/FileInput", [
11042	"moxie/runtime/html4/Runtime",
11043	"moxie/core/utils/Basic",
11044	"moxie/core/utils/Dom",
11045	"moxie/core/utils/Events",
11046	"moxie/core/utils/Mime",
11047	"moxie/core/utils/Env"
11048], function(extensions, Basic, Dom, Events, Mime, Env) {
11049	
11050	function FileInput() {
11051		var _uid, _files = [], _mimes = [], _options;
11052
11053		function addInput() {
11054			var comp = this, I = comp.getRuntime(), shimContainer, browseButton, currForm, form, input, uid;
11055
11056			uid = Basic.guid('uid_');
11057
11058			shimContainer = I.getShimContainer(); // we get new ref everytime to avoid memory leaks in IE
11059
11060			if (_uid) { // move previous form out of the view
11061				currForm = Dom.get(_uid + '_form');
11062				if (currForm) {
11063					Basic.extend(currForm.style, { top: '100%' });
11064				}
11065			}
11066
11067			// build form in DOM, since innerHTML version not able to submit file for some reason
11068			form = document.createElement('form');
11069			form.setAttribute('id', uid + '_form');
11070			form.setAttribute('method', 'post');
11071			form.setAttribute('enctype', 'multipart/form-data');
11072			form.setAttribute('encoding', 'multipart/form-data');
11073
11074			Basic.extend(form.style, {
11075				overflow: 'hidden',
11076				position: 'absolute',
11077				top: 0,
11078				left: 0,
11079				width: '100%',
11080				height: '100%'
11081			});
11082
11083			input = document.createElement('input');
11084			input.setAttribute('id', uid);
11085			input.setAttribute('type', 'file');
11086			input.setAttribute('name', _options.name || 'Filedata');
11087			input.setAttribute('accept', _mimes.join(','));
11088
11089			Basic.extend(input.style, {
11090				fontSize: '999px',
11091				opacity: 0
11092			});
11093
11094			form.appendChild(input);
11095			shimContainer.appendChild(form);
11096
11097			// prepare file input to be placed underneath the browse_button element
11098			Basic.extend(input.style, {
11099				position: 'absolute',
11100				top: 0,
11101				left: 0,
11102				width: '100%',
11103				height: '100%'
11104			});
11105
11106			if (Env.browser === 'IE' && Env.version < 10) {
11107				Basic.extend(input.style, {
11108					filter : "progid:DXImageTransform.Microsoft.Alpha(opacity=0)"
11109				});
11110			}
11111
11112			input.onchange = function() { // there should be only one handler for this
11113				var file;
11114
11115				if (!this.value) {
11116					return;
11117				}
11118
11119				if (this.files) {
11120					file = this.files[0];
11121				} else {
11122					file = {
11123						name: this.value
11124					};
11125				}
11126
11127				_files = [file];
11128
11129				this.onchange = function() {}; // clear event handler
11130				addInput.call(comp);
11131
11132				// after file is initialized as o.File, we need to update form and input ids
11133				comp.bind('change', function onChange() {
11134					var input = Dom.get(uid), form = Dom.get(uid + '_form'), file;
11135
11136					comp.unbind('change', onChange);
11137
11138					if (comp.files.length && input && form) {
11139						file = comp.files[0];
11140
11141						input.setAttribute('id', file.uid);
11142						form.setAttribute('id', file.uid + '_form');
11143
11144						// set upload target
11145						form.setAttribute('target', file.uid + '_iframe');
11146					}
11147					input = form = null;
11148				}, 998);
11149
11150				input = form = null;
11151				comp.trigger('change');
11152			};
11153
11154
11155			// route click event to the input
11156			if (I.can('summon_file_dialog')) {
11157				browseButton = Dom.get(_options.browse_button);
11158				Events.removeEvent(browseButton, 'click', comp.uid);
11159				Events.addEvent(browseButton, 'click', function(e) {
11160					if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file]
11161						input.click();
11162					}
11163					e.preventDefault();
11164				}, comp.uid);
11165			}
11166
11167			_uid = uid;
11168
11169			shimContainer = currForm = browseButton = null;
11170		}
11171
11172		Basic.extend(this, {
11173			init: function(options) {
11174				var comp = this, I = comp.getRuntime(), shimContainer;
11175
11176				// figure out accept string
11177				_options = options;
11178				_mimes = options.accept.mimes || Mime.extList2mimes(options.accept, I.can('filter_by_extension'));
11179
11180				shimContainer = I.getShimContainer();
11181
11182				(function() {
11183					var browseButton, zIndex, top;
11184
11185					browseButton = Dom.get(options.browse_button);
11186
11187					// Route click event to the input[type=file] element for browsers that support such behavior
11188					if (I.can('summon_file_dialog')) {
11189						if (Dom.getStyle(browseButton, 'position') === 'static') {
11190							browseButton.style.position = 'relative';
11191						}
11192
11193						zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 1;
11194
11195						browseButton.style.zIndex = zIndex;
11196						shimContainer.style.zIndex = zIndex - 1;
11197					}
11198
11199					/* Since we have to place input[type=file] on top of the browse_button for some browsers,
11200					browse_button loses interactivity, so we restore it here */
11201					top = I.can('summon_file_dialog') ? browseButton : shimContainer;
11202
11203					Events.addEvent(top, 'mouseover', function() {
11204						comp.trigger('mouseenter');
11205					}, comp.uid);
11206
11207					Events.addEvent(top, 'mouseout', function() {
11208						comp.trigger('mouseleave');
11209					}, comp.uid);
11210
11211					Events.addEvent(top, 'mousedown', function() {
11212						comp.trigger('mousedown');
11213					}, comp.uid);
11214
11215					Events.addEvent(Dom.get(options.container), 'mouseup', function() {
11216						comp.trigger('mouseup');
11217					}, comp.uid);
11218
11219					browseButton = null;
11220				}());
11221
11222				addInput.call(this);
11223
11224				shimContainer = null;
11225
11226				// trigger ready event asynchronously
11227				comp.trigger({
11228					type: 'ready',
11229					async: true
11230				});
11231			},
11232
11233			getFiles: function() {
11234				return _files;
11235			},
11236
11237			disable: function(state) {
11238				var input;
11239
11240				if ((input = Dom.get(_uid))) {
11241					input.disabled = !!state;
11242				}
11243			},
11244
11245			destroy: function() {
11246				var I = this.getRuntime()
11247				, shim = I.getShim()
11248				, shimContainer = I.getShimContainer()
11249				;
11250				
11251				Events.removeAllEvents(shimContainer, this.uid);
11252				Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
11253				Events.removeAllEvents(_options && Dom.get(_options.browse_button), this.uid);
11254				
11255				if (shimContainer) {
11256					shimContainer.innerHTML = '';
11257				}
11258
11259				shim.removeInstance(this.uid);
11260
11261				_uid = _files = _mimes = _options = shimContainer = shim = null;
11262			}
11263		});
11264	}
11265
11266	return (extensions.FileInput = FileInput);
11267});
11268
11269// Included from: src/javascript/runtime/html4/file/FileReader.js
11270
11271/**
11272 * FileReader.js
11273 *
11274 * Copyright 2013, Moxiecode Systems AB
11275 * Released under GPL License.
11276 *
11277 * License: http://www.plupload.com/license
11278 * Contributing: http://www.plupload.com/contributing
11279 */
11280
11281/**
11282@class moxie/runtime/html4/file/FileReader
11283@private
11284*/
11285define("moxie/runtime/html4/file/FileReader", [
11286	"moxie/runtime/html4/Runtime",
11287	"moxie/runtime/html5/file/FileReader"
11288], function(extensions, FileReader) {
11289	return (extensions.FileReader = FileReader);
11290});
11291
11292// Included from: src/javascript/runtime/html4/xhr/XMLHttpRequest.js
11293
11294/**
11295 * XMLHttpRequest.js
11296 *
11297 * Copyright 2013, Moxiecode Systems AB
11298 * Released under GPL License.
11299 *
11300 * License: http://www.plupload.com/license
11301 * Contributing: http://www.plupload.com/contributing
11302 */
11303
11304/**
11305@class moxie/runtime/html4/xhr/XMLHttpRequest
11306@private
11307*/
11308define("moxie/runtime/html4/xhr/XMLHttpRequest", [
11309	"moxie/runtime/html4/Runtime",
11310	"moxie/core/utils/Basic",
11311	"moxie/core/utils/Dom",
11312	"moxie/core/utils/Url",
11313	"moxie/core/Exceptions",
11314	"moxie/core/utils/Events",
11315	"moxie/file/Blob",
11316	"moxie/xhr/FormData"
11317], function(extensions, Basic, Dom, Url, x, Events, Blob, FormData) {
11318	
11319	function XMLHttpRequest() {
11320		var _status, _response, _iframe;
11321
11322		function cleanup(cb) {
11323			var target = this, uid, form, inputs, i, hasFile = false;
11324
11325			if (!_iframe) {
11326				return;
11327			}
11328
11329			uid = _iframe.id.replace(/_iframe$/, '');
11330
11331			form = Dom.get(uid + '_form');
11332			if (form) {
11333				inputs = form.getElementsByTagName('input');
11334				i = inputs.length;
11335
11336				while (i--) {
11337					switch (inputs[i].getAttribute('type')) {
11338						case 'hidden':
11339							inputs[i].parentNode.removeChild(inputs[i]);
11340							break;
11341						case 'file':
11342							hasFile = true; // flag the case for later
11343							break;
11344					}
11345				}
11346				inputs = [];
11347
11348				if (!hasFile) { // we need to keep the form for sake of possible retries
11349					form.parentNode.removeChild(form);
11350				}
11351				form = null;
11352			}
11353
11354			// without timeout, request is marked as canceled (in console)
11355			setTimeout(function() {
11356				Events.removeEvent(_iframe, 'load', target.uid);
11357				if (_iframe.parentNode) { // #382
11358					_iframe.parentNode.removeChild(_iframe);
11359				}
11360
11361				// check if shim container has any other children, if - not, remove it as well
11362				var shimContainer = target.getRuntime().getShimContainer();
11363				if (!shimContainer.children.length) {
11364					shimContainer.parentNode.removeChild(shimContainer);
11365				}
11366
11367				shimContainer = _iframe = null;
11368				cb();
11369			}, 1);
11370		}
11371
11372		Basic.extend(this, {
11373			send: function(meta, data) {
11374				var target = this, I = target.getRuntime(), uid, form, input, blob;
11375
11376				_status = _response = null;
11377
11378				function createIframe() {
11379					var container = I.getShimContainer() || document.body
11380					, temp = document.createElement('div')
11381					;
11382
11383					// IE 6 won't be able to set the name using setAttribute or iframe.name
11384					temp.innerHTML = '<iframe id="' + uid + '_iframe" name="' + uid + '_iframe" src="javascript:&quot;&quot;" style="display:none"></iframe>';
11385					_iframe = temp.firstChild;
11386					container.appendChild(_iframe);
11387
11388					/* _iframe.onreadystatechange = function() {
11389						console.info(_iframe.readyState);
11390					};*/
11391
11392					Events.addEvent(_iframe, 'load', function() { // _iframe.onload doesn't work in IE lte 8
11393						var el;
11394
11395						try {
11396							el = _iframe.contentWindow.document || _iframe.contentDocument || window.frames[_iframe.id].document;
11397
11398							// try to detect some standard error pages
11399							if (/^4(0[0-9]|1[0-7]|2[2346])\s/.test(el.title)) { // test if title starts with 4xx HTTP error
11400								_status = el.title.replace(/^(\d+).*$/, '$1');
11401							} else {
11402								_status = 200;
11403								// get result
11404								_response = Basic.trim(el.body.innerHTML);
11405
11406								// we need to fire these at least once
11407								target.trigger({
11408									type: 'progress',
11409									loaded: _response.length,
11410									total: _response.length
11411								});
11412
11413								if (blob) { // if we were uploading a file
11414									target.trigger({
11415										type: 'uploadprogress',
11416										loaded: blob.size || 1025,
11417										total: blob.size || 1025
11418									});
11419								}
11420							}
11421						} catch (ex) {
11422							if (Url.hasSameOrigin(meta.url)) {
11423								// if response is sent with error code, iframe in IE gets redirected to res://ieframe.dll/http_x.htm
11424								// which obviously results to cross domain error (wtf?)
11425								_status = 404;
11426							} else {
11427								cleanup.call(target, function() {
11428									target.trigger('error');
11429								});
11430								return;
11431							}
11432						}	
11433					
11434						cleanup.call(target, function() {
11435							target.trigger('load');
11436						});
11437					}, target.uid);
11438				} // end createIframe
11439
11440				// prepare data to be sent and convert if required
11441				if (data instanceof FormData && data.hasBlob()) {
11442					blob = data.getBlob();
11443					uid = blob.uid;
11444					input = Dom.get(uid);
11445					form = Dom.get(uid + '_form');
11446					if (!form) {
11447						throw new x.DOMException(x.DOMException.NOT_FOUND_ERR);
11448					}
11449				} else {
11450					uid = Basic.guid('uid_');
11451
11452					form = document.createElement('form');
11453					form.setAttribute('id', uid + '_form');
11454					form.setAttribute('method', meta.method);
11455					form.setAttribute('enctype', 'multipart/form-data');
11456					form.setAttribute('encoding', 'multipart/form-data');
11457					form.setAttribute('target', uid + '_iframe');
11458
11459					I.getShimContainer().appendChild(form);
11460				}
11461
11462				if (data instanceof FormData) {
11463					data.each(function(value, name) {
11464						if (value instanceof Blob) {
11465							if (input) {
11466								input.setAttribute('name', name);
11467							}
11468						} else {
11469							var hidden = document.createElement('input');
11470
11471							Basic.extend(hidden, {
11472								type : 'hidden',
11473								name : name,
11474								value : value
11475							});
11476
11477							// make sure that input[type="file"], if it's there, comes last
11478							if (input) {
11479								form.insertBefore(hidden, input);
11480							} else {
11481								form.appendChild(hidden);
11482							}
11483						}
11484					});
11485				}
11486
11487				// set destination url
11488				form.setAttribute("action", meta.url);
11489
11490				createIframe();
11491				form.submit();
11492				target.trigger('loadstart');
11493			},
11494
11495			getStatus: function() {
11496				return _status;
11497			},
11498
11499			getResponse: function(responseType) {
11500				if ('json' === responseType) {
11501					// strip off <pre>..</pre> tags that might be enclosing the response
11502					if (Basic.typeOf(_response) === 'string' && !!window.JSON) {
11503						try {
11504							return JSON.parse(_response.replace(/^\s*<pre[^>]*>/, '').replace(/<\/pre>\s*$/, ''));
11505						} catch (ex) {
11506							return null;
11507						}
11508					} 
11509				} else if ('document' === responseType) {
11510
11511				}
11512				return _response;
11513			},
11514
11515			abort: function() {
11516				var target = this;
11517
11518				if (_iframe && _iframe.contentWindow) {
11519					if (_iframe.contentWindow.stop) { // FireFox/Safari/Chrome
11520						_iframe.contentWindow.stop();
11521					} else if (_iframe.contentWindow.document.execCommand) { // IE
11522						_iframe.contentWindow.document.execCommand('Stop');
11523					} else {
11524						_iframe.src = "about:blank";
11525					}
11526				}
11527
11528				cleanup.call(this, function() {
11529					// target.dispatchEvent('readystatechange');
11530					target.dispatchEvent('abort');
11531				});
11532			}
11533		});
11534	}
11535
11536	return (extensions.XMLHttpRequest = XMLHttpRequest);
11537});
11538
11539// Included from: src/javascript/runtime/html4/image/Image.js
11540
11541/**
11542 * Image.js
11543 *
11544 * Copyright 2013, Moxiecode Systems AB
11545 * Released under GPL License.
11546 *
11547 * License: http://www.plupload.com/license
11548 * Contributing: http://www.plupload.com/contributing
11549 */
11550
11551/**
11552@class moxie/runtime/html4/image/Image
11553@private
11554*/
11555define("moxie/runtime/html4/image/Image", [
11556	"moxie/runtime/html4/Runtime",
11557	"moxie/runtime/html5/image/Image"
11558], function(extensions, Image) {
11559	return (extensions.Image = Image);
11560});
11561
11562expose(["moxie/core/utils/Basic","moxie/core/I18n","moxie/core/utils/Mime","moxie/core/utils/Env","moxie/core/utils/Dom","moxie/core/Exceptions","moxie/core/EventTarget","moxie/core/utils/Encode","moxie/runtime/Runtime","moxie/runtime/RuntimeClient","moxie/file/Blob","moxie/file/File","moxie/file/FileInput","moxie/file/FileDrop","moxie/runtime/RuntimeTarget","moxie/file/FileReader","moxie/core/utils/Url","moxie/file/FileReaderSync","moxie/xhr/FormData","moxie/xhr/XMLHttpRequest","moxie/runtime/Transporter","moxie/image/Image","moxie/core/utils/Events"]);
11563})(this);/**
11564 * o.js
11565 *
11566 * Copyright 2013, Moxiecode Systems AB
11567 * Released under GPL License.
11568 *
11569 * License: http://www.plupload.com/license
11570 * Contributing: http://www.plupload.com/contributing
11571 */
11572
11573/*global moxie:true */
11574
11575/**
11576Globally exposed namespace with the most frequently used public classes and handy methods.
11577
11578@class o
11579@static
11580@private
11581*/
11582(function(exports) {
11583	"use strict";
11584
11585	var o = {}, inArray = exports.moxie.core.utils.Basic.inArray;
11586
11587	// directly add some public classes
11588	// (we do it dynamically here, since for custom builds we cannot know beforehand what modules were included)
11589	(function addAlias(ns) {
11590		var name, itemType;
11591		for (name in ns) {
11592			itemType = typeof(ns[name]);
11593			if (itemType === 'object' && !~inArray(name, ['Exceptions', 'Env', 'Mime'])) {
11594				addAlias(ns[name]);
11595			} else if (itemType === 'function') {
11596				o[name] = ns[name];
11597			}
11598		}
11599	})(exports.moxie);
11600
11601	// add some manually
11602	o.Env = exports.moxie.core.utils.Env;
11603	o.Mime = exports.moxie.core.utils.Mime;
11604	o.Exceptions = exports.moxie.core.Exceptions;
11605
11606	// expose globally
11607	exports.mOxie = o;
11608	if (!exports.o) {
11609		exports.o = o;
11610	}
11611	return o;
11612})(this);
11613
11614/**
11615 * Plupload - multi-runtime File Uploader
11616 * v2.1.2
11617 *
11618 * Copyright 2013, Moxiecode Systems AB
11619 * Released under GPL License.
11620 *
11621 * License: http://www.plupload.com/license
11622 * Contributing: http://www.plupload.com/contributing
11623 *
11624 * Date: 2014-05-14
11625 */
11626/**
11627 * Plupload.js
11628 *
11629 * Copyright 2013, Moxiecode Systems AB
11630 * Released under GPL License.
11631 *
11632 * License: http://www.plupload.com/license
11633 * Contributing: http://www.plupload.com/contributing
11634 */
11635
11636/*global mOxie:true */
11637
11638;(function(window, o, undef) {
11639
11640var delay = window.setTimeout
11641, fileFilters = {}
11642;
11643
11644// convert plupload features to caps acceptable by mOxie
11645function normalizeCaps(settings) {		
11646	var features = settings.required_features, caps = {};
11647
11648	function resolve(feature, value, strict) {
11649		// Feature notation is deprecated, use caps (this thing here is required for backward compatibility)
11650		var map = { 
11651			chunks: 'slice_blob',
11652			jpgresize: 'send_binary_string',
11653			pngresize: 'send_binary_string',
11654			progress: 'report_upload_progress',
11655			multi_selection: 'select_multiple',
11656			dragdrop: 'drag_and_drop',
11657			drop_element: 'drag_and_drop',
11658			headers: 'send_custom_headers',
11659			urlstream_upload: 'send_binary_string',
11660			canSendBinary: 'send_binary',
11661			triggerDialog: 'summon_file_dialog'
11662		};
11663
11664		if (map[feature]) {
11665			caps[map[feature]] = value;
11666		} else if (!strict) {
11667			caps[feature] = value;
11668		}
11669	}
11670
11671	if (typeof(features) === 'string') {
11672		plupload.each(features.split(/\s*,\s*/), function(feature) {
11673			resolve(feature, true);
11674		});
11675	} else if (typeof(features) === 'object') {
11676		plupload.each(features, function(value, feature) {
11677			resolve(feature, value);
11678		});
11679	} else if (features === true) {
11680		// check settings for required features
11681		if (settings.chunk_size > 0) {
11682			caps.slice_blob = true;
11683		}
11684
11685		if (settings.resize.enabled || !settings.multipart) {
11686			caps.send_binary_string = true;
11687		}
11688		
11689		plupload.each(settings, function(value, feature) {
11690			resolve(feature, !!value, true); // strict check
11691		});
11692	}
11693	
11694	return caps;
11695}
11696
11697/** 
11698 * @module plupload	
11699 * @static
11700 */
11701var plupload = {
11702	/**
11703	 * Plupload version will be replaced on build.
11704	 *
11705	 * @property VERSION
11706	 * @for Plupload
11707	 * @static
11708	 * @final
11709	 */
11710	VERSION : '2.1.2',
11711
11712	/**
11713	 * Inital state of the queue and also the state ones it's finished all it's uploads.
11714	 *
11715	 * @property STOPPED
11716	 * @static
11717	 * @final
11718	 */
11719	STOPPED : 1,
11720
11721	/**
11722	 * Upload process is running
11723	 *
11724	 * @property STARTED
11725	 * @static
11726	 * @final
11727	 */
11728	STARTED : 2,
11729
11730	/**
11731	 * File is queued for upload
11732	 *
11733	 * @property QUEUED
11734	 * @static
11735	 * @final
11736	 */
11737	QUEUED : 1,
11738
11739	/**
11740	 * File is being uploaded
11741	 *
11742	 * @property UPLOADING
11743	 * @static
11744	 * @final
11745	 */
11746	UPLOADING : 2,
11747
11748	/**
11749	 * File has failed to be uploaded
11750	 *
11751	 * @property FAILED
11752	 * @static
11753	 * @final
11754	 */
11755	FAILED : 4,
11756
11757	/**
11758	 * File has been uploaded successfully
11759	 *
11760	 * @property DONE
11761	 * @static
11762	 * @final
11763	 */
11764	DONE : 5,
11765
11766	// Error constants used by the Error event
11767
11768	/**
11769	 * Generic error for example if an exception is thrown inside Silverlight.
11770	 *
11771	 * @property GENERIC_ERROR
11772	 * @static
11773	 * @final
11774	 */
11775	GENERIC_ERROR : -100,
11776
11777	/**
11778	 * HTTP transport error. For example if the server produces a HTTP status other than 200.
11779	 *
11780	 * @property HTTP_ERROR
11781	 * @static
11782	 * @final
11783	 */
11784	HTTP_ERROR : -200,
11785
11786	/**
11787	 * Generic I/O error. For example if it wasn't possible to open the file stream on local machine.
11788	 *
11789	 * @property IO_ERROR
11790	 * @static
11791	 * @final
11792	 */
11793	IO_ERROR : -300,
11794
11795	/**
11796	 * @property SECURITY_ERROR
11797	 * @static
11798	 * @final
11799	 */
11800	SECURITY_ERROR : -400,
11801
11802	/**
11803	 * Initialization error. Will be triggered if no runtime was initialized.
11804	 *
11805	 * @property INIT_ERROR
11806	 * @static
11807	 * @final
11808	 */
11809	INIT_ERROR : -500,
11810
11811	/**
11812	 * File size error. If the user selects a file that is too large it will be blocked and an error of this type will be triggered.
11813	 *
11814	 * @property FILE_SIZE_ERROR
11815	 * @static
11816	 * @final
11817	 */
11818	FILE_SIZE_ERROR : -600,
11819
11820	/**
11821	 * File extension error. If the user selects a file that isn't valid according to the filters setting.
11822	 *
11823	 * @property FILE_EXTENSION_ERROR
11824	 * @static
11825	 * @final
11826	 */
11827	FILE_EXTENSION_ERROR : -601,
11828
11829	/**
11830	 * Duplicate file error. If prevent_duplicates is set to true and user selects the same file again.
11831	 *
11832	 * @property FILE_DUPLICATE_ERROR
11833	 * @static
11834	 * @final
11835	 */
11836	FILE_DUPLICATE_ERROR : -602,
11837
11838	/**
11839	 * Runtime will try to detect if image is proper one. Otherwise will throw this error.
11840	 *
11841	 * @property IMAGE_FORMAT_ERROR
11842	 * @static
11843	 * @final
11844	 */
11845	IMAGE_FORMAT_ERROR : -700,
11846
11847	/**
11848	 * While working on files runtime may run out of memory and will throw this error.
11849	 *
11850	 * @since 2.1.2
11851	 * @property MEMORY_ERROR
11852	 * @static
11853	 * @final
11854	 */
11855	MEMORY_ERROR : -701,
11856
11857	/**
11858	 * Each runtime has an upper limit on a dimension of the image it can handle. If bigger, will throw this error.
11859	 *
11860	 * @property IMAGE_DIMENSIONS_ERROR
11861	 * @static
11862	 * @final
11863	 */
11864	IMAGE_DIMENSIONS_ERROR : -702,
11865
11866	/**
11867	 * Mime type lookup table.
11868	 *
11869	 * @property mimeTypes
11870	 * @type Object
11871	 * @final
11872	 */
11873	mimeTypes : o.mimes,
11874
11875	/**
11876	 * In some cases sniffing is the only way around :(
11877	 */
11878	ua: o.ua,
11879
11880	/**
11881	 * Gets the true type of the built-in object (better version of typeof).
11882	 * @credits Angus Croll (http://javascriptweblog.wordpress.com/)
11883	 *
11884	 * @method typeOf
11885	 * @static
11886	 * @param {Object} o Object to check.
11887	 * @return {String} Object [[Class]]
11888	 */
11889	typeOf: o.typeOf,
11890
11891	/**
11892	 * Extends the specified object with another object.
11893	 *
11894	 * @method extend
11895	 * @static
11896	 * @param {Object} target Object to extend.
11897	 * @param {Object..} obj Multiple objects to extend with.
11898	 * @return {Object} Same as target, the extended object.
11899	 */
11900	extend : o.extend,
11901
11902	/**
11903	 * Generates an unique ID. This is 99.99% unique since it takes the current time and 5 random numbers.
11904	 * The only way a user would be able to get the same ID is if the two persons at the same exact milisecond manages
11905	 * to get 5 the same random numbers between 0-65535 it also uses a counter so each call will be guaranteed to be page unique.
11906	 * It's more probable for the earth to be hit with an ansteriod. You can also if you want to be 100% sure set the plupload.guidPrefix property
11907	 * to an user unique key.
11908	 *
11909	 * @method guid
11910	 * @static
11911	 * @return {String} Virtually unique id.
11912	 */
11913	guid : o.guid,
11914
11915	/**
11916	 * Get array of DOM Elements by their ids.
11917	 * 
11918	 * @method get
11919	 * @for Utils
11920	 * @param {String} id Identifier of the DOM Element
11921	 * @return {Array}
11922	*/
11923	get : function get(ids) {
11924		var els = [], el;
11925
11926		if (o.typeOf(ids) !== 'array') {
11927			ids = [ids];
11928		}
11929
11930		var i = ids.length;
11931		while (i--) {
11932			el = o.get(ids[i]);
11933			if (el) {
11934				els.push(el);
11935			}
11936		}
11937
11938		return els.length ? els : null;
11939	},
11940
11941	/**
11942	 * Executes the callback function for each item in array/object. If you return false in the
11943	 * callback it will break the loop.
11944	 *
11945	 * @method each
11946	 * @static
11947	 * @param {Object} obj Object to iterate.
11948	 * @param {function} callback Callback function to execute for each item.
11949	 */
11950	each : o.each,
11951
11952	/**
11953	 * Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields.
11954	 *
11955	 * @method getPos
11956	 * @static
11957	 * @param {Element} node HTML element or element id to get x, y position from.
11958	 * @param {Element} root Optional root element to stop calculations at.
11959	 * @return {object} Absolute position of the specified element object with x, y fields.
11960	 */
11961	getPos : o.getPos,
11962
11963	/**
11964	 * Returns the size of the specified node in pixels.
11965	 *
11966	 * @method getSize
11967	 * @static
11968	 * @param {Node} node Node to get the size of.
11969	 * @return {Object} Object with a w and h property.
11970	 */
11971	getSize : o.getSize,
11972
11973	/**
11974	 * Encodes the specified string.
11975	 *
11976	 * @method xmlEncode
11977	 * @static
11978	 * @param {String} s String to encode.
11979	 * @return {String} Encoded string.
11980	 */
11981	xmlEncode : function(str) {
11982		var xmlEncodeChars = {'<' : 'lt', '>' : 'gt', '&' : 'amp', '"' : 'quot', '\'' : '#39'}, xmlEncodeRegExp = /[<>&\"\']/g;
11983
11984		return str ? ('' + str).replace(xmlEncodeRegExp, function(chr) {
11985			return xmlEncodeChars[chr] ? '&' + xmlEncodeChars[chr] + ';' : chr;
11986		}) : str;
11987	},
11988
11989	/**
11990	 * Forces anything into an array.
11991	 *
11992	 * @method toArray
11993	 * @static
11994	 * @param {Object} obj Object with length field.
11995	 * @return {Array}
11995 Array object containing all items.
11996	 */
11997	toArray : o.toArray,
11998
11999	/**
12000	 * Find an element in array and return it's index if present, otherwise return -1.
12001	 *
12002	 * @method inArray
12003	 * @static
12004	 * @param {mixed} needle Element to find
12005	 * @param {Array} array
12006	 * @return {Int} Index of the element, or -1 if not found
12007	 */
12008	inArray : o.inArray,
12009
12010	/**
12011	 * Extends the language pack object with new items.
12012	 *
12013	 * @method addI18n
12014	 * @static
12015	 * @param {Object} pack Language pack items to add.
12016	 * @return {Object} Extended language pack object.
12017	 */
12018	addI18n : o.addI18n,
12019
12020	/**
12021	 * Translates the specified string by checking for the english string in the language pack lookup.
12022	 *
12023	 * @method translate
12024	 * @static
12025	 * @param {String} str String to look for.
12026	 * @return {String} Translated string or the input string if it wasn't found.
12027	 */
12028	translate : o.translate,
12029
12030	/**
12031	 * Checks if object is empty.
12032	 *
12033	 * @method isEmptyObj
12034	 * @static
12035	 * @param {Object} obj Object to check.
12036	 * @return {Boolean}
12037	 */
12038	isEmptyObj : o.isEmptyObj,
12039
12040	/**
12041	 * Checks if specified DOM element has specified class.
12042	 *
12043	 * @method hasClass
12044	 * @static
12045	 * @param {Object} obj DOM element like object to add handler to.
12046	 * @param {String} name Class name
12047	 */
12048	hasClass : o.hasClass,
12049
12050	/**
12051	 * Adds specified className to specified DOM element.
12052	 *
12053	 * @method addClass
12054	 * @static
12055	 * @param {Object} obj DOM element like object to add handler to.
12056	 * @param {String} name Class name
12057	 */
12058	addClass : o.addClass,
12059
12060	/**
12061	 * Removes specified className from specified DOM element.
12062	 *
12063	 * @method removeClass
12064	 * @static
12065	 * @param {Object} obj DOM element like object to add handler to.
12066	 * @param {String} name Class name
12067	 */
12068	removeClass : o.removeClass,
12069
12070	/**
12071	 * Returns a given computed style of a DOM element.
12072	 *
12073	 * @method getStyle
12074	 * @static
12075	 * @param {Object} obj DOM element like object.
12076	 * @param {String} name Style you want to get from the DOM element
12077	 */
12078	getStyle : o.getStyle,
12079
12080	/**
12081	 * Adds an event handler to the specified object and store reference to the handler
12082	 * in objects internal Plupload registry (@see removeEvent).
12083	 *
12084	 * @method addEvent
12085	 * @static
12086	 * @param {Object} obj DOM element like object to add handler to.
12087	 * @param {String} name Name to add event listener to.
12088	 * @param {Function} callback Function to call when event occurs.
12089	 * @param {String} (optional) key that might be used to add specifity to the event record.
12090	 */
12091	addEvent : o.addEvent,
12092
12093	/**
12094	 * Remove event handler from the specified object. If third argument (callback)
12095	 * is not specified remove all events with the specified name.
12096	 *
12097	 * @method removeEvent
12098	 * @static
12099	 * @param {Object} obj DOM element to remove event listener(s) from.
12100	 * @param {String} name Name of event listener to remove.
12101	 * @param {Function|String} (optional) might be a callback or unique key to match.
12102	 */
12103	removeEvent: o.removeEvent,
12104
12105	/**
12106	 * Remove all kind of events from the specified object
12107	 *
12108	 * @method removeAllEvents
12109	 * @static
12110	 * @param {Object} obj DOM element to remove event listeners from.
12111	 * @param {String} (optional) unique key to match, when removing events.
12112	 */
12113	removeAllEvents: o.removeAllEvents,
12114
12115	/**
12116	 * Cleans the specified name from national characters (diacritics). The result will be a name with only a-z, 0-9 and _.
12117	 *
12118	 * @method cleanName
12119	 * @static
12120	 * @param {String} s String to clean up.
12121	 * @return {String} Cleaned string.
12122	 */
12123	cleanName : function(name) {
12124		var i, lookup;
12125
12126		// Replace diacritics
12127		lookup = [
12128			/[\300-\306]/g, 'A', /[\340-\346]/g, 'a',
12129			/\307/g, 'C', /\347/g, 'c',
12130			/[\310-\313]/g, 'E', /[\350-\353]/g, 'e',
12131			/[\314-\317]/g, 'I', /[\354-\357]/g, 'i',
12132			/\321/g, 'N', /\361/g, 'n',
12133			/[\322-\330]/g, 'O', /[\362-\370]/g, 'o',
12134			/[\331-\334]/g, 'U', /[\371-\374]/g, 'u'
12135		];
12136
12137		for (i = 0; i < lookup.length; i += 2) {
12138			name = name.replace(lookup[i], lookup[i + 1]);
12139		}
12140
12141		// Replace whitespace
12142		name = name.replace(/\s+/g, '_');
12143
12144		// Remove anything else
12145		name = name.replace(/[^a-z0-9_\-\.]+/gi, '');
12146
12147		return name;
12148	},
12149
12150	/**
12151	 * Builds a full url out of a base URL and an object with items to append as query string items.
12152	 *
12153	 * @method buildUrl
12154	 * @static
12155	 * @param {String} url Base URL to append query string items to.
12156	 * @param {Object} items Name/value object to serialize as a querystring.
12157	 * @return {String} String with url + serialized query string items.
12158	 */
12159	buildUrl : function(url, items) {
12160		var query = '';
12161
12162		plupload.each(items, function(value, name) {
12163			query += (query ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value);
12164		});
12165
12166		if (query) {
12167			url += (url.indexOf('?') > 0 ? '&' : '?') + query;
12168		}
12169
12170		return url;
12171	},
12172
12173	/**
12174	 * Formats the specified number as a size string for example 1024 becomes 1 KB.
12175	 *
12176	 * @method formatSize
12177	 * @static
12178	 * @param {Number} size Size to format as string.
12179	 * @return {String} Formatted size string.
12180	 */
12181	formatSize : function(size) {
12182
12183		if (size === undef || /\D/.test(size)) {
12184			return plupload.translate('N/A');
12185		}
12186
12187		function round(num, precision) {
12188			return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision);
12189		}
12190
12191		var boundary = Math.pow(1024, 4);
12192
12193		// TB
12194		if (size > boundary) {
12195			return round(size / boundary, 1) + " " + plupload.translate('tb');
12196		}
12197
12198		// GB
12199		if (size > (boundary/=1024)) {
12200			return round(size / boundary, 1) + " " + plupload.translate('gb');
12201		}
12202
12203		// MB
12204		if (size > (boundary/=1024)) {
12205			return round(size / boundary, 1) + " " + plupload.translate('mb');
12206		}
12207
12208		// KB
12209		if (size > 1024) {
12210			return Math.round(size / 1024) + " " + plupload.translate('kb');
12211		}
12212
12213		return size + " " + plupload.translate('b');
12214	},
12215
12216
12217	/**
12218	 * Parses the specified size string into a byte value. For example 10kb becomes 10240.
12219	 *
12220	 * @method parseSize
12221	 * @static
12222	 * @param {String|Number} size String to parse or number to just pass through.
12223	 * @return {Number} Size in bytes.
12224	 */
12225	parseSize : o.parseSizeStr,
12226
12227
12228	/**
12229	 * A way to predict what runtime will be choosen in the current environment with the
12230	 * specified settings.
12231	 *
12232	 * @method predictRuntime
12233	 * @static
12234	 * @param {Object|String} config Plupload settings to check
12235	 * @param {String} [runtimes] Comma-separated list of runtimes to check against
12236	 * @return {String} Type of compatible runtime
12237	 */
12238	predictRuntime : function(config, runtimes) {
12239		var up, runtime;
12240
12241		up = new plupload.Uploader(config);
12242		runtime = o.Runtime.thatCan(up.getOption().required_features, runtimes || config.runtimes);
12243		up.destroy();
12244		return runtime;
12245	},
12246
12247	/**
12248	 * Registers a filter that will be executed for each file added to the queue.
12249	 * If callback returns false, file will not be added.
12250	 *
12251	 * Callback receives two arguments: a value for the filter as it was specified in settings.filters
12252	 * and a file to be filtered. Callback is executed in the context of uploader instance.
12253	 *
12254	 * @method addFileFilter
12255	 * @static
12256	 * @param {String} name Name of the filter by which it can be referenced in settings.filters
12257	 * @param {String} cb Callback - the actual routine that every added file must pass
12258	 */
12259	addFileFilter: function(name, cb) {
12260		fileFilters[name] = cb;
12261	}
12262};
12263
12264
12265plupload.addFileFilter('mime_types', function(filters, file, cb) {
12266	if (filters.length && !filters.regexp.test(file.name)) {
12267		this.trigger('Error', {
12268			code : plupload.FILE_EXTENSION_ERROR,
12269			message : plupload.translate('File extension error.'),
12270			file : file
12271		});
12272		cb(false);
12273	} else {
12274		cb(true);
12275	}
12276});
12277
12278
12279plupload.addFileFilter('max_file_size', function(maxSize, file, cb) {
12280	var undef;
12281
12282	maxSize = plupload.parseSize(maxSize);
12283
12284	// Invalid file size
12285	if (file.size !== undef && maxSize && file.size > maxSize) {
12286		this.trigger('Error', {
12287			code : plupload.FILE_SIZE_ERROR,
12288			message : plupload.translate('File size error.'),
12289			file : file
12290		});
12291		cb(false);
12292	} else {
12293		cb(true);
12294	}
12295});
12296
12297
12298plupload.addFileFilter('prevent_duplicates', function(value, file, cb) {
12299	if (value) {
12300		var ii = this.files.length;
12301		while (ii--) {
12302			// Compare by name and size (size might be 0 or undefined, but still equivalent for both)
12303			if (file.name === this.files[ii].name && file.size === this.files[ii].size) {
12304				this.trigger('Error', {
12305					code : plupload.FILE_DUPLICATE_ERROR,
12306					message : plupload.translate('Duplicate file error.'),
12307					file : file
12308				});
12309				cb(false);
12310				return;
12311			}
12312		}
12313	}
12314	cb(true);
12315});
12316
12317
12318/**
12319@class Uploader
12320@constructor
12321
12322@param {Object} settings For detailed information about each option check documentation.
12323	@param {String|DOMElement} settings.browse_button id of the DOM element or DOM element itself to use as file dialog trigger.
12324	@param {String} settings.url URL of the server-side upload handler.
12325	@param {Number|String} [settings.chunk_size=0] Chunk size in bytes to slice the file into. Shorcuts with b, kb, mb, gb, tb suffixes also supported. `e.g. 204800 or "204800b" or "200kb"`. By default - disabled.
12326	@param {Boolean} [settings.send_chunk_number=true] Whether to send chunks and chunk numbers, or total and offset bytes.
12327	@param {String} [settings.container] id of the DOM element to use as a container for uploader structures. Defaults to document.body.
12328	@param {String|DOMElement} [settings.drop_element] id of the DOM element or DOM element itself to use as a drop zone for Drag-n-Drop.
12329	@param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message.
12330	@param {Object} [settings.filters={}] Set of file type filters.
12331		@param {Array} [settings.filters.mime_types=[]] List of file types to accept, each one defined by title and list of extensions. `e.g. {title : "Image files", extensions : "jpg,jpeg,gif,png"}`. Dispatches `plupload.FILE_EXTENSION_ERROR`
12332		@param {String|Number} [settings.filters.max_file_size=0] Maximum file size that the user can pick, in bytes. Optionally supports b, kb, mb, gb, tb suffixes. `e.g. "10mb" or "1gb"`. By default - not set. Dispatches `plupload.FILE_SIZE_ERROR`.
12333		@param {Boolean} [settings.filters.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`.
12334	@param {String} [settings.flash_swf_url] URL of the Flash swf.
12335	@param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs.
12336	@param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event.
12337	@param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message.
12338	@param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload.
12339	@param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog.
12340	@param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess.
12341	@param {Object} [settings.resize] Enable resizng of images on client-side. Applies to `image/jpeg` and `image/png` only. `e.g. {width : 200, height : 200, quality : 90, crop: true}`
12342		@param {Number} [settings.resize.width] If image is bigger, it will be resized.
12343		@param {Number} [settings.resize.height] If image is bigger, it will be resized.
12344		@param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100).
12345		@param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally.
12346	@param {String} [settings.runtimes="html5,flash,silverlight,html4"] Comma separated list of runtimes, that Plupload will try in turn, moving to the next if previous fails.
12347	@param {String} [settings.silverlight_xap_url] URL of the Silverlight xap.
12348	@param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files.
12349	@param {Boolean} [settings.send_file_name=true] Whether to send file name as additional argument - 'name' (required for chunked uploads and some other cases where file name cannot be sent via normal ways).
12350*/
12351plupload.Uploader = function(options) {
12352	/**
12353	 * Fires when the current RunTime has been initialized.
12354	 *
12355	 * @event Init
12356	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12357	 */
12358
12359	/**
12360	 * Fires after the init event incase you need to perform actions there.
12361	 *
12362	 * @event PostInit
12363	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12364	 */
12365
12366	/**
12367	 * Fires when the option is changed in via uploader.setOption().
12368	 *
12369	 * @event OptionChanged
12370	 * @since 2.1
12371	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12372	 * @param {String} name Name of the option that was changed
12373	 * @param {Mixed} value New value for the specified option
12374	 * @param {Mixed} oldValue Previous value of the option
12375	 */
12376
12377	/**
12378	 * Fires when the silverlight/flash or other shim needs to move.
12379	 *
12380	 * @event Refresh
12381	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12382	 */
12383
12384	/**
12385	 * Fires when the overall state is being changed for the upload queue.
12386	 *
12387	 * @event StateChanged
12388	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12389	 */
12390
12391	/**
12392	 * Fires when browse_button is clicked and browse dialog shows.
12393	 *
12394	 * @event Browse
12395	 * @since 2.1.2
12396	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12397	 */	
12398
12399	/**
12400	 * Fires for every filtered file before it is added to the queue.
12401	 * 
12402	 * @event FileFiltered
12403	 * @since 2.1
12404	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12405	 * @param {plupload.File} file Another file that has to be added to the queue.
12406	 */
12407
12408	/**
12409	 * Fires when the file queue is changed. In other words when files are added/removed to the files array of the uploader instance.
12410	 *
12411	 * @event QueueChanged
12412	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12413	 */ 
12414
12415	/**
12416	 * Fires after files were filtered and added to the queue.
12417	 *
12418	 * @event FilesAdded
12419	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12420	 * @param {Array} files Array of file objects that were added to queue by the user.
12421	 */
12422
12423	/**
12424	 * Fires when file is removed from the queue.
12425	 *
12426	 * @event FilesRemoved
12427	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12428	 * @param {Array} files Array of files that got removed.
12429	 */
12430
12431	/**
12432	 * Fires when just before a file is uploaded. This event enables you to override settings
12433	 * on the uploader instance before the file is uploaded.
12434	 *
12435	 * @event BeforeUpload
12436	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12437	 * @param {plupload.File} file File to be uploaded.
12438	 */
12439
12440	/**
12441	 * Fires when a file is to be uploaded by the runtime.
12442	 *
12443	 * @event UploadFile
12444	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12445	 * @param {plupload.File} file File to be uploaded.
12446	 */
12447
12448	/**
12449	 * Fires while a file is being uploaded. Use this event to update the current file upload progress.
12450	 *
12451	 * @event UploadProgress
12452	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12453	 * @param {plupload.File} file File that is currently being uploaded.
12454	 */	
12455
12456	/**
12457	 * Fires when file chunk is uploaded.
12458	 *
12459	 * @event ChunkUploaded
12460	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12461	 * @param {plupload.File} file File that the chunk was uploaded for.
12462	 * @param {Object} response Object with response properties.
12463	 */
12464
12465	/**
12466	 * Fires when a file is successfully uploaded.
12467	 *
12468	 * @event FileUploaded
12469	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12470	 * @param {plupload.File} file File that was uploaded.
12471	 * @param {Object} response Object with response properties.
12472	 */
12473
12474	/**
12475	 * Fires when all files in a queue are uploaded.
12476	 *
12477	 * @event UploadComplete
12478	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12479	 * @param {Array} files Array of file objects that was added to queue/selected by the user.
12480	 */
12481
12482	/**
12483	 * Fires when a error occurs.
12484	 *
12485	 * @event Error
12486	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12487	 * @param {Object} error Contains code, message and sometimes file and other details.
12488	 */
12489
12490	/**
12491	 * Fires when destroy method is called.
12492	 *
12493	 * @event Destroy
12494	 * @param {plupload.Uploader} uploader Uploader instance sending the event.
12495	 */
12496	var uid = plupload.guid()
12497	, settings
12498	, files = []
12499	, preferred_caps = {}
12500	, fileInputs = []
12501	, fileDrops = []
12502	, startTime
12503	, total
12504	, disabled = false
12505	, xhr
12506	;
12507
12508
12509	// Private methods
12510	function uploadNext() {
12511		var file, count = 0, i;
12512
12513		if (this.state == plupload.STARTED) {
12514			// Find first QUEUED file
12515			for (i = 0; i < files.length; i++) {
12516				if (!file && files[i].status == plupload.QUEUED) {
12517					file = files[i];
12518					if (this.trigger("BeforeUpload", file)) {
12519						file.status = plupload.UPLOADING;
12520						this.trigger("UploadFile", file);
12521					}
12522				} else {
12523					count++;
12524				}
12525			}
12526
12527			// All files are DONE or FAILED
12528			if (count == files.length) {
12529				if (this.state !== plupload.STOPPED) {
12530					this.state = plupload.STOPPED;
12531					this.trigger("StateChanged");
12532				}
12533				this.trigger("UploadComplete", files);
12534			}
12535		}
12536	}
12537
12538
12539	function calcFile(file) {
12540		file.percent = file.size > 0 ? Math.ceil(file.loaded / file.size * 100) : 100;
12541		calc();
12542	}
12543
12544
12545	function calc() {
12546		var i, file;
12547
12548		// Reset stats
12549		total.reset();
12550
12551		// Check status, size, loaded etc on all files
12552		for (i = 0; i < files.length; i++) {
12553			file = files[i];
12554
12555			if (file.size !== undef) {
12556				// We calculate totals based on original file size
12557				total.size += file.origSize;
12558
12559				// Since we cannot predict file size after resize, we do opposite and
12560				// interpolate loaded amount to match magnitude of total
12561				total.loaded += file.loaded * file.origSize / file.size;
12562			} else {
12563				total.size = undef;
12564			}
12565
12566			if (file.status == plupload.DONE) {
12567				total.uploaded++;
12568			} else if (file.status == plupload.FAILED) {
12569				total.failed++;
12570			} else {
12571				total.queued++;
12572			}
12573		}
12574
12575		// If we couldn't calculate a total file size then use the number of files to calc percent
12576		if (total.size === undef) {
12577			total.percent = files.length > 0 ? Math.ceil(total.uploaded / files.length * 100) : 0;
12578		} else {
12579			total.bytesPerSec = Math.ceil(total.loaded / ((+new Date() - startTime || 1) / 1000.0));
12580			total.percent = total.size > 0 ? Math.ceil(total.loaded / total.size * 100) : 0;
12581		}
12582	}
12583
12584
12585	function getRUID() {
12586		var ctrl = fileInputs[0] || fileDrops[0];
12587		if (ctrl) {
12588			return ctrl.getRuntime().uid;
12589		}
12590		return false;
12591	}
12592
12593
12594	function runtimeCan(file, cap) {
12595		if (file.ruid) {
12596			var info = o.Runtime.getInfo(file.ruid);
12597			if (info) {
12598				return info.can(cap);
12599			}
12600		}
12601		return false;
12602	}
12603
12604
12605	function bindEventListeners() {
12606		this.bind('FilesAdded FilesRemoved', function(up) {
12607			up.trigger('QueueChanged');
12608			up.refresh();
12609		});
12610
12611		this.bind('CancelUpload', onCancelUpload);
12612		
12613		this.bind('BeforeUpload', onBeforeUpload);
12614
12615		this.bind('UploadFile', onUploadFile);
12616
12617		this.bind('UploadProgress', onUploadProgress);
12618
12619		this.bind('StateChanged', onStateChanged);
12620
12621		this.bind('QueueChanged', calc);
12622
12623		this.bind('Error', onError);
12624
12625		this.bind('FileUploaded', onFileUploaded);
12626
12627		this.bind('Destroy', onDestroy);
12628	}
12629
12630
12631	function initControls(settings, cb) {
12632		var self = this, inited = 0, queue = [];
12633
12634		// common settings
12635		var options = {
12636			runtime_order: settings.runtimes,
12637			required_caps: settings.required_features,
12638			preferred_caps: preferred_caps,
12639			swf_url: settings.flash_swf_url,
12640			xap_url: settings.silverlight_xap_url
12641		};
12642
12643		// add runtime specific options if any
12644		plupload.each(settings.runtimes.split(/\s*,\s*/), function(runtime) {
12645			if (settings[runtime]) {
12646				options[runtime] = settings[runtime];
12647			}
12648		});
12649
12650		// initialize file pickers - there can be many
12651		if (settings.browse_button) {
12652			plupload.each(settings.browse_button, function(el) {
12653				queue.push(function(cb) {
12654					var fileInput = new o.FileInput(plupload.extend({}, options, {
12655						accept: settings.filters.mime_types,
12656						name: settings.file_data_name,
12657						multiple: settings.multi_selection,
12658						container: settings.container,
12659						browse_button: el
12660					}));
12661
12662					fileInput.onready = function() {
12663						var info = o.Runtime.getInfo(this.ruid);
12664
12665						// for backward compatibility
12666						o.extend(self.features, {
12667							chunks: info.can('slice_blob'),
12668							multipart: info.can('send_multipart'),
12669							multi_selection: info.can('select_multiple')
12670						});
12671
12672						inited++;
12673						fileInputs.push(this);
12674						cb();
12675					};
12676
12677					fileInput.onchange = function() {
12678						self.addFile(this.files);
12679					};
12680
12681					fileInput.bind('mouseenter mouseleave mousedown mouseup', function(e) {
12682						if (!disabled) {
12683							if (settings.browse_button_hover) {
12684								if ('mouseenter' === e.type) {
12685									o.addClass(el, settings.browse_button_hover);
12686								} else if ('mouseleave' === e.type) {
12687									o.removeClass(el, settings.browse_button_hover);
12688								}
12689							}
12690
12691							if (settings.browse_button_active) {
12692								if ('mousedown' === e.type) {
12693									o.addClass(el, settings.browse_button_active);
12694								} else if ('mouseup' === e.type) {
12695									o.removeClass(el, settings.browse_button_active);
12696								}
12697							}
12698						}
12699					});
12700
12701					fileInput.bind('mousedown', function() {
12702						self.trigger('Browse');
12703					});
12704
12705					fileInput.bind('error runtimeerror', function() {
12706						fileInput = null;
12707						cb();
12708					});
12709
12710					fileInput.init();
12711				});
12712			});
12713		}
12714
12715		// initialize drop zones
12716		if (settings.drop_element) {
12717			plupload.each(settings.drop_element, function(el) {
12718				queue.push(function(cb) {
12719					var fileDrop = new o.FileDrop(plupload.extend({}, options, {
12720						drop_zone: el
12721					}));
12722
12723					fileDrop.onready = function() {
12724						var info = o.Runtime.getInfo(this.ruid);
12725
12726						self.features.dragdrop = info.can('drag_and_drop'); // for backward compatibility
12727
12728						inited++;
12729						fileDrops.push(this);
12730						cb();
12731					};
12732
12733					fileDrop.ondrop = function() {
12734						self.addFile(this.files);
12735					};
12736
12737					fileDrop.bind('error runtimeerror', function() {
12738						fileDrop = null;
12739						cb();
12740					});
12741
12742					fileDrop.init();
12743				});
12744			});
12745		}
12746
12747
12748		o.inSeries(queue, function() {
12749			if (typeof(cb) === 'function') {
12750				cb(inited);
12751			}
12752		});
12753	}
12754
12755
12756	function resizeImage(blob, params, cb) {
12757		var img = new o.Image();
12758
12759		try {
12760			img.onload = function() {
12761				// no manipulation required if...
12762				if (params.width > this.width &&
12763					params.height > this.height &&
12764					params.quality === undef &&
12765					params.preserve_headers &&
12766					!params.crop
12767				) {
12768					this.destroy();
12769					return cb(blob);
12770				}
12771				// otherwise downsize
12772				img.downsize(params.width, params.height, params.crop, params.preserve_headers);
12773			};
12774
12775			img.onresize = function() {
12776				cb(this.getAsBlob(blob.type, params.quality));
12777				this.destroy();
12778			};
12779
12780			img.onerror = function() {
12781				cb(blob);
12782			};
12783
12784			img.load(blob);
12785		} catch(ex) {
12786			cb(blob);
12787		}
12788	}
12789
12790
12791	function setOption(option, value, init) {
12792		var self = this, reinitRequired = false;
12793
12794		function _setOption(option, value, init) {
12795			var oldValue = settings[option];
12796
12797			switch (option) {
12798				case 'max_file_size':
12799					if (option === 'max_file_size') {
12800						settings.max_file_size = settings.filters.max_file_size = value;
12801					}
12802					break;
12803
12804				case 'chunk_size':
12805					if (value = plupload.parseSize(value)) {
12806						settings[option] = value;
12807						settings.send_file_name = true;
12808					}
12809					break;
12810
12811				case 'multipart':
12812					settings[option] = value;
12813					if (!value) {
12814						settings.send_file_name = true;
12815					}
12816					break;
12817
12818				case 'unique_names':
12819					settings[option] = value;
12820					if (value) {
12821						settings.send_file_name = true;
12822					}
12823					break;
12824
12825				case 'filters':
12826					// for sake of backward compatibility
12827					if (plupload.typeOf(value) === 'array') {
12828						value = {
12829							mime_types: value
12830						};
12831					}
12832
12833					if (init) {
12834						plupload.extend(settings.filters, value);
12835					} else {
12836						settings.filters = value;
12837					}
12838
12839					// if file format filters are being updated, regenerate the matching expressions
12840					if (value.mime_types) {
12841						settings.filters.mime_types.regexp = (function(filters) {
12842							var extensionsRegExp = [];
12843
12844							plupload.each(filters, function(filter) {
12845								plupload.each(filter.extensions.split(/,/), function(ext) {
12846									if (/^\s*\*\s*$/.test(ext)) {
12847										extensionsRegExp.push('\\.*');
12848									} else {
12849										extensionsRegExp.push('\\.' + ext.replace(new RegExp('[' + ('/^$.*+?|()[]{}\\'.replace(/./g, '\\$&')) + ']', 'g'), '\\$&'));
12850									}
12851								});
12852							});
12853
12854							return new RegExp('(' + extensionsRegExp.join('|') + ')$', 'i');
12855						}(settings.filters.mime_types));
12856					}
12857					break;
12858	
12859				case 'resize':
12860					if (init) {
12861						plupload.extend(settings.resize, value, {
12862							enabled: true
12863						});
12864					} else {
12865						settings.resize = value;
12866					}
12867					break;
12868
12869				case 'prevent_duplicates':
12870					settings.prevent_duplicates = settings.filters.prevent_duplicates = !!value;
12871					break;
12872
12873				case 'browse_button':
12874				case 'drop_element':
12875						value = plupload.get(value);
12876
12877				case 'container':
12878				case 'runtimes':
12879				case 'multi_selection':
12880				case 'flash_swf_url':
12881				case 'silverlight_xap_url':
12882					settings[option] = value;
12883					if (!init) {
12884						reinitRequired = true;
12885					}
12886					break;
12887
12888				default:
12889					settings[option] = value;
12890			}
12891
12892			if (!init) {
12893				self.trigger('OptionChanged', option, value, oldValue);
12894			}
12895		}
12896
12897		if (typeof(option) === 'object') {
12898			plupload.each(option, function(value, option) {
12899				_setOption(option, value, init);
12900			});
12901		} else {
12902			_setOption(option, value, init);
12903		}
12904
12905		if (init) {
12906			// Normalize the list of required capabilities
12907			settings.required_features = normalizeCaps(plupload.extend({}, settings));
12908
12909			// Come up with the list of capabilities that can affect default mode in a multi-mode runtimes
12910			preferred_caps = normalizeCaps(plupload.extend({}, settings, {
12911				required_features: true
12912			}));
12913		} else if (reinitRequired) {
12914			self.trigger('Destroy');
12915			
12916			initControls.call(self, settings, function(inited) {
12917				if (inited) {
12918					self.runtime = o.Runtime.getInfo(getRUID()).type;
12919					self.trigger('Init', { runtime: self.runtime });
12920					self.trigger('PostInit');
12921				} else {
12922					self.trigger('Error', {
12923						code : plupload.INIT_ERROR,
12924						message : plupload.translate('Init error.')
12925					});
12926				}
12927			});
12928		}
12929	}
12930
12931
12932	// Internal event handlers
12933	function onBeforeUpload(up, file) {
12934		// Generate unique target filenames
12935		if (up.settings.unique_names) {
12936			var matches = file.name.match(/\.([^.]+)$/), ext = "part";
12937			if (matches) {
12938				ext = matches[1];
12939			}
12940			file.target_name = file.id + '.' + ext;
12941		}
12942	}
12943
12944
12945	function onUploadFile(up, file) {
12946		var url = up.settings.url
12947		, chunkSize = up.settings.chunk_size
12948		, retries = up.settings.max_retries
12949		, features = up.features
12950		, offset = 0
12951		, blob
12952		;
12953
12954		// make sure we start at a predictable offset
12955		if (file.loaded) {
12956			offset = file.loaded = chunkSize ? chunkSize * Math.floor(file.loaded / chunkSize) : 0;
12957		}
12958
12959		function handleError() {
12960			if (retries-- > 0) {
12961				delay(uploadNextChunk, 1000);
12962			} else {
12963				file.loaded = offset; // reset all progress
12964
12965				up.trigger('Error', {
12966					code : plupload.HTTP_ERROR,
12967					message : plupload.translate('HTTP Error.'),
12968					file : file,
12969					response : xhr.responseText,
12970					status : xhr.status,
12971					responseHeaders: xhr.getAllResponseHeaders()
12972				});
12973			}
12974		}
12975
12976		function uploadNextChunk() {
12977			var chunkBlob, formData, args = {}, curChunkSize;
12978
12979			// make sure that file wasn't cancelled and upload is not stopped in general
12980			if (file.status !== plupload.UPLOADING || up.state === plupload.STOPPED) {
12981				return;
12982			}
12983
12984			// send additional 'name' parameter only if required
12985			if (up.settings.send_file_name) {
12986				args.name = file.target_name || file.name;
12987			}
12988
12989			if (chunkSize && features.chunks && blob.size > chunkSize) { // blob will be of type string if it was loaded in memory 
12990				curChunkSize = Math.min(chunkSize, blob.size - offset);
12991				chunkBlob = blob.slice(offset, offset + curChunkSize);
12992			} else {
12993				curChunkSize = blob.size;
12994				chunkBlob = blob;
12995			}
12996
12997			// If chunking is enabled add corresponding args, no matter if file is bigger than chunk or smaller
12998			if (chunkSize && features.chunks) {
12999				// Setup query string arguments
13000				if (up.settings.send_chunk_number) {
13001					args.chunk = Math.ceil(offset / chunkSize);
13002					args.chunks = Math.ceil(blob.size / chunkSize);
13003				}
13003 else { // keep support for experimental chunk format, just in case
13004					args.offset = offset;
13005					args.total = blob.size;
13006				}
13007			}
13008
13009			xhr = new o.XMLHttpRequest();
13010
13011			// Do we have upload progress support
13012			if (xhr.upload) {
13013				xhr.upload.onprogress = function(e) {
13014					file.loaded = Math.min(file.size, offset + e.loaded);
13015					up.trigger('UploadProgress', file);
13016				};
13017			}
13018
13019			xhr.onload = function() {
13020				// check if upload made itself through
13021				if (xhr.status >= 400) {
13022					handleError();
13023					return;
13024				}
13025
13026				retries = up.settings.max_retries; // reset the counter
13027
13028				// Handle chunk response
13029				if (curChunkSize < blob.size) {
13030					chunkBlob.destroy();
13031
13032					offset += curChunkSize;
13033					file.loaded = Math.min(offset, blob.size);
13034
13035					up.trigger('ChunkUploaded', file, {
13036						offset : file.loaded,
13037						total : blob.size,
13038						response : xhr.responseText,
13039						status : xhr.status,
13040						responseHeaders: xhr.getAllResponseHeaders()
13041					});
13042
13043					// stock Android browser doesn't fire upload progress events, but in chunking mode we can fake them
13044					if (o.Env.browser === 'Android Browser') {
13045						// doesn't harm in general, but is not required anywhere else
13046						up.trigger('UploadProgress', file);
13047					} 
13048				} else {
13049					file.loaded = file.size;
13050				}
13051
13052				chunkBlob = formData = null; // Free memory
13053
13054				// Check if file is uploaded
13055				if (!offset || offset >= blob.size) {
13056					// If file was modified, destory the copy
13057					if (file.size != file.origSize) {
13058						blob.destroy();
13059						blob = null;
13060					}
13061
13062					up.trigger('UploadProgress', file);
13063
13064					file.status = plupload.DONE;
13065
13066					up.trigger('FileUploaded', file, {
13067						response : xhr.responseText,
13068						status : xhr.status,
13069						responseHeaders: xhr.getAllResponseHeaders()
13070					});
13071				} else {
13072					// Still chunks left
13073					delay(uploadNextChunk, 1); // run detached, otherwise event handlers interfere
13074				}
13075			};
13076
13077			xhr.onerror = function() {
13078				handleError();
13079			};
13080
13081			xhr.onloadend = function() {
13082				this.destroy();
13083				xhr = null;
13084			};
13085
13086			// Build multipart request
13087			if (up.settings.multipart && features.multipart) {
13088				xhr.open("post", url, true);
13089
13090				// Set custom headers
13091				plupload.each(up.settings.headers, function(value, name) {
13092					xhr.setRequestHeader(name, value);
13093				});
13094
13095				formData = new o.FormData();
13096
13097				// Add multipart params
13098				plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
13099					formData.append(name, value);
13100				});
13101
13102				// Add file and send it
13103				formData.append(up.settings.file_data_name, chunkBlob);
13104				xhr.send(formData, {
13105					runtime_order: up.settings.runtimes,
13106					required_caps: up.settings.required_features,
13107					preferred_caps: preferred_caps,
13108					swf_url: up.settings.flash_swf_url,
13109					xap_url: up.settings.silverlight_xap_url
13110				});
13111			} else {
13112				// if no multipart, send as binary stream
13113				url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params));
13114
13115				xhr.open("post", url, true);
13116
13117				xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header
13118
13119				// Set custom headers
13120				plupload.each(up.settings.headers, function(value, name) {
13121					xhr.setRequestHeader(name, value);
13122				});
13123
13124				xhr.send(chunkBlob, {
13125					runtime_order: up.settings.runtimes,
13126					required_caps: up.settings.required_features,
13127					preferred_caps: preferred_caps,
13128					swf_url: up.settings.flash_swf_url,
13129					xap_url: up.settings.silverlight_xap_url
13130				});
13131			}
13132		}
13133
13134		blob = file.getSource();
13135
13136		// Start uploading chunks
13137		if (up.settings.resize.enabled && runtimeCan(blob, 'send_binary_string') && !!~o.inArray(blob.type, ['image/jpeg', 'image/png'])) {
13138			// Resize if required
13139			resizeImage.call(this, blob, up.settings.resize, function(resizedBlob) {
13140				blob = resizedBlob;
13141				file.size = resizedBlob.size;
13142				uploadNextChunk();
13143			});
13144		} else {
13145			uploadNextChunk();
13146		}
13147	}
13148
13149
13150	function onUploadProgress(up, file) {
13151		calcFile(file);
13152	}
13153
13154
13155	function onStateChanged(up) {
13156		if (up.state == plupload.STARTED) {
13157			// Get start time to calculate bps
13158			startTime = (+new Date());
13159		} else if (up.state == plupload.STOPPED) {
13160			// Reset currently uploading files
13161			for (var i = up.files.length - 1; i >= 0; i--) {
13162				if (up.files[i].status == plupload.UPLOADING) {
13163					up.files[i].status = plupload.QUEUED;
13164					calc();
13165				}
13166			}
13167		}
13168	}
13169
13170
13171	function onCancelUpload() {
13172		if (xhr) {
13173			xhr.abort();
13174		}
13175	}
13176
13177
13178	function onFileUploaded(up) {
13179		calc();
13180
13181		// Upload next file but detach it from the error event
13182		// since other custom listeners might want to stop the queue
13183		delay(function() {
13184			uploadNext.call(up);
13185		}, 1);
13186	}
13187
13188
13189	function onError(up, err) {
13190		if (err.code === plupload.INIT_ERROR) {
13191			up.destroy();
13192		}
13193		// Set failed status if an error occured on a file
13194		else if (err.file) {
13195			err.file.status = plupload.FAILED;
13196			calcFile(err.file);
13197
13198			// Upload next file but detach it from the error event
13199			// since other custom listeners might want to stop the queue
13200			if (up.state == plupload.STARTED) { // upload in progress
13201				up.trigger('CancelUpload');
13202				delay(function() {
13203					uploadNext.call(up);
13204				}, 1);
13205			}
13206		}
13207	}
13208
13209
13210	function onDestroy(up) {
13211		up.stop();
13212
13213		// Purge the queue
13214		plupload.each(files, function(file) {
13215			file.destroy();
13216		});
13217		files = [];
13218
13219		if (fileInputs.length) {
13220			plupload.each(fileInputs, function(fileInput) {
13221				fileInput.destroy();
13222			});
13223			fileInputs = [];
13224		}
13225
13226		if (fileDrops.length) {
13227			plupload.each(fileDrops, function(fileDrop) {
13228				fileDrop.destroy();
13229			});
13230			fileDrops = [];
13231		}
13232
13233		preferred_caps = {};
13234		disabled = false;
13235		startTime = xhr = null;
13236		total.reset();
13237	}
13238
13239
13240	// Default settings
13241	settings = {
13242		runtimes: o.Runtime.order,
13243		max_retries: 0,
13244		chunk_size: 0,
13245		multipart: true,
13246		multi_selection: true,
13247		file_data_name: 'file',
13248		flash_swf_url: 'js/Moxie.swf',
13249		silverlight_xap_url: 'js/Moxie.xap',
13250		filters: {
13251			mime_types: [],
13252			prevent_duplicates: false,
13253			max_file_size: 0
13254		},
13255		resize: {
13256			enabled: false,
13257			preserve_headers: true,
13258			crop: false
13259		},
13260		send_file_name: true,
13261		send_chunk_number: true
13262	};
13263
13264	
13265	setOption.call(this, options, null, true);
13266
13267	// Inital total state
13268	total = new plupload.QueueProgress(); 
13269
13270	// Add public methods
13271	plupload.extend(this, {
13272
13273		/**
13274		 * Unique id for the Uploader instance.
13275		 *
13276		 * @property id
13277		 * @type String
13278		 */
13279		id : uid,
13280		uid : uid, // mOxie uses this to differentiate between event targets
13281
13282		/**
13283		 * Current state of the total uploading progress. This one can either be plupload.STARTED or plupload.STOPPED.
13284		 * These states are controlled by the stop/start methods. The default value is STOPPED.
13285		 *
13286		 * @property state
13287		 * @type Number
13288		 */
13289		state : plupload.STOPPED,
13290
13291		/**
13292		 * Map of features that are available for the uploader runtime. Features will be filled
13293		 * before the init event is called, these features can then be used to alter the UI for the end user.
13294		 * Some of the current features that might be in this map is: dragdrop, chunks, jpgresize, pngresize.
13295		 *
13296		 * @property features
13297		 * @type Object
13298		 */
13299		features : {},
13300
13301		/**
13302		 * Current runtime name.
13303		 *
13304		 * @property runtime
13305		 * @type String
13306		 */
13307		runtime : null,
13308
13309		/**
13310		 * Current upload queue, an array of File instances.
13311		 *
13312		 * @property files
13313		 * @type Array
13314		 * @see plupload.File
13315		 */
13316		files : files,
13317
13318		/**
13319		 * Object with name/value settings.
13320		 *
13321		 * @property settings
13322		 * @type Object
13323		 */
13324		settings : settings,
13325
13326		/**
13327		 * Total progess information. How many files has been uploaded, total percent etc.
13328		 *
13329		 * @property total
13330		 * @type plupload.QueueProgress
13331		 */
13332		total : total,
13333
13334
13335		/**
13336		 * Initializes the Uploader instance and adds internal event listeners.
13337		 *
13338		 * @method init
13339		 */
13340		init : function() {
13341			var self = this;
13342
13343			if (typeof(settings.preinit) == "function") {
13344				settings.preinit(self);
13345			} else {
13346				plupload.each(settings.preinit, function(func, name) {
13347					self.bind(name, func);
13348				});
13349			}
13350
13351			bindEventListeners.call(this);
13352
13353			// Check for required options
13354			if (!settings.browse_button || !settings.url) {
13355				this.trigger('Error', {
13356					code : plupload.INIT_ERROR,
13357					message : plupload.translate('Init error.')
13358				});
13359				return;
13360			}
13361
13362			initControls.call(this, settings, function(inited) {
13363				if (typeof(settings.init) == "function") {
13364					settings.init(self);
13365				} else {
13366					plupload.each(settings.init, function(func, name) {
13367						self.bind(name, func);
13368					});
13369				}
13370
13371				if (inited) {
13372					self.runtime = o.Runtime.getInfo(getRUID()).type;
13373					self.trigger('Init', { runtime: self.runtime });
13374					self.trigger('PostInit');
13375				} else {
13376					self.trigger('Error', {
13377						code : plupload.INIT_ERROR,
13378						message : plupload.translate('Init error.')
13379					});
13380				}
13381			});
13382		},
13383
13384		/**
13385		 * Set the value for the specified option(s).
13386		 *
13387		 * @method setOption
13388		 * @since 2.1
13389		 * @param {String|Object} option Name of the option to change or the set of key/value pairs
13390		 * @param {Mixed} [value] Value for the option (is ignored, if first argument is object)
13391		 */
13392		setOption: function(option, value) {
13393			setOption.call(this, option, value, !this.runtime); // until runtime not set we do not need to reinitialize
13394		},
13395
13396		/**
13397		 * Get the value for the specified option or the whole configuration, if not specified.
13398		 * 
13399		 * @method getOption
13400		 * @since 2.1
13401		 * @param {String} [option] Name of the option to get
13402		 * @return {Mixed} Value for the option or the whole set
13403		 */
13404		getOption: function(option) {
13405			if (!option) {
13406				return settings;
13407			}
13408			return settings[option];
13409		},
13410
13411		/**
13412		 * Refreshes the upload instance by dispatching out a refresh event to all runtimes.
13413		 * This would for example reposition flash/silverlight shims on the page.
13414		 *
13415		 * @method refresh
13416		 */
13417		refresh : function() {
13418			if (fileInputs.length) {
13419				plupload.each(fileInputs, function(fileInput) {
13420					fileInput.trigger('Refresh');
13421				});
13422			}
13423			this.trigger('Refresh');
13424		},
13425
13426		/**
13427		 * Starts uploading the queued files.
13428		 *
13429		 * @method start
13430		 */
13431		start : function() {
13432			if (this.state != plupload.STARTED) {
13433				this.state = plupload.STARTED;
13434				this.trigger('StateChanged');
13435
13436				uploadNext.call(this);
13437			}
13438		},
13439
13440		/**
13441		 * Stops the upload of the queued files.
13442		 *
13443		 * @method stop
13444		 */
13445		stop : function() {
13446			if (this.state != plupload.STOPPED) {
13447				this.state = plupload.STOPPED;
13448				this.trigger('StateChanged');
13449				this.trigger('CancelUpload');
13450			}
13451		},
13452
13453
13454		/**
13455		 * Disables/enables browse button on request.
13456		 *
13457		 * @method disableBrowse
13458		 * @param {Boolean} disable Whether to disable or enable (default: true)
13459		 */
13460		disableBrowse : function() {
13461			disabled = arguments[0] !== undef ? arguments[0] : true;
13462
13463			if (fileInputs.length) {
13464				plupload.each(fileInputs, function(fileInput) {
13465					fileInput.disable(disabled);
13466				});
13467			}
13468
13469			this.trigger('DisableBrowse', disabled);
13470		},
13471
13472		/**
13473		 * Returns the specified file object by id.
13474		 *
13475		 * @method getFile
13476		 * @param {String} id File id to look for.
13477		 * @return {plupload.File} File object or undefined if it wasn't found;
13478		 */
13479		getFile : function(id) {
13480			var i;
13481			for (i = files.length - 1; i >= 0; i--) {
13482				if (files[i].id === id) {
13483					return files[i];
13484				}
13485			}
13486		},
13487
13488		/**
13489		 * Adds file to the queue programmatically. Can be native file, instance of Plupload.File,
13490		 * instance of mOxie.File, input[type="file"] element, or array of these. Fires FilesAdded, 
13491		 * if any files were added to the queue. Otherwise nothing happens.
13492		 *
13493		 * @method addFile
13494		 * @since 2.0
13495		 * @param {plupload.File|mOxie.File|File|Node|Array} file File or files to add to the queue.
13496		 * @param {String} [fileName] If specified, will be used as a name for the file
13497		 */
13498		addFile : function(file, fileName) {
13499			var self = this
13500			, queue = [] 
13501			, filesAdded = []
13502			, ruid
13503			;
13504
13505			function filterFile(file, cb) {
13506				var queue = [];
13507				o.each(self.settings.filters, function(rule, name) {
13508					if (fileFilters[name]) {
13509						queue.push(function(cb) {
13510							fileFilters[name].call(self, rule, file, function(res) {
13511								cb(!res);
13512							});
13513						});
13514					}
13515				});
13516				o.inSeries(queue, cb);
13517			}
13518
13519			/**
13520			 * @method resolveFile
13521			 * @private
13522			 * @param {o.File|o.Blob|plupload.File|File|Blob|input[type="file"]} file
13523			 */
13524			function resolveFile(file) {
13525				var type = o.typeOf(file);
13526
13527				// o.File
13528				if (file instanceof o.File) { 
13529					if (!file.ruid && !file.isDetached()) {
13530						if (!ruid) { // weird case
13531							return false;
13532						}
13533						file.ruid = ruid;
13534						file.connectRuntime(ruid);
13535					}
13536					resolveFile(new plupload.File(file));
13537				}
13538				// o.Blob 
13539				else if (file instanceof o.Blob) {
13540					resolveFile(file.getSource());
13541					file.destroy();
13542				} 
13543				// plupload.File - final step for other branches
13544				else if (file instanceof plupload.File) {
13545					if (fileName) {
13546						file.name = fileName;
13547					}
13548					
13549					queue.push(function(cb) {
13550						// run through the internal and user-defined filters, if any
13551						filterFile(file, function(err) {
13552							if (!err) {
13553								// make files available for the filters by updating the main queue directly
13554								files.push(file);
13555								// collect the files that will be passed to FilesAdded event
13556								filesAdded.push(file); 
13557
13558								self.trigger("FileFiltered", file);
13559							}
13560							delay(cb, 1); // do not build up recursions or eventually we might hit the limits
13561						});
13562					});
13563				} 
13564				// native File or blob
13565				else if (o.inArray(type, ['file', 'blob']) !== -1) {
13566					resolveFile(new o.File(null, file));
13567				} 
13568				// input[type="file"]
13569				else if (type === 'node' && o.typeOf(file.files) === 'filelist') {
13570					// if we are dealing with input[type="file"]
13571					o.each(file.files, resolveFile);
13572				} 
13573				// mixed array of any supported types (see above)
13574				else if (type === 'array') {
13575					fileName = null; // should never happen, but unset anyway to avoid funny situations
13576					o.each(file, resolveFile);
13577				}
13578			}
13579
13580			ruid = getRUID();
13581			
13582			resolveFile(file);
13583
13584			if (queue.length) {
13585				o.inSeries(queue, function() {
13586					// if any files left after filtration, trigger FilesAdded
13587					if (filesAdded.length) {
13588						self.trigger("FilesAdded", filesAdded);
13589					}
13590				});
13591			}
13592		},
13593
13594		/**
13595		 * Removes a specific file.
13596		 *
13597		 * @method removeFile
13598		 * @param {plupload.File|String} file File to remove from queue.
13599		 */
13600		removeFile : function(file) {
13601			var id = typeof(file) === 'string' ? file : file.id;
13602
13603			for (var i = files.length - 1; i >= 0; i--) {
13604				if (files[i].id === id) {
13605					return this.splice(i, 1)[0];
13606				}
13607			}
13608		},
13609
13610		/**
13611		 * Removes part of the queue and returns the files removed. This will also trigger the FilesRemoved and QueueChanged events.
13612		 *
13613		 * @method splice
13614		 * @param {Number} start (Optional) Start index to remove from.
13615		 * @param {Number} length (Optional) Lengh of items to remove.
13616		 * @return {Array} Array of files that was removed.
13617		 */
13618		splice : function(start, length) {
13619			// Splice and trigger events
13620			var removed = files.splice(start === undef ? 0 : start, length === undef ? files.length : length);
13621
13622			// if upload is in progress we need to stop it and restart after files are removed
13623			var restartRequired = false;
13624			if (this.state == plupload.STARTED) { // upload in progress
13625				plupload.each(removed, function(file) {
13626					if (file.status === plupload.UPLOADING) {
13627						restartRequired = true; // do not restart, unless file that is being removed is uploading
13628						return false;
13629					}
13630				});
13631				
13632				if (restartRequired) {
13633					this.stop();
13634				}
13635			}
13636
13637			this.trigger("FilesRemoved", removed);
13638
13639			// Dispose any resources allocated by those files
13640			plupload.each(removed, function(file) {
13641				file.destroy();
13642			});
13643			
13644			if (restartRequired) {
13645				this.start();
13646			}
13647
13648			return removed;
13649		},
13650
13651		/**
13652		 * Dispatches the specified event name and it's arguments to all listeners.
13653		 *
13654		 *
13655		 * @method trigger
13656		 * @param {String} name Event name to fire.
13657		 * @param {Object..} Multiple arguments to pass along to the listener functions.
13658		 */
13659
13660		/**
13661		 * Check whether uploader has any listeners to the specified event.
13662		 *
13663		 * @method hasEventListener
13664		 * @param {String} name Event name to check for.
13665		 */
13666
13667
13668		/**
13669		 * Adds an event listener by name.
13670		 *
13671		 * @method bind
13672		 * @param {String} name Event name to listen for.
13673		 * @param {function}
13673 func Function to call ones the event gets fired.
13674		 * @param {Object} scope Optional scope to execute the specified function in.
13675		 */
13676		bind : function(name, func, scope) {
13677			var self = this;
13678			// adapt moxie EventTarget style to Plupload-like
13679			plupload.Uploader.prototype.bind.call(this, name, function() {
13680				var args = [].slice.call(arguments);
13681				args.splice(0, 1, self); // replace event object with uploader instance
13682				return func.apply(this, args);
13683			}, 0, scope);
13684		},
13685
13686		/**
13687		 * Removes the specified event listener.
13688		 *
13689		 * @method unbind
13690		 * @param {String} name Name of event to remove.
13691		 * @param {function} func Function to remove from listener.
13692		 */
13693
13694		/**
13695		 * Removes all event listeners.
13696		 *
13697		 * @method unbindAll
13698		 */
13699
13700
13701		/**
13702		 * Destroys Plupload instance and cleans after itself.
13703		 *
13704		 * @method destroy
13705		 */
13706		destroy : function() {
13707			this.trigger('Destroy');
13708			settings = total = null; // purge these exclusively
13709			this.unbindAll();
13710		}
13711	});
13712};
13713
13714plupload.Uploader.prototype = o.EventTarget.instance;
13715
13716/**
13717 * Constructs a new file instance.
13718 *
13719 * @class File
13720 * @constructor
13721 * 
13722 * @param {Object} file Object containing file properties
13723 * @param {String} file.name Name of the file.
13724 * @param {Number} file.size File size.
13725 */
13726plupload.File = (function() {
13727	var filepool = {};
13728
13729	function PluploadFile(file) {
13730
13731		plupload.extend(this, {
13732
13733			/**
13734			 * File id this is a globally unique id for the specific file.
13735			 *
13736			 * @property id
13737			 * @type String
13738			 */
13739			id: plupload.guid(),
13740
13741			/**
13742			 * File name for example "myfile.gif".
13743			 *
13744			 * @property name
13745			 * @type String
13746			 */
13747			name: file.name || file.fileName,
13748
13749			/**
13750			 * File type, `e.g image/jpeg`
13751			 *
13752			 * @property type
13753			 * @type String
13754			 */
13755			type: file.type || '',
13756
13757			/**
13758			 * File size in bytes (may change after client-side manupilation).
13759			 *
13760			 * @property size
13761			 * @type Number
13762			 */
13763			size: file.size || file.fileSize,
13764
13765			/**
13766			 * Original file size in bytes.
13767			 *
13768			 * @property origSize
13769			 * @type Number
13770			 */
13771			origSize: file.size || file.fileSize,
13772
13773			/**
13774			 * Number of bytes uploaded of the files total size.
13775			 *
13776			 * @property loaded
13777			 * @type Number
13778			 */
13779			loaded: 0,
13780
13781			/**
13782			 * Number of percentage uploaded of the file.
13783			 *
13784			 * @property percent
13785			 * @type Number
13786			 */
13787			percent: 0,
13788
13789			/**
13790			 * Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE.
13791			 *
13792			 * @property status
13793			 * @type Number
13794			 * @see plupload
13795			 */
13796			status: plupload.QUEUED,
13797
13798			/**
13799			 * Date of last modification.
13800			 *
13801			 * @property lastModifiedDate
13802			 * @type {String}
13803			 */
13804			lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString(), // Thu Aug 23 2012 19:40:00 GMT+0400 (GET)
13805
13806			/**
13807			 * Returns native window.File object, when it's available.
13808			 *
13809			 * @method getNative
13810			 * @return {window.File} or null, if plupload.File is of different origin
13811			 */
13812			getNative: function() {
13813				var file = this.getSource().getSource();
13814				return o.inArray(o.typeOf(file), ['blob', 'file']) !== -1 ? file : null;
13815			},
13816
13817			/**
13818			 * Returns mOxie.File - unified wrapper object that can be used across runtimes.
13819			 *
13820			 * @method getSource
13821			 * @return {mOxie.File} or null
13822			 */
13823			getSource: function() {
13824				if (!filepool[this.id]) {
13825					return null;
13826				}
13827				return filepool[this.id];
13828			},
13829
13830			/**
13831			 * Destroys plupload.File object.
13832			 *
13833			 * @method destroy
13834			 */
13835			destroy: function() {
13836				var src = this.getSource();
13837				if (src) {
13838					src.destroy();
13839					delete filepool[this.id];
13840				}
13841			}
13842		});
13843
13844		filepool[this.id] = file;
13845	}
13846
13847	return PluploadFile;
13848}());
13849
13850
13851/**
13852 * Constructs a queue progress.
13853 *
13854 * @class QueueProgress
13855 * @constructor
13856 */
13857 plupload.QueueProgress = function() {
13858	var self = this; // Setup alias for self to reduce code size when it's compressed
13859
13860	/**
13861	 * Total queue file size.
13862	 *
13863	 * @property size
13864	 * @type Number
13865	 */
13866	self.size = 0;
13867
13868	/**
13869	 * Total bytes uploaded.
13870	 *
13871	 * @property loaded
13872	 * @type Number
13873	 */
13874	self.loaded = 0;
13875
13876	/**
13877	 * Number of files uploaded.
13878	 *
13879	 * @property uploaded
13880	 * @type Number
13881	 */
13882	self.uploaded = 0;
13883
13884	/**
13885	 * Number of files failed to upload.
13886	 *
13887	 * @property failed
13888	 * @type Number
13889	 */
13890	self.failed = 0;
13891
13892	/**
13893	 * Number of files yet to be uploaded.
13894	 *
13895	 * @property queued
13896	 * @type Number
13897	 */
13898	self.queued = 0;
13899
13900	/**
13901	 * Total percent of the uploaded bytes.
13902	 *
13903	 * @property percent
13904	 * @type Number
13905	 */
13906	self.percent = 0;
13907
13908	/**
13909	 * Bytes uploaded per second.
13910	 *
13911	 * @property bytesPerSec
13912	 * @type Number
13913	 */
13914	self.bytesPerSec = 0;
13915
13916	/**
13917	 * Resets the progress to it's initial values.
13918	 *
13919	 * @method reset
13920	 */
13921	self.reset = function() {
13922		self.size = self.loaded = self.uploaded = self.failed = self.queued = self.percent = self.bytesPerSec = 0;
13923	};
13924};
13925
13926window.plupload = plupload;
13927
13928}(window, mOxie));
13929
13930/**
13931 * jquery.plupload.queue.js
13932 *
13933 * Copyright 2009, Moxiecode Systems AB
13934 * Released under GPL License.
13935 *
13936 * License: http://www.plupload.com/license
13937 * Contributing: http://www.plupload.com/contributing
13938 */
13939
13940/* global jQuery:true, alert:true */
13941
13942/**
13943jQuery based implementation of the Plupload API - multi-runtime file uploading API.
13944
13945To use the widget you must include _jQuery_. It is not meant to be extended in any way and is provided to be
13946used as it is.
13947
13948@example
13949	<!-- Instantiating: -->
13950	<div id="uploader">
13951		<p>Your browser doesn't have Flash, Silverlight or HTML5 support.</p>
13952	</div>
13953
13954	<script>
13955		$('#uploader').pluploadQueue({
13956			url : '../upload.php',
13957			filters : [
13958				{title : "Image files", extensions : "jpg,gif,png"}
13959			],
13960			rename: true,
13961			flash_swf_url : '../../js/Moxie.swf',
13962			silverlight_xap_url : '../../js/Moxie.xap',
13963		});
13964	</script>
13965
13966@example
13967	// Retrieving a reference to plupload.Uploader object
13968	var uploader = $('#uploader').pluploadQueue();
13969
13970	uploader.bind('FilesAdded', function() {
13971		
13972		// Autostart
13973		setTimeout(uploader.start, 1); // "detach" from the main thread
13974	});
13975
13976@class pluploadQueue
13977@constructor
13978@param {Object} settings For detailed information about each option check documentation.
13979	@param {String} settings.url URL of the server-side upload handler.
13980	@param {Number|String} [settings.chunk_size=0] Chunk size in bytes to slice the file into. Shorcuts with b, kb, mb, gb, tb suffixes also supported. `e.g. 204800 or "204800b" or "200kb"`. By default - disabled.
13981	@param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message.
13982	@param {Array} [settings.filters=[]] Set of file type filters, each one defined by hash of title and extensions. `e.g. {title : "Image files", extensions : "jpg,jpeg,gif,png"}`. Dispatches `plupload.FILE_EXTENSION_ERROR`
13983	@param {String} [settings.flash_swf_url] URL of the Flash swf.
13984	@param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs.
13985	@param {Number|String}
13985 [settings.max_file_size] Maximum file size that the user can pick, in bytes. Optionally supports b, kb, mb, gb, tb suffixes. `e.g. "10mb" or "1gb"`. By default - not set. Dispatches `plupload.FILE_SIZE_ERROR`.
13986	@param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event.
13987	@param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message.
13988	@param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload.
13989	@param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog.
13990	@param {Boolean} [settings.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`.
13991	@param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess.
13992	@param {Object} [settings.resize] Enable resizng of images on client-side. Applies to `image/jpeg` and `image/png` only. `e.g. {width : 200, height : 200, quality : 90, crop: true}`
13993		@param {Number} [settings.resize.width] If image is bigger, it will be resized.
13994		@param {Number} [settings.resize.height] If image is bigger, it will be resized.
13995		@param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100).
13996		@param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally.
13997	@param {String} [settings.runtimes="html5,flash,silverlight,html4"] Comma separated list of runtimes, that Plupload will try in turn, moving to the next if previous fails.
13998	@param {String} [settings.silverlight_xap_url] URL of the Silverlight xap.
13999	@param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files.
14000
14001	@param {Boolean} [settings.dragdrop=true] Enable ability to add file to the queue by drag'n'dropping them from the desktop.
14002	@param {Boolean} [settings.rename=false] Enable ability to rename files in the queue.
14003	@param {Boolean} [settings.multiple_queues=true] Re-activate the widget after each upload procedure.
14004*/
14005;(function($, o) {
14006	var uploaders = {};
14007
14008	function _(str) {
14009		return plupload.translate(str) || str;
14010	}
14011
14012	function renderUI(id, target, hasUploadBtn = false) {
14013		// Remove all existing non plupload items
14014		target.contents().each(function(i, node) {
14015			node = $(node);
14016
14017			if (!node.is('.plupload')) {
14018				node.remove();
14019			}
14020		});
14021
14022		let uploadBtn = '';
14023		if(hasUploadBtn){
14024			uploadBtn =  '<a href="#" class="plupload_button plupload_start">' + _('Start Upload') + '</a>';
14025		}
14026
14027		target.prepend(
14028			'<div class="plupload_wrapper plupload_scroll">' +
14029				'<div id="' + id + '_container" class="plupload_container">' +
14030					'<div class="plupload">' +
14031						//'<div class="plupload_header">' +
14032						//	'<div class="plupload_header_content">' +
14033						//		'<div class="plupload_header_title">' + _('Select files') + '</div>' +
14034						//		'<div class="plupload_header_text">' + _('Add files to the upload queue and click the start button.') + '</div>' +
14035						//	'</div>' +
14036						//'</div>' +
14037
14038						'<div class="plupload_content">' +
14039							'<div class="plupload_filelist_header">' +
14040								'<div class="plupload_file_name">' + _('Filename') + '</div>' +
14041								'<div class="plupload_file_action">&nbsp;</div>' +
14042								'<div class="plupload_file_status"><span>' + _('Status') + '</span></div>' +
14043								'<div class="plupload_file_size">' + _('Size') + '</div>' +
14044								'<div class="plupload_clearer">&nbsp;</div>' +
14045							'</div>' +
14046
14047							'<ul id="' + id + '_filelist" class="plupload_filelist"></ul>' +
14048
14049							'<div class="plupload_filelist_footer">' +
14050								'<div class="plupload_file_name">' +
14051									'<div class="plupload_buttons">' +
14052									'<a href="#" class="plupload_button plupload_add" id="' + id + '_browse">' + _('Add Files') + '</a>'+
14053										uploadBtn +
14054									'</div>' +
14055									'<span class="plupload_upload_status"></span>' +
14056								'</div>' +
14057								'<div class="plupload_file_action"></div>' +
14058								'<div class="plupload_file_status"><span class="plupload_total_status">0%</span></div>' +
14059								'<div class="plupload_file_size"><span class="plupload_total_file_size">0 b</span></div>' +
14060								'<div class="plupload_progress">' +
14061									'<div class="plupload_progress_container">' +
14062										'<div class="plupload_progress_bar"></div>' +
14063									'</div>' +
14064								'</div>' +
14065								'<div class="plupload_clearer">&nbsp;</div>' +
14066							'</div>' +
14067						'</div>' +
14068					'</div>' +
14069				'</div>' +
14070				'<input type="hidden" id="' + id + '_count" name="' + id + '_count" value="
140700" />' +
14071			'</div>'
14072		);
14073	}
14074
14075	$.fn.pluploadQueue = function (settings) {
14076		if (settings) {
14077			this.each(function() {
14078				var uploader, target, id, contents_bak;
14079
14080				target = $(this);
14081				id = target.attr('id');
14082
14083				if (!id) {
14084					id = plupload.guid();
14085					target.attr('id', id);
14086				}
14087
14088				contents_bak = target.html();
14089
14090				if (!settings.buttons) {
14091                    settings.buttons = {};
14092                    settings.buttons.upload = false;
14093                }
14094
14095				renderUI(id, target, settings.buttons.upload);
14096
14097				settings = $.extend({
14098					dragdrop : true,
14099					browse_button : id + '_browse',
14100					container : id
14101				}, settings);
14102
14103				// Enable drag/drop (see PostInit handler as well)
14104				if (settings.dragdrop) {
14105					settings.drop_element = id + '_filelist';
14106				}
14107
14108				uploader = new plupload.Uploader(settings);
14109
14110				uploaders[id] = uploader;
14111
14112				function handleStatus(file) {
14113					var actionClass;
14114
14115					if (file.status == plupload.DONE) {
14116						actionClass = 'plupload_done';
14117					}
14118
14119					if (file.status == plupload.FAILED) {
14120						actionClass = 'plupload_failed';
14121					}
14122
14123					if (file.status == plupload.QUEUED) {
14124						actionClass = 'plupload_delete';
14125					}
14126
14127					if (file.status == plupload.UPLOADING) {
14128						actionClass = 'plupload_uploading';
14129					}
14130
14131					var icon = $('#' + file.id).attr('class', actionClass).find('a').css('display', 'block');
14132					if (file.hint) {
14133						icon.attr('title', file.hint);	
14134					}
14135				}
14136
14137				function updateTotalProgress() {
14138					$('span.plupload_total_status', target).html(uploader.total.percent + '%');
14139					$('div.plupload_progress_bar', target).css('width', uploader.total.percent + '%');
14140					$('span.plupload_upload_status', target).html(
14141						o.sprintf(_('Uploaded %d/%d files'), uploader.total.uploaded, uploader.files.length)
14142					);
14143				}
14144
14145				function updateList() {
14146					var fileList = $('ul.plupload_filelist', target).html(''), inputCount = 0, inputHTML;
14147
14148					$.each(uploader.files, function(i, file) {
14149						inputHTML = '';
14150
14151						if (file.status == plupload.DONE) {
14152							if (file.target_name) {
14153								inputHTML += '<input type="hidden" name="' + id + '_' + inputCount + '_tmpname" value="' + plupload.xmlEncode(file.target_name) + '" />';
14154							}
14155
14156							inputHTML += '<input type="hidden" name="' + id + '_' + inputCount + '_name" value="' + plupload.xmlEncode(file.name) + '" />';
14157							inputHTML += '<input type="hidden" name="' + id + '_' + inputCount + '_status" value="' + (file.status == plupload.DONE ? 'done' : 'failed') + '" />';
14158	
14159							inputCount++;
14160
14161							$('#' + id + '_count').val(inputCount);
14162						}
14163
14164						fileList.append(
14165							'<li id="' + file.id + '">' +
14166								'<div class="plupload_file_name"><span>' + file.name + '</span></div>' +
14167								'<div class="plupload_file_action"><a href="javascript:void(0)">X</a></div>' +
14168								'<div class="plupload_file_status">' + file.percent + '%</div>' +
14169								'<div class="plupload_file_size">' + plupload.formatSize(file.size) + '</div>' +
14170								'<div class="plupload_clearer">&nbsp;</div>' +
14171								inputHTML +
14172							'</li>'
14173						);
14174
14175						handleStatus(file);
14176
14177						$('#' + file.id + '.plupload_delete a').click(function(e) {
14178							$('#' + file.id).remove();
14179							uploader.removeFile(file);
14180
14181							e.preventDefault();
14182						});
14183					});
14184
14185					$('span.plupload_total_file_size', target).html(plupload.formatSize(uploader.total.size));
14186
14187					if (uploader.total.queued === 0) {
14188						$('span.plupload_add_text', target).html(_('Add Files'));
14189					} else {
14190						$('span.plupload_add_text', target).html(o.sprintf(_('%d files queued'), uploader.total.queued));
14191					}
14192
14193					$('a.plupload_start', target).toggleClass('plupload_disabled', uploader.files.length == (uploader.total.uploaded + uploader.total.failed));
14194
14195					// Scroll to end of file list
14196					fileList[0].scrollTop = fileList[0].scrollHeight;
14197
14198					updateTotalProgress();
14199
14200					// Re-add drag message if there is no files
14201					if (!uploader.files.length && uploader.features.dragdrop && uploader.settings.dragdrop) {
14202						$('#' + id + '_filelist').append('<li class="plupload_droptext">' + _("Drag files here.") + '</li>');
14203					}
14204				}
14205
14206				function destroy() {
14207					delete uploaders[id];
14208					uploader.destroy();
14209					target.html(contents_bak);
14210					uploader = target = contents_bak = null;
14211				}
14212
14213				uploader.bind("UploadFile", function(up, file) {
14214					$('#' + file.id).addClass('plupload_current_file');
14215				});
14216
14217				uploader.bind('Init', function(up, res) {
14218					// Enable rename support
14219					if (!settings.unique_names && settings.rename) {
14220						target.on('click', '#' + id + '_filelist div.plupload_file_name span', function(e) {
14221							var targetSpan = $(e.target), file, parts, name, ext = "";
14222
14223							// Get file name and split out name and extension
14224							file = up.getFile(targetSpan.parents('li')[0].id);
14225							name = file.name;
14226							parts = /^(.+)(\.[^.]+)$/.exec(name);
14227							if (parts) {
14228								name = parts[1];
14229								ext = parts[2];
14230							}
14231
14232							// Display input element
14233							targetSpan.hide().after('<input type="text" />');
14234							targetSpan.next().val(name).focus().blur(function() {
14235								targetSpan.show().next().remove();
14236							}).keydown(function(e) {
14237								var targetInput = $(this);
14238
14239								if (e.keyCode == 13) {
14240									e.preventDefault();
14241
14242									// Rename file and glue extension back on
14243									file.name = targetInput.val() + ext;
14244									targetSpan.html(file.name);
14245									targetInput.blur();
14246								}
14247							});
14248						});
14249					}
14250
14251					//$('#' + id + '_container').attr('title', 'Using runtime: ' + res.runtime);
14252
14253					$('a.plupload_start', target).click(function(e) {
14254						if (!$(this).hasClass('plupload_disabled')) {
14255							uploader.start();
14256						}
14257
14258						e.preventDefault();
14259					});
14260
14261					$('a.plupload_stop', target).click(function(e) {
14262						e.preventDefault();
14263						uploader.stop();
14264					});
14265
14266					$('a.plupload_start', target).addClass('plupload_disabled');
14267				});
14268
14269				uploader.bind("Error", function(up, err) {
14270					var file = err.file, message;
14271
14272					if (file) {
14273						message = err.message;
14274
14275						if (err.details) {
14276							message += " (" + err.details + ")";
14277						}
14278
14279						if (err.code == plupload.FILE_SIZE_ERROR) {
14280							alert(_("Error: File too large:") + " " + file.name);
14281						}
14282
14283						if (err.code == plupload.FILE_EXTENSION_ERROR) {
14284							alert(_("Error: Invalid file extension:") + " " + file.name);
14285						}
14286						
14287						file.hint = message;
14288						$('#' + file.id).attr('class', 'plupload_failed').find('a').css('display', 'block').attr('title', message);
14289					}
14290
14291					if (err.code === plupload.INIT_ERROR) {
14292						setTimeout(function() {
14293							destroy();
14294						}, 1);
14295					}
14296				});
14297
14298				uploader.bind("PostInit", function(up) {
14299					// features are populated only after input components are fully instantiated
14300					if (up.settings.dragdrop && up.features.dragdrop) {
14301						$('#' + id + '_filelist').append('<li class="plupload_droptext">' + _("Drag files here.") + '</li>');
14302					}
14303				});
14304
14305				uploader.init();
14306
14307				uploader.bind('StateChanged', function() {
14308					if (uploader.state === plupload.STARTED) {
14309						$('li.plupload_delete a,div.plupload_buttons', target).hide();
14310						uploader.disableBrowse(true);
14311
14312						$('span.plupload_upload_status,div.plupload_progress,a.plupload_stop', target).css('display', 'block');
14313						$('span.plupload_upload_status', target).html('Uploaded ' + uploader.total.uploaded + '/' + uploader.files.length + ' files');
14314
14315						if (settings.multiple_queues) {
14316							$('span.plupload_total_status,span.plupload_total_file_size', target).show();
14317						}
14318					} else {
14319						updateList();
14320						$('a.plupload_stop,div.plupload_progress', target).hide();
14321						$('a.plupload_delete', target).css('display', 'block');
14322
14323						if (settings.multiple_queues && uploader.total.uploaded + uploader.total.failed == uploader.files.length) {
14324							$(".plupload_buttons,.plupload_upload_status", target).css("display", "inline");
14325							uploader.disableBrowse(false);
14326
14327							$(".plupload_start", target).addClass("plupload_disabled");
14328							$('span.plupload_total_status,span.plupload_total_file_size', target).hide();
14329						}
14330					}
14331				});
14332
14333				uploader.bind('FilesAdded', updateList);
14334
14335				uploader.bind('FilesRemoved', function() {
14336					// since the whole file list is redrawn for every change in the queue
14337					// we need to scroll back to the file removal point to avoid annoying
14338					// scrolling to the bottom bug (see #926)
14339					var scrollTop = $('#' + id + '_filelist').scrollTop();
14340					updateList();
14341					$('#' + id + '_filelist').scrollTop(scrollTop);
14342				});
14343
14344				uploader.bind('FileUploaded', function(up, file) {
14345					handleStatus(file);
14346				});
14347
14348				uploader.bind("UploadProgress", function(up, file) {
14349					// Set file specific progress
14350					$('#' + file.id + ' div.plupload_file_status', target).html(file.percent + '%');
14351
14352					handleStatus(file);
14353					updateTotalProgress();
14354				});
14355
14356				// Call setup function
14357				if (settings.setup) {
14358					settings.setup(uploader);
14359				}
14360			});
14361
14362			return this;
14363		} else {
14364			// Get uploader instance for specified element
14365			return uploaders[$(this[0]).attr('id')];
14366		}
14367	};
14368})(jQuery, mOxie);
14369
14370/// <reference path="NavigationApp.js" />
14371(function () {
14372    'use strict';
14373
14374    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
14375
14376    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
14377
14378    const app = angular.module('MemberPortal', [
14379        'ngRoute',
14380        'ngCookies',
14381        'pascalprecht.translate',
14382        'customFilters',
14383        'MemberPortalDirectives',
14384        'MemberPortalServices',
14385        'ngSlider',
14386        'ngAnimate',
14387        'ui.bootstrap',
14388        '720kb.datepicker'])
14389
14390        .constant('config', {
14391            // language mappings for limesurvey translations
14392            languageMap: {
14393                'en-US': 'en',
14394                'es-MX': 'es',
14395                'vi-VN': 'vi',
14396                'zh-TW': 'zh-Hans'
14397            }
14398        })
14399
14400        //============== LANGUAGE TRANSLATION
14401        .config(['$translateProvider', function ($translateProvider) {
14402            //LOAD via WEBAPI
14403            //  $translateProvider.useUrlLoader('/ws/englishspanish.json');
14404            //Load via File
14405            $translateProvider.useStaticFilesLoader({
14406                prefix: 'Languages/',
14407                suffix: '.txt' + cacheBust
14408            });
14409        }])
14410
14411        .config(['$routeProvider', '$locationProvider', '$httpProvider', '$provide',
14412            function ($routeProvider, $locationProvider, $httpProvider, $provide) {
14413
14414                //Check for HTML5 History API support.
14415                if (!!(window.history && history.pushState)) {
14416                    //Configure for html5 routing
14417                    //console.log("HTML5 Mode On");
14418                    $locationProvider.html5Mode(true);
14419                } else {
14420                    // console.log("Hashbang in HTML5 Mode");
14421                    //Configure for hashbang routes.
14422                    $locationProvider.html5Mode(true).hashPrefix('!');
14423                    //simulate URL rewriting
14424                    $provide.decorator('$sniffer', ['$delegate', function ($delegate) {
14425                        $delegate.history = false;
14426                        return $delegate;
14427                    }]);
14428                }
14429
14430                //Wires in the interceptor service that injects the bearer token to all request if available.
14431                $httpProvider.interceptors.push('dataInterceptorService');
14432
14433                //Make requests as Ajax Requests, allows the Request.IsAjaxRequest == true 
14434                //Turned  off because it interfered with cors requests...
14435                $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
14436
14437                //Define Angular Routes
14438                var routeResolver = $routeProvider
14439                    .when('/App/Home', {
14440                        templateUrl: urlBase + '/AppViews/Home',
14441                        controller: 'HomeController',
14442                        resolve: {
14443                            permission: [
14444                                'authService', function (authService) {
14445                                    return authService.AuthenticationCheck();
14446                                }
14447                            ]
14448                        }
14449                    })
14450                    .when('/App/Account/Login/:lang?', {
14451                        templateUrl: urlBase + '/App/pages/account/loginPage.html' + cacheBust,
14452                        controller: 'loginPageController',
14453                        caseInsensitiveMatch: true
14454                    })
14455                    .when('/App/Account/Register/:lang?', {
14456                        templateUrl: urlBase + '/App/pages/Account/registrationPage.html' + cacheBust,
14457                        caseInsensitiveMatch: true
14458                    })
14459                    .when('/App/Account/PasswordReset/:lang?', {
14460                        templateUrl: urlBase + '/App/pages/passwordReset.html' + cacheBust,
14461                        caseInsensitiveMatch: true
14462                    })
14463                    .when('/App/NotAuthorized', {
14464                        templateUrl: urlBase + '/App/pages/notAuthorized.html' + cacheBust,
14465                        caseInsensitiveMatch: true,
14466                        controller: 'NotAuthorizedController'
14467                    })
14468                    .when('/App/Eligibility/TemporaryIdCard', {
14469                        templateUrl: urlBase + '/AppViews/PrintTempIDCard',
14470                        controller: 'temporaryIdCardController',
14471                        activetab: 'printTempIdCard',
14472                        caseInsensitiveMatch: true,
14473                        resolve: {
14474                            permission: [
14475                                'authService', function (authService) {
14476                                    return authService.AuthenticationCheck();
14477                                }
14478                            ]
14479                        }
14480                    })
14481                    .when('/App/Eligibility/RequestIdCard', {
14482                        templateUrl: urlBase + '/AppViews/RequestIDCard' + cac
14482heBust,
14483                        controller: 'RequestNewIdController',
14484                        activetab: 'requestIdCard',
14485                        caseInsensitiveMatch: true,
14486                        resolve: {
14487                            permission: [
14488                                'authService', function (authService) {
14489                                    return authService.AuthenticationCheck();
14490                                }
14491                            ]
14492                        }
14493                    })
14494
14495                    /* Eligibility */
14496
14497                    .when('/App/Eligibility/Current', {
14498                        templateUrl: urlBase + '/AppViews/CurrentEligibility' + cacheBust,
14499                        controller: 'CurrentEligibilityController',
14500                        activetab: 'currentEligibility',
14501                        caseInsensitiveMatch: true,
14502                        resolve: {
14503                            permission: [
14504                                'authService', function (authService) {
14505                                    return authService.AuthenticationCheck();
14506                                }
14507                            ]
14508                        }
14509                    })
14510                    .when('/App/Eligibility/Historical', {
14511                        templateUrl: urlBase + '/AppViews/HistoricalEligibility' + cacheBust,
14512                        controller: 'HistoricalEligibilityController',
14513                        activetab: 'historicalEligibility',
14514                        caseInsensitiveMatch: true,
14515                        resolve: {
14516                            permission: [
14517                                'authService', function (authService) {
14518                                    return authService.AuthenticationCheck();
14519                                }
14520                            ]
14521                        }
14522                    })
14523                    /* My Doctor */
14524                    .when('/App/Eligibility/MyDoctor', {
14525                        templateUrl: urlBase + '/AppViews/MyDoctor' + cacheBust,
14526                        controller: 'MyDoctorController',
14527                        activetab: 'currentDoctor',
14528                        caseInsensitiveMatch: true,
14529                        resolve: {
14530                            permission: [
14531                                'authService', function (authService) {
14532                                    return authService.AuthenticationCheck();
14533                                }
14534                            ]
14535                        }
14536                    })
14537                    /* Change Doctor */
14538                    .when('/App/Eligibility/ChangeDoctor', {
14539                        templateUrl: urlBase + '/AppViews/ChangeDoctor' + cacheBust,
14540                        controller: 'ChangeDoctorController',
14541                        activetab: 'changeDoctor',
14542                        caseInsensitiveMatch: true,
14543                        resolve: {
14544                            permission: [
14545                                'authService', function (authService) {
14546                                    return authService.AuthenticationCheck();
14547                                }
14548                            ]
14549                        }
14550                    })
14551                    .when('/App/MyServices/InterpreterRequests', {
14552                        templateUrl: urlBase + '/AppViews/InterpreterRequests' + cacheBust,
14553                        controller: 'InterpreterRequestsController',
14554                        caseInsensitiveMatch: true,
14555                        resolve: {
14556                            permission: [
14557                                'authService', function (authService) {
14558                                    return authService.AuthenticationCheck();
14559                                }
14560                            ]
14561                        }
14562                    })
14563                    .when('/App/MyServices/TransportationRequests', {
14564                        templateUrl: urlBase + '/AppViews/TransportationRequests' + cacheBust,
14565                        controller: 'TransportationRequestsController',
14566                        controllerAs: 'vc',
14567                        caseInsensitiveMatch: true,
14568                        reloadOnSearch: false,
14569                        resolve: {
14570                            permission: [
14571                                'authService', function (authService) {
14572                                    return authService.AuthenticationCheck();
14573                                }
14574                            ]
14575                        }
14576                    })
14577                    /* Update Contact Info */
14578                    .when('/App/MyInformation/UpdateContactInfo', {
14579                        templateUrl: urlBase + '/AppViews/UpdateContactInfo' + cacheBust,
14580                        controller: 'UpdateContactInfoController',
14581                        caseInsensitiveMatch: true,
14582                        activetab: 'updateContactInfo',
14583                        resolve: {
14584                            permission: [
14585                                'authService', function (authService) {
14586                                    return authService.AuthenticationCheck();
14587                                }
14588                            ]
14589                        }
14590                    })
14591                    /* Change Password */
14592                    .when('/App/MyInformation/ChangePassword', {
14593                        templateUrl: urlBase + '/App/pages/Account/changePassword.html' + cacheBust,
14594                        controller: 'changePasswordController',
14595                        activetab: 'changePassword',
14596                        caseInsensitiveMatch: true,
14597                        resolve: {
14598                            permission: [
14599                                'authService', function (authService) {
14600                                    return authService.AuthenticationCheck();
14601                                }
14602                            ]
14603                        }
14604                    })
14605
14606                    /* My Correspondences */
14607                    .when('/App/MyInformation/MyCorrespondences', {
14608                        templateUrl: urlBase + '/App/pages/Account/correspondences.html' + cacheBust,
14609                        activetab: 'MyCorrespondences',
14610                        controller: 'MyCorrespondencesController',
14611                        controllerAs: 'vc',
14612                        caseInsensitiveMatch: true,
14613                        resolve: {
14614                            permission: [
14615                                'authService', function (authService) {
14616                                    return authService.AuthenticationCheck();
14617                                }
14618                            ]
14619                        }
14620                    })
14621                    /* PHI Release Form */
14622                    .when('/App/MyInformation/PhiReleaseForm', {
14623                        templateUrl: urlBase + '/App/pages/Account/phiReleaseForm.html' + cacheBust,
14624                        activetab: 'PhiReleaseForm',
14625                        controller: 'PhiReleaseFormController',
14626                        controllerAs: 'vc',
14627                        caseInsensitiveMatch: true,
14628                        resolve: {
14629                            permission: [
14630                                'authService', function (authService) {
14631                                    return authService.AuthenticationCheck();
14632                                }
14633                            ]
14634                        }
14635                    })
14636
14637                    /* Change Security Questions */
14638                    .when('/App/MyInformation/ChangeSecurityQuestions', {
14639                        templateUrl: urlBase + '/App/pages/Account/changeSecurityQuestions.html' + cacheBust,
14640                        activetab: 'changeSecurityQuestions',
14641                        caseInsensitiveMatch: true,
14642                        resolve: {
14643                            permission: [
14644                                'authService', function (authService) {
14645                                    return authService.AuthenticationCheck();
14646                                }
14647                            ]
14648                        }
14649                    })
14650                    .when('/App/MyInformation/ChangeCommunicationPreferences', {
14651                        templateUrl: urlBase + '/App/pages/Account/changeCommunicationPreferences.html' + cacheBust,
14652                        activetab: 'changeCommunicationPreferences',
14653                        caseInsensitiveMatch: true,
14654                        resolve: {
14655                            permission: [
14656                                'authService', function (authService) {
14657                                    return authService.AuthenticationCheck();
14658                                }
14659                            ]
14660                        }
14661                    })
14662
14663                    /* Health Delegate */
14664                    .when('/App/MyInformation/Delegates', {
14665                        templateUrl: urlBase + '/App/pages/MyInformation/delegates.html' + cacheBust,
14666                        controller: 'DelegatesController',
14667                        activetab: 'delegates',
14668                        caseInsensitiveMatch: true,
14669                        resolve: {
14670                            permission: [
14671                                'authService', function (authService) {
14672                                    return authService.AuthenticationCheck();
14673                                }
14674                            ]
14675                        }
14676                    })
14677                    .when('/App/MyInformation/add-delegate', {
14678                        templateUrl: urlBase + '/App/pages/MyInformation/addDelegate.html' + cacheBust,
14679                        controller: 'AddDelegateController',
14680                        activetab: 'delegates',
14681                        caseInsensitiveMatch: true,
14682                        resolve: {
14683                            permission: [
14684                                'authService', function (authService) {
14685                                    return authService.AuthenticationCheck();
14686                                }
14687                            ]
14688                        }
14689                    })
14690                    .when('/App/MyInformation/request-delegate', {
14691                        templateUrl: urlBase + '/App/pages/MyInformation/addDelegate.html' + cacheBust,
14692                        controller: 'AddDelegateController',
14693                        activetab: 'delegates',
14694                        caseInsensitiveMatch: true,
14695                        resolve: {
14696                            permission: [
14697                                'authService', function (authService) {
14698                                    return authService.AuthenticationCheck();
14699                                }
14700                            ]
14701                        }
14702                    })
14703
14704                    /* Rx Prescriptions */
14705                    .when('/App/HealthRecords/RxPrescriptions', {
14706                        templateUrl: urlBase + '/App/Pages/HealthRecords/RxPrescriptions.html' + cacheBust,
14707                        controller: 'RxPrescriptionsController',
14708                        activetab: 'rxPrescriptions',
14709                        caseInsensitiveMatch: true,
14710                        resolve: {
14711                            permission: [
14712                                'authService', function () {
14713                                    return '/App/Home';
14714                                }
14715                            ]
14716                        }
14717                    })
14718
14719                    /* Claims */
14720                    .when('/App/HealthRecords/MedicalClaims', {
14721                        templateUrl: urlBase + '/App/Pages/HealthRecords/Claims.html' + cacheBust,
14722                        controller: 'ClaimsController',
14723                        activetab: 'medicalClaims',
14724                        caseInsensitiveMatch: true,
14725                        resolve: {
14726                            permission: [
14727                                'authService', function () {
14728                                    return '/App/Home';
14729                                }
14730                            ]
14731                        }
14732                    })
14733                    .when('/App/HealthRecords/MedicalClaims/:claimId', {
14734                        templateUrl: urlBase + '/App/Pages/HealthRecords/ClaimDetails.html' + cacheBust,
14735                        controller: 'ClaimsController',
14736                        caseInsensitiveMatch: true,
14737                        resolve: {
14738                            permission: [
14739                                'authService', function (authService) {
14740                                    return authService.AuthenticationCheck();
14741                                }
14742                            ]
14743                        }
14744                    })
14745
14746                    /* Immunizations */
14747                    .when('/App/HealthRecords/ImmunizationRecords', {
14748                        templateUrl: urlBase + '/AppViews/ImmunizationRecords' + cac
14748heBust,
14749                        controller: 'ImmunizationsController',
14750                        activetab: 'immunizationRecords',
14751                        caseInsensitiveMatch: true,
14752                        resolve: {
14753                            permission: [
14754                                'authService', function (authService) {
14755                                    return authService.AuthenticationCheck();
14756                                }
14757                            ]
14758                        }
14759                    })
14760
14761                    /* Labs */
14762                    .when('/App/HealthRecords/LabWork', {
14763                        templateUrl: urlBase + '/AppViews/LabWork' + cacheBust,
14764                        controller: 'LabsController',
14765                        activetab: 'labWork',
14766                        caseInsensitiveMatch: true,
14767                        resolve: {
14768                            permission: [
14769                                'authService', function (authService) {
14770                                    return authService.AuthenticationCheck();
14771                                }
14772                            ]
14773                        }
14774                    })
14775
14776                    /* Authorizations */
14777                    .when('/App/HealthRecords/Authorizations/All', {
14778                        templateUrl: urlBase + '/AppViews/AllAuthorizations' + cacheBust,
14779                        controller: 'AuthorizationAllController',
14780                        activetab: 'allAuthorizations',
14781                        caseInsensitiveMatch: true,
14782                        resolve: {
14783                            permission: [
14784                                'authService', function (authService) {
14785                                    return authService.AuthenticationCheck();
14786                                }
14787                            ]
14788                        }
14789                    })
14790                    .when('/App/HealthRecords/Authorizations/Medical', {
14791                        templateUrl: urlBase + '/AppViews/MedicalAuthorizations' + cacheBust,
14792                        controller: 'AuthorizationMedicalController',
14793                        activetab: 'allAuthorizations',
14794                        caseInsensitiveMatch: true,
14795                        resolve: {
14796                            permission: [
14797                                'authService', function (authService) {
14798                                    return authService.AuthenticationCheck();
14799                                }
14800                            ]
14801                        }
14802                    })
14803                    .when('/App/HealthRecords/Authorizations/Medical/:AuthId/MemberLetter/', {
14804                        templateUrl: urlBase + '/App/pages/HealthRecords/MemberAuthorizationLetter.html',
14805                        controller: 'MemberAuthorizationLetterController',
14806                        caseInsensitiveMatch: true,
14807                        resolve: {
14808                            permission: [
14809                                'authService', function (authService) {
14810                                    return authService.AuthenticationCheck();
14811                                }
14812                            ]
14813                        }
14814                    })
14815
14816                    .when('/App/HealthRecords/Authorizations/Vision', {
14817                        templateUrl: urlBase + '/AppViews/VisionAuthorizations' + cacheBust,
14818                        controller: 'AuthorizationVisionController',
14819                        activetab: 'allAuthorizations',
14820                        caseInsensitiveMatch: true,
14821                        resolve: {
14822                            permission: [
14823                                'authService', function (authService) {
14824                                    return authService.AuthenticationCheck();
14825                                }
14826                            ]
14827                        }
14828                    }).when('/App/HealthRecords/Authorizations/Rx', {
14829                        templateUrl: urlBase + '/AppViews/RxAuthorizations' + cac
14829heBust,
14830                        controller: 'AuthorizationRxController',
14831                        activetab: 'allAuthorizations',
14832                        caseInsensitiveMatch: true,
14833                        resolve: {
14834                            permission: [
14835                                'authService', function (authService) {
14836                                    return authService.AuthenticationCheck();
14837                                }
14838                            ]
14839                        }
14840                    })
14841
14842                    /* Care Plans */
14843                    .when('/App/HealthRecords/CarePlans', {
14844                        templateUrl: urlBase + '/AppViews/CarePlans' + cacheBust,
14845                        controller: 'CarePlansController',
14846                        activetab: 'carePlans',
14847                        caseInsensitiveMatch: true,
14848                        resolve: {
14849                            permission: [
14850                                'authService', function (authService) {
14851                                    return authService.AuthenticationCheck();
14852                                }
14853                            ]
14854                        }
14855                    })
14856
14857                    /* HRA */
14858                    .when('/App/HealthRecords/HealthRiskAssessment', {
14859                        templateUrl: urlBase + '/AppViews/HealthRiskAssessment' + cacheBust,
14860                        controller: 'HRAController',
14861                        activetab: 'healthRiskAssessment',
14862                        caseInsensitiveMatch: true,
14863                        resolve: {
14864                            permission: [
14865                                'authService', function (authService) {
14866                                    return authService.AuthenticationCheck();
14867                                }
14868                            ]
14869                        }
14870                    })
14871
14872                    /* Confidential Communications */
14873                    .when('/App/HealthRecords/ConfidentialCommunications', {
14874                        templateUrl: urlBase + '/AppViews/ConfidentialCommunications' + cacheBust,
14875                        controller: 'ConfidentialCommunicationsController',
14876                        activetab: 'confidentialCommunications',
14877                        caseInsensitiveMatch: true,
14878                        resolve: {
14879                            permission: [
14880                                'authService', function (authService) {
14881                                    return authService.ShowCCRCheck() && authService.AuthenticationCheck() && authService.IsMediCalCheck();
14882                                }
14883                            ]
14884                        }
14885                    })
14886
14887                    /* Request for Medicare and Medi-Cal Prescription Drug Coverage Determination. */
14888                    .when('/App/HealthRecords/PrescriptionRequest', {
14889                        templateUrl: urlBase + '/App/pages/HealthRecords/PrescriptionRequest.html' + cacheBust,
14890                        controller: 'PrescriptionRequestController',
14891                        caseInsensitiveMatch: true,
14892                        resolve: {
14893                            permission: [
14894                                'authService', function (authService) {
14895                                    return authService.AuthenticationCheck();
14896                                }
14897                            ]
14898                        }
14899                    })
14900
14901                    /* EOB */
14902                    .when('/App/HealthRecords/ExplanationOfBenefits', {
14903                        templateUrl: urlBase + '/App/pages/HealthRecords/ExplanationOfBenefits.html' + cacheBust,
14904                        controller: 'EOBController',
14905                        activetab: 'eob',
14906                        caseInsensitiveMatch: true,
14907                        resolve: {
14908                            permission: [
14909                                'authService', function (authService) {
14910                                    return authService.AuthenticationCheck();
14911                                }
14912                            ]
14913                        }
14914                    })
14915
14916                    /* Ask IEHP */
14917                    .when('/App/Info/AskIEHP/:function', {
14918                        templateUrl: urlBase + '/AppViews/AskIEHP' + cacheBust,
14919                        controller: 'AskIEHPController',
14920                        caseInsensitiveMatch: true,
14921                        resolve: {
14922                            permission: [
14923                                'authService', function (authService) {
14924                                    return authService.AuthenticationCheck();
14925                                }
14926                            ],
14927                            feature: 'featureFlagService',
14928                            function(featureFlagService) { return featureFlagService.redirect('Ask IEHP', '/App/Info/AskIEHP_/:function'); }
14929                        }
14930                    })
14931                    /* Ask IEHP OLD */
14932                    .when('/App/Info/AskIEHP_/:function', {
14933                        templateUrl: urlBase + '/AppViews/AskIEHP' + cacheBust,
14934                        controller: 'AskIEHPControllerOld',
14935                        caseInsensitiveMatch: true,
14936                        resolve: {
14937                            permission: [
14938                                'authService', function (authService) {
14939                                    return authService.AuthenticationCheck();
14940                                }
14941                            ]
14942                        }
14943                    })
14944
14945                    .when('/App/Payments', {
14946                        templateUrl: urlBase + '/AppViews/Payments' + cacheBust,
14947                        controller: 'MakePaymentsController',
14948                        caseInsensitiveMatch: true,
14949                        resolve: {
14950                            permission: [
14951                                'authService', function (authService) {
14952                                    return authService.AuthenticationCheck();
14953                                }
14954                            ]
14955                        }
14956                    })
14957
14958                    /* Courses per category */
14959                    .when('/App/HealthCare/Courses/select', {
14960                        template: '<available-courses></available-courses>',
14961                        caseInsensitiveMatch: true,
14962                        activetab: 'availableCourseCategory',
14963                        resolve: {
14964                            permission: [
14965                                'authService', function (authService) {
14966                                    return authService.AuthenticationCheck();
14967                                }
14968                            ]
14969                        }
14970                    })
14971                    /* Available Courses Category */
14972                    .when('/App/HealthCare/Courses/Available', {
14973                        template: '<available-course-category></available-course-category>',
14974                        caseInsensitiveMatch: true,
14975                        activetab: 'availableCourseCategory',
14976                        resolve: {
14977                            permission: [
14978                                'authService', function (authService) {
14979                                    return authService.AuthenticationCheck();
14980                                }
14981                            ]
14982                        }
14983                    })
14984
14985                    .when('/App/HealthCare/Courses/History', {
14986                        template: '<health-ed-history></health-ed-history>',
14987                        caseInsensitiveMatch: true,
14988                        activetab: 'courseHistory',
14989                        resolve: {
14990                            permission: [
14991                                'authService', function (authService) {
14992                                    return authService.AuthenticationCheck();
14993                                }
14994                            ]
14995                        }
14996                    })
14997
14998                    /* Survey UI: These routes and pages might be deprecated, I'll leave them alone for now. */
14999                    .when('/App/HealthCare/HASurvey/:surveyId/:versionId/:typeId', {
15000                        templateUrl: urlBase + '/App/Pages/SurveyUI/HAsurvey.html' + cacheBust,
15001                        caseInsensitiveMatch: true,
15002                        resolve: {
15003                            permission: [
15004                                'authService', function (authService) {
15005                                    return authService.AuthenticationCheck();
15006                                }
15007                            ]
15008                        }
15009                    })
15010                    .when('/App/HealthCare/HASurvey/:surveyId/:versionId/:typeId/:transId', {
15011                        templateUrl: urlBase + '/App/Pages/SurveyUI/HAsurvey.html' + cacheBust,
15012                        caseInsensitiveMatch: true,
15013                        resolve: {
15014                            permission: [
15015                                'authService', function (authService) {
15016                                    return authService.AuthenticationCheck();
15017                                }
15018                            ]
15019                        }
15020                    })
15021                    .when('/App/HealthCare/HASurvey/History', {
15022                        templateUrl: urlBase + '/App/Pages/SurveyUI/HASurveyHistory.html' + cacheBust,
15023                        caseInsensitiveMatch: true,
15024                        controller: 'haSurveyHistoryController',
15025                        activetab: 'surveyHistory',
15026                        resolve: {
15027                            permission: [
15028                                'authService', function (authService) {
15029                                    return authService.AuthenticationCheck();
15030                                }
15031                            ]
15032                        }
15033                    })
15034                    .when('/App/HealthCare/HASurvey/Comparison', {
15035                        templateUrl: urlBase + '/App/Pages/SurveyUI/HASurveyComparison.html' + cacheBust,
15036                        caseInsensitiveMatch: true,
15037                        controller: 'haSurveyComparisonController',
15038                        resolve: {
15039                            permission: [
15040                                'authService', function (authService) {
15041                                    return authService.AuthenticationCheck();
15042                                }
15043                            ]
15044                        }
15045
15046                    })
15047                    //Inbox
15048                    .when('/App/Inbox', {
15049                        templateUrl: urlBase + '/App/Pages/portal-inbox/portal-inbox.html' + cacheBust,
15050                        caseInsensitiveMatch: true,
15051                        controller: 'portalInboxController',
15052                        controllerAs: 'vc',
15053                        resolve: {
15054                            permission: [
15055                                'authService', function (authService) {
15056                                    return authService.AuthenticationCheck();
15057                                }
15058                            ]
15059                        }
15060                    });
15061
15062                routeResolver.otherwise({ redirectTo: '/App/Home' });
15063            }]);
15064
15065    app.run(['$rootScope', '$q', 'authStatusService', 'memberDataService', 'requestedLanguageService',
15066        function ($rootScope, $q, authStatusService, memberDataService, requestedLanguageService) {
15067            $rootScope.redeterminationBannerMessage = null;
15068            const authStatus = authStatusService.getStatus();
15069
15070            if (!authStatus.isAuth || !authStatus.claims) return;
15071
15072            const memberNumber = authStatus.claims.username;
15073            const language = requestedLanguageService.getCurrentLanguage();
15074
15075            if (!memberNumber) return;
15076
15077            $q.all({
15078                leadRedetermination: memberDataService.checkLeadRedetermination(memberNumber),
15079                currentEligibility: memberDataService.getCurrentEligibility(language)
15080            })
15081                .then(function (responses) {
15082                    const leadRedetermination = responses.leadRedetermination.data;
15083                    const eligibility = memberDataService.utility.getPrimaryEligibilityRecord(
15084                        responses.currentEligibility.data
15085                    );
15086
15087                    const eligibilityStatusCode = eligibility && eligibility.EligibilityStatusCode;
15088                    const enoTransfer = getEnoTransfer(leadRedetermination);
15089
15090                    $rootScope.redeterminationBannerMessage = getRedeterminationBanner(
15091                        eligibilityStatusCode,
15092                        enoTransfer,
15093                        leadRedetermination
15094                    );
15095                })
15096                .catch(function (error) {
15097                    console.error("Redetermination status error:", error);
15098                });
15099
15100            function getEnoTransfer(leadRedetermination) {
15101                if (!leadRedetermination) return false;
15102
15103                const leadDetermination = leadRedetermination.leadDetermination ||
15104                    leadRedetermination.LeadDetermination ||
15105                    leadRedetermination;
15106
15107                return leadDetermination.enoTransfer === true ||
15108                    leadDetermination.enoTransfer === 'true' ||
15109                    leadDetermination.EnoTransfer === true ||
15110                    leadDetermination.EnoTransfer === 'true';
15111            }
15112
15113            function getRedeterminationBanner(eligibilityStatusCode, enoTransfer, leadRedetermination) {
15114                if (!enoTransfer) return null;
15115
15116                if (eligibilityStatusCode === 'A') {
15117                    const dueDate = getRedeterminationDueDate(leadRedetermination);
15118
15119                    return `Your Medi Cal coverage is due for renewal ${dueDate ? "by " + dueDate : "soon"}. If you don’t renew on time, Medi-Cal may end your coverage. Please update your information with the county and complete your renewal as soon as possible. Need help? Call us at 1-888-860-1296, Monday–Friday, 8 a.m.–5 p.m. (PST) for assistance.`;
15120                }
15121
15122                if (eligibilityStatusCode === 'D') {
15123                    return "Our records show that your Medi-Cal coverage is no longer active. To reinstate your benefits, please call us at 1 888 860 1296. We’re here to help."
15124                }
15125
15126                return null;
15127            }
15128
15129            function getRedeterminationDueDate(leadRedetermination) {
15130                if (!leadRedetermination || !leadRedetermination.reDeterminationDueDate) return '';
15131
15132                return moment(leadRedetermination.reDeterminationDueDate).format('MMMM DD, YYYY');
15133            }
15134        }]);
15135
15136
15137    app.run(['$rootScope', '$location', 'requestedLanguageService', function ($rootScope, $location, requestedLanguageService) {
15138
15139        // Listen for route changes to capture URL parameters
15140        $rootScope.$on('$routeChangeSuccess', function () {
15141
15142            // Grab search params (e.g., ?webview=true)
15143            var searchParams = $location.search();
15144
15145            // If webview parameter equals 'true', set flag to hide the surrounding navigation
15146            if (searchParams.webview === 'true' || searchParams.webview === true) {
15147                $rootScope.hideShellActive = true;
15148                console.log('$rootScope.hideShellActive', $rootScope.hideShellActive);
15149            } else {
15150                $rootScope.hideShellActive = false;
15151            }
15152
15153            if (searchParams.lang) {
15154                const selectedLanguageCode = requestedLanguageService.getCurrentLanguage();
15155
15156                const thresholdLanguageCodes = requestedLanguageService.getAllLanguages().map(m => m.code);
15157
15158                if (thresholdLanguageCodes.includes(searchParams.lang) && searchParams.lang !== selectedLanguageCode) {
15159                    requestedLanguageService.changeLanguage(searchParams.lang);
15160                }
15161            }
15162
15163        });
15164    }]);
15165
15166
15167
15168    app.run(['$templateCache', '$interpolate', function ($templateCache, $interpolate) {
15169        const startSym = $interpolate.startSymbol();
15170        const endSym = $interpolate.endSymbol();
15171        $templateCache.put('/dialogs/rxReminders.html', ' ' +
15172            '<div id="modalNotification" class="modal-header dialog-header-confirm"><button type="button" class="close" ng-click="closeModal()">&times;</button><h4 class="modal-title"><i class="fa fa-clock-o fa-2x"></i><span class="' + startSym + 'icon' + endSym + '"></span> ' + startSym + '"DIALOGS_RXREMINDERS" | translate' + endSym + ' </h4></div><div class="modal-body" ng-bind-html="msg">  </div><div class="rxPrescriptionsReminderOptionsContainer"><ul><li><input id="ckbox_option0" class="notif_ckbox" ng-click="reminderOptionClicked(0)"  type="checkbox" value="1WK"><label ng-bind-html="reminderOption0Label"></label>     </li><li><input  id="ckbox_option1"  ng-click="reminderOptionClicked(1)" class="notif_ckbox"  type="checkbox" value="1Day"><label ng-bind-html="reminderOption1Label"></label> </li><li> <input  id="ckbox_option2" ng-click="reminderOptionClicked(2)"  class="notif_ckbox"  type="checkbox" value="DOF"><label ng-bind-html="reminderOption2Label"></label> </li><li> <input  id="ckbox_option3" ng-click="reminderOptionClicked(3)"  class="notif_ckbox"  type="checkbox" value="DOF"><label ng-bind-html="reminderOption3Label"></label> </li></ul> </div> <div class="modal-footer">   <button type="button" class="btn btn-primary" ng-click="closeReminderModal()">' + startSym + '"DIALOGS_SET" | translate' + endSym + '</button></div>');
15173
15174    }]);
15175})();
15176(function () {
15177    var app = angular.module('MemberPortalDirectives', ['ui.bootstrap', 'MemberPortalServices', 'pascalprecht.translate', 'dialogs.main', 'ngMask']);
15178})();
15179(function () {
15180    'use strict';
15181
15182    angular
15183        .module('MemberPortal')
15184        .controller('HomeController', HomeController);
15185
15186    HomeController.$inject = [
15187        '$scope', 'authStatusService', 'localStorageService',
15188        'memberIDCardService', 'utilitiesService', 'healthDelegateService',
15189        'requestedLanguageService', 'memberDataService', '$rootScope', '$filter', 'environmentConfig'
15190    ];
15191
15192    function HomeController($scope, authStatusService, localStorageService,
15193        memberIdCardService, utilitiesService, healthDelegateService,
15194        requestedLanguageService, memberDataService, $rootScope, $filter, environmentConfig) {
15195
15196        $scope.getPrivacyUrl = getPrivacyUrl;
15197
15198        init();
15199
15200        function init() {
15201
15202            $scope.healthAppraisalPDHIUrl = environmentConfig.healthAppraisalPDHIUrl;
15203            $scope.ccaPaymentsRedirect = environmentConfig.ccaPaymentsRedirect;
15204
15205            const authStatus = authStatusService.getStatus();
15206
15207            const internaluser = authStatus.claims.internaluser;
15208
15209            const username = authStatus.claims.username;
15210            // Set the user meta tag with the username for Dynatrace RUM
15211
15212            $scope.isProxied = authStatusService.checkIfProxied();
15213
15214            $scope.isDelegateEligible = $scope.isProxied ? true : healthDelegateService.isDelegateEligible();
15215            
15216            $scope.LOB = authStatus.claims.lob;
15217
15218            $scope.isMobile = utilitiesService.isMobile();
15219
15220            $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
15221
15222            $scope.privacypolicy = requestedLanguageService.getPrivacyUrl($scope.currentLanguage);
15223
15224            $scope.isWidgetImage_EN_SP = true;  //default
15225            
15226            getPDHILogoCahnce();
15227
15228            /* CAll Correspondence and $broadcast to menu counters and Correspondence controller.*/
15229            if ($scope.LOB === 'CCA') {
15230                getCorrespondence();
15231            }
15232
15233            if (localStorageService.get('IDC_fetchStatus') == undefined || $scope.isProxied) {
15234                memberIdCardService.GetMemberTempCardImage();
15235            }
15236
15237            const origUsername = $scope.isProxied? authStatus.claims.origOwnerInfo.username: '';
15238            const dynatraceUser = internaluser !== "N/A" ? internaluser : $scope.isProxied? username + ',' + origUsername: username;
15239            document.querySelector('meta[name="user"]').setAttribute("content", dynatraceUser);
15240        }
15241
15242        $scope.$on('language-change', function (_event, key) {
15243            $scope.currentLanguage = key;
15244            getPDHILogoCahnce();
15245        });
15246
15247        $rootScope.$on('switchingProfile', function(_event, key) {
15248            $scope.switchingProfile = key.loadingProfile;
15249        });
15250
15251        function getPrivacyUrl (Language) {
15252            return requestedLanguageService.getPrivacyUrl(Language);
15253        };
15254
15255        function getPDHILogoCahnce() {
15256
15257            const englishSpanishLanguages = requestedLanguageService.
15258                getEnglishSpanishLanguages().map(m => m.code);
15259
15260            $scope.isWidgetImage_EN_SP = englishSpanishLanguages
15261                .includes($scope.currentLanguage);
15262        };  
15263
15264        //================= ALL Correspondence ============//
15265        function getCorrespondence() {
15266
15267            $scope.loading = true;
15268
15269            memberDataService.GetCorrespondence()
15270                .success(function (data) {
15271
15272                    $scope.errorMessage = '';
15273
15274                    if (!data) return;
15275                 
15276                    $rootScope.data = data;
15277                    $rootScope.$broadcast('my-correspondence', data);
15278                    
15279                }).error(function () {
15280                    $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
15281                }).finally(function () {
15282                    $scope.loading = false;
15283                });
15284        };
15285    };
15286})();
15287(function () {
15288    'use strict';
15289
15290    angular
15291        .module('MemberPortal')
15292        .controller('NotAuthorizedController', NotAuthorizedController);
15293
15294    NotAuthorizedController.$inject = [
15295        '$scope', '$location'
15296    ];
15297
15298    function NotAuthorizedController($scope, $location) {
15299
15300        $scope.search = $location.search();
15301    };
15302})();
15303(function () {
15304    'use strict';
15305
15306    angular
15307        .module('MemberPortal')
15308        .controller('changePasswordController', changePasswordController);
15309
15310    changePasswordController.$inject = [
15311        '$scope', '$filter'
15312    ];
15313
15314    function changePasswordController($scope, $filter) {
15315
15316        $scope.formSubmitButtonDisabled = true;
15317
15318        //===== JC: Convert to a Service
15319        $scope.checkPasswordMatch = function () {
15320
15321            const newPass1 = $('#newPassword').val();
15322            const newPass2 = $('#matchPassword').val();
15323
15324            if (newPass1 === newPass2 && newPass1.trim() !== '') {
15325                $scope.isPasswordMatch = true;
15326
15327                if ($('#currentPass').val() !== '')
15328                    $scope.formSubmitButtonDisabled = false;
15329            }
15330            else {
15331                $scope.isPasswordMatch = false;
15332                $scope.formSubmitButtonDisabled = true;
15333            }
15334        };
15335
15336    };
15337})();
15338(function () {
15339    'use strict';
15340
15341    angular
15342        .module('MemberPortal')
15343        .controller('loginPageController', loginPageController);
15344
15345    loginPageController.$inject = [
15346        '$scope', '$location', 'authService', 'requestedLanguageService'
15347    ];
15348
15349    function loginPageController($scope, $location, authService, requestedLanguageService) {
15350
15351        init();
15352
15353    function init() {
15354            
15355            $scope.username = '';
15356            $scope.password = '';
15357            $scope.showResetForm = false;
15358            $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
15359
15360            $scope.bannerImgSrc = `Content/Images/AER_${$scope.currentLanguage}.png`;
15361
15362            //Decode authString
15363           if ($location.search().authString) {
15364                authService.processLoginToken($location.search().authString)
15365                .then(function () {
15366
15367                    const urlBase = $('meta[name="ApplicationRoot"]').attr('content');
15368                    
15369                    const redirectUri = $location.search().redirectUri;
15370
15371                    if (redirectUri) {
15372                        return location.replace(urlBase + redirectUri);  // This handles /App/... OR full URLs
15373                    }
15374   
15375                    location.replace(urlBase + '/App/Home');
15376
15377                })
15378                .catch(function (err) {
15379
15380                    console.error("Failed to process login token:", err);
15381
15382                    // Optional: show an error screen, go to login page, etc.
15383                    const urlBase = $('meta[name="ApplicationRoot"]').attr('content');
15384                    location.replace(urlBase + '/App/login');
15385                });
15386            }
15387        }
15388        
15389        $scope.$on('language-change', function (_event, key) {
15390            
15391            $scope.currentLanguage = key;
15392  
15393            $scope.bannerImgSrc = `Content/Images/AER_${$scope.currentLanguage}.png`;
15394
15395        });
15396    };
15397})();
15398(function () {
15399    'use strict';
15400
15401    angular
15402        .module('MemberPortal')
15403        .controller('registrationPageController', registrationPageController);
15404
15405
15406    function registrationPageController() {
15407
15408
15409    };
15410})();
15411(function () {
15412    'use strict';
15413
15414    angular
15415        .module('MemberPortal')
15416        .controller('AskIEHPController', AskIEHPController);
15417
15418    AskIEHPController.$inject = [
15419        '$scope',
15420        '$rootScope',
15421        'authStatusService',
15422        'utilitiesService',
15423        'memberDataService',
15424        '$sce'
15425    ];
15426
15427    function AskIEHPController(
15428        $scope,
15429        $rootScope,
15430        authStatusService,
15431        utilitiesService,
15432        memberDataService,
15433        $sce) {
15434
15435        init();
15436
15437        function init() {
15438
15439            $scope.lobDescription = '';
15440
15441            window.addEventListener("message",
15442                function (event) {
15443
15444                    if (!event || event.origin !== window.location.origin) return;
15445
15446                    const data = event.data;
15447
15448                    if (data.event === 'scrollToTop') {
15449                        window.scrollTo({
15450                            top: 0,
15451                            behavior: 'smooth'
15452                        });
15453                    }
15454                });
15455
15456            $scope.isMobile = utilitiesService.isMobile();
15457            $scope.isCCA = $rootScope.isCCA;
15458
15459            if (!authStatusService || !authStatusService.getStatus() || !authStatusService.getStatus().claims) return;
15460
15461            embedForm();
15462
15463        }
15464
15465        function embedForm() {
15466
15467            const authClaims = authStatusService.getStatus().claims;
15468
15469            const memberID = authClaims.username;
15470
15471            const lob = authClaims.lob;
15472
15473            let lobDescription = '';
15474
15475            memberDataService.getEligibilityByDateOfService('en-US').success(function (data) {
15476                console.log('eligibility', data);
15477
15478                if (!data || data.length === 0) return;
15479
15480                lobDescription = data[0].LobDescription;
15481
15482            }).finally(function () {
15483
15484                const formIOBase = $('meta[name="iFormioBase"]').attr('content');
15485
15486                const memberIdParam = `MemberId=${memberID}`;
15487
15488                const lobParams = `lob=${lob}&lobDescription=${lobDescription}`;
15489
15490                const originParam = `origin=mpAskIehp`;
15491
15492                const url = `${formIOBase}?form=portalInbox&showDirectEmbed=true&${memberIdParam}&${lobParams}&${originParam}`;
15493
15494                $scope.iframeSrc = $sce.trustAsResourceUrl(url);
15495            });
15496        }
15497    };
15498})();
15499(function () {
15500    'use strict';
15501
15502    angular
15503        .module('MemberPortal')
15504        .controller('CourseHistoryController', CourseHistoryController);
15505
15506    CourseHistoryController.$inject = [
15507        '$scope', '$filter', 'memberDataService', 'dialogs'
15508    ];
15509
15510    function CourseHistoryController($scope, $filter, memberDataService, $dialogs) {
15511        $scope.loading = false;
15512        $scope.errorMessage = '';
15513        $scope.infoMessage = '';
15514        $scope.predicate = '';
15515        $scope.requestedSections = '';
15516        $scope.requirementsContainer = false;
15517        $scope.reverse = false;
15518        $scope.courseOptions = [];
15519        $scope.requirementsSelected = [];
15520
15521        $('#navbtn8 div').addClass('navItemSelected');
15522        $scope.$on('language-change', function (event, key) {
15523            $scope.currentLanguage = key;
15524        });
15525        $scope.timeConvert = function (time) {
15526            time = time.toString().match(/^([01]\d|2[0-3])(:)([0-5]\d)(:[0-5]\d)?$/) || [time];
15527
15528            if (time.length > 1) {
15529                time = time.slice(1);
15530                time[5] = +time[0] < 12 ? 'AM' : 'PM';
15531                time[0] = +time[0] % 12 || 12;
15532            }
15533            return time.join('');
15534        };
15535
15536        $scope.trimUpperCarrot = function (string) {
15537            const firstCarrotPos = string.indexOf('^');
15538            if (firstCarrotPos > 0)
15539                return string.substring(0, firstCarrotPos);
15540            else
15541                return string;
15542        };
15543
15544        //===================== Get Approved Course Sections
15545        $scope.GetCourseHistory = function () {
15546            // console.log('....getting history');
15547            $scope.loading = !$scope.loading;
15548            $scope.requestedSections = '';
15549
15550            const language = $scope.currentLanguage;
15551
15552            memberDataService.GetCourseHistory(language).success(
15553
15554                function (historyCourses) {
15555                    // console.log('...returned data from history ');
15556
15557                    $scope.historyCourses = historyCourses;
15558                    $scope.loading = !$scope.loading;
15559                    $scope.errorMessage = '';
15560                    $scope.infoMessage = $filter('translate')('form_noCourseHistoryFound');
15561
15562                }).error(function () {
15563                    $scope.loading = !$scope.loading;
15564                    $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
15565                });
15566        };
15567
15568        //===================== Put Cancel Course Section 
15569        $scope.cancelCourseSection = function (sectionId, pendingStatus) {
15570
15571            switch (pendingStatus) {
15572
15573                case 'Pending': $scope.showModal('DeleteEnrolledCourseSection', sectionId); break;
15574
15575            }
15576        };
15577
15578
15579        //  Modal
15580        $scope.showModal = function (type, options) {
15581            switch (type) {
15582                case 'SuccessfulRequest': $dialogs.notify($filter('translate')('page_healthEd_availableCourses_requestModalTitle'),
15583                    `<center class="overflowAuto">${$filter('translate')('page_healthEd_availableCourses_requestModalRequestSubmittedOk')}</center>`);
15584                    break;
15585                case 'UnsuccessfulRequest': $dialogs.notify($filter('translate')('page_healthEd_availableCourses_requestModalTitle'),
15586                    `<center class="overflowAuto">${$filter('translate')('page_healthEd_availableCourses_requestModalRequestSubmittedError')}</center>`);
15587                    break;
15588                case 'openMap': $dialogs.notify($filter('translate')('page_healthEd_availableCourses_requestMapTitle'),
15589                    `<center class="overflowAuto"> Show map here to ${options}<div id="mapCanvas"></div></center>`);
15590                    break;
15591
15592            }
15593        };
15594
15595        $scope.showMapForCourseSection = function (_sectionId, address) {
15596          
15597            $scope.showModal('openMap', address);
15598        };
15599
15600        //Look out for Language Change  
15601        $scope.$on('language-change', function () {
15602
15603            $scope.GetCourseHistory();
15604
15605        });
15606
15607        //Initialize
15608        $scope.GetCourseHistory();
15609
15610        $scope.focusOnElement = function (elementId) {
15611            window.setTimeout(function () {
15612                $('#' + elementId).focus();
15613            }, 500);
15614        };
15615
15616        $scope.focusOnElement('btnCourseHistory');
15617    };
15618})();
15619(function () {
15620    'use strict';
15621
15622    angular
15623        .module('MemberPortal')
15624        .controller('haSurveyComparisonController', haSurveyComparisonController);
15625
15626    haSurveyComparisonController.$inject = [
15627        '$scope', 'requestedLanguageService'
15628    ];
15629
15630    function haSurveyComparisonController($scope, requestedLanguageService) {
15631        $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
15632
15633        $scope.$on('language-change', function (_event, key) {
15634
15635            $scope.currentLanguage = key;
15636        });
15637
15638        $scope.getPrivacyUrl = getPrivacyUrl;
15639
15640        function getPrivacyUrl(language) {
15641            return requestedLanguageService.getPrivacyUrl(language);
15642        }
15643
15644    };
15645})();
15646(function () {
15647    'use strict';
15648
15649    angular
15650        .module('MemberPortal')
15651        .controller('haSurveyHistoryController', haSurveyHistoryController);
15652
15653    haSurveyHistoryController.$inject = [
15654        '$scope', 'requestedLanguageService'
15655    ];
15656
15657    function haSurveyHistoryController($scope, requestedLanguageService) {
15658
15659        $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
15660
15661        $scope.$on('language-change', function (_event, key) {
15662
15663            $scope.currentLanguage = key;
15664        });
15665
15666        $scope.getPrivacyUrl = getPrivacyUrl;
15667
15668        function getPrivacyUrl(language) {
15669            return requestedLanguageService.getPrivacyUrl(language);
15670        }
15671
15672    };
15673})();
15674(function () {
15675    'use strict';
15676
15677    angular
15678        .module('MemberPortal')
15679        .controller('surveyUIController', surveyUIController);
15680
15681    surveyUIController.$inject = [
15682        '$scope',
15683        'authStatusService',
15684        'memberDataService',
15685        '$routeParams',
15686        '$location',
15687        'requestedLanguageService'];
15688
15689    function surveyUIController(
15690        $scope,
15691        authStatusService,
15692        memberDataService,
15693        $routeParams,
15694        $location,
15695        requestedLanguageService) {
15696        /*********************************INITIALIZE SURVEY SCOPE AND INNER FUNCTION VARIABLES****************************************/
15697
15698        $scope.SurveyName = '';                             //name of the survey
15699        $scope.sectionOn = 0;                               //the current section the user is on
15700        $scope.CurrentSectionFromSurvey = 0;
15701        $scope.totalSections = 0;                           //total number of sections
15702        $scope.ActiveSection = [];                          //the Section currently being rendered
15703        $scope.SurveySectionTitles = [];                    //Titles of the different sections
15704        $scope.validForm = false;                           //set initial form validation to true
15705        $scope.SurveyIsFinshed = false;                     //boolean to keep track if the survey is finished
15706
15707        const claims = authStatusService.getStatus().claims;
15708
15709        $scope.memberSubNo = claims.username; 
15710        $scope.internalUser = claims.internaluser;
15711        $scope.userLoggedIn = claims.username;
15712
15713        $scope.language = requestedLanguageService.getCurrentLanguage();
15714        $scope.surveySectionRecieved = false;
15715        $scope.SurveySummaryInformation = [];               //initialize summary information array
15716        $scope.demo = {};
15717        $scope.Gender = null;
15718        $scope.Age = 0;
15719        $scope.SurveyError = false;
15720        $scope.ErrorMsg = '';
15721        $scope.formattedDOB = '';
15722        $scope.Today = new Date();
15723        $scope.SurveySectionTitlesListForSummary = [];
15724
15725        //extract survey css and data
15726        $scope.surveySectionHTML = [];
15727        $scope.surveyCSS = '';
15728
15729        $scope.getPrivacyUrl = getPrivacyUrl;
15730
15731        $scope.$on('language-change', function (event, key) {
15732
15733            $scope.language = key;
15734        });
15735
15736        var inSurveyId = parseInt($routeParams.surveyId);
15737
15738        function ChangeDateFormat(input) {
15739            const parts = input.match(/(\d+)/g);
15740            return new Date(parts[0], parts[1] - 1, parts[2]);
15741        }
15742
15743        function getPrivacyUrl(language) {
15744            return requestedLanguageService.getPrivacyUrl(language);
15745        }
15746
15747        function calculateAge(val) {
15748            const formDate = new ChangeDateFormat(val);
15749            const todayDate = new Date();
15750            const diff = todayDate - formDate;
15751            const age = Math.floor(diff / 31536000000);
15752            return age;
15753        }
15754
15755        memberDataService.GetDemographics().success(function (data) {
15756            $scope.demo = data;
15757            $scope.DateOfBirth = $scope.demo.DateOfBirth;
15758            $scope.formattedDOB = new Date($scope.DateofBirth);
15759            $scope.Age = calculateAge($scope.demo.DateOfBirth);
15760        });
15761
15762        const language = requestedLanguageService.getCurrentLanguage();
15763
15764        memberDataService.getCurrentEligibility(language).success(function (data) {
15765            $scope.Gender = data[0].Gender;
15766        });
15767
15768        $scope.redirectHome = function () {
15769            console.log("redirect home");
15770
15771            const link = 'App/HealthCare/HASurvey/History';
15772            $location.path(link);
15773        };
15774
15775
15776        /*************************************************NAVIGATION METHODS****************************************************/
15777        $scope.nextClicked = false;
15778
15779        $scope.getHaSurveyToken = function () {
15780            var haSurveyId = $('meta[name="haSurveyId"]').attr('content');
15781
15782            const payload = {
15783                surveyId: haSurveyId,
15784                firstName: $scope.demo.FirstName,
15785                lastName: $scope.demo.LastName,
15786                email: $scope.demo.EmailAddress,
15787                language: $scope.language,
15788                createNew: true
15789            };
15790
15791            memberDataService.GetHaSurveyToken(payload).success(function (resp) {
15792                const appBase = $('meta[name="reverseProxyServeysUrl"]').attr('content');
15793
15794                const url = appBase +
15795                    'index.php?r=survey/index&sid=' +
15796                    haSurveyId +
15797                    '&token=' +
15798                    resp.token +
15799                    '&newtest=Y';
15800
15801                window.open(url, '_blank');
15802
15803            }).error(function (error) {
15804                console.error('getHaSurveyToken', error);
15805            });
15806        };
15807
15808        //finish survey
15809        $scope.PrintLogoUrl = $('meta[name="ApplicationRoot"]').attr('content') + '/Content/Images/IEHP_LOGO_Survey.png';
15810
15811        $scope.FinishSurvey = function (isFormValid) {
15812            //save last survey question
15813            $scope.nextClicked = true;
15814            if ($scope.sectionOn === ($scope.totalSections - 1) && isFormValid) {
15815                $scope.surveySectionRecieved = false;
15816                let nextSection = $scope.ActiveSection.SectionId;
15817                nextSection++;
15818                $scope.SurveySection.IsCompleted = true;
15819
15820                memberDataService.SaveSurveySectionAnswers($scope.SurveySection).success(function () {
15821                    $scope.sectionOn++;
15822                    $scope.ActiveSectionName = 'Summary';
15823
15824                    const params = {
15825                        "SurveyId": inSurveyId,
15826                        "Language": $scope.language,
15827
15828                        "RespondentTransId": $scope.SurveySection.RespondingUser.RespondentTransId,
15829                        "RespondentTypeId": $scope.SurveySection.RespondingUser.RespondentTypeId
15830                    };
15831
15832                    memberDataService.requestSurveySummary(params).success(function (data) {
15833                        $scope.SurveySummaryInformation = formatSummarySections(data);
15834                        $scope.SurveyIsFinshed = true;
15835                        $('html, body').animate({ scrollTop: 0 });
15836                    }).error(function (error) {
15837                        console.log(error);
15838                        $scope.SurveyError = true;
15839                        $scope.ErrorMsg = error;
15840                    });
15841                }).error(function (error) {
15842                    $scope.SurveyError = true;
15843                    console.log(error);
15844                    $scope.ErrorMsg = error;
15845                });
15846            } else {
15847                $scope.SurveyIsFinshed = false;
15848            }
15849        };
15850
15851        /************************Print Summary************************/
15852        $scope.PrintSummary = function () {
15853            const documentContainer = document.getElementById('divtoprint');
15854            const windowObject = window.open('', 'PrintWindow');
15855            windowObject.document.writeln(
15856                '<style> ' +
15857                '.survey-summary-history-info {text-align:center;font-weight:bold;margin:0;color: #00477E;}' +
15858                '.survey-completed-date{text-align:center; font-size:12px;}' +
15859                '.survey-print-logo{height:70px;}
15859' +
15860                '.survey-summary-list {list-style-type: none;padding: 10px;}' +
15861                '.survey-summary-history-indent {padding-left: 18px;}' +
15862                '.survey-summary-history-name {margin: 0px 0px 15px;}' +
15863                '.non-printable{display:none;}' +
15864                '.survey-print-logo-wrapper{text-align:center;margin-bottom:8px!important;}' +
15865
15866                '@media print {' +
15867                'p {font-size: 10px; text-align:center; }' +
15868                '.non-printable {display: none;}' +
15869                '.survey-summary-history-indent{padding-left:20px;}' +
15870                '.survey-print-logo{height:55px;}' +
15871                '.survey-print-logo-wrapper{margin-bottom:8px!important;}' +
15872                '.survey-summary-history-header{margin:0 !important;font-size:12px;}' +
15873                '.survey-summary-list{list-style:none;}' +
15874                '.survey-hr-non-printable {display: none;}' +
15875                '.survey-summary-history-info{margin-bottom:8px;text-align:center !important;font-size:14px;}' +
15876                '.center-span{display:none;}' +
15877                '.survey-summary-history-labels{ font-size:10;font-weight:300;margin-bottom:5px;}' +
15878                '.survey-completed-date{text-align:center; font-size:12px;}' +
15879                '.survey-list-no-bullet { list-style:none;}' +
15880                '</style>' +
15881
15882                documentContainer.innerHTML);
15883            windowObject.document.close();
15884            windowObject.focus();
15885            windowObject.print();
15886            windowObject.close();
15887        };
15888        /*******************************************DEBUG FUNCTIONALITY******************************************************************/
15889
15890        $scope.PrintSurvey = function () {
15891            console.log($scope.SurveySection);
15892        };
15893
15894        $scope.PrintSurveyCopy = function () {
15895            console.log($scope.ActiveSection);
15896        };
15897    };
15898})();
15899(function () {
15900    'use strict';
15901
15902    angular
15903        .module('MemberPortal')
15904        .controller('InterpreterRequestsController', InterpreterRequestsController);
15905    //////////////////////////
15906
15907    InterpreterRequestsController.$inject = [
15908        '$scope', '$route', '$translate', 'memberDataService', '$filter',
15909        'requestedLanguageService', 'iFormioService', '$timeout', 'authStatusService'
15910    ];
15911
15912    function InterpreterRequestsController($scope, $route, $translate, memberDataService, $filter,
15913        requestedLanguageService, iFormioService, $timeout, authStatusService) {
15914
15915        $scope.getSource = getSource;
15916        $scope.changeProvider = changeProvider;
15917        $scope.backToList = backToList;
15918
15919        $scope.resetFilter = resetFilter;
15920        $scope.fromDateChange = fromDateChange;
15921        $scope.toDateChange = toDateChange;
15922
15923        $scope.filterData = filterData;
15924        $scope.showProvidersListModal = showProvidersListModal;
15925        $scope.cancelRequest = cancelRequest;
15926        $scope.showProvidersList = showProvidersList;
15927
15928        $scope.cancelAppointment = cancelAppointment;
15929
15930        $scope.closeProviderModal = closeProviderModal;
15931        $scope.selectProvider = selectProvider;
15932        $scope.closeCancelAppointment = closeCancelAppointment;
15933
15934        $scope.rescheduleAppointment = rescheduleAppointment;
15935
15936        $scope.expectedStartTimeChange = expectedStartTimeChange;
15937        $scope.closeRescheduleAppointment = closeRescheduleAppointment;
15938        $scope.rescheduleInterpreterRequest = rescheduleInterpreterRequest;
15939
15940        $scope.cancelReasons = [];
15941
15942        $scope.showProvidersListOnEnter = showProvidersListOnEnter;
15943        $scope.pageLimitChange = pageLimitChange;
15944        $scope.getStatusIcon = getStatusIcon;
15945
15946        $scope.getStatus = getStatus;
15947        $scope.expandCollapse = expandCollapse;
15948
15949        $scope.isSubmissionEditable = isSubmissionEditable;
15950        $scope.requestState = requestState;
15951
15952        $scope.addNewRequestChange = addNewRequestChange;
15953        $scope.back = back;
15954
15955        $scope.skipToFirst = skipToFirst;
15956        $scope.skipToLast = skipToLast;
15957        $scope.prevPage = prevPage;
15958        $scope.nextPage = nextPage;
15959
15960        init();
15961
15962        function init() {
15963
15964            $scope.isProxied = authStatusService.checkIfProxied();
15965
15966            $scope.orderByField = 'modified';
15967            $scope.reverseSort = true;
15968            $scope.sortArrowDirection = 'down';
15969            $scope.sort = sort;
15970
15971            $scope.showIFormio = false;
15972
15973            $scope.addNewRequest = false;
15974
15975            $scope.providersList = [];
15976            $scope.selectedProvder = "";
15977
15978            $scope.myDate = new Date();
15979            $scope.isOpen = false;
15980
15981            //================= PAGING ============//
15982            $scope.itemsPerPage = "20";
15983            $scope.currentMediTracPage = 0;
15984            $scope.InterpreterRequests = [];
15985            $scope.InterpreterRequestsClone = [];
15986
15987            $scope.searchPageNumber = $scope.currentMediTracPage;
15988            $scope.searchRowsPerPage = parseInt($scope.itemsPerPage);
15989
15990            $scope.activeDetails = null;
15991
15992            $scope.currentLanguage = null;
15993
15994            iFormioService.setElementId('iformio-iframe-interpreter-service');
15995            iFormioService.setIFormioParams('interpreterrequest', 'member-portal-new');
15996
15997            $scope.iFormioSrc = iFormioService.getIFormioSrc(false);
15998
15999            $scope.filteredProvider = [];
16000
16001            $scope.showInterpreterCancelSuccessMsg = false;
16002            $scope.inititalFormData = null;
16003
16004            $scope.fromDateFilter = '';
16005            $scope.toDateFilter = '';
16006
16007            $scope.minDate = '';
16008            $scope.maxDate = null;
16009
16010            $scope.CurrentEligibility = {};
16011            $scope.isAgeOver18 = false;
16012
16013            $scope.listLoading = false;
16014
16015            getCurrentEligibility();
16016            getCancelReasons();
16017
16018            $scope.getCreatedSource=getCreatedSource;
16019
16020            $scope.showCancelLoading = false;
16021            $scope.errorMsg = '';
16022
16023            $scope.loading = false;
16024            $scope.noRecordsMessage = null;
16025
16026            $scope.expectedStartDate = '';
16027            $scope.expectedStartTime = '';
16028            $scope.todayDate = '';
16029
16030            $scope.activeProvider = {};
16031
16032            $scope.showRescheduleLoading = false;
16033            $scope.showInterpreterRescheduleSuccessMsg = false;
16034            $scope.errorMessage = '';
16035
16036            $scope.selectedStatus = "all";
16037
16038            $scope.isAppointmentWithin5Days = false;
16039
16040            $scope.modalMsg = '';
16041        }
16042
16043        function populateForm() {
16044
16045            $scope.iFormioListener = iFormioService.iFormioListener;
16046            $scope.iFormioAppInitialized = function (e) {
16047
16048                iFormioService.setIFormioEvents({
16049                    submit: true
16050                });
16051                let formData = {}
16052                if ($scope.activeProvider.provider && $scope.activeProvider.address) {
16053                    const location = $scope.activeProvider?.address;
16054                    formData = {
16055                        authorization: authStatusService.getStatus().access_token,
16056                        provider: {
16057                            address:
16058                                (location.address.line1 || '') +
16059                                (location.address.line2 ? ', ' + location.address.line2 : ''),
16060                            city: location?.address?.city,
16061                            email: $scope.activeProvider.provider.email,
16062                            fax: location?.clns?.fax,
16063                            locationId: location.cl.id ? (location.cl.id) : 0,
16064                            npi: $scope.activeProvider.provider.identity?.npi,
16065                            phone: location.clns.phone,
16066                            state: location?.address?.state,
16067                            taxId: location.clns.tin[0].value,
16068                            zip: location?.address?.zip,
16069                            name: ($scope.activeProvider.provider.name.first || '') + ' ' + ($scope.activeProvider.provider.name.last || ''),
16070                            firstName: $scope.activeProvider.provider.name.first || '',
16071                            lastName: $scope.activeProvider.provider.name.last || '',
16072                        },
16073                        member: {
16074                            memberId: $scope.CurrentEligibility.MemberNumber,
16075                            firstName: $scope.CurrentEligibility.FirstName,
16076                            lastName: $scope.CurrentEligibility.LastName,
16077                            dateOfBirth: $scope.CurrentEligibility.DateOfBirth,
16078                            enrollmentDate: '',
16079                            ipaCode: $scope.CurrentEligibility.Ipa,
16080                            ipaName: $scope.CurrentEligibility.IpaDescription,
16081                            lob: $scope.CurrentEligibility.Lob,
16082                            lobDescription: $scope.CurrentEligibility.LobDescription,
16083                            gender: $scope.CurrentEligibility.Gender,
16084                            address1: $scope.CurrentEligibility.Address1,
16085                            address2: $scope.CurrentEligibility.Address2,
16086                            city: $scope.CurrentEligibility.City,
16087                            state: $scope.CurrentEligibility.State,
16088                            zip: $scope.CurrentEligibility.Zip,
16089                        },
16090                        submitter: {
16091                            name: $scope.CurrentEligibility.MemberNumber
16092                        },
16093                        durationHours: "01",
16094                        minutes: "00"
16095                    }
16096                }
16097
16098                const currentTranslationServiceLanguage = $translate.proposedLanguage() || $translate.use();
16099                const currentLanguageServiceLanguage = requestedLanguageService.getCurrentLanguage();
16100                // $translate language is not set initially so may have to use cookie language
16101                // If $translate language is set, use that to avoid issues with other tabs setting the cookie language
16102                $scope.currentLanguage = currentTranslationServiceLanguage ? currentTranslationServiceLanguage : currentLanguageServiceLanguage;
16103                formData.formLanguage = $scope.currentLanguage;
16104                formData.createdSource = "member-portal";
16105                $scope.inititalFormData=formData;
16106                iFormioService.setSubmission(formData);
16107                iFormioService.setShowForm(true, true);
16108                iFormioService.setLanguage(formData.formLanguage);
16109            };
16110
16111            $scope.formSuccessPage = false;
16112
16113            $scope.iFormioSubmit = function (e) {
16114                $timeout(() => {
16115                    $scope.inititalFormData = e.detail.data;
16116                    $scope.formSuccessPage = true;
16117                });
16118
16119                window.scrollTo({ top: 0, behavior: 'smooth' });
16120            };
16121
16122            window.addEventListener('message', $scope.iFormioListener);
16123            window.addEventListener('iFormio-appInitialized', $scope.iFormioAppInitialized);
16124            window.addEventListener('iFormio-submit', $scope.iFormioSubmit);
16125
16126            $timeout(function () {
16127                $scope.showIFormio = true;
16128            });
16129        };
16130
16131        $scope.toggleCancelDropdown = function () {
16132            document.getElementById("interpreter-cancellation-dd").classList.toggle("show");
16133        }
16134
16135        // Close the dropdown if the user clicks outside of it
16136        document.addEventListener('click', function (event) {
16137            const isDropdownButton = event.target.closest('.interpreter-cancellation-dropdown-btn');
16138
16139            // If the clicked element is NOT the dropdown button
16140            if (!isDropdownButton) {
16141                document.querySelectorAll('.interpreter-cancellation-items.show')
16142                    .forEach(dropdown => dropdown.classList.remove('show'));
16143            }
16144        });
16145
16146
16147        $scope.$on('language-change', function (event, key) {
16148            $scope.currentLanguage = key;
16149            iFormioService.setLanguage($scope.currentLanguage);
16150        });
16151
16152        function getSource(value) {
16153            switch (value) {
16154                case 'member360':
16155                    return 'Member Services';
16156                case 'provider-portal':
16157                    return value.provider ? value.provider.name : "";
16158                case 'member-portal':
16159                    return value.member ? value.member.memberId : "";
16160            }
16161        };
16162
16163        $scope.clinicNameCache = {};
16164
16165        function fetchClinicName(npi, providerName) {
16166            if (!npi || $scope.clinicNameCache.hasOwnProperty(npi)) return;
16167            $scope.clinicNameCache[npi] = '';
16168
16169            const providerSearchBase = $('meta[name="providerSearchBaseUrl"]').attr('content');
16170            fetch(providerSearchBase + '_search', {
16171                method: 'POST',
16172                headers: {
16173                    'Content-Type': 'application/json',
16174                    'Authorization': authStatusService.getStatus().access_token,
16175                },
16176                body: JSON.stringify({
16177                    searchString: npi,
16178                    pageNumber: 1,
16179                    recordsPerPage: 1000,
16180                    type: '[Interpreter Services] Fetch Providers List'
16181                })
16182            })
16183                .then(response => response.json())
16184                .then(data => {
16185                    const match = (data.list || []).find(p => p.provider?.identity?.npi === npi);
16186                    $scope.clinicNameCache[npi] = match?.provider?.locations?.[0]?.medicalgroup?.name || '';
16187                    $scope.$apply();
16188                })
16189                .catch(error => console.error('fetchClinicName error:', error));
16190        }
16191
16192        function getCreatedSource(createdSource, item) {
16193            const sources = (createdSource || '').split(',').map(s => s.trim());
16194
16195            switch (sources[0]) {
16196                case 'bulk-upload-MSR':
16197                case 'member360':
16198                    return 'Member Services';
16199
16200                case 'bulk-upload-Provider':
16201                    return sources[1] || '';
16202
16203                case 'member-portal':
16204                case 'Member- Medi-Cal IVA':
16205                case 'Member- Medi-Cal IVR':
16206                    return item.data?.member?.memberId || 'member-portal';
16207
16208                case 'provider-portal': {
16209                    const npi = item.data?.provider?.npi || '';
16210                    const providerName = item.data?.provider?.name || '';
16211                    fetchClinicName(npi, providerName);
16212                    return $scope.clinicNameCache[npi] || '';
16213                }
16214
16215                default:
16216                    return createdSource;
16217            }
16218        }
16219
16220
16221        function changeProvider(value) {
16222            $scope.selectedProvder = value;
16223        }
16224
16225        $scope.cancelReason='';
16226
16227        $scope.cancelAppReason = function (value) {
16228            $scope.cancelReason = value;
16229        }
16230
16231
16232        function backToList() {
16233            getFormioSubmissions();
16234            $scope.formSuccessPage = false;
16235            $scope.addNewRequest = false;
16236            $scope.activeProvider = null;
16237            $scope.showIFormio = false;
16238            $scope.showInterpreterCancelSuccessMsg = false;
16239            $scope.showInterpreterRescheduleSuccessMsg = false;
16240        }
16241
16242        function resetFilter(val) {
16243            $scope.fromDateFilter = '';
16244            $scope.toDateFilter = '';
16245            $scope.selectedProvder = "";
16246            $scope.selectedStatus = "all";
16247            $scope.PagedMediTracClaims = pageData($scope.InterpreterRequestsClone);
16248            $scope.showInterpreterCancelSuccessMsg = false;
16249            $scope.showInterpreterRescheduleSuccessMsg = false;
16250        }
16251
16252        function fromDateChange(val) {
16253            $scope.fromDateFilter = val;
16254            if ($scope.fromDateFilter) {
16255                $scope.minDate = $scope.fromDateFilter.toISOString().slice(0, 10);
16256
16257            }
16258        }
16259
16260        function toDateChange(value) {
16261            $scope.toDateFilter = value;
16262            if ($scope.toDateFilter) {
16263                $scope.maxDate = $scope.toDateFilter.toISOString().slice(0, 10);
16264            }
16265        }
16266
16267        function filterData() {
16268            const fromDate = $scope.fromDateFilter;
16269            const toDate = $scope.toDateFilter;
16270            const startDate = new Date(fromDate);
16271            const endDate = new Date(toDate);
16272            let data = [];
16273            if ($scope.selectedProvder.length) {
16274                data = $scope.InterpreterRequestsClone.filter((item) => item.data.provider.npi === JSON.parse($scope.selectedProvder).npi);
16275
16276            }
16277            let filteredData = data;
16278            if (fromDate && toDate) {
16279                filteredData = data.filter(item => {
16280                    const itemDateUTC = new Date(item.data.expectedStartDate);
16281                    const itemDateStringUTC = itemDateUTC.toISOString().slice(0, 10);
16282                    const startDateStringUTC = startDate.toISOString().slice(0, 10);
16283
16284                    const endDateStringUTC = endDate.toISOString().slice(0, 10);
16285                    return itemDateStringUTC >= startDateStringUTC && itemDateStringUTC <= endDateStringUTC;
16286                });
16287            }
16288            $scope.selectedStatus = "all";
16289            $scope.PagedMediTracClaims = pageData(filteredData);
16290        }
16291
16292        function showProvidersListModal() {
16293            $('#showProviders').modal('show');
16294        }
16295
16296        function getCurrentEligibility() {
16297            getFormioSubmissions();
16298
16299            const language = requestedLanguageService.getCurrentLanguage();
16300
16301            memberDataService.getCurrentEligibility(language).success(function (data) {
16302
16303                const currentEligibility = memberDataService.utility.getPrimaryEligibilityRecord(data);
16304
16305                $scope.CurrentEligibility = memberDataService.utility.formatAddress(currentEligibility);
16306
16307                $scope.isAgeOver18 = checkAgeIsGreaterThan18($scope.CurrentEligibility.DateOfBirth);
16308
16309
16310            }).error(function () {
16311                console.log($filter('translate')('form_unableToRetrieveData'));
16312            });
16313        };
16314
16315        function checkAgeIsGreaterThan18(date) {
16316            const dob = new Date(date);
16317            const now = new Date();
16318            let age = now.getFullYear() - dob.getFullYear();
16319            const monthDiff = now.getMonth() - dob.getMonth();
16320            if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < dob.getDate())) {
16321                age--;
16322            }
16323            console.log(age);
16324            return age >= 18;
16325        }
16326
16327        $scope.showCancelLoading = false;
16328         $scope.errorMsg='';
16329
16330        function cancelRequest(value) {
16331            if($scope.cancelReason===''){  
16332                return;
16333            }
16334            const tempVar=JSON.parse(JSON.stringify(value));
16335            tempVar.data.cancelledSource = "member-portal";
16336            tempVar.data.cancelledBy = value.data.member.memberId;
16337            tempVar.data.cancelledDateTime = new Date().toISOString();
16338            tempVar.data.cancelledReasons=$scope.cancelReason.value;
16339
16340            if (tempVar.data.jobId) {
16341                tempVar.data.reasonToCancel = "Cancelled member portal";
16342                tempVar.data.providerStatus = "Approved";
16343            } else {
16344                tempVar.data.declineReason = "Cancelled member portal";
16345                tempVar.data.providerStatus = "Cancelled";
16346            }
16347
16348            const data = {
16349                data: tempVar.data,
16350                _id: value._id
16351            }
16352
16353            $scope.showCancelLoading = true;
16354            $scope.errorMsg = '';
16355
16356            const formioBase = $('meta[name="formioBase"]').attr('content');
16357            const url = formioBase + '/' + 'interpreterrequest' + '/submission/' + value._id;
16358
16359            iFormioService.getToken().then(function (resp) {
16360                iFormioService.setToken(JSON.parse(resp.data));
16361                fetch(url, {
16362                    method: 'PUT',
16363                    headers: {
16364                        'Content-Type': 'application/json',
16365                        'x-jwt-token': JSON.parse(resp.data), // Include the JWT token here
16366                    },
16367                    body: JSON.stringify(data)
16368                }).then(response => {
16369                    if (!response.ok) {
16370                        throw new Error(`HTTP error! status: ${response.status}`);
16371                    }
16372                    return response.json();
16373                })
16374                    .then(result => {
16375                        console.log('Success:', result);
16376                        value.data=tempVar.data;
16377                        window.scrollTo({ top: 0, behavior: 'smooth' });
16378                        $scope.showCancelLoading = false;
16379                        $timeout(() => {
16380                            $scope.showInterpreterCancelSuccessMsg = true;
16381                        });
16382                        $scope.closeCancelAppointment();
16383
16384                    })
16385                    .catch(error => {
16386                        $scope.errorMsg = 'error_occurred';
16387                        $scope.showCancelLoading = false;
16388                        console.error('Error:', error);
16389                    });
16390
16391            }).catch(function (e) {
16392                console.log(e);
16393                deferred.reject(e);
16394                $scope.errorMsg = 'error_occurred';
16395                $scope.showCancelLoading = false;
16396            });
16397        }
16398
16399        function showProvidersList(value) {
16400
16401            if (value?.trim()?.length) {
16402                $scope.loading = true;
16403                const payloadData = {
16404                    "searchString": value,
16405                    "pageNumber": 1,
16406                    "recordsPerPage": 1000,
16407                    "type": "[Interpreter Services] Fetch Providers List"
16408                };
16409
16410                memberDataService.GetSubmittingProviderListInterpreter(payloadData)
16411                    .success(function (data) {
16412                        $scope.noRecordsMessage = null;
16413                        $scope.providersList = [];
16414                        data.list.forEach((item) => {
16415                            if (item.provider.identity.npi) {
16416                                item.provider.locations = item.provider.locations.filter((item) => item.clns && item.clns.tin && item.clns.tin[0].value);
16417                                if (item.provider.locations.length) {
16418                                    $scope.providersList.push(item);
16419                                }
16420                            }
16421                        });
16422
16423                        if ($scope.providersList.length === 0) {
16424                            $scope.noRecordsMessage = 'no_providers_found_message_interpreter';
16425                        }
16426
16427                        $scope.loading = false;
16428                    }).error(function (data) {
16429                        console.log('ERROR: Unfortunately we were not able to retrieve data at this time. Please try again in a few minutes.')
16430
16431                    })
16432                    .finally(function () {
16433                        $scope.loadingProviders = false;
16434                    });
16435            }
16436        }
16437
16438        function cancelAppointment(value) {
16439            if ($scope.hasDateTimePassed(value.data)) {
16440                $('#interpreterFormSubmit').modal('show');
16441                return;
16442            }
16443            $scope.activeDetails = value;
16444            $scope.errorMsg='';
16445            $scope.cancelReason='';
16446            $('#cancelAppointment').modal('show');
16447        }
16448
16449        function closeProviderModal() {
16450
16451            $('#showProviders').modal('hide');
16452            $scope.providersList = []
16453            $scope.RequestingProviderfilter = null;
16454        }
16455
16456        function selectProvider(provider, address) {
16457
16458            $scope.showIFormio = false;
16459            $scope.activeProvider = provider;
16460            $scope.activeProvider.address = address;
16461            populateForm();
16462
16463            closeProviderModal();
16464        }
16465
16466        function getCancelReasons() {
16467
16468            var dmsGateWayBaseUrl = $('meta[name="dmsGateWayBaseUrl"]').attr('content');
16469            const url = dmsGateWayBaseUrl+'generic-formio-ws/interpretercancelreaosns/submission?select=data&data.source=member-portal-new'
16470            fetch(url, {
16471                method: 'GET',
16472                headers: {
16473                    'Content-Type': 'application/json',
16474                    'Authorization': authStatusService.getStatus().access_token,
16475                },
16476            })
16477                .then(response => response.json())
16478                .then(data => {
16479                    $scope.cancelReasons = data.filter(item => item.data.source === "member-portal-new").map(item => item.data);
16480                })
16481                .catch(error => console.error('Error:', error))
16482        }
16483
16484        function closeCancelAppointment() {
16485
16486            $scope.activeDetails = null;
16487            $('#cancelAppointment').modal('hide');
16488        }
16489
16490        $scope.hasDateTimePassed = (data) => {
16491            let datePortion = '';
16492            let timePortion = '';
16493
16494            if (data.bookingList && data.bookingList.length) {
16495                datePortion = data.bookingList[0].expectedStartDate.split('T')[0];
16496                timePortion = data.bookingList[0].expectedStartTime;
16497            }
16498            datePortion = data.expectedStartDate.split('T')[0];
16499            timePortion = data.expectedStartTime;
16500            const combinedDateTime = datePortion + 'T' + timePortion;
16501            if (isWithinNext24Hours(combinedDateTime)) {
16502                $scope.modalMsg = "your_appointment_within_hours_message";
16503                return true;
16504            }
16505            const currentDateTime = new Date();
16506            const targetDateTime = new Date(combinedDateTime);
16507            $scope.modalMsg = "appointment_date_passed_message";
16508            return currentDateTime > targetDateTime;
16509        }
16510
16511        function isWithinNext24Hours(dateTime) {
16512            const now = new Date();
16513            const next24Hours = new Date(now.getTime() + 24 * 60 * 60 * 1000);
16514            const inputDate = new Date(dateTime);
16515
16516            return inputDate >
16516 now && inputDate <= next24Hours;
16517        }
16518
16519        function rescheduleAppointment(value) {
16520
16521            if ($scope.hasDateTimePassed(value.data)) {
16522                $('#interpreterFormSubmit').modal('show');
16523                return;
16524            }
16525
16526            $scope.showInterpreterRescheduleSuccessMsg = false;
16527            $scope.showInterpreterCancelSuccessMsg = false;
16528            $scope.activeDetails = value;
16529            $scope.expectedStartDate = new Date(value.data.expectedStartDate);
16530
16531            const parts = value.data.expectedStartTime.split(":");
16532
16533            const date = new Date();
16534            date.setHours(parts[0]);
16535            date.setMinutes(parts[1]);
16536            date.setSeconds(parts[2]);
16537            date.setMilliseconds(0);
16538
16539            $scope.expectedStartTime = date;
16540            $scope.errorMsg='';
16541
16542            $('#reScheduleAppointment').modal('show');
16543
16544            const today = new Date();
16545            const minDate = today.toISOString().split('T')[0];
16546            $scope.todayDate = minDate;
16547            $scope.isAppointmentWithin5Days = isWithinFiveDays($scope.expectedStartDate);
16548
16549            $scope.showInterpreterCancelSuccessMsg = false;
16550        }
16551
16552        $scope.expectedStartTimeChange = function (date) {
16553            $scope.expectedStartTime = date;
16554        }
16555
16556        function expectedStartTimeChange(date) {
16557            $scope.expectedStartDate = date;
16558            $scope.isAppointmentWithin5Days = isWithinFiveDays($scope.expectedStartDate);
16559            $scope.errorMessage = '';
16560            if (!$scope.expectedStartDate) {
16561                scope.isAppointmentWithin5Days = false;
16562                $scope.errorMessage = 'Please select a date.';
16563                return;
16564            }
16565        }
16566
16567        function isWithinFiveDays(date) {
16568            const today = new Date();
16569            const targetDate = new Date(date);
16570            const diffInTime = Math.abs(targetDate - today);
16571            const diffInDays = diffInTime / (1000 * 60 * 60 * 24);
16572            return diffInDays <= 5;
16573        }
16574
16575        function closeRescheduleAppointment() {
16576            $scope.activeDetails = null;
16577            $('#reScheduleAppointment').modal('hide');
16578        }
16579
16580        function rescheduleInterpreterRequest() {
16581
16582            const cloneData = JSON.parse(JSON.stringify($scope.activeDetails.data));
16583
16584            // Clear previous error message
16585            $scope.errorMessage = '';
16586
16587            // Validation: Ensure a date is selected
16588            if (!$scope.expectedStartDate) {
16589                $scope.errorMessage = 'Please select a date.';
16590                return;
16591            }
16592
16593            // Validation: Ensure a date is selected
16594            if (!$scope.expectedStartTime) {
16595                $scope.errorMessage = 'Please select a time.';
16596                return;
16597            }
16598            cloneData.expectedStartDate = $scope.expectedStartDate.toISOString();
16599            cloneData.isTriggerFromBackEnd=false;
16600            const date = new Date($scope.expectedStartTime); // Current date and time
16601            const hours = date.getHours(); // Extract hours (0-23)
16602            const minutes = date.getMinutes(); // Extract minutes (0-59)
16603
16604            // Format the time as HH:MM
16605            const time24hr = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:00`;
16606
16607            cloneData.expectedStartTime = time24hr;
16608            cloneData.providerStatus = "Awaiting Provider Confirmation";
16609            $scope.errorMsg = '';
16610
16611            //set appointment startdate time
16612            const datePart = cloneData.expectedStartDate?.substring(0, 10);
16613
16614            cloneData.appointmentStartDateTime = datePart + "T" + cloneData.expectedStartTime + "Z";
16615
16616            //set appointment end date time
16617            if(cloneData.appointmentStartDateTime &&(cloneData.durationHours && cloneData.minutes)){
16618                const startDateTime = new Date(cloneData.appointmentStartDateTime);
16619                const hh=cloneData.durationHours;
16620                const hoursToAdd=parseInt(hh) || 0;
16621                startDateTime.setHours(startDateTime.getHours()+hoursToAdd);
16622                const mm=cloneData.minutes;
16623                const minutesToAdd=parseInt(mm)||0;
16624                startDateTime.setMinutes(startDateTime.getMinutes()+minutesToAdd);
16625                cloneData.appointmentEndDateTime=startDateTime.toISOString();
16626            }
16627
16628            const data = {
16629                data: cloneData,
16630                _id: $scope.activeDetails._id
16631            }
16632
16633            $scope.showRescheduleLoading = true;
16634
16635            const formioBase = $('meta[name="formioBase"]').attr('content');
16636            const url = formioBase + '/' + 'interpreterrequest' + '/submission/' + $scope.activeDetails._id;
16637
16638            let responseStatus = '';
16639
16640            iFormioService.getToken().then(function (resp) {
16641                iFormioService.setToken(JSON.parse(resp.data));
16642                fetch(url, {
16643                    method: 'PUT',
16644                    headers: {
16645                        'Content-Type': 'application/json',
16646                        'x-jwt-token': JSON.parse(resp.data), // Include the JWT token here
16647                    },
16648                    body: JSON.stringify(data)
16649                }).then(response => {
16650                    if (!response.ok) {
16651                        responseStatus=response.status;
16652                        if(response.status===400){
16653                            $scope.errorMsg ="appointment_same_date_same_time_msg";
16654                            $scope.showRescheduleLoading = false;
16655                        }
16656                        throw new Error(`HTTP error! status: ${response.status}`);
16657                    }
16658
16659                    return response.json();
16660                })
16661                    .then(result => {
16662                        $scope.activeDetails.data= cloneData;
16663                        console.log('Success:', result);
16664                        $timeout(() => {
16665                            $scope.showInterpreterRescheduleSuccessMsg = true;
16666                        });
16667                        window.scrollTo({ top: 0, behavior: 'smooth' });
16668                        $scope.showRescheduleLoading = false;
16669
16670                        $scope.closeRescheduleAppointment();
16671                        $scope.activeDetails = null;
16672
16673
16674                    })
16675                    .catch(error => {
16676                        if(responseStatus!==400){
16677                            $scope.errorMsg = 'error_occurred';
16678                            $scope.showRescheduleLoading = false;
16679                            console.error('Error:', error);
16680                        }
16681
16682                    });
16683
16684            }).catch(function (e) {
16685                if(responseStatus!==400){
16686                    console.log(e);
16687                    deferred.reject(e);
16688                    $scope.errorMsg = 'error_occurred';
16689                    $scope.showRescheduleLoading = false;
16690                }
16691            });
16692        }
16693
16694        function showProvidersListOnEnter(event, value) {
16695            if (event.key === 'Enter') {
16696                $scope.showProvidersList(value);
16697            }
16698        };
16699
16700        function getFormioSubmissions() {
16701            const username = authStatusService.getStatus().claims.username;
16702            const sortDirection = $scope.sortArrowDirection === 'down' ? '-' : '';
16703
16704            // Build query string with template literals
16705            const params = [
16706                `data.member.memberId=${username}`,
16707                `skip=0`,
16708                `limit=9999`,
16709                `sort=${sortDirection}${$scope.orderByField}`
16710            ].join('&');
16711
16712            $scope.listLoading = true;
16713            iFormioService.getFormSubmissionsInterpreter(params).then(function (resp) {
16714                $scope.listLoading = false;
16715                // Partial Content
16716                if (resp.status === 206) {
16717                    $scope.showPaginator = true;
16718                }
16719                else {
16720                    $scope.showPaginator = false;
16721                }
16722
16723                const contentRange = resp.headers()['content-range'];
16724                const x = contentRange.split('/');
16725                let totalRecordsCount = 0;
16726                if (x && x[1]) {
16727                    totalRecordsCount = parseInt(x[1]);
16728                }
16729                $scope.total = totalRecordsCount;
16730                const list = preProcess(resp.data);
16731                if (list.length) {
16732                    list.filter((item) => {
16733                        if ($scope.filteredProvider.filter((prov) => prov.npi === item.data.provider.npi).length ===
16734                            0) {
16735                            if (item.data.provider.npi.length) {
16736                                $scope.filteredProvider.push({
16737                                    name: item.data.provider.name,
16738                                    npi: item.data.provider.npi
16739                                });
16740                            }
16741                        }
16742                    });
16743                }
16744                $scope.showInterpreterCancelSuccessMsg = false;
16745                $scope.showInterpreterRescheduleSuccessMsg = false;
16746                // $scope.filteredProvider=list.filter
16747
16748                $scope.InterpreterRequests = list;
16749                $scope.InterpreterRequestsClone = [...list];
16750                $scope.PagedMediTracClaims = pageData(list);
16751                // $scope.loading = false;
16752            }).catch(function (e) {
16753                console.log(e);
16754
16755            }).finally(function () {
16756                $scope.loading = false;
16757            });
16758        }
16759
16760        function pageLimitChange(value) {
16761            $scope.itemsPerPage = value;
16762            $scope.currentMediTracPage = 0;
16763            $scope.PagedMediTracClaims = pageData($scope.InterpreterRequestsClone);
16764        }
16765
16766        function getStatusIcon(status) {
16767            switch (status) {
16768                case "Open":
16769                case "New":
16770                case "Assigned":
16771                case "Offered":
16772                case "Awaiting Provider Confirmation":
16773                    return 'pendingIcon';
16774                case "Cancelled":
16775                    return 'cancelledIcon';
16776                case "Confirmed":
16777                    return 'confirmedIcon';
16778                case "Closed":
16779                    return 'completedIcon';
16780            }
16781        }
16782
16783        function getStatus(value) {
16784            if (!value) return value;
16785
16786            const statusMap = {
16787                'Open': "Pending",
16788                'New': "Pending",
16789                'Offered': "Pending",
16790                'Assigned': "Pending",
16791                'Closed': "Completed",
16792                "Awaiting Provider Confirmation": "Pending"
16793            }
16794
16795            for (let key in statusMap) {
16796                if (value.includes(key)) {
16797                    return value.replaceAll(key, statusMap[key]);
16798                }
16799            }
16800
16801            return value;
16802        }
16803
16804        function expandCollapse(value, index) {
16805            $scope.PagedMediTracClaims[$scope.currentMediTracPage].forEach((item) => {
16806                if (item.data !== value.data) {
16807                    item.isCollapsed = false;
16808                }
16809            });
16810
16811            value.isCollapsed = !value.isCollapsed;
16812        }
16813
16814        function isSubmissionEditable(submission) {
16815            if (
16816                submission.data.status.includes("Cancelled") ||
16817                submission.data.providerStatus.includes("Cancelled")
16818            ) {
16819                return false;
16820            }
16821            return true;
16822        }
16823
16824        function requestState(value) {
16825
16826            $scope.selectedStatus = value;
16827            let data = [];
16828            data = $scope.InterpreterRequestsClone.filter((item) =>
16829                $scope.getStatus(item.data.providerStatus.includes('Cancelled') ? 'Cancelled' : item.data.status).includes(value));
16830            if (value === 'all') {
16831                data = [...$scope.InterpreterRequestsClone];
16832            }
16833            $scope.PagedMediTracClaims = pageData(data);
16834        }
16835
16836        function addNewRequestChange(value) {
16837            $scope.addNewRequest = !$scope.addNewRequest;
16838            $scope.showInterpreterCancelSuccessMsg = false;
16839            $scope.showInterpreterRescheduleSuccessMsg = false;
16840            $scope.providersList = [];
16841            $scope.activeProvider = null;
16842            $scope.showIFormio = false;
16843            $scope.RequestingProviderfilter = null;
16844        }
16845
16846        function back(value) {
16847            $scope.addNewRequestChange();
16848        }
16849
16850        function pageData(arrayTopage) {
16851            if (!arrayTopage || !Array.isArray(arrayTopage)) {
16852                return [];
16853            }
16854
16855            // Use array slicing to create pages
16856            const pagedData = [];
16857            for (let i = 0; i < arrayTopage.length; i += parseInt($scope.itemsPerPage)) {
16858                pagedData.push(arrayTopage.slice(i, i + parseInt($scope.itemsPerPage)));
16859            }
16860            return pagedData;
16861        };
16862
16863        function skipToFirst(currentPageVar) {
16864            $scope[currentPageVar]=0; 
16865        };
16866
16867        function skipToLast(pagedData, currentPageVar) {
16868             $scope[currentPageVar]= pagedData.length - 1;
16869        };
16870
16871        function prevPage(currentPageVar) {
16872            if ($scope[currentPageVar] > 0) {
16873                $scope[currentPageVar]--; // Decrement the specific current page variable
16874            }
16875        };
16876
16877        function nextPage(pagedData, currentPageVar) {
16878            if ($scope[currentPageVar] < pagedData.length - 1) {
16879                $scope[currentPageVar]++; // Increment the specific current page variable
16880            }
16881        };
16882
16883        function sort(field) {
16884            $scope.orderByField = field; // Set the orderBy field
16885            $scope.reverseSort = !$scope.reverseSort // Reverse sort direction
16886            $scope.sortArrowDirection = $scope.reverseSort ? 'down' : 'up'; // Update the sorting icon direction
16887            getFormioSubmissions();
16888
16889        }
16890
16891        function preProcess(data) {
16892            // Sort the filtered data by EffectiveDate ascendingly 
16893            //data.sort((a, b) => moment(a.data.expectedStartDate) - moment(b.data.expectedStartDate));
16894
16895            return data;
16896        }
16897
16898        $scope.$on('language-change', function (event, key) {
16899            $scope.currentLanguage = key;
16900            iFormioService.setLanguage($scope.currentLanguage);
16901        });
16902    };
16903})();
16904(function () {
16905    'use strict';
16906
16907    angular
16908        .module('MemberPortal')
16909        .controller('ChangeDoctorController', ChangeDoctorController);
16910
16911    ChangeDoctorController.$inject = [
16912        '$scope',
16913        '$rootScope',
16914        'memberDataService',
16915        '$filter',
16916        'requestedLanguageService',
16917        'utilitiesService',
16918        '$timeout'
16919    ];
16920
16921    function ChangeDoctorController(
16922        $scope,
16923        $rootScope,
16924        memberDataService,
16925        $filter,
16926        requestedLanguageService,
16927        utilitiesService,
16928        $timeout) {
16929
16930        var providerNearTimeout = $timeout(function () {
16931            $('#providersNear').focus();
16932        }, 500);
16933
16934        var providerSearchResultsTimeout;
16935
16936        init();
16937
16938        function init() {
16939            $scope.loading = false;
16940            $scope.searchParam = {};
16941            $scope.searchParamString = '';
16942            $scope.searchResults = {};
16943            $scope.selectedResults = [];
16944            $scope.searchResultsString = '';
16945            $scope.searchParameters = {};
16946            $scope.SubscriberNumber = '';
16947            $scope.searchedRadius = '';
16948            $scope.errorMessage = '';
16949            $scope.infoMessage = '';
16950            $scope.successMessage = '';
16951            $scope.MemberAge = '';
16952            $scope.MemberLob = '';
16953            $scope.changeAllowed = true;
16954            $scope.IpaList = [];
16955            $scope.ShowOpenAccessMessage = false;
16956            $scope.mapsUnavailable = false;
16957
16958            // Google Maps is a hard dependency of this page (geocoding + results map).
16959            // If it is not loaded or fails to authorize, block the tool with a friendly message.
16960            if (!googleMapsLoaded() || window.iehpGoogleMapsAuthFailed) {
16961                blockForMapsUnavailable('Google Maps not loaded or authorization failed');
16962            } else if (typeof window.iehpOnGoogleMapsAuthFailure === 'function') {
16963                // Auth failures are reported asynchronously after the script loads.
16964                const unsubscribe = window.iehpOnGoogleMapsAuthFailure(function () {
16965                    blockForMapsUnavailable('Google Maps authorization failed');
16966                });
16967                $scope.$on('$destroy', unsubscribe);
16968            }
16969
16970            getAllIpa();
16971
16972            getEligibility();
16973        }
16974
16975        function googleMapsLoaded() {
16976            return !!(window.google && window.google.maps && typeof window.google.maps.Geocoder === 'function');
16977        }
16978
16979        function blockForMapsUnavailable(reason) {
16980            console.warn('Change Doctor blocked: ' + reason);
16981            $scope.mapsUnavailable = true;
16982            $scope.loading = false;
16983            $scope.searchResults = [];
16984            $scope.selectedResults = [];
16985            // May be invoked from a non-Angular callback (Google auth failure / geocoder).
16986            if (!$scope.$$phase && !$scope.$root.$$phase) { $scope.$apply(); }
16987        }
16988
16989        function getEligibility() {
16990
16991            const language = requestedLanguageService.getCurrentLanguage();
16992
16993            memberDataService.getEligibilityByDateOfService(language)
16994                .success(function (data) {
16995
16996                    $scope.member = memberDataService.utility.getPrimaryEligibilityRecord(data);
16997                    console.log($scope.member);
16998
16999                    // No usable eligibility record: show the standard error instead of throwing.
17000                    if (!$scope.member) {
17001                        $scope.errorMessage = $filter('translate')('error_occurred');
17002                        $scope.changeAllowed = false;
17003                        return;
17004                    }
17005
17006                    // Compose the address from the parts that exist; members can have null
17007                    // address fields, and geocoding a string with "null" in it returns garbage.
17008                    $scope.providersNear = [
17009                        $scope.member.Address1,
17010                        $scope.member.City,
17011                        $scope.member.State,
17012                        String($scope.member.Zip || '').substring(0, 5)
17013                    ].filter(function (part) {
17014                        return part && String(part).trim();
17015                    }).join(' ');
17016
17017                    $scope.MemberAge = utilitiesService.determineAge($scope.member.DateOfBirth);
17018
17019                    $scope.MemberLob = $scope.member.Lob;
17020
17021                    if ($scope.member.Ipa &&
17022                        $scope.member.Ipa.toLowerCase().indexOf('j') !== -1) {
17023                        //Is direct, set ipa dropdown to 'jjj'
17024                        $scope.providerIpa = { Code: 'JJJ' };
17025                    } else {
17026                        //Set ipa dropdown to match members ipa
17027                        $scope.providerIpa = { Code: $scope.member.Ipa };
17028                    }
17029                    if ($scope.member.LobDescription === 'Medi-Medi') {
17030
17031                        $scope.errorMessage = $filter('translate')('medi_change_pcp_error');
17032
17033                        $scope.changeAllowed = false;
17034                    }
17035                    else if ($scope.member.LobDescription === 'Open Access') {
17036
17037                        $scope.ShowOpenAccessMessage = true;
17038                        $scope.changeAllowed = false;
17039                    } else {
17040                        // Auto-search on load; with no usable address, search() uses the default center.
17041                        $scope.search();
17042                    }
17043                }).error(function () {
17044                    $scope.member = null;
17045                    $scope.errorMessage = $filter('translate')('error_occurred');
17046                });
17047        }
17048
17049        function getAllIpa() {
17050            memberDataService.getAllIpas().success(function (data) {
17051                if ($rootScope.isCCA) {
17052                    $scope.IpaList = data.filter(ipa => ipa.Code === 'JJJ');
17053                } else {
17054                    $scope.IpaList = data;
17055                }
17056            }).error(function () {
17057                $scope.IpaList = null;
17058            });
17059        }
17060        
17061        $scope.$on('pcp-selected', function () {
17062            $scope.successMessage = $filter('translate')('change_pcp_success');
17063        });
17064        
17065        $scope.$on('$destroy', function () {
17066
17067            if (providerNearTimeout) $timeout.cancel(providerNearTimeout);
17068
17069            if (providerSearchResultsTimeout) $timeout.cancel(providerSearchResultsTimeout);
17070        });
17071
17072        $scope.$on('checked-pending-pcp', function (event, isPending) {
17073
17074            $scope.changeAllowed = !isPending;
17075            console.log("$scope.changeAllowed", $scope.changeAllowed);
17076        });
17077
17078        $scope.submitForm = function () {
17079
17080            providerSearchResultsTimeout = $timeout(function () {
17081                    $('.providerSearchResults li').first().focus();
17082                },
17083                1500);
17084        };
17085
17086        $scope.search = function () {
17087            if ($scope.mapsUnavailable) { return; }
17088            $scope.loading = true;
17089
17090            const clinicTypeArray = [];
17091
17092            if ($scope.fqhc) {
17093                clinicTypeArray.push('FQHC');
17094            }
17095
17096            if ($scope.rhs) {
17097                clinicTypeArray.push('RHC');
17098            }
17099
17100            if ($scope.itc) {
17101                clinicTypeArray.push('ITC');
17102            }
17103
17104            $scope.searchParam.ClinicType = clinicTypeArray.join();
17105
17106            if ($scope.providersNear) {
17107                geocodeThenSearch($scope.providersNear);
17108            } else {
17109                searchFromDefaultLocation();
17110            }
17111        };
17112
17113        // Default center (Riverside, CA) used when no "near" address is provided.
17114        function searchFromDefaultLocation() {
17115            $scope.searchParam.CenterPoint_Lat = 33.953349; //Center of Riverside,ca
17116            $scope.searchParam.CenterPoint_Lng = -117.396156; //Center of Riverside,ca
17117            $scope.searchParam.Radius = 20;
17118            $scope.searchParam.SearchMethod = 'L';
17119            submitSearch();
17120        }
17121
17122        const GEOCODE_TIMEOUT_MS = 6000;
17123
17124        // Google does not guarantee the geocode callback fires after an auth failure
17125        // (e.g. InvalidKeyMapError). Any Maps-level failure blocks the page with a friendly
17126        // message rather than leaving the loading spinner running indefinitely.
17127        function geocodeThenSearch(address) {
17128            if (!googleMapsLoaded() || window.iehpGoogleMapsAuthFailed) {
17129                blockForMapsUnavailable('Geocoder unavailable');
17130                return;
17131            }
17132
17133            let settled = false;
17134            const settle = function (fn) {
17135                if (settled) { return; }
17136                settled = true;
17137                $timeout.cancel(geocodeTimeout);
17138                fn();
17139            };
17140            const geocodeTimeout = $timeout(function () {
17141                settle(function () { blockForMapsUnavailable('Geocode timed out'); });
17142            }, GEOCODE_TIMEOUT_MS);
17143
17144            try {
17145                const geocode = new google.maps.Geocoder();
17146
17147                geocode.geocode({
17148                    address: address
17149                }, function (results, status) {
17150                    settle(function () { handleGeocodeResult(results, status); });
17151                });
17152            } catch (e) {
17153                settle(function () { blockForMapsUnavailable('Geocoder threw: ' + e); });
17154            }
17155
17156            function handleGeocodeResult(results, status) {
17157                const GeocoderStatus = google.maps.GeocoderStatus;
17158                if (status === GeocoderStatus.OK && results && results.length) {
17159                    $scope.searchParam.CenterPoint_Lat = results[0].geometry.location.lat();
17160                    $scope.searchParam.CenterPoint_Lng = results[0].geometry.location.lng();
17161                    $scope.searchParam.Radius = $scope.Radius;
17162                    $scope.searchParam.SearchMethod = 'L';
17163                    submitSearch();
17164                } else if (status === GeocoderStatus.ZERO_RESULTS || status === GeocoderStatus.INVALID_REQUEST) {
17165                    // Address problem, not a service problem: fall back to the default center (original behavior).
17166                    searchFromDefaultLocation();
17167                } else {
17168                    // REQUEST_DENIED, OVER_QUERY_LIMIT, ERROR, UNKNOWN_ERROR: the service is unus
17168able.
17169                    blockForMapsUnavailable('Geocode failed (' + status + ')');
17170                }
17171            }
17172        }
17173
17174        function submitSearch() {
17175            $scope.infoMessage = null;
17176            $scope.errorMessage = null;
17177            $scope.searchParam.providersNear = $scope.searchForm.providersNear.$modelValue;
17178            $scope.searchParam.SubService1 = $scope.searchForm.providerSpec.$modelValue;
17179            $scope.searchParam.ProviderId = $scope.searchForm.providerId.$modelValue;
17180            $scope.searchParam.DocName = $scope.searchForm.providerLastName.$modelValue;
17181            $scope.searchParam.Gender = $scope.searchForm.providerGender.$modelValue;
17182
17183            if ($scope.searchParam.Gender === 'Varón') { $scope.searchParam.Gender = 'Male'; }
17184
17185            $scope.searchParam.Lang = $scope.searchForm.providerLanguage.$modelValue;
17186            $scope.searchParam.NewMbrs = 'true';
17187
17188            if ($scope.searchForm.providerIpa.$modelValue &&
17189                $scope.searchForm.providerIpa.$modelValue.Code) {
17190                $scope.searchParam.Ipa = $scope.searchForm.providerIpa.$modelValue.Code;
17191            } else {
17192                $scope.searchParam.Ipa = '';
17193            }
17194
17195            $scope.searchParam.Age = $scope.searchForm.MemberAge.$modelValue;
17196            $scope.searchParam.PlanType = $scope.searchForm.MemberLob.$modelValue;
17197
17198            $scope.searchParamString = JSON.stringify($scope.searchParam, null, '  ');
17199
17200            memberDataService.SearchProviders(JSON.stringify($scope.searchParam)).success(function (data) {
17201                $scope.loading = false;
17202                $scope.searchedRadius = $scope.Radius;
17203                if(data.length > 0) {
17204                    $scope.searchResults = data;
17205                    $scope.$broadcast('searchResultsReady', data);
17206                } else {            
17207                    $scope.searchResults = [];        
17208                    $scope.infoMessage = 'form_noResults'; // translation key; rendered with | translate in the view
17209                }
17210            }).error(function () {
17211                $scope.loading = false;
17212                $scope.errorMessage = $filter('translate')('error_occurred');
17213            });
17214        };
17215
17216    };
17217})();
17218(function () {
17219    'use strict';
17220
17221    angular
17222        .module('MemberPortal')
17223        .controller('MyDoctorController', MyDoctorController);
17224    //////////////////////////
17225
17226    MyDoctorController.$inject = [
17227        '$scope', '$route', 'memberDataService', '$filter', 'requestedLanguageService', '$timeout'
17228    ];
17229
17230    function MyDoctorController($scope, $route, memberDataService, $filter, requestedLanguageService, $timeout) {
17231        $scope.loading = false;
17232       
17233        $scope.data = {};
17234        $scope.primaryEligRecord = {};
17235
17236        $scope.SubscriberNumber = '';
17237        $scope.errorMessage = '';
17238        $scope.infoMessage = '';
17239
17240        var focusTimeout;
17241        
17242        $scope.getDoctor = function () {
17243
17244            $scope.loading = true;
17245
17246            const language = requestedLanguageService.getCurrentLanguage();
17247
17248            memberDataService.getCurrentEligibility(language).success(function (data) {
17249
17250                $scope.primaryEligRecord = memberDataService.utility.getPrimaryEligibilityRecord(data);
17251                $scope.loading = false;
17252                $scope.errorMessage = '';
17253
17254            }).error(function () {
17255
17256                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
17257                $scope.loading = false;
17258            });
17259        };
17260
17261        $scope.$on('obfuscation_toggled', function () {
17262
17263            $route.reload();
17264
17265        });
17266
17267        $scope.$on('destroy',
17268
17269            function () {
17270                if (focusTimeout) $timeout.cancel(focusTimeout);
17271            });
17272
17273        $scope.focusOnElement = function (elementId) {
17274
17275            focusTimeout = $timeout(function () {
17276
17277                $('#' + elementId).focus();
17278            }, 500);
17279        };
17280
17281        $scope.getDoctor();
17282
17283        $scope.focusOnElement('btnMyDoc');
17284
17285    };
17286})();
17287(function () {
17288    'use strict';
17289
17290    angular
17291        .module('MemberPortal')
17292        .controller('CurrentEligibilityController', CurrentEligibilityController);
17293
17294    CurrentEligibilityController.$inject = [
17295        '$scope', '$filter', '$route','authStatusService', 'memberDataService', 'requestedLanguageService', '$timeout', 'damSearchService'
17296    ];
17297
17298    function CurrentEligibilityController($scope, $filter, $route, authStatusService,
17299        memberDataService, requestedLanguageService, $timeout, damSearchService) {
17300
17301        $scope.loading = false;
17302        $scope.SubscriberNumber = '';
17303        $scope.CurrentEligibilityString = '';
17304
17305        $scope.CurrentEligibility = {};
17306        $scope.PlanDetails = {};
17307        $scope.errorMessage = '';
17308        $scope.planDetailsError = '';
17309
17310        const authStatus = authStatusService.getStatus();
17311
17312        $scope.isDSNP = authStatus.isDSNP;
17313        $scope.isCCA = authStatus.isCCA;
17314        $scope.isPrimary = authStatus.isPrimarySubscriber;
17315        $scope.lis = {
17316            indicatorName: "",
17317            drugTiers: []
17318        };
17319        $scope.currentYear = new Date().getFullYear();
17320
17321        $scope.eocUrl = null;
17322        $scope.sbcUrl = null;
17323        $scope.sobUrl = null;
17324
17325        $scope.showCopays = function() {
17326            var lis = $scope.lis;
17327            return  lis && lis.drugTiers && lis.drugTiers.length > 0;
17328        }
17329
17330        // for plan details accordions
17331        $scope.accordions = {
17332            isClinicOpen: false,
17333            isMentalHealthOpen: false,
17334            isLabsOpen: false,
17335            isRehabOpen: false,
17336            isEmergencyOpen: false
17337        }
17338
17339        var focusTimeout;
17340        var tooltipTimeout;
17341
17342        $scope.focusOnElement = function (elementId) {
17343            focusTimeout = $timeout(function () {
17344                $('#' + elementId).focus();
17345            }, 500);
17346        };
17347
17348        $scope.getCurrentEligibility = function () {
17349            $scope.loading = true;
17350            $scope.errorMessage = '';
17351            const language = requestedLanguageService.getCurrentLanguage();
17352            
17353            memberDataService.getCurrentEligibility(language).success(function (data) {
17354
17355                const currentEligibility = memberDataService.utility.getPrimaryEligibilityRecord(data);
17356
17357                $scope.CurrentEligibility =
17358                    memberDataService.utility.formatAddress(currentEligibility);
17359
17360                $scope.memberName = $scope.CurrentEligibility.FirstName + ' ' + $scope.CurrentEligibility.LastName;
17361
17362                //the monetary field may return "N/A"
17363                $scope.CurrentEligibility.CoPayWell = $filter('ifTextReturnNumber')($scope.CurrentEligibility.CoPayWell);
17364
17365                $scope.CurrentEligibility.CoPaySick = $filter('ifTextReturnNumber')($scope.CurrentEligibility.CoPaySick);
17366
17367                $scope.CurrentEligibility.CoPayEr = $filter('ifTextReturnNumber')($scope.CurrentEligibility.CoPayEr);
17368
17369                if (!$scope.CurrentEligibility.CoPayBrand ||
17370                    $scope.CurrentEligibility.CoPayBrand === 'N/A') {
17371                    $scope.CurrentEligibility.CoPayBrand = '$0';
17372                }
17373
17374                if (!$scope.CurrentEligibility.CoPayGeneric ||
17375                    $scope.CurrentEligibility.CoPayGeneric === 'N/A') {
17376                    $scope.CurrentEligibility.CoPayGeneric = '$0';
17377                }
17378
17379                $scope.loading = false;
17380
17381                if($scope.isCCA) {
17382                    $scope.getPlanDetails();
17383                    fetchCcaDocumentUrls($scope.CurrentEligibility.PlanCode);
17384                }
17385            }).error(function () {
17386                $scope.loading = false;
17387                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
17388            });
17389        };
17390
17391        $scope.getPlanDetails = function () {
17392            $scope.loading = true;
17393            $scope.planDetailsError = '';
17394            const language = requestedLanguageService.getCurrentLanguage();
17395            
17396            memberDataService.getPlanDetails(language).success(function (data) {
17397                $scope.PlanDetails = data;        
17398                if(!!$scope.PlanDetails.FamilyAccumulator) {
17399                    $scope.PlanDetails.FamilySpending = $scope.PlanDetails.FamilyAccumulator.InNetwork;
17400                    $scope.PlanDetails.FamilyAccumulator["isOpen"] = true;
17401                }
17402                if(!!$scope.PlanDetails.IndividualAccumulator) {
17403                    $scope.PlanDetails.IndividualSpending = $scope.PlanDetails.IndividualAccumulator.InNetwork;
17404                    $scope.PlanDetails.IndividualAccumulator["isOpen"] = true;
17405                }
17406                if(!!$scope.PlanDetails.Covered) {
17407                    $scope.PlanDetails.Covered = $scope.PlanDetails.Covered.map(c => ({...c, isOpen: false }));
17408                }
17409                $scope.loading = false;
17410            }).error(function () {
17411                $scope.planDetailsError = $filter('translate')('form_unableToRetrieveData');
17412                $scope.loading = false;
17413            });
17414        };
17415
17416        $scope.getLis = function(){
17417            const claims = authStatusService.getStatus().claims;
17418            var memberNumber = claims.username;
17419            memberDataService.getLis(memberNumber)
17420                .success((data) => {
17421                    $scope.lis = data;
17422                });
17423        }
17424
17425        function fetchCcaDocumentUrls(planCode) {
17426            var lang = requestedLanguageService.getLanguageshorthand(requestedLanguageService.getCurrentLanguage());
17427            var year = new Date().getFullYear();
17428            var basePath = '/content/dam/iehp-org/' + lang + '/documents/coveredcalifornia';
17429            var metaData = { groupNumber: [planCode] };
17430
17431            function safeDamSearch(path) {
17432                return damSearchService.search(metaData, path).then(function (response) {
17433                    var items = (response.data && response.data.items) || [];
17434                    return items.length > 0 ? items[0].downloadUrl : null;
17435                }).catch(function (error) {
17436                    console.warn('AEM DAM search failed for path: ' + path, error);
17437                    return null;
17438                });
17439            }
17440
17441            Promise.all([
17442                safeDamSearch(basePath + '/evidence-of-coverage/' + year),
17443                safeDamSearch(basePath + '/summary-of-benefits-and-coverage/' + year),
17444                safeDamSearch(basePath + '/schedule-of-benefits/' + year)
17445            ]).then(function (urls) {
17446                $scope.$apply(function () {
17447                    $scope.eocUrl = urls[0];
17448                    $scope.sbcUrl = urls[1];
17449                    $scope.sobUrl = urls[2];
17450                });
17451            });
17452        }
17453
17454        $scope.$on('obfuscation_toggled', function () {
17455            $route.reload();
17456        });
17457
17458        init();
17459        $scope.focusOnElement('btnCurrentStatus');
17460
17461        $scope.$on('language-change', function () {
17462            init();
17463        });
17464
17465        $scope.$on('destroy',
17466            function() {
17467                if (focusTimeout) $timeout.cancel(focusTimeout);
17468
17469                if (tooltipTimeout) $timeout.cancel(tooltipTimeout);
17470            });
17471
17472        function init() {
17473            $scope.getCurrentEligibility();
17474            resetTooltip();
17475            $scope.getLis();
17476        }
17477        
17478        // create a timeout to allow the controller to load data and translate
17479        function resetTooltip() {
17480            tooltipTimeout = $timeout(function() {
17481                $('[data-toggle="tooltip"]').each(function(index) {
17482                  // sometimes the title is blank for no apparent reason. don't override in these cases.
17483                  if ($(this).attr("title").length > 0) {
17484                    $( this ).attr("data-original-title", $(this).attr("title"));
17485                  }
17486                });
17487
17488                $timeout(function() {
17489                  // finally, activate the tooltips
17490                  $(document).tooltip({ selector: '[data-toggle="tooltip"]'});
17491                }, 500);
17492            }, 1500);
17493        }
17494        resetTooltip();
17495    };
17496})();
17497(function () {
17498    'use strict';
17499
17500    angular
17501        .module('MemberPortal')
17502        .controller('HistoricalEligibilityController', HistoricalEligibilityController);
17503
17504    HistoricalEligibilityController.$inject = [
17505        '$scope', '$route', 'memberDataService', '$filter', 'requestedLanguageService', '$timeout'
17506    ];
17507
17508    function HistoricalEligibilityController(
17509        $scope,
17510        $route,
17511        memberDataService,
17512        $filter,
17513        requestedLanguageService,
17514        $timeout) {
17515
17516        $scope.loading = false;
17517        $scope.SubscriberNumber = '';
17518        $scope.HistoricalEligibility = {};
17519        $scope.eligMedicare = [];
17520        $scope.eligMediCal = [];
17521        $scope.eligCCA = [];
17522        $scope.eligibility = [];
17523        $scope.errorMessage = '';
17524        $scope.infoMessage = '';
17525        $scope.LobDescription = '';
17526
17527        var focusTimeout;
17528
17529        $scope.getHistoricalEligibility = function () {
17530
17531            const language = requestedLanguageService.getCurrentLanguage();
17532
17533            $scope.loading = !$scope.loading;
17534            memberDataService.getHistoricalEligibility(language).success(function (data) {
17535                if (!data || data.length === 0) {
17536                    $scope.LobDescription = '';
17537                    $scope.eligMedicare = null;
17538                    $scope.eligMediCal = null;
17539                    $scope.eligCCA = null;
17540                    $scope.eligibility = [];
17541                    $scope.infoMessage = $filter('translate')('no_eligibility_found');
17542                }
17543                else {
17544                    $scope.eligMedicare = $.grep(data, function (n) {
17545                        return (n.IsMediCare);
17546                    });
17547                    $scope.eligMediCal = $.grep(data, function (n) {
17548                        return (n.IsMediCal);
17549                    });
17550                    $scope.eligCCA = $.grep(data, function (n) {
17551                        return (n.Lob === "CCA");
17552                    });
17553
17554                    $scope.LobDescription = data[0].LobDescription;
17555
17556                    $scope.eligibility = data
17557                        // add group property
17558                        .map(row => ({
17559                                ...row,
17560                                Title: row.IsMediCal ? 'MED'
17561                                        : row.IsMediCare ? 'MCR' 
17562                                        : row.Lob === 'CCA' ? 'CCA' 
17563                                        : null
17564                            }))
17565                        // filter out empty titles
17566                        .filter(row => row.Title)
17567                        // sort by termination data desc
17568                        .sort((a,b) => b.TerminationDate - a.TerminationDate)
17569                        // group by title
17570                        .reduce((group, row) => {
17571                            let groupKey = row['Title'];
17572                            if (!group[groupKey]) {
17573                                group[groupKey] = [];
17574                            }
17575                            group[groupKey].push(row);
17576                            return group;
17577                        }, {});
17578                }
17579                $scope.HistoricalEligibility = data;
17580                $scope.loading = !$scope.loading;
17581                $scope.errorMessage = '';
17582            }).error(function () {
17583                $scope.loading = !$scope.loading;
17584                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
17585            });
17586        };
17587
17588        $scope.$on('obfuscation_toggled', function () {
17589            $route.reload();
17590        });
17591
17592        $scope.$on('destroy',
17593            function() {
17594                if (focusTimeout) $timeout.cancel(focusTimeout);
17595            });
17596
17597        $scope.focusOnElement = function (elementId) {
17598            focusTimeout = $timeout(function () {
17599                $('#' + elementId).focus();
17600            }, 500);
17601        };
17602
17603        $scope.getHistoricalEligibility();
17604        $scope.focusOnElement('btnEligHistory');
17605
17606    };
17607})();
17608(function () {
17609    'use strict';
17610
17611    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
17612
17613    angular
17614        .module('MemberPortal')
17615        .controller('AuthorizationAllController', AuthorizationAllController);
17616
17617    AuthorizationAllController.$inject = [
17618        '$scope', '$rootScope', 'authStatusService', '$location',
17619        'memberDataService', '$filter', 'requestedLanguageService', '$timeout'
17620    ];
17621
17622    function AuthorizationAllController($scope, $rootScope, authStatusService, $location,
17623        memberDataService, $filter, requestedLanguageService, $timeout) {
17624
17625        let focusTimeout;
17626
17627        $scope.CanShowMemberLetter = CanShowMemberLetter;
17628        $scope.ViewMemberLetter = ViewMemberLetter;
17629        $scope.DetermineStatus = DetermineStatus;
17630        $scope.TypeOfService = TypeOfService;
17631
17632        init();
17633
17634        function init() {
17635
17636            $scope.loading = false;
17637            $scope.eligLoading = false;
17638            $scope.contentAvailable = false;
17639            $scope.dataString = {};
17640            $scope.errorMessage = '';
17641            $scope.infoMessage = '';
17642            $scope.authorizationNumber = '';
17643            $scope.fromDate = '';
17644            $scope.thruDate = '';
17645            $scope.IPAName = '';
17646            $rootScope.IPAName = '';
17647            $scope.SubscriberNumber = '';
17648
17649            $scope.data = {};
17650
17651            $scope.primaryEligRecord = {};
17652
17653
17654            $('#navbtn6 div').addClass('navItemSelected'); //Highlight Nav Item- My Health Records
17655
17656            $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
17657
17658            $scope.authStatus = authStatusService.getStatus();
17659
17660            $scope.agegroup = $scope.authStatus.claims.agegroup;
17661
17662            getDoctor();
17663
17664            getAuthorizations();
17665
17666            focusOnElement('btnAllAuthorizations');
17667        }
17668
17669        $scope.$on('language-change', function (_event, key) {
17670            $scope.currentLanguage = key;
17671          
17672        });
17673
17674        $scope.$on('destroy',
17675            function () {
17676                if (focusTimeout) $timeout.cancel(focusTimeout);
17677            });
17678
17679        $scope.DetailsUrl = function (auth) {
17680
17681            var url = urlBase + '/App/HealthRecords/Authorizations/';
17682
17683            url = url + ((auth.authType === 0 || auth.authType === 3) ? 'Medical/' :
17684                auth.authType === 1 ? 'Rx/' :
17685                    auth.authType === 2 ? 'Vision/' : '');
17686
17687            url = url + auth.eventNumber;
17688
17689            return url;
17690        };
17691
17692        function TypeOfService(item) {
17693            return (item.authType === 0 || item.authType === 3) ? $filter('translate')('form_medical') :
17694                item.authType === 1 ? $filter('translate')('form_pharmacy') :
17695                    item.authType === 2 ? $filter('translate')('form_vision') : '';
17696        };
17697
17698        function TypeOfServiceInEnglish(item) {
17699            return (item.authType === 0 || item.authType === 3) ? 'Medical' :
17700                item.authType === 1 ? 'Pharmacy' :
17701                item.authType === 2 ? 'Vision' : '';
17702        }
17703
17704        function DetermineStatus(item) {
17705            return memberDataService.utility.DetermineAuthStatus(item);
17706        };
17707
17708        function getAuthorizations() {
17709            $scope.loading = true;
17710
17711            const username = $scope.authStatus.claims.username;
17712
17713            const payload = {
17714                "AuthTypes": ['Medical', 'Vision', 'Behavioral', 'Rx'],
17715                "SubscriberNumber": username
17716            };
17717            
17718            if ($scope.authStatus.isDSNP) {
17719                payload["ExcludeMedImpactAuth"] = true;
17720            }
17721
17722            memberDataService.SearchAllAuthorizations(payload).success(function (data) {
17723                $scope.translateMessage = $filter('translate')('info_box_no_authorizations_foun
17723d');
17724                $scope.loading = false;
17725
17726                // Filter out the authorizations that are not complete or in progress
17727                // Based on a request from business REQ1518491
17728                $scope.data = data.filter(item =>
17729                    ["Complete", "In Progress"].includes(item.eventStatus)
17730                );
17731
17732                $scope.infoMessage = data.length < 1 ? $scope.translateMessage : '';
17733                $scope.errorMessage = '';
17734                $scope.dataString = JSON.stringify(data, null, '  '); //For the debug tool
17735
17736                $scope.highlightId = $location.search().id;
17737                console.log('$scope.highlightId', $scope.highlightId);
17738            }).error(function () {
17739
17740                $scope.loading = false;
17741                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
17742
17743            });
17744        };
17745
17746        function getDoctor() {
17747
17748            $scope.eligLoading = true;
17749
17750            const language = requestedLanguageService.getCurrentLanguage();
17751
17752            memberDataService.getCurrentEligibility(language).success(function (data) {
17753
17754                $scope.eligLoading = false;
17755                $scope.IPAName = data[0].IpaDescription;
17756                $rootScope.IPAName = data[0].IpaDescription;
17757                $scope.primaryEligRecord = memberDataService.utility.getPrimaryEligibilityRecord(data);
17758
17759                $scope.errorMessage = '';
17760
17761            }).error(function () {
17762
17763                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
17764                $scope.eligLoading = false;
17765
17766            });
17767        };
17768
17769        function CanShowMemberLetter(item) {
17770       
17771            return memberDataService.canShowMemberLetter(TypeOfServiceInEnglish, item);
17772
17773        };
17774
17775        function ViewMemberLetter(caseNumber) {
17776
17777            $scope.infoMessage = '';
17778
17779            memberDataService.viewMemberLetter($scope.data, caseNumber);
17780        };
17781        
17782        function focusOnElement(elementId) {
17783
17784            focusTimeout = $timeout(function () {
17785                $('#' + elementId).focus();
17786            }, 500);
17787        };
17788    };
17789})();
17790(function () {
17791    'use strict';
17792
17793    angular
17794        .module('MemberPortal')
17795        .controller('MemberAuthorizationLetterController', MemberAuthorizationLetterController);
17796
17797    MemberAuthorizationLetterController.$inject = [
17798        '$scope', '$routeParams'
17799    ];
17800
17801    function MemberAuthorizationLetterController($scope, $routeParams) {
17802        $scope.urlBase = $('meta[name="ApplicationRoot"]').attr('content');
17803
17804        var pdfDoc = null,
17805            pageNum = 1,
17806            pageNumPending = null,
17807            scale = 0.8,
17808            canvas = document.getElementById('letterCanvas'),
17809            ctx = canvas.getContext('2d');
17810
17811        /**
17812        * Get page info from document, resize canvas accordingly, and render page.
17813        * @param num Page number.
17814        */
17815        function renderPage(num) {
17816
17817            // Using promise to fetch the page
17818            pdfDoc.getPage(num).then(function (page) {
17819
17820                const viewport = page.getViewport(scale);
17821
17822                canvas.height = viewport.height;
17823                canvas.width = viewport.width;
17824
17825                // Render PDF page into canvas context
17826                const renderContext = {
17827                    canvasContext: ctx,
17828                    viewport: viewport
17829                };
17830
17831                const renderTask = page.render(renderContext);
17832
17833                // Wait for rendering to finish
17834                renderTask.promise.then(function () {
17835
17836                    if (pageNumPending !== null) {
17837                        // New page rendering is pending
17838                        renderPage(pageNumPending);
17839                        pageNumPending = null;
17840                    }
17841                });
17842            });
17843        }
17844    };
17845})();
17846(function () {
17847    'use strict';
17848
17849    angular
17850        .module('MemberPortal')
17851        .controller('AuthorizationMedicalDetailsController', AuthorizationMedicalDetailsController);
17852
17853    AuthorizationMedicalDetailsController.$inject = [
17854        '$scope', 'memberDataService', '$routeParams', '$filter'
17855    ];
17856
17857    function AuthorizationMedicalDetailsController($scope, memberDataService, $routeParams, $filter) {
17858
17859        $scope.loading = false;
17860        $scope.data = {};
17861        $scope.dataString = {};
17862        $scope.errorMessage = '';
17863        $scope.infoMessage = '';
17864
17865        $scope.authorizationNumber = '';
17866        $scope.fromDate = '';
17867        $scope.thruDate = '';
17868
17869        $scope.getAuthorizations = function () {
17870
17871            $scope.loading = !$scope.loading;
17872
17873            memberDataService.GetMedicalAuth({
17874
17875                authorizationNumber: $routeParams.AuthId
17876
17877            }).success(function (data) {
17878
17879                $scope.loading = !$scope.loading;
17880                $scope.data = data;
17881                $scope.dataString = JSON.stringify(data, null, '  ');
17882
17883            }).error(function () {
17884
17885                $scope.loading = !$scope.loading;
17886                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
17887                $scope.loading = false;
17888
17889            });
17890        };
17891
17892        $scope.DetermineStatus = function (item) {
17893
17894            return memberDataService.utility.DetermineAuthStatus(item);
17895        };
17896
17897        $scope.getAuthorizations();
17898    };
17899})();
17900(function () {
17901    'use strict';
17902
17903    angular
17904        .module('MemberPortal')
17905        .controller('AuthorizationMedicalController', AuthorizationMedicalController);
17906
17907    AuthorizationMedicalController.$inject = [
17908        '$scope', 'authStatusService', 'memberDataService', '$filter', 'requestedLanguageService', '$timeout'
17909    ];
17910
17911    function AuthorizationMedicalController($scope, authStatusService, memberDataService, $filter, requestedLanguageService, $timeout) {
17912
17913            const urlBase = $('meta[name="ApplicationRoot"]').attr('content');
17914
17915            $scope.loading = false;
17916            $scope.data = {};
17917            $scope.dataString = {};
17918            $scope.errorMessage = '';
17919            $scope.infoMessage = '';
17920
17921            var focusTimeout;
17922
17923            $scope.DetailsUrl = urlBase + '/App/HealthRecords/Authorizations/Medical/';
17924
17925            $scope.authStatus = authStatusService.getStatus();
17926
17927            $scope.agegroup = $scope.authStatus.claims.agegroup;
17928
17929            $scope.authorizationNumber = '';
17930            $scope.fromDate = '';
17931            $scope.thruDate = '';
17932            $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
17933
17934            $scope.$on('language-change', function (_event, key) {
17935                $scope.currentLanguage = key;
17936            });
17937            $scope.TypeOfService = function (item) {
17938                return (item.authType === 0 || item.authType === 3) ? $filter('translate')('form_medical') :
17939                    item.authType === 1 ? $filter('translate')('form_pharmacy') :
17940                    item.authType === 2 ? $filter('translate')('form_vision') : '';
17941        };
17942
17943            function TypeOfServiceInEnglish(item) {
17944                return (item.authType === 0 || item.authType === 3) ? 'Medical' :
17945                    item.authType === 1 ? 'Pharmacy' :
17946                    item.authType === 2 ? 'Vision' : '';
17947            }
17948
17949            $scope.DetermineStatus = function (item) {
17950                return memberDataService.utility.DetermineAuthStatus(item);
17951            };
17952
17953            $scope.DownloadMemberLetter = function (caseNumber) {
17954                $('#hiddenDownloadFrame').remove();
17955                memberDataService.GetMedicalAuthMemberLetter(caseNumber).success(function (data, textStatus) {
17956
17957                    if (textStatus === 200) {
17958
17959                        const fileName = `Medical-Authorization-Letter-${caseNumber}.pdf`;
17960                        const file = new Blob([data], { type: 'application/pdf' });
17961
17962                        if (window.navigator && window.navigator.msSaveOrOpenBlob) {
17963
17964                            window.navigator.msSaveOrOpenBlob(file, fileName);
17965                        }
17966                        else {
17967
17968                            const fileUrl = URL.createObjectURL(file);
17969                            const link = document.createElement('a');
17970                            document.body.appendChild(link);
17971                            link.href = fileUrl;
17972                            link.download = fileName;
17973                            link.click();
17974                            document.body.removeChild(link);
17975                        }
17976                    }
17977                }).error(function () { });
17978            };
17979
17980        $scope.CanShowMemberLetter = function (item) {
17981
17982            return memberDataService.canShowMemberLetter(TypeOfServiceInEnglish, item);
17983            };
17984
17985        $scope.ViewMemberLetter = function (caseNumber) {
17986
17987            memberDataService.viewMemberLetter($scope.data, caseNumber);
17988            };
17989
17990            $scope.getAuthorizations = function () {
17991                $scope.loading = !$scope.loading;
17992                
17993                memberDataService.SearchAllAuthorizations({
17994                    "AuthTypes": ['Medical', 'Behavioral'],
17995                    "SubscriberNumber": $scope.authStatus.claims.username
17996
17997                }).success(function (data) {
17998
17999                    $scope.translateMessage = $filter('translate')('info_box_no_authorizations_found');
18000                    $scope.loading = !$scope.loading;
18001                    $scope.data = data;
18002                    $scope.infoMessage = data.length < 1 ? $scope.translateMessage : '';
18003                    $scope.errorMessage = '';
18004                    $scope.dataString = JSON.stringify(data, null, '  ');
18005
18006                }).error(function () {
18007                    $scope.loading = !$scope.loading;
18008                    $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
18009                    $scope.loading = false;
18010                });
18011            };
18012
18013            $scope.getAuthorizations();
18014
18015            $scope.focusOnElement = function (elementId) {
18016                focusTimeout = $timeout(function () {
18017                    $('#' + elementId).focus();
18018                }, 500);
18019            };
18020
18021            $scope.$on('destroy',
18022                function() {
18023                    if (focusTimeout) $timeout.cancel(focusTimeout);
18024                });
18025
18026            $scope.focusOnElement('btnMedicalClaims');
18027
18028    };
18029})();
18030(function () {
18031    'use strict';
18032
18033    angular
18034        .module('MemberPortal')
18035        .controller('AuthorizationRxDetailsController', AuthorizationRxDetailsController);
18036
18037    AuthorizationRxDetailsController.$inject = [
18038        '$scope', 'memberDataService', '$routeParams', '$filter'
18039    ];
18040
18041    function AuthorizationRxDetailsController($scope, memberDataService, $routeParams, $filter) {
18042        $scope.loading = false;
18043        $scope.data = {};
18044        $scope.dataString = {};
18045        $scope.errorMessage = '';
18046        $scope.infoMessage = '';
18047
18048        $scope.authorizationNumber = '';
18049        $scope.fromDate = '';
18050        $scope.thruDate = '';
18051
18052        $scope.getAuthorizations = function () {
18053            $scope.loading = !$scope.loading;
18054
18055            memberDataService.GetRxAuth({
18056                authorizationNumber: $routeParams.AuthId
18057            }).success(function (data) {
18058
18059                $scope.loading = !$scope.loading;
18060                $scope.data = data;
18061                $scope.dataString = JSON.stringify(data, null, '  ');
18062
18063            }).error(function () {
18064
18065                $scope.loading = !$scope.loading;
18066                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
18067            });
18068
18069        };
18070
18071        $scope.getAuthorizations();
18072
18073    };
18074})();
18075(function () {
18076    'use strict';
18077
18078    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
18079
18080    angular
18081        .module('MemberPortal')
18082        .controller('AuthorizationRxController', AuthorizationRxController);
18083
18084    AuthorizationRxController.$inject = [
18085        '$scope', '$rootScope', 'authStatusService', 'memberDataService', '$filter', '$timeout'
18086    ];
18087
18088    function AuthorizationRxController($scope, $rootScope, authStatusService, memberDataService, $filter, $timeout) {
18089        $scope.medImpactSsoUrl = $('meta[name="medImpactSsoUrl"]').attr('content');
18090        $scope.loading = false;
18091        $scope.data = {};
18092        $scope.dataString = {};
18093        $scope.errorMessage = '';
18094        $scope.infoMessage = '';
18095
18096        $scope.authStatus = authStatusService.getStatus();
18097        $scope.agegroup = $scope.authStatus.claims.agegroup;
18098
18099        var focusTimeout;
18100
18101        $scope.authorizationNumber = '';
18102        $scope.fromDate = '';
18103        $scope.thruDate = '';
18104
18105        $scope.DetailsUrl = function (auth) {
18106            return urlBase + '/App/HealthRecords/Authorizations/Rx/' + auth.eventNumber;
18107        };
18108
18109        $scope.DetermineStatus = function (item) {
18110            return memberDataService.utility.DetermineAuthStatus(item);
18111        };
18112
18113        $scope.TypeOfService = function (item) {
18114            return (item.authType === 0 || item.authType === 3) ? $filter('translate')('form_medical') :
18115                item.authType === 1 ? $filter('translate')('form_pharmacy') :
18116                item.authType === 2 ? $filter('translate')('form_vision') : '';
18117        };
18118
18119        function TypeOfServiceInEnglish(item) {
18120            return (item.authType === 0 || item.authType === 3) ? 'Medical' :
18121                item.authType === 1 ? 'Pharmacy' :
18122                item.authType === 2 ? 'Vision' : '';
18123        }
18124
18125        $scope.CanShowMemberLetter = function (item) {
18126            return memberDataService.canShowMemberLetter(TypeOfServiceInEnglish, item);
18127        };
18128
18129        $scope.getAuthorizations = function () {
18130            $scope.loading = !$scope.loading;
18131
18132            const username = $scope.authStatus.claims.username;
18133
18134            const payload = {
18135                "AuthTypes": ['Rx'],
18136                "SubscriberNumber": username
18137            };
18138
18139            if ($scope.authStatus.isDSNP) {
18140                payload["ExcludeMedImpactAuth"] = true;
18141            }
18142
18143            memberDataService.SearchAllAuthorizations(payload).success(function (data) {
18144                $scope.translateMessage = $filter('translate')('info_box_no_authorizations_foun
18144d');
18145                $scope.loading = !$scope.loading;
18146                $scope.data = data;
18147                $scope.infoMessage = data.length < 1 ? $scope.translateMessage : '';
18148                $scope.errorMessage = '';
18149                $scope.dataString = JSON.stringify(data, null, '  ');
18150            }).error(function () {
18151                $scope.loading = !$scope.loading;
18152                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
18153            });
18154        };
18155
18156        $scope.ViewMemberLetter = function (caseNumber) {
18157            $scope.infoMessage = '';
18158
18159            memberDataService.GetMedicalAuthMemberLetter(caseNumber).success(function (data, textStatus) {
18160
18161                if (textStatus === 200) {
18162
18163                    const fileName = `Rx-Authorization-Letter-${caseNumber}.pdf`;
18164                    const file = new Blob([data], { type: 'application/pdf' });
18165
18166                    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
18167
18168                        window.navigator.msSaveOrOpenBlob(file, fileName);
18169                    }
18170                    else {
18171
18172                        const fileUrl = URL.createObjectURL(file);
18173                        const link = document.createElement('a');
18174                        document.body.appendChild(link);
18175                        link.href = fileUrl;
18176                        link.download = fileName;
18177                        link.click();
18178                        document.body.removeChild(link);
18179                    }
18180                }
18181                else {
18182                    const item = memberDataService.showNoLetter($scope.data, caseNumber);
18183                    $scope.translatedMemberLetterMessage = $filter('translate')('info_box_no_member_letters');
18184                    $scope.infoMessage = $scope.translatedMemberLetterMessage + ' (' + item.eventNumber + ')';
18185                }
18186            }).error(function () {
18187                //Grep through $scope.data, find case with caseNumber
18188                const item = $.grep($scope.data, function (element) {
18189                    return element.eventNumber === caseNumber;
18190                })[0];
18191
18192                item.showNoLetterFound = true;
18193                $scope.translatedMemberLetterMessage = $filter('translate')('info_box_no_member_letters');
18194                $scope.infoMessage = $scope.translatedMemberLetterMessage + ' (' + item.eventNumber + ')';
18195            });
18196        };
18197
18198        $scope.focusOnElement = function (elementId) {
18199
18200            focusTimeout = $timeout(function () {
18201                $('#' + elementId).focus();
18202            }, 500);
18203
18204        };
18205
18206        $scope.$on('destroy',
18207            function() {
18208                if (focusTimeout) $timeout.cancel(focusTimeout);
18209            });
18210
18211        $scope.focusOnElement('btnRxClaims');
18212
18213        $scope.getAuthorizations();      
18214    };
18215})();
18216(function () {
18217    'use strict';
18218
18219    angular
18220        .module('MemberPortal')
18221        .controller('AuthorizationVisionDetailsController', AuthorizationVisionDetailsController);
18222
18223    AuthorizationVisionDetailsController.$inject = [
18224        '$scope', 'authStatusService',
18225        'memberDataService', '$routeParams', '$filter'
18226    ];
18227
18228    function AuthorizationVisionDetailsController($scope, authStatusService, memberDataService, $routeParams, $filter) {
18229
18230        $scope.loading = false;
18231        $scope.data = {};
18232        $scope.dataString = {};
18233        $scope.errorMessage = '';
18234        $scope.infoMessage = '';
18235
18236        $scope.authorizationNumber = '';
18237        $scope.fromDate = '';
18238        $scope.thruDate = '';
18239
18240        const authStatus = authStatusService.getStatus();
18241
18242        $scope.agegroup = authStatus.claims.agegroup;
18243
18244        $scope.getAuthorizations = function () {
18245
18246            $scope.loading = !$scope.loading;
18247
18248            memberDataService.GetVisionAuth({
18249
18250                authorizationNumber: $routeParams.AuthId
18251
18252            }).success(function (data) {
18253
18254                $scope.loading = !$scope.loading;
18255                $scope.data = data;
18256                $scope.dataString = JSON.stringify(data, null, '  ');
18257
18258            }).error(function () {
18259
18260                $scope.loading = !$scope.loading;
18261                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
18262            });
18263        };
18264        $scope.getAuthorizations();
18265    };
18266})();
18267(function () {
18268    'use strict';
18269
18270    angular
18271        .module('MemberPortal')
18272        .controller('AuthorizationVisionController', AuthorizationVisionController);
18273
18274    AuthorizationVisionController.$inject = [
18275        '$scope', 'authStatusService', 'memberDataService', '$filter', 'requestedLanguageService'
18276    ];
18277
18278    function AuthorizationVisionController($scope, authStatusService, memberDataService, $filter, requestedLanguageService) {
18279
18280        const urlBase = $('meta[name="ApplicationRoot"]').attr('content');
18281
18282        $scope.loading = false;
18283        $scope.data = {};
18284        $scope.dataString = {};
18285        $scope.errorMessage = '';
18286        $scope.infoMessage = '';
18287
18288        $scope.DetailsUrl = urlBase + '/App/HealthRecords/Authorizations/Vision/';
18289
18290        $scope.authStatus = authStatusService.getStatus();
18291
18292        $scope.agegroup = $scope.authStatus.claims.agegroup;
18293
18294        $scope.authorizationNumber = '';
18295        $scope.fromDate = '';
18296        $scope.thruDate = '';
18297
18298        $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
18299
18300        $scope.$on('language-change', function (_event, key) {
18301            $scope.currentLanguage = key;
18302        });
18303
18304        $scope.TypeOfService = function (item) {
18305            /**                
18306                Medical - 0
18307                Pharmacy - 1
18308                Vision - 2
18309                Behavioral - 3
18310             */
18311            return (item.authType === 0 || item.authType === 3) ? $filter('translate')('form_medical') :
18312                item.authType === 1 ? $filter('translate')('form_pharmacy') :
18313                item.authType === 2 ? $filter('translate')('form_vision') : '';
18314        };
18315
18316        $scope.getAuthorizations = function () {
18317
18318            $scope.loading = !$scope.loading;
18319
18320            const username = $scope.authStatus.claims.username;
18321
18322            memberDataService.SearchAllAuthorizations({
18323                "AuthTypes": ['Vision'],
18324                "SubscriberNumber": username
18325
18326            }).success(function (data) {
18327
18328                $scope.translateMessage = $filter('translate')('info_box_no_authorizations_foun
18328d');
18329                $scope.loading = !$scope.loading;
18330                $scope.data = data;
18331                $scope.infoMessage = data.length < 1 ? $scope.translateMessage : '';
18332                $scope.errorMessage = '';
18333                $scope.dataString = JSON.stringify(data, null, '  ');
18334
18335            }).error(function () {
18336
18337                $scope.loading = !$scope.loading;
18338                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
18339            });
18340
18341        };
18342
18343        $scope.DetermineStatus = function (item) {
18344
18345            return memberDataService.utility.DetermineAuthStatus(item);
18346        };
18347
18348        $scope.getAuthorizations();
18349    };
18350})();
18351(function () {
18352    'use strict';
18353
18354    angular
18355        .module('MemberPortal')
18356        .controller('CarePlansController', CarePlansController);
18357
18358        CarePlansController.$inject = [
18359        '$scope', 'memberDataService', '$filter', 'requestedLanguageService'
18360    ];
18361
18362    function CarePlansController($scope, memberDataService, $filter, requestedLanguageService) {
18363
18364        $scope.loading = true;
18365        $scope.errorMessage = null;
18366        $scope.infoMessage = '';
18367        $scope.carePlanData = null;
18368
18369        $('#navbtn6 div').addClass('navItemSelected'); //Highlight Nav Item- My Health Records
18370
18371        $scope.$on('language-change', function () {});
18372
18373        $scope.GetCarePlans = function () {
18374            this.carePlanData = null;
18375            this.errorMessage = null;
18376            memberDataService.GetCarePlans().success(function (data) {
18377                $scope.carePlanData = data;
18378                $scope.loading = false;
18379            }).error(function () {
18380                console.error("can't get care plan data");
18381                $scope.loading = false;
18382                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
18383            });
18384        };
18385
18386        $scope.GetCarePlans();
18387    };
18388})();
18389(function () {
18390    'use strict';
18391
18392    angular
18393        .module('MemberPortal')
18394        .controller('ClaimsController', ClaimsController);
18395
18396    ClaimsController.$inject = ['$scope', 'authStatusService', 'memberDataService', '$routeParams', '$filter'];
18397
18398    function ClaimsController($scope, authStatusService, memberDataService, $routeParams, $filter) {
18399        $scope.loading = false;
18400        $scope.errorMessage = '';
18401        $scope.infoMessage = '';
18402
18403        $scope.authStatus = authStatusService.getStatus();
18404        $scope.isCCA = $scope.authStatus.isCCA;
18405        
18406        $('#navbtn6 div').addClass('navItemSelected'); //Highlight Nav Item- My Health Records
18407        
18408        const claimId = $routeParams.claimId;
18409        const source = $routeParams.source || "";
18410
18411        //================= PAGING ============//
18412        $scope.itemsPerPage = 10;
18413        $scope.currentMediTracPage = 0;
18414        $scope.currentHRPPage = 0;
18415        $scope.data = [];
18416
18417        $scope.urlBase = $('meta[name="ApplicationRoot"]').attr('content');
18418
18419        $scope.$on('language-change', function (event, key) {
18420            $scope.currentLanguage = key;
18421            if ($scope.errorMessage) {
18422                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
18423            }
18424        });
18425
18426        function pageData (arrayTopage) {
18427            if (!arrayTopage || !Array.isArray(arrayTopage)) {
18428                return [];
18429            }
18430
18431            // Sort the array by DateOfService in descending order
18432            arrayTopage.sort((a, b) => new Date(b.DateOfService) - new Date(a.DateOfService));
18433            $scope.RxTable = arrayTopage;
18434
18435            // Use array slicing to create pages
18436            const pagedData = [];
18437            for (let i = 0; i < arrayTopage.length; i += $scope.itemsPerPage) {
18438                pagedData.push(arrayTopage.slice(i, i + $scope.itemsPerPage));
18439            }
18440
18441            return pagedData;
18442        };
18443
18444        $scope.setPage = function (n, pagedData, currentPageVar) {
18445            if (pagedData && n >= 0 && n < pagedData.length) {
18446                $scope[currentPageVar] = n; // Update the specific current page variable
18447            }
18448        };
18449
18450        $scope.prevPage = function (currentPageVar) {
18451            if ($scope[currentPageVar] > 0) {
18452                $scope[currentPageVar]--; // Decrement the specific current page variable
18453            }
18454        };
18455
18456        $scope.nextPage = function (pagedData, currentPageVar) {
18457            if ($scope[currentPageVar] < pagedData.length - 1) {
18458                $scope[currentPageVar]++; // Increment the specific current page variable
18459            }
18460        };
18461
18462        //================= ALL CLAIMS ============//
18463        $scope.GetClaims = function () {
18464            $scope.loading = true;
18465
18466            memberDataService.GetClaims({
18467                claimId: null
18468            }).success(function (data) {
18469
18470                
18471                $scope.agegroup = $scope.authStatus.claims.agegroup;
18472                $scope.loading = false;
18473                $scope.errorMessage = '';
18474
18475                if (data) {
18476                    $scope.data = data;
18477
18478                    // Split data into mediTracData and hrpData based on ClaimSource
18479                    const mediTracData = data.filter(claim => claim.ClaimSource.toLowerCase() === "meditrac");
18480                    const hrpData = data.filter(claim => claim.ClaimSource.toLowerCase() === "hrp");
18481
18482                    // Paginate both arrays
18483                    $scope.PagedMediTracClaims = pageData(mediTracData);
18484                    $scope.PagedHRPClaims = pageData(hrpData);
18485                }
18486            }).error(function () {
18487                $scope.errorMessage = 'form_unableToRetrieveData';
18488                $scope.loading = !$scope.loading;
18489            });
18490        };
18491
18492        $scope.goBack = function () {
18493
18494            history.back();
18495            $scope.$apply();
18496        };
18497
18498        //================= CLAIM DETAILS ============//
18499        $scope.GetClaim = function (claimId, claimSource) {
18500            // Toggle loading state
18501            $scope.loading = true;
18502            $scope.isCcaClaim = claimSource.toLowerCase() === 'hrp';
18503
18504            // Fetch claim data
18505            memberDataService.GetClaim({
18506                claimNumber: claimId,
18507                source: claimSource
18508            }).then(
18509                // Success handler
18510                function (response) {
18511                    const data = response.data;
18512
18513                    // Check if claim lines are empty
18514                    if (data.ClaimLines.length === 0) {
18515                        $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
18516                        $scope.loading = false;
18517                        return;
18518                    }
18519
18520                    // Update scope with data
18521                    $scope.data = data;
18522                    $scope.errorMessage = '';
18523                },
18524                // Error handler
18525                function () {
18526                    $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
18527                }
18528            ).finally(function () {
18529                // Ensure loading is turned off
18530                $scope.loading = false;
18531            });
18532        };
18533
18534        if (claimId) {
18535
18536            $scope.GetClaim(claimId, source);
18537
18538        } else {
18539
18540            $scope.GetClaims();
18541        }
18542
18543        $scope.toggleBubble = function (item) {
18544            toggleBubble(`claimLine_${item.LineNo}`);
18545        }
18546
18547    };
18548})();
18549(function () {
18550    'use strict';
18551    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
18552    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
18553
18554    angular
18555        .module('MemberPortal')
18556        .controller('ConfidentialCommunicationsController', ConfidentialCommunicationsController);
18557
18558        ConfidentialCommunicationsController.$inject = [
18559        '$scope', '$translate', 'iFormioService', 'memberDataService', 'requestedLanguageService', '$timeout', 'authStatusService', '$uibModal', '$location'
18560    ];
18561
18562    function ConfidentialCommunicationsController($scope, $translate, iFormioService, memberDataService, requestedLanguageService, $timeout, authStatusService, $uibModal, $location) {
18563        $scope.loading = true;
18564        $scope.errorMessage = null;
18565        $scope.showIFormio = false;
18566        $scope.showButtons = false;
18567        $scope.showExisting = false;
18568        $scope.showRevocation = false;
18569        $scope.submitted = false;
18570        $scope.confirmLeave = false;
18571
18572        $scope.member = null;
18573        $scope.submission = null;
18574        $scope.currentLanguage = null;
18575
18576        $scope.formName = 'confidentialcommunicationsrequest';
18577        $scope.formParams = {};
18578
18579        $scope.submissionsParams = {
18580            limit: 1,
18581            sort: '-modified',
18582            'data.member.iehpId': null
18583        };
18584
18585        $('#navbtn6 div').addClass('navItemSelected'); //Highlight Nav Item- My Health Records
18586
18587        $scope.$on('$routeChangeStart', function(event, next, current) {
18588            // Do not open modal again after confirm leave and route change via $location.path
18589            if (!$scope.confirmLeave) {
18590                $scope.confirmNavigation(event, next, current);
18591            }
18592        });
18593
18594        $scope.confirmNavigation = function(event = null, next = null, _current = null) {
18595            // Revoke Active Request or Submit New Request (and not submitted)
18596            // Check showButtons instead of showIFormio because showIFormio is set last in a $timeout
18597            if (!$scope.showButtons && !$scope.showExisting && !$scope.submitted) {
18598
18599                // Leave form via route change
18600                if (!$scope.confirmLeave && event) {
18601                    // Prevent page close
18602                    event.preventDefault();
18603                }
18604    
18605                // Open confirmation modal
18606                var data = {};
18607    
18608                const modalInstance = $uibModal.open({
18609                    backdrop: 'static',
18610                    animation: true,
18611                    templateUrl: urlBase +
18612                        '/App/directives/leave-page/leave-page-modal.html' +
18613                        cacheBust,
18614                    windowClass: 'leavePageModal',
18615                    controller: 'leavePageModal',
18616                    scope: $scope,
18617                    controllerAs: 'vc',
18618                    size: 'md',
18619                    resolve: {
18620                        data: function () {
18621                            return data;
18622                        }
18623                    }
18624                });
18625    
18626                modalInstance.result.then(
18627                    // Leave
18628                    function (leave) {
18629                        if (leave) {
18630                            $scope.confirmLeave = true;
18631                            $scope.leave(next);
18632                        }
18633                    },
18634                    // Cancel
18635                    function () {}
18636                );
18637            }
18638            else {
18639                $scope.leave(next);
18640            }
18641        }
18642
18643        $scope.leave = function(next) {
18644            // Leave form via route change
18645            if (next) {
18646                $location.path(next.$$route.originalPath);
18647            }
18648            // Leave form via Back button on module page 
18649            else {
18650                $scope.goBack();
18651            }
18652        }
18653
18654        $scope.setFormParams = function () {
18655
18656            const authStatus = authStatusService.getStatus();
18657
18658            $scope.formParams = {
18659                memberNumber: $scope.member.SubscriberId,
18660                showRevocation: $scope.showRevocation,
18661                formLanguage: $scope.currentLanguage,
18662                createdBy: 'Self',
18663                soaToken: authStatus.access_token,
18664            };
18665
18666            // Show Active Requeset
18667            if ($scope.showExisting) {
18668                $scope.formParams.submissionId = $scope.submission._id;
18669            }
18670            // Revoke Active Request
18671            // Submit New Request
18672            else {
18673                $scope.formParams.firstName = capitalize($scope.member.FirstName);
18674                $scope.formParams.lastName = capitalize($scope.member.LastName);
18675                $scope.formParams.dateOfBirth = $scope.member.DateOfBirth;
18676            }
18677        }
18678
18679        $scope.$on('language-change', function (event, key) {
18680            $scope.currentLanguage = key;
18681            iFormioService.setLanguage($scope.currentLanguage);
18682        });
18683
18684        $scope.$on('$destroy', function() {
18685            $scope.removeListeners();
18686            window.removeEventListener('message', $scope.iFormioSubmittedListener);
18687            window.removeEventListener('beforeunload', $scope.iFormioBeforeUnloadListener);
18688        });
18689
18690        $scope.removeListeners = function() {
18691            window.removeEventListener('message', $scope.iFormioListener);
18692            window.removeEventListener('iFormio-appInitialized', $scope.iFormioAppInitialized);
18693        }
18694
18695        $scope.checkSubmissionStatus = function() {
18696            $scope.loading = true;
18697
18698            memberDataService.GetDemographics().then(function(resp) {      
18699                $scope.member = resp.data;
18700
18701                $scope.getFormioSubmissions($scope.member.SubscriberId);
18702            }).catch(function(e) {
18703                console.error(e);
18704                $scope.errorMessage = 'Unable to get demographics';
18705                $scope.loading = false;
18706            });
18707        };
18708
18709        $scope.getFormioSubmissions = function(iehpId) {
18710            $scope.submissionsParams['data.member.iehpId'] = iehpId;
18711
18712            iFormioService.getFormSubmissions($scope.submissionsParams).then(function(resp) {
18713                // Display button(s) based on existing submission
18714                if (resp.data.length) {
18715                    $scope.submission = resp.data[0];
18716                }
18717
18718                $scope.showButtons = true;
18719            }).catch(function(e) {
18720                console.error(e);
18721                $scope.errorMessage = 'Unable to get form submissions';
18722            }).finally(function() {
18723                $scope.loading = false;
18724            });
18725        }
18726
18727        $scope.populateForm = function() {
18728            $scope.setFormParams();
18729            iFormioService.setFormParams($scope.formParams);
18730            $scope.iFormioSrc = iFormioService.getIFormioSrc(false);
18731
18732            $scope.removeListeners();
18733
18734            $scope.iFormioListener = iFormioService.iFormioListener;
18735
18736            $scope.iFormioAppInitialized = function(_event) {
18737                window.removeEventListener('iFormio-appInitialized', $scope.iFormioAppInitialized);
18738                iFormioService.setShowForm(true, true);
18739            };
18740
18741            window.addEventListener('message', $scope.iFormioListener);
18742            window.addEventListener('iFormio-appInitialized', $scope.iFormioAppInitialized);
18743
18744            $timeout(function () {
18745                $scope.showIFormio = true;
18746            });
18747        };
18748
18749        $scope.goBack = function() {
18750            // Show options instead of form
18751            $scope.showIFormio = false;
18752            $scope.showButtons = true;
18753            // Reset flags
18754            $scope.confirmLeave = false;
18755            $scope.submitted = false;
18756            // Refresh data
18757            $scope.checkSubmissionStatus();
18758        }
18759
18760        $scope.makeRequest = function(showExisting, showRevocation) {
18761            // Show form instead of options
18762            $scope.showButtons = false;
18763            $scope.showExisting = showExisting;
18764            $scope.showRevocation = showRevocation;
18765            // Reset flags
18766            $scope.confirmLeave = false;
18767            $scope.submitted = false;
18768            $scope.populateForm();
18769        }
18770
18771        function capitalize(string) {
18772            return string
18773            .split(/\s/)
18774            .map((v) => v.charAt(0).toUpperCase() + v.slice(1).toLowerCase())
18775            .join(' ');
18776        }
18777
18778        function init() {
18779            $scope.iFormioBeforeUnloadListener = function(event) {
18780                if (!$scope.showButtons && !$scope.showExisting && !$scope.submitted) {
18781                    event.preventDefault();
18782                    return (event.returnValue = "");
18783                }
18784            };
18785
18786            $scope.iFormioSubmittedListener = function(event) {
18787                if (event.data === 'IFORMIO_FORM_SUBMITTED') {
18788                    $scope.submitted = true;
18789                }
18790            };
18791            
18792            window.addEventListener('beforeunload', $scope.iFormioBeforeUnloadListener);
18793            window.addEventListener('message', $scope.iFormioSubmittedListener);
18794
18795            const currentTranslationServiceLanguage = $translate.proposedLanguage() || $translate.use();
18796            const currentLanguageServiceLanguage = requestedLanguageService.getCurrentLanguage();
18797
18798            // $translate language is not set initially so may have to use cookie language
18799            // If $translate language is set, use that to avoid issues with other tabs setting the cookie language
18800            $scope.currentLanguage = currentTranslationServiceLanguage ? currentTranslationServiceLanguage : currentLanguageServiceLanguage;
18801
18802            iFormioService.setElementId('iformio-iframe-confidential-communications');
18803            iFormioService.setFormName($scope.formName);
18804            iFormioService.setFormSource('memberportal');
18805
18806            $scope.checkSubmissionStatus();
18807        }
18808
18809        init();
18810    };
18811})();
18812(function () {
18813    'use strict';
18814
18815    angular
18816        .module('MemberPortal')
18817        .controller('EOBController', EOBController);
18818
18819        EOBController.$inject = [
18820        '$scope', 'authStatusService', 'memberDataService', '$filter', 'requestedLanguageService'
18821    ];
18822
18823    function EOBController($scope, authStatusService, memberDataService, $filter, requestedLanguageService) {
18824        
18825      // Clear messages on page load.
18826      $scope.errorMessage = '';
18827      $scope.successMessage = '';
18828      $scope.infoMessage = '';
18829      $scope.loading = false;
18830
18831      $scope.documents = [];
18832
18833      // CCA payments redirect sso link
18834      $scope.ccaPaymentsRedirect = $('meta[name="ccaPaymentsRedirect"]').attr("content");
18835
18836    $scope.GetMemberEobDocuments = function() {
18837        $scope.loading = true;
18838        $scope.errorMessage = '';
18839        $scope.documents = [];
18840        memberDataService.GetMemberEobDocuments()
18841            .success(function(data) {
18842                $scope.documents = data
18843                    .sort((a,b) => (new Date(b.NoticeDate) - new Date(a.NoticeDate))); 
18844                $scope.loading = false;
18845            })
18846            .error(function() {
18847                $scope.loading = false;
18848                $scope.errorMessage =  $filter('translate')('form_unableToRetrieveData');
18849            })
18850    }
18851
18852        $scope.downloadEOB = function (doc) {
18853        console.log('download file>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ')
18854        $scope.errorMessage = '';
18855        memberDataService.DownloadEob(doc.EobDocumentId).success(function (data, textStatus) {
18856            if (textStatus === 200) {
18857                const fileName = `ExplanationOfBenefits-${moment(doc.NoticeDate).format("MM-DD-YYYY")}.pdf`;
18858                const file = new Blob([data], { type: 'application/pdf' });
18859
18860                if (window.navigator && window.navigator.msSaveOrOpenBlob) {
18861                    window.navigator.msSaveOrOpenBlob(file, fileName);
18862                }
18863                else {
18864                    const fileUrl = URL.createObjectURL(file);
18865                    const link = document.createElement('a');
18866                    document.body.appendChild(link);
18867
18868                    link.href = fileUrl;
18869                    link.download = fileName;
18870                    link.click();
18871
18872                    document.body.removeChild(link);
18873                }
18874            }
18875        }).error(function () {
18876            $scope.errorMessage =  $filter('translate')('form_unableToRetrieveData');
18877        });
18878    };
18879
18880      $scope.GetMemberEobDocuments();       
18881    };
18882})();
18883(function () {
18884    'use strict';
18885
18886    angular
18887        .module('MemberPortal')
18888        .controller('HRAController', HRAController);
18889
18890    HRAController.$inject = ['$scope', 'memberDataService',
18891        'requestedLanguageService', '$window', 'config', 'authStatusService'
18892    ];
18893
18894    function HRAController($scope, memberDataService,
18895        requestedLanguageService, $window, config, authStatusService) {
18896        
18897        $scope.getPrivacyUrl = getPrivacyUrl;
18898
18899        $scope.downloadHRA = downloadHRA;
18900
18901        $scope.redirectToHRA = redirectToHRA;
18902        
18903        init();
18904
18905        function init() {
18906
18907            $scope.errorMessage = null;
18908            $scope.infoMessage = '';
18909
18910            $scope.isHRAComplete = false;
18911            $scope.isHRAResponseReady = false;
18912
18913            $scope.HRAUrl = null;
18914            $scope.pdfData = null;
18915
18916            $scope.languageMap = [];
18917
18918            $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
18919
18920            $('#navbtn6 div').addClass('navItemSelected'); //Highlight Nav Item- My Health Records
18921
18922            const isProxied = authStatusService.checkIfProxied();
18923
18924            $scope.isEditable = !isProxied;
18925
18926            checkHRAStatus();
18927        }
18928
18929        $scope.$on('language-change', function (event, key) {
18930            $scope.currentLanguage = key;
18931        });
18932
18933        function checkHRAStatus() {
18934            $scope.loading = true;
18935            memberDataService.GetHRASurveyStatus()
18936                .then(
18937                    function success(response) {
18938                        const HRAData = response.data;
18939                        // if response contains url property, then check its value
18940                        if(typeof HRAData.url !== 'undefined') {
18941                            $scope.isHRAComplete = !HRAData.url; // HRA complete if url is null
18942                            $scope.HRAUrl = HRAData.url;             
18943                            // HRA survey response is null, check if PDF available
18944                            // else: if HRA URL is set, then stop loader and show link
18945                            if($scope.isHRAComplete) {
18946                                checkHRAResponseStatus();
18947                            } else {
18948                                $scope.loading = false;
18949                            }
18950                        } else {
18951                            // something went wrong as url property should always be present
18952                            $scope.loading = false;
18953                            $scope.errorMessage = 'HRA retrieval failed';
18954                        }
18955
18956                    },
18957                    function error() {
18958                        console.error("can't get care hra data");
18959                        $scope.errorMessage = 'unable to get HRA survey status';
18960                        $scope.loading = false;
18961                    });
18962        };
18963
18964        function checkHRAResponseStatus() {
18965            $scope.loading = true;
18966            $scope.pdfData = null;
18967            $scope.isHRAResponseReady = false;
18968            memberDataService.GetHRACarePlan()
18969                .then(
18970                    function success(response) {
18971                        $scope.loading = false;
18972                        if(response.status === 200) {
18973                            $scope.isHRAResponseReady = true;
18974                            $scope.pdfData = response.data;
18975                        } else if(response.status === 204){
18976                            $scope.isHRAResponseReady = false;
18977                        } else {
18978                            console.error("wrong status code for HRA PDF");
18979                            $scope.errorMessage = 'wrong PDF response';
18980                        }
18981                    },
18982                    function error() {
18983                        console.error("can't get care hra care plan");
18984                        $scope.errorMessage = 'unable to get HRA Care Plan';
18985                        $scope.loading = false;
18986                    });
18987        }
18988
18989        function redirectToHRA() {            
18990            // map the language code from member portal languages to limesurvey
18991            const memberLanguage = config.languageMap[$scope.currentLanguage];
18992            // append language code to url
18993            const url = updateQueryStringParameter($scope.HRAUrl, 'lang', memberLanguage);
18994            $window.open(url, '_blank');
18995        }
18996
18997        function downloadHRA() {
18998            $scope.loading = true;
18999            const fileName = 'IEHP_HealthRiskAssessment.pdf';
19000            if (window.navigator.msSaveOrOpenBlob) {
19001                window.navigator.msSaveOrOpenBlob(new Blob([$scope.pdfData], { type: 'application/pdf' }), filename);
19002            } else {
19003                const file = new Blob([$scope.pdfData], { type: 'application/pdf' });
19004                const fileUrl = URL.createObjectURL(file);
19005                const link = document.createElement('a');
19006                document.body.appendChild(link);
19007                link.href = fileUrl;
19008                link.download = fileName;
19009                link.click();
19010                document.body.removeChild(link);
19011            }
19012            $scope.loading = false;
19013        }
19014        
19015        function updateQueryStringParameter(uri, key, value) {
19016            let re = new RegExp("([?&])" + key + "=.*?(&|#|$)", "i");
19017            if( value === undefined ) {
19018                if (uri.match(re)) {
19019                  return uri.replace(re, '$1$2').replace(/[?&]$/, '').replaceAll(/([?&])&+/g, '$1').replace(/[?&]#/, '#');
19020              } else {
19021                  return uri;
19022              }
19023            } else {
19024                if (uri.match(re)) {
19025                    return uri.replace(re, '$1' + key + "=" + value + '$2');
19026              } else {
19027              let hash =  '';
19028                    if (uri.indexOf('#') !== -1) {
19029                  //Instead of using regex to match everything before #, using substring to directly get the part of the URI from the # onwards. 
19030                  //This avoids the backtracking vulnerability by not using greedy(.*) regex patterns
19031                  hash = uri.substring(uri.indexOf('#'));
19032                  uri = uri.substring(0, uri.indexOf('#'));
19033              }
19034              let separator = uri.indexOf('?') !== -1 ? "&" : "?";    
19035              return uri + separator + key + "=" + value + hash;
19036            }
19037            }  
19038        }
19039        
19040        function getPrivacyUrl(language) {
19041            return requestedLanguageService.getPrivacyUrl(language);
19042        }
19043    };
19044})();
19045(function () {
19046    'use strict';
19047
19048    angular
19049        .module('MemberPortal')
19050        .controller('ImmunizationsController', ImmunizationsController);
19051
19052    ImmunizationsController.$inject = [
19053        '$scope', 'memberDataService', '$filter', '$timeout'
19054    ];
19055
19056    function ImmunizationsController($scope, memberDataService, $filter, $timeout) {
19057
19058        $scope.loading = false;
19059        $scope.errorMessage = '';
19060        $scope.errorMessage = '';
19061        $scope.data = '';
19062
19063        $('#navbtn6 div').addClass('navItemSelected'); //Highlight Nav Item- My Health Records
19064
19065        //================= PAGING ============//
19066        $scope.PagedMedicalClaims = [];
19067        $scope.itemsPerPage = 5;
19068        $scope.currentPage = 0;
19069        $scope.gap = 0;
19070        $scope.data = [];
19071
19072        var focusTimeout;
19073
19074        $scope.pageItems = function (arrayTopage) {
19075
19076            $scope.RxTable = arrayTopage;
19077
19078            const arrayLength = arrayTopage.length;
19079
19080            for (let i = 0; i < arrayLength; i++) {
19081
19082                if (i % $scope.itemsPerPage === 0) {
19083
19084                    $scope.PagedMedicalClaims[Math.floor(i / $scope.itemsPerPage)] = [arrayTopage[i]];
19085
19086                } else {
19087
19088                    $scope.PagedMedicalClaims[Math.floor(i / $scope.itemsPerPage)].push(arrayTopage[i]);
19089                }
19090            }
19091
19092            $scope.gap = $scope.PagedMedicalClaims.length;
19093        };
19094
19095        $scope.range = function (size, start, end) {
19096
19097            const ret = [];
19098
19099            if (size < end) {
19100
19101                end = size;
19102                start = size - $scope.gap;
19103            }
19104
19105            for (let i = start; i < end; i++) {
19106
19107                ret.push(i);
19108            }
19109
19110            return ret;
19111        };
19112
19113        $scope.setPage = function (n) {
19114
19115            $scope.currentPage = n;
19116        };
19117
19118        $scope.prevPage = function () {
19119
19120            if ($scope.currentPage > 0) {
19121
19122                $scope.currentPage--;
19123            }
19124        };
19125
19126        $scope.nextPage = function () {
19127
19128            if ($scope.currentPage < $scope.PagedMedicalClaims.length - 1) {
19129
19130                $scope.currentPage++;
19131            }
19132        };
19133
19134        $scope.GetImmunization = function () {
19135
19136            $scope.loading = !$scope.loading;
19137
19138            $scope.errorMessage = '';
19139            $scope.infoMessage = '';
19140
19141            memberDataService.GetImmunization()
19142                .success(function (data) {
19143
19144                    $scope.data = data;
19145                 
19146                    if (data == null || data.SubscriberNumber == null) {
19147
19148                        $scope.infoMessage = $filter('translate')('info_box_no_immunization_found');
19149                    }
19150                    $scope.loading = !$scope.loading;
19151                })
19152                .error(function () {
19153                    $scope.errorMessage = $filter('translate')('info_box_problem_getting_data');
19154                    $scope.loading = !$scope.loading;
19155                });
19156        };
19157
19158        $scope.GetImmunization();
19159
19160        $scope.$on('destroy',
19161            function () {
19162                if (focusTimeout) $timeout.cancel(focusTimeout);
19163            });
19164
19165        $scope.focusOnElement = function (elementId) {
19166            focusTimeout = $timeout(function () {
19167                $('#' + elementId).focus();
19168            }, 500);
19169        };
19170
19171        $scope.focusOnElement('btnImmunizations');
19172    };
19173})();
19174(function () {
19175    'use strict';
19176
19177    angular
19178        .module('MemberPortal')
19179        .controller('LabsController', LabsController);
19180
19181    LabsController.$inject = [
19182        '$scope', 'authStatusService', 'memberDataService', '$filter', 'requestedLanguageService', '$timeout'
19183    ];
19184
19185    function LabsController($scope, authStatusService, memberDataService, $filter, requestedLanguageService, $timeout) {
19186
19187        $scope.loading = true;
19188        $scope.errorMessage = '';
19189        $scope.infoMessage = '';
19190
19191        var focusTimeout;
19192
19193        $('#navbtn6 div').addClass('navItemSelected'); //Highlight Nav Item- My Health Records
19194
19195        $scope.trimUpperCarrot = function (string) {
19196
19197            const firstCarrotPos = string.indexOf('^');
19198
19199            return firstCarrotPos > 0 ? string.substring(0, firstCarrotPos) : string;
19200        };
19201
19202        $scope.newGrouping = function (group_list, group_by, index) {
19203
19204            return index > 0 ? group_list[index - 1][group_by] !== group_list[index][group_by] : true;
19205
19206        };
19207
19208        $scope.$on('language-change', function () {
19209
19210            $scope.GetLabData();
19211
19212        });
19213
19214        $scope.cleanDesc = function (desc) {
19215
19216            if (!desc) return '';
19217            console.log("desc", desc);
19218            const targetPattern = "\\.br\\";
19219            const replaceWith = '<br/>';
19220
19221            const cleaned = desc.split(targetPattern).join(replaceWith);
19222            console.log("cleaned", cleaned);
19223            return cleaned;
19224        }
19225
19226        $scope.GetLabData = function () {
19227
19228            const language = requestedLanguageService.getCurrentLanguage();
19229
19230            memberDataService.GetLabData(language).success(function (data) {
19231                console.log("lab data", data);
19232
19233                $scope.data = data;
19234
19235                const authStatus = authStatusService.getStatus();
19236
19237                $scope.agegroup = authStatus.claims.agegroup;
19238                $scope.loading = false;
19239                $scope.infoMessage = data && data.length === 0 ? $filter('translate')('form_webservice_message_noLabDataFound') : '';
19240
19241
19242            }).error(function () {
19243                console.log("can't get lab data");
19244                $scope.loading = false;
19245                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
19246            });
19247        };
19248
19249        $scope.GetLabData();
19250
19251        $scope.$on('destroy',
19252            function () {
19253                if (focusTimeout) $timeout.cancel(focusTimeout);
19254            });
19255
19256        $scope.focusOnElement = function (elementId) {
19257            focusTimeout = $timeout(function () {
19258                $('#' + elementId).focus();
19259            }, 500);
19260        };
19261
19262        $scope.focusOnElement('btnLabResults');
19263    };
19264})();
19265(function () {
19266    'use strict';
19267
19268    angular
19269        .module('MemberPortal')
19270        .controller('PrescriptionRequestController', PrescriptionRequestController);
19271
19272    PrescriptionRequestController.$inject = [
19273        '$scope', 'authStatusService', 'memberDataService', '$route', 'requestedLanguageService', '$filter', '$timeout'];
19274
19275    function PrescriptionRequestController($scope, authStatusService, memberDataService, $route, requestedLanguageService, $filter, $timeout) {
19276
19277        $scope.loading = false;
19278        $scope.submitError = false;
19279        $scope.successfulSubmit = false;
19280        $scope.submitted = false;
19281        $scope.expeditedShow = false;
19282        $scope.toggleForm = false;
19283        $scope.edit = false;
19284        $scope.TestUserMode = true;
19285        $scope.dateOfSubmission = '';
19286        $scope.radioRequestor = '';
19287
19288        $scope.data = {};
19289        $scope.drugList = [];
19290
19291
19292        const claims = authStatusService.getStatus().claims;
19293
19294        var submitDrugRequestTimeout;
19295
19296        $scope.data.firstName = claims.firstname.toLowerCase();
19297        $scope.data.lastName = claims.lastname.toLowerCase();
19298        $scope.data.memberId = claims.subno;
19299        $scope.data.internalUser = claims.internaluser;
19300        $scope.data.lob = claims.lob;
19301
19302        $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
19303
19304        $scope.languageShortHand = requestedLanguageService.getLanguageshorthand($scope.currentLanguage);
19305
19306        lobDisplay(claims.lob);
19307
19308        getDoctor();
19309
19310        $scope.doctorName = '';
19311        $scope.data.doctorName = '';
19312        $scope.PcpId = '';
19313
19314        /* Function that checks if a field is empty
19315         * and then displays an error message if it is.
19316         * Doesn't display the error message when the 
19317         * form is initially loaded.
19318         */
19319        $scope.emptyField = function (input) {
19320            return !input && $scope.submitted;
19321        };
19322        /*
19323         * Function used to construct parameter object.
19324         * returns object with subscriberNumber which is
19325         * member ID.
19326         */
19327        //function constructParameterObj() {
19328
19329        //    return { subscriberNumber: $scope.data.memberId };
19330        //};
19331        /*
19332         * Function for getting current eligibility.
19333         * Used to get member information for auto
19334         * filling form. Also used to get PcpId.
19335         */
19336        function getDoctor() {
19337
19338            const language = requestedLanguageService.getCurrentLanguage();
19339
19340            memberDataService.getCurrentEligibility(language).success(function (data) {
19341
19342                const primaryEligRecord = memberDataService.utility.getPrimaryEligibilityRecord(data);
19343
19344                var address2 = '';
19345
19346                if (primaryEligRecord.Address2) {
19347
19348                    address2 = primaryEligRecord.Address2.toLowerCase();
19349                }
19350
19351                $scope.data.Address = primaryEligRecord.Address1.toLowerCase() + ' ' + address2;
19352                $scope.data.City = primaryEligRecord.City;
19353                $scope.data.State = primaryEligRecord.State;
19354                $scope.data.Zip = primaryEligRecord.Zip;
19355                //2015-05-12T00:00:00z in chrome using Date gives the wrong birth date.
19356                $scope.data.dateOfBirth = new Date(primaryEligRecord.DateOfBirth.replace(/-/g, '\/').replace(/T.+/, ''));
19357                $scope.data.phone = primaryEligRecord.Phone;
19358
19359                $scope.data.providerId = primaryEligRecord.PcpId || '1111111111';
19360
19361            });
19362        };
19363
19364        $scope.$on('destroy',
19365            function () {
19366                if (submitDrugRequestTimeout) $timeout.cancel(submitDrugRequestTimeout);
19367            });
19368
19369        $scope.SubmitButton = function () {
19370
19371            //Add medication input to list.
19372            $scope.addMedication('medication');
19373            $scope.addMedication('reason');
19374            //If doctor name is empty then mark it as unknown
19375            if ($scope.data.doctorName === '') {
19376                $scope.data.doctorName = 'Unknown';
19377            }
19378            //Timeout of zero ensures function runs after front end changes
19379            //by angularjs model changes.
19380            submitDrugRequestTimeout = $timeout(function () {
19381                $scope.SubmitDrugRequest();
19382            }, 0);
19383        };
19384        /*
19385         * Function that is called when submit button is pressed.
19386         * The function only works if the form is valid with required
19387         * field filled out.
19388         */
19389        $scope.SubmitDrugRequest = function () {
19390
19391            $scope.submitted = true;
19392
19393            if ($scope.prescriptionForm.$valid && $scope.radioRequestor === false) {
19394
19395                $scope.loading = !$scope.loading;
19396                $scope.expeditedShow = true;
19397
19398                $('input[type=text]').each(function (key, value) {
19399                    const item = $(value);
19400                    item.css('word-break', 'break-word');
19401                    item.attr('value', $(value).val());
19402
19403                    item.attr('placeholder', '');
19404                });
19405
19406                $('input[type=number]').each(function (key, value) {
19407                    const item = $(value);
19408                    item.css('word-break', 'break-word');
19409                    item.attr('value', item.val());
19410                    item.css('height', item[0].scrollHeight);
19411                    item.attr('placeholder', '');
19412                });
19413
19414                $('link').each(function (key, value) {
19415                    $(value).attr('href', $(value).prop('href'));
19416                });
19417
19418                $('input[type=radio]').each(function (key, value) {
19419                    $(value).attr('checked', $(value).prop('checked'));
19420                });
19421
19422                $('input[type=checkbox]').each(function (key, value) {
19423                    $(value).attr('checked', $(value).prop('checked'));
19424                });
19425
19426                $('textarea').each(function (key, value) {
19427                    const item = $(value);
19428                    item.css('word-break', 'break-word');
19429                    item.html(item.val());
19430                    item.css('height', item[0].scrollHeight * 1.15);
19431                    item.attr('placeholder', '');
19432                });
19433
19434                // Create a clone of the form for modifications and pdf in MedHok.
19435                var htmlData = $('.contentRight').clone();
19436                
19437                htmlData.find('p.dateOfSubmission').text('Submitted to IEHP on ' + getDateFormatted(new Date()));
19438
19439                htmlData.find('input[type=checkbox]').each(function (_key, value) {
19440                    $(value).attr('checked', $(value).prop('checked'));
19441                });
19442
19443                // replace input fields with paragraphs.
19444                var value = htmlData.find('textarea').val();
19445
19446                htmlData.find('input').not(':checkbox').each(function () {
19447                    $(this).replaceWith("<p style='display:inline'>" + this.value + '</p>');
19448                });
19449
19450                htmlData.find('textarea').replaceWith('<p>' + value + '</p>').end();
19451                htmlData.find('div.success').remove().end();
19452                htmlData.find('.hidden-print').remove().end();
19453                htmlData.find('.form').removeClass('ng-hide').end().find('*').not('form,ng-form,div,drug-input,table,tbody').addClass('printableFontSize').end();
19454                htmlData.find('div.panel').css('background-color', 'white');
19455
19456                if ($scope.drugList.length > 6) {
19457                    htmlData.find('div.form-group').css('font-size', '.8em');
19458                    htmlData.find('table.table').css('font-size', '.7em');
19459                    htmlData.find('label.control-label').css('font-size', '1em');
19460                }
19461
19462                htmlData = htmlData.find('script').remove().end().html();
19463
19464                var htmlHead = `<html><head>${$('head').html()}</head><body style='background-image:none;'>${htmlData}</body><html>`;
19465
19466                var drugCaseRequest = {
19467                    memberId: $scope.data.memberId,
19468                    alternatePhone: $scope.data.alternatePhone,
19469                    doctorName: $scope.data.doctorName,
19470                    doctorPhone: $scope.data.doctorPhone,
19471                    expedited: $scope.data.expedited,
19472                    additionalInformation: $scope.data.additionalInformation,
19473                    providerId: $scope.data.providerId,
19474                    medicationList: $scope.drugList,
19475                    lobName: $scope.data.lobName,
19476                    lob: $scope.data.lob,
19477                    internalUser: $scope.data.internalUser,
19478                    htmlData: htmlHead
19479                };
19480
19481                memberDataService.PostDrugRequest(JSON.stringify(drugCaseRequest)).then(
19482
19483                    function (response) {
19484                        $scope.data.rxCaseNumber = response.data.resultRxId;
19485                        $scope.recordId = response.data.primaryKey;
19486                        $scope.successfulSubmit = true;
19487                        $scope.toggleForm = true;
19488                        $scope.expeditedShow = false;
19489                        $scope.loading = !$scope.loading;
19490                    },
19491
19492                    function (error) {
19493                        console.log(error);
19494                        $scope.submitError = true;
19495                        $scope.successfulSubmit = false;
19496                        $scope.loading = !$scope.loading;
19497                    });
19498            }
19499        };
19500
19501        /* Helper Functions that checks what LOB there is.
19502         * If the line of business is associated with medicare
19503         * return medicare. If it is associated with Med-cal return
19504         * Medi-Cal. Otherwise return empty string.
19505         */
19506        function lobDisplay(lob) {
19507            switch (lob) {
19508                case 'MCR':
19509                case 'CMC':
19510                    $scope.data.lobName = 'Medicare';
19511                    $scope.data.expedited = false;
19512                    break;
19513                case 'MED':
19514                case 'MCL':
19515                case 'CCI':
19516                    $scope.data.lobName = 'Medi-Cal';
19517                    break;
19518                default:
19519                    break;
19520            }
19521        };
19522
19523        //Returns the date formatted mm/dd/yyyy hh:mm am/pm
19524        function getDateFormatted(date) {
19525            var dateString = date.getMonth() + 1 + '/' + date.getDate() + '/' + date.getFullYear();
19526            var meridiem = 'PM';
19527            var hours = date.getHours() % 12;
19528            var minutes = date.getMinutes();
19529            if (hours === 0) {
19530                hours = 12;
19531            }
19532            if (date.getHours() < 12) {
19533                meridiem = 'AM';
19534            }
19535            if (minutes < 10) {
19536                minutes = '0' + minutes;
19537            }
19538            dateString += ` ${hours}:${minutes}${meridiem}`;
19539            return dateString;
19540        }
19541
19542        // Call endpoint for downloadable pdf content.
19543        $scope.downloadPdf = function () {
19544            $('#btnDownloadPdf').html($filter('translate')('downloading'));
19545            $('#btnDownloadPdf').prop("disabled", true);
19546
19547            var fileName = 'PharmacyExceptionRequest.pdf';
19548            const params = {
19549                primaryKey: $scope.recordId,
19550                rxId: $scope.data.rxCaseNumber
19551            };
19552            if ($scope.data.lobName === 'Medicare') {
19553                fileName = 'MedicarePrescriptionDrugCoverageDetermination.pdf';
19554            }
19555            memberDataService.DrugRequestDisplayPdf(params).success(function (data, textStatus) {
19556
19557                if (textStatus === 200) {
19558
19559                    const file = new Blob([data], { type: 'application/pdf' });
19560
19561                    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
19562
19563                        window.navigator.msSaveOrOpenBlob(file, fileName);
19564                    }
19565                    else {
19566
19567                        const fileUrl = URL.createObjectURL(file);
19568                        const link = document.createElement('a');
19569                        document.body.appendChild(link);
19570                        link.href = fileUrl;
19571                        link.download = fileName;
19572                        link.click();
19573                        document.body.removeChild(link);
19574                    }
19575                }
19576            }).error(function () {
19577            }).finally(function() {
19578                $('#btnDownloadPdf').html($filter('translate')('done'));
19579            });
19580        };
19581
19582        //Function for restarting a Form
19583        $scope.resetForm = function () {
19584            $route.reload();
19585        };
19586
19587    };
19588})();
19589(function () {
19590    'use strict';
19591
19592
19593    angular
19594        .module('MemberPortal')
19595        .controller('RxPrescriptionsController', RxPrescriptionsController);
19596
19597    RxPrescriptionsController.$inject = [
19598        '$scope', '$rootScope', 'authStatusService', 'memberDataService', '$filter', 'dialogs',
19599        'requestedLanguageService', '$timeout'
19600    ];
19601
19602    function RxPrescriptionsController($scope, $rootScope, authStatusService,
19603        memberDataService, $filter, $dialogs, requestedLanguageService, $timeout) {
19604
19605        const authStatus = authStatusService.getStatus();
19606
19607        $scope.lob = authStatus.claims.lob;
19608
19609        $scope.medImpactSsoUrl = $('meta[name="medImpactSsoUrl"]').attr('content');
19610        
19611        $scope.loading = false;
19612        $scope.errorMessage = '';
19613        $scope.infoMessage = '';
19614        $scope.data = [];
19615        $scope.PagedRx = [];
19616        $scope.itemsPerPage = 10;
19617        $scope.currentPage = 0;
19618        $scope.gap = 0;
19619        
19620        var focusTimeout;
19621
19622        $scope.agegroup = authStatus.claims.agegroup;
19623        $scope.isDSNP = authStatus.isDSNP;
19624
19625        $scope.noResults = false;
19626        $scope.rxPrescriptionDrugRefillDate = 'Refill Date';
19627        $scope.rxPrescriptionDrugName = 'Drug Name';
19628        $scope.LobDescription = 'Open Access';
19629        $scope.showPrescriptionsData = false;
19630        $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
19631
19632        $scope.$on('language-change', function (_event, key) {
19633            $scope.currentLanguage = key;
19634        });
19635
19636        $('#navbtn6 div').addClass('navItemSelected'); //Highlight Nav Item- My Health Records
19637
19638        $scope.UserMessage = {
19639            show: false,
19640            title: '',
19641            message: ''
19642        };
19643
19644        $scope.pageItems = function (arrayTopage) {
19645
19646            $scope.RxTable = arrayTopage;
19647
19648            const arrayLength = arrayTopage.length;
19649
19650            for (let i = 0; i < arrayLength; i++) {
19651                if (i % $scope.itemsPerPage === 0) {
19652                    $scope.PagedRx[Math.floor(i / $scope.itemsPerPage)] = [arrayTopage[i]];
19653                } else {
19654                    $scope.PagedRx[Math.floor(i / $scope.itemsPerPage)].push(arrayTopage[i]);
19655                }
19656            }
19657
19658            $scope.gap = $scope.PagedRx.length;
19659        };
19660
19661        $scope.range = function (size, start, end) {
19662            const ret = [];
19663            if (size < end) {
19664                end = size;
19665                start = size - $scope.gap;
19666            }
19667            for (let i = start; i < end; i++) {
19668                ret.push(i);
19669            }
19670            return ret;
19671        };
19672
19673        $scope.setPage = function (n) {
19674            $scope.currentPage = n;
19675        };
19676
19677        $scope.prevPage = function () {
19678            if ($scope.currentPage > 0) {
19679                $scope.currentPage--;
19680            }
19681        };
19682
19683        $scope.nextPage = function () {
19684            if ($scope.currentPage < $scope.PagedRx.length - 1) {
19685                $scope.currentPage++;
19686            }
19687        };
19688
19689        $scope.getData = function () {
19690            $scope.loadingDrugHistory = !$scope.loadingDrugHistory;
19691            memberDataService.LoadRxPrescriptions().success(function (data) {
19692                $scope.loadingDrugHistory = !$scope.loadingDrugHistory;
19693                $scope.data = data;
19694                if ($scope.data === null || $scope.data.length === 0) {
19695                    $scope.noResults = true;
19696                    console.log('we are so here', $filter('translate')('form_noPharmacyInformationFound'))
19697                    $scope.infoMessage = $filter('translate')('form_noPharmacyInformationFound');
19698                }
19699                if (data) {
19700                    $scope.pageItems(data);
19701                    dataUpdated(data);
19702                }
19703
19704            }).error(function () {
19705
19706                $scope.loadingDrugHistory = !$scope.loadingDrugHistory;
19707                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
19708            }
19709            );
19710
19711            function dataUpdated(data) {
19712                for (let index in data) {
19713                    if (Object.prototype.hasOwnProperty.call(data, index)) {
19714                        // Convert the Data for display
19715                        // Form Prescribe by using Physician First and Last Name
19716                        data[index].PhysFullName =
19717                            data[index].PhysFirstName.trim() + ' ' + data[index].PhysLastName.trim();
19718                        // Form OrderDateDisplay by formatting OrderDate
19719                        data[index].OrderDateDisplay = $filter('date')(data[index].OrderDate, 'MM/dd/yy');
19720                    }
19721                }
19722
19723                $scope.rxData = {
19724                    Labels: [
19725                        'form_drugName',
19726                        'form_prescriber',
19727                        'form_filledBy',
19728                        'form_qty',
19729                        'form_filledOn'
19730                    ],
19731
19732                    ObjectPropertyNames: [
19733                        'DrugName',
19734                        'PhysFullName',
19735                        'PhrmName',
19736                        'MetricQuantity',
19737                        'OrderDateDisplay'
19738                    ],
19739                    Objects: data,
19740
19741                    Headings: {
19742                        "XS": ['Drug Info', 'Filled Info'] // two columns of data
19743                    },
19744
19745                    ColumnDefinition: {
19746                        "XS": [[0, 1], [4, 2, 3]] // two columns of data
19747                    }
19748                };
19749            }
19750        };       
19751
19752        //================== Modal Declaration
19753        $scope.showModal = function (type, description, options) {
19754            switch (type) {
19755                case 'SuccessfulRequest': $dialogs.notify($filter('translate')('page_healthEd_availableCourses_requestModalTitle'),
19756                    `<center class="overflowAuto">${$filter('translate')('page_healthEd_availableCourses_requestModalRequestSubmittedOk')}</center>`);
19757                    break;
19758                case 'ERROR': $dialogs.error($filter('translate')('page_rxPrescriptions_ERROR'),
19759                    `<center class="overflowAuto">${options}</center>`);
19760                    break;
19761
19762                case 'showReminderOptions':
19763                    $dialogs.create('/dialogs/rxReminders.html', 'RxRemindersController', {
19764                        desc: description, alertID: options
19765                    }, { key: false, back: 'static' });
19766
19767                    break;
19768            }
19769        };
19770
19771        getDoctor();
19772
19773        /*
19774         * Function for getting current eligibility.
19775         * Used to get member information for auto
19776         * filling form. Also used to get PcpId.
19777         */
19778        function getDoctor() {
19779
19780            const language = requestedLanguageService.getCurrentLanguage();
19781
19782            memberDataService.getCurrentEligibility(language).success(function (data) {
19783
19784                const primaryEligRecord = memberDataService.utility.getPrimaryEligibilityRecord(data);
19785
19786                $scope.LobDescription = primaryEligRecord.LobDescription;
19787            });
19788        };
19789        
19790
19791        //============= Show Modal
19792        $scope.showReminderOptions = function () {
19793            console.log('@@SHOWREM OPT');
19794        };
19795
19796        //============= INITIALIZE
19797        if ($scope.agegroup === '18+') {
19798            $scope.showPrescriptionsData = true;
19799            $scope.getData();
19800        }
19801        
19802
19803        $scope.$on('destroy',
19804            function () {
19805                if (focusTimeout) $timeout.cancel(focusTimeout);
19806            });
19807
19808        $scope.focusOnElement = function (elementId) {
19809            focusTimeout = $timeout(function () {
19810                $('#' + elementId).focus();
19811            }, 500);
19812        };
19813
19814        $scope.focusOnElement('btnRxPrescriptions');
19815    };
19816})();
19817(function () {
19818    'use strict';
19819
19820    angular
19821        .module('MemberPortal')
19822        .controller('RxRemindersController', RxRemindersController);
19823
19824    RxRemindersController.$inject = [
19825        '$scope', '$filter', 'data', 'memberDataService'
19826    ];
19827
19828    function RxRemindersController($scope, $filter, data, memberDataService) {
19829
19830        $scope.msg = data.desc;
19831        $scope.alertID = '';
19832        $scope.reminderOption0Label = $filter('translate')('page_healthrec_rxPrescriptions_reminder_option1');
19833        $scope.reminderOption1Label = $filter('translate')('page_healthrec_rxPrescriptions_reminder_option2');
19834        $scope.reminderOption2Label = $filter('translate')('page_healthrec_rxPrescriptions_reminder_option3');
19835        $scope.reminderOption3Label = $filter('translate')('page_healthrec_rxPrescriptions_reminder_option4');
19836
19837        $scope.reminderOptionChecked = [];
19838        $scope.reminderOptionChecked[0] = false;
19839        $scope.reminderOptionChecked[1] = false;
19840        $scope.reminderOptionChecked[2] = false;
19841        $scope.reminderOptionChecked[3] = false;
19842
19843        $scope.UpdateNotificationStatus = function (alertId) {
19844            $scope.alertID = alertId;
19845            memberDataService.mdsUpdateNotificationStatus(alertId).success(function () {
19846                $('#alert_' + alertId).fadeOut('fast');
19847            }).error(function () {
19848                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
19849            });
19850        };
19851
19852        $scope.snooze = function () {
19853            $('.modal').fadeOut('fast');
19854            $('[id^=alert_' + data.alertID + ']').fadeOut('slow', function () {
19855                $(this).remove();
19856            });
19857            $scope.UpdateNotificationStatus(data.alertID);
19858        };
19859        $scope.reminderOptionClicked = function (n) {
19860
19861            $scope.reminderOptionChecked[n] = !$scope.reminderOptionChecked[n];
19862
19863            if ($scope.reminderOptionChecked[n])
19864                $('#ckbox_option' + n).attr('checked', true);
19865
19866            //=========================================
19867            //=========================================
19868            //========================================= CHECKBOX TOGGLING WORKING SPORADICALLY, NEED TO COME BACK TO THIS
19869            //=========================================
19870            //=========================================
19871
19872            //console.log('n=' + n);
19873            if (n === 3) {
19874                console.log('3 checked=' + $scope.reminderOptionChecked[n]);
19875                if ($scope.reminderOptionChecked[n]) {
19876                    $scope.reminderOptionChecked[0] = false;
19877                    $('#ckbox_option0').attr('checked', false);
19878
19879                    $scope.reminderOptionChecked[1] = false;
19880                    $('#ckbox_option1').attr('checked', false);
19881
19882                    $scope.reminderOptionChecked[2] = false;
19883                    $('#ckbox_option2').attr('checked', false);
19884                }
19885            }
19886            else {
19887                $scope.reminderOptionChecked[3] = false;
19888
19889                $('#ckbox_option3').attr('checked', false);
19890            }
19891        };
19892
19893        $scope.closeModal = function () {
19894            $('.modal').fadeOut('fast');
19895        };
19896
19897        $scope.closeReminderModal = function () {
19898            console.log('CKBOX1=' + $scope.reminderOptionChecked[0]);
19899            console.log('CKBOX2=' + $scope.reminderOptionChecked[1]);
19900            console.log('CKBOX3=' + $scope.reminderOptionChecked[2]);
19901            console.log('CKBOX4=' + $scope.reminderOptionChecked[3]);
19902
19903            $('.modal').fadeOut('fast');
19904        };
19905
19906        $scope.hitEnter = function (evt) {
19907            if (angular.equals(evt.keyCode, 13) &&
19908                !(angular.equals($scope.name, null) || angular.equals($scope.name, '')))
19909                $scope.save();
19910        };
19911    };
19912})();
19913(function () {
19914    'use strict';
19915
19916    angular
19917        .module('MemberPortal')
19918        .controller('RequestNewIdController', RequestNewIdController);
19919
19920    RequestNewIdController.$inject = ['$scope', '$filter', 'memberDataService', 'authStatusService', '$timeout'];
19921
19922    function RequestNewIdController($scope, $filter, memberDataService, authStatusService, $timeout) {
19923
19924        $scope.requestHistory = [];
19925        $scope.hasPendingChange = true;
19926
19927        const claims = authStatusService.getStatus().claims;
19928
19929        var focusTimeout;
19930
19931        $scope.memberNumber = claims.subno;
19932
19933        function setPendingChangeStatus() {
19934            $scope.hasPendingChange = $scope.requestHistory.some(request => !request.IsCancelled && request.DateSynchronized === null);
19935        }
19936
19937        function handleSuccessResponse(response, callback) {
19938            callback(response.data);
19939        }
19940
19941        function handleErrorResponse(error) {
19942            $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
19943            console.error('Error:', error);
19944        }
19945
19946        $scope.GetCurrentIdCardRequests = function () {
19947            memberDataService.GetCurrentIdCardRequests($scope.memberNumber)
19948                .then(response => handleSuccessResponse(response, data => {
19949                    $scope.requestHistory = data;
19950                    setPendingChangeStatus();
19951                    $scope.infoMessage = $filter('translate')('no_requests_currently_available');
19952                }))
19953                .catch(handleErrorResponse);
19954        };
19955
19956        $scope.SubmitNewIdCardRequest = function () {
19957            memberDataService.SubmitNewIdCardRequest()
19958                .then(response => handleSuccessResponse(response, data => {
19959                    $scope.GetCurrentIdCardRequests();
19960                }))
19961                .catch(handleErrorResponse);
19962        };
19963
19964        $scope.CancelIdCardRequest = function (memberIdCardRequestKey) {
19965            const payload = {
19966                memberIdCardRequestKey,
19967                IsCancelled: true
19968            };
19969
19970            memberDataService.CancelIdCardRequest(payload)
19971                .then(response => handleSuccessResponse(response, data => {
19972                    $scope.GetCurrentIdCardRequests();
19973                }))
19974                .catch(handleErrorResponse);
19975        };
19976
19977
19978        $scope.$on('destroy',
19979            function () {
19980                if (focusTimeout) $timeout.cancel(focusTimeout);
19981            });
19982
19983        $scope.focusOnElement = function (elementId) {
19984            focusTimeout = $timeout(function () {
19985                $('#' + elementId).focus();
19986            }, 500);
19987        };
19988
19989        // Initialize
19990        $scope.GetCurrentIdCardRequests();
19991        $scope.focusOnElement('btnReqIDCard');
19992    }
19993})();
19994
19995(function () {
19996    'use strict';
19997
19998    angular
19999        .module('MemberPortal')
20000        .controller('temporaryIdCardController', temporaryIdCardController);
20001
20002    temporaryIdCardController.$inject = [
20003        '$scope',
20004        '$rootScope',
20005        '$filter',
20006        'localStorageService',
20007        'memberIDCardService',
20008        'utilitiesService',
20009        'authStatusService',
20010        'memberDataService',
20011        '$timeout'
20012    ];
20013
20014    function temporaryIdCardController(
20015        $scope,
20016        $rootScope,
20017        $filter,
20018        localStorageService,
20019        memberIDCardService,
20020        utilitiesService,
20021        authStatusService,
20022        memberDataService,
20023        $timeout) {
20024
20025        var focusTimeout;
20026
20027        $scope.addToGoogleWallet = addToGoogleWallet;
20028
20029        $scope.addToAppleWallet = addToAppleWallet; 
20030
20031        init();
20032
20033        function init() {
20034            $scope.isMobile = utilitiesService.isMobile();
20035
20036            $scope.cardViewSide = 'front';
20037
20038            $rootScope.ShowMemberIDCard = false;
20039
20040            $scope.errorMessage = '';
20041            $scope.message = '';
20042
20043            $rootScope.IEHPTemporaryIDCardFetchStatus = '';
20044
20045            $rootScope.ShowUrgentMessage = $filter('translate')('page_idcard_requestIDCard_urgentMessage') !== '';
20046
20047            $scope.infoMessage = $filter('translate')('form_fetchingIEHPMemberIDCard');
20048
20049            $scope.canShowDigitalPass = false;
20050
20051            $scope.digitalPass = {
20052                'Android': {
20053                    show: false
20054                },
20055                'iOS': {
20056                    show: false
20057                }
20058            }
20059
20060            getMobileDigitalPass();
20061
20062            $('#divIframeCont').fadeOut(50);
20063
20064            $('#navbtn2 div').addClass('navItemSelected'); //Highlight Nav Item- Mt IEHP ID Card
20065
20066            $rootScope.showErrorMessage = false;
20067            
20068            //Prepare Card Expiration Date (at end of month)
20069            var expDate = new Date();
20070            
20071            expDate = new Date(expDate.getFullYear(), expDate.getMonth() + 1, 0);
20072            
20073            $scope.expirationDate = formatDate(expDate); // Last day of month
20074
20075            focusOnElement('btnPrintTempCard');
20076
20077            getUrgentContactList();
20078
20079            getIdCard();
20080        }
20081
20082        function getMobileDigitalPass() {
20083            
20084            const isPhoneScreen = Math.min(window.screen.width, window.screen.height) < 768;
20085
20086            if ($scope.isMobile && isPhoneScreen) {
20087                const device = utilitiesService.getMobileOperatingSystem();
20088
20089                const deviceList = ['Android', 'iOS'];
20090                if (!deviceList.includes(device)) {
20091
20092                    deviceList.forEach(device => {
20093                        $scope.digitalPass[device].show = false;
20094                    });
20095
20096                    $scope.canShowDigitalPass = false;
20097                    $scope.loading = false;
20098                } else {
20099                    getDigitalPass(device);
20100                }
20101            }
20102        }
20103
20104        function formatDate(inDate) {
20105            
20106            var dd = inDate.getDate();
20107            var mm = inDate.getMonth() + 1; //January is 0!
20108            const yyyy = inDate.getFullYear();
20109
20110            if (dd < 10) {
20111                dd = `0${dd}`;
20112            }
20113
20114            if (mm < 10) {
20115                mm = `0${mm}`;
20116            }
20117
20118            const formattedDate = mm + '/' + dd + '/' + yyyy;
20119
20120            return formattedDate;
20121        };
20122
20123        function downloadDigitalPass(device) {
20124            const memberId = $scope.member.SubscriberId;
20125            const dob = $scope.member.DateOfBirth;
20126            $scope.showLoader = true;
20127
20128            memberDataService.GetMobilePass(device, memberId, dob).then(function (resp) {
20129                $scope.showLoader = false;
20130
20131                if (resp.data.response) {
20132                    const pass = resp.data.response.replace(/["]+/g, '');
20133                    // Create a temporary link element
20134                    const link = document.createElement('a');
20135
20136                    if (device === 'iOS') {
20137                        // Decode the base64 string to binary
20138                        const decoded = atob(pass);
20139
20140                        // Convert binary string to ArrayBuffer
20141                        const arrayBuffer = new Uint8Array(decoded.length);
20142                        for (let i = 0; i < decoded.length; i++) {
20143                            arrayBuffer[i] = decoded.charCodeAt(i);
20144                        }
20145
20146                        // Create a Blob from the ArrayBuffer
20147                        const blob = new Blob([arrayBuffer], { type: 'application/vnd.apple.pkpass' });
20148                        link.href = URL.createObjectURL(blob);
20149                        link.download = 'iehp-id-card.pkpass';
20150                    } else {
20151                        link.href = pass;
20152                    }
20153
20154                    // Append the link to the document and simulate a click
20155                    document.body.appendChild(link);
20156                    link.click();
20157
20158                    // Clean up by removing the link element
20159                    document.body.removeChild(link);
20160
20161                }
20162            }).catch(function (e) {
20163                console.error('unable to get digital pass for ' + device, e);
20164                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
20165                $scope.loading = false;
20166                $scope.canShowDigitalPass = false;
20167                $scope.showLoader = false;
20168            });
20169        }
20170
20171        function getUrgentContactList() {
20172            memberDataService.getUrgentContactList().success(function (response) {
20173
20174                if (!response.iehpPhones) return [];
20175
20176                const claims = authStatusService.getStatus().claims;
20177
20178                const lob = claims.lob;
20179
20180                const phoneList = response.iehpPhones.find(m => m.lobCode === lob);
20181
20182                console.log('$scope.phoneList');
20183
20184                return $scope.phoneList = phoneList ? phoneList.phones : [];
20185
20186            }).error(function () {
20187                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
20188                $scope.phoneList = [];
20189            });
20190        }
20191
20192        $scope.showModal = function () {
20193            $('#memberIdCardModal').modal({ backdrop: true });
20194        }
20195        
20196        function getDigitalPass(device) {
20197            $scope.showLoader = true;
20198
20199            //Prepare Google and Apple Pass Link
20200            memberDataService.GetDemographics().then(function (resp) {
20201                $scope.member = resp.data;
20202                $scope.canShowDigitalPass = true;
20203                $scope.digitalPass[device].show = true;
20204                $scope.showLoader = false;
20205            }).catch(function (e) {
20206                console.warn('unable to get demographics', e);
20207                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
20208                $scope.loading = false;
20209                $scope.canShowDigitalPass = false;
20210                $scope.showLoader = false;
20211            });
20212        }
20213
20214        function focusOnElement(elementId) {
20215            focusTimeout = $timeout(function () {
20216                $('#' + elementId).focus();
20217            }, 500);
20218        };
20219
20220        function addToGoogleWallet() {
20221            downloadDigitalPass('Android');
20222        }
20223
20224        function addToAppleWallet() {
20225            downloadDigitalPass('iOS');
20226        }
20227
20228        //Listener for Successful broadcast
20229        $rootScope.$on('IDCardFetched', function () {
20230
20231            $scope.infoMessage = '';
20232
20233            if ($rootScope.IEHPTemporaryIDCardFetchStatus === 'Success') {
20234
20235                $rootScope.showLoader = false;
20236                $rootScope.ShowMemberIDCard = true;
20237                $('#divIframeCont').fadeOut('slow');
20238
20239                $rootScope.IEHPTemporaryIDCardFrontSide = localStorageService.get('IDC_front');
20240                $rootScope.IEHPTemporaryIDCardBackSide = localStorageService.get('IDC_back');
20241            }
20242            else {
20243                $rootScope.IdNotCardFound = true;
20244                $('#divIframeCont').fadeIn('slow');
20245                $scope.infoMessage = '';
20246            }
20247        });
20248
20249        //Listener for Unsuccessful broadcast
20250        $rootScope.$on('IDCardFetchingError', function () {
20251
20252            $scope.message = $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
20253        });
20254
20255        function getIdCard() {
20256            if (localStorageService.get('IDC_fetchStatus') === 'Success') {
20257
20258                if ($rootScope.currentPage !== 'LoginForm' && $rootScope.IEHPTemporaryIDCardFrontSide == undefined) {
20259
20260                    if ($rootScope.IEHPTemporaryIDCardFrontSide == undefined) {
20261
20262                        memberIDCardService.GetMemberTempCardImage().then(function () {
20263
20264                            $rootScope.IEHPTemporaryIDCardFrontSide = localStorageService.get('IDC_front');
20265                            $rootScope.IEHPTemporaryIDCardBackSide = localStorageService.get('IDC_back');
20266                            $rootScope.ShowMemberIDCard = true;
20267                        }, function () {
20268
20269                            $rootScope.ShowMemberIDCard = false;
20270                            $rootScope.IdCardNotFound = true;
20271                        });
20272
20273                    }
20274                    else {
20275                        $rootScope.ShowMemberIDCard = true;
20276                        $scope.errorMessage = '';
20277                        $scope.infoMessage = '';
20278                    }
20279                }
20280                else {
20281
20282                    $scope.infoMessage = '';
20283                    $rootScope.ShowMemberIDCard = true;
20284                    $('#divIframeCont').fadeOut('slow');
20285                    $rootScope.IEHPTemporaryIDCardFrontSide = localStorageService.get('IDC_front');
20286                    $rootScope.IEHPTemporaryIDCardBackSide = localStorageService.get('IDC_back');
20287                }
20288            }
20289            else {
20290                $rootScope.ShowMemberIDCard = false;
20291                $scope.infoMessage = '';
20292            }
20293
20294            if ($rootScope.IEHPTemporaryIDCardFrontSide == undefined && localStorageService.get('IDC_fetchStatus') === 'Success') {
20295
20296                $rootScope.IEHPTemporaryIDCardFrontSide = localStorageService.get('IDC_front');
20297                $rootScope.IEHPTemporaryIDCardBackSide = localStorageService.get('IDC_back');
20298                $rootScope.ShowMemberIDCard = true;
20299                $scope.infoMessage = '';
20300                $rootScope.showLoader = false;
20301            }
20302            if (localStorageService.get('IDC_fetchStatus') === 'Not Found') {
20303
20304                $rootScope.showLoader = false;
20305                $rootScope.showErrorMessage = true;
20306                $scope.infoMessage = '';
20307                $('#divIframeCont').fadeIn('slow');
20308
20309            }
20310
20311        }
20312        
20313        $scope.$on('destroy',
20314            function () {
20315                if (focusTimeout) $timeout.cancel(focusTimeout);
20316            });
20317    };
20318})();
20319(function () {
20320    'use strict';
20321
20322    angular
20323        .module('MemberPortal')
20324        .controller('AddDelegateController', AddDelegateController);
20325
20326    AddDelegateController.$inject = ['healthDelegateService', 'authStatusService', '$location', '$scope'];
20327
20328    function AddDelegateController(healthDelegateService, authStatusService, $location, $scope) {
20329        
20330        const isProxied = authStatusService.checkIfProxied();
20331
20332        const isDelegateEligible = healthDelegateService.getIsCaregiverEligible();
20333
20334        //caregiver and ineligible member should not be able to add a new caregiver
20335        if (isProxied || !isDelegateEligible) {
20336            $location.path('App/Home');
20337        }
20338        //sharing the "add caregiver" and "request caregiver access" controller and view
20339        $scope.isRequestDelegate = $location.path().includes('request-delegate');
20340        console.log('is requesting delegate', $scope.isRequestDelegate);
20341
20342        $scope.pageTitle = $scope.isRequestDelegate ? 'Request Caregiver Access': 'Add a Caregiver';
20343        
20344        //console.log('$location.path()', $location.path());
20345    };
20346})();
20347(function () {
20348  'use strict';
20349
20350  angular
20351      .module('MemberPortal')
20352      .controller('DelegatesController', DelegatesController);
20353
20354  DelegatesController.$inject = [
20355      '$scope',
20356      'memberDataService',
20357      'requestedLanguageService',
20358      'authStatusService',
20359      '$location',
20360      'healthDelegateService',
20361      '$rootScope'
20362  ];
20363
20364    function DelegatesController($scope, memberDataService,
20365        requestedLanguageService, authStatusService, $location, healthDelegateService, $rootScope) {
20366
20367        init();
20368
20369        function init() {
20370
20371            const isProxied = authStatusService.checkIfProxied();
20372            console.log('isProxied', isProxied);
20373
20374            if (!isProxied) {
20375
20376                const isDelegateEligible = healthDelegateService.getIsCaregiverEligible();
20377
20378                if (!isDelegateEligible) {
20379                    //if a member accesses his own acc. but he is not delegate eligible, redirect to home
20380                    $location.path("App/Home");
20381                }
20382            }
20383
20384            $scope.errorMessage = '';
20385            $scope.successMessage = '';
20386            $scope.infoMessage = '';
20387            $scope.loading = false;
20388
20389            $scope.currentSection = 'processed';
20390            
20391            const queryParams = $location.search();
20392
20393            loadDefinition();
20394
20395            if (queryParams && queryParams.tab) {
20396                $scope.currentSection = queryParams.tab;
20397
20398                getPendingTotal();
20399
20400                const displayMsg = healthDelegateService.getPostUpSertMsg();
20401
20402                if (!displayMsg) return;
20403
20404                //reset msg
20405                healthDelegateService.setPostUpSertMsg('');
20406                
20407                console.log('display msg', displayMsg);
20408
20409                showUpdateResultMsg(displayMsg);
20410               
20411            } else {
20412
20413                const currentLanguage = requestedLanguageService.getCurrentLanguage();
20414
20415                const claims = authStatusService.getStatus().claims;
20416
20417                if (!claims) return;
20418
20419                const memberId = claims.username;
20420
20421                $scope.loading = true;
20422
20423                memberDataService.getPendingDelegates(memberId, currentLanguage).then(
20424                    function (resp) {
20425
20426                        console.log('delegate page: pending delegates from api', resp.data);
20427
20428                        if (!resp || !resp.data || resp.data.length === 0) { return; }
20429
20430                        if (resp.data.length > 0) {
20431                            $scope.currentSection = 'pending';
20432                            $scope.pendingRequests = resp.data;
20433                            $rootScope.$broadcast('refreshPendingTotal', resp.data.length);
20434                        }
20435                    }).finally(function() {
20436                    $scope.loading = false;
20437                });
20438            }
20439        }
20440
20441        function showUpdateResultMsg(msg) {
20442
20443            if (!msg) return;
20444
20445            Toastify({
20446                text: msg,
20447                offset: {
20448                    x: 0,
20449                    y: '3em'
20450                },
20451                backgroundColor: '#ffffff',
20452                duration: 3000,
20453                gravity: 'top',
20454                position: 'center',
20455                style: {
20456                    borderRadius: '5px',
20457                    border: '2px solid #4caf50',
20458                    color: 'black',
20459                    fontSize: '16px',
20460                    fontWeight: 'bold'
20461
20462                }
20463            }).showToast();
20464        }
20465
20466        function loadDefinition() {
20467
20468            const language = requestedLanguageService.getCurrentLanguage();
20469
20470            memberDataService.GetDelegateDef(language).then(
20471                function (definitions) {
20472                    $scope.definitions = definitions;
20473                    
20474                }
20475            );
20476        }
20477
20478        function getPendingTotal() {
20479            //console.log('getPendingTotal');
20480            healthDelegateService.loadPendingTotal().then(
20481                function (resp) {
20482                   // console.log('load pending total', resp);
20483                    
20484                    if (!resp) return;
20485
20486                    if (resp > 0) {
20487
20488                        $scope.pendingRequests = resp;
20489
20490                        $rootScope.$broadcast('refreshPendingTotal', resp);
20491                    }
20492                });
20493        } 
20494
20495        $scope.$on('language-change', function () {
20496
20497            init();
20498
20499        });
20500
20501        $rootScope.$on('switchingProfile',
20502            function() {
20503                init();
20504            });
20505
20506        $scope.toggleSection = function (section) {
20507            $scope.currentSection = section;
20508            $location.search('tab', section);
20509        };
20510    }
20511})();
20512(function () {
20513    'use strict';
20514
20515    angular
20516        .module('MemberPortal')
20517        .controller('MyCorrespondencesController', MyCorrespondencesController);
20518
20519    MyCorrespondencesController.$inject = ['$scope',
20520        'authStatusService', 'memberDataService', '$routeParams',
20521        '$filter', '$rootScope', '$timeout'];
20522
20523    function MyCorrespondencesController($scope,
20524        authStatusService, memberDataService, $routeParams,
20525        $filter, $rootScope, $timeout) {
20526        
20527        $scope.loading = false;
20528        $scope.errorMessage = '';
20529        $scope.infoMessage = '';
20530
20531        const authStatus = authStatusService.getStatus();
20532
20533        var focusTimeout;
20534
20535        $scope.isCCA = authStatus.isCCA;
20536        
20537        $('#navbtn6 div').addClass('navItemSelected');
20538        
20539        //================= PAGING ============//
20540        $scope.PagedMedicalClaims = [];
20541        $scope.itemsPerPage = 10;
20542        $scope.currentPage = 0;
20543        $scope.gap = 0;
20544        $scope.data = [];
20545        $scope.SelectedList = [];
20546
20547        $scope.orderByField = 'Date';
20548        $scope.reverseSort = true;
20549        $scope.sortArrowDirection = 'down';
20550
20551        $scope.CombineUnreadLettersCount = 0;
20552        $scope.CombineTotalLettersCount = 0;
20553
20554        $scope.urlBase = $('meta[name="ApplicationRoot"]').attr('content');
20555
20556        $scope.$on('language-change', function (event, key) {
20557            $scope.currentLanguage = key;
20558            if ($scope.errorMessage) {
20559                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
20560            }
20561        });
20562
20563        $rootScope.$on('my-correspondence', function (data) {
20564            $scope.data = data.currentScope.data;
20565            $scope.perpdataforCorrespondence();           
20566        });
20567
20568        $scope.pageItems = function (arrayToPage) {
20569            $scope.PagedMedicalClaims = [];
20570            if (arrayToPage.length > 0) {
20571                arrayToPage = arrayToPage
20572                    .sort((a, b) => Date.parse(b.Date) - Date.parse(a.Date));
20573          
20574                $scope.RxTable = arrayToPage;
20575
20576                for (let i = 0; i < arrayToPage.length; i++) {
20577
20578                    if (i % $scope.itemsPerPage === 0) {
20579
20580                        $scope.PagedMedicalClaims[Math.floor(i / $scope.itemsPerPage)] = [arrayToPage[i]];
20581
20582                    } else {
20583
20584                        $scope.PagedMedicalClaims[Math.floor(i / $scope.itemsPerPage)].push(arrayToPage[i]);
20585                    }
20586                }
20587
20588                $scope.gap = $scope.PagedMedicalClaims.length;
20589            }
20590        };
20591        
20592        $scope.setDropDownData = function () {
20593            $scope.currentPage = 0;
20594            $scope.perpdataforCorrespondence();
20595        };
20596
20597        $scope.perpdataforCorrespondence = function () {            
20598            var CombineUnreadLettersCount = 0;
20599            var CombineTotalLettersCount = 0;
20600            $scope.SelectedList = [];
20601            
20602            for (let i = 0; i < this.data.length; i++) {
20603                const category = this.data[i];
20604               
20605                CombineUnreadLettersCount = CombineUnreadLettersCount +
20606                    category.UnreadLettersCount;
20607
20608                CombineTotalLettersCount = CombineTotalLettersCount +
20609                    category.TotalLettersCount; 
20610
20611                if (this.Category === 'All Documents') {
20612                    /* Run thru the return data and for each category
20613                     copy all the records to new object for UI */
20614
20615                    if (category.CorrespondenceDocuments.length > 0) {
20616                        $scope.SelectedList = $scope.SelectedList
20617                            .concat(category.CorrespondenceDocuments);
20618                    } else {
20619                        $scope.SelectedList = [];
20620                    }
20621                } else if (this.Category === 'Unread Documents') {
20622                    /* Run through the return data and for each category
20623                     copy all the records the IsRead set false to new 
20624                     object for UI */
20625
20626                    if (category.CorrespondenceDocuments &&
20627                        category.CorrespondenceDocuments.length > 0) {
20628                        for (let cnt = 0; cnt < category
20629                            .CorrespondenceDocuments.length; cnt++) {
20630                            const correspondence = category
20631                                .CorrespondenceDocuments[cnt];
20632                            if (!correspondence.IsRead) {
20633                                $scope.SelectedList = $scope.SelectedList
20634                                    .concat(correspondence);
20635                            }
20636                        }
20637                    } else {
20638                        $scope.SelectedList = [];
20639                    }
20640                } else if (category.Category === this.Category) {
20641                    if (category.CorrespondenceDocuments.length > 0) {
20642                        $scope.SelectedList = $scope.SelectedList
20643                            .concat(category.CorrespondenceDocuments);
20644                    } else {
20645                        $scope.SelectedList = [];
20646                    }
20647                }
20648            }
20649
20650            $scope.pageItems($scope.SelectedList);
20651            $scope.CombineUnreadLettersCount = CombineUnreadLettersCount;
20652            $scope.CombineTotalLettersCount = CombineTotalLettersCount;
20653        };
20654   
20655        $scope.decrementCatagoryCounter = function (doc) {
20656            $scope.CombineUnreadLettersCount = $scope
20657                .CombineUnreadLettersCount - 1;
20658
20659            for (let i = 0; i < this.data.length; i++) {
20660                const category = this.data[i];
20661
20662                if (category.CorrespondenceDocuments && category
20663                    .CorrespondenceDocuments.length > 0) {
20664
20665                    for (let cnt = 0; cnt < category
20666                        .CorrespondenceDocuments.length; cnt++) {
20667                        const correspondence = category
20668                            .CorrespondenceDocuments[cnt];
20669                        if (correspondence.Id === doc.Id) {
20670                            category.UnreadLettersCount = category
20671                                .UnreadLettersCount - 1;
20672                        } else {
20673                            console.log('Item not found');
20674                        }
20675                    }
20676                }
20677            }
20678
20679            $scope.perpdataforCorrespondence();
20680        }
20681        
20682        $scope.downloadMyCorrespondence = function (doc) {
20683            $scope.loading = true;
20684
20685            $scope.errorMessage = '';
20686            memberDataService.DownloadMyCorrespondence(doc.Id)
20687                .success(function (data, textStatus) {
20688                    if (textStatus === 200) {
20689                        $scope.loading = false;
20690                        const fileName = `MyCorrespondence-${doc.Name}.pdf`;
20691                        const file = new Blob([data], { type: 'application/pdf' });
20692
20693                        if (window.navigator && window.navigator.msSaveOrOpenBlob) {
20694                            window.navigator.msSaveOrOpenBlob(file, fileName);
20695                        } else {
20696                            const fileUrl = URL.createObjectURL(file);
20697                            const link = document.createElement('a');
20698                            document.body.appendChild(link);
20699
20700                            link.href = fileUrl;
20701                            link.download = fileName;
20702                            link.click();
20703
20704                            document.body.removeChild(link);
20705                        }
20706                    }; 
20707
20708                 $scope.setIsRead(doc);
20709
20710                }).error(function () {
20711                    $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
20712                }).finally(function () {
20713                    $scope.loading = false;
20714            });
20715        };
20716            
20717        $scope.setIsRead = function (doc) {
20718            if (!doc.IsRead) {
20719
20720                const param = [{
20721                    documentId: doc.Id,
20722                    isRead: true                   
20723                }];
20724
20725                memberDataService.setIsRead(param)
20726                    .success(function () {
20727                        doc.IsRead = true;
20728                        $scope.decrementCatagoryCounter(doc);
20729                        $rootScope.$broadcast('my-correspondence', $scope.data);
20730                    }).error(function (msg) {
20731                        $scope.errorMessage = msg; 
20732                });
20733            }   
20734        };
20735
20736        $scope.range = function (size, start, end) {
20737            const ret = [];
20738            if (size < end) {
20739                end = size;
20740                start = size - $scope.gap;
20741            }
20742            for (let i = start; i < end; i++) {
20743                ret.push(i);
20744            }
20745            return ret;
20746        };
20747
20748        $scope.setPage = function (n) {
20749            $scope.currentPage = n;
20750        };
20751
20752        $scope.prevPage = function () {
20753            if ($scope.currentPage > 0) {
20754
20755                $scope.currentPage--;
20756            }
20757        };
20758
20759        $scope.nextPage = function () {
20760            if ($scope.currentPage < $scope.PagedMedicalClaims.length - 1) {
20761
20762                $scope.currentPage++;
20763            }
20764        };
20765
20766        //================= ALL Correspondence ============//
20767        $scope.GetCorrespondence = function () {
20768            console.log('GetCorrespondence');
20769            $scope.loading = true;
20770
20771            memberDataService.GetCorrespondence()
20772                .success(function (data) { 
20773
20774                    $scope.errorMessage = '';
20775
20776                if (data) {
20777                    $scope.data = data;
20778                    $rootScope.data = data;
20779                    $scope.perpdataforCorrespondence();
20780                    $rootScope.$broadcast('my-correspondence', $rootScope.data);
20781                }
20782            }).error(function () {
20783                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
20784            }).finally(function () {
20785                $scope.loading = false;
20786            });
20787        };
20788
20789        $scope.onLoadCorrespondence = function () {
20790            if ($rootScope.data) {
20791                $scope.data = $rootScope.data;
20792                $scope.perpdataforCorrespondence();               
20793            } else {
20794                $scope.GetCorrespondence();
20795            };
20796        };
20797
20798        $scope.goBack = function () {
20799            history.back();
20800            $scope.$apply();
20801        };
20802        
20803        //================= Get My Correspondence ============//
20804        $scope.onLoadCorrespondence();
20805
20806
20807        $scope.$on('destroy',
20808            function () {
20809                if (focusTimeout) $timeout.cancel(focusTimeout);
20810            });
20811
20812        $scope.focusOnElement = function (elementId) {
20813            focusTimeout = $timeout(function () {
20814                $('#' + elementId).focus();
20815            }, 500);
20816        };
20817
20818        $scope.focusOnElement('btnMedicalClaims');
20819
20820        $scope.toggleBubble = function (item) {
20821            toggleBubble(`claimLine_${item.LineNo}`);
20822        }
20823
20824        $scope.getNumber = function (row) {
20825            return parseInt(row.LineNo);
20826        };
20827
20828        $scope.sort = function (field) {
20829            $scope.orderByField = field; // Set the orderBy field
20830            $scope.reverseSort = !$scope.reverseSort; // Reverse sort direction
20831            $scope.sortArrowDirection = $scope.reverseSort ? 'down' : 'up'; // Update the sorting icon direction
20832        };
20833
20834        $scope.myOptionStyle = function () {
20835            return {
20836                'background': 'red',
20837                'color': 'white',
20838                'padding': '0.2em 0.5em',
20839                'border-radius': ' 0.5em',
20840                'margin-left': '50px',
20841                'font': 'bold'
20842            }
20843
20844        };
20845    };
20846})();
20847(function () {
20848    'use strict';
20849
20850    angular
20851        .module('MemberPortal')
20852        .controller('PhiReleaseFormController', PhiReleaseFormController);
20853
20854    PhiReleaseFormController.$inject = ['$scope', 'authStatusService',
20855        '$sce', 'utilitiesService', 'memberDataService', 'requestedLanguageService'];
20856
20857    function PhiReleaseFormController($scope, authStatusService,
20858        $sce, utilitiesService, memberDataService, requestedLanguageService) {
20859
20860        init();
20861
20862        function init() {
20863
20864            console.log('phi release');
20865
20866            window.addEventListener("message",
20867                function (event) {
20868
20869                    if (!event || event.origin !== window.location.origin) return;
20870
20871                    const data = event.data;
20872
20873                    if (data.event === 'scrollToTop') {
20874                        window.scrollTo({
20875                            top: 0,
20876                            behavior: 'smooth'
20877                        });
20878                    }
20879                });
20880
20881            $scope.isMobile = utilitiesService.isMobile();
20882
20883            if (!authStatusService || !authStatusService.getStatus() || !authStatusService.getStatus().claims) return;
20884
20885            const currentLanguage = requestedLanguageService.getCurrentLanguage();
20886
20887            embedForm(currentLanguage);
20888        }
20889
20890        $scope.$on('language-change', function (_event, key) {
20891
20892            $scope.currentLanguage = key;
20893
20894            const currentLanguage = requestedLanguageService.getCurrentLanguage();
20895
20896            embedForm(currentLanguage);
20897
20898        });
20899
20900        function embedForm(currentLanguage) {
20901
20902            const authClaims = authStatusService.getStatus().claims;
20903
20904            const memberID = authClaims.username;
20905
20906            const lob = authClaims.lob;
20907
20908            let lobDescription = '';
20909            let firstName = '';
20910            let lastName = '';
20911            let dob = '';
20912            let middleName = '';
20913
20914            memberDataService.getEligibilityByDateOfService(currentLanguage).success(function (data) {
20915                console.log('eligibility', data);
20916
20917                if (!data || data.length === 0) return;
20918
20919                lobDescription = data[0].LobDescription;
20920                firstName = data[0].FirstName;
20921                middleName = data[0].MiddleName;
20922                lastName = data[0].LastName;
20923                dob = data[0].DateOfBirth;
20924
20925            }).finally(function () {
20926
20927                const formIOBase = $('meta[name="iFormioBase"]').attr('content');
20928
20929                const memberIdParam = `MemberId=${memberID}`;
20930
20931                const lobParams = `lob=${lob}&lobDescription=${lobDescription}`;
20932
20933                const formattedDob = dob ? moment(dob).format() : '';
20934
20935                const memberParams = `firstName=${encodeURIComponent(firstName || '')}&middleName=${encodeURIComponent(middleName || '')}&lastName=${encodeURIComponent(lastName || '')}&dob=${formattedDob}`;
20936
20937                const originParam = `origin=mpPhiReleaseForm`;
20938
20939                const source = 'source=memberportal';
20940
20941                const languageParam = `lang=${currentLanguage}`;
20942                const url = `${formIOBase}?form=phiRelease&showDirectEmbed=true&${memberIdParam}&${source}&${lobParams}&${memberParams}&${originParam}&${languageParam}`;
20943
20944                $scope.iframeSrc = $sce.trustAsResourceUrl(url);
20945            });
20946        }
20947
20948    };
20949})();
20950(function () {
20951    'use strict';
20952    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
20953    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
20954
20955    angular
20956        .module('MemberPortal')
20957        .controller('UpdateContactInfoController', UpdateContactInfoController);
20958
20959    UpdateContactInfoController.$inject = [
20960        '$scope', '$rootScope', '$filter', '$route', 'memberDataService',
20961        'authStatusService', 'authService', '$uibModal', 'utilitiesService',
20962        'updateInformationService', 'requestedLanguageService', '$timeout'
20963    ];
20964
20965    function UpdateContactInfoController($scope,
20966        $rootScope,
20967        $filter,
20968        $route,
20969        memberDataService,
20970        authStatusService,
20971        authService,
20972        $uibModal,
20973        utilitiesService,
20974        updateInformationService, requestedLanguageService, $timeout) {
20975
20976        var initZipTimeout;
20977
20978        $scope.getQuestionName = getQuestionName;
20979
20980        $scope.editDemographicInfo = editDemographicInfo;
20981        $scope.editNcqaInfo = editNcqaInfo;
20982
20983        init();
20984         
20985        function init() {
20986    
20987            $scope.loading = false;
20988           
20989            //remove all messages on the page
20990            _resetMessages();
20991
20992            $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
20993           
20994            //holding all the member demographics (phone/address/email)
20995            $scope.memberDemographics = {};
20996
20997            //holding all the forms to each category 
20998            $scope.form = {};
20999
21000            _initZipCode();
21001            
21002            _getDemographicsViaApi();
21003
21004            // $filter('translate')('nothing_has_changed');
21005            $scope.nothingChangedMessaged = 'Nothing has changed since last Update.';
21006            $scope.TestUserMode = $rootScope.TestUserMode;
21007            
21008            //set page config. for validation rules and messages
21009            $scope.config = updateInformationService.getConfiguration();
21010            
21011            const isProxied = authStatusService.checkIfProxied();
21012
21013            console.log('isProxied', isProxied);
21014            $scope.isEditable = !isProxied;
21015        }
21016
21017        function getQuestionName(prefix, id) {
21018            //generate a name for radio button group
21019            //by removing white space and special characters
21020            //take the first 10 char.
21021            //this is necessary because ng-class and ng-show needs it
21022            //console.log('name', question.replace(/[^A-Z0-9]/ig, '').substring(0, 10) + id);
21023            return prefix + id;
21024        }
21025
21026        //function getQuestionName(question, id) {
21027        //    //generate a name for radio button group
21028        //    //by removing white space and special characters
21029        //    //take the first 10 char.
21030        //    //this is necessary because ng-class and ng-show needs it
21031        //    //console.log('name', question.replace(/[^A-Z0-9]/ig, '').substring(0, 10) + id);
21032        //    return question.replace(/[^A-Z0-9]/ig, '').substring(0, 10) + id;
21033        //}
21034
21035        function _enrichMemberInfo(member) {
21036            //adding age and current email
21037            const copy = angular.copy(member);
21038      
21039            copy.age = utilitiesService.determineAge(copy.DateOfBirth);
21040           // console.log('member age', copy.age);
21041
21042            $scope.authStatus = authStatusService.getStatus();
21043            copy.currentEmail = $scope.authStatus.claims.email;
21044
21045            return copy;
21046        }
21047
21048        $scope.$on('destroy',
21049            function () {
21050                if (initZipTimeout) $timeout.cancel(initZipTimeout);
21051            });
21052
21053        function _initZipCode() {
21054            initZipTimeout = $timeout(function () {
21055
21056                    const $zipCode = $('#zipCode');
21057
21058                    if ($zipCode.val() && $zipCode.val().length === 9) {
21059                        const baseZipCode = $zipCode.val().substring(0, 5);
21060                        const extensionZipCode = $zipCode.val().substring(5);
21061
21062                        $zipCode.val(baseZipCode + '-' + extensionZipCode);
21063                    };
21064                },
21065                500);
21066
21067            $('#zipCode').mask('99999?-9999',
21068                {
21069                    autoclear: false,
21070                    placeholder: ' '
21071                });
21072        }
21073
21074        function addDisplayOrder(response) {
21075            response.forEach(function (category) {
21076                switch (category.CategoryId) {
21077                case 1:
21078                        category.DisplayOrder = 2; //race & ethnicity 2nd
21079                        break;
21080                case 2:
21081                    category.DisplayOrder = 1; //language 1st
21082                    break;
21083                case 3:
21084                    category.DisplayOrder = 3; //gender identity 3rd
21085                    break;
21086                default:
21087                    category.DisplayOrder = category.CategoryId;
21088                }
21089            });
21090
21091            return response;
21092        }
21093
21094        function _getDemographicsViaApi() {
21095
21096            $scope.loading = true;
21097
21098            memberDataService.GetDemographics().success(function(data) {
21099                
21100                if (!data) throw 500;
21101                
21102               // console.log('member demographic api response', data);
21103
21104                $scope.memberDemographics = updateInformationService.mapResponseToVm(data);
21105               
21106                $scope.dob = $scope.memberDemographics.DateOfBirth;
21107                $scope.memberDemographics = _enrichMemberInfo($scope.memberDemographics);
21108               // console.log('$scope.memberDemographics', $scope.memberDemographics);
21109
21110                if ($scope.memberDemographics && $scope.memberDemographics.SubscriberId) {
21111
21112                    const params = {
21113                        memberId: $scope.memberDemographics.SubscriberId,
21114                        currentLang: $scope.currentLanguage
21115                    }
21116                    
21117                    memberDataService.GetNcqaAnswers(params).
21118                        then(function (response) {
21119
21120                            if (!response || !response.data) return;
21121
21122                            $scope.ncqaAnswers = addDisplayOrder(response.data);
21123
21124                           // console.log('$scope.ncqaAnswers', $scope.ncqaAnswers);
21125                        });
21126                }
21127
21128            }).error(function() {
21129
21130                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
21131                
21132            }).finally(function() {
21133                $scope.loading = false;
21134            });
21135        };
21136
21137        function _validateAddressViaApi(editedAddress) {
21138
21139            var addressValidation;
21140         
21141            $scope.loading = true;
21142
21143            const payload = {
21144                Address1: editedAddress.Address,
21145                Address2: editedAddress.Address2,
21146                City: editedAddress.City,
21147                State: editedAddress.State,
21148                Zip5: editedAddress.Zip
21149            };
21150            console.log('validating the new address...');
21151            memberDataService.ValidateMemberAddress(payload).success(function(data) {
21152
21153                //console.log('addr. verification result', data);
21154
21155                //it's not a valid validation response
21156                //the validation endpoint errors out. In this case, we bypass the addr. validation
21157                if (!data || data.Validity === -1) {
21158
21159                    $scope.loading = false;
21160                   
21161                    console.log('the new address is valid', editedAddress);
21162                    _submitValidatedDemographics();
21163                    return false;
21164                }
21165
21166                addressValidation = {
21167                    userEnteredAddress: {
21168                        Address1: data.RawAddress.Address1,
21169                        Address2: data.RawAddress.Address2,
21170                        City: data.RawAddress.City,
21171                        State: data.RawAddress.State,
21172                        Zip5: data.RawAddress.Zip5,
21173                        Zip4: data.RawAddress.Zip4
21174                    }
21175                };
21176
21177                if (data.OfficialAddress) {
21178                    addressValidation.officialAddress = {
21179                        Address1: data.OfficialAddress.Address1,
21180                        Address2: data.OfficialAddress.Address2,
21181                        City: data.OfficialAddress.City,
21182                        State: data.OfficialAddress.State,
21183                        Zip5: data.OfficialAddress.Zip5,
21184                        Zip4: data.OfficialAddress.Zip4
21185                    };
21186                }
21187
21188                addressValidation.notNormalized = !data.isMatch;
21189                addressValidation.notFound = !addressValidation.officialAddress;
21190                $scope.addressValidation = addressValidation;
21191
21192                //if the entry is not a standard USPS address.
21193                if ($scope.addressValidation.notNormalized) {
21194
21195                    _openAddressConfirmModal();
21196                    
21197                } else {
21198                    console.log('this is a valid address');
21199                    _submitValidatedDemographics();
21200                }
21201
21202            }).error(function() {
21203
21204                console.log('address validation api call failed.');
21205                //if the validation API failed, still submit the change.
21206                _submitValidatedDemographics();
21207                return false;
21208
21209            }).finally(function() {
21210                $scope.loading = false;
21211            });
21212        }
21213
21214        function _openAddressConfirmModal() {
21215
21216            var data = {
21217                recommended: $scope.addressValidation
21218            };
21219
21220            const modalInstance = $uibModal.open({
21221                backdrop: 'static',
21222                animation: true,
21223                templateUrl: urlBase +
21224                    '/App/directives/my-information/address-validation-modal.html' +
21225                    cacheBust,
21226                windowClass: 'modalOne',
21227                controller: 'addressValidationModal',
21228                scope: $scope,
21229                controllerAs: 'address',
21230                size: 'md',
21231                resolve: {
21232                    data: function() {
21233                        return data;
21234                    }
21235                }
21236            });
21237
21238            modalInstance.result.then(function(continueWithRecommended) {
21239                    //console.log('continue with what we recommended?', continueWithRecommended);
21240
21241                if (continueWithRecommended) {
21242                    //update the address fields and the payload with the recommended address
21243                    $scope.memberDemographics.Address = $scope.addressValidation.officialAddress.Address1;
21244
21245                    $scope.memberDemographics.Address2 = $scope.addressValidation.officialAddress.Address2;
21246
21247                    $scope.memberDemographics.City = $scope.addressValidation.officialAddress.City;
21248
21249                    $scope.memberDemographics.State = $scope.addressValidation.officialAddress.State;
21250
21251                    $scope.memberDemographics.Zip = $scope.addressValidation.officialAddress.Zip5 + $scope.addressValidation.officialAddress.Zip4;
21252                }
21253
21254                    _submitValidatedDemographics();
21255                },
21256                function() {
21257                    //console.log('Modal dismissed at: ' + new Date());
21258                    //scroll and focus the address field
21259                });
21260        }
21261
21262        function _resetMessages() {
21263            $scope.errorMessage = '';
21264            $scope.infoMessage = '';
21265            $scope.successMessage = '';
21266        }
21267
21268        function _submitValidatedDemographics() {
21269
21270            $scope.loading = true;
21271
21272            const params = _getDemographics('payload');
21273            console.log('updating params', params);
21274        
21275            memberDataService.UpdateDemographics(params).success(function(data) {
21276                console.log('demographics is saved');
21277                $scope.memberDemographics = data;
21278
21279                $scope.successMessage = $filter('translate')('success');
21280
21281                _getDemographicsViaApi();
21282
21283            }).error(function() {
21284                $scope.errorMessage = $filter('translate')('error_occurred');
21285
21286            }).finally(function() {
21287                $scope.loading = false;
21288            });
21289        }
21290
21291        function _submitNcqaAnswers(editModel, categoryId) {
21292        
21293            $scope.loading = true;
21294
21295            const params = _mapEditVmToPayload(editModel, categoryId);
21296            console.log('params', params);
21297
21298            memberDataService.SaveNcqaAnswers(params).success(function (data) {
21299
21300                console.log('response for saving ncqa', data);
21301                if (data===true) {
21302                    $scope.successMessage = $filter('translate')('success');
21303                }
21304
21305                _getDemographicsViaApi();
21306
21307            }).error(function () {
21308                $scope.errorMessage = $filter('translate')('error_occurred');
21309
21310            }).finally(function () {
21311                $scope.loading = false;
21312            });
21313        }
21314
21315        function _submitAddress() {
21316
21317            const editedAddress = _getDemographics('address').address;
21318
21319            if (!editedAddress) {
21320                console.warn('no new address');
21321                return false;
21322            }
21323
21324            if (!editedAddress.Address ||
21325                !editedAddress.City ||
21326                !editedAddress.State ||
21327                !editedAddress.Zip) {
21328
21329                return false;
21330            }
21331            console.log('saving address...', editedAddress);
21332            _validateAddressViaApi(editedAddress);
21333        }
21334
21335        function _checkAnythingUpdated(originalState, currentState) {
21336
21337            let isUpdated = false;
21338
21339            if (!angular.equals(currentState, originalState)) {
21340                console.log('something changed');
21341               isUpdated = true;
21342            } else {
21343                console.log('nothing changed');
21344                $scope.successMessage = $scope.nothingChangedMessaged;
21345                $("html, body").animate({ scrollTop: 0 }, "slow");
21346            }
21347
21348            return isUpdated;
21349        }
21350
21351        function _submitEmailChange() {
21352
21353            $scope.loading = true;
21354
21355            const editedEmail = _getDemographics('email').email;
21356            
21357
21358            const param = {
21359                UserName: $scope.authStatus.claims.username,
21360                Password: editedEmail.Password,
21361                NewEmail: editedEmail.NewEmail,
21362                ConfirmEmail: editedEmail.ConfirmEmail,
21363                name: 'WebMemberPortal'
21364            };
21365            console.log('update email param', param);
21366
21367            authService.ChangeEmail(param).success(function () {
21368                //call logging API
21369                memberDataService.activityWrite('MyInformation-ChangeEmail');
21370
21371                $scope.memberDemographics.currentEmail = $scope.memberDemographics.newEmail;
21372                //Clear the form
21373                $scope.memberDemographics.newEmail = '';
21374                $scope.memberDemographics.matchEmail = '';
21375                $scope.memberDemographics.password = '';
21376
21377                //Display Success Message
21378                $scope.successMessage = $filter('translate')('form_successMessageEmailUpdate');
21379
21380                //Refresh Token to reflect new email
21381                authService.refreshToken().then(function (resp) {
21382                    console.log('token refreshed', resp);
21383                });
21384
21385                //Should we inform the user that an email has been sent.
21386            }).error(function () {
21387                $scope.errorMessage = $filter('translate')('form_thereWasAProblemWithEmailChange');
21388
21389            }).finally(function() {
21390                $scope.loading = false;
21391            });
21392        };
21393
21394        function _openEditModal(questions, config, hasAnswers) {
21395            const data = {
21396                category: questions,
21397                configuration: config,
21398                hasAnswers: hasAnswers
21399            };
21400
21401            const modalInstance = $uibModal.open({
21402                backdrop: 'static',
21403                animation: true,
21404                templateUrl: urlBase +
21405                    '/App/directives/my-information/update-contact-info-modal.html' +
21406                    cacheBust,
21407                windowClass: 'modalOne',
21408                controller: 'updateContactInfoModal',
21409                scope: $scope,
21410                controllerAs: 'modal',
21411                size: 'lg',
21412                resolve: {
21413                    data: function () {
21414                        return data;
21415                    }
21416                }
21417            });
21418
21419            modalInstance.result.then(function (categoryId) {
21420                console.log('category id passed from the edit modal...', categoryId);
21421                _UpdateMyProfileInfo(categoryId);
21422                $("html, body").animate({ scrollTop: 0 }, "slow");
21423            });
21424        }
21425
21426        function editDemographicInfo(categoryName) {
21427
21428            //for preexisting categories, we use category name
21429            console.log('this is a demographic category (phone/address/email)');
21430            _resetMessages();
21431           
21432            $scope.initEditStateCached = angular.copy(_getDemographics(categoryName));
21433            
21434            console.log('initial state saved...', $scope.initEditStateCached);
21435
21436            _openEditModal();
21437        }
21438
21439        function editNcqaInfo(categoryId) {
21440            //open edit UI
21441            //for SOGI categories, we use numeric category ID
21442            console.log('category id', categoryId);
21443            _resetMessages();
21444
21445            if (!angular.isNumber(categoryId)) return;
21446
21447            $scope.loading = true;
21448
21449            memberDataService.GetNcQaQuestions($scope.currentLanguage).then(function (response) {
21450                if (!response || !response.data) return;
21451
21452                console.log('question response', response.data);
21453
21454                $scope.questionnaire = response.data;
21455
21456                generateEditViewModel(categoryId);
21457
21458                return;
21459            }).finally(function() {
21460                $scope.loading = false;
21461            });
21462            
21463        }
21464
21465        function generateEditViewModel(categoryId) {
21466            const questionsPerCategory = $scope.questionnaire
21467                .find(m => m.CategoryId === categoryId);
21468            //console.log('questions Per Category', questionsPerCategory);
21469            
21470            if (!questionsPerCategory || !questionsPerCategory.Questions) {
21471                $scope.errorMessage = 'No questions found.';
21472                return;
21473            }
21474
21475            questionsPerCategory.Questions.forEach(function (question) {
21476                if (question.QuestionDiscretion) {
21477                    question.QuestionDiscretion =
21478                        question.QuestionDiscretion.replace('\\n', '<br/>');
21479                }
21480            });
21481
21482            $scope.editModels = _getNcqaViewModel(categoryId);
21483
21484            $scope.initEditStateCached = angular.copy($scope.editModels);
21485            console.log('initial state saved...', $scope.initEditStateCached);
21486
21487            const hasAnswers = Object.values($scope.editModels)
21488                .filter(m => m !== null && m !== 'null').length > 0;
21489            console.log(`this category (${categoryId}) has answers?`, hasAnswers);
21490
21491            //console.log('$scope.editModels', $scope.editModels);
21492            _openEditModal(questionsPerCategory, $scope.config, hasAnswers);
21493        }
21494
21495        function _getNcqaViewModel(categoryId) {
21496            //parsing the answers to create the edit view model
21497            const vm = {};
21498
21499            const category = $scope.ncqaAnswers.find(m => m.CategoryId === categoryId);
21500            //console.log('category', category);
21501
21502            if (!category || !category.Answers) return {};
21503
21504            category.Answers.forEach(function (item) {
21505               // console.log('answer item', item);
21506
21507                vm[item.QuestionId] = item.AnswerId ? item.AnswerId : item.AnswerOther ? item.AnswerOther: null;
21508                
21509                if (item.AnswerOther) {
21510                vm[`other${item.QuestionId}`] = item.AnswerOther;
21511                }
21512            });
21513
21514           // console.log('edit view model', vm);
21515            return vm;
21516        }
21517
21518        function _getDemographics(categoryName) {
21519            //get the current state of member demographics
21520            //the property name is the same as api payload
21521            //each category is for comparing with the original state 
21522            //when no argument is passed, it grabs the api payload
21523
21524            if (!$scope.memberDemographics) return {};
21525
21526            let member = {};
21527
21528            const address = {
21529                Address: $scope.memberDemographics.Address,
21530                Address2: $scope.memberDemographics.Address2,
21531                City: $scope.memberDemographics.City,
21532                State: $scope.memberDemographics.State,
21533                Zip: $scope.memberDemographics.Zip,
21534                ShareDataWithState: $scope.memberDemographics.ShareDataWithState
21535            };
21536
21537            const email = {
21538                Password: $scope.memberDemographics.password,
21539                NewEmail: $scope.memberDemographics.newEmail,
21540                ConfirmEmail: $scope.memberDemographics.matchEmail
21541            }
21542
21543            const phone = {
21544                Phone1: $scope.memberDemographics.phonePrimaryPart1 +
21545                    $scope.memberDemographics.phonePrimaryPart2 +
21546                    $scope.memberDemographics.phonePrimaryPart3,
21547                Phone2: $scope.memberDemographics.phoneSecondaryPart1 +
21548                    $scope.memberDemographics.phoneSecondaryPart2 +
21549                    $scope.memberDemographics.phoneSecondaryPart3,
21550                Phone3: $scope.memberDemographics.phoneMobilePart1 +
21551                    $scope.memberDemographics.phoneMobilePart2 +
21552                    $scope.memberDemographics.phoneMobilePart3
21553            };
21554
21555            const claims = authStatusService.getStatus().claims;
21556
21557            switch (categoryName) {
21558            case 'address':
21559                    member = {
21560                        CategoryId: categoryName,
21561                        address: {
21562                            ...address
21563                        }
21564                };
21565                break;
21566            case 'phone':
21567                    member = {
21568                        CategoryId: categoryName,
21569                        phone: {
21570                            ...phone
21571                        }
21572                };
21573                    break;
21574                case 'email':
21575                    member = {
21576                        CategoryId: categoryName,
21577                        email: {
21578                            ...email
21579                        }
21580                };
21581                break;
21582            case 'payload':
21583                    member = {
21584                        SubscriberId: $scope.memberDemographics.SubscriberId,
21585                        EmailAddress: claims.email,
21586                        DateOfBirth: $scope.dob,
21587                    ...address,
21588                    ...phone
21589                };
21590                break;
21591            };
21592
21593           // console.log('member demographics view model', member);
21594            return member;
21595        }
21596
21597        function _mapEditVmToPayload(editVm, categoryId) {
21598           
21599            const questionsPerCategory = $scope.questionnaire
21600                .find(m => m.CategoryId === categoryId);
21601
21602            if (!questionsPerCategory) return {};
21603            //console.log('questionsPerCategory', questionsPerCategory);
21604
21605            const authStatus = authStatusService.getStatus();
21606            const memberNumber = authStatus?.claims?.username;
21607
21608            const answerSource = 'WebMemberPortal';
21609            const answers = [];
21610            
21611            for (const key in editVm) {
21612              //  console.log(`${key}: ${editVm[key]}`);
21613
21614                if (!angular.isNumber(Number(key))) continue;
21615
21616                const question = questionsPerCategory.Questions
21617                        .find(m => m.QuestionId === Number(key));
21618                    //console.log('question', question);
21619                    if (!question) continue;
21620
21621                    //console.log('found', question.QuestionLabel);
21622
21623                    const item = {
21624                        QuestionId: key,
21625                        QuestionLabel: question.QuestionLabel,
21626                        AnswerId: question.QuestionId===9? null: editVm[key],
21627                        AnswerOther: editVm[`other${key}`] ? editVm[`other${key}`] : null,
21628                        MemberNumber: utilitiesService.formatMemberId(memberNumber),
21629                        AnswerSource: answerSource,
21630                        UpdatedBy: memberNumber
21631                    }
21632
21633                    answers.push(item);
21634            }
21635
21636            return answers;
21637        }
21638
21639        function _UpdateMyProfileInfo(categoryType) {
21640            //redirect to saving method for both demographics and ncqa categories
21641
21642           // console.log('updating category type', categoryType);
21643
21644            if (categoryType === 'email') {
21645                _submitEmailChange();
21646                return false;
21647            }
21648
21649            //check to see if user entered a valid address
21650            //if no, prompt user to edit OR accept recommendation and submit
21651            //if yes, submit
21652            if (categoryType === 'address') {
21653                
21654                if (!_checkAnythingUpdated($scope.initEditStateCached,
21655                    _getDemographics(categoryType))) {
21656                   // console.log('there has been no change in address');
21657                    return false;
21658                }
21659                _submitAddress();
21660                return false;
21661            }
21662
21663            if (categoryType === 'phone') {
21664                if (!_checkAnythingUpdated($scope.initEditStateCached,
21665                    _getDemographics(categoryType))) {
21666                   // console.log('there has been no change in phone');
21667                    return false;
21668                }
21669                _submitValidatedDemographics();
21670                return false;
21671            }
21672            
21673            //get the current member state for the selected category
21674            //const currentDemographicState = getDemographics(category);
21675            //console.log('current DemographicState', currentDemographicState);
21676
21677            //check to see if the member state has changed
21678            //if no, stop the process
21679           // console.log('$scope.initEditStateCached', $scope.initEditStateCached);
21680           // console.log('$scope.editModels', $scope.editModels);
21681            if (!_checkAnythingUpdated($scope.initEditStateCached, $scope.editModels)) {
21682               // console.log('there has been no change in NCQA answers');
21683                return false;
21684            }
21685        
21686            _resetMessages();
21687            //console.log('something has changed');
21688            
21689            //if there are changes other than address change, submit
21690            _submitNcqaAnswers($scope.editModels, categoryType);
21691
21692            return false;
21693        };
21694
21695        $scope.$on('language-change', function (event, key) {
21696           // console.log('language changed');
21697            init();
21698        });
21699
21700        $scope.$on('obfuscation_toggled', function () {
21701            $scope.TestUserMode = $rootScope.TestUserMode;
21702            $route.reload();
21703
21704        });
21705    };
21706})();
21707(function () {
21708    'use strict';
21709
21710    angular
21711        .module('MemberPortal')
21712        .controller('fontSizeController', fontSizeController);
21713
21714    fontSizeController.$inject = ['$scope'];
21715
21716    function fontSizeController($scope) {
21717
21718        const vm = this;
21719
21720        vm.decreaseFontSize = function () {
21721            $('body').css('font-size', '10px');
21722            $('#btnFontSize_small').addClass('font-variation');
21723            $('#btnFontSize_large').removeClass('font-variation');
21724            $('#btnFontSize_normal').removeClass('font-variation');
21725        };
21726
21727        vm.normalFontSize = function () {
21728            $('body').css('font-size', '12px');
21729            $('#btnFontSize_normal').addClass('font-variation');
21730            $('#btnFontSize_large').removeClass('font-variation');
21731            $('#btnFontSize_small').removeClass('font-variation');
21732        };
21733
21734        vm.increaseFontSize = function () {
21735            $('body').css('font-size', '14px');
21736            $('#btnFontSize_large').addClass('font-variation');
21737            $('#btnFontSize_small').removeClass('font-variation');
21738            $('#btnFontSize_normal').removeClass('font-variation');
21739        };
21740    };
21741})();
21742(function () {
21743  'use strict';
21744  /*the NavCtrl controls footer, header-nav and _layout views*/
21745  angular.module('MemberPortal').controller('NavCtrl', NavCtrl);
21746
21747  NavCtrl.$inject = [
21748    '$scope',
21749    '$rootScope',
21750    'authStatusService',
21751    '$location',
21752    'requestedLanguageService',
21753    'utilitiesService',
21754    'healthDelegateService',
21755    'environmentConfig',
21756    'memberDataService',
21757    'toastifyService'
21758  ];
21759
21760  function NavCtrl(
21761    $scope,
21762    $rootScope,
21763    authStatusService,
21764    $location,
21765    requestedLanguageService,
21766    utilitiesService,
21767    healthDelegateService,
21768    environmentConfig,
21769    memberDataService,
21770    toastifyService
21771  ) {
21772      var context = this;
21773
21774      /*functions needed in the view*/
21775      context.getPrivacyUrl = getPrivacyUrl;
21776      context.goUrl = goUrl;
21777      context.openMenuEligibility = openMenuEligibility;
21778
21779      context.openMenuMyDoctor = openMenuMyDoctor;
21780      context.openMenuMyInformation = openMenuMyInformation;
21781      context.openMenuHealth = openMenuHealth;
21782      context.openMenuInterpreterRequests = openMenuInterpreterRequests;
21783      context.openMenuCourses = openMenuCourses;
21784      context.toggleTestUserMode = toggleTestUserMode; 
21785
21786      init();
21787
21788      function init() {
21789          
21790          const authStatus = authStatusService.getStatus();
21791          
21792          context.isAuth = authStatus.isAuth;
21793
21794          context.logoLink = context.isAuth ? 'App/Home' : 'https://www.iehp.org/'
21795          
21796          if (!context.isAuth) return;
21797
21798          context.isProxied = authStatusService.checkIfProxied();
21799          context.isDSNP = authStatus.isDSNP;
21800          context.isMediCal = authStatus.isMediCal;
21801          context.isMediCareNoMediCal = authStatus.isMediCareNoMediCal;
21802
21803          const authClaims = authStatus.claims;
21804          const isOver18 = authClaims?.agegroup === '18+';
21805
21806          // must be 18+ and NOT DSNP/MEDICARE and NOT proxied to view PDHI widget
21807          context.showPDHIWidget = isOver18 && !context.isDSNP && !context.isProxied;
21808          
21809          context.userName = authClaims.username;
21810
21811          context.currentLanguage = requestedLanguageService.getCurrentLanguage();
21812          
21813          context.isDelegateEligible = healthDelegateService.isDelegateEligible();
21814          //console.log('context.isDelegateEligible', context.isDelegateEligible);
21815          
21816          context.isCCA = authStatus.isCCA;
21817          context.isPrimary = authStatus.isPrimarySubscriber;
21818          context.showTransportationRequests = isOver18 && !context.isCCA && !context.isProxied;
21819          getCorrespondenceCounts();
21820
21821          setMemberFirstName(authClaims.firstname);
21822
21823          setMenuInitState();
21824
21825          setUrlLinks();
21826
21827          //openRedirect('phiReleaseForm');
21828      }     
21829
21830      function openRedirect(type) {
21831
21832          if (type === 'phiReleaseForm') {
21833              const currentLanguage = requestedLanguageService.getCurrentLanguage();
21834
21835              const thresholdLang = requestedLanguageService.getLanguageshorthand(currentLanguage);
21836
21837              console.log('currentLanguage', currentLanguage);
21838              console.log('thresholdLang', thresholdLang);
21839
21840              context.redirectUrl = `https://www.iehp.org/${thresholdLang}/member-materials/personal-health-information`;
21841          }
21842      }
21843
21844      function setMenuInitState() {
21845          context.menuOpenStatus = false;
21846          context.menuOpenStatusEligibility = false;
21847          context.menuOpenStatusIEHPIDCard = false;
21848          context.menuOpenStatusMyDoctor = false;
21849          context.menuOpenStatusMyInformation = false;
21850          context.menuOpenStatusInterpreterRequests = false;
21851          context.menuOpenStatusHealth = false;
21852      }
21853
21854      function setUrlLinks() {
21855          // madie client redirect sso link
21856          //context.madieRedirectSso = environmentConfig.madieRedirectSso;
21857
21858          // incomm client redirect sso link
21859          context.incommRedirectSso = environmentConfig.incommRedirectSso;
21860
21861          // CCA cost estimator redirect sso link
21862          context.ccaResourcesRedirect = environmentConfig.ccaResourcesRedirect;
21863
21864          context.healthAppraisalPDHIUrl = environmentConfig.healthAppraisalPDHIUrl;
21865      }
21866
21867      function setMemberFirstName(firstname) {
21868
21869          if (!firstname) return;
21870
21871          $rootScope.firstName = utilitiesService
21872              .capFirstLetter(firstname.toLowerCase());
21873      }
21874
21875      function getCorrespondenceCounts() {
21876          // console.log('GetCorrespondence');
21877
21878          $scope.CombineUnreadLettersCount = 0;
21879          if ($rootScope.data) {
21880              $scope.data = $rootScope.data;
21881
21882              for (var i = 0; i < $scope.data.length; i++) {
21883                  var catagory = $scope.data[i];
21884                  $scope.CombineUnreadLettersCount = $scope.CombineUnreadLettersCount + catagory.UnreadLettersCount;
21885              }
21886          }
21887      };
21888    
21889      function goUrl(url) {
21890          $location.path(url);
21891        };
21892        
21893      function getPrivacyUrl(language) {
21894          return requestedLanguageService.getPrivacyUrl(language);
21895          }
21896      
21897      function openMenuEligibility() {
21898          $('.submenuContainer').fadeOut(500);
21899          $('#subNavEligibility').css('display', 'block');
21900
21901          context.menuOpenStatusEligibility = true;
21902          context.menuOpenStatusIEHPIDCard = false;
21903          context.menuOpenStatusMyDoctor = false;
21904          context.menuOpenStatusMyInformation = false;
21905          context.menuOpenStatusInterpreterRequests = false;
21906          context.menuOpenStatusHealth = false;
21907
21908          $('.navItemSelected').removeClass('navItemSelected');
21909
21910          if (context.menuOpenStatusEligibility) {
21911            $('#navbtn1 div').addClass('navItemSelected');
21912          }
21913          };
21914
21915      function openMenuMyDoctor() {
21916          $('.submenuContainer').fadeOut(500);
21917          $('#subNavMyDoctor').css('display', 'block');
21918
21919          context.menuOpenStatusIEHPIDCard = false;
21920          context.menuOpenStatusEligibility = false;
21921          context.menuOpenStatusMyDoctor = true;
21922          context.menuOpenStatusMyInformation = false;
21923          context.menuOpenStatusInterpreterRequests = false;
21924          context.menuOpenStatusHealth = false;
21925
21926      $('.navItemSelected').removeClass('navItemSelected');
21927
21928          if (context.menuOpenStatusMyDoctor) {
21929        $('#navbtn3 div').addClass('navItemSelected');
21930      }
21931    };
21932
21933      function openMenuMyInformation() {
21934          $('.submenuContainer').fadeOut(500);
21935          $('#subNavMyInformation').css('display', 'block');
21936
21937          context.menuOpenStatusMyInformation = true;
21938          context.menuOpenStatusInterpreterRequests = false;
21939          context.menuOpenStatusEligibility = false;
21940          context.menuOpenStatusIEHPIDCard = false;
21941          context.menuOpenStatusMyDoctor = false;
21942          context.menuOpenStatusHealth = false;
21943
21944          $('.navItemSelected').removeClass('navItemSelected');
21945
21946          if (context.menuOpenStatusMyInformation) {
21947            $('#navbtn5 div').addClass('navItemSelected');
21948          }
21949        };
21950
21951      function openMenuInterpreterRequests() {
21952      $('.submenuContainer').fadeOut(500);
21953      $('#subNavInterpreterRequests').css('display', 'block');
21954      context.menuOpenStatusInterpreterRequests = true;
21955      context.menuOpenStatusMyInformation = false;
21956      context.menuOpenStatusEligibility = false;
21957      context.menuOpenStatusIEHPIDCard = false;
21958      context.menuOpenStatusMyDoctor = false;
21959      context.menuOpenStatusHealth = false;
21960
21961      $('.navItemSelected').removeClass('navItemSelected');
21962
21963      if (context.menuOpenStatusInterpreterRequests) {
21964        $('#navbtn5 div').addClass('navItemSelected');
21965      }
21966    };
21967
21968      function openMenuHealth() {
21969      $('.submenuContainer').fadeOut(500);
21970      $('#subNavHealth').css('display', 'block');
21971
21972      context.menuOpenStatusHealth = true;
21973      context.menuOpenStatusEligibility = false;
21974      context.menuOpenStatusIEHPIDCard = false;
21975      context.menuOpenStatusMyDoctor = false;
21976      context.menuOpenStatusMyInformation = false;
21977      context.menuOpenStatusInterpreterRequests = false;
21978
21979
21980      $('.navItemSelected').removeClass('navItemSelected');
21981
21982      if (context.menuOpenStatusHealth) {
21983        $('#navbtn6 div').addClass('navItemSelected');
21984      }
21985    };
21986
21987      function openMenuCourses() {
21988      $('.submenuContainer').fadeOut(500);
21989      $('#subNavCourses').css('display', 'block');
21990      context.menuOpenStatusCourses = true;
21991      context.menuOpenStatusHealth = false;
21992      context.menuOpenStatusEligibility = false;
21993      context.menuOpenStatusIEHPIDCard = false;
21994      context.menuOpenStatusMyDoctor = false;
21995      context.menuOpenStatusMyInformation = false;
21996      context.menuOpenStatusInterpreterRequests = false;
21997
21998
21999      $('.navItemSelected').removeClass('navItemSelected');
22000
22001      if (context.menuOpenStatusCourses) {
22002        $('#navbtn6 div').addClass('navItemSelected');
22003      }
22004    };
22005
22006      function toggleTestUserMode() {
22007      $rootScope.TestUserMode = !$rootScope.TestUserMode;
22008
22009      if ($rootScope.TestUserMode) {
22010        $('#toggleTUM')
22011          .addClass('obfuscationEnabled')
22012          .removeClass('obfuscationDisabled');
22013      } else {
22014        $('#toggleTUM')
22015          .addClass('obfuscationDisabled')
22016          .removeClass('obfuscationEnabled');
22017      }
22018
22019      $rootScope.$broadcast('obfuscation_toggled', $rootScope.TestUserMode);
22020      };
22021
22022      /* pub/sub*/
22023      $rootScope.$on('logged-out', function () {
22024          init();
22025      });
22026
22027      $scope.$on('language-change', function (_event, key) {
22028          context.currentLanguage = key;
22029      });
22030
22031      $scope.$on('logon-success', function () {
22032          init();
22033      });
22034
22035      $rootScope.$on('switchingProfile', function (_event, key) {
22036
22037          const switchingProfile = key.loadingProfile;
22038
22039          return switchingProfile ? null : init();
22040      });
22041
22042      $rootScope.$on('my-correspondence', function (data) {
22043          getCorrespondenceCounts();
22044      });
22045  }
22046})();
22047
22048(function () {
22049    "use strict";
22050
22051    angular.module("MemberPortal").controller("SideNavCtrl", SideNavCtrl);
22052
22053    SideNavCtrl.$inject = [
22054        "$scope",
22055        "$route",
22056        "requestedLanguageService",
22057        "utilitiesService",
22058        "$rootScope",
22059        "healthDelegateService",
22060        "authStatusService"
22061    ];
22062
22063    function SideNavCtrl(
22064        $scope,
22065        $route,
22066        requestedLanguageService,
22067        utilitiesService,
22068        $rootScope,
22069        healthDelegateService,
22070        authStatusService
22071    ) {
22072        const vm = this;
22073
22074        init();
22075
22076        function init() {
22077
22078            vm.$route = $route;
22079
22080            vm.isMobile = utilitiesService.isMobile();
22081
22082            const authStatus = authStatusService.getStatus();
22083
22084            vm.isCCA = authStatus.isCCA;
22085
22086            vm.isDSNP = authStatus.isDSNP;
22087            
22088            vm.isDelegateEligible = authStatusService.checkIfProxied() ? true :
22089                healthDelegateService.getIsCaregiverEligible();
22090
22091           updateCorrespondenceCounts();
22092            // CCA cost estimator redirect sso link
22093            vm.ccaResourcesRedirect = $('meta[name="ccaResourcesRedirect"]').attr("content");
22094
22095            vm.currentLanguage = requestedLanguageService.getCurrentLanguage();
22096
22097            //openRedirect('phiReleaseForm');
22098        }
22099        
22100        $scope.$on("language-change", function (_event, key) {
22101            vm.currentLanguage = key;
22102            //openRedirect('phiReleaseForm');
22103        });
22104
22105        $rootScope.$on('my-correspondence', function () {
22106
22107            getCorrespondenceCounts();
22108        });
22109
22110        $rootScope.$on('logon-success', function () {
22111            init();
22112        });
22113
22114        function openRedirect(type) {
22115
22116            if (type === 'phiReleaseForm') {
22117                const currentLanguage = requestedLanguageService.getCurrentLanguage();
22118
22119                const thresholdLang = requestedLanguageService.getLanguageshorthand(currentLanguage);
22120
22121                console.log('currentLanguage', currentLanguage);
22122                console.log('thresholdLang', thresholdLang);
22123
22124                vm.redirectUrl = `https://www.iehp.org/${thresholdLang}/member-materials/personal-health-information`;
22125            }
22126        }
22127
22128        function updateCorrespondenceCounts() {
22129
22130            getCorrespondenceCounts();
22131        };
22132
22133        function getCorrespondenceCounts() {
22134
22135            $scope.CombineUnreadLettersCount = 0;
22136            if ($rootScope.data) {
22137                $scope.data = $rootScope.data;
22138
22139                for (var i = 0; i < $scope.data.length; i++) {
22140                    var catagory = $scope.data[i];
22141                    $scope.CombineUnreadLettersCount = $scope.CombineUnreadLettersCount + catagory.UnreadLettersCount;
22142                }
22143            }
22144        };
22145    }
22146})();
22147
22148(function () {
22149  'use strict';
22150
22151  angular
22152      .module('MemberPortal')
22153      .controller('MakePaymentsController', MakePaymentsController);
22154
22155  MakePaymentsController.$inject = [
22156      '$scope',
22157      'authStatusService',
22158      'memberDataService',
22159      '$filter'
22160  ];
22161
22162    function MakePaymentsController($scope, authStatusService, memberDataService, $filter) {
22163
22164        init();
22165
22166        function init() {
22167
22168            $scope.isProxied = authStatusService.checkIfProxied();
22169
22170            $scope.errorMessage = '';
22171            $scope.successMessage = '';
22172            $scope.infoMessage = '';
22173            $scope.loading = false;
22174
22175            $scope.invoices = [];
22176
22177            // CCA payments redirect sso link
22178            $scope.ccaPaymentsRedirect = $('meta[name="ccaPaymentsRedirect"]').attr('content');
22179
22180            getMemberInvoices();
22181        }
22182        
22183         function getMemberInvoices() {
22184            $scope.loading = true;
22185            $scope.errorMessage = '';
22186            $scope.invoices = [];
22187            memberDataService.GetMemberInvoices()
22188                .success(function(data) {
22189                    $scope.invoices = data
22190                        .map(doc => ({
22191                            ...doc,
22192                            filename: String('00000000000' + doc.InvoiceId).slice(-10)
22193                        })) // 10 char filename
22194                        .sort((a, b) => (new Date(b.BillingPeriod) - new Date(a.BillingPeriod)));
22195                    $scope.loading = false;
22196                })
22197                .error(function() {
22198                    $scope.loading = false;
22199                    $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
22200                });
22201        }
22202
22203        $scope.downloadInvoice = function (invoiceId) {
22204            $scope.errorMessage = '';
22205            memberDataService.DownloadInvoice(invoiceId).success(function (data, textStatus) {
22206                if (textStatus === 200) {
22207                    const fileName = `Invoice-${invoiceId}.pdf`;
22208                    const file = new Blob([data], { type: 'application/pdf' });
22209
22210                    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
22211                        window.navigator.msSaveOrOpenBlob(file, fileName);
22212                    }
22213                    else {
22214                        const fileUrl = URL.createObjectURL(file);
22215                        const link = document.createElement('a');
22216                        document.body.appendChild(link);
22217
22218                        link.href = fileUrl;
22219                        link.download = fileName;
22220                        link.click();
22221
22222                        document.body.removeChild(link);
22223                    }
22224                }
22225            }).error(function () {
22226                $scope.errorMessage =  $filter('translate')('form_unableToRetrieveData');
22227            });
22228        };
22229  };
22230})();
22231(function () {
22232    "use strict";
22233    angular
22234        .module("MemberPortal")
22235        .controller("portalInboxController", PortalInboxController);
22236
22237    PortalInboxController.$inject = [
22238        "$scope",
22239        "authStatusService",
22240        "memberDataService",
22241        "inboxService",
22242        "$location",
22243        "$rootScope"
22244    ];
22245    function PortalInboxController(
22246        $scope,
22247        authStatusService,
22248        memberDataService,
22249        inboxService,
22250        $location,
22251        $rootScope
22252    ) {
22253
22254        var vm = this;
22255
22256        vm.showDetails = showDetails;
22257
22258        vm.searchPageNumber = vm.currentPage;
22259
22260        vm.pageChangedPgcqt = pageChangedPgcqt;
22261
22262        vm.pageLimitChange = pageLimitChange;
22263        vm.getMessages = getMessages;
22264        vm.getArchivedMessages = getArchivedMessages;
22265
22266        vm.applyFilters = applyFilters;
22267        vm.resetPagination = resetPagination;
22268        vm.applySorting = applySorting;
22269
22270        vm.applyCategory = applyCategory;
22271        vm.getIconSrcByType = getIconSrcByType;
22272        vm.getMessageCategoriesCount = getMessageCategoriesCount;
22273
22274        vm.updateSearchParams = updateSearchParams;
22275
22276        vm.openFilterDropdown = openFilterDropdown;
22277        vm.clearFilters = clearFilters;
22278        vm.toggleFilter = toggleFilter;
22279
22280        vm.showSelectedFilters = showSelectedFilters;
22281        vm.openFilterDropdown = openFilterDropdown;
22282        vm.isArchivedFilterSelected = isArchivedFilterSelected;
22283
22284        vm.displayAllowedFilters = displayAllowedFilters;
22285        vm.onSelectAllToggle = onSelectAllToggle;
22286        vm.isMessageSelected = isMessageSelected;
22287        vm.getEndRecord = getEndRecord;
22288
22289        initialize();
22290
22291        function initialize() {
22292
22293            vm.title = "Inbox";
22294            vm.showMessageDetail = false;
22295
22296            vm.messages = [];
22297            vm.messageCategoriesCount = [];
22298            vm.selectedMessages = new Set();
22299
22300            vm.selectAll = false;
22301            vm.enableSelectAll = false;
22302
22303            vm.showPaginator = true;
22304            vm.currentPage = 1;
22305            vm.rowsPerPage = "5";
22306            vm.totalItems = 0;
22307
22308            vm.sortDirection = "descending";
22309            vm.sortKey = "modifiedDateTime";
22310
22311            vm.filters = angular.copy(inboxService.getFilters());
22312            vm.tempFilters = angular.copy(inboxService.getFilters());
22313
22314            vm.category = null;
22315            vm.activeCategory = null;
22316
22317            assignSearchParams();
22318            vm.getMessageCategoriesCount();
22319            vm.getMessages();
22320        }
22321
22322        function assignSearchParams() {
22323            const params = $location.search();
22324            if (params.messageState) {
22325                switch (params.messageState) {
22326                    case "needsMoreInfo":
22327                        vm.filters.find(function (x) {
22328                            return x.key === "reply";
22329                        }).selected = true;
22330                        vm.activeCategory = "";
22331                        break;
22332                    case "important":
22333                        vm.filters.find(function (x) {
22334                            return x.key === "important";
22335                        }).selected = true;
22336                        vm.activeCategory = "";
22337                        break;
22338                    case "archived":
22339                        vm.filters.find(function (x) {
22340                            return x.key === "archived";
22341                        }).selected = true;
22342                        vm.activeCategory = "archived";
22343                        break;
22344                }
22345            }
22346            vm.sortKey = params.SortBy || 'modifiedDateTime';
22347            vm.sortDirection = params.SortDirection || 'descending';
22348            vm.rowsPerPage = params.Limit || '5';
22349
22350
22351            const skip = parseInt(params.skip) || 0;
22352
22353            if (!skip) {
22354                vm.searchPageNumber = 1;
22355            } else {
22356                vm.searchPageNumber = Math.floor(skip / parseInt(vm.rowsPerPage)) + 1;
22357            }
22358            vm.currentPage = vm.searchPageNumber;
22359        }
22360
22361        function clearFilters() {
22362            vm.filters = angular.copy(inboxService.getFilters());
22363            vm.getMessages();
22364        };
22365
22366        function toggleFilter(filter) {
22367            filter.selected = !filter.selected;
22368            vm.getMessages();
22369        };
22370
22371        function showSelectedFilters() {
22372            if (vm.filters) {
22373                return (
22374                    vm.filters.filter(function (x) {
22375                        return x.selected && x.key !== "archived";
22376                    }).length !== 0
22377                );
22378            }
22379            return false;
22380        };
22381
22382        function openFilterDropdown() {
22383            vm.tempFilters = angular.copy(vm.filters);
22384        };
22385
22386        function isArchivedFilterSelected() {
22387            return vm.filters.find((x) => x.key === "archived" && x.selected)
22388                ? true
22389                : false;
22390        };
22391
22392        function displayAllowedFilters() {
22393            return vm.filters.filter(f => f.key !== "archived");
22394        }
22395
22396        function pageLimitChange() {
22397
22398            vm.updateSearchParams();
22399            vm.resetPagination();
22400            vm.getMessages();
22401        }
22402
22403        function pageChangedPgcqt(data) {
22404            vm.searchPageNumber = data;
22405
22406            vm.getMessages();
22407
22408            vm.updateSearchParams();
22409
22410        }
22411
22412        function showDetails(detail) {
22413            memberDataService
22414                .TakeMessageAction(detail.id, "read")
22415                .success(function () {
22416                    if (detail.readState !== true && memberDataService.count !== 0) {
22417                        memberDataService.count = memberDataService.count - 1;
22418                    }
22419                    detail.readState = true;
22420                    vm.message = detail;
22421                    vm.showMessageDetail = true;
22422                })
22423                .error(function (error) {
22424                    console.error(error);
22425                });
22426        }
22427
22428        function getIconSrcByType(type) {
22429            const url = inboxService.getIconSrcByType(type);
22430
22431            return url;
22432        }
22433
22434        function getMessageCategoriesCount() {
22435            const tabOrder = ["all", "pdr"];
22436            memberDataService
22437                .GetMessageCategoriesCount()
22438                .success(function (data) {
22439                    vm.messageCategoriesCount = data
22440                        .map(function (x) {
22441                            if (vm.activeCategory !== "archived" && x.messageCategory.toLowerCase() === "all") {
22442                                x.activeTab = true;
22443                            } else if (vm.activeCategory === "archived" && x.messageCategory.toLowerCase() === "archived") {
22444                                x.activeTab = true;
22445
22446                            } else {
22447                                x.activeTab = false;
22448                            }
22449                            return x;
22450                        })
22451                        .sort((a, b) => {
22452                            let aIdx = tabOrder.indexOf(a.messageCategory.toLowerCase());
22453                            let bIdx = tabOrder.indexOf(b.messageCategory.toLowerCase());
22454
22455                            if (aIdx < 0) {
22456                                aIdx = 1000;
22457                            }
22458                            if (bIdx < 0) {
22459                                bIdx = 1000;
22460                            }
22461
22462                            return aIdx - bIdx;
22463                        });
22464                })
22465                .error(function (error) {
22466                    console.error(error);
22467                });
22468        }
22469
22470        function getEndRecord() {
22471            return Math.min(vm.currentPage * vm.rowsPerPage, vm.totalItems);
22472        }
22473
22474        function resetPagination() {
22475            vm.searchPageNumber = 1;
22476        }
22477
22478        function applyCategory(category) {
22479            vm.category = category;
22480
22481            if (category.messageCategory.toLowerCase() !== "archived") {
22482                vm.resetPagination();
22483                vm.getMessages();
22484                vm.updateSearchParams();
22485            }
22486        }
22487
22488        function getArchivedMessages() {
22489            vm.filters = angular.copy(inboxService.getFilters());
22490            vm.filters.find(function (x) {
22491                return x.key === "archived";
22492            }).selected = true;
22493            vm.resetPagination();
22494            vm.getMessages();
22495            vm.updateSearchParams();
22496        }
22497
22498        function applyFilters(filters) {
22499            vm.filters = angular.copy(filters);
22500            vm.resetPagination();
22501            vm.getMessages();
22502        }
22503
22504        function applySorting(sortDirection) {
22505            vm.sortDirection = sortDirection;
22506            vm.resetPagination();
22507            vm.getMessages();
22508            vm.updateSearchParams();
22509        }
22510
22511        function updateSearchParams() {
22512
22513            let archivedTab = vm.filters.find(function (x) {
22514                return x.key === "archived";
22515            });
22516            let params = {
22517                SortBy: vm.sortKey,
22518                SortDirection: vm.sortDirection,
22519                Limit: vm.rowsPerPage,
22520                skip: parseInt(vm.rowsPerPage) * (vm.searchPageNumber - 1),
22521                activeTab: archivedTab.selected ? undefined : vm.activeCategory,
22522                messageState: archivedTab.selected ? "archived" : undefined
22523            }
22524
22525            // delete params that are not set
22526            params = Object.entries(params).reduce((acc, [key, value]) => {
22527                if (value !== undefined && value !== null && value !== '') {
22528                    acc[key] = value;
22529                }
22530                return acc;
22531            }, {});
22532
22533        }
22534
22535        function getMessages() {
22536
22537            vm.loadingMessages = true;
22538
22539            const authStatus = authStatusService.getStatus();
22540            const memberId = authStatus.claims.username;
22541            const skip = parseInt(vm.rowsPerPage) * (vm.searchPageNumber - 1);
22542
22543            var queryString = `?ContextIdentityMemberNumber=${memberId}&SortBy=${vm.sortKey}&SortDirection=${vm.sortDirection}&Limit=${vm.rowsPerPage}&skip=${skip}`;
22544
22545            if (vm.filters) {
22546                vm.filters.forEach(function (x) {
22547                    if (x.key === "important" && x.selected) {
22548                        queryString = queryString + "&ImportantOnly=true";
22549                    }
22550                    if (x.key === "reply" && x.selected) {
22551                        queryString = queryString + "&MessageState=Needs More Info";
22552                    }
22553                    if (x.key === "unread" && x.selected) {
22554                        queryString = queryString + "&UnreadOnly=true";
22555                    }
22556                    if (x.key === "read" && x.selected) {
22557                        queryString = queryString + "&ReadOnly=true";
22558                    }
22559                    if (x.key === "archived" && x.selected) {
22560                        queryString = queryString + "&ArchivedOnly=true";
22561                    }
22562                });
22563            }
22564
22565            if (vm.category && vm.category.messageCategory) {
22566                switch (vm.category.messageCategory.toLowerCase()) {
22567                    case "all":
22568                        break;
22569                    case "g & a":
22570                        queryString = queryString + "&MessageCategory=GNA";
22571                        break;
22572                    default:
22573                        queryString =
22574                            queryString + "&MessageCategory=" + vm.category.messageCategory;
22575                        break;
22576                }
22577            }
22578
22579            memberDataService
22580                .GetMessages(queryString)
22581                .success(function (data) {
22582                    let hasActionableMessages = false;
22583
22584                    vm.totalItems = data.totalCount;
22585                    vm.messages = data.results.map(m => {
22586                        //broadcastMessage's readState is managed locally through cookie
22587                        const message = {
22588                            ...m,
22589                            readState: m.isBroadcastMessage ? inboxService.isBroadcastMessageRead(m.id) : m.readState,
22590                        };
22591
22592                        if (!message.isBroadcastMessage) {
22593                            hasActionableMessages = true;
22594                        }
22595                        //console.log('message before altering', m);
22596
22597                        if (m.replies && m.replies.length > 0) {
22598                            const origMessage = m.replies.find(reply => reply.parentMessageId === null);
22599
22600                            //console.log('originalMessage', origMessage);
22601
22602                            message.currentMessageState.status = origMessage.currentMessageState.status;
22603                            message.importantFlag = origMessage.importantFlag;
22604                            message.messageCategory = origMessage.messageCategory;
22605                            //console.log('updated message', message);
22606                        }
22607
22608                        return {
22609                            ...message
22610                        };
22611                    });
22612
22613                    vm.readBroadcastMessageCnt = inboxService.getReadBroadcastMessageCnt(vm.messages);
22614
22615                    vm.selectAll = false;
22616                    vm.selectedMessages = new Set();
22617
22618                    vm.enableSelectAll = hasActionableMessages;
22619                    vm.searchParams = $location.search();
22620                })
22621                .error(function (error) {
22622                    console.error(error);
22623                })
22624                .finally(function () {
22625                    vm.loadingMessages = false;
22626                });
22627        }
22628
22629        function isMessageSelected(messageId) {
22630            return vm.selectedMessages.has(messageId);
22631        };
22632
22633        function onSelectAllToggle() {
22634            if (vm.selectAll) {
22635                vm.messages.forEach((m) => {
22636                    if (!m.isBroadcastMessage) {
22637                        vm.selectedMessages.add(m.id);
22638                    }
22639                });
22640            } else {
22641                vm.selectedMessages = new Set();
22642            }
22643        };
22644
22645        $rootScope.$on('replied-success', function () {
22646
22647            Toastify({
22648                text: 'Thanks for your reply!',
22649                offset: {
22650                    x: 0,
22651                    y: '3em'
22652                },
22653                backgroundColor: '#ffffff',
22654                duration: 3000,
22655                gravity: 'top',
22656                position: 'center',
22657                style: {
22658                    borderRadius: '5px',
22659                    border: '1px solid #4caf50',
22660                    color: 'black',
22661                    fontSize: '16px',
22662                    fontWeight: 'bold'
22663                }
22664            }).showToast();
22665        });
22666
22667        $scope.$on("reloadInbox", () => {
22668            vm.getMessages();
22669        });
22670
22671        $rootScope.$on("reloadCategoryCount", () => {
22672            vm.getMessageCategoriesCount();
22673            $rootScope.$emit("updateHeaderMessageCounts");
22674        });
22675
22676        $scope.$on("messageSelected", (event, { messageId, checked }) => {
22677            if (checked) {
22678                vm.selectedMessages.add(messageId);
22679            } else {
22680                vm.selectedMessages.delete(messageId);
22681            }
22682            vm.selectAll = vm.selectedMessages.size === vm.messages.length;
22683        });
22684
22685        $scope.$on("batchEmailAction", (event, { action, value }) => {
22686            if (vm.selectedMessages.size > 0) {
22687                const ids = Array.from(vm.selectedMessages);
22688                memberDataService
22689                    .InboxMessageBatchUpdate(ids, { action, state: value })
22690                    .then(() => {
22691                        if (action === "read") {
22692                            vm.messages = vm.messages.map((m) => ({
22693                                ...m,
22694                                readState: this.selectedMessages.has(m.id)
22695                                    ? value
22696                                    : m.readState,
22697                            }));
22698                        } else if (action === "archive") {
22699                            $scope.$emit("reloadInbox");
22700                        }
22701                        $rootScope.$emit("reloadCategoryCount");
22702                    })
22703                    .catch((err) => {
22704                        console.error(err);
22705                    })
22706                    .finally(() => {
22707                        this.selectedMessages = new Set();
22708                        this.selectAll = false;
22709                    });
22710            }
22711        });
22712    }
22713})();
22714
22715(function () {
22716    'use strict';
22717
22718    angular
22719        .module('MemberPortal')
22720        .controller('TransportationRequestsController', TransportationRequestsController);
22721
22722    TransportationRequestsController.$inject = [
22723        '$sce', 'authStatusService', 'confirmationModalService', '$location',
22724        'toastifyService', 'utilitiesService', 'transportationService'
22725    ];
22726
22727    function TransportationRequestsController(
22728        $sce, authStatusService, confirmationModalService, $location,
22729        toastifyService, utilitiesService, transportationService
22730    ) {
22731
22732        const vm = this;
22733
22734        vm.onTabChange = onTabChange;
22735        vm.openRowHeader = openRowHeader;
22736        vm.confirmCancellation = confirmCancellation;
22737        vm.searchByDateRange = searchByDateRange;
22738        vm.isValidPhone = isValidPhone;
22739        vm.validateDateRange = validateDateRange;
22740
22741        init();
22742
22743        // -------------------------------
22744        // Initialization
22745        // -------------------------------
22746        function init() {
22747
22748            if (!hasPageAccess()) {
22749                $location.path('App/Home');
22750                return;
22751            }
22752
22753            vm.isLoading = false;
22754            vm.errorMsg = null;
22755            vm.trustedUrl = null;
22756
22757            vm.dateRangeFrm = {};
22758
22759            vm.tabs = transportationService.getTabsConfig();
22760            console.log('tabs', vm.tabs);
22761
22762            const defaultTab = transportationService.getSingleTabConfig(vm.tabs, 'upcoming');
22763            loadActiveTab(defaultTab);
22764
22765            //disableRequestNewIfIneligible();
22766            getNewRequestUrl();
22767        }
22768
22769        function isValidPhone(phoneStr) {
22770          // Regex: matches '+' followed by exactly 11 digits (0-9)
22771          const phonePattern = /^\+\d{11}$/;
22772          
22773          return phonePattern.test(phoneStr);
22774        }
22775
22776        // -------------------------------
22777        // Load Data for Active Tab
22778        // -------------------------------
22779        function loadTabData(searchType, from, to) {
22780
22781            const allowedSearchTypes = ['upcoming', 'past'];
22782            
22783            if (!allowedSearchTypes.includes(searchType)) return;
22784
22785            vm.isLoading = true;                
22786            vm.errorMsg = null;                   
22787
22788            const selectedTab = transportationService.getSelectedTabConfig(vm.tabs);
22789
22790            if (!selectedTab) {
22791                vm.isLoading = false;
22792                return;
22793            }
22794    
22795            const memberNumber = getMemberNumber();
22796            if (!memberNumber) {
22797                vm.isLoading = false;
22798                return;
22799            }
22800
22801            const params = {
22802                memberNumber,
22803                startDate: from,
22804                endDate: to,
22805                searchType
22806            };
22807
22808            transportationService.getTripsByDateRange(params)
22809                .then(function(resp) {
22810                  console.log('load ride data from API', resp.data.rides);
22811                    if (!resp || !resp.data) {
22812                        vm.requests = [];
22813                        return;
22814                    }
22815
22816                    vm.requests = transportationService.mapRequestsApiResponseToUi(resp.data.rides);
22817
22818                    if (vm.lastCanceledTripId) {
22819                        const found = vm.requests.find(m=>m.tripId === vm.lastCanceledTripId)
22820                        
22821                        if (found) {found.headerOpen = true;}
22822                    }
22823
22824                    console.log('mapped trips', vm.requests);
22825
22826                })
22827                .catch(function(error) {
22828
22829                    if (error.status === 500 && error.data.message === 'Failed to find member') {
22830                        vm.requests = null;
22831                    }
22832
22833                    vm.errorMsg = 'Unable to retrieve your existing request. Please try again later.';
22834                })
22835                .finally(function() {
22836                    vm.isLoading = false;
22837                });
22838        }
22839
22840        function validateDateRange(toDate, fromDate, selectedTab) {
22841
22842            vm.fromDateErrorMsg = '';
22843            vm.toDateErrorMsg = '';
22844
22845            console.log('validating...');
22846
22847            if (!toDate || !fromDate || !selectedTab) {
22848                console.warn('validateDateRange: missing critical inputs');
22849                return;
22850            }
22851
22852            const format = "MM/DD/YYYY";
22853
22854            // parse user input (strict)
22855            const from = moment(fromDate, format, true);
22856            const to   = moment(toDate, format, true);
22857
22858            if (!from.isValid()){
22859
22860                vm.fromDateErrorMsg = 'wrong date format';
22861                vm.dateRangeFrm['fromDate'].$setValidity('check', !from.isValid());
22862                return false;
22863            }
22864                
22865            if (!to.isValid()) {
22866                vm.toDateErrorMsg = 'wrong date format';
22867                vm.dateRangeFrm['endDate'].$setValidity('check', !to.isValid());
22868                return false;               
22869            }
22870
22871            // parse allowed min/max (strict)
22872            const minAllowed = moment(selectedTab.minAllowedDate, format, true);
22873            const maxAllowed = moment(selectedTab.maxAllowedDate, format, true);
22874
22875            console.log('minAllowed', minAllowed);
22876            console.log('maxAllowed', maxAllowed);
22877            if (!minAllowed.isValid() || !maxAllowed.isValid()) {
22878                console.warn("Invalid min/max allowed date values in selected Tab.");
22879                return false;
22880            }
22881
22882            if (from.isBefore(minAllowed)) {
22883                console.error('less than allowed date');
22884                 vm.fromDateErrorMsg = 'Less than allowed date';
22885                 vm.dateRangeFrm['fromDate'].$setValidity('check', !vm.fromDateErrorMsg);
22886            };
22887
22888            if (to.isAfter(maxAllowed)) {
22889                  console.error('exceeds allowed date');
22890                 vm.toDateErrorMsg = 'Exceeds allowed date';
22891                 vm.dateRangeFrm['endDate'].$setValidity('check', !vm.toDateErrorMsg);
22892            }
22893        }
22894
22895        // -------------------------------
22896        // Search by Date Range
22897        // -------------------------------
22898        function searchByDateRange(searchType) {
22899   
22900            if (!vm.dateRangeFrm) return;
22901       
22902            vm.dateRangeFrm.$setSubmitted();
22903
22904            if (vm.dateRangeFrm.$invalid) {
22905                console.warn("Form invalid");
22906                return;
22907            }
22908
22909            vm.isSearching = true;
22910            vm.errorMsg = null;
22911
22912            const from = moment(vm.fromDate).format('YYYY-MM-DD'); 
22913            const to   = moment(vm.toDate).format('YYYY-MM-DD'); 
22914
22915            const params = {
22916                memberNumber: getMemberNumber(),
22917                startDate: from,
22918                endDate: to,
22919                searchType
22920            };
22921
22922            transportationService.getTripsByDateRange(params)
22923                .then(function(resp) {
22924
22925                    console.log('resp', resp);
22926
22927                    if (!resp || resp.status !== 200 || !resp.data || !Array.isArray(resp.data.rides)) { 
22928
22929                      vm.requests = [];
22930
22931                       vm.errorMsg = 'Unable to retrieve your existing request. Please try again later.';
22932
22933                      return;                     
22934                    }
22935
22936                    vm.requests = transportationService.mapRequestsApiResponseToUi(resp.data.rides);
22937
22938                })
22939                .catch(function(error) {
22940                    vm.errorMsg = 'Unable to retrieve your existing request. Please try again later.';
22941                })
22942                .finally(function() {
22943                    vm.isSearching = false;
22944                });
22945        }
22946
22947        // -------------------------------
22948        // Page Access Check
22949        // -------------------------------
22950        function hasPageAccess() {
22951            const authStatus = authStatusService.getStatus();
22952
22953            if (!authStatus) return false;
22954
22955            const isProxied = authStatusService.checkIfProxied();
22956
22957            const authClaims = authStatus.claims;
22958
22959            const isCCA = authStatus.isCCA;
22960
22961            const isOver18 = authClaims?.agegroup === '18+';
22962
22963            return isOver18 && !isCCA && !isProxied;
22964        }
22965
22966        // -------------------------------
22967        // Load Active Tab Based on Routing
22968        // -------------------------------
22969        function loadActiveTab(defaultTab) {
22970
22971            const urlTab = $location.search().tab;
22972
22973            if (urlTab) {
22974                vm.selectedTab = transportationService.getSingleTabConfig(vm.tabs, urlTab);   
22975                onTabChange(vm.selectedTab);
22976            }
22977            else {
22978                $location.search('tab', defaultTab.code);
22979                onTabChange(defaultTab);
22980            }             
22981        }
22982
22983        // -------------------------------
22984        // Get Member Number
22985        // -------------------------------
22986        function getMemberNumber() {
22987
22988            const authStatus = authStatusService.getStatus();
22989
22990            if (!authStatus.isAuth || authStatus.isCCA) {
22991                vm.errorMsg = 'You do not appear to be eligible to request transportation.';
22992                return null;
22993            }
22994
22995            return authStatus.claims.username;
22996        }
22997
22998        // -------------------------------
22999        // Disable New Request Tab
23000        // -------------------------------
23001        function disableNewRequestTab() {
23002            const createNewTab = vm.tabs.find(m => m.code === 'new');
23003            if (createNewTab) {
23004                createNewTab.isDisabled = true;
23005            }
23006        }
23007
23008        function disableRequestNewIfIneligible() {
23009
23010            const memberNumber = getMemberNumber();
23011            if (!memberNumber) return;
23012
23013            transportationService.isTransportationEligible(memberNumber)
23014                .then(function(resp) {
23015
23016                    const eligible = resp?.data?.isPilot === true;
23017
23018                    vm.newRequestIneligible = !eligible;
23019
23020                    if (!eligible) disableNewRequestTab();
23021
23022                })
23023                .catch(function() {
23024                    vm.newRequestIneligible = true;
23025                    disableNewRequestTab();
23026                });
23027        }
23028
23029        // -------------------------------
23030        // Toggle Row Header
23031        // -------------------------------
23032        function openRowHeader(ride) {
23033
23034            vm.requests.forEach(r => {
23035                    if (r !== ride) r.headerOpen = false;
23036                });
23037
23038            ride.headerOpen = !ride.headerOpen;
23039        }
23040
23041        // -------------------------------
23042        // Reset Date Inputs for Selected Tab
23043        // -------------------------------
23044        function resetDateRangeInputs(tab) {
23045         
23046            vm.fromDate = tab.defaultStartDate;
23047            vm.toDate = tab.defaultEndDate;
23048        }
23049
23050        // -------------------------------
23051        // Handle Tab Change
23052        // -------------------------------
23053        function onTabChange(tab) {
23054
23055            vm.tabs.forEach(t => t.active = false);
23056            tab.active = true;
23057
23058            vm.selectedTab = tab;
23059
23060            $location.search('tab', tab.code);
23061
23062            const fromDate = tab.defaultStartDate;
23063            const toDate = tab.defaultEndDate;
23064
23065            const from = moment(fromDate).format('YYYY-MM-DD');
23066            const to   = moment(toDate).format('YYYY-MM-DD');
23067
23068            loadTabData(tab.code, from, to);
23069            resetDateRangeInputs(tab);
23070        }
23071
23072        // -------------------------------
23073        // Confirm Cancellation
23074        // -------------------------------
23075        function confirmCancellation(toBeCanceled, rideTripId) {
23076            console.log('confirm cancellation...', toBeCanceled);
23077            let cancelingTripIds = [];
23078            let cancelingTrips = [];
23079            let title, message, parentId;
23080
23081
23082            if (toBeCanceled?.relatedTripIds) {
23083                cancelingTripIds = toBeCanceled.relatedTripIds;
23084                cancelingTrips = toBeCanceled.legs;
23085                parentId = toBeCanceled.tripId;
23086
23087                title = 'Cancel Trips';
23088                message = 'Are you sure you want to cancel selected trips?';
23089            }
23090            else if (toBeCanceled?.legId) {
23091                cancelingTripIds = [toBeCanceled.legId];
23092                cancelingTrips = [toBeCanceled];
23093                parentId = rideTripId;
23094
23095                title = 'Cancel Trip';
23096                message = 'Are you sure you want to cancel selected trip?';
23097            }
23098
23099            confirmationModalService.show(
23100                {
23101                    cancelingTrips,
23102                    title,
23103                    message,
23104                    modalType: 'cancel-transportation-modal',
23105                    yesBtn: 'Yes',
23106                    noBtn: 'No',
23107                    iconClass: 'fa fa-exclamation-triangle fa-4x color-red'
23108                },
23109                function () {
23110                    cancelTrips(cancelingTrips, cancelingTripIds, parentId);
23111                }
23112            );
23113        }
23114
23115        // -------------------------------
23116        // Cancel Trips
23117        // -------------------------------
23118        function cancelTrips(cancelingTrips, cancelingTripIds, parentId) {
23119
23120            console.log('cancelingTrips', cancelingTrips);
23121            console.log('cancelingTripIds', cancelingTripIds);
23122            console.log('parentId', parentId);
23123
23124            cancelingTrips.isCanceling = true;  
23125
23126            transportationService.cancelTransportationAppt({ cancelingTripIds })
23127                .then(function(resp) {
23128
23129                  if (resp.status === 200) {
23130                       toastifyService.showSuccess('Your trip has been canceled.');
23131                       const selectedTab = transportationService.getSelectedTabConfig(vm.tabs);
23132
23133                        const fromDate = vm.fromDate? vm.fromDate: selectedTab.defaultStartDate;
23134                        const toDate = vm.toDate? vm.toDate: selectedTab.defaultEndDate;
23135
23136                        const from = moment(fromDate).format('YYYY-MM-DD');
23137                        const to   = moment(toDate).format('YYYY-MM-DD');
23138
23139                        vm.lastCanceledTripId = parentId;
23140
23141                       loadTabData(selectedTab.code, from, to);
23142                  }
23143                  else {
23144                    showCancellationError();
23145                  }
23146                })
23147                .catch(function(error) {
23148                    showCancellationError();
23149                })
23150                .finally(function() {
23151                    cancelingTrips.isCanceling = false;                 
23152                });
23153        }
23154
23155        function showCancellationError() {
23156                    const title = "Cancellation Error";
23157                    const message = "We couldn't cancel one or more of your trips. Please check and try again.";
23158
23159                    confirmationModalService.show(
23160                        {
23161                            title,
23162                            message,
23163                            modalType: 'cancel-transportation-modal',
23164                            yesBtn: 'Yes',
23165                            noBtn: 'No',
23166                            iconClass: 'fa fa-exclamation-triangle fa-4x color-red'
23167                        });
23168        }
23169
23170        // -------------------------------
23171        // Get New Request URL
23172        // -------------------------------
23173        function getNewRequestUrl() {
23174
23175            const authStatus = authStatusService.getStatus();
23176
23177            if (!authStatus.isAuth || authStatus.isCCA) {
23178                toastifyService.showError("We couldn't submit your request. Please log back in and try again.");
23179                return;
23180            }
23181
23182            const memberNumber = authStatus.claims?.username;
23183            if (!memberNumber) {
23184                $location.path("/App/Account/Login");
23185                return;
23186            }
23187
23188            const requestId = memberNumber + utilitiesService.getTimeStamp();
23189
23190            const payload = {
23191                    requestId: requestId,
23192                    memberID: memberNumber
23193                };
23194                
23195            vm.isLoading = true;
23196
23197            transportationService.getTransportationEntryFormUrl(payload)
23198                .then(function(resp) {
23199console.log('ursur url', resp);
23200                    const url = resp?.data?.sessionSUrl;
23201
23202                    if (!url || !url.startsWith('https://')) {
23203                        toastifyService.showError("We couldn't submit your request. Please try again later.");
23204                        return;
23205                    }
23206
23207                    vm.trustedUrl = $sce.trustAsResourceUrl(url);
23208
23209                })
23210                .catch(function(err) {
23211                    console.error('Error retrieving transportation request:', err);
23212                })
23213                .finally(function() {
23214                    vm.isLoading = false;
23215                });
23216        }
23217
23218    }
23219})();
23220(function () {
23221    'use strict';
23222
23223    // Example banner config with all available properties:
23224    // {
23225    //     id: 'example-banner',              // Unique identifier for the banner
23226    //     active: true,                       // Set to false to disable without removing
23227    //     showPreLogin: true,                 // Show on login page before user LOB is known (default: false)
23228    //     lob: ['mcr', 'cmc', 'med'],        // Whitelist of LOBs that see this banner. Use ['*'] for all LOBs
23229    //     bgColor: '#FEBA41',                  // Optional background color override (default: #0082c8)
23230    //     link: {                             // URL per language (falls back to en-US if language missing)
23231    //         'en-US': 'https://www.iehp.org/en/members/example',
23232    //         'es-MX': 'https://www.iehp.org/es/members/example',
23233    //         'zh-TW': 'https://www.iehp.org/zh/members/example',
23234    //         'vi-VN': 'https://www.iehp.org/vi/members/example'
23235    //     },
23236    //     image: {                            // Image path per language (falls back to en-US if language missing)
23237    //         'en-US': 'Content/Images/example_EN.png',
23238    //         'es-MX': 'Content/Images/example_SP.png',
23239    //         'zh-TW': 'Content/Images/example_CH.png',
23240    //         'vi-VN': 'Content/Images/example_VI.png'
23241    //     },
23242    //     altKey: 'banner_example_alt',       // angular-translate key for image alt text
23243    //     titleKey: 'banner_example_title'    // angular-translate key for tooltip title
23244    // }
23245
23246    angular.module('MemberPortalDirectives')
23247        .constant('bannerConfig', [
23248            {
23249                id: 'telehealth',
23250                active: true,
23251                lob: ['*'], // enter * for all
23252                showPreLogin: true,
23253                link: {
23254                    'en-US': 'https://www.iehp.org/telehealth',
23255                    'es-MX': 'https://www.iehp.org/es/care-options/telehealth',
23256                    'zh-TW': 'https://www.iehp.org/zh/care-options/telehealth',
23257                    'vi-VN': 'https://www.iehp.org/vi/care-options/telehealth'
23258                },
23259                image: {
23260                    'en-US': 'Content/Images/Telehealth Banner_member-portal.jpg',
23261                    'es-MX': 'Content/Images/Telehealth Banner_member-portal_SP.jpg',
23262                    'zh-TW': 'Content/Images/Telehealth Banner_member-portal_CH.jpg',
23263                    'vi-VN': 'Content/Images/Telehealth Banner_member-portal_VI.jpg'
23264                },
23265                altKey: 'banner_telehealth_alt',
23266                titleKey: 'banner_telehealth_title',
23267                bgColor: '#346c6d'
23268            }
23269        ]);
23270})();
23271
23272(function () {
23273    'use strict';
23274
23275    const app = angular.module('MemberPortalDirectives');
23276
23277    app.directive('iehpBanner',
23278        [
23279            function () {
23280                return {
23281                    scope: {
23282                        config: '='
23283                    },
23284                    restrict: 'EA',
23285                    bindToController: true,
23286                    templateUrl: 'App/directives/account/banner/iehp-banner.directive.html',
23287                    controller: iehpBannerCtrl,
23288                    controllerAs: 'ctrl'
23289                };
23290            }
23291        ]);
23292
23293    app.controller('iehpBannerCtrl', iehpBannerCtrl);
23294
23295    iehpBannerCtrl.$inject = ['$scope', 'requestedLanguageService'];
23296
23297    function iehpBannerCtrl($scope, requestedLanguageService) {
23298        const vm = this;
23299        const englishCode = requestedLanguageService.getEnglishCode();
23300
23301        vm.lan = requestedLanguageService.getCurrentLanguage();
23302        updateLocalized();
23303
23304        $scope.$on('language-change', function (_event, languageCode) {
23305            vm.lan = languageCode || englishCode;
23306            updateLocalized();
23307        });
23308
23309        $scope.$watch('ctrl.config', function () {
23310            updateLocalized();
23311        });
23312
23313        function updateLocalized() {
23314            if (!vm.config) return;
23315
23316            vm.currentLink = vm.config.link[vm.lan] || vm.config.link[englishCode];
23317            vm.currentImage = vm.config.image[vm.lan] || vm.config.image[englishCode];
23318            vm.bannerId = 'banner-' + vm.config.id + '-' + vm.lan;
23319        }
23320    }
23321})();
23322
23323(function () {
23324    'use strict';
23325
23326    const app = angular.module('MemberPortalDirectives');
23327
23328    app.directive('iehpRotate',
23329        [
23330            function () {
23331                return {
23332                    scope: {},
23333                    restrict: 'EA',
23334                    bindToController: true,
23335                    templateUrl: 'App/directives/account/banner/iehp-rotate.directive.html',
23336                    controller: iehpRotateCtrl,
23337                    controllerAs: 'vc'
23338                };
23339            }
23340        ]);
23341
23342    app.controller('iehpRotateCtrl', iehpRotateCtrl);
23343
23344    iehpRotateCtrl.$inject = ['$scope', 'bannerConfig', 'authStatusService'];
23345
23346    function isColorDark(hex) {
23347        let normalized = hex.replace('#', '');
23348        if (normalized.length === 3) {
23349            normalized = normalized[0] + normalized[0] + normalized[1] + normalized[1] + normalized[2] + normalized[2];
23350        }
23351        const r = parseInt(normalized.substring(0, 2), 16);
23352        const g = parseInt(normalized.substring(2, 4), 16);
23353        const b = parseInt(normalized.substring(4, 6), 16);
23354        const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
23355        return luminance < 0.5;
23356    }
23357
23358    function isBannerVisible(banner, isLoggedIn, memberLob) {
23359        if (!banner.active) return false;
23360
23361        if (!isLoggedIn) return !!banner.showPreLogin;
23362
23363        const lobList = banner.lob.map(function (l) { return l.toLowerCase(); });
23364
23365        if (lobList.indexOf('*') !== -1) return true;
23366
23367        return lobList.indexOf(memberLob) !== -1;
23368    }
23369
23370    function iehpRotateCtrl($scope, bannerConfig, authStatusService) {
23371        const vm = this;
23372        let index = 0;
23373        let bannerInterval = null;
23374
23375        let authStatus;
23376        try { authStatus = authStatusService.getStatus(); } catch (e) { authStatus = null; }
23377
23378        const memberLob = (authStatus && authStatus.claims && authStatus.claims.lob)
23379            ? authStatus.claims.lob.toLowerCase()
23380            : '';
23381
23382        const isLoggedIn = !!(authStatus && authStatus.isAuth);
23383
23384        vm.banners = bannerConfig.filter(function (b) {
23385            return isBannerVisible(b, isLoggedIn, memberLob);
23386        });
23387
23388        if (vm.banners.length === 0) return;
23389
23390        vm.currentBanner = vm.banners[0];
23391        vm.arrowStyle = {};
23392        updateArrowColor();
23393        startAutoRotate();
23394
23395        function updateArrowColor() {
23396            const color = vm.currentBanner.bgColor || '#0082c8';
23397            vm.arrowStyle = { 'color': isColorDark(color) ? '#fff' : '#000' };
23398        }
23399
23400        function goTo(newIndex) {
23401            index = newIndex;
23402            vm.currentBanner = vm.banners[index];
23403            updateArrowColor();
23404        }
23405
23406        vm.next = function () {
23407            goTo((index + 1) % vm.banners.length);
23408            resetAutoRotate();
23409        };
23410
23411        vm.prev = function () {
23412            goTo((index - 1 + vm.banners.length) % vm.banners.length);
23413            resetAutoRotate();
23414        };
23415
23416        function startAutoRotate() {
23417            bannerInterval = setInterval(function () {
23418                $scope.$apply(function () {
23419                    goTo((index + 1) % vm.banners.length);
23420                });
23421            }, 5000);
23422        }
23423
23424        function resetAutoRotate() {
23425            clearInterval(bannerInterval);
23426            startAutoRotate();
23427        }
23428
23429        $scope.$on('$destroy', function () {
23430            clearInterval(bannerInterval);
23431        });
23432    }
23433})();
23434
23435(function () {
23436    'use strict';
23437
23438    const app = angular.module('MemberPortalDirectives');
23439
23440    const urlBase = $('meta[name="ApplicationRoot"]').attr('content');
23441    const cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
23442
23443    app.directive('changeCommPrefForm',
23444        [
23445            function () {
23446                return {
23447                    restrict: 'EA',
23448                    templateUrl: urlBase +
23449                        '/App/directives/account/change-comm-pref-form/changeCommPrefForm.html' +
23450                        cacheBust,
23451                    controller: changeCommPrefFormCtrl,
23452                    controllerAs: 'contact'
23453                };
23454            }
23455        ]);
23456
23457    app.controller('changeCommPrefFormCtrl', changeCommPrefFormCtrl);
23458
23459    changeCommPrefFormCtrl.$inject = [
23460        '$scope',
23461        'requestedLanguageService',
23462        'memberDataService',
23463        '$timeout',
23464        '$route',
23465        'authStatusService'
23466    ];
23467
23468    function changeCommPrefFormCtrl($scope,
23469        requestedLanguageService, memberDataService, $timeout, $route, authStatusService) {
23470
23471        const vm = this;
23472        
23473        vm.selectAll = selectAll;
23474        vm.saveContactDetails = saveContactDetails;
23475        vm.toggleEdit = toggleEdit;
23476        vm.saveNotifications = saveNotifications;
23477        vm.cancelNotifications = cancelNotifications;
23478        vm.trackChange = trackChange;
23479        vm.isChannelShown = isChannelShown;
23480        vm.checkSelectAll = checkSelectAll;
23481
23482        init();
23483
23484        function mapContactDetails(contact) {
23485
23486            return {
23487                email: contact.email,
23488                mobilePhone: contact.phone,
23489                address: formatAddress(contact.address),
23490                isPhoneOptIn: contact.phone.isEnabled
23491            };
23492        } 
23493
23494        function formatAddress(address) {
23495
23496            if (!address || !address.line1) return '';
23497
23498            return `${address.line1} ${address.line2} ${address.city}, ${address.state} ${address.zip}`;
23499        }
23500
23501        function mapCommPrefSettings(settings) {
23502            const commPrefSettings = [];
23503
23504            settings.forEach(function (item) {
23505                
23506                const setting = {
23507                    prefTitle: item.name,
23508                    prefSettings: matchChannel(item.channels),
23509                    prefCode: item.code
23510                }
23511
23512                commPrefSettings.push(setting);
23513            });
23514           
23515            return commPrefSettings;
23516        }
23517
23518        function matchChannel(channels) {
23519           
23520            const availableChannels = vm.availableChannels.map(m => m.code.toLowerCase());
23521         
23522            const matched = channels.filter(m => availableChannels.includes(m.code.toLowerCase()));
23523       
23524            return matched;
23525        }
23526
23527        function toggleEdit() {
23528            
23529            vm.isContactDetailsEditable = !vm.isContactDetailsEditable;
23530            console.log('toggle edit', vm.isContactDetailsEditable);
23531
23532            $('document').ready(function () {
23533                $("#mobilePhoneTxt").mask("(999) 999-9999");
23534            });
23535
23536            vm.contactDetails.newMobilePhoneNumber = vm.contactDetails.mobilePhone.phoneNumber;
23537            vm.contactDetails.newEmail = vm.contactDetails.email;
23538        }
23539
23540        function customizeOrderBy(settings) {
23541            const customOrder = {
23542                sms: 1,
23543                email: 2,
23544                postal: 3
23545            };
23546
23547            settings.forEach(function(item) {
23548                item.prefSettings.sort(compare);
23549            });
23550
23551            return settings;
23552
23553            function compare(a, b) {
23554                const settingA = a.code.toLowerCase();
23555                const settingB = b.code.toLowerCase();
23556
23557                let comparison = 0;
23558
23559                if (customOrder[settingA] > customOrder[settingB]) {
23560                    comparison = 1;
23561                }
23562                else if (customOrder[settingA] < customOrder[settingB]) {
23563                    comparison = -1;
23564                }
23565
23566                return comparison;
23567            }
23568        }
23569
23570        function initSelectAll() {
23571            vm.checkAll = {
23572                sms: false,
23573                email: false,
23574                postal: false
23575            };
23576        }
23577
23578        function cancelNotifications() {
23579            $route.reload();
23580        }
23581
23582        function saveNotifications() {
23583          
23584            vm.savingNotifications = true;
23585            vm.prefSaveError = false;
23586            vm.prefSaveSuccess = false;
23587            
23588            vm.prefChanges.username = vm.authStatus.claims.username;
23589
23590            console.log('saving notifications', vm.prefChanges);
23591
23592            memberDataService.SaveCommPref(vm.prefChanges).success(function() {
23593                vm.savingNotifications = false;
23594                vm.prefSaveSuccess = true;
23595            }).error(function() {
23596                vm.savingNotifications = false;
23597                vm.prefSaveError = true;
23598            });
23599        }
23600
23601        function unmaskPhoneNo(maskedPhoneNo) {
23602
23603            if (!maskedPhoneNo) return null;
23604
23605            return maskedPhoneNo.replace(/\D/g, '');
23606        }
23607
23608        function saveMobilePhoneNumber(username, newMobile) {
23609            vm.savingMobile = true;
23610            vm.mobileSaveError = false;
23611            
23612            const payload = {
23613                username: username,
23614                phone: {
23615                    phoneNumber: newMobile
23616                }
23617            };
23618
23619            memberDataService.SaveMobilePhoneNumber(payload).success(function () {
23620
23621                vm.savingMobile = false;
23622
23623                vm.contactDetails.mobilePhone.phoneNumber = unmaskPhoneNo(vm.contactDetails.newMobilePhoneNumber);
23624
23625                vm.isContactDetailsEditable = false;
23626            }).error(function () {
23627                vm.savingMobile = false;
23628                vm.mobileSaveError = true;
23629            });
23630
23631        }
23632
23633        function saveCommPrefEmail(username, newEmail) {
23634
23635            vm.savingEmail = true;
23636            vm.emailSaveError = false;
23637
23638            const payload = {
23639                username: username,
23640                email: newEmail
23641            };
23642
23643            memberDataService.SaveCommPrefEmail(payload).success(function () {
23644                vm.savingEmail = false;
23645
23646                vm.contactDetails.email = vm.contactDetails.newEmail;
23647
23648                vm.isContactDetailsEditable = false;
23649            }).error(function () {
23650                vm.savingEmail = false;
23651                vm.emailSaveError = true;
23652            });
23653        }
23654
23655        function saveContactDetails(form) {
23656            console.log('form', form);
23657            if (!form.$valid) return;
23658
23659            console.log('saving contact info...', form);
23660            
23661            const username = vm.authStatus.claims.username;
23662
23663            if (vm.contactDetails.newMobilePhoneNumber !== vm.contactDetails.mobilePhone.phoneNumber) {
23664                saveMobilePhoneNumber(username, vm.contactDetails.newMobilePhoneNumber);
23665            }
23666
23667            if (vm.contactDetails.newEmail !== vm.contactDetails.email) {
23668                saveCommPrefEmail(username, vm.contactDetails.newEmail);
23669            }
23670        }
23671
23672        function updatePrefSettingChanges(prefSetting, channelChanged, originalChannel) {
23673            if (!prefSetting || !channelChanged) return;
23674
23675            const existingPrefSetting = vm.prefChanges.contactPreference
23676                .find(m => m.code.toLowerCase() === prefSetting.prefCode.toLowerCase());
23677            
23678            if (existingPrefSetting) {
23679                const existingChannel = existingPrefSetting.channels
23680                    .find(m => m.code.toLowerCase() === channelChanged.code.toLowerCase());
23681         
23682                const matchOriginalValue = channelChanged.isSet === originalChannel.isSet;
23683           
23684                if (existingChannel && !matchOriginalValue) {
23685                    existingChannel.isSet = channelChanged.isSet;
23686                }
23687                else if (existingChannel && matchOriginalValue) {
23688
23689                    existingPrefSetting.channels =
23690                        existingPrefSetting.channels
23691                        .filter(m => m.code.toLowerCase() !== originalChannel.code.toLowerCase());
23692
23693                    if (existingPrefSetting.channels.length === 0) {
23694                        vm.prefChanges.contactPreference = vm.prefChanges.contactPreference.filter(m => m.code.toLowerCase() !== existingPrefSetting.code.toLowerCase());
23695                    }
23696                }
23697                else {
23698                    existingPrefSetting.channels.push(generateChannel(channelChanged));
23699                }
23700                
23701            } else {
23702                const setting = generatePrefSetting(prefSetting, channelChanged);
23703              
23704                vm.prefChanges.contactPreference.push(setting);
23705            }
23706
23707            console.log('vm.prefChanges', vm.prefChanges);
23708        }
23709        
23710        function generatePrefSetting(prefSetting, channelChanged) {
23711
23712            return {
23713                code: prefSetting.prefCode,
23714                channels: [generateChannel(channelChanged)]
23715            };
23716        }
23717
23718        function generateChannel(channelChanged) {
23719            return {
23720                code: channelChanged.code,
23721                isSet: channelChanged.isSet
23722            }
23723        }
23724
23725        function checkSelectAll(item, channel) {
23726            //after clicking "select all" under a channel,
23727            //if any of notifications unchecked for the channel
23728            //then uncheck the "select all"
23729            if (!channel.isSet) {
23730                const channelSelected = channel.code.toLowerCase();
23731                vm.checkAll[channelSelected] = false;
23732            }
23733        }
23734
23735        function trackChange(prefSetting, channelChanged) {
23736          
23737            const originalSetting = vm.prefOriginalState.pref
23738                .find(m => m.prefCode === prefSetting.prefCode);
23739
23740            const originalChannel = originalSetting.prefSettings
23741                .find(m => m.code.toLowerCase() === channelChanged.code.toLowerCase());
23742            
23743            updatePrefSettingChanges(prefSetting, channelChanged, originalChannel);
23744        }
23745
23746        function init() {
23747
23748            vm.isContactDetailsEditable = false;
23749            vm.savingMobile = false;
23750            vm.savingNotifications = false;
23751            vm.contactSaveError = false;
23752            vm.prefSaveError = false;
23753            vm.prefSaveSuccess = false;
23754            vm.prefLoading = false;
23755
23756            vm.currentLanguage = requestedLanguageService.getCurrentLanguage();
23757
23758            vm.prefChanges = {
23759                contactPreference: []
23760            };
23761
23762            vm.authStatus = authStatusService.getStatus();
23763
23764            const isProxied = authStatusService.checkIfProxied();
23765
23766            vm.isEditable = !isProxied;
23767            
23768            vm.availableChannels = [];
23769            
23770            vm.servicePhoneNo = vm.authStatus.isCCA ? '1-855-433-4347' : '1-800-440-4347'; 
23771
23772            vm.prefOriginalState = {};
23773
23774            initSelectAll();
23775
23776            loadCommunicationPrefs();
23777        }
23778
23779        function selectAll(selectedChannel) {
23780            
23781            vm.commPrefSettings.forEach(function(setting) {
23782                setting.prefSettings.forEach(function (channel) {
23783                  
23784                    if (channel.code.toLowerCase() === selectedChannel.toLowerCase() &&
23785                        !channel.isReadonly && channel.isApplicable) {
23786
23787                        channel.isSet = vm.checkAll[selectedChannel];
23788
23789                        trackChange(setting, channel);
23790                    }
23791                });
23792            });
23793
23794            console.log('vm.prefChanges', vm.prefChanges);
23795        }
23796
23797        function setPrefDefault(contactDetails, settings) {
23798          
23799            if (!contactDetails.isPhoneOptIn) {
23800                settings.forEach(function(setting) {
23801                    setting.prefSettings.forEach(function(channel) {
23802                        if (channel.code.toLowerCase() === 'sms') {
23803                            channel.isReadonly = true;
23804                        }
23805                    });
23806                });
23807            }
23808
23809            return settings;
23810        }
23811
23812        function cloneOriginalState(pref, contact) {
23813            const prefClone = JSON.parse(JSON.stringify(pref));
23814            const contactClone = JSON.parse(JSON.stringify(contact));
23815
23816            return {
23817                pref: prefClone,
23818                contact: contactClone
23819            };
23820        }
23821
23822        function isChannelShown(channel) {
23823            return vm.availableChannels.some(m => m.code.toLowerCase() === channel.toLowerCase());
23824        }
23825        
23826        function loadCommunicationPrefs() {
23827
23828            const memberNumber = vm.authStatus.claims.username;
23829
23830            const channelTotalWidthInPercent = 40;
23831
23832            vm.prefLoading = true;
23833            vm.loadingError = false;
23834
23835            memberDataService.GetCommPref(memberNumber, vm.currentLanguage).then(function (result) {
23836            
23837                console.log('preferences from api', result.data);
23838                vm.contactDetails = mapContactDetails(result.data.contactDetail);
23839               
23840                vm.availableChannels = result.data.availableChannels;
23841
23842                vm.channelWidth = channelTotalWidthInPercent / vm.availableChannels.length;
23843
23844                const mappedSettings = mapCommPrefSettings(result.data.contactPreference);
23845               
23846                const orderedSettings = customizeOrderBy(mappedSettings);
23847                
23848                vm.commPrefSettings = setPrefDefault(vm.contactDetails, orderedSettings);
23849
23850                vm.prefOriginalState = cloneOriginalState(vm.commPrefSettings, vm.contactDetails);
23851
23852                vm.prefLoading = false;
23853            }, function (error) {
23854                console.log('error loading comm preferences', error);
23855                vm.loadingError = true;
23856                vm.prefLoading = false;
23857            });
23858        }
23859
23860        $scope.$on('language-change', function () {
23861
23862            init();
23863
23864        });
23865    }
23866})();
23867
23868(function () {
23869    'use strict';
23870
23871    const app = angular.module('MemberPortalDirectives');
23872
23873    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
23874    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
23875
23876    app.directive('changeEmailForm',
23877        [
23878        function () {
23879        return {
23880                restrict: 'EA',
23881                templateUrl: urlBase + '/App/directives/account/change-email-form/changeEmailForm.html' + cacheBust,
23882                controller: changeEmailFormCtrl,
23883                controllerAs: 'ctrl'
23884            };
23885        }
23886    ]);
23887
23888    app.controller('changeEmailFormCtrl', changeEmailFormCtrl);
23889
23890    changeEmailFormCtrl.$inject = ['$scope', 'authService', '$filter', 'authStatusService', 'memberDataService'];
23891
23892    function changeEmailFormCtrl($scope, authService, $filter, authStatusService, memberDataService) {
23893            const vm = this;
23894
23895            $scope.loading = false;
23896            $scope.urlBase = urlBase;
23897
23898            vm.errorMessage = '';
23899            vm.infoMessage = '';
23900            vm.successMessage = '';
23901
23902            vm.authStatus = authStatusService.getStatus();
23903            vm.currentEmail = vm.authStatus.claims.email;
23904
23905        $scope.checkEmailMatch = function () {
23906
23907                $('#emailMatchString').fadeIn('slow');
23908                const newEmail1 = $('#newEmail').val();
23909                const newEmail2 = $('#matchEmail').val();
23910
23911                if ((newEmail1 === newEmail2) && (newEmail1.trim() !== '')) {
23912                    $('#emailMatchString').html($filter('translate')('form_emailsMatch')).addClass("emailSucess");
23913                    $('#emailMatchString').html($filter('translate')('form_emailsMatch')).removeClass("email_NotMatch");
23914                    $('#matchEmail').html($filter('translate')('form_emailsMatch')).removeClass("inputMissMatch");
23915                   
23916                    $scope.isEmailMatch = true;
23917                }
23918                else {
23919                    $('#emailMatchString').html($filter('translate')('form_emailsDoNotMatch')).addClass("email_NotMatch");
23920                    $('#emailMatchString').html($filter('translate')('form_emailsDoNotMatch')).removeClass("emailSucess");
23921                    $('#matchEmail').html($filter('translate')('form_emailsDoNotMatch')).addClass("inputMissMatch");
23922                    $scope.isEmailMatch = false;
23923                }
23924            };
23925
23926        $scope.SubmitEmailChange = function () {
23927
23928            $scope.loading = true;
23929
23930            vm.errorMessage = '';
23931            vm.infoMessage = '';
23932            vm.successMessage = '';
23933
23934           
23935           const parameterModel = {
23936                UserName: vm.authStatus.claims.username,
23937                Password: vm.password,
23938                NewEmail: vm.newEmail,
23939                ConfirmEmail: vm.matchEmail,
23940                name: 'WebMemberPortal'
23941            };
23942
23943                //call logging API
23944            memberDataService.activityWrite('MyInformation-ChangeEmail');
23945
23946            authService.ChangeEmail(parameterModel).success(function () {
23947                $scope.loading = false;
23948
23949                //Clear the form
23950                vm.newEmail = '';
23951                vm.matchEmail = '';
23952                vm.password = '';
23953
23954                //Display Success Message
23955                vm.successMessage = $filter('translate')('form_successMessageEmailUpdate');// "Success! Your email has been changed.";
23956
23957                //Refresh Token to reflect new email
23958                authService.refreshToken().then(function (resp) {
23959                    console.log('token refreshed', resp);
23960                });
23961
23962                //Should we inform the user that an email has been sent.
23963            }).error(function () {
23964                $scope.loading = false;
23965                vm.errorMessage = $filter('translate')('form_thereWasAProblemWithEmailChange');
23966              
23967            });
23968            };
23969        };
23970})();
23971(function () {
23972    'use strict';
23973
23974    const app = angular.module('MemberPortalDirectives');
23975
23976    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
23977    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
23978
23979    app.directive('changePasswordForm',
23980        [
23981            function () {
23982            return {
23983                restrict: 'EA',
23984                templateUrl: urlBase + '/App/directives/account/change-password-form/changePasswordForm.html' + cacheBust,
23985                controller: changePasswordFormCtrl,
23986                controllerAs: 'ctrl'
23987            };
23988    }]);
23989
23990    app.controller('changePasswordFormCtrl', changePasswordFormCtrl);
23991
23992    changePasswordFormCtrl.$inject = ['$rootScope', '$scope',
23993        '$filter', 'authService', 'authStatusService', 'memberDataService'];
23994
23995    function changePasswordFormCtrl($rootScope, $scope,
23996        $filter,
23997        authService,
23998        authStatusService,
23999        memberDataService) {
24000
24001        $scope.loading = false;
24002        $scope.errorMessage = '';
24003        $scope.infoMessage = '';
24004        $scope.successMessage = '';
24005        $scope.urlBase = urlBase;
24006        $scope.txtPassword1Show = false;
24007        $scope.currentPassShow = false;
24008        $scope.newPasswordShow = false;
24009        $scope.matchPasswordShow = false;
24010       
24011        $scope.authStatus = authStatusService.getStatus();
24012
24013        const isProxied = authStatusService.checkIfProxied();
24014
24015        //console.log('isProxied', isProxied);
24016        $scope.isEditable = !isProxied;
24017
24018        //========= PASSWORD STRENGTH
24019        $scope.checkPasswordStrength = function(e) {
24020            if (e.isShiftKey) return;
24021
24022            const newPassword = $('#newPassword').val();
24023
24024            $rootScope.$broadcast('password-changed', newPassword);
24025            console.log("triggered the password change");
24026        }
24027
24028        $scope.showHidePassword = function (elementId) {
24029            const pwdField = $('#' + elementId);
24030         
24031            pwdField.attr('type') === 'password' ? pwdField.prop('type', 'text') : pwdField.prop('type', 'password');
24032
24033            $scope[elementId + 'Show'] = !$scope[elementId + 'Show']; // Control the eye icon and its title in the template            
24034        }
24035
24036        $scope.SubmitPasswordChange = function() {
24037            $scope.loading = true;
24038            $scope.errorMessage = '';
24039            $scope.infoMessage = '';
24040            $scope.successMessage = '';
24041            
24042            const parameterModel = {
24043                UserName: $scope.authStatus?.claims?.username,
24044                OldPassword: $('#currentPass').val(), // $scope.changePasswordForm.currentpass.value,
24045                ConfirmPassword: $('#matchPassword').val(), //  $scope.changePasswordForm.newPassword.value,
24046                NewPassword: $('#newPassword').val(), //$scope.changePasswordForm.newPasswordConfirmation.value,
24047                name: 'WebMemberPortal'
24048            };
24049
24050            memberDataService.activityWrite('MyInformation-ChangePassword');
24051
24052            authService.ChangePassword(parameterModel).success(function() {
24053
24054                $scope.loading = false;
24055
24056                $scope.successMessage = $filter('translate')('form_successfullyChangedPassword');
24057                //Clear the form
24058                $('#currentPass').val('');
24059                $('#matchPassword').val('');
24060                $('#newPassword').val('');
24061
24062
24063                //Display Success Message
24064                //Should we inform the user that an email has been sent.
24065
24066            }).error(function() {
24067                $scope.loading = false;
24068                $scope.errorMessage = $filter('translate')('form_problemChangingPassword');
24069
24070            });
24071        };
24072    };
24073})();
24074
24075
24076
24077(function () {
24078    'use strict';
24079
24080    const app = angular.module('MemberPortalDirectives');
24081
24082    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
24083    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
24084
24085    app.directive('changeSecurityQuestionsForm',
24086        [
24087            function() {
24088                return {
24089                    restrict: 'EA',
24090                    templateUrl: urlBase +
24091                        '/App/directives/account/change-security-questions-form/changeSecurityQuestionsForm.html' +
24092                        cacheBust,
24093                    controller: changeSecurityQuestionsFormCtrl,
24094                    controllerAs: 'ctrl'
24095                };
24096            }
24097        ]);
24098
24099    app.controller('changeSecurityQuestionsFormCtrl', changeSecurityQuestionsFormCtrl);
24100
24101    changeSecurityQuestionsFormCtrl.$inject = [
24102        '$scope',
24103        'authService',
24104        'requestedLanguageService',
24105        'authStatusService'
24106    ];
24107
24108    function changeSecurityQuestionsFormCtrl($scope, authService,
24109        requestedLanguageService, authStatusService) {
24110
24111            $scope.loading = false;
24112            $scope.failure = false;
24113            $scope.info = false;
24114            $scope.success = false;
24115            $scope.submitting = false;
24116
24117            // Questions
24118            $scope.validate = { questions: false, answers: false, password: false, questionSelect: false };
24119            $scope.question = ['', '', ''];
24120            $scope.answer = [null, null, null];
24121            $scope.password = null;
24122            $scope.questions = [];
24123
24124            // Retrieve Question List
24125            init();
24126
24127            function init() {
24128                const language = requestedLanguageService.getCurrentLanguage;
24129
24130                const isProxied = authStatusService.checkIfProxied();
24131
24132               // console.log('isProxied', isProxied);
24133                $scope.isEditable = !isProxied;
24134
24135                authService.getAllQuestions(language).then(function (result) {
24136                    $scope.questions = result.data.map(function (q) {
24137                        return (Object.assign({}, q, { disabled: false }));
24138                    });
24139                });
24140            }
24141
24142            $scope.$on('language-change', function () {
24143
24144                init();
24145
24146            });
24147
24148            /* When a new question is selected, mark the list as dirty for rebuilding */
24149            $scope.$watchCollection('question', function (newValue) {
24150                $scope.questions.forEach(function (q) {
24151                    q.disabled = newValue.includes(q.QuestionId.toString());
24152                });
24153
24154                $scope.validate.questions = newValue.reduce(function (p, c) { return !c ? false : p, true });
24155
24156                $scope.validate.questionSelect = !$scope.question.includes('');
24157            });
24158
24159            /* When an answer is modified, validate */
24160            $scope.$watchCollection('answer', function (newValue) {
24161                $scope.validate.answers = newValue.reduce(function (p, c) { return !c ? false : p, true });
24162                $scope.validate.questionSelect = !$scope.question.includes('');
24163            });
24164
24165            /* When password is modified, validate */
24166            $scope.$watch('password',
24167                function (newValue) {
24168                    $scope.validate.password = !!newValue;
24169                });
24170
24171            /**
24172             * Get the actual question text in chosen language for a question at index.
24173             * @param {any} index
24174             */
24175            $scope.getQuestion = function (index) {
24176                if (!$scope.questions[index]) return '';
24177
24178                return $scope.questions[index].Question;
24179            };
24180
24181            /**
24182             * Update security questions with QuestionIds in $scope.question
24183             **/
24184        $scope.submit = function () {
24185
24186                $scope.submitting = true;
24187                $scope.success = false;
24188                $scope.failure = false;
24189                $scope.info = false;
24190
24191                const claims = authStatusService.getStatus().claims;
24192
24193            //map(currentValue, index)
24194            const questionSet = $scope.question.map(function (id, i) {
24195                
24196                return $scope.questions.reduce(function (results, item) {
24197
24198                        return item.QuestionId === parseInt(id) ? {
24199                            QuestionId: item.QuestionId,
24200                            SubscriberNumber: claims.username,
24201                            Answer: $scope.answer[i]
24202                        } : results;
24203                    }, null);
24204                });
24205
24206                const submit = {
24207                    name: 'WebMemberPortal',
24208                    UserName: claims.username,
24209                    Password: $scope.password,
24210                    questions: questionSet
24211                };
24212
24213                authService.submitQuestions(submit)
24214                    .then(function () {
24215                        $scope.success = true;
24216                        $scope.question = ['', '', ''];
24217                        $scope.answer = [null, null, null];
24218                        $scope.password = null;
24219                        $scope.questions = [];
24220                    })
24221                    .catch(function () {
24222                        $scope.failure = true;
24223                    })
24224                    .finally(function () {
24225                        $scope.submitting = false;
24226                    });
24227            };
24228        }
24229})();
24230
24231(function () {
24232  "use strict";
24233
24234  var urlBase = $('meta[name="ApplicationRoot"]').attr("content");
24235  var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr("content")}`;
24236
24237  const app = angular.module("MemberPortalDirectives");
24238
24239  app.directive("loginForm", [
24240    function () {
24241      return {
24242        restrict: "EA",
24243        templateUrl:
24244          urlBase +
24245          "/App/directives/account/login-form/loginForm.html" +
24246          cacheBust,
24247        controller: loginFormCtrl,
24248        controllerAs: "ctrl",
24249      };
24250    },
24251  ]);
24252
24253  app.controller("loginFormCtrl", loginFormCtrl);
24254
24255  loginFormCtrl.$inject = [
24256    "$scope",
24257    "authService",
24258    "$location",
24259    "authStatusService",
24260    "memberIDCardService",
24261    "$filter",
24262  ];
24263
24264  function loginFormCtrl(
24265    $scope,
24266    authService,
24267    $location,
24268    authStatusService,
24269    memberIDCardService,
24270    $filter
24271  ) {
24272    $scope.loading = false;
24273    $scope.errorMessage = "";
24274    $scope.infoMessage = "";
24275    $scope.showPassword = false;
24276    $scope.captchaResponse = null;
24277
24278    if (authStatusService.getStatus().isAuth) {
24279      $("#divSignInSignOut").css("display", "block");
24280      $("#langCont").css("display", "block");
24281      $location.path("App/Home");
24282    }
24283
24284    $scope.setCaptchaResponse = function(response) {
24285      $scope.captchaResponse = response;
24286    }
24287
24288    $scope.submit = function () {
24289      $scope.loading = !$scope.loading;
24290      $scope.errorMessage = "";
24291
24292      if(!$scope.captchaResponse) {
24293        $scope.loading = false;
24294        $scope.errorMessage = 'Invalid ReCaptcha'
24295        return;
24296      }
24297
24298      if (!$scope.username || $scope.username.length < 10) {
24299        $scope.loading = false;
24300        $scope.errorMessage = $filter("translate")(
24301          "form_signInPage_wrongUserNamePassword"
24302        );
24303      }
24304
24305      authService
24306        .login({
24307          username: $scope.username.substring(0, 14),
24308          password: $scope.password,
24309          recaptcha: $scope.captchaResponse
24310        })
24311        .then(
24312          function () {
24313            memberIDCardService.GetMemberTempCardImage();
24314
24315            $scope.loading = !$scope.loading;
24316
24317            $("#divSignInSignOut").css("display", "block");
24318            $("#langCont").css("display", "block");
24319          },
24320          function (response) {
24321            $scope.loading = !$scope.loading;
24322
24323            if (response.error && response.error === "temporary password") {
24324              $("#bakModal").fadeIn("fast");
24325              $scope.showResetForm = true;
24326            } else if (
24327              response.error_description &&
24328              response.error_description !== ""
24329            ) {
24330              $scope.errorMessage = response.error_description;
24331            } else {
24332              $scope.errorMessage = $filter("translate")(
24333                "form_signInPage_wrongUserNamePassword"
24334              );
24335              $scope.setCaptchaResponse(null)
24336            }
24337          }
24338        );
24339    };
24340  }
24341})();
24342
24343(function () {
24344  "use strict";
24345
24346  const app = angular.module("MemberPortalDirectives");
24347
24348  app.directive("passwordResetForm", [
24349    function () {
24350      const urlBase = $('meta[name="ApplicationRoot"]').attr("content");
24351      const cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr(
24352        "content"
24353      )}`;
24354
24355      return {
24356        restrict: "EA",
24357        templateUrl:
24358          urlBase +
24359          "/App/directives/account/password-reset-form/passwordResetForm.html" +
24360          cacheBust,
24361        controller: passwordResetFormController,
24362        controllerAs: "ctrl",
24363      };
24364    },
24365  ]);
24366
24367  app.controller("passwordResetFormController", passwordResetFormController);
24368
24369  passwordResetFormController.$inject = [
24370    "$scope",
24371    "$filter",
24372    "$location",
24373    "authService",
24374    "dialogs",
24375    "$route",
24376    "requestedLanguageService",
24377  ];
24378
24379  function passwordResetFormController(
24380    $scope,
24381    $filter,
24382    $location,
24383    authService,
24384    $dialogs,
24385    $route,
24386    requestedLanguageService
24387  ) {
24388    $scope.step = 1;
24389    $scope.password = "";
24390    $scope.passwordConfirm = "";
24391    $scope.loadingPRStep1 = false;
24392    $scope.loadingPRStep2 = false;
24393    $scope.loadingPRStep3 = false;
24394    $scope.loadingPRStep4 = false;
24395    $scope.newPasswordShow = false;
24396    $scope.matchPasswordShow = false;
24397    $scope.captchaResponse = null;
24398
24399    $scope.dateOfBirthIsValid = false;
24400
24401    $scope.setCaptchaResponse = function(response) {
24402      $scope.captchaResponse = response;
24403    }
24404
24405    $scope.reloadRoute = function () {
24406      $route.reload();
24407    };
24408
24409    //$scope.questions = getQuestions();
24410
24411    $scope.currentQuestionIndex = 0;
24412
24413    $scope.nextQuestion = function () {
24414      $scope.currentQuestionIndex++;
24415
24416      if ($scope.currentQuestionIndex >= $scope.questions.length) {
24417        $scope.currentQuestionIndex = 0;
24418      }
24419    };
24420
24421    $scope.currentCulture = requestedLanguageService.getCurrentLanguage();
24422
24423    $scope.$on("language-change", function (event, key) {
24424      $scope.currentCulture = key;
24425    });
24426
24427    //function getQuestions() {
24428    //this is for testing
24429    //    $scope.questions = [];
24430
24431    //    return [
24432    //        {
24433    //            SubscriberNumber: 0,
24434    //            UsersName: null,
24435    //            QuestionId: 1,
24436    //            Question: $filter('translate')('reset_pwd_question_1'),
24437    //            Code: 'sttkmnvl',
24438    //            Answer: null
24439    //        },
24440    //        {
24441    //            SubscriberNumber: 0,
24442    //            UsersName: null,
24443    //            QuestionId: 2,
24444    //            Question: $filter('translate')('reset_pwd_question_2'),
24445    //            Code: 'sttkmnvl',
24446    //            Answer: null
24447    //        },
24448    //        {
24449    //            SubscriberNumber: 0,
24450    //            UsersName: null,
24451    //            QuestionId: 3,
24452    //            Question: $filter('translate')('reset_pwd_question_3'),
24453    //            Code: 'sttkmnvl',
24454    //            Answer: null
24455    //        },
24456    //        {
24457    //            SubscriberNumber: 0,
24458    //            UsersName: null,
24459    //            QuestionId: 31,
24460    //            Question: $filter('translate')('reset_pwd_question_4'),
24461    //            Code: 'sttkmnvl',
24462    //            Answer: null
24463    //        },
24464    //        {
24465    //            SubscriberNumber: 0,
24466    //            UsersName: null,
24467    //            QuestionId: 24,
24468    //            Question: $filter('translate')('reset_pwd_question_5'),
24469    //            Code: 'sttkmnvl',
24470    //            Answer: null
24471    //        },
24472    //        {
24473    //            SubscriberNumber: 0,
24474    //            UsersName: null,
24475    //            QuestionId: 31,
24476    //            Question: $filter('translate')('reset_pwd_question_6'),
24477    //            Code: 'sttkmnvl',
24478    //            Answer: null
24479    //        },
24480    //        {
24481    //            SubscriberNumber: 0,
24482    //            UsersName: null,
24483    //            QuestionId: 17,
24484    //            Question: $filter('translate')('reset_pwd_question_7'),
24485    //            Code: 'sttkmnvl',
24486    //            Answer: null
24487    //        }];
24488    //}
24489
24490    $scope.showHidePassword = function (elementId) {
24491      const pwdField = $("#" + elementId);
24492
24493      pwdField.attr("type") === "password"
24494        ? pwdField.prop("type", "text")
24495        : pwdField.prop("type", "password");
24496
24497      $scope[elementId + "Show"] = !$scope[elementId + "Show"]; // Control the eye icon and its title in the template
24498    };
24499
24500    if (!isMobile.any()) {
24501      $("#memberDob").mask("99/99/9999");
24502    }
24503
24504    $scope.addSlashesToDate = function (e) {
24505      const dob = $("#memberDob");
24506      var dateValue = dob.val();
24507      const dateLength = dateValue.length;
24508
24509      if (isMobile.any()) {
24510        if (e.keyCode === 8) {
24511          //backspace
24512          dateValue = dateValue.substring(0, dateValue.length);
24513          dob.val(dateValue);
24514        } else {
24515          switch (dateLength) {
24516            case 2:
24517              dob.val(dateValue + "/");
24518              break;
24519            case 5:
24520              dob.val(dateValue + "/");
24521              break;
24522          }
24523        }
24524      }
24525
24526      $scope.dateOfBirthIsValid = false;
24527
24528      if (dateValue.substring(6, 10).indexOf("_") === -1) {
24529        $scope.dateOfBirthIsValid = authService.validateDate(dateValue);
24530      }
24531    };
24532
24533    $scope.checkPasswordStrength = function (e) {
24534      if (e.isShiftKey) return;
24535
24536      const newPassword = $("#newPassword").val();
24537
24538      $scope.$broadcast("password-changed", newPassword);
24539    };
24540
24541    $scope.checkPasswordMatch = function () {
24542      const newPass1 = $("#newPassword").val();
24543      const newPass2 = $("#matchPassword").val();
24544
24545      if (newPass1 === newPass2 && newPass1.trim() !== "") {
24546        $("#passwordMatchIcon")
24547          .removeClass("colorRed")
24548          .addClass("colorGreen")
24549          .fadeIn("slow");
24550
24551        if ($("#currentPass").val() !== "")
24552          $scope.formSubmitButtonDisabled = false;
24553      } else {
24554        $("#passwordMatchIcon").addClass("colorRed").removeClass("colorGreen");
24555
24556        $scope.formSubmitButtonDisabled = true;
24557      }
24558    };
24559
24560    //Step 1
24561    $scope.checkMemberDetails = function () {
24562      $scope.loadingPRStep1 = true;
24563      $scope.MemberId = $scope.MemberId.substring(0, 14);
24564
24565      $("#cancelPRStep1").fadeOut(100);
24566      $("#submitPRStep1").fadeOut(100);
24567
24568      if(!$scope.captchaResponse) {
24569        $dialogs.error(
24570          $filter("translate")("form_memberDetails"),
24571          '<center class="colorRed">Invalid Captcha</center>'
24572        );
24573        $scope.loadingPRStep1 = false;
24574        return;
24575      }
24576
24577      const param = {
24578        Email: $scope.email,
24579        SubscriberNumber: $scope.MemberId,
24580        DateOfBirth: $scope.MemberDob,
24581        recaptcha: $scope.captchaResponse
24582      };
24583
24584      //debugging
24585      //$scope.loadingPRStep1 = false;
24586      //$scope.step++;
24587
24588      authService
24589        .InitiatePasswordReset(param)
24590        .success(function () {
24591          $scope.loadingPRStep1 = false;
24592          $scope.step++;
24593        })
24594        .error(function (data) {
24595          $("#submitPRStep1").fadeIn(3000);
24596          $("#cancelPRStep1").fadeIn(3000);
24597          $scope.loadingPRStep1 = false;
24598          if (data.Message === "Request failed:") {
24599            $dialogs.error(
24600              $filter("translate")("form_memberDetails"),
24601              '<center class="colorRed">' +
24602                $filter("translate")(
24603                  "page_resetPass_incorrectSubmittedInformation"
24604                ) +
24605                "</center>"
24606            );
24607          } else {
24608            $dialogs.error(
24609              $filter("translate")("form_memberDetails"),
24610              '<center class="colorRed">' +
24611                $filter("translate")(
24612                  "page_resetPass_incorrectSubmittedInformation"
24613                ) +
24614                "</center>"
24615            );
24616          }
24617        });
24618    };
24619
24620    //Step 2
24621    $scope.VerifyEmailCode = function () {
24622      $scope.loadingPRStep2 = true;
24623
24624      $("#cancelPRStep2").fadeOut(100);
24625      $("#submitPRStep2").fadeOut(100);
24626
24627      //debugging
24628      //$scope.loadingPRStep2 = false;
24629      //$scope.currentQuestion = $scope.questions[0];
24630      //$scope.step++;
24631
24632      const param = {
24633        SubscriberNumber:
24634          $scope.MemberId.length === 12
24635            ? $scope.MemberId + "00"
24636            : $scope.MemberId,
24637        Code: $scope.emailCode,
24638      };
24639
24640      authService
24641        .VerifyEmailCode(param, $scope.currentCulture)
24642        .success(function (data) {
24643          $scope.loadingPRStep2 = false;
24644          if (data.Status === "Success") {
24645            $scope.questions = data.Value;
24646            $scope.currentQuestion = data.Value[0];
24647            $scope.step++;
24648          } else {
24649            $("#submitPRStep2").fadeIn("slow");
24650            $("#cancelPRStep2").fadeIn("slow");
24651
24652            $dialogs.error(
24653              $filter("translate")("form_emailVerification"),
24654              '<center class="classOrange">' +
24655                $filter("translate")("page_resetPass_incorrectCode") +
24656                "</center>"
24657            );
24658          }
24659        })
24660        .error(function () {
24661          $scope.loadingPRStep2 = false;
24662
24663          $dialogs.error(
24664            $filter("translate")("form_emailVerification"),
24665            '<center class="colorRed">' +
24666              $filter("translate")("page_resetPass_incorrectCode") +
24667              "</center>"
24668          );
24669
24670          $("#submitPRStep2").fadeIn("slow");
24671          $("#cancelPRStep2").fadeIn("slow");
24672        });
24673    };
24674
24675    //Step 3
24676    $scope.SubmitSecurityQuestionAnswer = function (isValid) {
24677      $scope.loadingPRStep3 = true;
24678
24679      $("#cancelPRStep3").fadeOut(100);
24680      $("#submitPRStep3").fadeOut(100);
24681
24682      if (!isValid) {
24683        $("#submitPRStep3").fadeIn("slow");
24684      }
24685
24686      const param = {
24687        SubscriberNumber:
24688          $scope.MemberId.length === 12
24689            ? $scope.MemberId + "00"
24690            : $scope.MemberId,
24691        UsersName:
24692          $scope.MemberId.length === 12
24693            ? $scope.MemberId + "00"
24694            : $scope.MemberId,
24695        Code: $scope.questions[$scope.currentQuestionIndex].Code,
24696        QuestionId: $scope.questions[$scope.currentQuestionIndex].QuestionId,
24697        Answer: $scope.securityAnswer,
24698      };
24699
24700      //debugging
24701      //$scope.loadingPRStep3 = false;
24702      //$scope.Code = "test";
24703      //$scope.step++;
24704
24705      authService
24706        .SubmitSecurityQuestionAnswer(param)
24707        .success(function (data) {
24708          $scope.loadingPRStep3 = false;
24709          data.Value = data.Value.replaceAll('"', "");
24710          $scope.Code = data.Value;
24711          $scope.step++;
24712        })
24713        .error(function (data) {
24714          $scope.loadingPRStep3 = false;
24715          $("#submitPRStep3").fadeIn("slow");
24716          $("#cancelPRStep3").fadeIn("slow");
24717
24718          if (data.Message === "Members question & answer failed") {
24719            $dialogs.error(
24720              $filter("translate")("form_securityQuestion"),
24721              '<center class="classOrange">' +
24722                $filter("translate")("page_resetPass_wrongAnswer") +
24723                "</center>"
24724            );
24725          } else {
24726            $dialogs.error(
24727              $filter("translate")("form_securityQuestion"),
24728              '<center class="colorRed">' +
24729                $filter("translate")("form_didNotReceiveYourEntry") +
24730                "</center>"
24731            );
24732          }
24733        });
24734    };
24735
24736    //Step 4
24737    $scope.SubmitNewPassword = function () {
24738      $scope.loadingPRStep4 = true;
24739
24740      $("#cancelPRStep4").fadeOut(100);
24741      $("#submitPRStep4").fadeOut(100);
24742
24743      if ($scope.password !== $scope.newPasswordConfirmation) {
24744        alert($filter("translate")("form_passwordsDoNotMatch"));
24745        $("#cancelPRStep4").fadeIn(100);
24746        $("#submitPRStep4").fadeIn(100);
24747        $scope.loadingPRStep4 = false;
24748        return;
24749      }
24750
24751      const paramModel = {
24752        UserName: $scope.MemberId,
24753        OldPassword: $scope.Code,
24754        NewPassword: $scope.password,
24755        ConfirmPassword: $scope.newPasswordConfirmation,
24756        name: "WebMemberPortal",
24757      };
24758
24759      //debug
24760      //$scope.loadingPRStep4 = false;
24761      //$('#submitPRStep4').fadeIn('slow');
24762      //$('#cancelPRStep4').fadeIn('slow');
24763
24764      //$dialogs.notify($filter("translate")("form_newPassword"), '<center>' + $filter('translate')('page_resetPass_successfully') + '</center>');
24765
24766      //$scope.lastStep();
24767
24768      //console.log(paramModel);
24769      authService
24770        .SubmitNewPassword(paramModel)
24771        .success(function () {
24772          $scope.loadingPRStep4 = false;
24773          $("#submitPRStep4").fadeIn("slow");
24774          $("#cancelPRStep4").fadeIn("slow");
24775
24776          $dialogs.notify(
24777            $filter("translate")("form_newPassword"),
24778            '<center class="classGreen">' +
24779              $filter("translate")("page_resetPass_successfully") +
24780              "</center>"
24781          );
24782
24783          $scope.lastStep();
24784        })
24785        .error(function () {
24786          $scope.loadingPRStep4 = false;
24787          $("#submitPRStep4").fadeIn("slow");
24788          $("#cancelPRStep4").fadeIn("slow");
24789
24790          $dialogs.error(
24791            $filter("translate")("form_securityQuestion"),
24792            '<center class="colorRed">' +
24793              $filter("translate")("form_didNotReceiveYourEntry") +
24794              "</center>"
24795          );
24796        });
24797    };
24798
24799    //Help Modal
24800    $scope.showHelpModal = function (type) {
24801      switch (type) {
24802        case "IEHPMemID":
24803          $dialogs.notify(
24804            $filter("translate")(
24805              "form_help_modal_title_whereToFindYourMemberID"
24806            ),
24807            '<center class="overflowAuto"><img src="Content/Images/IEHPMemCard_memIDv2.png"></center>'
24808          );
24809          break;
24810
24811        case "PCPPinCode":
24812          $dialogs.notify(
24813            $filter("translate")(
24814              "form_help_modal_title_whereToFindYourPCPPinCode"
24815            ),
24816            '<center class="overflowAuto"><img src="Content/Images/IEHPMemCard_pcpCode.png"></center>'
24817          );
24818          break;
24819      }
24820    };
24821
24822    $scope.nextStep = function () {
24823      $scope.step++;
24824    };
24825
24826    $scope.lastStep = function () {
24827      $location.path("App/Account/Login").replace();
24828    };
24829
24830    $scope.opened = false;
24831    $scope.maxDate = new Date();
24832    $scope.open = function ($event) {
24833      $event.preventDefault();
24834      $event.stopPropagation();
24835
24836      $scope.isOpened = true;
24837    };
24838
24839    $scope.dateOptions = {
24840      formatYear: "yyyy",
24841      startingDay: 1,
24842      showWeeks: false,
24843    };
24844  }
24845})();
24846
24847(function () {
24848    'use strict';
24849
24850    const app = angular.module('MemberPortalDirectives');
24851
24852    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
24853    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
24854
24855    app.directive('passwordStrength',
24856        [
24857            function () {
24858                return {
24859                    scope: {},
24860                    restrict: 'EA',
24861                    templateUrl: urlBase +
24862                        '/App/directives/account/password-strength/passwordStrength.html' +
24863                        cacheBust,
24864                    controller: passwordStrengthCtrl,
24865                    controllerAs: 'pwdCtrl'
24866                };
24867            }
24868        ]);
24869
24870    app.controller('passwordStrengthCtrl', passwordStrengthCtrl);
24871
24872    passwordStrengthCtrl.$inject = ['$scope', '$filter', 'passwordStrength'];
24873
24874    function passwordStrengthCtrl($scope, $filter, passwordStrength) {
24875        const vm = this;
24876        vm.$scope = $scope;
24877
24878        vm.hasRequired = hasRequired;
24879
24880        vm.passwordRequired = [];
24881
24882        function hasRequired(required) {
24883            return vm.passwordRequired.includes(required);
24884        }
24885
24886        $scope.$on('language-change', function (event, key) {
24887
24888            switch (vm.passwordRequired.length) {
24889                case 1:
24890                    $('#passwordStrengthString').html($filter('translate')('form_passwordStrengthWeak'));
24891                    break;
24892                case 2:
24893                    $('#passwordStrengthString').html($filter('translate')('form_passwordStrengthWeak'));
24894                    break;
24895                case 3:
24896                    $('#passwordStrengthString').html($filter('translate')('form_passwordStrengthMedium'));
24897                    break;
24898                case 4:
24899                    $('#passwordStrengthString').html($filter('translate')('form_passwordStrengthStrong'));
24900                    break;
24901                case 5:
24902                    $('#passwordStrengthIcon').removeClass('colorRed').addClass('colorGreen');
24903                    $('#passwordStrengthString').html($filter('translate')('form_passwordStrengthVeryStrong'));
24904                    break;
24905                default:
24906                    $('#passwordStrengthIcon').removeClass('colorGreen').addClass('colorRed');
24907            }
24908        });
24909
24910        $scope.$on('password-changed', function (event, data) {
24911            
24912            console.log("catch the password change");
24913
24914            const minLength = 6;
24915
24916            vm.passwordRequired = passwordStrength.CheckPasswordStrength(data, minLength);
24917
24918            $('#passwordStrengthString').html('').fadeIn('slow');
24919
24920            const classStrength = 'passwordStrength_' + vm.passwordRequired.length +
24921                ' input-group col-xs-12';
24922            $('#passwordStrengthBar').removeClass().addClass(classStrength);
24923
24924            switch (vm.passwordRequired.length) {
24925                case 1:
24926                    $('#passwordStrengthString').html($filter('translate')('form_passwordStrengthWeak'));
24927                    break;
24928                case 2:
24929                    $('#passwordStrengthString').html($filter('translate')('form_passwordStrengthWeak'));
24930                    break;
24931                case 3:
24932                    $('#passwordStrengthString').html($filter('translate')('form_passwordStrengthMedium'));
24933                    break;
24934                case 4:
24935                    $('#passwordStrengthString').html($filter('translate')('form_passwordStrengthStrong'));
24936                    break;
24937                case 5:
24938                    $('#passwordStrengthIcon').removeClass('colorRed').addClass('colorGreen');
24939                    $('#passwordStrengthString').html($filter('translate')('form_passwordStrengthVeryStrong'));
24940                    break;
24941                default:
24942                    $('#passwordStrengthIcon').removeClass('colorGreen').addClass('colorRed');
24943            }
24944        });
24945    };
24946})();
24947/**
24948 * Recaptcha Directive
24949 * Uses recaptcha v2
24950 */
24951(function () {
24952    'use strict';
24953    const siteKey = $('meta[name="recaptchaKey"]').attr('content');
24954
24955    // Register directive with Angular
24956    angular
24957        .module('MemberPortalDirectives')
24958        .directive('recaptcha', recaptchaDirective)
24959
24960    function recaptchaDirective($interval) {
24961        return {
24962            restrict: 'E',  // Element or Attribute
24963            scope: {
24964                onVerify: '&',
24965                onExpired: '&?'
24966            },
24967            template: '<div class="recaptcha-container"></div>',
24968            link: function(scope, element) {
24969                let widgetId = null;
24970
24971                function renderCaptcha() {
24972                    if(window.grecaptcha && window.grecaptcha.render) {
24973                        if(widgetId !== null) return;
24974
24975                        let container = element[0].querySelector('.recaptcha-container');
24976                        container.innerHTML = '';
24977
24978                        widgetId = grecaptcha.render(container, {
24979                            sitekey: siteKey,
24980                            callback: function(response) {
24981                                scope.$apply(function() {
24982                                    scope.onVerify({ response: response })
24983                                })
24984                            },
24985                            'expired-callback': function() {
24986                                scope.$apply(function() {
24987                                    if(scope.onExpired) {
24988                                        scope.onExpired();
24989                                    }
24990                                })
24991                            }
24992                        })
24993                    }
24994                }
24995
24996                let check = $interval(function() {
24997                    if(window.grecaptcha && window.grecaptcha.render) {
24998                        renderCaptcha();
24999                        $interval.cancel(check)
25000                    }
25001                })
25002
25003                scope.$parent.$on('recaptcha-reset', function() {
25004                    if(widgetId !== null) {
25005                        grecaptcha.reset(widgetId);
25006                    }
25007                })
25008            }
25009        };
25010    }
25011})();
25012(function () {
25013  "use strict";
25014
25015  const app = angular.module("MemberPortalDirectives");
25016
25017  var urlBase = $('meta[name="ApplicationRoot"]').attr("content");
25018  var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr("content")}`;
25019
25020  app.directive("registrationForm", [
25021    function () {
25022      return {
25023        scope: {},
25024        restrict: "EA",
25025        templateUrl:
25026          urlBase +
25027          "/App/directives/account/registration-form/registrationForm.html" +
25028          cacheBust,
25029        controller: registrationFormCtrl,
25030        controllerAs: "ctrl",
25031      };
25032    },
25033  ]);
25034
25035  //app.filter('excludeFrom', [function () {
25036  //    return function (inputArray, filterCriteria) {
25037  //        return inputArray.filter(function (item) {
25038  //            // if the value of filterCriteria is "falsy", retain the inputArray as it is
25039  //            // then check if the currently checked item in the inputArray is different from the filterCriteria,
25040  //            // if so, keep it in the filtered results
25041  //            return !filterCriteria || !angular.equals(item, filterCriteria);
25042  //        });
25043  //    };
25044  //}]);
25045
25046  app.controller("registrationFormCtrl", registrationFormCtrl);
25047
25048  registrationFormCtrl.$inject = [
25049    "$scope",
25050    "$filter",
25051    "authService",
25052    "$location",
25053    "dialogs",
25054    "requestedLanguageService",
25055  ];
25056
25057  function registrationFormCtrl(
25058    $scope,
25059    $filter,
25060    authService,
25061    $location,
25062    $dialogs,
25063    requestedLanguageService
25064  ) {
25065    $scope.SubscriberNumber = "";
25066    $scope.errorMessage = "";
25067    $scope.infoMessage = "";
25068    $scope.step = 1;
25069    $scope.currentStep = 1;
25070    $scope.isAuthorizedPerson = false;
25071    $scope.isOpenAccessMember = false;
25072    $scope.DateOfBirth = "";
25073    $scope.dateOfBirthIsValid = false;
25074    $scope.loadingRegStep1 = false;
25075    $scope.loadingRegStep2 = false;
25076    $scope.loadingRegStep3 = false;
25077    $scope.loadingRegStep4 = false;
25078    $scope.hasEmailCode = false;
25079    $scope.step2FormSubmitButtonDisabled = true;
25080    $scope.showLastFour = false;
25081    $scope.showDoctorPin = false;
25082    $scope.txtPassword1Show = false;
25083    $scope.txtPassword2Show = false;
25084    $scope.captchaResponse = null;
25085    
25086    $scope.setCaptchaResponse = function(response) {
25087      $scope.captchaResponse = response;
25088    }
25089
25090    $scope.showNextStep = function () {
25091      $scope.currentStep = ++$scope.currentStep;
25092    };
25093
25094    $scope.validateTypedCode = function (event) {
25095      const confirmCode = event.target.value;
25096      $scope.hasEmailCode = confirmCode && confirmCode.length > 0;
25097    };
25098
25099    $scope.validatePastedCode = function (event) {
25100      var confirmCode;
25101
25102      if (typeof event.originalEvent.clipboardData !== "undefined") {
25103        confirmCode = event.originalEvent.clipboardData.getData("text/plain");
25104      } else {
25105        // To support browsers without clipboard API (IE and older browsers)
25106        $timeout(function () {
25107          confirmCode = angular.element(e.currentTarget).val().length > 0;
25108        });
25109      }
25110
25111      $scope.hasEmailCode = confirmCode.length > 0;
25112    };
25113
25114    $scope.clearScreen = function (step) {
25115      switch (step) {
25116        case 1:
25117          $("#subscriberNo").val("").focus();
25118          $("#regDob").val("").mask("99/99/9999");
25119
25120          $scope.DateOfBirth = "";
25121          $scope.SubscriberNumber = "";
25122          $scope.isAuthorizedPerson = false;
25123
25124          $scope.dateOfBirthIsValid = false;
25125          $scope.stepOne.subscriberNo.$valid = false;
25126
25127          break;
25128        case 2:
25129          $("#stepTwo")[0].reset();
25130
25131          // Trigger the password strength directive.
25132          const newPassword = $("#txtPassword1").val();
25133          $scope.Password = newPassword;
25134          $scope.$broadcast("password-changed", newPassword);
25135
25136          $scope.stepTwo.$invalid = true;
25137          $scope.stepTwo.$valid = false;
25138
25139          // $scope.stepTwo.txtLastFour.$valid = false;
25140          // if ($scope.stepTwo.step2PinCode) {
25141          //   $scope.stepTwo.step2PinCode.$valid = false;
25142          // }
25143
25144          $scope.stepTwo.txtPassword1.$valid = false;
25145          $scope.stepTwo.step2Email.$valid = false;
25146          $scope.stepTwo.txtPassword2.$valid = false;
25147          // $("#txtLastFour").focus();
25148          break;
25149        case 3:
25150          $("#emailCode").val("");
25151          $scope.hasEmailCode = false;
25152          $scope.stepThree.emailCode.$valid = false;
25153          $scope.stepThree.$invalid = true;
25154          break;
25155        case 4:
25156          $("#stepFour")[0].reset();
25157          $scope.stepFour.answerOne.$valid = false;
25158          $scope.stepFour.answerTwo.$valid = false;
25159          $scope.stepFour.answerThree.$valid = false;
25160          $scope.stepFour.$invalid = true;
25161          $("#answerOne").focus();
25162          break;
25163      }
25164    };
25165
25166    if (!isMobile.any()) {
25167      $("#regDob").mask("99/99/9999");
25168    }
25169
25170    $scope.showHidePassword = function (elementId) {
25171      const pwdField = $("#" + elementId);
25172
25173      pwdField.attr("type") === "password"
25174        ? pwdField.prop("type", "text")
25175        : pwdField.prop("type", "password");
25176
25177      $scope[elementId + "Show"] = !$scope[elementId + "Show"]; // Control the eye icon and its title in the template
25178    };
25179
25180    $scope.addSlashesToDate = function (e) {
25181      const dob = $("#regDob");
25182      var dateValue = dob.val();
25183      const dateLength = dateValue.length;
25184
25185      if (isMobile.any()) {
25186        if (e.keyCode === 8) {
25187          //backspace
25188          dateValue = dateValue.substring(0, dateValue.length);
25189          dob.val(dateValue);
25190        } else {
25191          switch (dateLength) {
25192            case 2:
25193              dob.val(dateValue + "/");
25194              break;
25195            case 5:
25196              dob.val(dateValue + "/");
25197              break;
25198          }
25199        }
25200      }
25201
25202      $scope.dateOfBirthIsValid = false;
25203      if (dateValue.substring(6, 10).indexOf("_") === -1) {
25204        $scope.dateOfBirthIsValid = authService.validateDate(dateValue);
25205      }
25206    };
25207
25208    $scope.checkPasswordStrength = function (e) {
25209      if (e.isShiftKey) return;
25210
25211      const newPassword = $("#txtPassword1").val();
25212
25213      $scope.$broadcast("password-changed", newPassword);
25214    };
25215
25216    $scope.currentCulture = requestedLanguageService.getCurrentLanguage();
25217
25218    var _extractQuestions = function () {
25219      const questions = $scope.AllSecurityQuestions;
25220      const allSecurityQuestionsLength = questions.length;
25221
25222      if (allSecurityQuestionsLength === 0) {
25223        return;
25224      }
25225
25226      $scope.AllQuestionsForCulture = [];
25227
25228      for (let i = 0; i < allSecurityQuestionsLength; i++) {
25229        const question = {
25230          id: questions[i].QuestionId,
25231          question: questions[i].Question,
25232        };
25233
25234        $scope.AllQuestionsForCulture.push(question);
25235      }
25236    };
25237
25238    $scope.$on("language-change", function (event, key) {
25239      $scope.currentCulture = key;
25240
25241      const param = {
25242        SubscriberNumber: $scope.SubscriberNumber,
25243        Code: $scope.EmailCode,
25244        name: "WebMemberPortal",
25245      };
25246      //Swap All Questions List
25247      authService
25248        .RegistrationStepThree(param, $scope.currentCulture)
25249        .success(function (data) {
25250          //retrieve security questions
25251          if (data.Status === "Success") {
25252            $scope.AllSecurityQuestions = data.Value; //Questions
25253            _extractQuestions();
25254          } else {
25255            $dialogs.error(
25256              $filter("translate")("form_emailCode"),
25257              '<center class="classOrange">' +
25258                $filter("translate")("form_help_modal_incorrectCode") +
25259                "</center>"
25260            );
25261          }
25262        });
25263    });
25264
25265    /**********************************************************/
25266    /**********************************************************/
25267
25268    $scope.AllQuestionsForCulture = [];
25269    $scope.AllSecurityQuestions = [];
25270
25271    $scope.StepOne = function () {
25272      $scope.loadingRegStep1 = true;
25273
25274      $("#cancelRegStep1").fadeOut(100);
25275      $("#submitRegStep1").fadeOut(100);
25276      $scope.SubscriberNumber = $scope.SubscriberNumber.substring(0, 14);
25277
25278      if(!$scope.captchaResponse) {
25279        $dialogs.error(
25280          $filter("translate")("form_memberDetails"),
25281          '<center class="colorRed">Invalid Captcha</center>'
25282        );
25283        $scope.loadingRegStep1 = false;
25284        return;
25285      }
25286      
25287      const param = {
25288        SubscriberNumber: $scope.SubscriberNumber,
25289        DateOfBirth: $("#regDob").val(),
25290        recaptcha: $scope.captchaResponse
25291      };
25292
25293      authService
25294        .RegistrationStepOne(param)
25295        .success(function (data) {
25296          $scope.step++;
25297          $scope.data = data.Value;
25298          $scope.errorMessage = "";
25299          $scope.showLastFour = true;
25300
25301          // BUSINESS RULE: HIDE PCP PIN CODE FOR CMC & MEDI-MEDI MEMBERS & CCA
25302          // $scope.showDoctorPin = true;
25303          // if (data.Value.IsMcr || data.Value.IsMediMedi || data.Value.IsCca) {
25304          //   $scope.showDoctorPin = false;
25305          // }
25306
25307          if (data.Value !== "openaccess") {
25308            $scope.showNextStep();
25309          }
25310        })
25311        .error(function (data) {
25312          $("#cancelRegStep1").fadeIn(3000);
25313          $("#submitRegStep1").fadeIn(3000);
25314          $scope.loadingRegStep1 = false;
25315
25316          if (data.Message === "confirmed") {
25317            $dialogs.error(
25318              $filter("translate")("form_memberDetails"),
25319              '<center class="classOrange">' +
25320                $filter("translate")("page_reg_alreadyRegisteredSignIn") +
25321                "</center>"
25322            );
25323          } else if (data.Message === "failed") {
25324            $dialogs.error(
25325              $filter("translate")("form_memberDetails"),
25326              '<center class="colorRed">' +
25327                $filter("translate")("form_theInformationYouSubmittedIsIncorrect") +
25328                "</center>"
25329            );
25330          } else {
25331            if (data.Message === "openaccess" || data.Value === "openaccess") {
25332              $dialogs.error(
25333                $filter("translate")("form_memberDetails"),
25334                '<center class="classOrange">' +
25335                  $filter("translate")("form_registrationOpenAccessNotice") +
25336                  "</center>"
25337              );
25338              $scope.isOpenAccessMember = true;
25339            } else
25340              $dialogs.error(
25341                $filter("translate")("form_memberDetails"),
25342                '<center class="classOrange">' +
25343                  $filter("translate")("form_registrationFailed") +
25344                  "</center>"
25345              );
25346          }
25347
25348          $("#submitRegStep1").fadeIn("slow");
25349        });
25350    };
25351
25352    $scope.checkPasswordMatch = function () {
25353      if ($scope.passwordConfirm) {
25354        if ($scope.Password !== $scope.passwordConfirm) {
25355          $scope.stepTwo.txtPassword2.$setValidity("passwordMatch", false);
25356        } else {
25357          $scope.stepTwo.txtPassword2.$setValidity("passwordMatch", true);
25358        }
25359        $("#txtPassword2").blur();
25360      }
25361    };
25362
25363    $scope.StepTwo = function () {
25364      $scope.loadingRegStep2 = true;
25365
25366      $("#cancelRegStep2").fadeOut(100);
25367      $("#submitRegStep2").fadeOut(100);
25368
25369      const param = {
25370        SubscriberNumber: $scope.SubscriberNumber.substring(0, 12),
25371        DateOfBirth: $("#regDob").val(),
25372        // PcpPinCode: $("#step2PinCode").val(), // BUSINESS RULE: MATCH CCA REGISTRATION
25373        Email: $scope.Email,
25374        EmailMemberConformation: $scope.Email,
25375        Password: $scope.Password,
25376        ConfirmPassword: $scope.Password,
25377        UserName: $scope.SubscriberNumber,
25378        AcceptedTerms: $scope.isAuthorizedPerson,
25379        ResourceName: "WebMemberPortal",
25380      };
25381
25382      // if ($scope.data.SsnOrPhone === "phone") {
25383      //   param.PhoneNumber = $scope.LastFour;
25384      // } else {
25385      //   param.SocialSecurityNumber = $scope.LastFour;
25386      // }
25387      
25388      authService
25389        .RegistrationStepTwo(param)
25390        .success(function (data) {
25391          $scope.data = data;
25392          $scope.step++;
25393          $scope.showNextStep();
25394        })
25395        .error(function () {
25396          $("#submitRegStep2").fadeIn(3000);
25397          $("#cancelRegStep2").fadeIn(3000);
25398          $scope.loadingRegStep2 = false;
25399          $dialogs.error(
25400            $filter("translate")("form_registrationIncorrecInfoTitle"),
25401            `<center class="colorRed">${$filter("translate")(
25402              "form_theInformationYouSubmittedIsIncorrect"
25403            )}</center>`
25404          );
25405        });
25406    };
25407
25408    $scope.StepThree = function () {
25409      $scope.loadingRegStep3 = true;
25410
25411      $("#cancelRegStep3").fadeOut(100);
25412      $("#submitRegStep3").fadeOut(100);
25413
25414      const param = {
25415        SubscriberNumber: $scope.SubscriberNumber,
25416        Code: $scope.EmailCode,
25417        name: "WebMemberPortal",
25418      };
25419
25420      authService
25421        .RegistrationStepThree(param, $scope.currentCulture)
25422        .success(function (data) {
25423          if (data.Status === "Success") {
25424            $scope.step++;
25425            $scope.AllSecurityQuestions = data.Value; //Questions
25426            _extractQuestions();
25427            $scope.showNextStep();
25428          } else {
25429            $dialogs.error(
25430              $filter("translate")("form_emailCode"),
25431              '<center class="classOrange">' +
25432                $filter("translate")("form_help_modal_incorrectCode") +
25433                "</center>"
25434            );
25435          }
25436        })
25437        .error(function () {
25438          $("#submitRegStep3").fadeIn(3000);
25439          $("#cancelRegStep3").fadeIn(3000);
25440          $scope.loadingPRStep3 = false;
25441
25442          $dialogs.error(
25443            $filter("translate")("form_emailCode"),
25444            '<center class="colorRed">' +
25445              $filter("translate")("form_help_modal_incorrectCode") +
25446              "</center>"
25447          );
25448          $("#submitRegStep3").fadeIn("slow");
25449        });
25450    };
25451
25452    $scope.StepFour = function () {
25453      $scope.loadingRegStep4 = true;
25454
25455      $("#cancelRegStep4").fadeOut(100);
25456      $("#submitRegStep4").fadeOut(100);
25457
25458      const code = $scope.AllSecurityQuestions[0].Code;
25459
25460      const param = [
25461        {
25462          SubscriberNumber: $scope.SubscriberNumber,
25463          QuestionId: $scope.questionOne.id,
25464          Answer: $scope.AnswerOne,
25465          Code: code,
25466          UsersName: $scope.SubscriberNumber,
25467        },
25468        {
25469          SubscriberNumber: $scope.SubscriberNumber,
25470          QuestionId: $scope.questionTwo.id,
25471          Answer: $scope.AnswerTwo,
25472          Code: code,
25473          UsersName: $scope.SubscriberNumber,
25474        },
25475        {
25476          SubscriberNumber: $scope.SubscriberNumber,
25477          QuestionId: $scope.questionThree.id,
25478          Answer: $scope.AnswerThree,
25479          Code: code,
25480          UsersName: $scope.SubscriberNumber,
25481        },
25482      ];
25483
25484      authService
25485        .RegistrationStepFour(param)
25486        .success(function (data) {
25487          if (data.Status === "Success") {
25488            $scope.successMessage =
25489              '<center class="classGreen">' +
25490              $filter("translate")("page_reg_registeredSuccessfully") +
25491              "</center>";
25492
25493            $dialogs.notify(
25494              $filter("translate")("form_registration"),
25495              '<center class="classGreen">' +
25496                $filter("translate")("page_reg_registeredSuccessfully") +
25497                "</center>"
25498            );
25499            $location.path("/App/Account/Login");
25500          } else {
25501            $dialogs.error(
25502              $filter("translate")("form_registration"),
25503              '<center class="classOrange">' + data.Description + "</center>"
25504            );
25505          }
25506          /*
25507           * data == "success" , everything is good.
25508           * data == "confirmed", means that there is already a confirmed account for that subno
25509           * data == "attempts", bad attempt, at some point you will have to start over
25510           * 400 == bad request, a value didn't match, or some other validation error occurred.
25511           */
25512        })
25513        .error(function () {
25514          $("#submitRegStep4").fadeIn(3000);
25515          $("#cancelRegStep4").fadeIn(3000);
25516          $scope.loadingPRStep3 = false;
25517
25518          $dialogs.error(
25519            $filter("translate")("form_emailCode"),
25520            '<center class="colorRed">' +
25521              $filter("translate")("form_didNotReceiveYourEntry") +
25522              "</center>"
25523          );
25524        });
25525    };
25526
25527    //Help Modal
25528    $scope.showHelpModal = function (type) {
25529      switch (type) {
25530        case "IEHPMemID":
25531          $dialogs.notify(
25532            $filter("translate")(
25533              "form_help_modal_title_whereToFindYourMemberID"
25534            ),
25535            '<center class="overflowAuto"><img src="Content/Images/IEHPMemCard_memIDv2.png"></center>'
25536          );
25537          break;
25538        case "PCPPinCode":
25539          $dialogs.notify(
25540            $filter("translate")(
25541              "form_help_modal_title_whereToFindYourPCPPinCode"
25542            ),
25543            '<center  class="overflowAuto"><img src="Content/Images/IEHPMemCard_pcpCode.png"></center>'
25544          );
25545          break;
25546      }
25547    };
25548
25549    $scope.compareTo = function () {
25550      return {
25551        require: "ngModel",
25552        scope: {
25553          otherModelValue: "=compareTo",
25554        },
25555        link: function (scope, element, attributes, ngModel) {
25556          ngModel.$validators.compareTo = function (modelValue) {
25557            return modelValue === scope.otherModelValue;
25558          };
25559
25560          scope.$watch("otherModelValue", function () {
25561            ngModel.$validate();
25562          });
25563        },
25564      };
25565    };
25566  }
25567})();
25568
25569(function () {
25570    'use strict';
25571
25572    const app = angular.module('MemberPortalDirectives');
25573
25574    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
25575    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
25576
25577    app.directive('submitNewPasswordForm',
25578        [
25579            function() {
25580                return {
25581                    restrict: 'EA',
25582                    templateUrl: urlBase +
25583                        '/App/directives/account/submit-new-password-form/submitNewPasswordForm.html' +
25584                        cacheBust,
25585                    controller: submitNewPasswordFormCtrl,
25586                    controllerAs: 'ctrl'
25587                };
25588            }
25589        ]);
25590
25591    app.controller('submitNewPasswordFormCtrl', submitNewPasswordFormCtrl);
25592
25593    submitNewPasswordFormCtrl.$inject = ['$scope', 'authService', '$filter'];
25594
25595    function submitNewPasswordFormCtrl($scope, authService, $filter) {
25596
25597        $scope.loading = false;
25598        $scope.errorMessage = '';
25599        $scope.infoMessage = '';
25600        $scope.successMessage = '';
25601        $scope.formSubmitButtonDisabled = true;
25602        $scope.newPasswordConfirmation = '';
25603        $scope.newPasswordConfirmation = '';
25604
25605        $scope.closeChangePass = function () {
25606      
25607            $('#bakModal').fadeOut('fast');
25608            $('#submitNewPasswordForm').fadeOut('fast');
25609
25610            $scope.showResetForm = false;
25611            $('#submitNewPasswordForm').fadeIn('fast');
25612        };
25613
25614            //Submit New Password
25615        $scope.SubmitNewPassword = function () {
25616
25617            $scope.loading = true;
25618            $scope.errorMessage = '';
25619            $scope.infoMessage = '';
25620            $scope.successMessage = '';
25621
25622            const parameterModel = {
25623                UserName: $scope.username.substring(0,12), //12.15.2015 MemberID not UserName, so need to truncate to size 12
25624                NewPassword: $scope.newPassword,
25625                ConfirmPassword: $scope.newPasswordConfirmation,
25626                OldPassword: $scope.password,
25627                name:'WebMemberPortal'
25628            };
25629
25630            authService.SubmitNewPassword(parameterModel).success(function () {
25631                $scope.loading = false;
25632                $scope.successMessage = $filter('translate')('page_resetPass_successfully');// "Success! Your password has been changed.";
25633                $scope.showResetForm = false;
25634
25635                //Clear the form
25636                $scope.password = '';
25637                $('#bakModal').fadeOut('slow');
25638
25639                //Display Success Message
25640                //Should we inform the user that an email has been sent.
25641
25642            }).error(function() {
25643                $scope.loading = false;
25644                $scope.errorMessage = $filter('translate')('page_resetPass_problemResettingPassword');// 'There was problem with your request. We were not able to change your password';
25645                //Show error message
25646            });
25647        }; 
25648
25649        //========= PASSWORD STRENGTH
25650        $scope.checkPasswordStrength = function (e) {
25651            if (e.isShiftKey) return;
25652
25653            const newPassword = $("#newPassword").val();
25654            $rootScope.$broadcast('password-changed', newPassword);
25655        };
25656
25657        $scope.checkIfPasswordsMatch = function () {
25658
25659            $scope.formSubmitButtonDisabled = !($scope.newPassword === $scope.newPasswordConfirmation &&
25660                                                $scope.newPasswordConfirmation.length > 0);
25661
25662        };
25663    };
25664})();
25665
25666
25667
25668/**
25669 * Firebase Alert Directive
25670 * 
25671 * Displays alert messages configured in Firebase Remote Config
25672 * based on the specified remote configuration key.
25673 */
25674(function () {
25675    'use strict';
25676
25677    // Cache template path configuration
25678    const appRoot = $('meta[name="ApplicationRoot"]').attr('content');
25679    const cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
25680    const templatePath = `${appRoot}/App/directives/alert/firebase-alert.html${cacheBust}`;
25681
25682    // Register directive with Angular
25683    angular
25684        .module('MemberPortalDirectives')
25685        .directive('firebaseAlert', firebaseAlertDirective)
25686        .controller('FirebaseAlertController', FirebaseAlertController);
25687
25688    /**
25689     * Firebase Alert Directive Definition
25690     * @returns {Object} Directive Definition Object
25691     */
25692    function firebaseAlertDirective() {
25693        return {
25694            restrict: 'EA',                       // Element or Attribute
25695            scope: {
25696                remoteConfigKey: '@'              // One-way binding for config key
25697            },
25698            templateUrl: templatePath,            // External template
25699            controller: 'FirebaseAlertController', // Controller
25700            controllerAs: 'alertCtrl',            // Controller alias
25701            bindToController: true                // Bind to controller instead of scope
25702        };
25703    }
25704
25705    // Inject dependencies for FirebaseAlertController
25706    FirebaseAlertController.$inject = [
25707        '$scope',
25708        '$rootScope',
25709        'utilitiesService',
25710        'requestedLanguageService',
25711        'firebaseService'
25712    ];
25713
25714    function FirebaseAlertController($scope, $rootScope, utilitiesService, requestedLanguageService, firebaseService) {
25715        // Controller reference for use in nested functions
25716        const vm = this;
25717
25718        // Public properties
25719        vm.language = '';
25720        vm.alert = null;
25721        vm.showAlert = false;
25722
25723        // Initialize controller
25724        activate();
25725
25726        /**
25727         * Activates the controller
25728         */
25729        function activate() {
25730            initializeLanguage();
25731            setupEventListeners();
25732
25733            // Fetch alerts on directive initialization
25734            if (firebaseService.updateAlerts) {
25735                firebaseService.updateAlerts()
25736                    .catch(error => console.error('Failed to initialize alerts:', error));
25737            }
25738
25739            // Log initialization (can be removed in production)
25740            console.debug('Firebase Alert Controller initialized with key:', vm.remoteConfigKey);
25741        }
25742
25743
25744        function initializeLanguage() {
25745            vm.language = requestedLanguageService.getCurrentLanguage().toLowerCase();
25746        }
25747
25748        function setupEventListeners() {
25749            // Listen for language changes
25750            $scope.$on('language-change', handleLanguageChange);
25751
25752            // Listen for alert updates from Firebase
25753            $rootScope.$on('update', handleAlertUpdate);
25754        }
25755
25756        /**
25757         * Handles language change events
25758         */
25759        function handleLanguageChange(_event, newLanguage) {
25760            vm.language = (newLanguage || '').toLowerCase() || 'en-us';
25761
25762            // Update UI if needed after language change
25763            safeApply();
25764        }
25765
25766        /**
25767         * Handles alert update events from Firebase
25768         */
25769        function handleAlertUpdate(_event, alertsData) {
25770            // Early return if no data or not an object
25771            if (!alertsData || typeof alertsData !== 'object') {
25772                vm.showAlert = false;
25773                safeApply();
25774                return;
25775            }
25776
25777            // Get the specific alert for this directive
25778            const alertKey = vm.remoteConfigKey;
25779            const alert = alertsData.hasOwnProperty(alertKey) ? alertsData[alertKey] : null;
25780
25781            updateAlertVisibility(alert);
25782        }
25783
25784        /**
25785         * Updates the alert visibility based on status and display period
25786         */
25787        function updateAlertVisibility(alert) {
25788            // Default to not showing if alert is missing or invalid
25789            if (!alert || !alert.status) {
25790                vm.showAlert = false;
25791                safeApply();
25792                return;
25793            }
25794
25795            // Check if alert is active and within its display time window
25796            const isWithinTimeWindow = utilitiesService.isWithinDateTimeWindow(alert.displayPeriod);
25797            vm.showAlert = alert.status && isWithinTimeWindow;
25798
25799            // Update the alert data if visible
25800            vm.alert = vm.showAlert ? alert : null;
25801
25802            // Apply changes to the scope
25803            safeApply();
25804        }
25805
25806        /**
25807         * Safely applies scope changes if not already in a digest cycle
25808         */
25809        function safeApply() {
25810            if (!$scope.$$phase && !$rootScope.$$phase) {
25811                $scope.$apply();
25812            }
25813        }
25814    }
25815})();
25816(function () {
25817    const app = angular.module('MemberPortalDirectives');
25818
25819    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
25820    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
25821
25822    app.directive('authorizationSubnav', function () {
25823        return {
25824            restrict: 'EA',
25825            templateUrl: urlBase + '/App/directives/authorization-subnav/authorizationSubnav.html' + cacheBust,
25826            controller: authorizationSubnavCtrl,
25827            controllerAs: 'ctrl'
25828        };
25829    });
25830
25831    app.controller('authorizationSubnavCtrl', authorizationSubnavCtrl);
25832
25833    authorizationSubnavCtrl.$inject = ['$scope', '$location'];
25834
25835    function authorizationSubnavCtrl($scope, $location) {
25836
25837        $scope.currentPath = $location.path().substring(1); //Removes the leading slash '/'
25838
25839        $scope.subnavpages = [
25840            { path: 'App/HealthRecords/Authorizations/All', name: 'page_healthrec_allAuthorizations_title'},
25841            { path: 'App/HealthRecords/Authorizations/Medical', name: 'form_medical'},
25842            { path: 'App/HealthRecords/Authorizations/Rx', name: 'form_pharmacy'},
25843            { path: 'App/HealthRecords/Authorizations/Vision', name: 'form_vision'}
25844        ];
25845    };
25846})();
25847
25848
25849
25850(function () {
25851    'use strict';
25852
25853    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
25854    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
25855
25856    const app = angular.module('MemberPortalDirectives');
25857
25858    app.directive('availableCourseCategory', function availableCourseCategory() {
25859            return {
25860                restrict: 'AE',
25861                templateUrl: urlBase + '/App/directives/available-courses/available-course-category.html' + cacheBust,
25862                scope: {},
25863                controller: availableCourseCategoryController,
25864                controllerAs: 'vc'
25865            };
25866        });
25867
25868    availableCourseCategoryController.$inject = ['$scope', 'memberDataService',
25869        '$location', 'requestedLanguageService', '$filter'];
25870
25871    function availableCourseCategoryController($scope, memberDataService,
25872        $location, requestedLanguageService, $filter) {
25873            var vm = this;
25874            vm.loading = false;
25875            vm.data = [];
25876
25877            vm.lan = requestedLanguageService.getCurrentLanguage();
25878
25879            vm.loadFormOptions = loadFormOptions;
25880            vm.onSelectCat = onSelectCat;
25881
25882            $scope.$on('language-change',
25883                function(event, key) {
25884                    vm.lan = key;
25885                    init();
25886                });
25887
25888            init();
25889
25890            function init() {
25891                vm.loadFormOptions();
25892            }
25893
25894            function loadFormOptions() {
25895                vm.loading = true;
25896                memberDataService.HealthEdOptionsLookUp(vm.lan)
25897                    .success(function(resp) {
25898                        console.log('loadFormOptions', resp);
25899                        for (let i in resp) {
25900                            if (Object.prototype.hasOwnProperty.call(resp, i)) {
25901                                if (resp[i].Type === 'Category') {
25902                                    vm.categories = resp[i].TypeList;
25903                                }
25904                            }
25905                        }
25906                        vm.loading = false;
25907                    })
25908                    .error(function() {
25909                        vm.loading = false;
25910                        vm.errorMessage = $filter('translate')('form_unableToRetrieveData');
25911                    });
25912            }
25913
25914            function onSelectCat(id) {
25915                console.log('onSelectCat', id);
25916                $location.path('/App/HealthCare/Courses/select').search({ categoryId: id });
25917            }
25918        }
25919
25920})();
25921
25922
25923
25924(function () {
25925    'use strict';
25926
25927    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
25928    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
25929
25930    const app = angular.module('MemberPortalDirectives');
25931
25932    app.directive('availableCourses', function availableCourses() {
25933            return {
25934                restrict: 'AE',
25935                templateUrl: urlBase + '/App/directives/available-courses/available-courses.html' + cacheBust,
25936                scope: {},
25937                controller: availableCoursesController,
25938                controllerAs: 'vc'
25939            };
25940        });
25941
25942    availableCoursesController.$inject = ['$scope', 'memberDataService', '$uibModal', '$route', '$location',
25943        'requestedLanguageService', '$filter', 'authStatusService', '$q', 'utilitiesService'];
25944
25945    function availableCoursesController($scope, memberDataService, $uibModal, $route, $location,
25946        requestedLanguageService, $filter, authStatusService, $q, utilitiesService) {
25947
25948            const vm = this;
25949
25950            vm.registerForCourse = registerForCourse;
25951
25952            vm.backToCategory = backToCategory;
25953            vm.getLocationDetail = getLocationDetail;
25954
25955            init();
25956
25957        function init() {
25958
25959                vm.availableCoursesActivities = [];
25960
25961                vm.lan = requestedLanguageService.getCurrentLanguage();
25962
25963                vm.isProxied = authStatusService.checkIfProxied();
25964
25965                vm.categoryId = $route.current.params.categoryId;
25966                console.log('vm.categoryId', vm.categoryId);
25967                vm.categoryId ? loadCurrentCategory() : backToCategory();
25968
25969        }
25970
25971        async function loadCurrentCategory() {
25972
25973            vm.loading = true;
25974
25975            try {
25976                //formOptions is needed later on the confirmation modal 
25977                const formOptions = await memberDataService.HealthEdOptionsLookUp(vm.lan);
25978                //member info is needed later for user interaction
25979                const memberInfo = await memberDataService.GetDemographics();
25980
25981                //console.log('formOptions', formOptions.data);
25982                //console.log('member info', memberInfo.data);
25983
25984                getCurrentCategory(formOptions.data);
25985                getMemberDemographics(memberInfo.data);
25986
25987                $q.all([
25988                    memberDataService.GetActivitiesByCategory(vm.lan, vm.categoryId),
25989                    memberDataService.GetCoursesByCategory(vm.lan, vm.categoryId)
25990                ]).then(([activities, courses]) => {
25991                    //console.warn('activities', activities.data);
25992                    //console.warn('courses', courses.data);
25993
25994                    vm.activities = activities.data;
25995
25996                    if (!courses.data || courses.data.List.length === 0) {
25997
25998                        vm.availableCoursesActivities = null;
25999
26000                        vm.infoMessage = $filter('translate')('no_available_courses_found');
26001
26002                        return;
26003                    }
26004
26005                    courses.data.List.forEach(function(item) {
26006                        const match = vm.activities.find(m => m.ActivityId === item.ActivityId);
26007                        console.log('matched activity and course', match);
26008                        if (match) {
26009                            item.Activity = match;
26010                            item.ActivityName = match.Name;
26011                        }
26012                    });
26013
26014                    vm.availableCoursesActivities = courses.data.List;
26015
26016                    console.log('GetAvailableProgramsAndCourses', vm.availableCoursesActivities);
26017                });
26018
26019            } catch (error) {
26020                console.log('error', error);
26021                vm.errorMessage = $filter('translate')('form_unableToRetrieveData');
26022
26023            } finally {
26024                vm.loading = false;
26025            }
26026            }
26027
26028        function getCurrentCategory(formOptions) {
26029
26030            const categories = formOptions.find(m => m.Type === 'Category').TypeList;
26031
26032            vm.cat = categories.find(m => (m.Id === Number(vm.categoryId)));
26033
26034            console.log('current category', vm.cat);
26035        }
26036
26037        function backToCategory() {
26038            $location.path('/App/HealthCare/Courses/Available');
26039        }
26040        
26041        function getMemberDemographics(member) {
26042            vm.member = member;
26043
26044            vm.member.Age = utilitiesService.determineAge(vm.member.DateOfBirth);
26045
26046            console.log('getMemberDemographics', vm.member);
26047        }
26048
26049        function registerForCourse(course) {
26050            console.log('registerForCourse', course);
26051
26052            const match = vm.activities.find(m => m.ActivityId === course.ActivityId);
26053            
26054            var data = {
26055                member: vm.member,
26056                activity: match,
26057                course: course,
26058                options: vm.formOptions
26059            };
26060
26061            const modalInstance = $uibModal.open({
26062                backdrop: 'static',
26063                animation: true,
26064                templateUrl: urlBase + '/App/directives/available-courses/course-options-modal.html' + cacheBust,
26065                windowClass: 'courseOptionsModal',
26066                controller: 'courseOptionsModal',
26067                scope: $scope,
26068                controllerAs: 'vc',
26069                size: 'md',
26070                resolve: {
26071                    data: function () {
26072                        return data;
26073                    }
26074                }
26075            });
26076
26077            modalInstance.result.then(function (data) {
26078                console.log('modal closed', data);
26079
26080            }, function () {
26081                console.log('Modal dismissed at: ' + new Date());
26082            });
26083        }
26084
26085        function getLocationDetail(course) {
26086            console.log('getLocationDetail', course);
26087
26088            if (!course.facilityDetail) {
26089                memberDataService.HealthEdFacilityDetail(course.FacilityId)
26090                    .success(function (facility) {
26091                        console.log('Facility detail', facility);
26092                        course.facilityDetail = facility;
26093
26094                    }).error(function () {
26095
26096                    });
26097            }
26098        }
26099
26100        $scope.$on('language-change', function (event, key) {
26101            vm.lan = key;
26102            init();
26103        });
26104
26105        }
26106
26107})();
26108
26109
26110
26111(function () {
26112    'use strict';
26113    angular
26114        .module('MemberPortalDirectives')
26115        .controller('courseOptionsModal', courseOptionsModal);
26116
26117    courseOptionsModal.$inject = ['data', '$modalInstance', 'iFormioService', '$scope', '$timeout', 'requestedLanguageService'];
26118    function courseOptionsModal(data, $modalInstance, iFormioService, $scope, $timeout, requestedLanguageService) {
26119        var vm = this;
26120
26121        vm.data = data;
26122        vm.loading = true;
26123        vm.showIFormio = false;
26124        vm.showSuccessMessage = false;
26125        vm.lan = requestedLanguageService.getCurrentLanguage();
26126
26127        vm.onNo = onNo;
26128        vm.close = close;
26129
26130        vm.formParams = '&iFormioLoad=true';
26131        vm.formParams += '&formLanguage=' + vm.lan;
26132        vm.formParams += '&courseId=' + vm.data.course.CourseId;
26133        vm.iframeSize = 'small';
26134
26135        $scope.$on('$destroy',
26136        function() {
26137            console.log('courseOptionsModal destroyed');
26138            window.removeEventListener('message', $scope.iFormioListener);
26139            window.removeEventListener('iFormio-appInitialized', $scope.iFormioAppInitialized);
26140            window.removeEventListener('iFormio-submit', $scope.iFormioSubmit);
26141        });
26142
26143        init();
26144
26145        function init() {
26146            console.log('courseOptionsModal', vm.data);
26147
26148            if (vm.data.course.ActivityType !== 'Mailing') {
26149                vm.iframeSize = 'large';
26150            }
26151
26152            iFormioService.setElementId('iformio-iframe-hc-course-registration');
26153            iFormioService.setIFormioParams(vm.data.course.RegistrationForm + vm.formParams, 'memberportal');
26154            vm.iFormioSrc = iFormioService.getIFormioSrc(false);
26155
26156            populateForm();
26157        }
26158
26159        function populateForm() {
26160            $scope.iFormioListener = iFormioService.iFormioListener;
26161
26162            $scope.iFormioAppInitialized = function(e) {
26163                console.log(e);
26164
26165                $timeout(function () {
26166                    vm.loading = false;
26167                });
26168
26169                vm.formioData = {
26170                    memberNumber: vm.data.member.SubscriberId,
26171                    email: iFormioService.getEmailAddress(),
26172                };
26173
26174                console.log(vm.formioData);
26175
26176                iFormioService.setIFormioEvents({
26177                    submit: true
26178                });
26179                iFormioService.setSubmission(vm.formioData);
26180                iFormioService.setShowForm(true, false);
26181            };
26182
26183            $scope.iFormioSubmit = function(e) {
26184                console.log(e);
26185
26186                $timeout(function () {
26187                    vm.showSuccessMessage = true;
26188                });
26189            };
26190
26191            window.addEventListener('message', $scope.iFormioListener);
26192            window.addEventListener('iFormio-appInitialized', $scope.iFormioAppInitialized);
26193            window.addEventListener('iFormio-submit', $scope.iFormioSubmit);
26194
26195            $timeout(function () {
26196                vm.showIFormio = true;
26197            });
26198        };
26199
26200        function onNo() {
26201            $modalInstance.dismiss();
26202        }
26203
26204        function close() {
26205            $modalInstance.close(vm.data.course);
26206        }
26207    }
26208})();
26209
26210(function () {
26211    'use strict';
26212
26213    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
26214    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
26215
26216    angular
26217        .module('MemberPortalDirectives')
26218        .directive('carePlanDetails', carePlanDetails);
26219
26220    function carePlanDetails() {
26221
26222        const directive = {
26223            scope: {
26224                details: '='
26225            },
26226            restrict: 'E',
26227            templateUrl: urlBase +
26228                '/App/directives/care-plan-details/care-plan-details.directive.html' + cacheBust,
26229            controller: carePlanDetailsSharedController,
26230            controllerAs: 'cpd'
26231        };
26232
26233        carePlanDetailsSharedController.$inject = ['$scope'];
26234
26235        function carePlanDetailsSharedController($scope) {
26236
26237            const vm = this;
26238
26239        }
26240
26241        return directive;
26242
26243    }
26244
26245})();
26246//---------NOT IN USE---------------------------------------------------------
26247//------------------------------------------------------------------
26248(function () {
26249    'use strict';
26250
26251    const app = angular.module('MemberPortalDirectives');
26252
26253    //------------------------------------------------------------
26254    //------------------------------------------------------------
26255    app.directive('commonTable', function () {
26256        const directive = {
26257            scope: {
26258                data: '=',
26259                headingstyle: '@',
26260                labelstyle: '@',
26261                linestyle: '@'
26262            },
26263            restrict: 'E',
26264            template: '<table class="table table-condensed table-striped hidden-xs">\
26265                <thead>\
26266                    <tr>\
26267                        <th ng-repeat="header in data.Labels" ng-style="headingstyle">{{header | translate}}</th>\
26268                    </tr>\
26269                </thead>\
26270                <tbody>\
26271                    <tr ng-repeat="row in Model">\
26272                        <td ng-repeat="value in row" ng-style="linestyle">{{value}}</td>\
26273                    </tr>\
26274                </tbody>\
26275            <table>\
26276            <table class="table table-condensed hidden-sm hidden-md hidden-lg" ng-style="style">\
26277                <thead>\
26278                    <tr>\
26279                        <th ng-repeat="heading in Headings" ng-style="headingstyle">{{heading}}</th>\
26280                    </tr>\
26281                </thead>\
26282                <tbody>\
26283                    <tr ng-repeat="row in XsModel">\
26284                        <td ng-repeat="col in row.columns">\
26285                            <div ng-repeat="line in col.lines">\
26286                                <label ng-style="labelstyle">{{line.label | translate}}:</label>\
26287                                <span ng-style="linestyle">{{line.data}}</span>\
26288                            </div>\
26289                        </td>\
26290                    </tr>\
26291                </tbody>\
26292            </table>',
26293            controller: directiveController
26294        };
26295
26296        directiveController.$inject = ['$scope'];
26297
26298        return directive;
26299
26300        function directiveController($scope) {
26301            console.log('directiveController');
26302            console.log($scope.data);
26303            $scope.dataTest = {
26304                Labels: [
26305                    'Drug Name',
26306                    'Prescriber',
26307                    'Filled By',
26308                    'Qty',
26309                    'Filled On'
26310                ],
26311
26312                Model: [
26313                    ['Levothyroxine 100 Mcg Tab', 'Brian Salabarrieta', 'Cvs Pharmacy 09849', '90', '08/01/16'],
26314                    ['Levothyroxine 100 Mcg Tab', 'Brian Salabarrieta', 'Cvs Pharmacy 09849', '90', '05/05/16'],
26315                    ['Levothyroxine 100 Mcg Tab', 'Brian Salabarrieta', 'Cvs Pharmacy 09849', '90', '03/12/16'],
26316                    ['Levothyroxine 100 Mcg Tab', 'Arturo Tripp', 'Cvs Pharmacy 09849', '10', '01/20/16']
26317                ],
26318
26319                ObjectPropertyNames: [
26320                    'DrugName',
26321                    'PhysicanFullName',
26322                    'PhrmName',
26323                    'MetricQuantity',
26324                    'OrderDate'
26325                ],
26326
26327                // Rows and Object Property Name are converted to Objects
26328                Objects: [
26329                ],
26330
26331                // Headings used in the XS view
26332                Headings: {
26333                    //"XS": ["Drug Info", "Filled Info"]
26334                    "XS": ['Drug Information']
26335                },
26336                ColumnDefinition: {
26337                    //"XS": [[0, 1], [2, 4, 3]]
26338                    "XS": [[0, 1, 2, 4, 3]]
26339                }
26340
26341                // Example 1:  
26342                //
26343                //   header_xs: [["Label1","Label2","Label3"]] // array of one element containing an array
26344                //
26345                //  - SM, MD, LG View: Displays 3 columns
26346                //    Header:    Label1     Label2      Label3
26347                //    Data Row:  Data1      Data2       Data3
26348                //
26349                //  - XS View: Displays 1 Column each table row has the following (and no header)
26350                //    Label1: Data1
26351                //    Label2: Data2
26352                //    Label3: Data3
26353
26354                // Example 2:
26355                //
26356                //    header_xs: [["Label1","Label2","Label3"],["Label4","Label5"]] // array of two elements contains two arrays
26357                //
26358                //  - SM, MD, LG View: Displays 3 columns
26359                //    Header:    Label1     Label2     Label3     Label4     Label5
26360                //    Data Row:  Data1      Data2      Data3      Data4      Data5
26361                //
26362                //  - XS View: Displays 1 Column each table row has the following (and no header)
26363                //    Column1: Data1
26364                //    Column2: Data2
26365                //    Column3: Data3
26366                // ["Column1","Column2","Column3"]
26367            };
26368
26369            // $scope.data is passed into the directive.  Uncomment the following like to use the internal
26370            // test data and decouple the directive from the rest of the member portal for development
26371            //$scope.data = $scope.dataTest;
26372
26373            if ($scope.headingstyle == null) $scope.headingstyle = { "font-size": '1.4em' };
26374            if ($scope.labelstyle == null) $scope.labelstyle = { "font-size": '1.0em' };
26375            if ($scope.linestyle == null) $scope.linestyle = { "font-size": '1.2em' };
26376
26377            var dataObj = null;
26378            $scope.$watch('data', function (newValue, oldValue) {
26379                const bsWidth = 'XS';
26380                if ($scope.data !== undefined) {
26381                    dataObj = typeof ($scope.data) === 'object' ? $scope.data : JSON.parse($scope.data);
26382                    $scope.XsModel = processData(dataObj.ColumnDefinition[bsWidth], dataObj.Labels, dataObj.ObjectPropertyNames, dataObj.Objects);
26383                    $scope.Model = processDataForDefaultBsWidth(dataObj.ObjectPropertyNames, dataObj.Objects);
26384                    $scope.Headings = $scope.getHeadings();
26385                }
26386            });
26387
26388            $scope.getHeadings = function () {
26389                const bsWidth = 'XS';
26390                return dataObj.Headings[bsWidth];
26391            };
26392
26393            $scope.getRows = function (bsWidth) {
26394                bsWidth = 'XS';
26395                const rows = processData(dataObj.ColumnDefinition[bsWidth], dataObj.Labels, dataObj.ObjectPropertyNames, dataObj.Objects);
26396                return rows;
26397            };
26398
26399            // convert the input data into data that can be interated by the UI
26400
26401            // line_mapping - line_mapping_xs: [[0, 1, 2], [3, 4]]
26402            // labels - data_label: ["Drug Name","Prescriber","Filled By","Qty","Filled On"],
26403            // rows - ["Levothyroxine 100 Mcg Tab", "Brian Salabarrieta", "Cvs Pharmacy 09849", "90", "08/01/16"],
26404            //        ["Levothyroxine 100 Mcg Tab", "Brian Salabarrieta", "Cvs Pharmacy 09849", "90", "05/05/16"],
26405            //        ["Levothyroxine 100 Mcg Tab", "Brian Salabarrieta", "Cvs Pharmacy 09849", "90", "03/12/16"],
26406
26407            // desired result: rows -> columns -> lines -> label/data
26408
26409            function processDataForDefaultBsWidth(propertyNames, dataObjects) {
26410                const result = [];
26411                for (let iObject = 0; iObject < dataObjects.length; iObject++) {
26412                    const values = [];
26413                    for (let iProp = 0; iProp < propertyNames.length; iProp++) {
26414                        const propName = propertyNames[iProp];
26415                        values.push(dataObjects[iObject][propName]);
26416                    }
26417                    result.push(values);
26418                }
26419                return result;
26420            }
26421
26422            function processData(line_mapping, dataLabels, propertyNames, dataObjects) {
26423
26424                if (typeof (dataObjects) === 'string' && dataObjects.length > 0) dataObjects = JSON.parse(dataObjects);
26425
26426                if (!line_mapping || line_mapping.length === 0) console.log('processData: line_mapping is invali
26426d');
26427                if (!dataLabels || dataLabels.length === 0) console.log('processData: dataLabels is invalid');
26428                if (!propertyNames || propertyNames.length === 0) console.log('processData: propertyNames is invalid');
26429
26430                var bError = false;
26431                if (!line_mapping || line_mapping.length === 0) bError = true;
26432                if (!dataLabels || dataLabels.length === 0) bError = true;
26433                if (!propertyNames || propertyNames.length === 0) bError = true;
26434
26435                if (!dataObjects || dataObjects.length === 0) bError = true;
26436                if (bError) return;
26437             
26438                const resultRows = {};
26439                var iPropertyNameIndex = 0;
26440                for (iPropertyNameIndex = 0; iPropertyNameIndex < propertyNames.length; iPropertyNameIndex++) {
26441                    const row = { columns: [] };
26442                    // line_mapping is a double array, iMap1 is the index into the first array, and iMap2 is the index into the second arrays.
26443                    let iMap1 = 0; // column
26444                    let iMap2 = 0; // property displayed in the column
26445                    for (iMap1 = 0; iMap1 < line_mapping.length; iMap1++) {
26446                        const column = { lines: [] };
26447                        for (iMap2 = 0; iMap2 < line_mapping[iMap1].length; iMap2++) {
26448                            // grap the property label and data, store it on a data line object,
26449                            // and add it to the column object
26450                            const rowIndex = line_mapping[iMap1][iMap2];
26451                            const label = dataLabels[rowIndex];
26452                            const propname = propertyNames[rowIndex];
26453                            const data = dataObjects[rowIndex][propname];
26454                            const line = {
26455                                label: label,
26456                                data: data
26457                            };
26458                            column.lines.push(line);
26459                        }
26460                        row.columns.push(column);
26461                    }
26462                    resultRows[iPropertyNameIndex] = row;
26463                }
26464
26465                return resultRows;
26466            }
26467        }
26468    });
26469})();
26470
26471/**
26472 * dateRangeValidator
26473 * -------------------
26474 * Cross-field validator for angularjs-datepicker inputs.
26475 * Ensures two datepicker models form a valid chronological range:
26476 * - both dates must exist and be valid in the configured date format
26477 * - start date cannot be after end date
26478 *
26479 * Integrates with ngModel to set $error.dateRange, allowing forms
26480 * to block submission, show error messages, and highlight invalid fields.
26481 *
26482 * Does not alter model values or interfere with angularjs-datepicker’s
26483 * native parsing, formatting, or min/max date logic.
26484 */
26485
26486(function () {
26487    'use strict';
26488
26489    angular
26490        .module('MemberPortalDirectives')
26491        .directive('dateRangeValidator', dateRangeValidator);
26492
26493    dateRangeValidator.$inject = [];
26494
26495    function dateRangeValidator() {
26496
26497        const directive = {
26498            require: 'ngModel',
26499            scope: {
26500                startModel: '=dateRangeStart',
26501                endModel: '=dateRangeEnd'
26502            },
26503            restrict: 'A',
26504            controller: dateRangeValidatorController,
26505            controllerAs: 'drv',
26506            bindToController: true
26507        };
26508
26509        dateRangeValidatorController.$inject = ['$scope', '$element'];
26510
26511        function dateRangeValidatorController($scope, $element) {
26512
26513            const vm = this;
26514
26515            init();
26516
26517            function init() {
26518                // Watcher picks up changes triggered by angularjs-datepicker
26519                $scope.$watchGroup(
26520                    ['drv.startModel', 'drv.endModel'],
26521                    function () {
26522                        validateRange();
26523                    }
26524                );
26525            }
26526
26527            function validateRange() {
26528
26529                const dateFormat = "MM/DD/YYYY";
26530
26531                const start = moment(vm.startModel, dateFormat, true);
26532                const end   = moment(vm.endModel, dateFormat, true);
26533
26534                let isValid = true;
26535
26536                // If either date is empty — don't invalidate the range
26537                // Let angularjs-datepicker manage required and format validation.
26538                if (!vm.startModel || !vm.endModel) {
26539                    updateValidity(true);
26540                    return;
26541                }
26542
26543                // Check format
26544                if (!start.isValid() || !end.isValid()) {
26545                    isValid = false;
26546                }
26547
26548                // Check chronological order
26549                if (isValid && start.isAfter(end)) {
26550                    isValid = false;
26551                }
26552
26553                updateValidity(isValid);
26554            }
26555
26556            function updateValidity(flag) {
26557                const ngModelCtrl = $element.controller('ngModel');
26558                if (ngModelCtrl) {
26559                    ngModelCtrl.$setValidity('dateRange', flag);
26560                }
26561            }
26562        }
26563
26564        return directive;
26565    }
26566})();
26567(function () {
26568    const app = angular.module('MemberPortalDirectives');
26569
26570    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
26571    //const toggleDebugTool = $('meta[name="DebugTool"]').attr('content');
26572    var cacheBust = '?v=' + $('meta[name="templateCacheBust"]').attr('content');
26573
26574    const enableDebugTools = document.domain !== 'members.iehp.org';
26575    
26576    //If domain == members.iehp.org, do not declare debug tool directive
26577    if (enableDebugTools) {
26578        app.directive('debugTool', function () {
26579            return {
26580                scope: {
26581                    data: '='
26582                },
26583                restrict: 'EA',
26584                transclude: true,
26585                templateUrl: urlBase + '/App/directives/debug-tool/debugTool.html' + cacheBust,
26586                controller: 'debugToolCtrl',
26587                controllerAs: 'ctrl'
26588            };
26589        });
26590    }
26591
26592    app.controller('debugToolCtrl', ['$scope', 'authStatusService', 'authService', '$rootScope',
26593        function ($scope, authStatusService, authService, $rootScope) {
26594        $scope.hide = true;
26595        $scope.debugToggleAction = 'Show';
26596        const authStatus = authStatusService.getStatus();
26597
26598        $scope.refreshToken = function() {
26599            authService.refreshToken();
26600        };
26601
26602        $scope.dataString = function () {
26603            return JSON.stringify($scope.data, null, '  ');
26604        };
26605
26606        $scope.authStatusString = function() {
26607            return JSON.stringify(authStatus, null, '  ');
26608        };
26609
26610        $scope.toggleHide = function () {
26611            $scope.hide = !$scope.hide;
26612            $scope.debugToggleAction = ($scope.hide) ? 'Show' : 'Hide';
26613        };
26614
26615        $scope.toggleLoading = function() {
26616            $scope.$parent.loading = !$scope.$parent.loading;
26617        };
26618
26619        $scope.toggleErrorMessage = function() {
26620            if ($scope.$parent.errorMessage === '') {
26621                $scope.$parent.errorMessage = 'Error Message';
26622            } else {
26623                $scope.$parent.errorMessage = '';
26624            }
26625        };
26626
26627        $scope.test = function() {
26628            authService.test().success(function() {
26629                alert('yay');
26630            }).error(function() {
26631                alert('boo');
26632            });
26633        };
26634
26635        $scope.keepAlive = function() {
26636            $rootScope.$broadcast('inactivity-prompt');
26637        };
26638
26639    }]);
26640
26641
26642    if (enableDebugTools) {
26643        /* Data Node */
26644        app.directive('debugToolDataNode', function () {
26645            return {
26646                scope: {
26647                    name: '@',
26648                    data: '='
26649                },
26650                restrict: 'E',
26651                templateUrl: urlBase + '/App/directives/debug-tool/debugToolDataNode.html' + cacheBust,
26652                controller: 'debugToolDataNodeCtrl',
26653                controllerAs: 'ctrl'
26654            };
26655        });
26656    }
26657
26658    app.controller('debugToolDataNodeCtrl', ['$scope', function ($scope) {
26659
26660        $scope.dataString = function () {
26661            return JSON.stringify($scope.data, null, '  ');
26662        };
26663
26664    }]);
26665})();
26666
26667
26668
26669(function () {
26670    'use strict';
26671
26672    angular
26673        .module('MemberPortalDirectives')
26674        .directive('delegateForm', delegateForm);
26675
26676    delegateForm.$inject = ['environmentConfig'];
26677
26678    function delegateForm(environmentConfig) {
26679
26680        const directive = {
26681            scope: true,
26682            restrict: 'AE',
26683            templateUrl: environmentConfig.appRoot +
26684                '/App/directives/delegate-form/delegate-form.directive.html'
26685                + environmentConfig.cacheBust,
26686            controller: delegateFormController,
26687            controllerAs: 'df',
26688            bindToController: true
26689        };
26690
26691        delegateFormController.$inject = ['healthDelegateService',
26692            'memberDataService', '$route', '$rootScope',
26693            '$uibModal', '$filter', '$location', 'authService',
26694            'authStatusService', 'requestedLanguageService', '$scope', '$timeout'];
26695
26696        function delegateFormController(healthDelegateService,
26697            memberDataService, $route, $rootScope,
26698            $uibModal, $filter, $location, authService,
26699            authStatusService, requestedLanguageService, $scope, $timeout) {
26700
26701            const vm = this;
26702
26703            vm.cancelAddDelegate = cancelAddDelegate;
26704
26705            vm.searchNewMember = searchNewMember;
26706
26707            vm.submitNewDelegateForm = submitNewDelegateForm;
26708            
26709            vm.populateUserInfo = populateUserInfo;
26710
26711            vm.toggleSection = toggleSection;
26712
26713            vm.cancelAddCaregiver = cancelAddCaregiver;
26714
26715            vm.confirmNewMember = confirmNewMember;
26716
26717            var focusTimeout;
26718
26719            init();
26720
26721            function init() {
26722                
26723                const authStatus = authStatusService.getStatus(); 
26724
26725                vm.memberId = authStatus.claims.username;
26726
26727                vm.isCCA = authStatus.isCCA;
26728                console.log('isCCA', vm.isCCA);
26729
26730                vm.currentSection = vm.isCCA ? 'selectMember' : 'addMember';
26731
26732                toggleSection(vm.currentSection);
26733                
26734                vm.newMember = {};
26735
26736                vm.consentAcknowledged = false;
26737
26738                vm.accessFromDate = moment().format('MM/DD/YYYY');
26739                
26740                vm.minAccessDate = moment().format('MM/DD/YYYY');
26741
26742                //default to the end of the year for cca. if the member selected later has a diff. max date, 
26743                //it will receive the max date from api.
26744                vm.maxAccessDate = moment().endOf('year').format('MM/DD/YYYY');
26745               
26746                vm.today = moment().format('MM/DD/YYYY');
26747
26748                vm.newDelegateForm = {};
26749
26750                vm.addNewMemberForm = {};
26751
26752                vm.currentLanguage = requestedLanguageService.getCurrentLanguage();
26753
26754                const isProxied = authStatusService.checkIfProxied();
26755
26756                console.log('isProxied', isProxied);
26757                vm.isEditable = !isProxied;
26758
26759                vm.currentAccessType = $scope.isRequestDelegate ? 'Request' : 'Add';
26760
26761                loadDelegateCandidates(vm.currentAccessType);
26762            }
26763
26764            function resetForm() {
26765                vm.errorMsgSearchCaregiver = null;
26766                vm.messageType = null;
26767                vm.loadingSubmitNewMember = false;
26768
26769                if (vm.newMember) {
26770                    vm.newMember.isEligible = false;
26771                }
26772            }
26773
26774            function searchNewMember(newMemberForm) {
26775                console.log('newMemberForm', newMemberForm);
26776                resetForm();
26777
26778                vm.addNewMemberSubmitted = true;
26779                
26780                if (newMemberForm.$invalid) return;
26781
26782                vm.loadingSubmitNewMember = true;
26783
26784                const params = {
26785                    firstName: vm.newMember.firstName,
26786                    lastName: vm.newMember.lastName,
26787                    memberId: vm.newMember.id,
26788                    dateOfBirth: moment(vm.newMember.dob).toISOString(),
26789                    accessType: vm.currentAccessType
26790            };
26791
26792                memberDataService.searchCaregiverMember(params, vm.currentLanguage).then(function(resp) {
26793                    console.log('member search response', resp);
26794                    if (!resp || !resp.data) {
26795                        throw new Error('member search api response error');
26796                    }
26797
26798                    const result = resp.data;
26799
26800                    vm.maxAccessDate = result.accessMaxDate;
26801
26802                    if (result.messageType === 'success') {
26803                        vm.newMember.isEligible = true;
26804                        vm.newMember.firstName = result.firstName;
26805                        vm.newMember.lastName = result.lastName;
26806                    }
26807                    else if (result.messageType === 'warning') {
26808                        vm.newMember.isEligible = true;
26809                        vm.newMember.firstName = result.firstName;
26810                        vm.newMember.lastName = result.lastName;
26811                        vm.messageType = result.messageType;
26812                        vm.errorMsgSearchCaregiver = result.message;
26813                        console.log('vm.messageType', vm.messageType);
26814                        console.log('vm.errorMsgSearchCaregiver', vm.errorMsgSearchCaregiver);
26815                    }
26816                    else {
26817                        vm.messageType = result.messageType;
26818                        vm.errorMsgSearchCaregiver = result.message;
26819                    }
26820                    vm.loadingSubmitNewMember = false;
26821
26822                }, function() {
26823                    vm.errorMsgSearchCaregiver = 'Member Search Error.';
26824                    vm.loadingSubmitNewMember = false;
26825                });
26826            }
26827
26828            function toggleSection(section) {
26829                vm.currentSection = section;
26830
26831                resetForm();
26832
26833                if (section === 'addMember') {
26834
26835                    vm.newMember = {};
26836
26837                    focusTimeout = $timeout(function () {
26838
26839                        if (Object.keys(vm.addNewMemberForm).length === 0) return;
26840                        
26841                        vm.addNewMemberForm.$setPristine();
26842                        vm.addNewMemberForm.$setUntouched();
26843
26844                        $('#addNewMemberForm input[name="inputFirstName"]').focus();
26845                        }, 300);
26846                }
26847                else if (section === 'selectMember') {
26848
26849                    vm.selectedMemberId = null;
26850                    vm.selectedMemberEmail = null;
26851
26852                    focusTimeout = $timeout(function () {
26853
26854                        if (Object.keys(vm.newDelegateForm).length === 0) return;
26855
26856                        vm.newDelegateForm.$setPristine();
26857                        vm.newDelegateForm.$setUntouched();
26858
26859                        $('#newDelegateForm select[name="memberCandidateSelect"]').focus();
26860                    }, 300);
26861                }
26862            };
26863
26864            $scope.$on('destroy',
26865                function() {
26866                    if (focusTimeout) $timeout.cancel(focusTimeout);
26867                });
26868
26869            function populateUserInfo(selectedMemberId) {
26870                console.log('selected member', selectedMemberId);
26871
26872                const selectedMember = vm.proxyCandidates.find(m => m.memberNumber === selectedMemberId);
26873
26874                if (selectedMember) {
26875                    vm.selectedMemberEmail = selectedMember.email;
26876                    vm.selectedMemberName = getSelectedDelegateInfo('selectMember').memberName;
26877                } else {
26878                    vm.selectedMemberEmail = null;
26879                }
26880            }
26881
26882            function confirmNewMember() {
26883                vm.newMember.confirmed = true;
26884                vm.selectedMemberName = getSelectedDelegateInfo('addMember').memberName;
26885            }
26886
26887            function cancelAddCaregiver() {
26888                $location.path('App/MyInformation/Delegates');
26889            }
26890            
26891            function confirmAddDelegate({ selectedMemberId, selectedMemberName }) {
26892             
26893                const translatedCancel = $filter('translate')('form_cancel');
26894
26895                const translatedApprove = $filter('translate')('approve');
26896
26897                const translatedConfirmMsg = $scope.isRequestDelegate ? $filter('translate')('page_delegate_add_confirm_request') :
26898                    $filter('translate')('page_delegate_add_confirm');
26899
26900                const supplemental = `(${vm.accessFromDate} - ${vm.accessEndDate})`;
26901
26902                const data = {
26903                    message: translatedConfirmMsg,
26904                    additionalInfo: selectedMemberName,
26905                    supplementalInfo: supplemental,
26906                    yesBtn: translatedApprove,
26907                    noBtn: translatedCancel
26908                };
26909
26910                const modalInstance = $uibModal.open({
26911                    animation: true,
26912                    templateUrl: environmentConfig.appRoot +
26913                        '/App/directives/shared/confirmation-modal/confirmation-modal.html' +
26914                        environmentConfig.cacheBust,
26915                    windowClass: 'modalOne',
26916                    controller: 'confirmationModal',
26917                    backdrop: 'static',
26918                    keyboard: false,
26919                    controllerAs: 'confirm',
26920                    size: 'md',
26921                    resolve: {
26922                        data: function () {
26923                            return data;
26924                        }
26925                    }
26926                });
26927
26928                modalInstance.result.then(function (confirmed) {
26929                  
26930                    console.log('confirmed', confirmed);
26931                   
26932                    if (confirmed) {
26933
26934                        vm.errorMsg = '';
26935
26936                        const effective = new Date(vm.accessFromDate);
26937                        const term = new Date(vm.accessEndDate);
26938
26939                        const params = {
26940                            selectedMemberId: selectedMemberId,
26941                            effectiveDate: effective.toISOString(),
26942                            terminationDate: term.toISOString(),
26943                            accessType: vm.currentAccessType
26944                        }
26945
26946                        console.log('add delegate api params', params);
26947
26948                        vm.saving = true;
26949                        
26950                        memberDataService.CreateNewDelegate(params, vm.currentLanguage).then(function (resp) {
26951                            console.log('create new delegate resp', resp.data);
26952
26953                            if (resp.data.updated) {
26954
26955                                healthDelegateService.setPostUpSertMsg(resp.data.message);
26956
26957                                $location.path('/App/MyInformation/Delegates')
26958                                    .search({ tab: 'pending' });
26959                              
26960                            } else {
26961                                vm.errorMsg = resp.data.message;
26962                            }
26963
26964                            vm.saving = false;
26965                        }).catch(function () {
26966                            //if error
26967                            const error =
26968                                $filter('translate')('page_healthrec_rxPrescriptions_DrugCoverageDetermination_SubmittedError');
26969                            vm.errorMsg = error;
26970                            vm.saving = false;
26971                        });
26972                    }
26973                });
26974            }
26975
26976            function submitNewDelegateForm(newDelegateForm) {
26977
26978                $scope.$broadcast('newDelegateSubmitted');
26979
26980                if (!newDelegateForm.$valid) {
26981
26982                    const firstInvalidElement = document.querySelector('form .ng-invalid');
26983              
26984                    if (firstInvalidElement) {
26985                        firstInvalidElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
26986                    }
26987
26988                    return;
26989                }
26990
26991                const selected = getSelectedDelegateInfo(vm.currentSection);
26992                console.log('selected member', selected);
26993                confirmAddDelegate({ selectedMemberId: selected.memberId, selectedMemberName: selected.memberName });
26994            }
26995
26996            function getSelectedDelegateInfo(currentSection) {
26997
26998                const selected = {
26999                    memberName: '',
27000                    memberId: ''
27001                }
27002
27003                if (currentSection === 'selectMember') {
27004                    const delegated = vm.proxyCandidates
27005                        .find(m => m.memberNumber === vm.selectedMemberId);
27006
27007                    selected.memberName = delegated.memberName;
27008                    selected.memberId = delegated.memberNumber;
27009                }
27010                else if (currentSection === 'addMember') {
27011                    selected.memberName = vm.newMember.firstName + ' ' + vm.newMember.lastName;
27012                    selected.memberId = vm.newMember.id;
27013                }
27014
27015                return selected;
27016            }
27017
27018            function cancelAddDelegate() {
27019
27020                const link = 'App/MyInformation/Delegates';
27021                $location.path(link);
27022            }
27023            
27024            function loadDelegateCandidates(accessType) {
27025
27026                vm.loading = true;
27027                vm.loadingDelegatesError = null;
27028
27029                memberDataService.getPotentialDelegates(vm.memberId).then(
27030                    function (results) {
27031                        
27032                        if (!results || !results.data || results.data.length === 0) {
27033
27034                            vm.proxyCandidates = [];
27035
27036                            return;
27037                        }
27038                        
27039                        const candidates = results.data.filter(m => m.accessType === accessType);
27040
27041                        candidates.forEach(function(item) {
27042                            const memberName = [item.firstName, item.middleName, item.lastName].join(' ');
27043
27044                            item.memberName = memberName.replace(/\s+/g, ' ');
27045                        });
27046                        
27047                        vm.proxyCandidates = candidates;
27048                       // console.log('vm.proxyCandidates', vm.proxyCandidates);
27049                    }, function (error) {
27050                        console.error('load proxy candidates failed', error);
27051                        vm.proxyCandidates = [];
27052                        vm.loadingDelegatesError = $filter('translate')('temp_unavailable');
27053                    })
27054                    .finally(function() {
27055                        vm.loading = false;
27056                    });
27057            }
27058
27059            $scope.$on('language-change', function () {
27060
27061                init();
27062
27063            });
27064        }
27065
27066        return directive;
27067    }
27068})();
27069(function () {
27070    'use strict';
27071
27072    angular
27073        .module('MemberPortalDirectives')
27074        .directive('delegateRequestHistory', delegateRequestHistory);
27075
27076    delegateRequestHistory.$inject = ['environmentConfig'];
27077    function delegateRequestHistory(environmentConfig) {
27078
27079        const directive = {
27080            scope: {},
27081            restrict: 'E',
27082            templateUrl: environmentConfig.appRoot +
27083                '/App/directives/delegate-request-history/delegate-request-history.directive.html'
27084                + environmentConfig.cacheBust,
27085            controller: delegateRequestHistoryController,
27086            controllerAs: 'hist',
27087            bindToController: true
27088        };
27089
27090        delegateRequestHistoryController.$inject = ['$scope', 'healthDelegateService',
27091            'memberDataService', 'authStatusService', 'requestedLanguageService', '$filter'];
27092
27093        function delegateRequestHistoryController($scope, healthDelegateService,
27094            memberDataService, authStatusService, requestedLanguageService, $filter) {
27095         
27096            const vm = this;
27097
27098            init();
27099
27100            $scope.$on('language-change', function () {
27101                init();
27102            });
27103
27104            function init() {
27105
27106                const claims = authStatusService.getStatus().claims;
27107
27108                vm.memberID = claims.username;
27109                
27110                loadHistory();
27111            }
27112
27113            function loadHistory() {
27114
27115                vm.loading = true;
27116                vm.loadingDelegatesError = null;
27117
27118                const currentLanguage = requestedLanguageService.getCurrentLanguage();
27119
27120                memberDataService.getHistoryDelegates(vm.memberID, currentLanguage).then(function (resp) {
27121          
27122                    console.log('history delegates from api', resp.data);
27123
27124                    if (!resp.data) { return; }
27125
27126                    const history = healthDelegateService.mapDelegates(resp.data);
27127
27128                    vm.history = history.sort((a, b) => new Date(b.requestDate) - new Date(a.requestDate));
27129
27130                    vm.isDescending = true;
27131
27132                }, function (error) {
27133                    console.error('error loading delegate history', error);
27134                    vm.history = [];
27135                    vm.loadingDelegatesError = $filter('translate')('temp_unavailable');
27136                }).finally(function () {
27137                    vm.loading = false;
27138                });
27139            }
27140        }
27141
27142        return directive;
27143    }
27144})();
27145(function () {
27146    'use strict';
27147
27148    angular
27149        .module('MemberPortalDirectives')
27150        .directive('delegateRequestPending', delegateRequestPending);
27151        
27152    delegateRequestPending.$inject = ['environmentConfig']; 
27153
27154    function delegateRequestPending(environmentConfig) {
27155
27156        const directive = {
27157            scope: {
27158                pendingRequests: '='
27159            },
27160            restrict: 'E',
27161            templateUrl: environmentConfig.appRoot +
27162                '/App/directives/delegate-request-pending/delegate-request-pending.directive.html'
27163                + environmentConfig.cacheBust,
27164            controller: delegateRequestPendingController,
27165            controllerAs: 'pending',
27166            bindToController: true
27167        };
27168
27169        delegateRequestPendingController.$inject = ['$filter', '$scope',
27170            'healthDelegateService', 'memberDataService', '$timeout',
27171            '$rootScope', 'authStatusService', '$uibModal', '$window',
27172            'requestedLanguageService', 'authService', '$location'];
27173
27174        function delegateRequestPendingController($filter, $scope,
27175            healthDelegateService, memberDataService, $timeout,
27176            $rootScope, authStatusService, $uibModal, $window,
27177            requestedLanguageService, authService, $location) {
27178           
27179            const vm = this;
27180
27181            vm.executeActions = executeActions;
27182            
27183            init();
27184
27185            $scope.$on('language-change', function () {
27186                init();
27187            });
27188
27189            function init() {
27190
27191                const claims = authStatusService.getStatus().claims;
27192
27193                vm.memberID = claims.username;
27194
27195                vm.translatedCancel = $filter('translate')('form_cancel');
27196
27197                vm.translatedAccept = $filter('translate')('form_accept');
27198
27199                vm.translatedApprove = $filter('translate')('approve');
27200
27201                vm.translatedReject = $filter('translate')('form_reject');
27202
27203                vm.translatedWithdraw = $filter('translate')('form_withdraw');
27204
27205                vm.currentLanguage = requestedLanguageService.getCurrentLanguage();
27206
27207                const isProxied = authStatusService.checkIfProxied();
27208
27209                //console.log('isProxied', isProxied);
27210                vm.isEditable = !isProxied;
27211
27212                if (vm.pendingRequests) {
27213                    //if data is passed from the caregiver landing page
27214                    processPendingDelegates(vm.pendingRequests);
27215                } else {
27216                    loadPendingDelegates();
27217                }
27218            }
27219
27220            function processPendingDelegates(results) {
27221              
27222                const outbound = results.filter(m => m.myRequest === true);
27223                const inbound = results.filter(m => m.myRequest !== true);
27224                /* console.log('processed outbound', outbound);*/
27225                /* console.log('processed inbound', inbound);*/
27226                if (outbound.length > 0) {
27227                    const mappedOutbound = healthDelegateService.mapDelegates(outbound);
27228
27229                    vm.outbound = mapOutboundDisplayFields(mappedOutbound);
27230
27231                    //console.log('vm.outbound', vm.outbound);
27232
27233                } else {
27234                    vm.outbound = [];
27235                }
27236
27237                if (inbound.length > 0) {
27238                    const mappedInbound = healthDelegateService.mapDelegates(inbound);
27239
27240                    vm.inbound = mapInboundDisplayFields(mappedInbound);
27241
27242                    //console.log('vm.inbound', vm.inbound);
27243
27244                } else {
27245                    vm.inbound = [];
27246                }
27247            }
27248
27249            function loadPendingDelegates() {
27250               // console.log('calling pending api locally');
27251                vm.loading = true;
27252                vm.loadingDelegatesError = null;
27253
27254                memberDataService.getPendingDelegates(vm.memberID, vm.currentLanguage).then(function (resp) {
27255              
27256                   // console.log('pending delegates from api', resp);
27257
27258                    if (!resp || !resp.data || resp.data.length === 0) { return; };
27259
27260                    processPendingDelegates(resp.data);
27261
27262                    const pendingTotal = resp.data.length;
27263
27264                    $rootScope.$broadcast('refreshPendingTotal', pendingTotal);
27265
27266                }, function (error) {
27267                    console.error('error loading pending delegates', error);
27268                    vm.outbound = [];
27269                    vm.inbound = [];
27270                    vm.loadingDelegatesError = $filter('translate')('temp_unavailable');
27271                }).finally(function() {
27272                    vm.loading = false;
27273                });
27274            }
27275            
27276            function executeActions(requestId, action, start, end) {
27277                console.log('action', action);
27278                console.log('start date', start);
27279                console.log('end date', end);
27280                let targetRequest = {};
27281
27282                if (['reject', 'accept', 'approve'].includes(action)) {
27283                    targetRequest = vm.inbound.find(m => m.id === requestId);
27284
27285                } else if (['withdraw'].includes(action)) {
27286                    console.log('vm.inbound', vm.inbound);
27287                    console.log('requestId', requestId);
27288                    targetRequest = vm.outbound.find(m => m.id === requestId);
27289                }
27290
27291                targetRequest.effectiveDate = start;
27292
27293                targetRequest.terminationDate = end;
27294
27295                const memberName = targetRequest.memberName;
27296
27297                console.log(action + ' target request', targetRequest);
27298
27299                const supplemental = `(${start} - ${end})`;
27300
27301                const data = {
27302                    message: healthDelegateService.getConfirmation(action),
27303                    additionalInfo: memberName,
27304                    supplementalInfo: supplemental,
27305                    yesBtn: healthDelegateService.getTranslateKey(action),
27306                    noBtn: vm.translatedCancel
27307                };
27308
27309                const modalInstance = $uibModal.open({
27310                    animation: true,
27311                    templateUrl: environmentConfig.appRoot +
27312                        '/App/directives/shared/confirmation-modal/confirmation-modal.html',
27313                    windowClass: 'modalOne',
27314                    controller: 'confirmationModal',
27315                    backdrop: 'static',
27316                    keyboard: false,
27317                    controllerAs: 'confirm',
27318                    size: 'md',
27319                    resolve: {
27320                        data: function () {
27321                            return data;
27322                        }
27323                    }
27324                });
27325
27326                modalInstance.result.then(function (confirmed) {
27327
27328                    console.log('modal closed:', JSON.stringify(confirmed));
27329
27330                    if (confirmed) {
27331                        console.log('calling update status api...', requestId);
27332                        vm.loading = true;
27333
27334                        const params = healthDelegateService.getUpdateStatusParams(targetRequest, action);
27335
27336                        console.log('update status api params', params);
27337
27338                        memberDataService.UpdateDelegateStatus(params, vm.currentLanguage).then(function (resp) {
27339
27340                            console.log('update status resp', resp.data);
27341
27342                            if (!resp || !resp.data) throw 'Update failed.';
27343
27344                            if (!resp.data.updated) throw resp.data.message;
27345
27346                            authService.refreshToken().then(function (status) {
27347                                console.log('token refreshed', status);
27348
27349                                healthDelegateService.setPostUpSertMsg(resp.data.message);
27350
27351                                $location.path('/App/MyInformation/Delegates')
27352                                    .search({ tab: 'pending' });
27353                            });
27354                        },
27355                        function (error) {
27356                            Toastify({
27357                                text: error,
27358                                offset: {
27359                                    x: 0,
27360                                    y: '3em'
27361                                },
27362                                backgroundColor: '#FFDCE0',
27363                                duration: 3000,
27364                                gravity: 'top',
27365                                position: 'center',
27366                                style: {
27367                                    borderRadius: '5px',
27368                                    color: '#B50303',
27369                                    fontSize: '16px',
27370                                    fontWeight: 'bold'
27371                                }
27372                            }).showToast();
27373                        }).finally(function () {
27374                            vm.loading = false;
27375                        });
27376                    }
27377                });
27378            }
27379
27380            function assignOptions(maxAllowedDate) {
27381                //we use maxAllowedDate as a flag for request access
27382                //ideally we shall use a dedicated request type as flag
27383                //but currently request type is for display so need to be translated
27384                if (maxAllowedDate) {
27385                    return [
27386                        {
27387                            label: vm.translatedApprove,
27388                            code: 'approve'
27389                        },
27390                        {
27391                            label: vm.translatedReject,
27392                            code: 'reject'
27393                        }
27394                    ];
27395                } else {
27396                    return [
27397                        {
27398                            label: vm.translatedAccept,
27399                            code: 'accept'
27400                        },
27401                        {
27402                            label: vm.translatedReject,
27403                            code: 'reject'
27404                        }
27405                    ];
27406                }
27407            }
27408
27409            function mapInboundDisplayFields(requests) {
27410                console.log('pending inbound requests', requests);
27411                const mappedRequests = [];
27412
27413                requests.forEach(function (item) {
27414
27415                    const request = {
27416                        id: item.requestId,
27417                        status: item.status,
27418                        statusForDisplay: item.statusForDisplay,
27419                        memberName: item.memberName,
27420                        memberNumber: item.memberNumber,
27421                        effectiveDate: item.effectiveDate,
27422                        terminationDate: item.terminationDate,
27423                        options: assignOptions(item.maxAllowedDate),
27424                        cssClass: item.cssClass,
27425                        maxAllowedDate: item.maxAllowedDate
27426                    };
27427
27428                    request.parentFields = healthDelegateService.mapParentFields(item);
27429
27430                    request.childFields = healthDelegateService.mapPendingChildFields(item);
27431                    //console.log('request.childFields', request.childFields);
27432                    mappedRequests.push(request);
27433                });
27434
27435                return mappedRequests;
27436            }
27437
27438            function mapOutboundDisplayFields(requests) {
27439                console.log('pending outbound requests', requests);
27440                const mappedRequests = [];
27441
27442                requests.forEach(function(item) {
27443
27444                    const request = {
27445                        id: item.requestId,
27446                        status: item.status,
27447                        statusForDisplay: item.statusForDisplay,
27448                        memberName: item.displayName,
27449                        memberNumber: item.displayMemberId,
27450                        options: [
27451                                    {
27452                                        label: vm.translatedWithdraw,
27453                                        code: 'withdraw'
27454                                    }
27455                                ],
27456                        cssClass: item.cssClass
27457                    };
27458
27459                    request.parentFields = healthDelegateService.mapParentFields(item);
27460
27461                    console.log('request.parentFields', request.parentFields);
27462
27463                    request.childFields = healthDelegateService.mapPendingChildFields(item);
27464
27465                    mappedRequests.push(request);
27466                });
27467
27468                return mappedRequests;
27469            }
27470        }
27471
27472        return directive;
27473    }
27474})();
27475(function () {
27476    'use strict';
27477
27478    angular
27479        .module('MemberPortalDirectives')
27480        .directive('delegateRequestProcessed', delegateRequestProcessed);
27481        
27482    delegateRequestProcessed.$inject = ['environmentConfig'];
27483
27484    function delegateRequestProcessed(environmentConfig) {
27485
27486        const directive = {
27487            scope: {},
27488            restrict: 'E',
27489            templateUrl: environmentConfig.appRoot +
27490                '/App/directives/delegate-request-processed/delegate-request-processed.directive.html'
27491                + environmentConfig.cacheBust,
27492            controller: delegateRequestProcessedController,
27493            controllerAs: 'active',
27494            bindToController: true
27495        };
27496
27497        delegateRequestProcessedController.$inject = ['healthDelegateService',
27498            '$filter', 'memberDataService', '$scope', '$rootScope',
27499            '$uibModal', '$timeout', '$window', 'authStatusService',
27500            'requestedLanguageService', 'authService'];
27501
27502        function delegateRequestProcessedController(healthDelegateService,
27503            $filter, memberDataService, $scope, $rootScope,
27504            $uibModal, $timeout, $window, authStatusService,
27505            requestedLanguageService, authService) {
27506        
27507            const vm = this;
27508
27509            vm.executeActions = executeActions;
27510            vm.toggleBubble = toggleBubble;
27511
27512            init();
27513
27514            $scope.$on('language-change', function () {
27515                init();
27516            });
27517
27518            function init() {
27519
27520                const claims = authStatusService.getStatus().claims;
27521
27522                vm.memberID = claims.username;
27523
27524                vm.translatedCancel = $filter('translate')('form_cancel');
27525
27526                vm.isProxied = authStatusService.checkIfProxied();
27527
27528                vm.isEditable = !vm.isProxied;
27529                
27530
27531                vm.shouldAddBtnHidden = true;
27532
27533                vm.currentLanguage = requestedLanguageService.getCurrentLanguage();
27534                
27535                if (!vm.isProxied) {
27536
27537                    setCaregiverBtnVisibility();
27538                }
27539                
27540                loadProcessedDelegates();
27541            }
27542
27543            function toggleBubble(displayId) {
27544                toggleBubble(displayId);
27545            }
27546
27547            function setCaregiverBtnVisibility() {
27548              
27549                vm.loadingAddBtn = true;
27550
27551                healthDelegateService.isCaregiverEligible(vm.currentLanguage).then(function (resp) {
27552
27553                    vm.shouldAddBtnHidden = vm.isProxied;
27554
27555                    vm.shouldAddBtnDisabled = vm.isProxied || !resp.addCaregiver;
27556                    vm.shouldRequestAccessBtnDisabled = vm.isProxied || !resp.requestAccess;
27557
27558                    vm.addBtnDisabledMsg = resp.addCaregiverMessage;
27559                    vm.requestAccessBtnDisabledMsg = resp.requestAccessMessage;
27560
27561                    console.log('processed delegate: should disable "add caregiver" button', vm.shouldAddBtn
27561Disabled);
27562                    
27563                    console.log('processed delegate: should hide "add caregiver" button', vm.shouldAddBtnHidden);
27564                    
27565                    
27566                }).catch(function () {
27567                    vm.shouldAddBtnHidden = true;
27568                    vm.shouldRequestAccessBtnDisabled = true;
27569                }).finally(function () {
27570                    vm.loadingAddBtn = false;
27571                });
27572            }
27573
27574            function loadProcessedDelegates() {
27575
27576                vm.loading = true;
27577                vm.loadingDelegatesError = null;
27578                
27579                memberDataService.getProcessedDelegates(vm.memberID, vm.currentLanguage).then(function (resp) {
27580          
27581                  // console.log('processed delegates from api', resp.data);
27582
27583                    if (!resp || !resp.data || resp.data.length === 0) { return; };
27584
27585                    const outbound = resp.data.filter(m => m.isMyDelegate === true);
27586                    const inbound = resp.data.filter(m => m.isMyDelegate !== true);
27587                    //console.log('processed outbound from api', outbound);
27588                    //console.log('processed inbound from api', inbound);
27589                    if (outbound.length > 0) {
27590
27591                        const mappedOutbound = healthDelegateService.mapDelegates(outbound);
27592
27593                        vm.outbound = mapOutboundDisplayFields(mappedOutbound);
27594
27595                    } else {
27596                        vm.outbound = [];
27597                    }
27598
27599                    if (inbound.length > 0) {
27600                        const mappedInbound = healthDelegateService.mapDelegates(inbound);
27601
27602                        vm.inbound = mapInboundDisplayFields(mappedInbound);
27603
27604                    } else {
27605                        vm.inbound = [];
27606                    }
27607                    
27608                }).catch(function(error) {
27609                    console.error('error loading processed delegates', error);
27610                    vm.outbound = [];
27611                    vm.inbound = [];
27612                    vm.loadingDelegatesError = $filter('translate')('temp_unavailable');
27613                }).finally(function () {
27614                    vm.loading = false;
27615                });
27616            }
27617
27618            function mapOutboundDisplayFields(requests) {
27619
27620                const mappedRequests = [];
27621
27622                requests.forEach(function (item) {
27623
27624                    const request = healthDelegateService.getTransactionObj(item, ['revoke']);
27625
27626                    request.parentFields = healthDelegateService.mapParentFields(item);
27627
27628                    request.childFields = healthDelegateService.mapChildFields(item);
27629
27630                    mappedRequests.push(request);
27631                });
27632
27633                console.log('processed mapped outbound requests', mappedRequests);
27634
27635                return mappedRequests;
27636            }
27637
27638            function mapInboundDisplayFields(requests) {
27639
27640                const mappedRequests = [];
27641
27642                requests.forEach(function(item) {
27643
27644                    const request = healthDelegateService.getTransactionObj(item, ['cancel']);
27645
27646                    request.parentFields = healthDelegateService.mapParentFields(item);
27647
27648                    request.childFields = healthDelegateService.mapChildFields(item);
27649
27650                    mappedRequests.push(request);
27651                });
27652
27653               // console.log('processed mapped inbound requests', mappedRequests);
27654
27655                return mappedRequests;
27656            }
27657
27658            function executeActions(requestId, action, start, end) {
27659                console.log('start date', start);
27660                console.log('end date', end);
27661
27662                console.log('processed: action', action);
27663                console.log('vm.inbound', vm.inbound);
27664
27665                if (vm.isProxied) return;
27666
27667                let targetRequest = {};
27668
27669                if (['revoke'].includes(action)) {
27670                    targetRequest = vm.outbound.find(m => m.id === requestId);
27671                } else if (['cancel'].includes(action)) {
27672                    targetRequest = vm.inbound.find(m => m.id === requestId);
27673                }
27674
27675                targetRequest.effectiveDate = start;
27676
27677                targetRequest.terminationDate = end;
27678
27679                console.log(action + ' target request', targetRequest);
27680
27681                const memberName = targetRequest.memberName;
27682
27683                const supplemental = `(${start} - ${end})`;
27684
27685                const data = {
27686                    message: healthDelegateService.getConfirmation(action),
27687                    additionalInfo: memberName,
27688                    supplementalInfo: supplemental,
27689                    yesBtn: healthDelegateService.getTranslateKey(action),
27690                    noBtn: vm.translatedCancel
27691                };
27692
27693                console.log('processed translated', data);
27694
27695                const modalInstance = $uibModal.open({
27696                    animation: true,
27697                    templateUrl: environmentConfig.appRoot +
27698                        '/App/directives/shared/confirmation-modal/confirmation-modal.html',
27699                    windowClass: 'modalOne',
27700                    controller: 'confirmationModal',
27701                    backdrop: 'static',
27702                    keyboard: false,
27703                    controllerAs: 'confirm',
27704                    size: 'md',
27705                    resolve: {
27706                        data: function () {
27707                            return data;
27708                        }
27709                    }
27710                });
27711
27712                modalInstance.result.then(function (confirmed) {
27713
27714                    console.log('modal closed:', JSON.stringify(confirmed));
27715
27716                    if (confirmed) {
27717                        console.log('calling update status api...', requestId);
27718                        vm.loading = true;
27719
27720                        const params = healthDelegateService.getUpdateStatusParams(targetRequest, action);
27721
27722                        console.log('update status api params', params);
27723                       
27724                        memberDataService.UpdateDelegateStatus(params, vm.currentLanguage).then(function (resp) {
27725
27726                            console.log('update status resp', resp);
27727
27728                            if (!resp || !resp.data) throw 'Update failed.';
27729
27730                            if (!resp.data.updated) throw resp.data.message;
27731
27732                            authService.refreshToken().then(function (status) {
27733                                console.log('token refreshed', status);
27734                            });
27735
27736                            $timeout(function () {
27737                                $window.location.reload();
27738                                },
27739                                500);
27740                        },
27741                        function (error) {
27742                            Toastify({
27743                                text: error,
27744                                offset: {
27745                                    x: 0,
27746                                    y: '3em'
27747                                },
27748                                backgroundColor: '#FFDCE0',
27749                                duration: 3000,
27750                                gravity: 'top',
27751                                position: 'center',
27752                                style: {
27753                                    borderRadius: '5px',
27754                                    color: '#B50303',
27755                                    fontSize: '16px',
27756                                    fontWeight: 'bold'
27757                                }
27758                            }).showToast();
27759                        }).finally(function () {
27760                            vm.loading = false;
27761                        });
27762                    }
27763                });
27764            }
27765        }
27766
27767        return directive;
27768    }
27769})();
27770(function () {
27771    'use strict';
27772
27773    const app = angular.module('MemberPortalDirectives');
27774
27775    // Google Maps invokes window.gm_authFailure when the API fails to authorize
27776    // (e.g. InvalidClientIdMapError, MissingKeyMapError, RefererNotAllowedMapError).
27777    // This fires asynchronously after the script loads, so track it globally and
27778    // notify any live map directive so it can fall back to the "unavailable" state.
27779    let mapsAuthFailed = false;
27780    const mapsAuthFailureListeners = [];
27781    const previousAuthFailure = window.gm_authFailure;
27782    window.gm_authFailure = function () {
27783        mapsAuthFailed = true;
27784        window.iehpGoogleMapsAuthFailed = true;
27785        mapsAuthFailureListeners.slice().forEach(fn => fn());
27786        if (typeof previousAuthFailure === 'function') { previousAuthFailure(); }
27787    };
27788    // Shared hooks so page controllers (e.g. change-doctor) can react to a Maps auth failure.
27789    window.iehpOnGoogleMapsAuthFailure = function (fn) {
27790        mapsAuthFailureListeners.push(fn);
27791        return function unsubscribe() {
27792            const idx = mapsAuthFailureListeners.indexOf(fn);
27793            if (idx > -1) { mapsAuthFailureListeners.splice(idx, 1); }
27794        };
27795    };
27796
27797    const googleMapsLoaded = function () {
27798        return !!(window.google && window.google.maps && typeof window.google.maps.Map === 'function');
27799    };
27800
27801    app.directive('doctorSearchMap', function() {
27802        const urlBase = $('meta[name="ApplicationRoot"]').attr('content');
27803        const cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
27804
27805        const link = function (scope, element, attrs) {
27806            scope.map = null;
27807            scope.markers = [];
27808            scope.mapAvailable = false;
27809
27810            const disableMap = function (reason) {
27811                if (reason) { console.warn('Google Maps unavailable; doctor search map disabled.', reason); }
27812                scope.mapAvailable = false;
27813                scope.map = null;
27814                if (!scope.$$phase && !scope.$root.$$phase) { scope.$apply(); }
27815            };
27816
27817            if (!googleMapsLoaded() || mapsAuthFailed) {
27818                disableMap(mapsAuthFailed ? 'API authorization failed' : 'google.maps script not loaded');
27819                return;
27820            }
27821
27822            try {
27823                const mapOptions = {
27824                    //IEHP is the center
27825                    //center: new google.maps.LatLng(34.081915, -117.568733),
27826                    mapTypeControl: false,
27827                    scrollwheel: false,
27828                    streetViewControl : false
27829                };
27830                scope.map = new google.maps.Map($(element).find('.mapObject')[0], mapOptions);
27831                scope.mapAvailable = true;
27832            } catch (e) {
27833                disableMap(e);
27834                return;
27835            }
27836
27837            // Auth failures are reported after the map is constructed; downgrade if one arrives.
27838            const unsubscribe = window.iehpOnGoogleMapsAuthFailure(function () { disableMap('API authorization failed'); });
27839            scope.$on('$destroy', unsubscribe);
27840        };
27841
27842        return {
27843            scope: {},
27844            restrict: 'EA',
27845            templateUrl: urlBase + '/App/directives/doctor-search-map/doctorSearchMap.html' + cacheBust,
27846            controller: doctorSearchMapCtrl,
27847            controllerAs: 'ctrl',
27848            link: link
27849        };
27850    });
27851
27852    app.controller('doctorSearchMapCtrl', doctorSearchMapCtrl);
27853
27854    doctorSearchMapCtrl.$inject = ['$scope'];
27855
27856    function doctorSearchMapCtrl($scope) {
27857        var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
27858        
27859
27860        $scope.$on('searchResultsReady', function (event, markers) {
27861            // The results list works without the map; never let a map failure break the page.
27862            if (!$scope.mapAvailable || !$scope.map) { return; }
27863            try {
27864                renderMarkers(markers);
27865            } catch (e) {
27866                console.warn('Failed to render doctor search map markers.', e);
27867                $scope.mapAvailable = false;
27868                $scope.map = null;
27869            }
27870        });
27871
27872        function renderMarkers(markers) {
27873            const bounds = new google.maps.LatLngBounds();
27874
27875            const markersLength = $scope.markers.length;
27876            //Remove Markers from map
27877            for (let i = 0; i < markersLength; i++) {
27878                $scope.markers[i].setMap(null);
27879            }
27880            //Empty marker array
27881            $scope.markers = [];
27882
27883            if (markers.length < 1) {
27884                //Re-center the map and set zoom default
27885                //scope.map.setCenter(new google.maps.LatLng(34.081915, -117.568733)).setZoom(10);  //Change to use search input location
27886                return;
27887            }
27888
27889            const getMarkerIcon = function (type, isDefault) {
27890                var size = new google.maps.Size(24, 37, 'px', 'px'); //Our standard marker size
27891                const originPosition = [];
27892                switch ((type.trim().toLowerCase())) {
27893                case 'here': //"You are Here" Marker.
27894                    originPosition.push(new google.maps.Point(450, 0));
27895                    originPosition.push(new google.maps.Point(490, 0));
27896                    size = new google.maps.Size(39, 34, 'px', 'px'); //'Here' icon is a different size
27897                    break;
27898                case 'hosp': //Hospital : Selected Hospital
27899                    (isDefault) ? originPosition.push(new google.maps.Point(100, 0)) : originPosition.push(new google.maps.Point(125, 0));
27900                    break;
27901                case 'ph': //Pharmacy : Selected Pharmacy
27902                    (isDefault) ? originPosition.push(new google.maps.Point(150, 0)) : originPosition.push(new google.maps.Point(175, 0));
27903                    break;
27904                case 'uc': //Urgent Care : Selected Urgent Care
27905                    (isDefault) ? originPosition.push(new google.maps.Point(0, 0)) : originPosition.push(new google.maps.Point(25, 0));
27906                    break;
27907                case 'vs': //Vision Services : Selected Vision Services
27908                    (isDefault) ? originPosition.push(new google.maps.Point(250, 0)) : originPosition.push(new google.maps.Point(275, 0));
27909                    break;
27910                case 'sp': //Specialist : Selected Specialist
27911                    (isDefault) ? originPosition.push(new google.maps.Point(200, 0)) : originPosition.push(new google.maps.Point(225, 0));
27912                    break;
27913                default: //Default or PCD : Selected Default
27914                    (isDefault) ? originPosition.push(new google.maps.Point(300, 0)) : originPosition.push(new google.maps.Point(325, 0));
27915                }
27916                // google.maps.Icon literal (replaces the deprecated google.maps.MarkerImage constructor)
27917                const icon = {
27918                    url: urlBase + '/Content/Images/doctor-search-icons/ps_sprite.png',
27919                    size: size,
27920                    origin: originPosition[0]
27921                };
27922                return icon;
27923            };
27924            
27925            //Icon Setup
27926            const icon = getMarkerIcon('pcd', true);
27927
27928            const getProvidersWithSameLocation = function (markers, marker) {
27929                //TODO: Switch to using an angular filter instead of jQuery grep.
27930                const matches = $.grep(markers, function (e, i) {
27931                    return e.Lat === marker.Lat && e.Lng === marker.Lng;
27932                });
27933
27934                var providers = [];
27935                angular.forEach(matches, function (value, key, obj) {
27936                    providers.push(value.ProvId);
27937                });
27938                return providers;
27939            };
27940
27941            const setInfoWindowEvents = function (marker, map) {
27942                const infoBoxTemplate = function (marker) {
27943                    if (marker.relatedResults && marker.relatedResults.length > 1) {
27944                        //marker.properties....
27945                        return '<div class="infoBubble" style="font-size:7pt;">' +
27946                            '<div style="float:right;"><span style="font-size:15px;font-weight:bold;">' +
27947                            Number(marker.properties.Distance).toFixed(1) + '</span> mile(s)</div>' +
27948                            '<p><strong>Multiple Results: ' + marker.relatedResults.length + '</strong></p>' +
27949                            '<p>' +
27950                            '<div>' + marker.properties.StreetAddr + '</div>' +
27951                            '<div>' + marker.properties.CityStZip + '</div>' +
27952                            '</p>' +
27953                            '<div class="infoBubbleArrow"></div><div class="infoBubbleArrow_after"></div></div>';
27954                    }
27955                    else {
27956                        //marker.properties....
27957                        return '<div class="infoBubble" style="font-size:7pt;">' +
27958                            '<div style="float:right;"><span style="font-size:15px;font-weight:bold;">' +
27959                            Number(marker.properties.Distance).toFixed(1) + '</span> mile(s)</div>' +
27960                            '<p><strong>' + marker.properties.FullName + '</strong></p>' +
27961                            '<p>' +
27962                            '<div>' + marker.properties.StreetAddr + '</div>' +
27963                            '<div>' + marker.properties.CityStZip + '</div>' +
27964                            '</p>' +
27965                            '<div class="infoBubbleArrow"></div><div class="infoBubbleArrow_after"></div></div>';
27966                    }
27967                };
27968
27969                var infoBox = new InfoBox({
27970                    boxClass: 'mapInfoBox',
27971                    content: infoBoxTemplate(marker),
27972                    disableAutoPan: false,
27973                    pixelOffset: new google.maps.Size((marker.ProvType === 'Here') ? -99 : -92, -46), //(X, Y) //Need to replace static height offset with dynamic value.
27974                    zIndex: null,
27975                    boxStyle: {
27976                        width: '200px'
27977                    },
27978                    closeBoxMargin: '10px 2px 2px 2px',
27979                    closeBoxURL: '',
27980                    infoBoxClearance: new google.maps.Size(1, 1),
27981                    isHidden: false,
27982                    enableEventPropagation: false,
27983                    alignBottom: true
27984                });
27985
27986                //Marker: Mouse Over
27987                google.maps.event.addListener(marker, 'mouseover', function () {
27988                    infoBox.open(map, marker);
27989                });
27990
27991                //Marker: Mouse Out
27992                google.maps.event.addListener(marker, 'mouseout', function () {
27993                    infoBox.close();
27994                });
27995
27996                google.maps.event.addListener(marker, 'click', function () {
27997                    //Show this item and any related
27998                    const dude = $.grep($scope.$parent.searchResults, function (e, i) {
27999                        var isMatch = false;
28000                        angular.forEach(marker.relatedResults, function (relatedId, key, obj) {
28001                            if (relatedId === e.ProvId) {
28002                                isMatch = true;
28003                            }
28004                        });
28005                        return isMatch;
28006                    });
28007                    $scope.$parent.selectedResults = dude;
28008                    $scope.$apply();
28009                });
28010            };
28011
28012            //Build marker objects, and append to map.
28013            const locations = markers.map(m => m.Locations.map(l => ({...l, FullName: m.FullName}))).flat();
28014            for (let i = 0; i < locations.length; i++) {
28015                const item = locations[i];
28016                const latLng = new google.maps.LatLng(item.Lat, item.Lng);
28017
28018                //const a = $filter;
28019                const newMarker = new google.maps.Marker({
28020                    position: latLng,
28021                    map: $scope.map,
28022                    icon: icon,
28023                    objectScope: $scope,
28024                    properties: item,
28025                    relatedResults: getProvidersWithSameLocation(locations, item) //Set an array of any results with same location
28026                });
28027
28028                $scope.markers.push(newMarker);
28029
28030                bounds.extend(latLng);
28031                
28032                setInfoWindowEvents(newMarker, $scope.map); //Infobox 
28033            }
28034            
28035            $scope.map.fitBounds(bounds);
28036            $scope.map.setZoom(12);
28037        }
28038    };
28039})();
28040
28041
28042(function () {
28043    'use strict';
28044
28045    const app = angular.module('MemberPortalDirectives');
28046
28047    // Drug Form Directive
28048    app.directive('drugInput', [function () {
28049
28050        const urlBase = $('meta[name="ApplicationRoot"]').attr('content');
28051        const cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
28052        return {
28053            require: '^form',
28054            restrict: 'E',
28055            scope: false,
28056            templateUrl: urlBase + '/App/directives/drug-request/drugInput.html' + cacheBust,
28057            link: function (scope, element, attrs, formCtrl) {
28058                scope.form = formCtrl;
28059                scope.emptyDrugField = true;
28060                scope.$watchCollection('newDrug', function (newDrug, oldDrug) {
28061                    if ((newDrug.name.length > 2 || newDrug.reason.length > 2) || (scope.drugList && scope.drugList.length > 0)) {
28062                        formCtrl.$setValidity('emptyField', true);
28063                    }
28064                    else {
28065                        formCtrl.$setValidity('emptyField', false);
28066                    }
28067                });
28068                scope.SetFocus = function (inputShow, inputHide) {
28069                    angular.element('li.' + inputHide).removeClass('active');
28070                    angular.element('li.' + inputShow).addClass('active');
28071                    angular.element('div.' + inputShow).addClass('active');
28072                    angular.element('div.' + inputHide).removeClass('active');
28073                    //Set the input field to focus.
28074                    angular.element("input[name='" + inputShow + "']").focus();
28075                };
28076                scope.deleteDrug = function (index) {
28077                    if (index > -1) {
28078                        scope.drugList.splice(index, 1);
28079                        if (scope.drugList.length === 0 &&
28080                            scope.newDrug.name.length === 0 &&
28081                            scope.newDrug.reason.length === 0) {
28082
28083                            formCtrl.$setValidity('emptyField', false);
28084                        }
28085                    }
28086                };
28087            },
28088            controller: ['$scope', '$timeout', function ($scope, $timeout) {
28089                $scope.newDrug = {};
28090                $scope.newDrug.name = '';
28091                $scope.newDrug.reason = '';
28092                var tempDrug = {};
28093                var tempNewDrug = {};
28094                $scope.edit = false;
28095                $scope.currentIndex = -1;
28096                $scope.addMedication = addMedication;
28097                $scope.editReason = false;
28098                $scope.editName = false;
28099
28100                var focusMedTimeout;
28101                var focusReasonTimeout;
28102
28103                getMedicationFormularyLinkUrl();
28104
28105                function getMedicationFormularyLinkUrl() {
28106
28107                    $scope.formularyLink = 'about:blank';
28108
28109                    if ($scope.data.lobName === 'Medicare') {
28110                        $scope.formularyLink = `https://iehp.org/${$scope.languageShortHand}/members/iehp-dualchoice-cal-mediconnect-plan?target=prescription-drugs`;
28111                    }
28112                    else if ($scope.data.lobName === 'Medi-Cal') {
28113                        $scope.formularyLink = 'https://medi-calrx.dhcs.ca.gov/home/cdl';
28114                    }
28115
28116                    console.log('link', $scope.formularyLink);
28117                }
28118
28119                function addMedication(tab) {
28120                    if (tab === 'medication') {
28121                        if ($scope.newDrug.name && $scope.newDrug.name !== '') {
28122                            const temp = $scope.newDrug.reason;
28123                            $scope.newDrug.reason = '';
28124                            $scope.drugList.push($scope.newDrug);
28125                            $scope.newDrug = {};
28126                            $scope.newDrug.name = '';
28127                            $scope.newDrug.reason = temp;
28128                        }
28129                    } else {
28130                        if ($scope.newDrug.reason && $scope.newDrug.reason !== '') {
28131                            const tempName = $scope.newDrug.name;
28132                            const tempQuantity = $scope.newDrug.quantity;
28133                            $scope.newDrug.name = 'Unknown';
28134                            $scope.newDrug.quantity = 'Unknown';
28135                            $scope.drugList.push($scope.newDrug);
28136                            $scope.newDrug = {};
28137                            $scope.newDrug.name = tempName;
28138                            $scope.newDrug.quantity = tempQuantity;
28139                            $scope.newDrug.reason = '';
28140                        }
28141                    }
28142                };
28143
28144                $scope.editDrug = function (index) {
28145
28146                    if (index > -1 && $scope.edit === false) {
28147
28148                        $scope.edit = true;
28149                        $scope.currentIndex = index;
28150
28151                        //Store any new drug being added before edit.
28152                        tempNewDrug.name = $scope.newDrug.name;
28153                        tempNewDrug.quantity = $scope.newDrug.quantity;
28154                        tempNewDrug.reason = $scope.newDrug.reason;
28155
28156                        //Store Drug in array of drugs incase of cancel.
28157                        tempDrug.name = $scope.drugList[index].name;
28158                        tempDrug.quantity = $scope.drugList[index].quantity;
28159                        tempDrug.reason = $scope.drugList[index].reason;
28160
28161                        //Store the drug into the new drug field for edit.
28162                        //Add new drug back into array for binding to work.
28163                        $scope.newDrug.name = $scope.drugList[index].name;
28164                        $scope.newDrug.quantity = $scope.drugList[index].quantity;
28165
28166                        //Lock other fields from being updated and set focus.
28167                        $scope.newDrug.reason = $scope.drugList[index].reason;
28168                        $scope.drugList[index] = $scope.newDrug;
28169                        $scope.drugList[index].edit = true;
28170
28171                        if ($scope.newDrug.name === 'Unknown') {
28172
28173                            $scope.editReason = true;
28174                            //When tab is hidden use settimeout to let tab show first
28175                            //then focus.
28176                            focusReasonTimeout = $timeout(function () {
28177
28178                                $scope.SetFocus('ReasonTab', 'MedicationTab');
28179                            }, 0);
28180                        }
28181                        else {
28182
28183                            $scope.editName = true;
28184
28185                            focusMedTimeout = $timeout(function(){
28186                                $scope.SetFocus('MedicationTab', 'ReasonTab');
28187                            }, 0);
28188
28189                        }
28190                    }
28191                };
28192
28193                $scope.$on('destroy',
28194                    function () {
28195
28196                        if (focusReasonTimeout) $timeout.cancel(focusReasonTimeout);
28197
28198                        if (focusMedTimeout) $timeout.cancel(focusMedTimeout);
28199                    });
28200
28201                $scope.saveDrug = function () {
28202                    if ($scope.currentIndex > -1) {
28203                        $scope.drugList[$scope.currentIndex].edit = false;
28204                        $scope.edit = false;
28205
28206                        $scope.drugList[$scope.currentIndex].name = $scope.newDrug.name;
28207                        $scope.drugList[$scope.currentIndex].quantity = $scope.newDrug.quantity;
28208                        $scope.drugList[$scope.currentIndex].reason = $scope.newDrug.reason;
28209
28210                        $scope.newDrug = {};
28211                        $scope.newDrug.name = tempNewDrug.name;
28212                        $scope.newDrug.quantity = tempNewDrug.quantity;
28213                        $scope.newDrug.reason = tempNewDrug.reason;
28214
28215                        tempDrug = {};
28216                        tempNewDrug = {};
28217                        $scope.currentIndex = -1;
28218                        $scope.editName = false;
28219                        $scope.editReason = false;
28220                    }
28221                };
28222
28223                $scope.cancelDrug = function (index) {
28224                    $scope.drugList[index].edit = false;
28225                    $scope.edit = false;
28226                    $scope.newDrug = {};
28227
28228                    $scope.newDrug.name = tempNewDrug.name;
28229                    $scope.newDrug.quantity = tempNewDrug.quantity;
28230                    $scope.newDrug.reason = tempNewDrug.reason;
28231
28232                    $scope.drugList[index].name = tempDrug.name;
28233                    $scope.drugList[index].quantity = tempDrug.quantity;
28234                    $scope.drugList[index].reason = tempDrug.reason;
28235                    tempDrug = {};
28236
28237                    $scope.editName = false;
28238                    $scope.editReason = false;
28239                };
28240
28241                $scope.emptyField = function (form, input) {
28242
28243                    return !input && $scope.submitted;
28244                };
28245            }]
28246        };
28247    }]);
28248})();
28249
28250(function () {
28251    'use strict';
28252
28253    const app = angular.module('MemberPortalDirectives');
28254
28255    app.directive('phoneInfo', function () {
28256
28257        const urlBase = $('meta[name="ApplicationRoot"]').attr('content');
28258        const cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
28259        return {
28260            require: '^form',
28261            restrict: 'E',
28262            transclude: true,
28263            scope: {
28264                setRequired: '@?',
28265                myStyle: '@?',
28266                number: '=',
28267                phoneName: '@?'
28268            },
28269            templateUrl: urlBase + '/App/directives/drug-request/phoneInfo.html' + cacheBust,
28270            link: function (scope, elem, attrs, ctrl) {
28271                scope.phoneNumber = {
28272                    prefix: '',
28273                    areaCode: '',
28274                    lineNumber: ''
28275                };
28276                scope.invalidNumber = false;
28277                scope.successful = false;
28278                scope.autoTab = autoTab;
28279
28280                //Raise a flag for any input that isn't a number and isn't complete
28281                function buildNumber(number) {
28282
28283                    scope.number = number.areaCode + number.prefix + number.lineNumber;
28284
28285                    if (/(^$)|^[0-9]{10}$/.test(scope.number)) {
28286
28287                        scope.invalidNumber = false;
28288                        ctrl.$setValidity(scope.phoneName, true);
28289
28290                    } else {
28291
28292                        scope.invalidNumber = true;
28293                        ctrl.$setValidity(scope.phoneName, false);
28294                    }
28295                }
28296
28297                //Used to auto tab when input box of phone number is filled.
28298                function autoTab(length, name) {
28299
28300                    if (length === 3) {
28301
28302                        angular.element("input[name='" + name + "']").focus();
28303                    }
28304                }
28305
28306                //Block any input that isn't a number
28307                //Check for common invalid numbers like 555 or 123 numbers.
28308                function numberOnly(number, event) {
28309                    scope.number = number.areaCode + number.prefix + number.lineNumber;
28310                    if (/^[0-9]{0,10}$/.test(scope.number)) {
28311                        //scope.invalidNumber = false;
28312                    } else {
28313                        event.preventDefault();
28314                    }
28315                }
28316
28317                scope.$watchCollection('phoneNumber', buildNumber);
28318
28319                scope.$parent.$watch('successfulSubmit', function (value) {
28320                    if (value === true) {
28321                        scope.successful = true;
28322                    }
28323                });
28324            }
28325        };
28326    });
28327})();
28328(function () {
28329    'use strict';
28330
28331    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
28332    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
28333    const app = angular.module('MemberPortalDirectives');
28334
28335    app.directive('eligibilityHistoryTable', function () {
28336        return {
28337            scope: {
28338                eligArray: '='
28339            },
28340            restrict: 'EA',
28341            templateUrl: urlBase + '/App/directives/eligibility-history-table/eligibilityHistoryTable.html' + cacheBust,
28342            bindToController: true,
28343            controller: eligibilityHistoryTableCtrl,
28344        };
28345    });
28346
28347    app.controller('eligibilityHistoryTableCtrl', eligibilityHistoryTableCtrl);
28348
28349    eligibilityHistoryTableCtrl.$inject = ['$scope'];
28350
28351    function eligibilityHistoryTableCtrl($scope) {
28352        $(document).ready(function () {
28353            $scope.orderByField = 'EffectiveDate';
28354            $scope.reverseSort = true;
28355            $scope.sortArrowDirection = 'down';
28356            $scope.sort = sort;
28357
28358            $scope.eligArray = prepareTimeline($scope.eligArray);
28359
28360            function prepareTimeline(data) {
28361                const cleanData = preProcess(data);
28362
28363                const outputArray = [];
28364
28365                // Initialize a variable to keep track of the current period                
28366                let currentPeriod = null;
28367
28368                for (let i = 0; i < cleanData.length; i++) {
28369                    const record = cleanData[i];
28370
28371                    // If this is the first record, set the current period to this record
28372                    if (i === 0) {
28373                        currentPeriod = { ...record, TerminationDate: record.TerminationDate || 'form_current' };
28374                        continue;
28375                    }
28376
28377                    // Destructure the properties we care about from the current record
28378                    const { StatusDescription, PcpName, TerminationDate } = record;
28379
28380                    // Check if there's in change in coverage status or assigned PCP
28381                    if (StatusDescription !== currentPeriod.StatusDescription || PcpName !== currentPeriod.PcpName) {
28382                        // If they are different, push the previous period to the output array and update currentPeriod
28383                        outputArray.push({ ...currentPeriod, PcpName: currentPeriod.PcpName || 'not_available',});
28384
28385                        currentPeriod = { ...record };
28386                    } else {
28387                        currentPeriod.TerminationDate = TerminationDate || 'form_current';
28388                    }
28389                }
28390
28391                if (currentPeriod) {
28392                    outputArray.push({ ...currentPeriod, PcpName: currentPeriod.PcpName || 'not_available',});
28393                }
28394
28395                return outputArray;
28396            }
28397
28398            function preProcess(data) {
28399                // Filter out records where EffectiveDate is same or before termination date
28400                data = data.filter(row => moment(row.EffectiveDate).isSameOrBefore(row.TerminationDate) || !row.TerminationDate);
28401
28402                // Sort the filtered data by EffectiveDate ascendingly 
28403                data.sort((a, b) => moment(a.TerminationDate) - moment(b.EffectiveDate));
28404
28405                return data;
28406            }
28407
28408            function sort(field) {
28409                $scope.orderByField = field; // Set the orderBy field
28410                $scope.reverseSort = !$scope.reverseSort // Reverse sort direction
28411                $scope.sortArrowDirection = $scope.reverseSort ? 'down' : 'up'; // Update the sorting icon direction
28412            }
28413        });
28414    };
28415})();
28416(function () {
28417  'use strict';
28418
28419  var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
28420  var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
28421 
28422  const app = angular.module('MemberPortalDirectives');
28423
28424  app.directive('loginPartial', function () {
28425    return {
28426      restrict: 'EA',
28427      templateUrl:
28428        urlBase +
28429        '/App/directives/header-loginout/headerLoginOut.html' +
28430        cacheBust,
28431      controller: loginPartialCtrl,
28432      controllerAs: 'ctrl',
28433    };
28434  });
28435
28436  app.controller('loginPartialCtrl', loginPartialCtrl);
28437
28438  loginPartialCtrl.$inject = [
28439    "$scope",
28440    "$rootScope",
28441    "$route",
28442    "authService",
28443    "$location",
28444    "authStatusService",
28445    "memberDataService",
28446    "$cacheFactory",
28447    "inboxService",
28448  ];
28449
28450  function loginPartialCtrl(
28451    $scope,
28452    $rootScope,
28453    $route,
28454    authService,
28455    $location,
28456    authStatusService,
28457    memberDataService,
28458    $cacheFactory,
28459    inboxService
28460  ) {
28461        const vm = this;
28462
28463        init();
28464
28465      function init() {
28466         
28467          $rootScope.isCCA = false;
28468          $rootScope.isPrimary = false;
28469          $rootScope.isDSNP = false;
28470          $rootScope.isSPD = null;
28471          $rootScope.isMediCal = null;
28472          $rootScope.showPDHIWidget = false;
28473          $rootScope.switchProfile = false;
28474
28475          vm.unreadCount = 0;
28476
28477          updateAuthStatus();
28478      }
28479      
28480        function updateAuthStatus() {
28481      
28482            const authStatus = authStatusService.getStatus();
28483            vm.isAuth = authStatus.isAuth;
28484            vm.isDSNP = authStatus.isDSNP;
28485            vm.isOver18 = authStatus.claims?.agegroup === "18+";
28486            vm.isProxied = authStatusService.checkIfProxied();            
28487
28488            if (!vm.isAuth) return;
28489
28490
28491            setMessageCount();
28492            setMemberClassification(authStatus);
28493
28494            // must be 18+ and NOT DSNP/MEDICARE to view Icon to PDHI and Sublink "Health"                
28495            if (vm.isOver18 && !vm.isDSNP && !vm.isProxied) {
28496                $rootScope.showPDHIWidget = true;
28497            }
28498
28499            $("#divSignInSignOut").css("display", "block");
28500            $("#langCont").css("display", "block");
28501
28502            $rootScope.jti = authStatus.claims.jti;
28503      }
28504
28505      function setMemberClassification(authStatus) {
28506          
28507            $rootScope.isCCA = authStatus.isCCA;
28508            $rootScope.isPrimary = authStatus.isPrimarySubscriber;
28509            $rootScope.isDSNP = authStatus.isDSNP;
28510            $rootScope.isMediCal = authStatus.isMediCal;
28511
28512            IsMemberCurrentlySPD();
28513        }
28514      
28515        function resetMemberClassification() {
28516            $rootScope.isCCA = false;
28517            $rootScope.isPrimary = false;
28518            $rootScope.isDSNP = false;
28519            $rootScope.isSPD = null;
28520            $rootScope.isMediCal = null;
28521            $rootScope.redeterminationBannerMessage = null;
28522        }
28523
28524        function IsMemberCurrentlySPD() {
28525            memberDataService.IsMemberCurrentlySPD().then(function (response) {
28526                if (
28527                    response.status === 200 &&
28528                        typeof response.data.isSpd !== 'undefined'
28529                ) {
28530                    $rootScope.isSPD = response.data.isSpd;
28531                }
28532            });
28533        }
28534
28535      function clearAllHttpCache() {
28536            const defaultCache = $cacheFactory.get('$http');
28537            defaultCache.removeAll();
28538      };
28539
28540      function setMessageCount() {
28541
28542          const authStatus = authStatusService.getStatus();
28543
28544          if (!authStatus.isAuth) return;
28545
28546          const memberNumber = authStatus.claims.username;
28547
28548            memberDataService
28549                  .GetMessageCountAggregations(memberNumber)
28550                  .then(function (response) {
28551                      const counts = response.data;
28552
28553                      const cookieRead = inboxService.getReadBroadcastMessageCnt();
28554
28555                      const totalUnread = counts.unreadCount + counts.broadcastCount - cookieRead;
28556                      vm.unreadCount = totalUnread > 0 ? totalUnread : 0;
28557                  });
28558        }
28559
28560        $scope.$on('authStatusUpdated', function () {
28561            updateAuthStatus();
28562        });
28563
28564        vm.login = function () {
28565            $location.path(urlBase + '/App/Account/Login');
28566        };
28567
28568        vm.logOut = function () {
28569            $('#divSignInSignOut').css('display', 'none');
28570            $('#langCont').css('display', 'none');
28571
28572            authService.logOut();
28573            
28574            vm.isAuth = false;
28575            resetMemberClassification();
28576
28577            $location.path(urlBase + "/App/Account/Login");
28578        };
28579
28580        $rootScope.$on("logged-out", function () {
28581          console.log('headerloginout logged out');
28582            clearAllHttpCache();
28583            init();
28584        });
28585
28586        $scope.$on('logon-success', function () {
28587            init();
28588        });
28589
28590      $rootScope.$on('switchingProfile', function (_event, key) {
28591          const switchingProfile = key.loadingProfile;
28592
28593          return switchingProfile? null: init();
28594        });
28595
28596        $rootScope.$on('obfuscation_toggled', function () {
28597            $route.reload();
28598        });
28599
28600        $rootScope.$on("updateHeaderMessageCounts", function () {
28601            setMessageCount();
28602        });
28603    };
28604})();
28605
28606(function () {
28607    'use strict';
28608    angular
28609        .module('MemberPortalDirectives')
28610        .controller('dropCourseOptionsModal', dropCourseOptionsModal);
28611
28612    dropCourseOptionsModal.$inject = ['data', '$modalInstance',
28613        'memberDataService', 'requestedLanguageService', 'authStatusService'];
28614    function dropCourseOptionsModal(data, $modalInstance,
28615        memberDataService, requestedLanguageService, authStatusService) {
28616        var vm = this;
28617
28618        vm.data = data;
28619        vm.today = moment().format('MM/DD/YYYY');
28620        vm.lan = requestedLanguageService.getCurrentLanguage();
28621
28622        vm.onNo = onNo;
28623        vm.onYes = onYes;
28624
28625        init();
28626
28627        function init() {
28628            console.log('drop courseOptionsModal', vm.data.course);
28629            vm.isProxied = authStatusService.checkIfProxied();
28630
28631            loadLookUp();
28632        }
28633
28634        function onNo() {
28635            $modalInstance.dismiss();
28636        }
28637
28638        function onYes() {
28639            console.log('on yes', vm.dropReason);
28640
28641            if (vm.dropReason) {
28642                const param = {
28643                    RegistrationId: parseInt(vm.data.course.RegistrationId),
28644                    RegistrationStatusId: vm.RegistrationStatusId,
28645                    UpdateByUserId: 'Self',
28646                    DropReasonId: vm.dropReason
28647                };
28648
28649                memberDataService.HealthEdDropCourse(vm.data.course.RegistrationId, param)
28650                    .success(function(resp) {
28651                        console.log('course dropped', resp);
28652                        if (resp.RegistrationStatus === 'failed') {
28653                            vm.error = 'error_' + resp.ErrorList[0].Code.toString();
28654                        } else {
28655                            $modalInstance.close(resp);
28656
28657                        }
28658                    })
28659                    .error();
28660            }
28661        }
28662
28663        function loadLookUp() {
28664            memberDataService.HealthEdOptionsLookUp(vm.lan)
28665                .success(function(resp) {
28666                    console.log('look up', resp);
28667                   
28668                    for (let i in resp) {
28669                        if (resp[i].Type === 'DropReasonEnglish') {
28670                            vm.dropCourseEng = resp[i].TypeList;
28671
28672                        }
28673                        if (resp[i].Type === 'DropReasonSpanish') {
28674                            vm.dropCourseSp = resp[i].TypeList;
28675
28676                        }
28677
28678                        if (resp[i].Type === 'RegistrationStatus') {
28679                            for (let x in resp[i].TypeList) {
28680                                if (resp[i].TypeList[x].Name === 'Dropped') {
28681                                    vm.RegistrationStatusId = resp[i].TypeList[x].Id;
28682                                }
28683                            }
28684                        }
28685                    }
28686
28687                    console.log('drop options', vm.dropCourseEng);
28688                })
28689                .error();
28690
28691        }
28692    }
28693
28694})();
28695
28696(function () {
28697    'use strict';
28698
28699    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
28700    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
28701
28702    angular
28703        .module('MemberPortalDirectives')
28704        .directive('healthEdHistory', healthEdHistory);
28705
28706    healthEdHistory.$inject = [];
28707    function healthEdHistory() {
28708        const directive = {
28709            restrict: 'AE',
28710            templateUrl: urlBase + '/App/directives/health-ed-history/health-ed-history.html' + cacheBust,
28711            scope: {},
28712            controller: healthEdHistoryController,
28713            controllerAs: 'vc'
28714        };
28715
28716        healthEdHistoryController.$inject = ['$scope', 'memberDataService', '$uibModal',
28717            'requestedLanguageService', '$filter', 'authStatusService'];
28718        function healthEdHistoryController($scope, memberDataService, $uibModal,
28719            requestedLanguageService, $filter, authStatusService) {
28720            var vm = this;
28721            vm.$scope = $scope;
28722            vm.loading = false;
28723            vm.memberCourses = [];
28724            vm.droppedCourses = [];
28725            vm.waitList = [];
28726            vm.lan = requestedLanguageService.getCurrentLanguage();
28727
28728            vm.loadHistory = loadHistory;
28729            vm.onDropCourse = onDropCourse;
28730            vm.showDetail = showDetail;
28731            vm.showDocument = showDocument;
28732
28733            $scope.$on('language-change', function (event, key) {
28734                vm.lan = key;
28735                init();
28736            });
28737
28738            init();
28739
28740            function init() {
28741
28742                vm.isProxied = authStatusService.checkIfProxied();
28743
28744                console.log('healthEdHistoryController loaded');
28745                getMemberDemographics();
28746
28747            }
28748
28749            function getMemberDemographics() {
28750
28751                vm.loading = true;
28752                vm.errorMsg = null;
28753
28754                memberDataService.GetDemographics()
28755
28756                    .success(function (data) {
28757
28758                        vm.member = data;
28759
28760                        console.log('getMemberDemographics', vm.member);
28761                        vm.loadHistory(vm.lan, vm.member.SubscriberId, false);
28762                    }).error(function () {
28763                        vm.errorMsg = $filter('translate')('form_unableToRetrieveData');
28764                    }).finally(function() {
28765                        vm.loading = false;
28766                    });
28767            }
28768
28769            function showDetail(item, index) {
28770
28771                console.log('showDetail', item);
28772                if (!item.courseDetail) {
28773                    vm.errorMsg = null;
28774                    memberDataService.HealthEdCourseDetail(vm.lan, item.CourseId)
28775                        .success(function (data) {
28776
28777                            console.log('HealthEdCourseDetail', data);
28778                            if (data.StartTime) {
28779                                data.StartTime = '2019-04-13T' + data.StartTime;
28780                                data.EndTime = '2019-04-13T' + data.EndTime;
28781                            }
28782                            item.courseDetail = data;
28783
28784                            if (data.FacilityId) {
28785                                memberDataService.HealthEdFacilityDetail(data.FacilityId)
28786                                    .success(function (facility) {
28787                                        console.log('Facility detail', facility);
28788                                        item.facilityDetail = facility;
28789
28790                                    }).error(function () {
28791                                        vm.errorMsg = $filter('translate')('form_unableToRetrieveData');
28792                                    });
28793                            }
28794
28795                            memberDataService.HealthEdCourseDocuments(vm.lan, item.CourseId)
28796                                .success(function (docs) {
28797
28798                                    console.log('documents detail', docs);
28799                                    item.documents = docs;
28800
28801                                }).error(function () {
28802                                    vm.errorMsg = $filter('translate')('form_unableToRetrieveData');
28803                                });
28804
28805                            memberDataService.HealthEdMemberCorrespondence(item.MemberNumber)
28806                                .success(function (correspondence) {
28807                                    item.correspondence = [];
28808                                    for (let a in correspondence.MemberCorrespondence[0].Labels) {
28809
28810                                        if (item.CourseId === correspondence.MemberCorrespondence[0].Labels[a].CourseId) {
28811
28812                                            correspondence.MemberCorrespondence[0].Labels[a].type = $filter('translate')('Labels');
28813
28814                                            item.correspondence.push(correspondence.MemberCorrespondence[0].Labels[a]);
28815                                        }
28816                                    }
28817
28818                                    for (let b in correspondence.MemberCorrespondence[0].Confirmations) {
28819
28820                                        if (item.CourseId === correspondence.MemberCorrespondence[0].Confirmations[b].CourseId) {
28821
28822                                            correspondence.MemberCorrespondence[0].Confirmations[b].type = $filter('translate')('Confirmation');
28823
28824                                            correspondence.MemberCorrespondence[0].Confirmations[b].LabelDate =
28825                                                correspondence.MemberCorrespondence[0].Confirmations[b].ConfirmationDate;
28826
28827                                            item.correspondence.push(correspondence.MemberCorrespondence[0].Confirmations[b]);
28828                                        }
28829                                    }
28830
28831                                    for (let c in correspondence.MemberCorrespondence[0].Reminders) {
28832
28833                                        if (item.CourseId === correspondence.MemberCorrespondence[0].Reminders[c].CourseId) {
28834
28835                                            correspondence.MemberCorrespondence[0].Reminders[c].type = $filter('translate')('Reminder');
28836
28837                                            correspondence.MemberCorrespondence[0].Reminders[c].LabelDate =
28838                                                correspondence.MemberCorrespondence[0].Reminders[c].ReminderDate;
28839
28840                                            item.correspondence.push(correspondence.MemberCorrespondence[0].Reminders[c]);
28841                                        }
28842                                    }
28843
28844                                }).error(function () {
28845                                    vm.errorMsg = $filter('translate')('form_unableToRetrieveData');
28846                                });
28847
28848                        }).error(function () {
28849                            vm.errorMsg = $filter('translate')('form_unableToRetrieveData');
28850                        });
28851                }
28852            }
28853            
28854            function loadHistory(languageCode, memberId, excludeDroppedMembers) {
28855
28856                vm.waitList = [];
28857
28858                vm.errorMsg = null;
28859
28860                memberDataService.HealthEdHistory(languageCode, memberId, excludeDroppedMembers)
28861                    .success(function (resp) {
28862
28863                        console.log('HealthEdHistory', resp);
28864
28865                        for (let i in resp) {
28866                            if (resp[i].RegistrationStatus !== 'Dropped') {
28867                                resp[i].title = 'Course History';
28868                                resp[i].showDate = resp[i].RegistrationDate;
28869                            }
28870
28871                            if (resp[i].RegistrationStatus === 'Dropped') {
28872                                resp[i].title = 'Dropped Course';
28873                                resp[i].showDate = resp[i].UpdateDate;
28874                            }
28875                        }
28876
28877                        vm.allCourse = resp;
28878                        memberDataService.HealthEdCourseWaitlist(languageCode, memberId)
28879                            .success(function (wl) {
28880                                console.log('wait list', wl);
28881                                vm.memberCourses.isCustomHeaderOpen = vm.memberCourses.length > 0 ? true : false;
28882                                vm.droppedCourses.isCustomHeaderOpen = vm.memberCourses.length > 0 ? true : false;
28883                                vm.waitList = wl;
28884                                vm.loading = false;
28885
28886                            }).error(function() {
28887                                vm.errorMsg = $filter('translate')('form_unableToRetrieveData');
28888                            });
28889                    })
28890                    .error(function() {
28891                        vm.errorMsg = $filter('translate')('form_unableToRetrieveData');
28892                    });
28893            };
28894
28895            function onDropCourse(course) {
28896                console.log('onDropCourse', course);
28897
28898                var data = {
28899                    course: course
28900                };
28901
28902                const modalInstance = $uibModal.open({
28903                    animation: true,
28904                    templateUrl: urlBase + '/App/directives/health-ed-history/drop-course-options-modal.html' + cacheBust,
28905                    windowClass: '',
28906                    controller: 'dropCourseOptionsModal',
28907                    scope: $scope,
28908                    controllerAs: 'vc',
28909                    size: 'md',
28910                    resolve: {
28911                        data: function () {
28912                            return data;
28913                        }
28914                    }
28915                });
28916
28917                modalInstance.result.then(function (data) {
28918                    console.log('modal closed', data);
28919                    vm.loadHistory(vm.lan, vm.member.SubscriberId, false);
28920
28921                }, function () {
28922                    console.log('Modal cancel at: ' + new Date());
28923                });
28924            }
28925
28926            function showDocument(doc) {
28927                console.log('showDocument', doc);
28928                if (doc.DocumentType.toLowerCase() === 'url') {
28929                    window.open(doc.Path, '_blank');
28930                } else {
28931                    memberDataService.GetHealthEdDocument(doc.CourseId, doc.Path)
28932                        .success(function (blob) {
28933
28934                            console.log('got file', blob);
28935                            const fileUrl = URL.createObjectURL(blob);
28936
28937                            const link = document.createElement('a');
28938                            document.body.appendChild(link);
28939                            link.href = fileUrl;
28940                            link.download = doc.Path;
28941                            link.click();
28942                            document.body.removeChild(link);
28943                        }).error(function () {
28944                            vm.errorDownloadMsg = 'Error downloading your file.';
28945                        });
28946                }
28947            }
28948        }
28949
28950        return directive;
28951
28952    }
28953
28954})();
28955
28956(function () {
28957    'use strict';
28958
28959    angular
28960        .module('MemberPortalDirectives')
28961        .directive('iehpLegacyTabs', iehpLegacyTabs);
28962
28963    iehpLegacyTabs.$inject = ['environmentConfig'];
28964
28965    function iehpLegacyTabs(environmentConfig) {
28966
28967        const directive = {
28968            scope: {
28969                collections: '=',
28970                selectTabFn: '&'
28971            },
28972            restrict: 'E',
28973            templateUrl: environmentConfig.appRoot + '/App/directives/iehp-legacy-tabs/iehp-tabs.directive.html' + environmentConfig.cacheBust,
28974            controller: iehpLegacyTabsController,
28975            controllerAs: 'tabs',
28976            bindToController: true
28977        };
28978
28979        iehpLegacyTabsController.$inject = [];
28980
28981        function iehpLegacyTabsController() {
28982         
28983            const vm = this;
28984            //console.log('collections', vm.collections);
28985            vm.selectTab = selectTab;
28986
28987            init();
28988
28989            function init() {
28990                vm.activeTab = null;
28991            }
28992
28993            function selectTab(selectedTab) {
28994
28995                console.log('selectedTab', selectedTab);
28996
28997                if (selectedTab.isDisabled) return;
28998                
28999                // Deactivate all tabs
29000                angular.forEach(vm.collections, function (tab) {
29001                    tab.active = false;
29002                });
29003
29004                // Activate selected tab
29005                selectedTab.active = true;
29006                vm.activeTab = selectedTab;
29007                vm.selectTabFn({ lob: selectedTab });
29008            }
29009
29010        }
29011
29012        return directive;
29013    }
29014})();
29015(function () {
29016    'use strict';
29017
29018    const app = angular.module('MemberPortalDirectives');
29019
29020    app.directive('yellowCard', function () {
29021        const urlBase = $('meta[name="ApplicationRoot"]').attr('content');
29022        const cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
29023
29024        return {
29025            scope: {
29026                data : '='
29027            },
29028            restrict: 'EA',
29029            templateUrl: urlBase + '/App/directives/immunizations/yellowCardTable.html' + cacheBust,
29030            bindToController: true,
29031            controller: yellowCardController,
29032            controllerAs: 'ctrl'
29033        };
29034    });
29035
29036    app.controller('yellowCardController', yellowCardController);
29037
29038    yellowCardController.$inject = ['$scope'];
29039
29040    function yellowCardController($scope) {
29041        $scope.errorMessage = '';
29042        $scope.infoMessage = '';
29043
29044        $scope.loading = false; 
29045        $scope.TodaysDate = new Date();
29046
29047        $scope.urlBase = $('meta[name="ApplicationRoot"]').attr('content');
29048
29049        $scope.printYellowCard = function () {
29050
29051            var yc = $('#yellowCardCont').html();
29052            yc = yc.replace('reducefontatprinttime', 'printableFontSize');
29053            yc = "<div class='printableFontSize'>" + yc.replace('non-printable','" style="display:none !important') + '</div>';
29054            yc = $('<div/>').append(yc).html();
29055        
29056            $scope.PrintPopup(yc); 
29057        };
29058
29059        $scope.PrintPopup = function(data) {
29060            const mywindow = window.open('', 'ImmunizationRecord', 'height=400,width=600');
29061
29062           mywindow.document.write('<html><head><base href="' +
29063               document.domain +
29064               '"><title>Immunization Record</title>');
29065
29066           mywindow.document.write('</head><body>');
29067           mywindow.document.write(data);
29068           mywindow.document.write('</body></html>');
29069           console.log(mywindow.document);
29070           mywindow.document.close(); // necessary for IE >= 10
29071           mywindow.focus(); // necessary for IE >= 10
29072
29073           mywindow.print();
29074
29075           return true;
29076       };
29077    };
29078})();
29079
29080
29081
29082(function () {
29083    'use strict';
29084
29085    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
29086    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
29087
29088    angular.module('MemberPortalDirectives').directive('languageSelector', languageSelector);
29089
29090    function languageSelector() {
29091
29092        const directive = {
29093            restrict: 'EA',
29094            templateUrl:
29095                urlBase + '/App/directives/language-selector/language-selector.html' + cacheBust,
29096            controller: languageSelectorController,
29097            controllerAs: 'vc'
29098        };
29099
29100        languageSelectorController.$inject = ['requestedLanguageService',
29101            'authStatusService', '$scope', '$translate', '$rootScope'];
29102
29103        function languageSelectorController(requestedLanguageService,
29104            authStatusService, $scope, $translate, $rootScope) {
29105            const vm = this;
29106
29107            vm.$scope = $scope;
29108            vm.swapLanguage = swapLanguage;
29109
29110            init();
29111
29112            function init() {
29113                //get all the available languages
29114                let languages = requestedLanguageService.getAllLanguages();
29115                //get the selected language from cookie
29116                let currentLanguage = requestedLanguageService.getCurrentLanguage();
29117
29118                const authStatus = authStatusService.getStatus();
29119                // limit languages to english/spanish for cca
29120                if(authStatus.isCCA) {
29121                    languages = requestedLanguageService.getEnglishSpanishLanguages();
29122
29123                    const englishSpanishLanguages = languages.map(m => m.code);
29124
29125                    // default to english for CCA members
29126                    if (!englishSpanishLanguages.includes(currentLanguage)) {
29127
29128                        const englishCode = requestedLanguageService.getEnglishCode();
29129
29130                        requestedLanguageService.changeLanguage(englishCode);
29131                        currentLanguage = englishCode;
29132                    }
29133                }
29134
29135                vm.languages = languages;
29136                
29137                vm.pageLan = currentLanguage;
29138
29139                $translate.use(vm.pageLan);
29140
29141                //console.log('language set to', vm.pageLan);
29142            }
29143
29144            $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
29145            
29146            function swapLanguage(newLang) {
29147                //set cookie and $rootScope
29148                requestedLanguageService.changeLanguage(newLang);
29149
29150                vm.pageLan = newLang;
29151                
29152                $scope.currentLanguage = newLang;
29153            }
29154
29155            $rootScope.$on('switchingProfile', function (_event, key) {
29156
29157                const switchingProfile = key.loadingProfile;
29158
29159                return switchingProfile ? null : init();
29160            });
29161        }
29162
29163        return directive;
29164    }
29165})();
29166(function () {
29167    'use strict';
29168    angular
29169        .module('MemberPortalDirectives')
29170        .controller('leavePageModal', leavePageModal);
29171
29172    leavePageModal.$inject = ['data', '$modalInstance', 'requestedLanguageService'];
29173    function leavePageModal(data, $modalInstance, requestedLanguageService) {
29174        var vm = this;
29175
29176        vm.data = data;
29177        vm.lan = requestedLanguageService.getCurrentLanguage();
29178        vm.onNo = onNo;
29179        vm.onYes = onYes;
29180        
29181        function onNo() {
29182            $modalInstance.dismiss();
29183        }
29184
29185        function onYes() {
29186            $modalInstance.close(true);
29187        }
29188    }
29189})();
29190(function () {
29191    'use strict';
29192
29193    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
29194    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
29195
29196    const app = angular.module('MemberPortalDirectives');
29197
29198    app.directive('myDoctor', function () {
29199        return {
29200            scope: {
29201                elig: '='
29202            },
29203            restrict: 'EA',
29204            templateUrl: urlBase + '/App/directives/my-doctor/myDoctor.html' + cacheBust,
29205            bindToController: true,
29206            controller: 'myDoctorCtrl',
29207            controllerAs: 'ctrl'
29208        };
29209    });
29210
29211    app.controller('myDoctorCtrl', [ function () {
29212
29213    }]);
29214})();
29215(function () {
29216    'use strict';
29217    angular
29218        .module('MemberPortalDirectives')
29219        .controller('addressValidationModal', addressValidationModal);
29220
29221    addressValidationModal.$inject = ['data', '$modalInstance', 'requestedLanguageService'];
29222    function addressValidationModal(data, $modalInstance, requestedLanguageService) {
29223        var vm = this;
29224
29225        vm.data = data;
29226        vm.lan = requestedLanguageService.getCurrentLanguage();
29227        vm.onNo = onNo;
29228        vm.onYes = onYes;
29229        vm.close = close;
29230
29231        init();
29232
29233        function init() {
29234            vm.error = null;
29235        }
29236
29237        function onNo() {
29238            $modalInstance.dismiss();
29239        }
29240
29241        function onYes() {
29242            $modalInstance.close(true);
29243
29244        }
29245
29246        function close() {
29247            $modalInstance.dismiss();
29248        }
29249    }
29250
29251})();
29252(function () {
29253    'use strict';
29254    angular
29255        .module('MemberPortalDirectives')
29256        .controller('updateContactInfoModal', updateContactInfoModal);
29257
29258    updateContactInfoModal.$inject = ['data', '$modalInstance', 'requestedLanguageService',
29259        '$scope', 'authStatusService', '$filter', '$sce'];
29260    function updateContactInfoModal(data, $modalInstance, requestedLanguageService,
29261        $scope, authStatusService, $filter, $sce) {
29262        var vm = this;
29263        
29264        vm.ncqaCategory = data.category;
29265        vm.config = data.configuration;
29266        vm.answerLabel = {};
29267
29268        console.log('NCQA questions received in modal', vm.ncqaCategory);
29269        //console.log('config', vm.config);
29270
29271        vm.categoryType = vm.ncqaCategory ? vm.ncqaCategory.CategoryId :
29272            $scope.initEditStateCached.CategoryId;
29273
29274        //console.log('categoryType, only ncqa use data.category to store the view model: ',vm.categoryType);
29275
29276        //numeric categoryType => NCQA-style form (ethnicity, language, SOGI, ADA, etc.)
29277        vm.formName = angular.isNumber(vm.categoryType) ? 'ncqa' : vm.categoryType;
29278
29279        vm.lan = requestedLanguageService.getCurrentLanguage();
29280        vm.onNo = onNo;
29281        vm.onYes = onYes;
29282        vm.close = close;
29283        vm.hasAnswers = data.hasAnswers;
29284        vm.checkOther = checkOther;
29285        vm.showResults = showResults;
29286        vm.onSelectPredefinedAnswer = onSelectPredefinedAnswer;
29287        vm.getAnswerLabel = getAnswerLabel;
29288        vm.makeAutocompleteGone = makeAutocompleteGone;
29289        vm.parseAsHtml = parseAsHtml;
29290        
29291        init();
29292
29293        $scope.$watch('editModels',
29294            function () {
29295                if (!angular.equals($scope.initEditStateCached, $scope.editModels)) {
29296                    vm.hasAnswers = true;
29297                    console.log('edit models changed');
29298                }
29299            }, true);
29300
29301        function init() {
29302
29303            vm.errorMsg = '';
29304
29305            const authStatus = authStatusService.getStatus();
29306
29307            if (!authStatus || authStatus.claims.internaluser === 'N/A') return;
29308
29309            //if we impersonate a member account, we want to disable the change email feature 
29310            vm.shouldDisableEmail = authStatus.claims.internaluser.startsWith('i');
29311            console.log('if internal user, we should disable the email update feature', vm.shouldDisableEmail);
29312        }
29313
29314        function makeAutocompleteGone($event) {
29315            //close the suggestion panel when clicking outside of it
29316          
29317            const containTarget = $('.ncqa-field').find($event.target).length > 0;
29318            if (!containTarget) {
29319                $('.autocomplete-result').hide();
29320            }
29321        }
29322
29323        function lookupOptionIdOtherByQuestionId(questionId) {
29324            const question = vm.ncqaCategory.Questions
29325                .find(m => m.QuestionId === questionId);
29326
29327            //console.log('question', question);
29328            if (!question) return null;
29329
29330            if (!question.Options) return null;
29331
29332            const option = question.Options.find(m => m.IsOther === true);
29333
29334            if (!option) return null;
29335
29336            return option.OptionId;
29337        }
29338
29339        function lookupOptionIdByLabel(questionId, optionLabel) {
29340            //console.log('questionId', questionId);
29341            //console.log('optionLabel', optionLabel);
29342            const question = vm.ncqaCategory.Questions
29343                .find(m => m.QuestionId === questionId);
29344            //console.log('question', question);
29345            if (!question) return null;
29346
29347            if (!question.Options) return null;
29348
29349            const option = question.Options.find(m => m.OptionLabel === optionLabel);
29350
29351            if (!option) return null;
29352
29353            return option.OptionId;
29354        }
29355
29356        function getAnswerLabel(questionId) {
29357            //on page load, we need to prepopulate an autocomplete answer field.
29358            let answerLabel = '';
29359
29360            if ($scope.editModels[`other${questionId}`]) {
29361               // console.log('other', $scope.editModels['other' + questionId]);
29362                answerLabel = $scope.editModels[`other${questionId}`];
29363
29364            } else if ($scope.editModels[questionId]) {
29365                answerLabel = lookupOptionLabelById(questionId, $scope.editModels[questionId]);
29366
29367            } else {
29368                console.log('no answer');
29369            }
29370         
29371            return answerLabel;
29372        }
29373
29374        function lookupOptionLabelById(questionId, optionId) {
29375
29376            const question = vm.ncqaCategory.Questions.find(m => m.QuestionId === questionId);
29377            //console.log('question', question);
29378            if (!question) return null;
29379
29380            return question.Options.find(m => m.OptionId === optionId).OptionLabel;
29381        }
29382
29383        function onSelectPredefinedAnswer(selectedTerm, questionId) {
29384            const $answerField = $(`#answer${questionId}`);
29385            const $suggestionPanel = $(`#result${questionId}`);
29386
29387            //save the selected option from the dropdown list
29388            let result = selectedTerm.target.innerText;
29389            console.log('result', result);
29390            const notFound = $filter('translate')('not_found_from_list');
29391           if (result.includes(notFound) ||
29392                !lookupOptionIdByLabel(questionId, result)) {
29393                console.log('answer not found');
29394                saveOtherField(questionId);
29395
29396                $suggestionPanel.toggle(false);
29397              
29398                return;
29399           }
29400           else if (result.length > 20) {
29401               console.log('input length over 20');
29402               result = result.substring(0, 20);
29403               $answerField.val(result);
29404
29405               saveOtherField(questionId);
29406
29407               $suggestionPanel.toggle(false);
29408           }
29409            else {
29410                console.log('no match');
29411                $answerField.val(result);
29412                $suggestionPanel.toggle(false);
29413
29414                $scope.editModels[questionId] = lookupOptionIdByLabel(questionId, result);
29415                $scope.editModels[`other${questionId}`] = null;
29416            }
29417        }
29418
29419        function saveOtherField(questionId) {
29420
29421            const $answerField = $(`#answer${questionId}`);
29422            console.log('$answerField.val().trim()', $answerField.val().trim());
29423
29424            $answerField.removeClass('ng-valid ng-invalid invalid-entry').addClass('ng-valid');
29425
29426            const hasSpecialChar =
29427                /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/g.test($answerField.val().trim());
29428
29429            if (hasSpecialChar) {
29430                console.log('input invalid');
29431                $answerField.removeClass('ng-valid').addClass('ng-invalid invalid-entry');
29432                return;
29433            } 
29434
29435            $scope.editModels[`other${questionId}`] = $answerField.val().trim();
29436
29437            $scope.editModels[questionId] = lookupOptionIdOtherByQuestionId(questionId);
29438
29439        }
29440
29441        function autocompleteMatch(input, optionLabels) {
29442            if (!input) {
29443                return optionLabels;
29444            }
29445            
29446            return optionLabels.filter((m) => m.toLowerCase().includes(input.toLowerCase()));
29447        }
29448
29449        function showResults(questionId, options) {
29450
29451            const $answerField = $(`#answer${questionId}`);
29452            const $suggestionPanel = $(`#result${questionId}`);
29453
29454            const optionLabels = options.filter(m => m.IsOther !== true).map(m => m.OptionLabel);
29455            //for autocomplete, we don't want to display "if other, pls specify..." option
29456           
29457            $suggestionPanel.html('');
29458
29459            const query = $answerField.val();
29460            console.log('query', query);
29461
29462            let list = '';
29463
29464            const terms = autocompleteMatch(query, optionLabels);
29465
29466            console.log('matching terms', terms);
29467
29468            const hasError = $answerField.hasClass('invalid-entry');
29469
29470            if (terms.length === 0 && !hasError) {
29471                list = '<li>' + $filter('translate')('not_found_from_list') + '</li>'
29472                saveOtherField(questionId);
29473            } else {
29474                $answerField.removeClass('ng-valid ng-invalid invalid-entry').addClass('ng-valid');
29475
29476                terms.forEach(function (term) {
29477                    list += `<li>${term}</li>`;
29478                });
29479            }
29480
29481            $suggestionPanel.toggle(list.length > 0);
29482
29483            $suggestionPanel.html(list);
29484        }
29485
29486        function checkOther(optionId, questionId) {
29487            //control other field behavior based on user selection
29488            const question = vm.ncqaCategory.Questions.find(m => m.QuestionId === questionId);
29489
29490            const option = question.Options.find(m => m.OptionId === optionId);
29491     
29492            if (option.IsOther === false) {
29493                //if a user selects another option, 
29494                //the other field is no longer assoc.with the new option
29495                //so the "other" field vm needs to be reset
29496                
29497                $scope.editModels[`other${questionId}`] = null;
29498                //console.log('set other view model to null, question id:', questionId);
29499            } else {
29500                $scope.editModels[`other${questionId}`] = '';
29501               // console.log('set other view model to empty, question id:', questionId);
29502            }
29503        }
29504
29505        function onNo() {
29506            $modalInstance.dismiss();
29507        }
29508
29509        function onYes(formName) {
29510            //console.log('submitting form');
29511            if (formName.$valid) {
29512                console.log('form is valid', formName);
29513                $modalInstance.close(vm.categoryType);
29514            } else {
29515                console.log('form is invalid');
29516                vm.errorMsg = 'Please check the form and try again.';
29517
29518                return;
29519            }
29520        }
29521
29522        function close() {
29523            $modalInstance.dismiss();
29524        }
29525
29526        function parseAsHtml(str) {
29527            str = str.replace(/\\n/g, '<br>');
29528            return $sce.trustAsHtml(str);
29529        }
29530    }
29531})();
29532(function () {
29533'use strict';
29534
29535    const app = angular.module('MemberPortalDirectives');
29536
29537    app.controller('notificationCtrl', notificationCtrl);
29538
29539    notificationCtrl.$inject = ['data', '$modalInstance', 'notificationService'];
29540
29541    function notificationCtrl(data, $modalInstance, notificationService) {
29542
29543        const vm = this;
29544
29545        vm.data = data;
29546
29547        vm.onYes = haveRead;
29548
29549        vm.onClose = close;
29550
29551        vm.onYesAll = onYesAll;
29552
29553        vm.shouldShow = notificationService.shouldShow;
29554
29555        function onYesAll() {
29556            console.log("vm.data.displayList", vm.data.displayList);
29557            notificationService.saveReadMessages(vm.data.displayList);
29558
29559            vm.data.displayList.length = 0;
29560
29561            $modalInstance.close();
29562        }
29563
29564        function haveRead(message) {
29565            console.log("auto display have read");
29566            vm.data.displayList = vm.data.displayList.filter(m => m.AlertType !== message.AlertType);
29567
29568            notificationService.saveReadMessage(message);
29569
29570            if (vm.data.displayList.length === 0) {
29571                $modalInstance.close();
29572            }
29573        }
29574
29575        function close() {
29576            $modalInstance.dismiss();
29577        }
29578    };
29579})();
29580(function () {
29581
29582    'use strict';
29583
29584    const app = angular.module('MemberPortalDirectives');
29585
29586    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
29587    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
29588
29589    app.directive('notificationsPanel', function () {
29590        return {
29591            scope: {
29592                displayUnread: '=?'
29593            },
29594            restrict: 'EA',
29595            bindToController: true,
29596            templateUrl: urlBase + '/App/directives/notifications-panel/notificationsPanel.html' + cacheBust,
29597            controller: notificationsPanelCtrl,
29598            controllerAs: 'ctrl'
29599        };
29600    });
29601
29602    app.controller('notificationsPanelCtrl', notificationsPanelCtrl);
29603
29604    notificationsPanelCtrl.$inject = [
29605        '$scope',
29606        '$rootScope',
29607        'featureFlagService',
29608        'memberDataService',
29609        'requestedLanguageService',
29610        'authStatusService',
29611        '$uibModal',
29612        'notificationService'
29613        ];
29614
29615    function notificationsPanelCtrl(
29616        $scope,
29617        $rootScope,
29618        featureFlagService,
29619        memberDataService,
29620        requestedLanguageService,
29621        authStatusService,
29622        $uibModal,
29623        notificationService) {
29624
29625        var vm = this;
29626        vm.alertList = [];
29627        vm.displayList = [];
29628        vm.errorMessage = '';
29629        vm.loading = false;
29630        vm.isCCA = $rootScope.isCCA || false;
29631        
29632        if($rootScope.isSPD !== null) {
29633            init();
29634        } else {
29635            memberDataService.IsMemberCurrentlySPD()
29636                .then(function (response) {
29637                    if(response.status === 200 && typeof response.data.isSpd !== 'undefined') {
29638                        $rootScope.isSPD = response.data.isSpd;
29639                        init();
29640                    }
29641                });
29642        }
29643
29644        function init() {
29645            vm.currentLanguage = requestedLanguageService.getCurrentLanguage();
29646
29647            vm.bannerImgSrc = `Content/Images/AER_${vm.currentLanguage}.png`;
29648
29649            const authStatus = authStatusService.getStatus();
29650
29651            if (!authStatus.isAuth) return;
29652
29653            vm.isCCA = authStatus.isCCA || false;
29654            const lob = authStatus.claims.lob;
29655            getAnnouncements(lob, vm.currentLanguage);
29656            vm.maxDisplayed = 4;
29657        }
29658
29659        /*************************************************************
29660         * * * * * * * * * * * * Culture Switching * * * * * * * * * *
29661         *************************************************************/
29662        $scope.$on('language-change', function (event, key) {
29663
29664            vm.currentLanguage = key;
29665
29666            vm.bannerImgSrc = `Content/Images/AER_${vm.currentLanguage}.png`;
29667
29668            vm.alertList.length = 0;
29669
29670            const claims = authStatusService.getStatus().claims;
29671
29672            const lob = claims.lob;
29673            
29674            getAnnouncements(lob, vm.currentLanguage);
29675
29676        });
29677
29678        vm.showAll = function(displayed) {
29679            if (!displayed || displayed.length === 0) return;
29680
29681            const data = {
29682                displayList: displayed
29683            }
29684
29685            $uibModal.open({
29686                backdrop: 'static',
29687                animation: true,
29688                templateUrl: urlBase +
29689                    '/App/directives/notifications-panel/modals/show-all-modal.html' +
29690                    cacheBust,
29691                controller: 'notificationCtrl',
29692                scope: $scope,
29693                controllerAs: 'vc',
29694                size: 'md',
29695                resolve: {
29696                    data: function () {
29697                        return data;
29698                    }
29699                }
29700            });
29701        }
29702
29703        vm.showModal = function (displayed) {
29704            let displayList = [];
29705
29706            const isMultipleItems = angular.isArray(displayed);
29707
29708            if (!isMultipleItems) {
29709                displayList.push(displayed);
29710            } else {
29711                displayList = displayed;
29712            }
29713
29714            openModal(displayList);
29715            
29716        };
29717
29718        function displayNotifications(notifications) {
29719            
29720            notifications.forEach(function(item) {
29721                if (notificationService.shouldShow(item.AlertType)) {
29722                    vm.displayList.push(item);
29723                }
29724            });
29725
29726            if (vm.displayList.length > 0 && !!vm.displayUnread) {
29727                vm.showModal(vm.displayList);
29728            }
29729        }
29730
29731        function openModal(displayList) {
29732
29733            if (!displayList || displayList.length === 0) return;
29734
29735            const data = {
29736                displayList: displayList
29737            }
29738
29739            $uibModal.open({
29740                backdrop: 'static',
29741                animation: true,
29742                templateUrl: urlBase +
29743                    '/App/directives/notifications-panel/modals/notification-modal.html' +
29744                    cacheBust,
29745                controller: 'notificationCtrl',
29746                scope: $scope,
29747                controllerAs: 'vc',
29748                size: 'md',
29749                resolve: {
29750                    data: function () {
29751                        return data;
29752                    }
29753                }
29754            });
29755        }
29756        
29757        function getAlerts(language, useCache) {
29758            vm.loading = true;
29759            
29760            memberDataService.GetMemberNotifications(language, useCache)
29761                .success(function (data) {                    
29762                    vm.loading = false;
29763                    
29764                    if (data.length > 0) {
29765
29766                        // Filter out the Health Appraisal alerts for DSNP members
29767                        // We chose to do this on the UI because we couldn't scope what systems are using the endpoint.
29768                        if ($rootScope.isDSNP) {
29769                            data = data.filter(x => x.AlertType !== 'HRACN')
29770                        }
29771
29772                        data = notificationService.sortAlerts(data);
29773                        vm.alertList.push(...data);
29774                    }
29775
29776                    if ($rootScope.isDSNP || $rootScope.isSPD) {
29777                        // TODO: WEX will be opened to all DSNP members. is this flag still needed?
29778                      $rootScope.isSsbciOptIn = notificationService.isSsbciOptIn();
29779                      verifyHRANotice(language);
29780                    }
29781
29782                    $scope.errorMessage = '';
29783                }).error(function () {
29784                    vm.loading = false;
29785                    //vm.errorMessage = $filter('translate')('form_unableToRetrieveData');
29786                });
29787        };
29788
29789        function getAnnouncements(lineOfBusiness, language, useCache) {
29790
29791            vm.loading = true;
29792            vm.haveNotifications = false;
29793
29794            const params = {
29795                lob: lineOfBusiness,
29796               type: ''
29797            }
29798
29799            if (featureFlagService.isFeatureEnabled('magellan')) {
29800               // console.log('magellan feature flag is on, skipping announcement.');
29801                getAlerts(vm.currentLanguage, useCache);
29802
29803            } else {
29804                memberDataService.GetAnnouncements(params, language, useCache)
29805                    .success(function (announcements) {
29806                        if (announcements.length > 0) {
29807                            vm.haveNotifications = true;
29808
29809                            const convertedAlerts = announcements.map(notificationService.convertToAlert);
29810
29811                            vm.alertList.push(...convertedAlerts);
29812
29813                            displayNotifications(convertedAlerts);
29814                        }
29815                        getAlerts(vm.currentLanguage, useCache);             
29816
29817                        $scope.errorMessage = '';
29818
29819                    }).error(function () {
29820                        console.error("announcement retrieval error out");
29821                        vm.loading = false;
29822                    });
29823            }
29824        }
29825
29826        function verifyHRANotice() {
29827            vm.loading = true;
29828            // check if HRA survey has been taken
29829            memberDataService.GetHRASurveyStatus()
29830              .success(function (HRAData) {
29831                // if url property is NOT null, then survey is available and needs to be taken
29832                if(!!HRAData.url) {
29833                  // create HRA Notification
29834                  vm.alertList.push({
29835                    AlertTitle: notificationService.getAlertTitle({ AlertType: 'HRA' }),
29836                    AlertDescription: notificationService.getAlertDescription({ AlertType: 'HRA' }),
29837                    AlertType: "HRA",
29838                    CreateDate: new Date()
29839                  });
29840                  vm.haveNotifications = true;
29841                  vm.alertList = notificationService.sortAlerts(vm.alertList);
29842                }
29843
29844                vm.loading = false;
29845              })
29846            .error(function () {
29847                console.error('unable to check HRA status');
29848                vm.loading = false;
29849            })
29850        }
29851    };
29852})();
29853(function () {
29854    'use strict';
29855
29856    const app = angular.module('MemberPortalDirectives');
29857
29858    app.controller('showAllCtrl', showAllCtrl);
29859
29860    showAllCtrl.$inject = ['data', '$modalInstance'];
29861
29862    function showAllCtrl(data, $modalInstance) {
29863
29864        const vm = this;
29865
29866        vm.data = data;
29867
29868        vm.onClose = close;
29869        
29870        function close() {
29871            
29872            $modalInstance.dismiss();
29873        }
29874    };
29875})();
29876(function () { 
29877    'use strict';
29878
29879    const app = angular.module('MemberPortalDirectives');
29880
29881    app.directive('comparePasswordTo', function () {
29882
29883        return {
29884            require: ['ngModel', '^form'],
29885            scope: {
29886                otherModelValue: '=comparePasswordTo'
29887            },
29888            link: function (scope, element, attr, ctrl) {
29889                ctrl[0].$parsers.unshift(function (value) {
29890                    const fieldName = attr.comparePasswordTo;
29891                
29892                    if (ctrl[1][fieldName].$modelValue && ctrl[1][fieldName].$modelValue === value) {
29893                       
29894                        ctrl[0].$setValidity('passwordMatch', true);
29895
29896                        return value;
29897
29898                    } else {
29899
29900                        ctrl[0].$setValidity('passwordMatch', false);
29901                        return undefined;
29902                    }
29903                });
29904            }
29905        }; 
29906
29907    });
29908})();
29909 
29910
29911
29912(function () {
29913    'use strict';
29914
29915    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
29916    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
29917
29918    const app = angular.module('MemberPortalDirectives');
29919
29920    app.directive('pendingPcpChange', function () {
29921        return {
29922            scope: {},
29923            restrict: 'EA',
29924            templateUrl: urlBase + '/App/directives/pending-pcp-change/PendingPcpChangeRequest.html' + cacheBust,
29925            controller: pendingPcpChangeCtrl,
29926            controllerAs: 'ctrl'
29927        };
29928    });
29929
29930    app.controller('pendingPcpChangeCtrl', pendingPcpChangeCtrl);
29931
29932    pendingPcpChangeCtrl.$inject = ['$scope', 'memberDataService', '$filter'];
29933
29934    function pendingPcpChangeCtrl($scope, memberDataService, $filter) {
29935        $scope.loading = false;
29936        $scope.data = {};
29937        $scope.dataString = {};
29938        $scope.errorMessage = '';
29939        $scope.successMessage = '';
29940        $scope.showDetails = false;
29941
29942        $scope.isPending = false;
29943
29944        $scope.$on('pcp-selected', function () {
29945            $scope.getPendingPcpChanges();
29946        });
29947
29948        $scope.getPendingPcpChanges = function () {         
29949            $scope.loading = true;
29950            $scope.data = [];
29951            memberDataService.getPendingPcpChanges().success(function (data) {
29952                if (data.length > 0) {
29953                    $scope.data = data;
29954                    $scope.isPending = true;
29955                } else {
29956                    $scope.isPending = false;
29957                }
29958                $scope.loading = false;
29959                $scope.$broadcast('checked-pending-pcp', $scope.isPending);
29960
29961            }).error(function () {
29962                $scope.isPending = false;
29963                $scope.errorMessage = $filter('translate')('form_unableToRetrieveData');
29964                $scope.loading = false;
29965                $scope.$broadcast('checked-pending-pcp', $scope.isPending);
29966            });
29967        };
29968
29969        $scope.cancelRequest = function (id) {
29970            $scope.loading = true;
29971            $scope.successMessage = '';
29972            $scope.errorMessage = '';
29973            memberDataService.deleteRequest(id).success(function () {
29974                $scope.successMessage = $filter('translate')('form_SuccessfulSubmit');
29975                $scope.getPendingPcpChanges();
29976                $scope.loading = false;
29977            }).error(function () {
29978                $scope.loading = false;
29979                $scope.errorMessage = $filter('translate')('page_healthrec_rxPrescriptions_DrugCoverageDetermination_SubmittedError');             
29980            });
29981        };
29982
29983        $scope.getPendingPcpChanges();
29984
29985    };
29986})();
29987//------------------------------------------------------------------
29988//------------------------------------------------------------------
29989(function () {
29990    'use strict';
29991    
29992    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
29993    const app = angular.module('MemberPortalDirectives');
29994    const medImpactSsoUrl = $('meta[name="medImpactSsoUrl"]').attr('content');
29995    //------------------------------------------------------------
29996    //------------------------------------------------------------
29997    app.directive('pharmacyPanels', function () {
29998        const directive = {
29999            scope: {
30000                lob: '@',
30001            },
30002            restrict: 'E',
30003            // maybe move to template file
30004            templateUrl: 'App/directives/pharmacy-panels/pharmacy-panels.html' +
30005                cacheBust,
30006            controller: directiveController
30007        };
30008
30009        directiveController.$inject = ['$scope', 'requestedLanguageService', 'authStatusService'];
30010
30011        return directive;
30012
30013        function directiveController($scope, requestedLanguageService, authStatusService) {
30014
30015            init();
30016
30017            function init() {
30018
30019                $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
30020                $scope.debug = getDebugHelper();
30021                $scope.panels = getPanelConfiguration();
30022
30023                $scope.mode = 'SHOW_PHARMACY_EXCEPTION';
30024
30025                $scope.isProxied = authStatusService.checkIfProxied();
30026
30027                $scope.bootstrapConfigs = ['XS', 'SM', 'MD', 'LG'];
30028
30029                $scope.getLayoutConfig = getLayoutConfig;
30030
30031                $scope.lobConfig = getLineOfBusinessConfiguration($scope.lob);
30031 // $scope.lob and $scope.lobDisplay passed in from outside the directive
30032
30033                $scope.btnManager = getButtonManager($scope.lobConfig);
30034            }
30035
30036            $scope.$on('language-change', function (event, key) {
30037                $scope.currentLanguage = requestedLanguageService.getCurrentLanguage();
30038            });
30039
30040            $scope.debug.onModeChanged = function () {
30041                $scope.mode = $scope.debug.Mode;
30042            };
30043
30044            $scope.$watch('lob', function () {
30045                $scope.lobConfig.lob = $scope.lob;
30046                $scope.lobConfig.updateLOB($scope.lob);
30047            });
30048
30049            $scope.isPanelVisible = function (id) {
30050                var bRet = true;
30051
30052                switch (id) {
30053                    case 2: bRet = $scope.mode === 'SHOW_PRESCRIPTION_EXCEPTION'; break;
30054                    case 3: bRet = $scope.mode === 'SHOW_PHARMACY_EXCEPTION'; break;
30055                    default:
30056                        break;
30057                }
30058                return bRet;
30059            };
30060
30061            //------------------------------------------------------------
30062            // Returns a LayoutConfig object with the following properties
30063            // - panelClass - class string for the panel ui component
30064            // - panelStyle - style string for the panel ui component
30065            // - buttonClass - class string for the button in the panel
30066            // - buttonStyle - style string for the button in the panel
30067            //------------------------------------------------------------
30068            function getLayoutConfig(bsWidth, panelID, buttonID, uiClass) {
30069
30070                var layoutConfig = getInitLayoutConfig();
30071                layoutConfig = updateLayoutConfig(layoutConfig, bsWidth, panelID, buttonID, uiClass);
30072                return layoutConfig;
30073
30074                //------------------------------------------------------------
30075                // bootstrapHelper.getBootStrapVisibilityString(bsWidth) - returns the html class attribute value needed to
30076                // only show the bootstrap configuration (ie. XS, SM, MD, LG)
30077                //------------------------------------------------------------
30078                function getBootStrapVisibilityString(bsWidth) {
30079
30080                    var boostrapClass = '';
30081
30082                    switch (bsWidth) {
30083                        case 'XS': boostrapClass = ' visible-xs'; break;
30084                        case 'SM': boostrapClass = ' visible-sm'; break;
30085                        case 'MD': boostrapClass = ' visible-md'; break;
30086                        case 'LG': boostrapClass = ' visible-lg'; break;
30087                        default:
30088                            console.log('error: invalid bootstrapConfig ' + bsWidth);
30089                            break;
30090                    }
30091
30092                    return boostrapClass;
30093                }
30094
30095                //------------------------------------------------------------
30096                // Returns a layout configuration with the default styles
30097                //------------------------------------------------------------
30098                function getInitLayoutConfig() {
30099
30100                    const layoutConfig = {
30101                        panelClass: null,
30102                        panelStyle: null,
30103                        buttonDivClass: null,
30104                        buttonDivStyle: null,
30105                        buttonClass: null,
30106                        buttonStyle: null,
30107                        showProgressForButtons: false // determines whether the progress spinner is shown for the buttons for a panel, used for loading buttons
30108                    };
30109
30110                    // default style for the panel
30111                    layoutConfig.panelStyle = {
30112                        // "border": '1px solid #D6D6D6',
30113                        // "background-color": '#E7E7E7',
30114                        "padding": '1.5rem'
30115                    };
30116
30117                    layoutConfig.buttonDivStyle = {
30118                    };
30119
30120                    // default style for the button
30121                    layoutConfig.buttonStyle = {
30122                        "box-shadow": "0px 2px 2px rgba(0, 0, 0, 0.24)",
30123                        "border-radius": "100px",
30124                        "max-width": '350px',
30125                        "font-size": '0.9em',
30126                        "background-color": '#01213E',
30127                        "color": 'white',
30128                        //"border-radius": "0", // can't use square buttons (it's inconsistent with the rest of the site)
30129                        //"border": "",
30130                        // "margin-top": '15px',
30131                        "padding": '8px 5px',
30132                        "white-space": 'normal',
30133                        "font-family": 'Poppins',
30134                        "font-style": 'normal',
30135                        "font-weight": '600',
30136                    };
30137                    return layoutConfig;
30138                }
30139
30140                //------------------------------------------------------------
30141                // Takes and returns an updated layoutconfigration based on the
30142                // bootstrap configraiton
30143                //------------------------------------------------------------
30144                function updateLayoutConfig(layoutConfig, bsWidth, panelID, buttonID, uiClass) {
30145
30146                    var boostrapClassString = getBootStrapVisibilityString(bsWidth);
30147
30148                    layoutConfig.showProgressForButtons = panelID === 1;
30149
30150                    switch (panelID) {
30151                        case 1: { // Plan Access Button
30152                            layoutConfig.bulletListClass = 'col-xs-12 col-md-12';
30153                            layoutConfig.buttonStyle['max-width'] = '315px';
30154                            switch (bsWidth) {
30155                                case 'XS': {
30156                                    layoutConfig.panelClass = 'rxPanel_2 form panel col-xs-12' + boostrapClassString;
30157                                    layoutConfig.buttonDivClass = 'col-xs-12';
30158                                    layoutConfig.buttonClass = 'btn rxButtonMain_2 col-xs-12' + boostrapClassString;
30159                                    layoutConfig.buttonStyle['bottom'] = 'auto';
30160                                    layoutConfig.buttonStyle['position'] = 'relative';
30161                                }
30162                                    break;
30163                                case 'SM': {
30164                                    layoutConfig.panelClass = 'rxPanel_2 form panel col-sm-12' + boostrapClassString;
30165                                    layoutConfig.buttonDivClass = 'col-xs-12';
30166                                    layoutConfig.buttonClass = 'btn col
30166-xs-12' + boostrapClassString;
30167                                    layoutConfig.buttonStyle['bottom'] = 'auto';
30168                                    layoutConfig.buttonStyle['position'] = 'relative';
30169                                }
30170                                    break;
30171                                case 'MD':
30172                                case 'LG':
30173                                default: {
30174                                    layoutConfig.panelClass = 'rxPanel_2 form panel col-sm-12' + boostrapClassString;
30175                                    layoutConfig.buttonDivClass = 'col-sm-offset-3 col-md-offset-0 col-md-6';
30176                                    layoutConfig.buttonDivStyle['right'] = '44px';
30177                                    layoutConfig.buttonDivStyle['bottom'] = '24px';
30178                                    layoutConfig.buttonClass = 'btn rxButtonMain_2 col-md-12 col-lg-12 pull-right' + boostrapClassString;
30179                                    layoutConfig.buttonStyle['bottom'] = 'auto';
30180                                    layoutConfig.buttonStyle['position'] = 'relative';
30181                                    layoutConfig.buttonStyle['top'] = '2.3em';
30182                                    layoutConfig.buttonStyle['margin-top'] = '0px';
30183                                    layoutConfig.buttonStyle['right'] = '14px';
30184                                    break;
30185                                }
30186                            }
30187                            // Tweak the padding so the button shows proper spacing according to the number of lines
30188                            // shown in the button.
30189                            /* layoutConfig.buttonStyle['padding-top'] = '1.1em';*/
30190                            break;
30191                        }
30192                        case 2: // Prescription Exception
30193                        case 3: // Prior Authorization
30194                        case 4: // Medical Formulary
30195                        default: {
30196                            switch (bsWidth) {
30197                                case 'XS': {
30198                                    layoutConfig.panelClass = 'form panel col-xs-12' + boostrapClassString;
30199                                    layoutConfig.panelStyle['right'] = '';
30200                                    layoutConfig.panelStyle['min-height'] = '';
30201                                    layoutConfig.buttonDivClass = 'col-xs-12';
30202                                    layoutConfig.buttonClass = 'btn col-xs-12' + boostrapClassString;
30203                                    layoutConfig.buttonStyle['bottom'] = 'auto';
30204                                    layoutConfig.buttonStyle['position'] = 'relative';
30205                                    layoutConfig.panelStyle['margin-top'] = '1rem';
30206                                }
30207                                    break;
30208                                case 'SM': {
30209                                    layoutConfig.panelClass = 'form panel col-xs-12' + boostrapClassString;
30210                                    layoutConfig.buttonDivClass = 'col-xs-12';
30211                                    layoutConfig.buttonClass = 'btn col-xs-12' + boostrapClassString;
30212                                    layoutConfig.panelStyle['margin-top'] = '1rem';
30213                                }
30214                                    break;
30215                                case 'MD':
30216                                case 'LG': {
30217                                    layoutConfig.panelClass = 'form panel rxPanel_2 col-xs-6' + boostrapClassString;
30218                                    layoutConfig.panelStyle['margin-top'] = '1rem';
30219
30220                                    if ($scope.isPanelVisible(2)) layoutConfig.panelStyle['height'] = '500px'; 
30221                                    if ($scope.isPanelVisible(3)) {
30222                                        if ($scope.lobConfig.isShowMedicare()|| $scope.lobConfig.isShowCCA()) {
30223                                            layoutConfig.panelStyle['height'] = '500px'; // medicare text is longer ... make more space
30224                                            if (panelID === 3) {
30225                                                layoutConfig.panelStyle['border-top-right-radius'] = '0';
30226                                                layoutConfig.panelStyle['border-bottom-right-radius'] = '0';
30227                                                layoutConfig.panelStyle['border-right'] = '1px solid #CCD3D8';
30228                                            }
30229                                        }
30230
30231                                        else
30232                                            layoutConfig.panelStyle['height'] = '500px';
30232 // needs to be adjusted with the button Div below - see **A**
30233                                    }
30234
30235                                    // 2017.02.01
30236                                    if (panelID === 4 || panelID === 5) {
30237                                        if ($scope.lobConfig.isShowMedicare()) {
30238                                            layoutConfig.panelStyle['height'] = '250px'; // medicare text is longer ... make more space
30239                                            layoutConfig.panelStyle['border-top-left-radius'] = '0';
30240                                            layoutConfig.panelStyle['border-bottom-left-radius'] = '0';
30241                                            if (panelID === 5) {
30242                                                layoutConfig.panelStyle['margin-top'] = '0';
30243                                                layoutConfig.panelStyle['border-top'] = '1px solid #CCD3D8';
30244                                                layoutConfig.panelStyle['border-top-right-radius'] = '0';
30245                                            } else {
30246                                                layoutConfig.panelStyle['border-bottom-right-radius'] = '0';
30247                                            }
30248                                        } else if ($scope.lobConfig.isShowCCA()) {
30249                                            layoutConfig.panelStyle['height'] = '250px'; // medicare text is longer ... make more space
30250                                            layoutConfig.panelStyle['border-top-left-radius'] = '0';
30251                                            layoutConfig.panelStyle['margin-top'] = '0';
30252                                            layoutConfig.panelStyle['border-top'] = '1px solid #CCD3D8';
30253                                            layoutConfig.panelStyle['border-top-right-radius'] = '0';
30254                                            if(panelID === 4) {
30255                                                layoutConfig.panelStyle['border-right'] = '1px solid #CCD3D8';
30256                                                layoutConfig.panelStyle['border-bottom-right-radius'] = '0';
30257                                            } else {
30258                                                layoutConfig.panelStyle['border-bottom-left-radius'] = '0';
30259                                            }
30260                                        } else {
30261                                            layoutConfig.panelStyle['height'] = '250px'; // needs to be adjusted with the button Div below - see **A**
30262                                            if (panelID === 5) {
30263                                                layoutConfig.panelStyle['border-top-left-radius'] = '0';
30264                                                layoutConfig.panelStyle['border-bottom-left-radius'] = '0';
30265                                                layoutConfig.panelStyle['border-left'] = '1px solid #CCD3D8';
30266                                            } else {
30267                                                layoutConfig.panelStyle['border-top-right-radius'] = '0';
30268                                                layoutConfig.panelStyle['border-bottom-right-radius'] = '0';
30269                                            }
30270                                        }
30271                                    }
30272
30273                                    layoutConfig.buttonDivClass = 'col-xs-12';
30274
30275                                    layoutConfig.buttonClass = 'btn rxButtonMain_2x col-md-12' + boostrapClassString;
30276                                    layoutConfig.buttonStyle['width'] = '100%';// "btn rxButtonMain_2x col-xs-12 col-sm-12 col-md-12 col-lg-12" + boostrapClassString;
30277
30278                                    if ([2, 3, 5].includes(panelID)) {
30279                                        layoutConfig.buttonStyle['width'] = '100%';
30280                                    }
30281
30282                                    break;
30283                                }
30284                                default: {
30285                                    break;
30286                                }
30287                            }
30288                            switch (panelID) {
30289                                case 2:
30290                                    {
30291                                        layoutConfig.buttonStyle['padding-top'] = '15px';
30292                                    }
30293                                    break;
30294                                case 3: {
30295                                    if ($scope.lobConfig.isShowMediCal()) {
30296                                        layoutConfig.buttonStyle['padding-top'] = '15px';
30297                                    }
30298                                }
30299                                    break;
30300                                case 4: {
30301                                    // Panel 4's button Medication Formulary can always fit on one line "Medical Formulary"
30302                                    // layoutConfig.buttonStyle['padding-top'] = '5px';
30303                                }
30304                                    break;
30305                            }
30306                        }
30307                    }
30308                    return layoutConfig;
30309                }
30310            }
30311
30312            function getLineOfBusinessConfiguration(lob) {
30313
30314                const lobConfig = {
30315                    updateLOB: updateLOB,
30316                    lob: lob,
30317                    isShowMedicare: isShowMedicare,
30318                    isShowMediCal: isShowMediCal,
30319                    isShowCCA: isShowCCA
30320                };
30321
30322                $scope.$watch('lobConfig.LOB',
30323                    function () {
30324                        var str = '';
30325                        if (lobConfig.isShowMediCal()) str = str + ' MediCal';
30326                        if (lobConfig.isShowMedicare()) str = str + ' Medicare';
30327                        if (lobConfig.isShowCCA()) str = str + ' IEHP Covered';
30328                        lobConfig.Description = lobConfig.lob + ' ' + str;
30329                    });
30330
30331                function updateLOB() {
30332                    for (let index in $scope.panels) {
30333                        // Update section title and remove the 2nd bullet for DSNP members.
30334                        if ($scope.panels[index].ID === 1) {
30335                            if (lobConfig.isShowCCA()) {
30336                                $scope.panels[index].title = 'page_healthrec_rxPrescriptions_MedImpactConsumerPortal';
30337                                $scope.panels[index].bulletList = $scope.panels[index].LobBulletList['CCA'] || [];
30338                            } else if (lobConfig.isShowMedicare()) {
30339                                $scope.panels[index].title = 'page_healthrec_rxPrescriptions_MedImpactConsumerPortal';
30340                                $scope.panels[index].bulletList = $scope.panels[index].LobBulletList['MCR'] || [];
30341                            } else {
30342                                $scope.panels[index].title = 'page_healthrec_rxPrescriptions_PlanAccess';
30343                                $scope.panels[index].bulletList = $scope.panels[index].LobBulletList['MED'] || [];
30344                            }
30345                        }                        
30346
30347                        if ($scope.panels[index].ID === 3) {
30348                            if (lobConfig.isShowMediCal() || lobConfig.isShowCCA()) {
30349                                $scope.panels.splice(index, 1); // remove this section/panel for Medi-Cal
30350                            } else {
30351                                $scope.panels[index].title =
30352                                    'page_healthrec_rxPrescriptions_PriorAuthorization_Medicare';
30353                                $scope.panels[index].subtext =
30354                                    'page_healthrec_rxPrescriptions_PriorAuthorization_Description_Medicare';
30355                                $scope.panels[index].buttons[0].Text =
30356                                    'page_healthrec_rxPrescriptions_PriorAuthorization_Button_Medicare1';
30357                                $scope.panels[index].buttons[0].Text2 =
30358                                    'page_healthrec_rxPrescriptions_PriorAuthorization_Button_Medicare2';
30359                                $scope.panels[index].buttons[0].Text3 =
30360                                    'page_healthrec_rxPrescriptions_PriorAuthorization_Button_Medicare3';
30361                            }
30362                        }
30363
30364                        if ($scope.panels[index].ID === 6) {
30365                            if (lobConfig.isShowMediCal() || lobConfig.isShowMedicare()) {
30366                                $scope.panels.splice(index, 1); // remove this section/panel for non-CCA
30367                            }
30368                        }
30369                    }
30370                }
30371
30372                function isShowMedicare() {
30373                    return ['MCR', 'CMC'].includes(lobConfig.lob);
30374                }
30375
30376                function isShowMediCal() {
30377                    return ['MED', 'CCI', 'MCL'].includes(lobConfig.lob);
30378                }
30379
30380                function isShowCCA() {
30381                    return ['CCA'].includes(lobConfig.lob);
30382                }
30383
30384                return lobConfig;
30385            }
30386
30387            function getUrlManager(lobConfig) {
30388
30389                var urlManager = {
30390                    lobConfig: lobConfig,
30391                    getMedicationFormularyLinkUrl: getMedicationFormularyLinkUrl,
30392                    launchMedicationFormularyLinkUrl: launchMedicationFormularyLinkUrl,
30393                    launchPrescriptionLinkUrl: launchPrescriptionLinkUrl
30394                };
30395                return urlManager;
30396
30397                function getMedicationFormularyLinkUrl() {
30398                    const language = $scope.currentLanguage;
30399                    const lobConfig = urlManager.lobConfig;
30400
30401                    if (lobConfig.isShowMedicare()) return 'https://iehp.org/' + language.substr(0, 2) + '/members/iehp-dualchoice?target=prescription-drugs';
30402                    if (lobConfig.isShowMediCal()) return 'https://medi-calrx.dhcs.ca.gov/
30402home/cdl';                    
30403                    if (lobConfig.isShowCCA()) return 'https://iehp.org/content/dam/iehp-org/' + language.substr(0, 2) + '/documents/coveredcalifornia/Formulary.pdf';
30404                    return 'about:blank'; // ERROR
30405                }
30406
30407                function launchMedicationFormularyLinkUrl() {
30408                    const url = getMedicationFormularyLinkUrl();
30409                    window.open(url);
30410                }
30411
30412                function getPrescriptionLinkUrl() {
30413                    const language = $scope.currentLanguage;
30414                    const lobConfig = urlManager.lobConfig;
30415
30416                    const baseUrl = 'https://iehp.org/';
30417
30418                    if (lobConfig.isShowMedicare()) {
30419                        return baseUrl + language.substr(0, 2) +
30420                            '/members/iehp-dualchoice-cal-mediconnect-plan?' +
30421                            'target=prescription-drugs';
30422                    }
30423
30424                    if (lobConfig.isShowMediCal()) {
30425                        return baseUrl + language.substr(0, 2) + '/members/medical';
30426                    }
30427
30428                    // TODO: update to CCA link for pharmacy benefits
30429                    if (lobConfig.isShowCCA()) {
30430                        return baseUrl + language.substr(0, 2) + '/browse-plans/covered-california';
30431                    }
30432
30433                    return baseUrl;
30434                }
30435
30436                function launchPrescriptionLinkUrl() {
30437                    const url = getPrescriptionLinkUrl();
30438                    window.open(url);
30439                }
30440            }
30441
30442            function getButtonManager(lobConfig) {
30443                var buttonManager = {
30444                    onButtonClick: onButtonClick,
30445                    isButtonVisible: isButtonVisible,
30446                    urlManager: null
30447                };
30448
30449                buttonManager.urlManager = getUrlManager(lobConfig);
30450
30451                return buttonManager;
30452
30453                function onButtonClick(panel) {
30454                    switch (panel.ID) {
30455                        case 4: // Medication Formulary
30456                            buttonManager.urlManager.launchMedicationFormularyLinkUrl();
30457                            break;
30458                        case 5:
30459                            buttonManager.urlManager.launchPrescriptionLinkUrl();
30460                            break;
30461                    }
30462                }
30463
30464                //----------------------------------------------------------------
30465                // Determines the visibility of the Member Plan Access buttons
30466                //----------------------------------------------------------------
30467                function isButtonVisible(panel, button) {
30468                    //console.log('panel', panel);
30469                    //console.log('button', button);
30470                    if (panel.ID === 1) { // Plan Member Access                         
30471                        switch (button.ID_NAME) { // button name coming from the template
30472                            case 'MEDICAL': {
30473                                return $scope.lobConfig.isShowMediCal();
30474                            }
30475                            case 'DUALCHOICEDSNP': {
30476                                return $scope.lobConfig.isShowMedicare();
30477                            }
30478                            case 'CCA': {
30479                                return $scope.lobConfig.isShowCCA();
30480                            }
30481                        }
30482                    }
30483                    if ($scope.isProxied) {
30484                        return false;
30485                    }
30486                    //the rest of the buttons on the page default to true
30487                    return true;
30488                }
30489            }
30490
30491            //------------------------------------------------------------
30492            // getPanelConfiguration()
30493            // Data driven UI ... the data for the panels are stored here
30494            //------------------------------------------------------------
30495            function getPanelConfiguration() {
30496                // array of configuration data for each of the panels
30497                const data = [
30498                    //----------------------------------------------------------------
30499                    // Plan Member Access
30500                    //----------------------------------------------------------------
30501                    {
30502                        ID: 1,
30503                        //title: 'page_healthrec_rxPrescriptions_PlanAccess',  // "Plan Member Access or MedImpact Consumer Portal for DSNP check updateLOB()"
30504                        subtext: 'page_healthrec_rxPrescriptions_planMemberAccessDisclaimer', // "By clicking on these buttons you will ..."
30505                        bulletList: [],
30506                        LobBulletList: {
30507                            MED: [
30508                                'page_healthrec_rxPrescriptions_list3',
30509                                'page_healthrec_rxPrescriptions_list4',
30510                                'page_healthrec_rxPrescriptions_list5',
30511                                'page_healthrec_rxPrescriptions_list6',
30512                                'page_healthrec_rxPrescriptions_list7',
30513                                'page_healthrec_rxPrescriptions_list8',
30514                                'page_healthrec_rxPrescriptions_list9',
30515                                'page_healthrec_rxPrescriptions_list10',
30516                                'page_healthrec_rxPrescriptions_list11',
30517                            ],
30518                            MCR: [
30519                                'page_healthrec_rxPrescriptions_list1',
30520                                'page_healthrec_rxPrescriptions_list2',
30521                                'page_healthrec_rxPrescriptions_list3',
30522                                'page_healthrec_rxPrescriptions_list5',
30523                                'page_healthrec_rxPrescriptions_list6',
30524                                'page_healthrec_rxPrescriptions_list7',
30525                                'page_healthrec_rxPrescriptions_list8',
30526                                'page_healthrec_rxPrescriptions_list9',
30527                                'page_healthrec_rxPrescriptions_list10',
30528                                'page_healthrec_rxPrescriptions_list11',
30529                            ],
30530                            CCA: [
30531                                'page_healthrec_rxPrescriptions_list3',
30532                                'page_healthrec_rxPrescriptions_list4',
30533                                'page_healthrec_rxPrescriptions_list5',
30534                                'page_healthrec_rxPrescriptions_list6',
30535                                'page_healthrec_rxPrescriptions_list7',
30536                                'page_healthrec_rxPrescriptions_list8',
30537                                'page_healthrec_rxPrescriptions_list9',
30538                                'page_healthrec_rxPrescriptions_list10',
30539                                'page_healthrec_rxPrescriptions_list11',
30540                            ],
30541                        },                            
30542                        messageShown: false,
30543                        buttons:
30544                            [
30545                                {
30546                                    ID: 0,
30547                                    ID_NAME: 'MEDICAL',
30548                                    Text: 'page_healthrec_rxPrescriptions_mediCal_button',
30549                                    Description: '',
30550                                    glyphClass: 'newWindowIcon',
30551                                    href: 'https://medi-calrx.dhcs.ca.gov/member/',
30552                                },
30553                                {
30554                                    ID: 1,
30555                                    ID_NAME: 'DUALCHOICEDSNP',
30556                                    Text: 'page_healthrec_rxPrescriptions_MedImpactConsumerPortal',
30557                                    glyphClass: 'newWindowIcon',
30558                                    href: medImpactSsoUrl
30559                                },
30560                                {
30561                                    ID: 2,
30562                                    ID_NAME: 'CCA',
30563                                    Text: 'page_healthrec_rxPrescriptions_MedImpactConsumerPortal',
30564                                    glyphClass: 'newWindowIcon',
30565                                    href: medImpactSsoUrl
30566                                }
30567                            ]
30568                    },
30569                    //----------------------------------------------------------------
30570                    // Send Prescription Exception (not in use?)
30571                    //----------------------------------------------------------------
30572                    {
30573                        ID: 2,
30574                        title: 'page_healthrec_rxPrescriptions_PrescriptionException',
30575                        subtext: '', //"page_healthrec_rxPrescriptions_PrescriptionException_Description",
30576                        messageText: 'icohelp_RxPrescriptionException',
30577                        messageShown: true,
30578                        hideMessageIcon: true,
30579                        displayExtWindowIcon: false,
30580                        buttons: [
30581                            {
30582                                ID: 0,
30583                                Text: 'page_healthrec_rxPrescriptions_PrescriptionException_Button',
30584                                glyphClass: 'glyphicon glyphicon-arrow-right',
30585                                href: 'App/Info/AskIEHP/RxException'
30586                            }
30587                        ],
30588                    },
30589                    //----------------------------------------------------------------
30590                    // Pharmacy Exception (formerly Prior Authorization)
30591                    //----------------------------------------------------------------
30592                    {
30593                        ID: 3,
30594                        //title: 'page_healthrec_rxPrescriptions_PriorAuthorization',
30595                        //subtext: 'page_healthrec_rxPrescriptions_PriorAuthorization_Description',
30596                        messageShown: false,
30597                        buttons: [
30598                            {
30599                                ID: 0,
30600                                Text: '', // Text specified in LOB Code.  Look for page_healthrec_rxPrescriptions_PriorAuthorization_Button_Medicare.
30601                                glyphClass: 'newWindowIcon',
30602                                href: 'https://mp.medimpact.com/partdcoveragedetermination'  
30603                            }
30604                        ]
30605                    },
30606                    //----------------------------------------------------------------
30607                    // Medication Formulary
30608                    //----------------------------------------------------------------
30609                    {
30610                        ID: 4,
30611                        title: 'page_healthrec_rxPrescriptions_MedicationFormulary',
30612                        subtext: 'page_healthrec_rxPrescriptions_MedicationFormulary_Description',
30613                        messageShown: false,
30614                        buttons: [
30615                            {
30616                                ID: 0,
30617                                Text: 'page_healthrec_rxPrescriptions_MedicationFormulary_Button',
30618                                glyphClass: 'newWindowIcon',
30619                                useOnClick: true
30620                            }
30621                        ]
30622                    },
30623                    //----------------------------------------------------------------
30624                    // Pharmacy Benefit Information
30625                    //----------------------------------------------------------------
30626                    {
30627                        ID: 5,
30628                        title: 'page_healthrec_rxPrescriptions_MedicationFormulary_PharmacyLink',
30629                        subtext: 'page_healthrec_rxPrescriptions_MedicationFormulary_PharmacyLink_Description',
30630                        messageShown: false,
30631                        buttons: [
30632                            {
30633                                ID: 0,
30634                                Text: 'page_healthrec_rxPrescriptions_MedicationFormulary_PharmacyLink_Button1',
30635                                Text2: 'page_healthrec_rxPrescriptions_MedicationFormulary_PharmacyLink_Button2',
30636                                glyphClass: 'newWindowIcon',
30637                                useOnClick: true
30638                            }
30639                        ]
30640                    },
30641                    //----------------------------------------------------------------
30642                    // CCA Prescription Drug Exception
30643                    //----------------------------------------------------------------
30644                    {
30645                        ID: 6,
30646                        title: 'page_healthrec_rxPrescriptions_cca_prescription_drug_exception_title',
30647                        subtext: 'page_healthrec_rxPrescriptions_cca_prescription_drug_exception_subtitle',
30648                        messageShown: true,
30649                        buttons: [
30650                            {
30651                                ID: 0,
30652                                Text: 'page_healthrec_rxPrescriptions_cca_prescription_drug_exception_button',
30653                                glyphClass: 'newWindowIcon',
30654                                href: 'https://mp.medimpact.com/emrf'
30655                            }
30656                        ]
30657                    }
30658                ];
30659                return data;
30660            }
30661
30662            //------------------------------------------------------------
30663            //------------------------------------------------------------
30664            function getDebugHelper() {
30665                var debugHelper = {
30666                    // TODO: SSH 2016.08.18 - It would be nice to integrate this with the main member portal's debug panel
30667                    Enabled: false, // shown or hides the debugging panel
30668                    On: false, // toggles the bootstrap width for display
30669                    toggle: toggle,
30670                    Mode: 'SHOW_PHARMACY_EXCEPTION',
30671                    isEnabledPharmacyException: isEnabledPharmacyException,
30672                    toggleMode: toggleMode,
30673                    onModeChanged: null,
30674                    LOB: '',
30675                    LOBDescription: '',
30676                    onLOBChange: onLOBChange
30677                };
30678
30679                return debugHelper;
30680
30681                function toggle() {
30682                    debugHelper.On = !debugHelper.On;
30683                }
30684
30685                function isEnabledPharmacyException() {
30686                    return debugHelper.Mode === 'SHOW_PHARMACY_EXCEPTION';
30687                }
30688
30689                function toggleMode() {
30690                    if (debugHelper.Mode === 'SHOW_PHARMACY_EXCEPTION') debugHelper.Mode = 'SHOW_PRESCRIPTION_EXCEPTION';
30691                    else debugHelper.Mode = 'SHOW_PHARMACY_EXCEPTION';
30692                    if (debugHelper.onModeChanged) debugHelper.onModeChanged();
30693                }
30694
30695                function onLOBChange() {
30696                    debugHelper.LOBDescription = debugHelper.LOB;
30697                }
30698            }
30699        }
30700    });
30701})();
30702
30703(function () {
30704  "use strict";
30705    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
30706  angular
30707    .module("MemberPortalDirectives")
30708    .directive("inboxActions", inboxActions);
30709
30710  inboxActions.$inject = [];
30711
30712  function inboxActions() {
30713    const directive = {
30714      scope: {
30715        compactMode: "=",
30716        read: "=",
30717        archived: "=",
30718      },
30719      restrict: "E",
30720      templateUrl:
30721          'App/directives/portal-inbox/inbox-actions.directive.html' + cacheBust,
30722      controller: inboxActionsController,
30723      controllerAs: "actions",
30724      bindToController: true,
30725    };
30726
30727    inboxActionsController.$inject = ["$scope"];
30728
30729    function inboxActionsController($scope) {
30730      const vm = this;
30731
30732      vm.setup = () => {
30733        vm.emailActions = [];
30734        const archiveAction = {
30735          icon: "inventory.svg",
30736          description: "Archive",
30737          actionName: "archive",
30738          value: true,
30739        };
30740        const moveToInboxAction = {
30741          icon: "inventory.svg",
30742          description: "Move To Inbox",
30743          actionName: "archive",
30744          value: false,
30745        };
30746        const readAction = {
30747          icon: "email_read.svg",
30748          description: "Mark As Read",
30749          actionName: "read",
30750          value: true,
30751        };
30752        const unreadAction = {
30753          icon: "email_unread.svg",
30754          description: "Mark As Unread",
30755          actionName: "read",
30756          value: false,
30757        };
30758        if (!vm.archived) {
30759          if (vm.compactMode) {
30760            vm.read
30761              ? vm.emailActions.push(unreadAction)
30762              : vm.emailActions.push(readAction);
30763            vm.emailActions.push(
30764              vm.archived ? moveToInboxAction : archiveAction
30765            );
30766          } else {
30767            vm.emailActions = [
30768              readAction,
30769              unreadAction,
30770              vm.archived ? moveToInboxAction : archiveAction,
30771            ];
30772          }
30773        } else {
30774          vm.emailActions = [moveToInboxAction];
30775        }
30776      };
30777
30778      vm.setup();
30779
30780      $scope.takeEmailAction = function (action, value) {
30781        $scope.$emit("takeEmailAction", { action, value });
30782        // simulate instant feedback
30783        if (vm.compactMode && vm.hasOwnProperty(action)) {
30784          vm[action] = value;
30785          vm.setup();
30786        }
30787      };
30788
30789      $scope.batchEmailAction = function (action, value) {
30790        $scope.$emit("batchEmailAction", { action, value });
30791      };
30792    }
30793
30794    return directive;
30795  }
30796})();
30797
30798(function () {
30799    'use strict';
30800    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
30801
30802    angular
30803        .module('MemberPortalDirectives')
30804        .directive('inboxAttachmentLink', inboxAttachmentLink);
30805
30806    inboxAttachmentLink.$inject = ['$location', '$window', 'inboxService', 'memberDataService'];
30807
30808    function inboxAttachmentLink($location, $window, inboxService, memberDataService) {
30809
30810        const directive = {
30811            scope: {
30812                attachment: '='
30813            },
30814            restrict: 'E',
30815            templateUrl: 'App/directives/portal-inbox/inbox-attachment-link.directive.html' + cacheBust,
30816            controller: inboxAttachmentLinkController,
30817            controllerAs: 'link',
30818            bindToController: true
30819        };
30820
30821        inboxAttachmentLinkController.$inject = [];
30822
30823        function inboxAttachmentLinkController() {
30824
30825            const vm = this;
30826
30827            vm.processAttachmentLink = processAttachmentLink;
30828            vm.getIconSrcByType = getIconSrcByType;
30829
30830            const specialUrls = [
30831                'escheatment/entry',
30832                'escheatment/status',
30833                'claims/disputes-status'];
30834
30835            const specialCaseHandlers = {
30836                'escheatment/entry': (url) => {
30837
30838                    const checkNumber = url.split('=')[1];
30839
30840                    $location.path('/escheatment/entry/' + checkNumber);
30841                },
30842                'escheatment/status': (url) => {
30843
30844                    const claimNumber = url.split('=')[1];
30845
30846                    $location.path('/escheatment/status/' + claimNumber);
30847                },
30848                'claims/disputes-status': (url) => {
30849
30850                    const caseNumber = url.split('=')[1];
30851                    $location.path('/claims/disputes-status/' + caseNumber);
30852                }
30853            }
30854
30855            function createDataURI(base64Data, mimeType) {
30856                return `data:${mimeType};base64,${base64Data}`;
30857            }
30858
30859            function downloadFile(url) {
30860
30861                vm.downloading = true;
30862
30863                memberDataService.downloadSubmission(url).then(function (resp) {
30864                    console.log('download file resp', resp);
30865
30866                    if (!resp || !resp.data) {
30867
30868                        vm.downloading = false;
30869
30870                        return;
30871                    }
30872
30873                    const base64 = resp.data.base64EncodedDocument;
30874                    const contentType = resp.data.type;
30875                    const filename = resp.data.displayName;
30876
30877                    const link = document.createElement('a');
30878                    document.body.appendChild(link);
30879                    link.href = createDataURI(base64, contentType);
30880                    link.download = filename;
30881
30882                    link.click();
30883                    document.body.removeChild(link);
30884
30885                    vm.downloading = false;
30886                }, function () {
30887                    vm.downloading = false;
30888                });
30889            }
30890
30891            function processAttachmentLink(url) {
30892                const matched = specialUrls.find(m => url.toLowerCase().includes(m));
30893
30894                console.log('matched', matched);
30895
30896                if (!matched) {
30897                    console.log('no match');
30898                    downloadFile(url);
30899
30900                    return false;
30901                }
30902
30903                return specialCaseHandlers[matched](url);
30904            }
30905
30906            function getIconSrcByType(type) {
30907
30908                const url = inboxService.getIconSrcByType(type);
30909
30910                return url;
30911            }
30912        }
30913
30914        return directive;
30915    }
30916})();
30917(function () {
30918  "use strict";
30919  var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
30920  angular
30921    .module("MemberPortalDirectives")
30922    .directive("inboxCategoryGroup", inboxCategoryGroup);
30923
30924  inboxCategoryGroup.$inject = ['$rootScope'];
30925
30926  function inboxCategoryGroup($rootScope) {
30927    const directive = {
30928      scope: {
30929        messageCategories: "=",
30930        activeCategory: "=",
30931        applyMessageCategory: "&",
30932        readBroadcastMsgCnt: "=",
30933        allMessages: "="
30934      },
30935      restrict: "E",
30936      templateUrl:'App/directives/portal-inbox/inbox-category-group.directive.html' +
30937                cacheBust, 
30938      controller: inboxCategoryGroupController,
30939      controllerAs: "categories",
30940      bindToController: true,
30941    };
30942
30943      inboxCategoryGroupController.$inject = ['inboxService'];
30944
30945      function inboxCategoryGroupController(inboxService) {
30946          const vm = this;
30947          
30948          vm.calculateUnreadCnt = function (category) {
30949      
30950              vm.readBroadcastMsgCnt = inboxService.getReadBroadcastMessageCnt(vm.allMessages);
30951      
30952              return category.messageCategory.toLowerCase() === 'all' ?
30953                  category.unreadCount - vm.readBroadcastMsgCnt :
30954                  category.unreadCount;
30955          }
30956
30957          $rootScope.$on('updateHeaderMessageCounts', function () {
30958
30959              const allCategory = vm.messageCategories.find((c) => c.messageCategory.toLowerCase() === 'all');
30960              
30961              vm.calculateUnreadCnt(allCategory);
30962          });
30963
30964
30965          vm.makeActiveTab = function (category) {
30966            vm.messageCategories.forEach(function (x) {
30967              x.activeTab = x.messageCategory === category.messageCategory;
30968            });
30969            vm.applyMessageCategory({ category: category });
30970          };
30971
30972          vm.filterCategories = function() {
30973              return vm.messageCategories.filter((c) => c.messageCategory.toLowerCase() !== "archived");
30974              }
30975
30976          vm.getArchived = function() {
30977              return vm.messageCategories.filter((c) => c.messageCategory.toLowerCase() === "archived");
30978          }
30979    }
30980
30981    return directive;
30982  }
30983})();
30984
30985(function () {
30986  "use strict";
30987  var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
30988
30989  angular
30990    .module("MemberPortalDirectives")
30991    .directive("inboxFilters", inboxFilters);
30992
30993  inboxFilters.$inject = [];
30994
30995  function inboxFilters() {
30996    const directive = {
30997      scope: {
30998        filters: "=",
30999        applyFilters: "&",
31000        openFilters: "&",
31001      },
31002      restrict: "E",
31003      templateUrl: 'App/directives/portal-inbox/inbox-filters.directive.html' +
31004            cacheBust,
31005      controller: inboxFiltersController,
31006      controllerAs: "vm",
31007      bindToController: true,
31008    };
31009
31010    inboxFiltersController.$inject = ["$scope", "inboxService"];
31011
31012    function inboxFiltersController($scope, inboxService) {
31013      const vm = this;
31014      vm.dropdownStatus = {
31015        isOpen: false,
31016      };
31017      vm.saveFilters = function () {
31018        vm.dropdownStatus.isOpen = false;
31019        vm.applyFilters({ filters: vm.filters });
31020      };
31021      vm.onClickFilterIcon = function () {
31022        vm.openFilters();
31023      };
31024
31025      vm.allowedFilters = vm.filters.filter(f => f.key !== "archived");
31026    }
31027
31028    return directive;
31029  }
31030})();
31031
31032(function () {
31033    'use strict';
31034    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
31035
31036    angular
31037        .module('MemberPortalDirectives')
31038        .directive('inboxMessageGroup', inboxMessageGroup);
31039
31040    inboxMessageGroup.$inject = [];
31041
31042    function inboxMessageGroup() {
31043        const directive = {
31044            scope: {
31045                message: '=',
31046                selected: '='
31047            },
31048            restrict: 'E',
31049            templateUrl: 'App/directives/portal-inbox/inbox-message-group.directive.html' +
31050                cacheBust,
31051            controller: inboxMessageGroupController,
31052            controllerAs: 'group',
31053            bindToController: true
31054        };
31055
31056        inboxMessageGroupController.$inject = ['$scope', 'memberDataService',
31057            'inboxService', '$rootScope'];
31058
31059        function inboxMessageGroupController($scope, memberDataService,
31060            inboxService, $rootScope) {
31061            const vm = this;
31062
31063            vm.showMoreReplies = showMoreReplies;
31064            vm.onSelectToggle = onSelectToggle;
31065            vm.onExpandingMsg = onExpandingMsg;
31066
31067            init();
31068
31069            function init() {
31070                vm.replyLimit = 3;
31071            }
31072
31073            function onSelectToggle() {
31074                $scope.$emit('messageSelected',
31075                    { messageId: vm.message.id, checked: vm.selected });
31076            }
31077
31078            function isToggleEnabled() {
31079                return vm.expanded === true ||
31080                    (vm.message.messageBody.length > 200 || (vm.message.replies && vm.message.replies.length > 0));
31081            }
31082
31083            function onExpandingMsg() {
31084
31085                if (isToggleEnabled()) {
31086                    vm.message.expand = !vm.message.expand;
31087                }
31088
31089                if (!vm.message.readState) {
31090                    // intercept broadcast
31091                    if (vm.message.isBroadcastMessage) {
31092                        vm.message.readState = true;
31093                        inboxService.markReadBroadcastMessage(vm.message.id);
31094                        $rootScope.$emit("updateHeaderMessageCounts");
31095                        return;
31096                    }
31097
31098                    //mark as read in the backend
31099                    memberDataService
31100                        .InboxMessageUpdateReadStatus(vm.message.id, true)
31101                        .success(function () {
31102                            $rootScope.$emit("reloadCategoryCount");
31103                        })
31104                        .error(function (error) {
31105                            console.error(error);
31106                        });
31107                    vm.message.readState = true;
31108                }
31109            }
31110
31111            function showMoreReplies() {
31112                vm.replyLimit = vm.message.replies.length;
31113            }
31114        }
31115
31116        return directive;
31117    }
31118})();
31119
31120(function () {
31121    "use strict";
31122    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
31123
31124    angular
31125        .module("MemberPortalDirectives")
31126        .directive("inboxMessage", inboxMessage);
31127
31128    inboxMessage.$inject = [];
31129
31130    function inboxMessage() {
31131
31132        const directive = {
31133            scope: {
31134                message: '=',
31135                showStatus: '=',
31136                showActions: '=',
31137                hideReply: '=',
31138                hideSubject: '=',
31139                expanded: '='
31140            },
31141            restrict: 'E',
31142            templateUrl: 'App/directives/portal-inbox/inbox-message.directive.html' +
31143                cacheBust,
31144            controller: inboxMessageController,
31145            controllerAs: 'thread',
31146            bindToController: true
31147        };
31148
31149        inboxMessageController.$inject = [
31150            "$scope",
31151            "memberDataService",
31152            "$uibModal",
31153            "inboxService",
31154            "authStatusService",
31155            "$rootScope"
31156        ];
31157
31158        function inboxMessageController(
31159            $scope,
31160            memberDataService,
31161            $uibModal,
31162            inboxService,
31163            authStatusService,
31164            $rootScope
31165        ) {
31166            const vm = this;
31167
31168            vm.getIconSrcByType = getIconSrcByType;
31169
31170            vm.replyToMsg = replyToMsg;
31171            vm.isNeedMoreAttention = isNeedMoreAttention;
31172            vm.expandMessageBody = expandMessageBody;
31173
31174            init();
31175
31176            function init() {
31177
31178                vm.message.cssClass = inboxService.getCssClassPerCategoryName(vm.message.messageCategory);
31179
31180                if (vm?.message?.createDate) {
31181                    vm.message.createDateDesc = inboxService.addTodayDesc(vm.message.createDate);
31182                }
31183
31184                if(vm.message && vm.message.currentMessageState && vm.message.currentMessageState.modifiedDateTime) {
31185                    vm.message.modifiedDateTimeDesc = inboxService.addTodayDesc(vm.message.currentMessageState.modifiedDateTime);
31186                }
31187
31188                vm.message.needsMoreAttention = isNeedMoreAttention(vm.message);
31189
31190                vm.isProxied = authStatusService.checkIfProxied();
31191            }
31192
31193            function isNeedMoreAttention(msg) {
31194                return inboxService.isNeededReply(msg);
31195            }
31196
31197            function replyToMsg(msg) {
31198
31199                const data = {
31200                    message: msg,
31201                    categoryCssClass: vm.message.cssClass
31202                };
31203
31204                $uibModal.open({
31205                    animation: true,
31206                    templateUrl: 'App/directives/portal-inbox/inbox-reply-modal.html',
31207                    windowClass: 'modalOne inboxModal',
31208                    controller: 'inboxReplyModal',
31209                    backdrop: 'static',
31210                    keyboard: false,
31211                    controllerAs: 'reply',
31212                    footerStyle: {
31213                        display: 'none'
31214                    },
31215                    size: 'xl',
31216                    resolve: {
31217                        data: function () {
31218                            return data;
31219                        }
31220                    }
31221                });
31222            }
31223
31224            function getIconSrcByType(type) {
31225                const url = inboxService.getIconSrcByType(type);
31226
31227                return url;
31228            }
31229
31230            function isToggleEnabled() {
31231                return vm.expanded === true ||
31232                    (vm.message.messageBody.length > 200 || (vm.message.replies && vm.message.replies.length > 0));
31233            }
31234
31235            function expandMessageBody() {
31236
31237                if (isToggleEnabled()) {
31238                    vm.expanded = !vm.expanded;
31239                }
31240                
31241                if (!vm.message.readState) {
31242
31243                    // intercept broadcast
31244                    if (vm.message.isBroadcastMessage) {
31245                        vm.message.readState = true;
31246                        inboxService.markReadBroadcastMessage(vm.message.id);
31247                        $rootScope.$emit("updateHeaderMessageCounts");
31248                        return;
31249                    }
31250
31251                    //mark as read in the backend
31252                    memberDataService
31253                        .InboxMessageUpdateReadStatus(vm.message.id, true)
31254                        .success(function () {
31255                            $rootScope.$emit("reloadCategoryCount");
31256                        })
31257                        .error(function (error) {
31258                            console.error(error);
31259                        });
31260                    vm.message.readState = true;
31261                }
31262            }
31263
31264
31265            $scope.$on("takeEmailAction", (event, { action, value }) => {
31266                if (vm.message?.id) {
31267                    memberDataService
31268                        .TakeMessageAction(action, value, vm.message.id)
31269                        .then(() => {
31270                            if (action === "read") {
31271                                vm.message.readState = value;
31272                            } else if (action === "archive") {
31273                                $scope.$emit("reloadInbox");
31274                            }
31275                            $rootScope.$emit("reloadCategoryCount");
31276                        })
31277                        .catch((err) => {
31278                            console.error(err);
31279                        });
31280                }
31281            });
31282        }
31283        return directive;
31284    }
31285})();
31286
31287(function () {
31288    'use strict';
31289    angular
31290        .module('MemberPortal')
31291        .controller('inboxReplyModal', inboxReplyModal);
31292
31293    inboxReplyModal.$inject = ['data', '$rootScope', '$scope', '$modalInstance',
31294        'memberDataService', 'inboxService', 'plUploadInboxService'];
31295
31296    function inboxReplyModal(data, $rootScope, $scope, $modalInstance,
31297        memberDataService, inboxService, plUploadInboxService) {
31298        var vm = this;
31299
31300        vm.data = data;
31301        vm.today = moment().format('MM/DD/YYYY');
31302        vm.closeModal = closeModal;
31303
31304        vm.isMsgNeedsMoreInfo = isMsgNeedsMoreInfo;
31305        vm.submitComment = submitComment;
31306        vm.showMoreReplies = showMoreReplies;
31307
31308        init();
31309
31310        function init() {
31311
31312            vm.replyLimit = 5;
31313
31314            //mark as read in the backend
31315            memberDataService.InboxMessageUpdateReadStatus(vm.data.message.id, true);
31316
31317            if (vm.data.message && vm.data.message.createDate) {
31318
31319                vm.todayDesc = inboxService.addTodayDesc(vm.data.message.createDate);
31320            }
31321
31322            console.log('reply modal: vm.data.message', vm.data.message);
31323        }
31324
31325        $rootScope.$on('uploaded-success', function (event, args) {
31326            sendComment(args.links);
31327        });
31328
31329        function showMoreReplies() {
31330            vm.replyLimit = vm.data.message.replies.length;
31331        }
31332
31333        function submitComment(form) {
31334
31335            if (!form.$valid) return;
31336
31337            const hasPendingUploads = plUploadInboxService.getHasFiles();
31338
31339            if (hasPendingUploads) {
31340                //notify uploader to upload files
31341                $scope.$broadcast('upload-attachment');
31342            } else {
31343                sendComment();
31344            }
31345        }
31346
31347        function sendComment(messageLinks) {
31348            /*if there is parent message id, it means replying to a message with reply, otherwise, it means replying to the orig. */
31349            const parentId = vm.data.message.parentMessageId ? vm.data.message.parentMessageId : vm.data.message.id;
31350            console.log('send comment: messageLinks', messageLinks);
31351            const payload = {
31352                parentMessageId: parentId,
31353                messageBody: vm.comment
31354            };
31355
31356            if (messageLinks && messageLinks.length > 0) {
31357                payload.MessageLinks = [...messageLinks];
31358            }
31359
31360            console.log('reply submit payload', payload);
31361
31362            memberDataService.sendComments(parentId, payload)
31363                .success(function (resp) {
31364                    console.log('submit comment api response', resp);
31365
31366                    vm.comment = '';
31367
31368                    vm.errorMessage = null;
31369
31370                    closeModal();
31371
31372                    //notify parent page, the reply is submitted
31373                    $rootScope.$emit('replied-success');
31374                    
31375                }).error(function (error) {
31376                    console.warn(error);
31377                    $scope.errorMessage = 'Error saving record.';
31378                });
31379        }
31380
31381        function closeModal() {
31382            $modalInstance.dismiss();
31383        }
31384
31385        function isMsgNeedsMoreInfo(message) {
31386            const needReply = inboxService.isNeededReply(message);
31387
31388            return needReply;
31389        }
31390    }
31391})();
31392(function () {
31393  "use strict";
31394  var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
31395
31396  angular
31397    .module("MemberPortalDirectives")
31398    .directive("inboxSorter", inboxSorter);
31399
31400  inboxSorter.$inject = [];
31401
31402  function inboxSorter() {
31403    const directive = {
31404      scope: {
31405        applySorting: "&",
31406      },
31407      restrict: "E",
31408        templateUrl: 'App/directives/portal-inbox/inbox-sorter.directive.html' +
31409            cacheBust,
31410      controller: inboxSorterController,
31411      controllerAs: "vs",
31412      bindToController: true,
31413    };
31414
31415    inboxSorterController.$inject = [];
31416
31417    function inboxSorterController() {
31418      const vm = this;
31419      vm.updateSorting = function (sortDirection) {
31420        vm.applySorting({ sortDirection: sortDirection });
31421      };
31422    }
31423
31424    return directive;
31425  }
31426})();
31427
31428(function () {
31429    'use strict';
31430    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
31431    angular
31432        .module('MemberPortalDirectives')
31433        .directive('inboxUpload', inboxUpload);
31434
31435    inboxUpload.$inject = [];
31436
31437    function inboxUpload() {
31438
31439        const directive = {
31440            scope: {},
31441            restrict: 'E',
31442            templateUrl: 'App/directives/portal-inbox/inbox-upload.directive.html' +
31443                cacheBust,
31444            controller: inboxUploadController,
31445            controllerAs: 'uploading',
31446            bindToController: true
31447        };
31448
31449        inboxUploadController.$inject = ['$rootScope', 'plUploadInboxService',
31450            '$timeout', '$scope'];
31451
31452        function inboxUploadController($rootScope, plUploadInboxService,
31453            $timeout, $scope) {
31454
31455            const vm = this;
31456
31457            init();
31458
31459            $scope.$on('upload-attachment', function () {
31460
31461                const hasPendingUploads = plUploadInboxService.getHasFiles();
31462                
31463                if (hasPendingUploads) {
31464                    uploadFiles();
31465                }
31466            });
31467
31468            function uploadFiles() {
31469
31470                vm.loading = true;
31471                vm.uploadError = '';
31472                
31473                plUploadInboxService.bindFilterObject();
31474
31475                $('#inboxSubmission').modal('show');
31476
31477                vm.modalMsg = 'Document submission is in progress, please wait...';
31478
31479                plUploadInboxService.submitDocuments();
31480            }
31481
31482            function init() {
31483
31484                $timeout(function () {
31485                        plUploadInboxService.initializePlupload(getSubmissionLogic);
31486                    },
31487                    500);
31488            }
31489
31490            function getSubmissionLogic() {
31491
31492                const instance = plUploadInboxService.getPluploadInstance();
31493
31494                instance.bind('UploadComplete', function (uploader, messageLinks) {
31495                    console.log('upload complete', messageLinks);
31496                    vm.uploadError = '';
31497                    vm.failed = [];
31498
31499                    const errors = plUploadInboxService.getUploadError();
31500
31501                    // upload error handling 
31502                    if (errors) {
31503                        console.log(' UPLOAD ERR!!!   --------------- ', errors);
31504                        const files = [];
31505                        errors.forEach(function (item) {
31506                            files.push(item.file.name);
31507                        });
31508
31509                        vm.modalMsg = 'The following files have failed to upload:';
31510                        vm.failed = files;
31511                        vm.uploadError = 'Please try again. ';
31512
31513                        vm.loading = false;
31514
31515                        return;
31516
31517                    } else {
31518                        vm.loading = false;
31519
31520                        vm.modalMsg = null;
31521
31522                        $('#inboxSubmission').modal('hide');
31523
31524                        $rootScope.$emit('uploaded-success', { links: messageLinks });
31525
31526                        console.log('no uploading errors');
31527                        
31528                        $('#plupLoadUploader').removeClass('no-after');
31529
31530                    }
31531                });
31532            }
31533        }
31534
31535        return directive;
31536    }
31537})();
31538(function () {
31539    'use strict';
31540
31541    const app = angular.module('MemberPortalDirectives');
31542
31543    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
31544    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
31545
31546    app.directive('providerChangeResult', function () {
31547        return {
31548            scope: {
31549                provider: '=',
31550                member: '='
31551            },
31552            restrict: 'EA',
31553            templateUrl: urlBase + '/App/directives/provider-change-result/providerChangeResult.html' + cacheBust,
31554            bindToController: true,
31555            controller: providerChangeResultCtrl,
31556            controllerAs: 'ctrl'
31557        };
31558    });
31559
31560
31561    app.controller('providerChangeResultCtrl', providerChangeResultCtrl);
31562
31563    providerChangeResultCtrl.$inject = ['$scope', 'memberDataService', '$rootScope', '$timeout'];
31564
31565    function providerChangeResultCtrl($scope, memberDataService, $rootScope, $timeout) {
31566        const vm = this;
31567
31568        var focusTimeout;
31569
31570        $scope.loading = false;
31571        //$scope.data = {};
31572        //$scope.dataString = {};
31573        //$scope.showLocation = false;
31574        $scope.errorMessage = '';
31575
31576        $scope.$on('destroy',
31577            function () {
31578                if (focusTimeout) $timeout.cancel(focusTimeout);
31579            });
31580
31581        $scope.focusOnElement = function (elementId) {
31582            focusTimeout = $timeout(function () {
31583                $('#' + elementId).focus();
31584            }, 500);
31585        };
31586
31587        vm.selectPcp = function (provider, contract) {
31588
31589            window.scrollTo(0, 0); $('#aChangeRequestDetails').focus();
31590          
31591            const param = {
31592                NewNPI: provider.Npi,
31593                NewDirectoryID: contract.DirectoryId,
31594                SelectedTermReason: '1MOTH' 
31595            };
31596
31597            memberDataService.SubmitPcpChange(param).success(function () {
31598
31599                $rootScope.$broadcast('pcp-selected', provider, contract);
31600
31601            }).error(function (msg) {
31602
31603                $scope.errorMessage = msg; //"Aw Snap! There was a problem canceling your pcp change request.";
31604            });
31605        };
31606
31607        $scope.isCompatible = function(providerIpaCode, memberIpaCode) {
31608
31609            const memberIsDirect = memberIpaCode.toLowerCase().indexOf('j') !== -1;
31610            const providerIsDirect = providerIpaCode.toLowerCase().indexOf('j') !== -1;
31611
31612            return !!(memberIsDirect && providerIsDirect) || providerIpaCode === memberIpaCode;
31613        };
31614    };
31615})();
31616
31617
31618
31619(function () {
31620    'use strict';
31621
31622    const app = angular.module('MemberPortalDirectives');
31623    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
31624    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
31625
31626    app.directive('providerSearchSlider', function () {
31627
31628        return {
31629            scope: {
31630                miles: '='
31631            },
31632            restrict: 'EA',
31633            templateUrl: urlBase + '/App/directives/provider-search-slider/providerSearchSlider.html' + cacheBust,
31634            bindToController: true,
31635            controller: providerSearchSliderCtrl,
31636            controllerAs: 'ctrl'
31637        };
31638    });
31639
31640    app.controller('providerSearchSliderCtrl', providerSearchSliderCtrl);
31641
31642    providerSearchSliderCtrl.$inject = ['$scope'];
31643
31644    function providerSearchSliderCtrl($scope) {
31645        const vm = this;
31646        vm.miles = 5;
31647
31648        vm.options = {
31649            from: 1,
31650            to: 20,
31651            step: 1,
31652            dimension: ' mi',
31653            css: {
31654                'background': { "background-color": 'white' },
31655                'before': { "background-color": 'white' },
31656                'default': { "background-color": 'white' },
31657                'after': { "background-color": 'white' },
31658                'pointer': {  },
31659                'range' : {"background-color": 'white'} // use it if double value
31660            }
31661        };
31662    };
31663})();
31664
31665
31666
31667(function () {
31668
31669    'use strict';
31670
31671    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
31672    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
31673
31674    const app = angular.module('MemberPortalDirectives');
31675
31676    app.directive('rxRemindersModal', function () {
31677
31678        return {
31679            scope: {
31680                prescription: '=',
31681                notification: '='
31682            },
31683            restrict: 'EA',
31684            templateUrl: urlBase + '/App/directives/rx-reminders/rx-reminders-modal.html' + cacheBust,
31685            bindToController: true,
31686            controller: rxRemindersController,
31687            controllerAs: 'rxrm'
31688        };
31689    });
31690
31691    rxRemindersController.$inject = ['$scope', '$filter', 'authStatusService', 'memberDataService', '$uibModalStack'];
31692
31693    function rxRemindersController($scope, $filter, authStatusService, memberDataService, $uibModalStack) {
31694    
31695        var vm = this;
31696
31697        vm.alertID = '';
31698        const authStatus = authStatusService.getStatus();
31699        vm.HasEmail = authStatus.claims.email !== 'N/A'; //true; //For developing set to true
31700
31701        vm.DaysBack = vm.prescription.DaysBack; //temp var, holds current state of modal, overwrites actual model in vm.prescription on save
31702        vm.IsNotificationRepeated = vm.prescription.IsNotificationRepeated;  //temp var, holds current state of modal, overwrites actual model in vm.prescription on save
31703        vm.displayInvalidSubmission = false;
31704
31705        vm.expirationDate = new Date($filter('date')(vm.prescription.OrderDate, 'MM/dd/yy', 'utc'));
31706        vm.expirationDate.setDate(vm.expirationDate.getDate() + Number(vm.prescription.DaysSupply));
31707
31708        vm.submitReminder = submitReminder;
31709        vm.removeReminder = removeReminder;
31710        vm.cancelReminder = cancelReminder;
31711        vm.closeModal = closeModal;
31712
31713        function submitReminder() {
31714
31715            if ($scope.rxReminderUpdate.$invalid) {
31716
31717                vm.displayInvalidSubmission = true;
31718            }
31719            else {
31720
31721                vm.prescription.IsReminderActive = true;
31722                const rxReminderObj = buildRxReminder(vm.prescription, vm.DaysBack, vm.IsNotificationRepeated);
31723                memberDataService.SaveRxReminder(rxReminderObj).success(function () {
31724                    vm.prescription.DaysBack = vm.DaysBack;
31725                    vm.prescription.IsNotificationRepeated = vm.IsNotificationRepeated;
31726                });
31727
31728                vm.closeModal();
31729            }
31730        };
31731
31732        function removeReminder() {
31733
31734            if (vm.prescription.IsReminderActive) {
31735
31736                const rxReminderObj = {
31737                    SubscriberId: vm.prescription.MemberId,
31738                    NDC: vm.prescription.NDC,
31739                    IsActive: false
31740                };
31741
31742                memberDataService.SaveRxReminder(rxReminderObj).success(function () {
31743
31744                    vm.prescription.DaysBack = null;
31745                    vm.prescription.IsNotificationRepeated = null;
31746                });
31747            }
31748
31749            vm.prescription.IsReminderActive = false;
31750
31751            vm.closeModal();
31752        }
31753
31754        function cancelReminder() {
31755           
31756            vm.closeModal();
31757        }
31758
31759        function closeModal() {
31760
31761            $uibModalStack.dismissAll();
31762        };
31763
31764        function buildRxReminder(rxReminder, daysBack, isRepeated) {
31765
31766            const rxReminderObj = {
31767                SubscriberId: rxReminder.MemberId,
31768                NDC: rxReminder.NDC,
31769                DaysBack: daysBack,
31770                IsNotificationRepeated: isRepeated,
31771                IsActive: true
31772            };
31773
31774            return rxReminderObj;
31775        };
31776
31777        vm.hitEnter = function (evt) {
31778
31779            if (angular.equals(evt.keyCode, 13) &&
31780                !(angular.equals(vm.name, null) ||
31781                    angular.equals(vm.name, '')))
31782                vm.save();
31783        };
31784    };
31785
31786})();
31787(function () {
31788
31789    'use strict';
31790
31791    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
31792
31793    const app = angular.module('MemberPortalDirectives');
31794
31795    app.directive('rxReminders', function () {
31796
31797        return {
31798            scope: {
31799                prescription: '=',
31800                notification: '='
31801            },
31802            restrict: 'EA',
31803            templateUrl: 'App/directives/rx-reminders/rx-reminders.html' +
31804                cacheBust,
31805            bindToController: true,
31806            controller: rxRemindersController,
31807            controllerAs: 'rxr'
31808        };
31809    });
31810
31811    rxRemindersController.$inject = ['$scope', '$uibModal'];
31812
31813    function rxRemindersController($scope, $uibModal) {
31814        const vm = this;
31815
31816        vm.isPrescriptionFilled = vm.prescription && vm.prescription.Type !== 'UnfulFilled' ? true : false;
31817
31818        vm.showReminderOptions = function () {
31819
31820            $uibModal.open({
31821                template: '<rx-reminders-modal prescription="rxr.prescription" notification="rxr.notification"></rx-reminders-modal>',
31822                scope: $scope
31823            });
31824        };
31825    }
31826})();
31827(function () {
31828    'use strict';
31829
31830    angular
31831        .module('MemberPortalDirectives')
31832        .directive('autoJump', autoJump);
31833
31834    function autoJump() {
31835
31836        const directive = {
31837            scope: {},
31838            link: link,
31839            restrict: 'A'
31840        };
31841
31842        function link(scope, element, attrs, ngModel) {
31843
31844            const inputField = element[0].querySelector('.form-control');
31845            //select all text in the field
31846            inputField.onclick = function(e) {
31847                const target = e.srcElement;
31848                if (target.tagName.toLowerCase() === 'input') {
31849                    target.select();
31850                    return;
31851                }
31852            }
31853
31854            const container = element[0];
31855            container.onkeyup = function (e) {
31856                //auto jump to the next field
31857                const target = e.srcElement;
31858
31859                const maxLength = parseInt(target.attributes['maxlength'].value, 10);
31860                const myLength = target.value.length;
31861                if (myLength >= maxLength) {
31862                    let next = target;
31863                    while (next = next.nextElementSibling) {
31864                        if (!next)
31865                            break;
31866                        if (next.tagName.toLowerCase() === 'input') {
31867                            next.focus()
31868                            next.select();
31869                            break;
31870                        }
31871                    }
31872                }
31873            }
31874        }
31875
31876        return directive;
31877
31878    }
31879
31880})();
31881(function () {
31882    'use strict';
31883
31884    const app = angular.module('MemberPortalDirectives');
31885
31886    app.directive('dropdown', function dropdown($rootScope) {
31887        return {
31888            restrict: 'E',
31889            templateUrl: 'templates/dropdown.html',
31890            scope: {
31891                placeholder: '@',
31892                list: '=',
31893                selected: '=',
31894                property: '@'
31895            },
31896            link: function (scope) {
31897                scope.listVisible = false;
31898                scope.isPlaceholder = true;
31899
31900                scope.select = function (item) {
31901                    scope.isPlaceholder = false;
31902                    scope.selected = item;
31903                };
31904
31905                scope.isSelected = function (item) {
31906                    return item[scope.property] === scope.selected[scope.property];
31907                };
31908
31909                scope.show = function () {
31910                    scope.listVisible = true;
31911                };
31912
31913                $rootScope.$on('documentClicked', function (inner, target) {
31914
31915                    if (!$(target[0]).is('.dropdown-display.clicked') &&
31916                        !$(target[0]).parents('.dropdown-display.clicked').length > 0)
31917                        scope.$apply(function () {
31918                            scope.listVisible = false;
31919                        });
31920                });
31921
31922                scope.$watch('selected', function () {
31923                    scope.isPlaceholder = scope.selected[scope.property] === undefined;
31924                    scope.display = scope.selected[scope.property];
31925                });
31926            }
31927        };
31928    });
31929
31930}) ();
31931(function () {
31932    const app = angular.module('MemberPortalDirectives');
31933
31934    app.directive('fullScreenAjaxLoader', function () {
31935        return {
31936            scope: {
31937                show: '=',
31938                full: '@'
31939            },
31940            restrict: 'EA',
31941            //replace: true,
31942            template: '<div ng-show="show" class="full-screen-bg"><div class="full-screen-loader" ng-class="{\'loading\':show}"></div>'
31943
31944        };
31945    });
31946
31947
31948})();
31949(function () {
31950    'use strict';
31951
31952
31953    const app = angular.module('MemberPortalDirectives');
31954
31955    app.directive('infoBoxWithCloseButton', function infoBoxWithCloseButton() {
31956        return {
31957            scope: {
31958                message: '@'
31959            },
31960            restrict: 'EA',
31961            template: '<div class="info bg-info alert alert-info" ng-show="message != \'\'">{{message}}   <i class="glyphicon glyphicon-remove" /></div>'
31962        };
31963    });
31964
31965})();
31966(function () {
31967    'use strict';
31968
31969
31970    const app = angular.module('MemberPortalDirectives');
31971
31972    app.directive('messageBox', function messageBox() {
31973        return {
31974            scope: {
31975                message: '@',
31976                type: '@'
31977            },
31978            restrict: 'EA',
31979            template: '<div class="info bg-{{type}} alert alert-{{type}}"><p>{{message}}</p></div>'
31980        };
31981    });
31982
31983})();
31984(function () {
31985    'use strict';
31986
31987
31988    const app = angular.module('MemberPortalDirectives');
31989
31990    app.directive('messageBoxes', function messageBoxes() {
31991        return {
31992            scope: {
31993                errorMessage: '@',
31994                infoMessage: '@',
31995                successMessage: '@'
31996            },
31997            restrict: 'EA',
31998            template: '<danger-box message="{{errorMessage}}"></danger-box>' +
31999                      '<info-box message="{{infoMessage}}"></info-box>' +
32000                      '<success-box message="{{successMessage}}"></success-box>'
32001        };
32002    });
32003
32004})();
32005(function () {
32006    'use strict';
32007
32008    angular
32009        .module('MemberPortalDirectives')
32010        .directive('numberOnly', numberOnly);
32011
32012    function numberOnly() {
32013
32014        const directive = {
32015            scope: {},
32016            require: 'ngModel',
32017            link: link,
32018            restrict: 'A'
32019        };
32020
32021        function link(scope, element, attrs, ngModel) {
32022            function inputValue(val) {
32023                if (val) {
32024                    const digits = val.replace(/[^0-9]/g, '');
32025                    if (digits !== val) {
32026                        ngModel.$setViewValue(digits);
32027                        ngModel.$render();
32028                    }
32029                    return parseInt(digits, 10);
32030                }
32031                return undefined;
32032            }
32033            ngModel.$parsers.push(inputValue);
32034        }
32035
32036        return directive;
32037
32038    }
32039
32040})();
32041(function () {
32042    'use strict';
32043
32044    angular
32045        .module('MemberPortalDirectives')
32046        .directive('urgentContactList', urgentContactList);
32047
32048    const urlBase = $('meta[name="ApplicationRoot"]').attr('content');
32049
32050    const cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
32051
32052    urgentContactList.$inject = [];
32053
32054    function urgentContactList() {
32055
32056        const directive = {
32057            scope: {
32058                loading: '=',
32059                phoneList: '='
32060            },
32061            restrict: 'E',
32062            templateUrl: urlBase + '/App/directives/shared/urgent-contact-list.directive.html' + cacheBust,
32063            controller: urgentContactListController,
32064            controllerAs: 'ucl',
32065            bindToController: true
32066        };
32067
32068        urgentContactListController.$inject = [];
32069
32070        function urgentContactListController() {
32071
32072            const vm = this;
32073        }
32074
32075        return directive;
32076    }
32077})();
32078(function () {
32079    'use strict';
32080
32081    angular
32082        .module('MemberPortal')
32083        .controller('confirmationModal', confirmationModal);
32084
32085    confirmationModal.$inject = ['data', '$modalInstance'];
32086
32087    function confirmationModal(data, $modalInstance) {
32088     
32089        const vm = this;
32090
32091        vm.data = data;
32092        vm.onNo = onNo;
32093        vm.onYes = onYes;
32094        
32095        console.log('confirmationModal', vm.data);
32096
32097        function onNo() {
32098            
32099            $modalInstance.dismiss();
32100        }
32101
32102        function onYes() {
32103
32104            vm.data.answer = true;
32105
32106            $modalInstance.close(vm.data.answer);
32107        }
32108    }
32109})();
32110(function () {
32111    'use strict';
32112    angular
32113        .module('MemberPortalDirectives')
32114        .directive('dangerBox', dangerBox);
32115
32116    dangerBox.$inject = ['environmentConfig'];
32117    function dangerBox(environmentConfig) {
32118        const directive = {
32119            restrict: 'AE',
32120            templateUrl: environmentConfig.appRoot + '/App/directives/shared/danger-box/danger-box.directive.html' +
32121                environmentConfig.cacheBust,
32122            scope: {
32123                message: '@',
32124            },
32125            controller: dangerBoxController,
32126            controllerAs: 'dangerBox',
32127            bindToController: true
32128        };
32129
32130        dangerBoxController.$inject = [];
32131        function dangerBoxController() {
32132
32133            const vm = this;
32134        }
32135
32136        return directive;
32137    }
32138})();
32139(function () {
32140    'use strict';
32141
32142    angular
32143        .module('MemberPortalDirectives')
32144        .directive('definitionTable', definitionTable);
32145
32146    definitionTable.$inject = ['environmentConfig'];
32147
32148    function definitionTable(environmentConfig) {
32149
32150        const directive = {
32151            scope: {
32152                title: '@',
32153                definitions: '=',
32154                headerCssClass: "@?",
32155                definitionCssClass: "@?"
32156            },
32157            restrict: 'E',
32158            templateUrl: environmentConfig.appRoot + '/App/directives/shared/definition-table/definition-table.directive.html' + environmentConfig.cacheBust,
32159            controller: definitionTableController,
32160            controllerAs: 'dt',
32161            bindToController: true
32162        };
32163
32164        definitionTableController.$inject = ['$sce'];
32165
32166        function definitionTableController($sce) {
32167
32168            const vm = this;
32169
32170            vm.isShownDefinition = false;
32171
32172            vm.parseAsHtml = parseAsHtml;
32173
32174            function parseAsHtml(str) {
32175                return $sce.trustAsHtml(str);
32176            }
32177
32178            //console.log('headerCssClass', vm.headerCssClass);
32179            //console.log('definitionCssClass', vm.definitionCssClass);
32180        }
32181
32182        return directive;
32183    }
32184})();
32185(function () {
32186    'use strict';
32187
32188    angular
32189        .module('MemberPortalDirectives')
32190        .directive('expandableBox', expandableBox);
32191        
32192    expandableBox.$inject = ['environmentConfig'];
32193
32194    function expandableBox(environmentConfig) {
32195
32196        const directive = {
32197            scope: {
32198                formType: '@',
32199                editable: '=',
32200                isFirstRecord: '=?',
32201                parentFields: '=',
32202                childFields: '=',
32203                status: '=',
32204                options: '=',
32205                cssClass: '=',
32206                transactionId: '=',
32207                maxAllowedDate: '=',
32208                actionTrigger: '&'
32209            },
32210            restrict: 'E',
32211            templateUrl: function(tElement, tAttrs) {
32212                const type = tAttrs.formType? `-${tAttrs.formType}`: '';
32213
32214                return environmentConfig.appRoot +
32215                    '/App/directives/shared/expandable-box/expandable-box' + type + '.directive.html' +
32216                    environmentConfig.cacheBust;
32217            },
32218            controller: expandableBoxController,
32219            controllerAs: 'eb',
32220            bindToController: true
32221        };
32222
32223        expandableBoxController.$inject = ['authStatusService'];
32224
32225        function expandableBoxController(authStatusService) {
32226
32227            const vm = this;
32228
32229            init();
32230
32231            function init() {
32232
32233                //console.log('action code', vm.options);
32234                //console.log('parentFields', vm.parentFields);
32235
32236                const authStatus = authStatusService.getStatus();
32237
32238                vm.isCCA = authStatus.isCCA;
32239                console.log('isCCA', vm.isCCA);
32240                //we expand the top record if user needs consent to approve
32241                vm.hideDetails = !vm.maxAllowedDate || !vm.isFirstRecord;
32242
32243                let accessDateRange = '';
32244
32245                //console.log('vm.childFields', vm.childFields);
32246                if (vm.formType === 'processed') {
32247                    accessDateRange = vm.childFields[1];
32248                }
32249                else if (vm.formType === 'pending') {
32250                    accessDateRange = vm.childFields[3];
32251                }
32252                
32253                console.log('accessDateRange', accessDateRange);
32254
32255                if (accessDateRange && accessDateRange.value) {
32256                    vm.accessStartDate = accessDateRange.value.split(' - ')[0];
32257                    vm.accessEndDate = accessDateRange.value.split(' - ')[1];
32258                }
32259            }
32260
32261           // console.log('expandable: editable', vm.editable);
32262            vm.actionClicked = function (requestId, code, startDate, endDate) {
32263                //console.log('action clicked', code);
32264                //console.log('requestId', requestId);
32265
32266                vm.actionTrigger({
32267                    requestId: requestId,
32268                    code: code,
32269                    start: startDate,
32270                    end: endDate
32271                });
32272            }
32273        }
32274
32275        return directive;
32276    }
32277})();
32278(function () {
32279    'use strict';
32280
32281    angular
32282        .module('MemberPortalDirectives')
32283        .directive('fromToDatepicker', fromToDatepicker);
32284
32285    fromToDatepicker.$inject = ['environmentConfig', '$filter'];
32286
32287    function fromToDatepicker(environmentConfig, $filter) {
32288
32289        const directive = {
32290            scope: {
32291                fromDate: '=',
32292                toDate: '=',
32293                minAllowedDate: '=?',
32294                maxAllowedDate: '=?',
32295                allowEdit: '=?',
32296                readOnly: '=?',
32297                allowUndo: '=?'
32298            },
32299            restrict: 'E',
32300            templateUrl: environmentConfig.appRoot + '/App/directives/shared/from-to-datepicker/from-to-datepicker.directive.html' + environmentConfig.cacheBust,
32301            controller: fromToDatepickerController,
32302            //dr: date range
32303            controllerAs: 'dr' ,
32304            bindToController: true
32305        };
32306
32307        fromToDatepickerController.$inject = ['$scope'];
32308
32309        function fromToDatepickerController($scope) {
32310      
32311            const vm = this;
32312
32313            vm.enterEditMode = enterEditMode;
32314
32315            vm.validateDate = validateDate;
32316
32317            vm.discardDateChanges = discardDateChanges;
32318
32319            init();
32320
32321            function init() {
32322
32323               // console.log('start', vm.fromDate);
32324               // console.log('end', vm.toDate);
32325
32326                vm.dateRangeFrm = {};
32327
32328                //store original values so the fields can be reset.
32329                vm.origFromDate = vm.fromDate;
32330                vm.origToDate = vm.toDate;
32331         
32332                vm.defaultStartDateString = vm.fromDate.replace(/(\d\d)\/(\d\d)\/(\d{4})/, "$3-$1-$2");
32333
32334                if (vm.maxAllowedDate) {
32335                    vm.defaultEndDateString = vm.maxAllowedDate.replace(/(\d\d)\/(\d\d)\/(\d{4})/, "$3-$1-$2");
32336                }
32337
32338                if (!vm.minAllowedDate) {
32339                    vm.minAllowedDate = moment().format('MM/DD/YYYY');
32340                }
32341
32342                if (vm.readOnly == null) {
32343                    //by default, hide datepicker, show date range 
32344                    vm.readOnly = true;
32345                }
32346
32347                if (vm.allowEdit == null) {
32348                    //by default, not allow editing
32349                    vm.allowEdit = false;
32350                }
32351            }
32352
32353            $scope.$on('newDelegateSubmitted',
32354                function() {
32355                    // console.log('submitting date range', vm.dateRangeFrm);
32356                    //needed to trigger ng-form validation on submission
32357                    vm.dateRangeFrm.$setSubmitted();
32358                });
32359
32360            function discardDateChanges() {
32361                vm.fromDate = vm.origFromDate;
32362                vm.toDate = vm.origToDate;
32363
32364                vm.readOnly = true;
32365            }
32366
32367            function enterEditMode() {
32368
32369                vm.dateRangeFrm.$setPristine();
32370                vm.dateRangeFrm.$setUntouched();
32371
32372                vm.readOnly = false;
32373            }
32374
32375            function validateDate(fromDate, endDate) {
32376                console.log('validating date...');
32377                console.log('vm.maxAllowedDate', vm.maxAllowedDate);
32378                console.log('vm.minAllowedDate', vm.minAllowedDate);
32379                console.log('fromDate', fromDate);
32380                console.log('endDate', endDate);
32381
32382                vm.dateRangeFrm.fromDate.$setValidity('eligError', true);
32383                vm.dateRangeFrm.endDate.$setValidity('eligError', true);
32384                vm.dateRangeFrm.endDate.$setValidity('dateRangeError', true);
32385
32386                if (vm.minAllowedDate) {
32387                    
32388                    if (fromDate && moment(fromDate).isBefore(moment(vm.minAllowedDate))) {
32389                        vm.dateRangeFrm.fromDate.$setValidity('eligError', false);
32390                        console.warn('from date cannot be earlier than min date');
32391                        vm.eligErrorFromDate = $filter('translate')('page_delegate_date_validation_eligibility_msg_1');
32392                    }
32393             
32394                    if (endDate && moment(endDate).isBefore(moment(vm.minAllowedDate))) {
32395                        vm.dateRangeFrm.endDate.$setValidity('eligError', false);
32396                        vm.eligErrorEndDate = $filter('translate')('page_delegate_date_validation_eligibility_msg_1');
32397                        console.warn('End date cannot be earlier than min date');
32398                    }
32399                }
32400
32401                if (vm.maxAllowedDate) {
32402
32403                    if (fromDate && moment(fromDate).isAfter(moment(vm.maxAllowedDate))) {
32404                        vm.eligErrorFromDate = $filter('translate')('page_delegate_date_validation_eligibility_msg');
32405                        vm.dateRangeFrm.fromDate.$setValidity('eligError', false);
32406                        console.warn('from date cannot be over api max date');
32407                    } 
32408
32409                    if (endDate && moment(endDate).isAfter(moment(vm.maxAllowedDate))) {
32410                        vm.eligErrorEndDate = $filter('translate')('page_delegate_date_validation_eligibility_msg');
32411                        vm.dateRangeFrm.endDate.$setValidity('eligError', false);
32412                        console.warn('end date cannot be over api max date');
32413                    } 
32414                }
32415
32416                if (fromDate && endDate && moment(fromDate).isAfter(moment(endDate))) {
32417                    vm.explicitRangeError = $filter('translate')('page_delegate_date_validation_msg');
32418
32419                    if (!vm.dateRangeFrm.endDate.$error.eligError) {
32420                        vm.dateRangeFrm.endDate.$setValidity('dateRangeError', false);
32421                    }
32422
32423                    console.warn('end date cannot be earlier than from date');
32424                }
32425            }
32426
32427        }
32428
32429        return directive;
32430    }
32431})();
32432(function () {
32433    'use strict';
32434
32435    angular
32436        .module('MemberPortalDirectives')
32437        .directive('iehpBadge', iehpBadge);
32438
32439    iehpBadge.$inject = ['environmentConfig'];
32440    function iehpBadge(environmentConfig) {
32441
32442        const directive = {
32443            scope: {
32444                positionClass: '@',
32445                customClasses: '@',
32446                displayText: '=?',
32447                badgeType: '@'
32448            },
32449            restrict: 'AE',
32450            templateUrl: environmentConfig.appRoot +
32451                '/App/directives/shared/iehp-badge/iehp-badge.directive.html'
32452                + environmentConfig.cacheBust,
32453            controller: iehpBadgeController,
32454            controllerAs: 'badge',
32455            bindToController: true
32456        };
32457
32458        iehpBadgeController.$inject = ['$scope', '$rootScope'];
32459
32460        function iehpBadgeController($scope, $rootScope) {
32461
32462            const vm = this;
32463            
32464            $rootScope.$on('refreshPendingTotal',
32465                function (event, pendingTotal) {
32466                   // console.log('badge refresh pending total', pendingTotal);
32467                    vm.displayText = pendingTotal;
32468                });
32469        }
32470
32471        return directive;
32472    }
32473})();
32474(function () {
32475    'use strict';
32476
32477    angular
32478        .module('MemberPortalDirectives')
32479        .directive('iehpTag', iehpTag);
32480
32481    
32482    iehpTag.$inject = ['environmentConfig'];
32483
32484    function iehpTag(environmentConfig) {
32485
32486        const directive = {
32487            scope: {
32488                tagText: '@',
32489                tagCssClass: '@',
32490                tagHelperText: '@?'
32491            },
32492            restrict: 'E',
32493            templateUrl: environmentConfig.appRoot +
32494                '/App/directives/shared/iehp-tag/iehp-tag.directive.html' + environmentConfig.cacheBust,
32495            controller: iehpTagController,
32496            controllerAs: 'tag',
32497            bindToController: true
32498        };
32499
32500        iehpTagController.$inject = [];
32501
32502        function iehpTagController() {}
32503
32504        return directive;
32505    }
32506})();
32507(function () {
32508    angular.module('MemberPortalDirectives')
32509        .directive('indicatorLight', function () {
32510        return {
32511            scope: {
32512                alignRight: '@ilAlignRight',
32513                config: '=ilConfig',
32514                src: '=ilSource',
32515                orange: '@ilOrange',
32516                red: '@ilRed',
32517                green: '@ilGreen',
32518                defaultHidden: '@'
32519            },
32520            template: [
32521                '<span>',
32522                '<ng-transclude></ng-transclude>',
32523                '<span>'].join(''),
32524            transclude: true,
32525            restrict: 'EA',
32526            link: function (scope, element) {
32527                var red, orange, green, grey, blue, defaultHidden;
32528
32529                //If config object is provided, it will override scope inputs (except source)
32530                if (scope.config) {
32531                    //console.log('use config', scope.config);
32532                    red = scope.config.red || [];
32533                    orange = scope.config.orange || [];
32534                    green = scope.config.green || [];
32535                    grey = scope.config.grey || [];
32536                    blue = scope.config.blue || [];
32537                    defaultHidden = scope.config.defaultHidden || false;
32538                } else {
32539
32540                    red = scope.red || [];
32541                    orange = scope.orange || [];
32542                    green = scope.green || [];
32543                    grey = scope.grey || [];
32544                    blue = scope.blue || [];
32545                    defaultHidden = scope.defaultHidden || false;
32546                }
32547
32548                const light = angular.element('<span class="indicator-light"></span>');
32549
32550                if (scope.alignRight) {
32551                    element.find('span').eq(0).append(light.css('margin-left', '0.5rem'));
32552                } else {
32553                    element.find('span').eq(0).prepend(light.css('margin-right', '0.5rem'));
32554                }
32555
32556                light.removeClass('red');
32557                light.removeClass('green');
32558                light.removeClass('orange');
32559                light.removeClass('grey');
32560                light.removeClass('blue');
32561
32562                if (red.indexOf(scope.src) > -1) {
32563                    light.addClass('red');
32564                } else if (orange.indexOf(scope.src) > -1) {
32565                    light.addClass('orange');
32566                } else if (green.indexOf(scope.src) > -1) {
32567                    light.addClass('green');
32568                } else if (grey.indexOf(scope.src) > -1) {
32569                    light.addClass('grey');
32570                } else if (blue.indexOf(scope.src) > -1) {
32571                    light.addClass('blue');
32572                } else if (defaultHidden) {
32573                    light.css('display', 'none');
32574                }
32575            }
32576        }
32577    });
32578})();
32579(function () {
32580    'use strict';
32581    angular
32582        .module('MemberPortalDirectives')
32583        .directive('infoBox', infoBox);
32584
32585    infoBox.$inject = ['environmentConfig'];
32586    function infoBox(environmentConfig) {
32587        const directive = {
32588            restrict: 'AE',
32589            templateUrl: environmentConfig.appRoot + '/App/directives/shared/info-box/info-box.directive.html' +
32590                environmentConfig.cacheBust,
32591            scope: {
32592                message: '@',
32593            },
32594            controller: infoBoxController,
32595            controllerAs: 'infoBox',
32596            bindToController: true
32597        };
32598
32599        infoBoxController.$inject = [];
32600        function infoBoxController() {
32601
32602            const vm = this;
32603        }
32604
32605        return directive;
32606    }
32607})();
32608(function () {
32609    'use strict';
32610
32611    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
32612
32613    const app = angular.module('MemberPortalDirectives');
32614
32615    app.directive('keepAlive', [
32616        function() {
32617        return {
32618                    scope: {},
32619                    restrict: 'EA',
32620                    controller: 'keepAliveController',
32621                    controllerAs: 'ctrl',
32622                    templateUrl: 'App/directives/shared/keep-alive/keep-alive.directive.html' +
32623                        cacheBust
32624                };
32625            }
32626        ]);
32627
32628    app.controller('keepAliveController', keepAliveController);
32629
32630    keepAliveController.$inject = [
32631        '$scope',
32632        '$document',
32633        'authStatusService',
32634        'authService',
32635        '$filter',
32636        '$location',
32637        '$timeout',
32638        'environmentConfig'
32639    ];
32640
32641        function keepAliveController($scope,
32642            $document,
32643            authStatusService,
32644            authService,
32645            $filter,
32646            $location,
32647            $timeout,
32648            environmentConfig) {
32649
32650            var idleTime = 0;
32651            var idleInterval;
32652            var activityPromptCountdownIntervalId;
32653
32654            var logoutTimeout;
32655            var refreshTokenTimeout;
32656
32657            $scope.showActivityPrompt = false;
32658            $scope.errorMessage = '';
32659
32660            const timeoutMilliseconds = environmentConfig.appMode === 'Prod'? 60000: 300000; //1 min in prod, 5 min in dev
32661
32662            init();
32663
32664            function init() {
32665
32666                setActivityDetectors();
32667
32668                idleInterval = window.setInterval(incrementTimer, timeoutMilliseconds);
32669
32670                $scope.showActivityPrompt = false;
32671            }
32672
32673            function incrementTimer() {
32674                //console.log('checking user activity...');
32675              
32676                const authStatus = authStatusService.getStatus();
32677
32678                //If not signed in, return and ignore the rest of this function
32679                if (!authStatus.isAuth) {
32680                    console.log("user is not logged in");
32681                    $scope.$apply();
32682                    return;
32683                }
32684
32685                idleTime++; //Adds 1 (minute) to idleTime
32686                //console.log("add 1 min. to idle time", idleTime);
32687                //Check local_issue_date
32688                const expirationDate = new Date(authStatus.claims.exp * 1000);
32689
32690                //How long has it been since issued?
32691                var timeRemaining = expirationDate - new Date();  //Difference in milliseconds
32692
32693                if (timeRemaining <= 0) {
32694                    console.log("time is up, logging out...");
32695                    $scope.logMeOut();
32696
32697                } else {
32698                    timeRemaining = (timeRemaining / 1000) / 60; //Converted to Minutes
32699                }
32700
32701                //Uncomment, to test ActivityPrompt
32702                //timeRemaining = timeRemaining - idleTime * 3;
32703               // console.log("time remaining in minutes", timeRemaining);
32704                //Check time remaining, if less than 3 min, begin timeout process
32705                if (timeRemaining < 3) {
32706
32707                    if (idleTime < 3) {
32708                        console.log("token is going to expire shortly and " +
32709                            "user has been active, refreshing token");
32710                        authService.refreshToken();
32711
32712                    } else {
32713                        console.log("token is going to expire shortly and" +
32714                            "user has been idling for over 3 min, going to prompt");
32715                        //prompt user for activity: "Are you still there?"
32716                        //set 1 minute countdown, and logout if no response
32717                        initActivityPrompt();
32718                    }
32719                }
32720
32721                $scope.$apply();
32722            };
32723
32724            function setActivityDetectors() {
32725                $document.on('click', resetIdleTime);
32726                $document.on('mousemove', resetIdleTime);
32727                $document.on('keypress', resetIdleTime);
32728                $document.on('scroll', resetIdleTime);
32729            };
32730
32731            function resetIdleTime() {
32732                idleTime = 0;
32733            };
32734
32735            function initActivityPrompt() {
32736
32737                $scope.showActivityPrompt = true;
32738
32739                $scope.ActivityPromptCountdown = 60;
32740
32741                window.clearInterval(idleInterval);
32742
32743                activityPromptCountdownIntervalId = window.setInterval(decrementLogoutCountdown, 1000);
32744            };
32745
32746            function decrementLogoutCountdown() {
32747
32748               // console.log('counting down before logging out', $scope.ActivityPromptCountdown);
32749
32750                $scope.ActivityPromptCountdown--;
32751
32752                if ($scope.ActivityPromptCountdown === 0) {
32753
32754                    window.clearInterval(activityPromptCountdownIntervalId);
32755                    $scope.showActivityPrompt = false;
32756
32757                   logoutTimeout = $timeout(function () {
32758                        $scope.logMeOut();
32759                        //$scope.$apply();
32760                    }, 1000);
32761                }
32762
32763                $scope.$apply();
32764            };
32765
32766            $scope.$on('$destroy', function () {
32767                window.clearInterval(idleInterval);
32768                window.clearInterval(activityPromptCountdownIntervalId);
32769
32770                if (logoutTimeout) $timeout.cancel(logoutTimeout);
32771
32772                if (refreshTokenTimeout) $timeout.cancel(refreshTokenTimeout);
32773            });
32774
32775            $scope.stopIdle = function () {
32776
32777                window.clearInterval(activityPromptCountdownIntervalId);
32778                $scope.showActivityPrompt = false;
32779                console.log("don't log me out. refreshing token and resetting auth status");
32780
32781                $scope.errorMessage = '';
32782
32783                refreshTokenTimeout = $timeout(function () {
32784                    authService.refreshToken().then(function () {
32785                        console.log("token refreshed");
32786                        idleTime = 0;
32787
32788                        idleInterval = setInterval(incrementTimer, timeoutMilliseconds);
32789                    },
32790                        function (reason) {
32791                            console.error('refresh token error', reason);
32792                            //Handle error on refresh
32793                            $scope.errorMessage = $filter('translate')('need_to_log_in');
32794                        }
32795                    );
32796                }, 500);
32797
32798
32799            };
32800
32801            $scope.logMeOut = function () {
32802                console.log("logging out. clear interval", activityPromptCountdownIntervalId);
32803
32804                $scope.showActivityPrompt = false;
32805
32806                window.clearInterval(activityPromptCountdownIntervalId);
32807                
32808                authService.logOut();
32809
32810                authStatusService.clearAll();
32811                $scope.isAuth = false;
32812
32813                const urlBase = $('meta[name="ApplicationRoot"]').attr('content');
32814
32815                $location.path(urlBase + '/App/Account/Login');
32816            };
32817
32818            $scope.$on('inactivity-prompt', function () {
32819                //debug only
32820                initActivityPrompt();
32821
32822            });
32823        };
32824})();
32825(function () {
32826    'use strict';
32827
32828    angular
32829        .module('MemberPortalDirectives')
32830        .directive('profileSwitch', profileSwitch);
32831        
32832    profileSwitch.$inject = ['environmentConfig'];
32833    function profileSwitch(environmentConfig) {
32834
32835        const directive = {
32836            scope: {},
32837            restrict: 'E',
32838            templateUrl: environmentConfig.appRoot +
32839                '/App/directives/shared/profile-switch/profile-switch.directive.html'
32840                + environmentConfig.cacheBust,
32841            controller: profileSwitchController,
32842            controllerAs: 'ps',
32843            bindToController: true
32844        };
32845
32846        profileSwitchController.$inject = [
32847            'memberDataService',
32848            'authStatusService',
32849            'utilitiesService',
32850            '$timeout',
32851            '$scope',
32852            '$rootScope',
32853            'authService',
32854            'healthDelegateService',
32855            '$location'
32856        ];
32857
32858        function profileSwitchController(memberDataService, authStatusService,
32859            utilitiesService, $timeout, $scope, $rootScope, authService,
32860            healthDelegateService, $location) {
32861
32862            const vm = this;
32863
32864            vm.switchProfile = switchProfile;
32865
32866            var delegateTimeout;
32867
32868            init();
32869
32870            function init() {
32871
32872                vm.firstName = utilitiesService.capFirstLetter($rootScope.firstName);
32873
32874                vm.isProxied = authStatusService.checkIfProxied();
32875
32876                const authStatus = authStatusService.getStatus();
32877
32878                const isDelegateEligible = vm.isProxied? true: healthDelegateService.getIsCaregiverEligible();
32879
32880                if (!isDelegateEligible) { return; }
32881
32882                vm.delegates = [];
32883
32884                vm.indicatorConfig = {
32885                    green: [true],
32886                    defaultHidden: false
32887                };
32888
32889                vm.isCaregiver = authStatus.claims?.proxymembers !== 'N/A';
32890
32891                delegateTimeout = $timeout(getDelegateList, 500);
32892            }
32893
32894            $rootScope.$on('obfuscation_toggled', function () {
32895                const testUserMode = $rootScope.TestUserMode;
32896
32897                if (testUserMode) {
32898                    vm.firstName = $filter('obfuscateValue')(vm.firstName);
32899                }
32900            });
32901
32902            $rootScope.$on('logon-success', function () {
32903                init();
32904            });
32905
32906            $rootScope.$on('refreshPendingTotal', function () {
32907                init();
32908            });
32909
32910            function getDelegateList() {
32911
32912                const authStatus = authStatusService.getStatus();
32913
32914                if (!authStatus || !authStatus.isAuth) return;
32915
32916                vm.loading = true;
32917
32918                vm.internaluser = authStatus.claims.internaluser;
32919
32920                vm.firstName = utilitiesService.capFirstLetter($rootScope.firstName);
32921
32922                const isProxied = authStatusService.checkIfProxied();
32923
32924                vm.memberNumber = isProxied? authStatus.claims.origOwnerInfo.username: authStatus.claims.username;
32925
32926                memberDataService.GetActiveDelegates(vm.memberNumber).then(
32927                    function(results) {
32928                        //console.log('active delegates results', results);
32929                        if (!results || !results.data ||
32930                            (angular.isArray(results.data) && results.data.length === 0) ||
32931                            Object.keys(results.data).length === 0) {
32932                                
32933                            return;
32934                        }
32935                        
32936                        results.data.forEach(function(item) {
32937
32938                            item.memberName = getMemberNameLabelText(item, authStatus);
32939
32940                            item.status = setStatus(results.data, item);
32941                            
32942                        });
32943
32944                        results.data.sort((a, b) => a.firstName.localeCompare(b.firstName));
32945                     
32946                        vm.delegates = results.data;
32947                        
32948                       // console.log('delegates', vm.delegates);
32949                    }, function() {
32950                        vm.delegates = [];
32951                        console.warn('error loading delegates');
32952                    }).finally(function() {
32953                    vm.loading = false;
32954                });
32955            }
32956
32957            function getMemberNameLabelText(item, authStatus) {
32958
32959                const isProxied = authStatusService.checkIfProxied();
32960
32961                const originalOwnerMemberNumber =
32962                    isProxied ? authStatus.claims.origOwnerInfo.username :
32963                                authStatus.claims.username;
32964
32965                const self = item.memberNumber === originalOwnerMemberNumber ? ' (self)' : '';
32966
32967                return utilitiesService.capFirstLetter(item.firstName) +
32968                    ' ' +
32969                    utilitiesService.capFirstLetter(item.lastName) +
32970                    self;
32971            }
32972
32973            function setStatus(delegateList, result) {
32974
32975                const claims = authStatusService.getStatus().claims;
32976              
32977                return claims.proxiedmembernumber === 'N/A' ? result.delegator : result.memberNumber === claims.proxiedmembernumber;
32978            }
32979
32980            function postProfileSwitchLogic() {
32981
32982                delegateTimeout = $timeout(getDelegateList, 1000);
32983
32984                $location.path('App/Home');
32985            }
32986
32987            $scope.$on('destroy',
32988                function() {
32989                    $timeout.cancel(delegateTimeout);
32990                });
32991
32992            function switchProfile(profile) {
32993
32994                vm.loading = true;
32995
32996                const claims = authStatusService.getStatus().claims;
32997                console.log('claims', claims);
32998                const currentProxiedMemberNumber = claims.proxiedmembernumber;
32999
33000                const isProxied = authStatusService.checkIfProxied();
33001
33002                const origOwnerMemberId = isProxied? claims.origOwnerInfo.username: claims.username;
33003                
33004                if (currentProxiedMemberNumber === 'N/A' && profile.memberNumber === origO
33004wnerMemberId) return;
33005
33006                if (currentProxiedMemberNumber !== 'N/A' && currentProxiedMemberNumber === profile.memberNumber) return;
33007                
33008                console.log('switching...');
33009
33010                if (profile.memberNumber === origOwnerMemberId) {
33011
33012                    $rootScope.$broadcast('switchingProfile', { loadingProfile: true });
33013
33014                    authService.switchToDefault(profile).then(function () {
33015                        vm.isProxied = false;
33016
33017                        postProfileSwitchLogic();
33018
33019                        },
33020                        function (error) {
33021                
33022                        Toastify({
33023                            text: error.Message,
33024                            offset: {
33025                                x: 0,
33026                                y: '3em'
33027                            },
33028                            backgroundColor: '#FFDCE0',
33029                            duration: 3000,
33030                            gravity: 'top',
33031                            position: 'center',
33032                            style: {
33033                                borderRadius: '5px',
33034                                color: '#B50303',
33035                                fontSize: '16px',
33036                                fontWeight: 'bold'
33037                            }
33038                        }).showToast();
33039                    })
33040                    .finally(function () {
33041                        $rootScope.$broadcast('switchingProfile', { loadingProfile: false });
33042                        vm.loading = false;
33043                    });
33044
33045                } else {
33046
33047                    authService.switchToken(profile).then(function () {
33048
33049                        $rootScope.$broadcast('switchingProfile', { loadingProfile: true });
33050
33051                        vm.isProxied = true;
33052                        
33053                        postProfileSwitchLogic();
33054
33055                    }, function (error) {
33056       
33057                        Toastify({
33058                            text: error.Message,
33059                            offset: {
33060                                x: 0,
33061                                y: '3em'
33062                            },
33063                            backgroundColor: '#FFDCE0',
33064                            duration: 3000,
33065                            gravity: 'top',
33066                            position: 'center',
33067                            style: {
33068                                borderRadius: '5px',
33069                                color: '#B50303',
33070                                fontSize: '16px',
33071                                fontWeight: 'bold'
33072                            }
33073                        }).showToast();
33074                    })
33075                    .finally(function () {
33076                        $rootScope.$broadcast('switchingProfile', { loadingProfile: false });
33077                        vm.loading = false;
33078                    });
33079                }
33080            }
33081        }
33082
33083        return directive;
33084    }
33085})();
33086(function () {
33087    'use strict';
33088
33089    angular
33090        .module('MemberPortalDirectives')
33091        .directive('skeletonLoader', skeletonLoader);
33092
33093    skeletonLoader.$inject = ['environmentConfig'];
33094
33095    function skeletonLoader(environmentConfig) {
33096
33097        const directive = {
33098            scope: {
33099                isLoading: '=',
33100                skeletonTemplate: '@'
33101            },
33102            restrict: 'E',
33103            transclude: true,
33104            templateUrl: function (elem, attrs) {
33105
33106                const template = attrs.skeletonTemplate ?? 'skeleton-loader-default.html';
33107
33108                //console.log('skeleton template', template);
33109
33110                return environmentConfig.appRoot +
33111                    '/App/directives/shared/skeleton-loader/templates/' + template +
33112                    environmentConfig.cacheBust;
33113            },
33114            controller: skeletonLoaderController,
33115            controllerAs: 'skeleton',
33116            bindToController: true
33117        };
33118
33119        skeletonLoaderController.$inject = [];
33120
33121        function skeletonLoaderController() {
33122
33123            const vm = this;
33124
33125            vm.isLoading = true; // Initial loading state
33126
33127        }
33128
33129        return directive;
33130    }
33131})();
33132(function () {
33133    'use strict';
33134    angular
33135        .module('MemberPortalDirectives')
33136        .directive('successBox', successBox);
33137
33138    successBox.$inject = ['environmentConfig'];
33139    function successBox(environmentConfig) {
33140        const directive = {
33141            restrict: 'AE',
33142            templateUrl: environmentConfig.appRoot + '/App/directives/shared/success-box/success-box.directive.html' +
33143                environmentConfig.cacheBust,
33144            scope: {
33145                message: '@',
33146            },
33147            controller: successBoxController,
33148            controllerAs: 'successBox',
33149            bindToController: true
33150        };
33151
33152        successBoxController.$inject = [];
33153        function successBoxController() {
33154
33155            const vm = this;
33156        }
33157
33158        return directive;
33159    }
33160})();
33161(function () {
33162    'use strict';
33163    angular
33164        .module('MemberPortalDirectives')
33165        .directive('warningBox', warningBox);
33166
33167    warningBox.$inject = ['environmentConfig'];
33168    function warningBox(environmentConfig) {
33169        const directive = {
33170            restrict: 'AE',
33171            templateUrl: environmentConfig.appRoot + '/App/directives/shared/warning-box/warning-box.directive.html' +
33172                environmentConfig.cacheBust,
33173            scope: {
33174                message: '@',
33175            },
33176            controller: warningBoxController,
33177            controllerAs: 'warningBox',
33178            bindToController: true
33179        };
33180
33181        warningBoxController.$inject = [];
33182        function warningBoxController() {
33183
33184            const vm = this;
33185        }
33186
33187        return directive;
33188    }
33189})();
33190(function () {
33191    'use strict';
33192    angular
33193        .module('MemberPortalDirectives')
33194        .controller('warningMessageModal', warningMessageModal);
33195
33196    warningMessageModal.$inject = ['data', '$modalInstance'];
33197
33198    function warningMessageModal(data, $modalInstance) {
33199
33200        console.log('warning modal controller...');
33201        var vm = this;
33202
33203        vm.data = data;
33204        vm.today = moment().format('MM/DD/YYYY');
33205
33206        vm.onNo = onNo;
33207        vm.onYes = onYes;
33208
33209        init();
33210
33211        function init() {
33212            console.log('warningMessageModal', vm.data);
33213        }
33214        
33215        function onNo() {
33216            $modalInstance.dismiss();
33217
33218            data.answer = false;
33219        }
33220
33221        function onYes() {
33222            $modalInstance.close(data);
33223
33224            data.answer = true;
33225        }
33226    }
33227
33228})();
33229
33230
33231
33232(function () {
33233
33234    'use strict';
33235
33236    const app = angular.module('MemberPortalDirectives');
33237
33238    app.directive('showErrors', function () {
33239
33240        return {
33241            restrict: 'A',
33242            require: '^form',
33243            link: function (scope, el, attrs, formCtrl) {
33244
33245                const inputNgEl = angular.element(el[0].querySelector('[name]'));
33246
33247                var inputName = inputNgEl.attr('name');
33248
33249                var helpText = angular.element(el[0].querySelector('.help-block'));
33250                var checkMark = angular.element(el[0].querySelector('.glyphicon-ok,.glyphiconCorrect'));
33251
33252                const helpBtn = angular.element(el[0].querySelector('.help-block-btn'));
33253
33254                inputNgEl.bind('blur', function () {
33255
33256                    el.toggleClass('has-error', formCtrl[inputName].$invalid);
33257                    helpText.toggleClass('hide', formCtrl[inputName].$valid);
33258                    checkMark.toggleClass('hide', formCtrl[inputName].$invalid);
33259                });
33260
33261                helpBtn.bind('click', function () {
33262
33263                    if (formCtrl[inputName].$valid || formCtrl[inputName].$pristine) {
33264
33265                        helpText.toggleClass('hide');
33266                    }
33267                });
33268
33269                scope.$on('show-errors-event', function () {
33270
33271                    el.toggleClass('has-error', formCtrl[inputName].$invalid);
33272                });
33273
33274            }
33275        };
33276    });
33277})();
33278(function () {
33279
33280    'use strict';
33281
33282    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
33283
33284    const app = angular.module('MemberPortalDirectives');
33285
33286    app.directive('simpleAjaxLoader', function () {
33287
33288        return {
33289            scope: {
33290                show: '=',
33291                height: '='
33292            },
33293            restrict: 'EA',
33294            templateUrl: 'App/directives/simple-ajax-loader/simpleAjaxLoader.html' +
33295                cacheBust
33296        };
33297    });
33298})();
33299(function () {
33300
33301    'use strict';
33302
33303    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
33304
33305    const app = angular.module('MemberPortalDirectives');
33306
33307    app.directive('simpleErrorBox', function () {
33308
33309        return {
33310
33311            scope: {
33312                msg: '@message'
33313            },
33314            restrict: 'EA',
33315            templateUrl: 'App/directives/simple-error-box/simpleErrorBox.html' +
33316                cacheBust
33317        };
33318    });
33319})();
33320(function () {
33321
33322    'use strict';
33323
33324    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
33325    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
33326
33327    const app = angular.module('MemberPortalDirectives');
33328
33329    app.directive('showSurveyHistory',
33330        function () {
33331
33332            return {
33333                restrict: 'EA',
33334                templateUrl: urlBase + '/App/directives/survey-ui/surveyHistory.html' + cacheBust,
33335                controller: surveyHistoryController,
33336                controllerAs: 'surveyHistoryCtrl'
33337            };
33338        });
33339
33340    app.controller('surveyHistoryController', surveyHistoryController);
33341
33342    surveyHistoryController.$inject = ['$scope', 'requestedLanguageService', 'memberDataService', '$location'];
33343
33344    function surveyHistoryController($scope, requestedLanguageService, memberDataService, $location) {
33345
33346        const vm = this;
33347
33348        $scope.language = requestedLanguageService.getCurrentLanguage();
33349        $scope.getPrivacyUrl = getPrivacyUrl;
33350
33351        $scope.$on('language-change', function (event, key) {
33352
33353            $scope.language = key;
33354        });
33355
33356        vm.loadPendingSurvey = loadPendingSurvey;
33357        vm.createNewSurveyFromList = createNewSurveyFromList;
33358        vm.viewSummary = viewSummary;
33359
33360        var haSurveyId = $('meta[name="haSurveyId"]').attr('content');
33361        var appBase = $('meta[name="reverseProxyServeysUrl"]').attr('content');
33362        var templateBase = $('meta[name="reverseProxyTemplateUrl"]').attr('content');
33363
33364        init();
33365
33366        function init() {
33367            console.log('surveyHistoryController loaded');
33368            getSurveyHistory();
33369
33370        }
33371
33372        function getPrivacyUrl(language) {
33373            return requestedLanguageService.getPrivacyUrl(language);
33374        }
33375
33376        function getSurveyHistory() {
33377            
33378            $scope.isLoading = true;
33379
33380            memberDataService.GetDemographics().success(function (data) {
33381
33382                console.log('member data', data);
33383
33384                $scope.demo = data;
33385                $scope.DateOfBirth = $scope.demo.DateOfBirth;
33386                $scope.Age = calculateAge($scope.demo.DateOfBirth);
33387
33388                const param = {
33389                    surveyId: haSurveyId,
33390                    firstName: data.FirstName,
33391                    lastName: data.LastName,
33392                    email: data.Email,
33393                    language: $scope.language,
33394                    createNew: false
33395                };
33396
33397                memberDataService.getLimeSurveyList(param).success(function(data) {
33398                    vm.isLoading = false;
33399
33400                    vm.surveyData = data;
33401
33402                    const pendingSurveysLength = data.PendingSurveys.length;
33403
33404                    for (let i = 0; i < pendingSurveysLength; i++) {
33405                        data.PendingSurveys[i].participant.completedDate = '';
33406                        vm.surveyData.CompletedSurveys.push(data.PendingSurveys[i]);
33407                    }
33408
33409                    console.log('survey data', vm.surveyData);
33410
33411                }).error(function(error) {
33412                    vm.isLoading = false;
33413                    console.log(error);
33414                });
33415            });
33416        }
33417
33418        function loadPendingSurvey() {
33419            const url = appBase +
33420                'index.php?r=survey/index&sid=' +
33421                haSurveyId +
33422                '&token=' +
33423                vm.surveyData.continueToken +
33424                '&newtest=Y';
33425            window.open(url, '_blank');
33426        }
33427
33428        function createNewSurveyFromList() {
33429            var link = 'App/HealthCare/HASurvey/';
33430            const path = '2' + '/' + '1' + '/' + '1';
33431            link += path;
33432            $location.path(link);
33433        }
33434
33435
33436        //Helper function 
33437        function calculateAge(val) {
33438            const formDate = changeDateFormat(val);
33439            const todayDate = new Date();
33440            const diff = todayDate - formDate;
33441            const age = Math.floor(diff / 31536000000);
33442            return age;
33443        }
33444
33445        function changeDateFormat(input) {
33446            const parts = input.match(/(\d+)/g);
33447            return new Date(parts[0], parts[1] - 1, parts[2]);
33448        }
33449
33450        function viewSummary(data) {
33451            console.log('viewSummary', data);
33452            const url = templateBase +
33453                'view?templateId=7&surveyId=' +
33454                haSurveyId +
33455                '&participantToken=' +
33456                data.token +
33457                '&language=' +
33458                $scope.language +
33459                '&showToolbar=true&member=' +
33460                data.participant.memberId;
33461            window.open(url, '_blank');
33462        }
33463
33464    };
33465})();
33466(function () {
33467    'use strict';
33468
33469    const app = angular.module('MemberPortalDirectives');
33470
33471    var urlBase = $('meta[name="ApplicationRoot"]').attr('content');
33472    var cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
33473
33474    app.directive('myMaxlength', [function () {
33475        return {
33476            restrict: 'A',
33477            require: 'ngModel',
33478            link: function (scope, elem, attrs, ctrl) {
33479                attrs.$set('ngTrim', 'false');
33480                var maxlength = parseInt(attrs.myMaxlength, 10);
33481                ctrl.$parsers.push(function (value) {
33482                    if (value.length > maxlength) {
33483                        value = value.substr(0, maxlength);
33484                        ctrl.$setViewValue(value);
33485                        ctrl.$render();
33486                    }
33487                    return value;
33488                });
33489            }
33490        };
33491    }]);
33492
33493    app.directive('renderCheckBoxCombo', function () {
33494
33495        return {
33496            scope: true,
33497            controller: function () {
33498            },
33499            restrict: 'EA',
33500            templateUrl: urlBase + '/App/directives/survey-ui/surveyUICheckBoxCombo' + cacheBust
33501        };
33502    });
33503
33504    app.directive('renderDisableChildrenRadio', function () {
33505
33506        return {
33507            scope: true,
33508            controller: function () {
33509            },
33510            restrict: 'EA',
33511            templateUrl: urlBase + '/App/directives/survey-ui/surveyUIDisableChildrenRadio.html' + cacheBust
33512        };
33513    });
33514
33515    app.directive('renderTrueFalseRadio', function () {
33516
33517        return {
33518            scope: true,
33519            controller: function () {
33520            },
33521            restrict: 'EA',
33522            templateUrl: urlBase + '/App/directives/survey-ui/surveyUI2ChoiceRadio.html' + cacheBust
33523        };
33524    });
33525
33526    app.directive('renderAgree', function () {
33527
33528        return {
33529            scope: true,
33530            controller: function () {
33531            },
33532            restrict: 'EA',
33533            templateUrl: urlBase + '/App/directives/survey-ui/surveyUIAgreement.html' + cacheBust
33534        };
33535    });
33536
33537    app.directive('renderComputedRadioTable', function () {
33538
33539        return {
33540            scope: true,
33541            controller: function () {
33542            },
33543            restrict: 'EA',
33544            templateUrl: urlBase + '/App/directives/survey-ui/surveyUIComputedRadioTable.html' + cacheBust
33545        };
33546    });
33547
33548    app.directive('renderCheckboxMultigroup', function () {
33549
33550        return {
33551            scope: true,
33552            controller: function () {
33553            },
33554            restrict: 'EA',
33555            templateUrl: urlBase + '/App/directives/survey-ui/surveyUICheckBoxMultiGroup.html' + cacheBust
33556        };
33557    });
33558
33559    app.directive('renderCalendar', function () {
33560
33561        return {
33562            restrict: 'EA',
33563            templateUrl: urlBase + '/App/directives/survey-ui/surveyUICalendar.html' + cacheBust,
33564            controller: 'surveyCalendarController',
33565            controllerAs: 'surveyCalendarCtrl'
33566        };
33567    });
33568
33569    app.directive('pickdate', function () {
33570
33571        return {
33572            restrict: 'A',
33573            require: 'ngModel'
33574        };
33575    });
33576
33577    app.directive('renderTable', function () {
33578
33579        return {
33580            scope: true,
33581            controller: function () {
33582            },
33583            restrict: 'EA',
33584            templateUrl: urlBase + '/App/directives/survey-ui/surveyUITable.html' + cacheBust
33585        };
33586    });
33587
33588    app.directive('renderCheckbox', function () {
33589
33590        return {
33591            scope: true,
33592            controller: function () {
33593            },
33594            restrict: 'EA',
33595            templateUrl: urlBase + '/App/directives/survey-ui/surveyUICheckBoxQuestions.html' + cacheBust
33596        };
33597    });
33598
33599    app.directive('renderComputedRadio', function () {
33600
33601        return {
33602            scope: true,
33603            controller: function () {
33604            },
33605            restrict: 'EA',
33606            templateUrl: urlBase + '/App/directives/survey-ui/surveyUIRadioComputedField.html' + cacheBust
33607        };
33608    });
33609
33610    app.directive('renderRadio', function () {
33611
33612        return {
33613            scope: true,
33614            controller: function () {
33615            },
33616            restrict: 'EA',
33617            templateUrl: urlBase + '/App/directives/survey-ui/surveyUIRadioQuestion.html' + cacheBust
33618        };
33619    });
33620
33621    app.directive('renderRadioWhenNotOther', function () {
33622
33623        return {
33624            scope: true,
33625            controller: function () {
33626            },
33627            restrict: 'EA',
33628            templateUrl: urlBase + '/App/directives/survey-ui/surveyUIRadioQuestionWhenNotOther.html' + cacheBust
33629        };
33630    });
33631
33632    app.directive('renderComputedUserInput', function () {
33633
33634        return {
33635            scope: true,
33636            controller: function () {
33637            },
33638            restrict: 'EA',
33639            templateUrl: urlBase + '/App/directives/survey-ui/surveyUITextComputedfield.html' + cacheBust
33640        };
33641    });
33642
33643    app.directive('renderUserInput', function () {
33644
33645        return {
33646            scope: true,
33647            controller: function () {
33648            },
33649            restrict: 'EA',
33650            templateUrl: urlBase + '/App/directives/survey-ui/surveyUIUserInput.html' + cacheBust
33651        };
33652    });
33653
33654    app.directive('renderUserInputNumeric', function () {
33655
33656        return {
33657            scope: true,
33658            controller: function () {
33659            },
33660            restrict: 'EA',
33661            templateUrl: urlBase + '/App/directives/survey-ui/surveyUIUserInputNumeric.html' + cacheBust
33662        };
33663    });
33664
33665    app.directive('showSurveyComparison', function () {
33666
33667        return {
33668            restrict: 'EA',
33669            templateUrl: urlBase + '/App/directives/survey-ui/surveyComparison.html' + cacheBust,
33670            controller: 'surveyComparisonController',
33671            controllerAs: 'surveyComparisonCtrl'
33672        };
33673    });
33674
33675    app.directive('surveyFormSidePanel', function () {
33676
33677        return {
33678            restrict: 'EA',
33679            templateUrl: urlBase + '/App/directives/survey-ui/surveyUISidePanelMemberPortal.html' + cacheBust
33680        };
33681    });
33682
33683    app.directive('historySummary', function () {
33684
33685        return {
33686            scope: true,
33687            controller: 'surveyHistoryController',
33688            controllerAs: 'surveyHistoryCtrl',
33689            restrict: 'EA',
33690            templateUrl: urlBase + '/App/directives/survey-ui/surveyUIHistorySummary.html' + cacheBust
33691        };
33692    });
33693
33694    app.directive('surveyFinish', function () {
33695
33696        return {
33697            scope: true,
33698            controller: function () {
33699            },
33700            restrict: 'EA',
33701            templateUrl: urlBase + '/App/directives/survey-ui/surveyUIFinish.html' + cacheBust
33702        };
33703    });
33704
33705    app.directive('surveyForm', function () {
33706
33707        return {
33708            restrict: 'EA',
33709            templateUrl: urlBase + '/App/directives/survey-ui/surveyUIv3.html' + cacheBust,
33710            controller: 'surveyUIController',
33711            controllerAs: 'SurveyCtrl'
33712        };
33713    });
33714
33715    /*********************************ANGULAR FACTORY THAT HANDLES HTTP REQUESTS SURVEY UI**************************************************/
33716
33717    app.controller('surveyComparisonController', [
33718        '$scope', '$rootScope', 'memberDataService', 'authStatusService', '$location', '$filter', '$modal', 'requestedLanguageService',
33719        function ($scope, $rootScope, memberDataService, authStatusService, $location, $filter, $modal, requestedLanguageService) {
33720            $scope.SurveysToTakeArray = [];
33721            $scope.PendingSurveysArray = [];
33722            $scope.SurveySummariesArray = [];
33723            $scope.SurveyComparisons = {};
33724
33725            const claims = authStatusService.getStatus().claims;
33726            $scope.memberSubNo = claims.username;
33727
33728            $scope.language = requestedLanguageService.getCurrentLanguage();
33729            $scope.PendingSurvey = {};
33730            $scope.SurveyList = [];
33731            $scope.foundPendingSurvey = false;
33732            $scope.SuccessfulHistoryRetrieval = false;
33733            $scope.ShowAttestation = false;
33734            $scope.ShowContinue = false;
33735            $scope.Attestation = '';
33736            $scope.Overwrite = null;
33737            $scope.AgeVerification = 0;
33738            $scope.sortReverse = false;
33739            $scope.DateOfBirth = '';
33740            $scope.Age = 0;
33741            $scope.showButtons = false;
33742            $scope.SurveyIsFinshedLoading = false;
33743            $scope.PleaseSwitchLanguageNotification = false;
33744
33745            $scope.$on('language-change', function (event, key) {
33746
33747                $scope.language = key;
33748            });
33749
33750            //=========== Localization Array for Survey Comparison
33751            $scope.getSurveyComparisonTitle = function (inIndex) {
33752
33753                return 'form_healthcare_SurveyComparison_title_' + inIndex;
33754            };
33755
33756            var orderBy = $filter('orderBy');
33757            $scope.sortType = 'ResponseCreatedOn';
33758            var surveyId = 2;
33759
33760            function changeDateFormat(input) {
33761                console.log('@CHANGEDATEFORMAT');
33762                var date = new Date;
33763                if (input !== null && input !== undefined) {
33764                    const parts = input.match(/(\d+)/g);
33765                    date = new Date(parts[0], parts[1] - 1, parts[2]);
33766                }
33767                return date;
33768            }
33769
33770            function calculateAge(val) {
33771                const newDate = changeDateFormat(val);
33772                const todayDate = new Date();
33773                const diff = todayDate - newDate;
33774                const age = Math.floor(diff / 31536000000);
33775                return age;
33776            }
33777
33778            $scope.PrintSummary = function () {
33779                window.print();
33780            };
33781
33782            $scope.showAttestation = function (value) {
33783                $scope.Overwrite = false;
33784                if (value) {
33785                    $scope.Overwrite = true;
33786                }
33787
33788                $scope.ShowAttestation = true;
33789                $scope.DateOfBirth = '';
33790            };
33791
33792            $scope.hideAttestation = function () {
33793                $scope.ShowAttestation = false;
33794                $('#regDOB').prop('readonly', false);
33795            };
33796
33797            $scope.showContinue = function () {
33798                $scope.ShowContinue = true;
33799            };
33800
33801            //sorts history table
33802            $scope.changeSort = function (value) {
33803                $scope.sortReverse = !$scope.sortReverse;
33804                $scope.sortType = value;
33805                $scope.TempArray = [];
33806
33807                for (let i = 0; i < $scope.PagedArray.length; i++) {
33808                    for (let k = 0; k < $scope.PagedArray[i].length; k++) {
33809                        $scope.TempArray.push($scope.PagedArray[i][k]);
33810                    }
33811                }
33812                $scope.TempArray = orderBy($scope.TempArray, value, $scope.sortReverse);
33813                $scope.pageItems($scope.TempArray);
33814            };
33815
33816            $scope.createNewSurvey = function () {
33817                var link = 'App/HealthCare/HASurvey/';
33818                const path = '2' + '/' + '1' + '/' + '1';
33819                link += path;
33820                $location.path(link);
33821            };
33822
33823            $scope.createNewSurveyFromList = function () {
33824                console.log('@1743 NS ');
33825                const respondentInfo = {
33826                    "RespondentTransId": $scope.PendingSurvey.RespondentTransId,
33827                    "isHidden": true
33828                };
33829
33830                memberDataService.CancelSurvey(respondentInfo).success(function () {
33831                    var link = 'App/HealthCare/HASurvey/';
33832                    const path = '2' + '/' + '1' + '/' + '1';
33833                    link += path;
33834                    $location.path(link);
33835                }).error(function (error) {
33836                    console.log(error);
33837                });
33838            };
33839
33840            $scope.LoadPendingSurvey = function () {
33841                console.log('@1761 LPS ');
33842                if ($scope.PendingSurvey.SurveyLanguage === $scope.language.toUpperCase()) {
33843                    let link = 'App/HealthCare/HASurvey/';
33844                    const path = $scope.PendingSurvey.SurveyId + '/' + '2' + '/' + '2' + '/' + $scope.PendingSurvey.RespondentTransId;
33845                    link += path;
33846                    $location.path(link);
33847                }
33848            };
33849
33850            /*************************Date Mask*************************************************/
33851            $scope.newDOB = '';
33852            $scope.maskDate = function (e, valid) {
33853                const thisDate = $('#regDOB').val();
33854                const numOnlyFilter = /[0-9]/;
33855                const pressedKey = String.fromCharCode(e.which);
33856
33857                if (e.keyCode === 8) //delete
33858                {
33859                    $scope.newDOB = $scope.newDOB.substring(0, $scope.newDOB.length - 1);
33860                }
33861                else {
33862                    //Regex plus numkeypad support
33863                    if (pressedKey.match(numOnlyFilter) ||
33864                        [96, 97, 98, 99, 100, 101, 102, 103, 104, 105].includes(e.keyCode)) {
33865                        $scope.newDOB = thisDate;
33866                    }
33867
33868                    if ([2,5].includes($scope.newDOB.length)) {
33869                        $scope.newDOB += '/';
33870                    }
33871
33872                    $('#regDOB').val($scope.newDOB);
33873                }
33874
33875                if (valid) {
33876                    const formDate = new Date($scope.newDOB);
33877                    const todayDate = new Date();
33878                    const diff = todayDate - formDate;
33879                    const age = Math.floor(diff / 31536000000);
33880
33881                    $scope.AgeVerification = age;
33882                    $scope.DateOfBirth = $scope.newDOB;
33883
33884                    if ($scope.AgeVerification > 17) {
33885                        $('#regDOB').prop('readonly', true);
33886                    }
33887                }
33888            };
33889
33890            /*************************************Pagination Station**********************************/
33891
33892            //arrays used for pagination of medication and case results
33893            $scope.PagedArray = [];
33894            $scope.itemsPerPage = 10;
33895            $scope.currentPage = 0;
33896            $scope.gap = 0;
33897
33898            $scope.pageItems = function (arrayTopage) {
33899                for (let i = 0; i < arrayTopage.length; i++) {
33900                    if (i % $scope.itemsPerPage === 0) {
33901                        $scope.PagedArray[Math.floor(i / $scope.itemsPerPage)] = [arrayTopage[i]];
33902                    } else {
33903                        $scope.PagedArray[Math.floor(i / $scope.itemsPerPage)].push(arrayTopage[i]);
33904                    }
33905                }
33906                $scope.gap = $scope.PagedArray.length;
33907            };
33908
33909            $scope.range = function (size, start, end) {
33910                const ret = [];
33911                if (size < end) {
33912                    end = size;
33913                    start = size - $scope.gap;
33914                }
33915                for (let i = start; i < end; i++) {
33916                    ret.push(i);
33917                }
33918                return ret;
33919            };
33920
33921            $scope.setPage = function (n) {
33922                $scope.currentPage = n;
33923            };
33924            $scope.prevPage = function () {
33925                if ($scope.currentPage > 0) {
33926                    $scope.currentPage--;
33927                }
33928            };
33929
33930            $scope.nextPage = function () {
33931                if ($scope.currentPage < $scope.PagedArray.length - 1) {
33932                    $scope.currentPage++;
33933                }
33934            };
33935
33936            /************************************************************************C*******************/
33937            function getLatestPendingSurvey(data) {
33938                if (data.HistoryItems.length > 0) {
33939                    for (let i = 0; i < data.HistoryItems.length; i++) {
33940                        if (data.HistoryItems[i].RespondentCanContinue) {
33941                            $scope.PendingSurvey = data.HistoryItems[i];
33942                            data.HistoryItems.splice(i, 1);
33943
33944                            $scope.foundPendingSurvey = true;
33945                        }
33946                    }
33947                }
33948            }
33949
33950            function getSurveyList(surveyId) {
33951                memberDataService.GetDemographics().success(function (data) {
33952                    $scope.demo = data;
33953                    $scope.DateOfBirth = $scope.demo.DateOfBirth;
33954                    $scope.Age = calculateAge($scope.demo.DateOfBirth);
33955                    memberDataService.getSurveyList(surveyId).success(function (data) {
33956                        if ($scope.Age >= 18) {
33957                            $scope.SurveyList = data;
33958                        }
33959                        $scope.SuccessfulHistoryRetrieval = true;
33960                        //todo filter surveys to only show appropriate surveys, for now only one survey exists, survey 2
33961                    }).error(function (error) {
33962                        console.log(error);
33963                    });
33964                });
33965            }
33966
33967
33968            function initializeSurveyComparison(memberId, surveyId, languageCode) {
33969                console.log('@1888--------------- initializeSurveyComparison ');
33970                const params = {
33971                    "RespondentValue": memberId,
33972                    "SurveyId": surveyId,
33973                    "LanguageCode": languageCode
33974                };
33975                //======= Fetch Survey Comparison
33976                memberDataService.getRecentSurveyComparison(params).success(function (data) {
33977                    console.log('@1896 ------------ GET SURVEYHISTORY SUCCESS');
33978
33979                    $scope.SurveyIsFinshedLoading = true;
33980
33981                    console.log('----------------' + JSON.stringify(data));
33982
33983                    $scope.SurveyComparisons = {
33984                        "SubscriberID": '199701018576',
33985                        "SurveyVersionID": '2',
33986
33987                        "RecentSurvey": {
33988                            "TransID": '3047',
33989                            "TypeID": '1',
33990                            "CompletedDate": '8/20/2017 8:17AM'
33991                        },
33992
33993                        "PreviousSurvey": {
33994                            "TransID": '2924',
33995                            "TypeID": '1',
33996                            "CompletedDate": '4/11/2017 9:13AM'
33997                        },
33998
33999                        "SurveyCharacteristics": [
34000                            {
34001                                "CharacteristicID": '25',
34002                                "Characteristic": 'BMI Index',
34003                                "RecentValue": '[320] - Overweight',
34004                                "PreviousValue": '[110] - Underweight'
34005                            },
34006                            {
34007                                "CharacteristicID": '26',
34008                                "Characteristic": 'Smoking & Tobacco Use',
34009                                "RecentValue": 'Yes',
34010                                "PreviousValue": 'No'
34011                            },
34012                            {
34013                                "CharacteristicID": '27',
34014                                "Characteristic": 'Physical Activity',
34015                                "RecentValue": '1-2 Days a Week',
34016                                "PreviousValue": '3-5 Days a Week'
34017                            },
34018                            {
34019                                "CharacteristicID": '28',
34020                                "Characteristic": 'Eating Healthy',
34021                                "RecentValue": 'More than once a day',
34022                                "PreviousValue": '1 to 2 times a week'
34023                            },
34024                            {
34025                                "CharacteristicID": '30',
34026                                "Characteristic": 'Stress',
34027                                "RecentValue": 'A lot',
34028                                "PreviousValue": 'Hardly Any'
34029                            },
34030                            {
34031                                "CharacteristicID": '31',
34032                                "Characteristic": 'Productivity & Absenteeism',
34033                                "RecentValue": 'Not at all',
34034                                "PreviousValue": 'Several Days'
34035                            },
34036                            {
34037                                "CharacteristicID": '33',
34038                                "Characteristic": 'Breast Cancer Screening',
34039                                "RecentValue": 'No',
34040                                "PreviousValue": 'Yes'
34041                            },
34042                            {
34043                                "CharacteristicID": '37',
34044                                "Characteristic": 'Colorectal Cancer Screening',
34045                                "RecentValue": 'n/a',
34046                                "PreviousValue": 'Yes'
34047                            },
34048                            {
34049                                "CharacteristicID": '38',
34050                                "Characteristic": 'Cervical Cancer Screening',
34051                                "RecentValue": 'n/a',
34052                                "PreviousValue": 'n/a'
34053                            },
34054                            {
34055                                "CharacteristicID": '39',
34056                                "Characteristic": 'Influenza Vaccination',
34057                                "RecentValue": 'Yes',
34058                                "PreviousValue": 'No'
34059                            },
34060                            {
34061                                "CharacteristicID": '40',
34062                                "Characteristic": 'At-risk drinking',
34063                                "RecentValue": '1-2',
34064                                "PreviousValue": '3-4'
34065                            },
34066                            {
34067                                "CharacteristicID": '41',
34068                                "Characteristic": 'Depressive Symptoms',
34069                                "RecentValue": '[32] 1-4 Minimal Depression ',
34070                                "PreviousValue": '[55] 5-9 Mild Depression '
34071                            },
34072                            {
34073                                "CharacteristicID": '47',
34074                                "Characteristic": 'Safety Behavior',
34075                                "RecentValue": '0 ',
34076                                "PreviousValue": '1 time'
34077                            }
34078                        ]
34079                    };
34080
34081                    $scope.SurveyComparisons = data;
34082
34083                    $scope.showButtons = true;
34084
34085                }).error(function (error) {
34086                    console.log(error);
34087                });
34088            }
34089
34090            $scope.viewSummaryFromComparison = function (o) {
34091                console.log('o:' + JSON.stringify(o));
34092                const z = {};
34093                const a = {
34094                    'SurveyId': $scope.SurveyComparisons.SurveyID, // '2',
34095                    'RespondentTransId': o.RespondentTransID,//   '3047',
34096                    'RespondentTypeId': o.RespondentTypeID,  // '1'
34097                    'ResponseCompletedOn': o.CompletedDate
34098                };
34099                console.log('a:' + JSON.stringify(a));
34100                z.survey = a;
34101
34102                $scope.viewSummary(z);
34103            };
34104
34105            $scope.viewSummary = function (elem) {
34106                console.log('----------@VIEW SUMMARY1 ');
34107                console.log(JSON.stringify(elem));
34108
34109                console.log('SurveyId : ' + elem.survey.SurveyId);
34110
34111                const params = {
34112                    "SurveyId": elem.survey.SurveyId,
34113                    "Language": $scope.language.toUpperCase(),
34114                    "RespondentTransId": elem.survey.RespondentTransId,
34115                    "RespondentTypeId": elem.survey.RespondentTypeId
34116                };
34117                memberDataService.requestSurveySummary(params).success(function (data) {
34118                    $scope.SurveyIsFinshedLoading = true;
34119                    $scope.surveySummary = formatSummarySections(data);
34120                    $scope.openSummaryModal($scope.surveySummary, elem);
34121                }).error(function () {
34122                    console.log(error);
34123                }).error(function (error) {
34124                    console(error);
34125                });
34126            };
34127            //Fetch Comparison
34128            initializeSurveyComparison($scope.memberSubNo, surveyId, $scope.language.toUpperCase());
34129            getSurveyList(surveyId);
34130
34131            /***********SHOW PAST SUMMARIES IN MODAL WINDOW*******************/
34132            $scope.PrintSummary = function () {
34133                const documentContainer = document.getElementById('divtoprint');
34134                const windowObject = window.open('', 'PrintWindow', '');
34135                windowObject.document.writeln(
34136                    '<style> ' +
34137                    '.survey-summary-history-info {text-align:center;font-weight:bold;margin:0;color: #00477E;}' +
34138                    '.survey-completed-date{text-align:center; font-size:12px;}' +
34139                    '.survey-print-logo{height:70px;}' +
34140                    '.survey-summary-list {list-style-type: none;padding: 10px;}' +
34141                    '.survey-summary-history-indent {padding-left: 18px;}' +
34142                    '.survey-summary-history-name {margin: 0px 0px 15px;}' +
34143                    '.non-printable{display:none;}' +
34144                    '.survey-print-logo-wrapper{text-align:center;margin-bottom:8px!important;}' +
34145
34146                    '@media print {' +
34147                    'p {font-size: 10px; text-align:center; }' +
34148                    '.non-printable {display: none;}' +
34149                    '.survey-summary-history-indent{padding-left:20px;}' +
34150                    '.survey-print-logo{height:55px;}' +
34151                    '.survey-print-logo-wrapper{margin-bottom:8px!important;}' +
34152                    '.survey-summary-history-header{margin:0 !important;font-size:12px;}' +
34153                    '.survey-summary-list{list-style:none;}' +
34154                    '.survey-hr-non-printable {display: none;}' +
34155                    '.survey-summary-history-info{margin-bottom:8px;text-align:center !important;font-size:14px;}' +
34156                    '.center-span{display:none;}' +
34157                    '.survey-summary-history-labels{ font-size:10;font-weight:300;margin-bottom:5px;}' +
34158                    '.survey-completed-date{text-align:center; font-size:12px;}' +
34159                    '.survey-list-no-bullet { list-style:none;}' +
34160                    '</style>' +
34161                    documentContainer.innerHTML);
34162                windowObject.document.close();
34163                windowObject.focus();
34164                windowObject.print();
34165                windowObject.close();
34166            };
34167
34168            $scope.openSummaryModal = function (summaries, elem) {
34169                var modalInstance = $modal.open({
34170                    animation: true,
34171                    templateUrl: 'myModalContent.html',
34172                    size: 'lg',
34173                    scope: function () {
34174                        const scope = $rootScope.$new();
34175                        scope.items = summaries;
34176                        scope.surveySummaryInfo = elem.survey;
34177                        scope.PleaseSwitchLanguageNotification = elem.survey.SurveyLanguage !== $scope.language.toUpperCase();
34178                        scope.PrintLogoUrl = $('meta[name="ApplicationRoot"]').attr('content') + '/Content/Images/IEHP_LOGO_Survey.png';
34179                        scope.cancel = function () {
34180                            modalInstance.dismiss();
34181                        };
34182                        return scope;
34183                    }()
34184                });
34185            };
34186        }]);
34187
34188    function getSurveySectionTitlesfromSummary(summaries) {
34189        const sectionTitlesExtracted = [];
34190        for (let cnt2 = 0; cnt2 < summaries.Summaries.length; cnt2++) {
34191            if (sectionTitlesExtracted.indexOf(summaries.Summaries[cnt2].SectionName) === -1) {
34192                sectionTitlesExtracted.push(summaries.Summaries[cnt2].SectionName);
34193            }
34194        }
34195
34196        const surveySectionObject = [];
34197        for (let cnt = 0; cnt < sectionTitlesExtracted.length; cnt++) {
34198            surveySectionObject.push(
34199                {
34200                    'SectionName': sectionTitlesExtracted[cnt],
34201                    'SummariesList': []
34202                });
34203        }
34204
34205
34206        return surveySectionObject;
34207    };
34208
34209    function formatSummarySections(summaries) {
34210        const surveySectionObjectList = getSurveySectionTitlesfromSummary(summaries);
34211
34212        var bmiCategory = '';
34213
34214        for (let i = 0; i < summaries.Summaries.length; i++) {
34215            for (let x = 0; x < surveySectionObjectList.length; x++) {
34216                if (summaries.Summaries[i].SectionName === surveySectionObjectList[x].SectionName) {
34217
34218                    if (summaries.Summaries[i].TotalScoreBMI &&
34219                        summaries.Summaries[i].SectionName === $filter('translate')('personal_health_history')) {
34220
34221                        if (!bmiCategory) {
34222                            bmiCategory = processBmi(summaries.Summaries[i].TotalScoreBMI);
34223                            surveySectionObjectList[x].BmiCategory = bmiCategory;
34224                            surveySectionObjectList[x].SummariesList.push(summaries.Summaries[i]);
34225                        }
34226                    }
34227                    else {
34228                        surveySectionObjectList[x].SummariesList.push(summaries.Summaries[i]);
34229                    }
34230                }
34231            }
34232        }
34233        console.log(surveySectionObjectList);
34234
34235        const surveyObject = {
34236            'SurveySectionsList': surveySectionObjectList,
34237            'SurveyTitle': summaries.SurveyTitle,
34238            'OverAllRiskAndWellnessScore': summaries.OverAllRiskAndWellnessScore
34239
34240        };
34241        return surveyObject;
34242    };
34243
34244    function processBmi(bmiVal) {
34245        const bmi = parseFloat(bmiVal);
34246        if (bmi < 18.5)
34247            return 1;
34248        else if (bmi >= 18.5 && bmi <= 24.9)
34249            return 2;
34250        else if (bmi >= 25.0 && bmi <= 29.9)
34251            return 3;
34252        else
34253            return 4;
34254
34255    }
34256
34257    //controller used for Calender types
34258    app.controller('surveyCalendarController', [
34259        '$scope',
34260        function ($scope) {
34261            $scope.max = new Date();
34262
34263            $scope.open = function ($event, dt) {
34264                $event.preventDefault();
34265                $event.stopPropagation();
34266                dt.opened = true;
34267            };
34268
34269            $scope.dateOptions = {
34270                formatYear: 'yy',
34271                startingDay: 1,
34272                showWeeks: false,
34273                yearRange: 2,
34274                showButtonBar: false,
34275                allowInvalid: true
34276            };
34277            $scope.format = 'MM/dd/yyyy';
34278        }]);
34279})();
34280(function () {
34281    const app = angular.module('MemberPortalDirectives');
34282
34283    app.directive('temporaryIdFrame', ['memberDataService', 'requestedLanguageService',
34284        function (memberDataService, requestedLanguageService) {
34285
34286        const urlBase = $('meta[name="ApplicationRoot"]').attr('content');
34287        const cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
34288
34289        const link = function (scope, element, attrs) {
34290
34291            scope.idCardUrl = 'https://www.iehp.org/Secure_Site/Eligibility/IdCards/Forms/';
34292            scope.loading = true;
34293
34294            var getTodayDate = function () {
34295
34296                var today = new Date();
34297                var dd = today.getDate();
34298                var mm = today.getMonth() + 1; //January is 0!
34299                const yyyy = today.getFullYear();
34300
34301                if (dd < 10) {
34302                    dd = '0' + dd;
34303                }
34304
34305                if (mm < 10) {
34306                    mm = '0' + mm;
34307                }
34308
34309                today = mm + '/' + dd + '/' + yyyy;
34310                return today;
34311            };
34312
34313            var determineIdCardFormName = function(elig) {
34314                //Using the eligibility line, determine which Id card form to use
34315
34316                const plan = (elig.LobDescription === 'Healthy Kids') ? 'HealthyKids' :
34317                    (elig.LobDescription === 'Medi-Medi') ? 'MediMedi' :
34318                    (elig.LobDescription === 'Cal MediConnect') ? 'CMCDuals' :
34319                    (elig.LobDescription === 'Open Access') ? 'OpenAccess' :
34320                    (elig.LobDescription === 'Medicare') ? 'Medicare' : 'MediCal';
34321
34322                return plan + '.asp';
34323            };
34324
34325            const language = requestedLanguageService.getCurrentLanguage();
34326
34327            memberDataService.getCurrentEligibility(language).success(function (data) {
34328                memberDataService.activityWrite('MyIEHPIDCard-Temporary');
34329
34330
34331                const elig = memberDataService.utility.getPrimaryEligibilityRecord(data);
34332
34333                //Append Id Card Form to Url
34334                scope.idCardUrl += determineIdCardFormName(elig);
34335
34336                //Append Subno and DOS
34337                scope.idCardUrl += `?id=${elig.SubscriberId}&dos=${getTodayDate()}`;
34338
34339                //Update Iframe src with Url
34340                angular.element(element[0].querySelector('iframe')).attr('src', scope.idCardUrl);
34341                scope.loading = !scope.loading;
34342
34343            }).error(function () {
34344
34345                scope.loading = !scope.loading;
34346            });
34347
34348            $('#imgPrintTempCard').click(function () {
34349             
34350                window.open(scope.idCardUrl, '', 'height=400,width=650,location=no,'); 
34351
34352            });
34353
34354        };
34355
34356        return {
34357            scope: {},
34358            restrict: 'EA',
34359            templateUrl: urlBase + '/App/directives/temporaryIdFrame/temporaryIdFrame.html' + cacheBust,
34360            link: link
34361        };
34362    }]);
34363
34364
34365})();
34366(function () {
34367    'use strict';
34368    const app = angular.module('MemberPortalServices', ['ui.bootstrap', 'LocalStorageModule', 'ngResource']);
34369
34370    app.config(['localStorageServiceProvider', function (localStorageServiceProvider) {
34371        localStorageServiceProvider
34372          .setStorageType('sessionStorage');
34373
34374        localStorageServiceProvider
34375            .setStorageCookie(45, '/');
34376    }]);
34377})();
34378/**
34379 * DAM Search Service
34380 * Handles all interactions with the AEM DAM Search API
34381 */
34382/**
34383 * API Response Structure Documentation:
34384 *
34385 * ApiResponse: {
34386 *   items: Item[]
34387 * }
34388 *
34389 * Item: {
34390 *   path: string,
34391 *   properties: ItemProperties
34392 * }
34393 *
34394 * ItemProperties: {
34395 *   dam:numPages: number ,
34396 *   dc:format: string ,
34397 *   dam:size: number ,
34398 *   dc:title: string ,
34399 *   jcr:lastModified: number , -- jcr is DATE FROM AEM
34400 *   xmp:CreateDate: number,
34401 *   xmp:ModifyDate: number -- xmp is DATE FROM PDF
34402 * }
34403 */
34404(function () {
34405    'use strict';
34406    angular
34407        .module('MemberPortalServices')
34408        .service('damSearchService', damSearchService);
34409    damSearchService.$inject = ['$http'];
34410    function damSearchService($http) {
34411
34412        return {
34413            search: search,
34414            getDownloadUrl: getDownloadUrl
34415        };
34416
34417        /**
34418         * Decode common HTML entities in a string (e.g. "&#9;" → tab, "&amp;" → "&")
34419         * Handles numeric character references and the most common named entities
34420         * @param {string} val - String potentially containing HTML entities
34421         * @returns {string} Decoded string
34422         */
34423        function decodeHtmlEntities(val) {
34424            return val
34425                .replace(/&#(\d+);
34425/g, function (_, code) { return String.fromCharCode(code); })
34426                .replace(/&amp;/g, '&')
34427                .replace(/&lt;/g, '<')
34428                .replace(/&gt;/g, '>')
34429                .replace(/&nbsp;/g, ' ');
34430        }
34431
34432        /**
34433         * Normalize metadata values for case-insensitive matching against AEM DAM asset tags.
34434         * Decodes HTML entities (e.g. "&#9;"), trims whitespace, and lowercases all string values.
34435         * @param {Object} metaData - Object containing arrays of metadata values
34436         * @returns {Object} New metadata object with all string values normalized
34437         */
34438        function normalizeMetaData(metaData) {
34439            var normalized = {};
34440            Object.keys(metaData).forEach(function (key) {
34441                normalized[key] = metaData[key].map(function (val) {
34442                    if (!val) return val;
34443                    return decodeHtmlEntities(val).trim().toLowerCase();
34444                });
34445            });
34446            return normalized;
34447        }
34448
34449        /**
34450         * Search for files in AEM DAM
34451         * @param {Object} metaData - Object containing search criteria
34452         * @param {string} searchPath - The DAM path to search in (optional)
34453         * @returns {Promise} Promise that resolves to transformed file data with pre-computed download URLs
34454         */
34455        function search(metaData, searchPath, condition) {
34456            var aemDamSearchApi = document.querySelector('meta[name="aemDamSearchApi"]').content;
34457            var payload = {
34458                path: searchPath || '/content/dam/apps/member-portal/en',
34459                metaData: normalizeMetaData(metaData),
34460                condition: condition
34461            };
34462            return $http.post(aemDamSearchApi, payload)
34463                .then(function (response) {
34464                    var items = (response.data && response.data.items && Array.isArray(response.data.items))
34465                        ? response.data.items
34466                        : [];
34467
34468                    var filesWithUrls = items.map(function (file) {
34469                        if (!file || !file.path) {
34470                            return file;
34471                        }
34472
34473                        file.downloadUrl = getDownloadUrl(file.path);
34474                        return file;
34475                    });
34476
34477                    response.data.items = filesWithUrls;
34478                    return response;
34479                })
34480                .catch(function (error) {
34481                    console.error('AEM DAM Search API Error:', error);
34482                    throw error;
34483                });
34484        }
34485
34486        /**
34487         * Generate download URL for a file path
34488         * @param {string} filePath - The file path from the API response
34489         * @returns {string} Complete download URL
34490         */
34491        function getDownloadUrl(filePath) {
34492            if (!filePath) return '';
34493
34494            var domainMeta = document.querySelector('meta[name="aemDomain"]');
34495            if (!domainMeta || !domainMeta.content) {
34496                console.warn('aemDomain meta tag not found');
34497                return filePath;
34498            }
34499
34500            var aemDomain = domainMeta.content;
34501            var cleanPath = filePath.startsWith('/') ? filePath.substring(1) : filePath;
34502            return aemDomain + '/' + cleanPath;
34503        }
34504    }
34505})();
34506
34507angular.module('MemberPortalServices')
34508    .constant('environmentConfig', (function () {
34509
34510        const appRoot = $('meta[name="ApplicationRoot"]').attr('content');
34511
34512        let appMode = $('meta[name="appMode"]').attr('content');
34513
34514        const formioBase = $('meta[name="formioBase"]').attr('content');
34515
34516        const iFormioBase = $('meta[name="iFormioBase"]').attr('content');
34517
34518        const iFormioOrigin = $('meta[name="iFormioOrigin"]').attr('content');
34519
34520        let soaUrl = $('meta[name="soaUrl"]').attr('content');
34521
34522        const cacheBust = `?v=${$('meta[name="templateCacheBust"]').attr('content')}`;
34523
34524        const gatewayBase = `?v=${$('meta[name="gatewayBase"]').attr('content')}`;
34525
34526        const authServiceBaseUri = $('meta[name="authServiceBase"]').attr('content');
34527
34528        const haSurveyId = $('meta[name="haSurveyId"]').attr('content');
34529
34530        const reverseProxyServeysUrl = $('meta[name="reverseProxyServeysUrl"]').attr('content');
34531
34532        const healthAppraisalPDHIUrl = $('meta[name="healthAppraisalPDHIUrl"]').attr('content');
34533
34534        const ccaPaymentsRedirect = $('meta[name="ccaPaymentsRedirect"]').attr("content");
34535
34536        const medImpactSsoUrl = $('meta[name="medImpactSsoUrl"]').attr('content');
34537
34538        const showCCR = $('meta[name="showCCR"]').attr("content");
34539
34540        const maintenanceFrom = $('meta[name="maintenanceFrom"]').attr('content');
34541
34542        const maintenanceTo = $('meta[name="maintenanceTo"]').attr('content');
34543
34544        const templateBase = $('meta[name="reverseProxyTemplateUrl"]').attr('content');
34545
34546        const languageServiceBase = $('meta[name="languageServiceApiBase"]').attr('content');
34547
34548        const featureFlags = $('meta[name="featureFlags"]').attr('content');
34549
34550        const healthConnectServiceBase = $('meta[name="healthConnectServiceBase"]').attr('content');
34551
34552        const geoAddressBase = $('meta[name="geoAddressApiBase"]').attr('content');
34553
34554        const ccaResourcesRedirect  = $('meta[name="ccaResourcesRedirect"]').attr('content');
34555
34556        const maintenanceMode = $('meta[name="maintenanceMode"]').attr('content');
34557
34558        const reverseProxyServeysApiUrl = $('meta[name="reverseProxyServeysApiUrl"]').attr('content');
34559
34560        const maintenanceModeStartDate = $('meta[name="maintenanceModeStartDate"]').attr('content');
34561
34562        const maintenanceModeEndDate = $('meta[name="maintenanceModeEndDate"]').attr('content');
34563
34564        const incommRedirectSso = $('meta[name="incommRedirect"]').attr('content');
34565
34566        //const madieRedirectSso = $('meta[name="madieRedirect"]').attr('content');
34567
34568        if (appMode && typeof appMode === 'string') {
34569            appMode = appMode.toLowerCase();
34570        }
34571
34572        /********** DEV mode   *************/
34573        if (appMode === 'dev') {
34574            const cookies = {};
34575
34576            const x = document.cookie;
34577            const c = x.split(';');
34578
34579            for (let i = c.length - 1; i >= 0; i--) {
34580                const C = c[i].split('=');
34581                C[0] = C[0].replace(/\s+/g, '');
34582                cookies[C[0]] = C[1];
34583            }
34584            if (cookies['iehpSoaUrl']) {
34585                soaUrl = cookies['iehpSoaUrl'];
34586            }
34587        }
34588
34589        const getFullUrl = function (url) {
34590
34591            return appRoot + url + cacheBust;
34592        }
34593
34594        const config = {
34595            url: getFullUrl,
34596            dataServiceBaseUri: soaUrl,
34597            gatewayBase: gatewayBase,
34598            authServiceBaseUri: authServiceBaseUri,
34599            formioBase: formioBase,
34600            iFormioBase: iFormioBase,
34601            iFormioOrigin: iFormioOrigin,
34602            cacheBust: cacheBust,
34603            appRoot: appRoot,
34604            reverseProxyServeysUrl: reverseProxyServeysUrl,
34605            mode: appMode,
34606            haSurveyId: haSurveyId,
34607            healthConnectServiceBase: healthConnectServiceBase,
34608            healthAppraisalPDHIUrl: healthAppraisalPDHIUrl,
34609            ccaPaymentsRedirect: ccaPaymentsRedirect,
34610            medImpactSsoUrl: medImpactSsoUrl,
34611            showCCR: showCCR,
34612            maintenanceFrom: maintenanceFrom,
34613            maintenanceTo: maintenanceTo,
34614            templateBase: templateBase,
34615            languageServiceBase: languageServiceBase,
34616            featureFlags: featureFlags,
34617            geoAddressBase: geoAddressBase,
34618            maintenanceMode: maintenanceMode,
34619            reverseProxyServeysApiUrl: reverseProxyServeysApiUrl,
34620            maintenanceModeStartDate: maintenanceModeStartDate,
34621            maintenanceModeEndDate: maintenanceModeEndDate,
34622            ccaResourcesRedirect: ccaResourcesRedirect,
34623            incommRedirectSso: incommRedirectSso,
34624            //madieRedirectSso: madieRedirectSso
34625        };
34626
34627        return config;
34628
34629    })());
34630(function () {
34631    'use strict';
34632
34633    const app = angular.module('MemberPortalServices');
34634
34635    app.factory('authService', ['$http', '$rootScope', '$q', 'transformRequestAsFormPost',
34636        '$location', 'authStatusService', '$filter', 'utilitiesService', 'localStorageService',
34637        function ($http, $rootScope, $q, transformRequestAsFormPost,
34638            $location, authStatusService, $filter, utilitiesService, localStorageService) {
34639
34640            const authService = {};
34641
34642            var urlBase = $('meta[name="authServiceBase"]').attr('content');
34643
34644            var languageServiceBase = $('meta[name="languageServiceApiBase"]').attr('content');
34645
34646            /*******************************************************************
34647            * * * * * * * * * * * * * * *  REGISTRATION * * * * * * * * * * * * 
34648            *******************************************************************/
34649
34650            const validateDate = function (inputText) {
34651                //mm/dd/yyyy
34652                const dateFormat = /^(0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])[\/\-]\d{4}$/;
34653                var leapYear = false;
34654
34655                // Match the date format through regular expression
34656                if (!inputText.match(dateFormat)) return false;
34657                //console.log("correct date format", inputText);
34658
34659                // Extract the string into month, date and year
34660                const dateParts = inputText.split('/');
34661
34662                const mm = parseInt(dateParts[0]);
34663                const dd = parseInt(dateParts[1]);
34664                const yy = parseInt(dateParts[2]);
34665                console.log('month: ' + mm + ' dd: ' + dd + ' year: ' + yy);
34666                // Create list of days of a month [assume there is no leap year by default]
34667
34668                if (mm === 1 || mm > 2) {
34669                    const listOfDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
34670
34671                    if (dd > listOfDays[mm - 1]) {
34672                        return false;
34673                    }
34674                }
34675                else if (mm === 2) {
34676                    if ((!(yy % 4) && yy % 100) || !(yy % 400)) {
34677                        leapYear = true;
34678                    }
34679
34680                    if ((leapYear === false) && (dd >= 29)) {
34681                        return false;
34682                    }
34683
34684                    if ((leapYear === true) && (dd > 29)) {
34685                        return false;
34686                    }
34687                }
34688
34689                return true;
34690            };
34691
34692            const registrationStepOne = function (params) {
34693                return $http({
34694                    method: 'POST',
34695                    url: urlBase + 'api/Registration/EligibilityCheck',
34696                    data: params,
34697                    transformRequest: transformRequestAsFormPost
34698                });
34699            };
34700
34701            const registrationStepTwo = function (params) {
34702                return $http({
34703                    method: 'POST',
34704                    url: urlBase + 'api/Registration/Register',
34705                    data: params,
34706                    transformRequest: transformRequestAsFormPost
34707                });
34708            };
34709
34710            const registrationStepThree = function (params, language) {
34711
34712                const config = {
34713                    'Content-type': 'application/json',
34714                    'headers': { 'X-Language': language }
34715                }
34716
34717                const url = languageServiceBase + 'verify-email-code';
34718
34719                return $http.post(url,params,config);
34720            };
34721
34722            const registrationStepFour = function (params) {
34723                return $http({
34724                    method: 'POST',
34725                    url: urlBase + 'api/Registration/SubmitSecurityQuestions',
34726                    data: params
34727                });
34728            };
34729
34730
34731            /*******************************************************************
34732             * * * * * * * * * * * * * * *  AUTHENTICATION * * * * * * * * * * * * * 
34733             *******************************************************************/
34734
34735            const _login = function (loginData) {
34736
34737                loginData.grant_type = 'password';
34738                loginData.name = 'WebMemberPortal';
34739
34740                var deferred = $q.defer();
34741
34742                $http({
34743                        method: 'POST',
34744                    url: urlBase + 'token',
34745                        data: loginData,
34746                        transformRequest: transformRequestAsFormPost
34747                    })
34748                    .success(function (response) {
34749
34750                        authStatusService.storeAuthResponse(response);
34751                        //authStatusService.SetTokenIntoCookie();
34752
34753                        const authStatus = authStatusService.getStatus();
34754
34755                        if (!authStatus.isAuth) {
34756                            deferred.reject($filter('translate')('user_name_password_validation'));
34757                            return;
34758                        }
34759
34760                        const firstName = authStatus.claims.firstname;
34761
34762                        $rootScope.firstName = utilitiesService
34763                            .capFirstLetter(firstName);
34764
34765                        deferred.resolve(response);
34766                        $rootScope.$broadcast('logon-success');
34767                        $location.path('/App/Home');
34768
34769                         
34770                    }).error(function (err, status) {
34771
34772                        if (!err) { err = {}; }
34773
34774                        err.status_code = status;
34775                        deferred.reject(err);
34776                    });
34777
34778                return deferred.promise;
34779            };
34780
34781            const _logOut = function () {
34782                //Revoke Token
34783                const jti = authStatusService.getStatus().claims.jti;
34784
34785                _SignTokenOut(jti).success(function () {
34786                    //Success
34787                }).error(function () {
34788                    console.log('@AuthServ.STO ERROR');
34789                });
34790
34791                //Clear Everything From Local Storage
34792                authStatusService.clearAll();
34793
34794                //Broadcast logged-out event
34795                $rootScope.$broadcast('logged-out');
34796            };
34797
34798            //SignTokenOut
34799            function _SignTokenOut(jti) {
34800                const params = {
34801                    jti: jti
34802                };
34803                return $http.post(urlBase + 'api/account/RevokeToken', params);
34804            };
34805
34806            function _switchToDefault() {
34807                console.warn('switching back to default profile');
34808                var deferred = $q.defer();
34809
34810                //store the refresh token and the session id.
34811                const status = authStatusService.getStatus();
34812
34813                const param = {};
34814                param.grant_type = 'refresh_token';
34815                param.refresh_token = status.refresh_token;
34816                param.session_id = status.claims.sessionid;
34817                
34818                const switchUrl = urlBase + 'api/InternalUser/InternalUserLoginRequest';
34819                
34820
34821                $http({
34822                    method: 'POST',
34823                    url: switchUrl,
34824                    data: param,
34825                    transformRequest: transformRequestAsFormPost,
34826                    headers: {
34827                        "X-Make-Anonymous": 'true'
34828                    }
34829                })
34830                    .success(function (response) {
34831
34832                        /*console.log('switched token', response);*/
34833                        if (!response) {
34834                            deferred.reject('No backend response. Switch Profile Failed.');
34835                            return;
34836                        }
34837
34838                        //Clear Everything From Local Storage
34839                        authStatusService.clearAll();
34840                        console.log('cleared token from cookie and session');
34841                        authStatusService.storeAuthResponse(response);
34842                        //authStatusService.SetTokenIntoCookie();
34843
34844                        const claims = authStatusService.getStatus().claims;
34845                        console.log('authService: claims', claims);
34846                        $rootScope.firstName = utilitiesService
34847                            .capFirstLetter(claims.firstname.toLowerCase());
34848                            
34849                        console.log('switch to default user: $rootScope.firstName', $rootScope.firstName);
34850
34851                        if (authStatusService.getStatus().isAuth) {
34852                            console.log('authenticated...');
34853                            deferred.resolve(response);
34854                            $rootScope.$broadcast('logon-success');
34855
34856                            if ($location.path() !== '/App/Home') {
34857                                $location.path('/App/Home');
34858                            }
34859
34860                        } else {
34861                            console.log('failed auth');
34862                            deferred.reject('Profile Switch Failed.');
34863                        }
34864
34865                        deferred.resolve(status);
34866                    }).error(function (err) {
34867                        deferred.reject(err);
34868                    });
34869
34870                return deferred.promise;
34871            }
34872
34873            function _switchToken(proxiedMember) {
34874                console.warn('switching profile');
34875                var deferred = $q.defer();
34876
34877                //store the refresh token and the session id.
34878                const status = authStatusService.getStatus();
34879
34880                const param = {};
34881                param.grant_type = 'proxy_token';
34882                param.refresh_token = status.refresh_token;
34883                param.session_id = status.claims.sessionid;
34884                param.proxied_member_number = proxiedMember.memberNumber;
34885               
34886                const switchUrl = urlBase + 'api/InternalUser/InternalUserLoginRequest';
34887                
34888                $http({
34889                        method: 'POST',
34890                        url: switchUrl,
34891                        data: param,
34892                        transformRequest: transformRequestAsFormPost,
34893                        headers: {
34894                            "X-Make-Anonymous": 'true'
34895                        }
34896                    })
34897                    .success(function (response) {
34898
34899                        /*console.log('switched token', response);*/
34900                        if (!response) {
34901                            deferred.reject('No backend response. Switch Profile Failed.');
34902                            return;
34903                        }
34904
34905                        //Clear Everything From Local Storage
34906                        authStatusService.clearAll();
34907                        console.log('cleared token from cookie and session');
34908                        authStatusService.storeAuthResponse(response);
34909                        //authStatusService.SetTokenIntoCookie();
34910                        
34911                        $rootScope.firstName = utilitiesService
34912                            .capFirstLetter(proxiedMember.firstName.toLowerCase());
34913                        console.log('switch profile: $rootScope.firstName', $rootScope.firstName);
34914                       
34915                        if (authStatusService.getStatus().isAuth) {
34916                            console.log('authenticated...');
34917                            deferred.resolve(response);
34918                            $rootScope.$broadcast('logon-success');
34919
34920                            if ($location.path() !== '/App/Home') {
34921                                $location.path('/App/Home');
34922                            }
34923
34924                        } else {
34925                            console.log('failed auth');
34926                            deferred.reject('Switch Profile Failed.');
34927                        }
34928
34929                        deferred.resolve(status);
34930                    }).error(function (err) {
34931                        deferred.reject(err);
34932                    });
34933
34934                return deferred.promise;
34935            }
34936
34937            const _refreshToken = function () {
34938                //authStatusService
34939
34940                var deferred = $q.defer();
34941
34942                const status = authStatusService.getStatus();
34943                const param = {};
34944                param.grant_type = 'refresh_token';
34945                param.refresh_token = status.refresh_token;
34946                param.session_id = status.claims.sessionid;
34947                param.name = 'WebMemberPortal';
34948
34949                const isProxied = authStatusService.checkIfProxied();
34950                console.log('authservice isProxied', isProxied);
34951                console.log('param', param);
34952                if (isProxied) {
34953                    param.proxied_member_number = status.claims.proxiedmembernumber;
34954                }
34955                
34956                const refreshUrl = urlBase + 'api/InternalUser/InternalUserLoginRequest';
34957                
34958                
34959                $http({
34960                        method: 'POST',
34961                        url: refreshUrl,
34962                        data: param,
34963                        transformRequest: transformRequestAsFormPost,
34964                        headers: {
34965                            "X-Make-Anonymous": 'true'
34966                        }
34967                    })
34968                    .success(function (response) {
34969                        //console.log('refreshed token', response);
34970                        const refreshedStatus = authStatusService.storeAuthResponse(response);
34971                        deferred.resolve(refreshedStatus);
34972                    }).error(function (err) {
34973                        deferred.reject(err);
34974                    });
34975
34976                return deferred.promise;
34977            };
34978
34979const _processLoginToken = function (token) {
34980
34981                const deferred = $q.defer();
34982
34983                authStatusService.clearAll();
34984
34985                try {
34986                    // decode → parse
34987                    const authResponseObj = JSON.parse(
34988                        UTF8ArrToStr(base64DecToArr(token))
34989                    );
34990
34991                    authStatusService.storeAuthResponse(authResponseObj);
34992                    deferred.resolve(authResponseObj);
34993
34994                } catch (err) {
34995
34996                    console.error("Failed to process login token:", err);
34997
34998                    // optional: clear any data written before failure
34999                    authStatusService.clearAll();
35000
35001                    deferred.reject({
35002                        error: true,
35003                        message: "Invalid or corrupted token",
35004                        detail: err
35005                    });
35006                }
35007
35008                return deferred.promise;
35009            };
35010
35011            const broadcastResults = function () {
35012                $rootScope.$broadcast('authStatusUpdated');
35013            };
35014
35015            /********************************************************************************/
35016            /********************************************************************************/
35017
35018            /*******************************************************************
35019             * * * * * * * * * * SIMPLE AUTHORIZATION RESOLVE * * * * * * * * * * 
35020             *******************************************************************/
35021
35022            //http://www.codeproject.com/Tips/811782/AngularJS-Security-Authorization-on-Angular-Routes
35023            //http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx
35024
35025            const _AuthenticationCheck = function () {
35026
35027                const deferred = $q.defer();
35028
35029                _getAuthenticationStatus(deferred);
35030
35031                return deferred.promise;
35032
35033            };
35034
35035            function _getAuthenticationStatus(deferred) {
35036
35037                const authStatus = authStatusService.getStatus();
35038
35039                if (authStatus.isAuth) {
35040                    deferred.resolve();
35041                } else {
35042                    //If user does not have required access, we will route the user to unauthorized access page
35043                    $location.path('App/Account/Login');
35044                    //As there could be some delay when location change event happens, 
35045                    //we will keep a watch on $locationChangeSuccess event
35046                    // and would resolve promise when this event occurs.
35047                    $rootScope.$on('$locationChangeSuccess', function () {
35048                        deferred.resolve();
35049                    });
35050                }
35051            };
35052
35053            /********************************************************************************/
35054            /********************************************************************************/
35055
35056            /*******************************************************************
35057             * * * * * * * * * * IS MEDI-CAL RESOLVE * * * * * * * * * * 
35058             *******************************************************************/
35059
35060            const _IsMediCalCheck = function () {
35061
35062                const deferred = $q.defer();
35063
35064                _getMediCalStatus(deferred);
35065
35066                return deferred.promise;
35067
35068            };
35069
35070            function _getMediCalStatus(deferred) {
35071
35072                const authStatus = authStatusService.getStatus();
35073
35074                if (authStatus.isMediCal) {
35075                    deferred.resolve();
35076                } else {
35077                    //If user does not have required access, we will route the user to unauthorized access page
35078                    $location.path('App/Account/Login');
35079                    //As there could be some delay when location change event happens, 
35080                    //we will keep a watch on $locationChangeSuccess event
35081                    // and would resolve promise when this event occurs.
35082                    $rootScope.$on('$locationChangeSuccess', function () {
35083                        deferred.resolve();
35084                    });
35085                }
35086            };
35087
35088            /********************************************************************************/
35089            /********************************************************************************/
35090
35091            /*******************************************************************
35092             * * * * * * * * * * SHOW CCR RESOLVE * * * * * * * * * * 
35093             *******************************************************************/
35094
35095            const _ShowCCRCheck = function () {
35096
35097                const deferred = $q.defer();
35098
35099                _getShowCCRStatus(deferred);
35100
35101                return deferred.promise;
35102
35103            };
35104
35105            function _getShowCCRStatus(deferred) {
35106
35107                var showCCR = $('meta[name="showCCR"]').attr("content") === 'true';
35108
35109                if (showCCR) {
35110                    deferred.resolve();
35111                } else {
35112                    //If user does not have required access, we will route the user to unauthorized access page
35113                    $location.path('App/Account/Login');
35114                    //As there could be some delay when location change event happens, 
35115                    //we will keep a watch on $locationChangeSuccess event
35116                    // and would resolve promise when this event occurs.
35117                    $rootScope.$on('$locationChangeSuccess', function () {
35118                        deferred.resolve();
35119                    });
35120                }
35121            };
35122           
35123
35124            /*******************************************************************
35125             * * * * * * * * * * * * * CHANGE PASSWORD * * * * * * * * * * * * * 
35126             *******************************************************************/
35127
35128            var _ChangePassword = function (param) {
35129
35130                return $http.post(urlBase + 'api/Account/ChangePassword', param);
35131            };
35132
35133
35134            /*******************************************************************
35135            * * * * * * * * * * * * * CHANGE EMAIL * * * * * * * * * * * * * 
35136            *******************************************************************/
35137
35138            const _ChangeEmail = function (param) {
35139
35140                return $http.post(urlBase + 'api/Account/ChangeEmail', param);
35141            };
35142
35143            /*******************************************************************
35144             * * * * * * * * * * * * *  PASSWORD RESET * * * * * * * * * * * * * 
35145             *******************************************************************/
35146
35147            const _InitiatePasswordReset = function (params) {
35148
35149                return $http({
35150                    method: 'POST',
35151                    url: urlBase + 'api/Account/ForgotPassword',
35152                    data: params,
35153                    transformRequest: transformRequestAsFormPost
35154                });
35155            };
35156
35157            const _VerifyEmailCode = function (params, language) {
35158                //used for password reset
35159                const config = {
35160                    headers: {
35161                        'Content-type': 'application/json',
35162                        'X-Language': language
35163                    }
35164                }
35165
35166                const url = languageServiceBase + 'reset-password-confirm-email';
35167
35168                return $http.post(url, params, config);
35169            };
35170
35171            const _SubmitSecurityQuestionAnswer = function (params) {
35172                return $http({
35173                    method: 'POST',
35174                    url: urlBase + 'api/Account/ConfirmMembersQuestion',
35175                    data: params,
35176                    transformRequest: transformRequestAsFormPost
35177                });
35178            };
35179
35180            const _SubmitNewPassword = function (params) {
35181
35182                return _ChangePassword(params);
35183
35184            };
35185
35186            /*******************************************************************
35187             * * * * * * * * * * * *  SECURITY QUESTIONS * * * * * * * * * * * *
35188             *******************************************************************/
35189
35190            const _getAllQuestions = function (language) {
35191                return $http.get(languageServiceBase + 'get-all-questions',
35192                    { headers: { 'X-Language': language } });
35193            };
35194            
35195            const _submitQuestions = function (param) {
35196                return $http({
35197                    method: 'POST',
35198                    url: urlBase + 'api/Account/ProcessQuestions',
35199                    data: param
35200                });
35201            };
35202
35203            const _test = function () {
35204                return $http({
35205                    method: 'POST',
35206                    url: urlBase + 'api/Account/Test'
35207                });
35208            };
35209
35210            /********************************************************************************/
35211            /********************************************************************************/
35212
35213            authService.switchToken = _switchToken;
35214            authService.switchToDefault = _switchToDefault;
35215            authService.broadcastResults = broadcastResults;
35216
35217            //Log-in / Log-out / Refresh Token
35218            authService.login = _login;
35219            authService.logOut = _logOut;
35220            authService.refreshToken = _refreshToken;
35221            authService.processLoginToken = _processLoginToken;
35222
35223            //Register
35224            authService.submitRegistration = registrationStepTwo;       //Register
35225            authService.VerifyMemberData = registrationStepOne;         //EligCheck
35226            authService.RegistrationStepOne = registrationStepOne;      //EligCheck
35227            authService.RegistrationStepTwo = registrationStepTwo;      //Register
35228            authService.RegistrationStepThree = registrationStepThree;  //Verify Email Code
35229            authService.RegistrationStepFour = registrationStepFour;    //SubmitSecurityQuestions
35230            authService.validateDate = validateDate;
35231            //Password Reset
35232            authService.InitiatePasswordReset = _InitiatePasswordReset;
35233            authService.VerifyEmailCode = _VerifyEmailCode;
35234            authService.SubmitSecurityQuestionAnswer = _SubmitSecurityQuestionAnswer;
35235            authService.SubmitNewPassword = _SubmitNewPassword;
35236
35237            //Change Password
35238            authService.ChangePassword = _ChangePassword;
35239
35240            //Change Email
35241            authService.ChangeEmail = _ChangeEmail;
35242
35243            //Check Authentication Status
35244            authService.AuthenticationCheck = _AuthenticationCheck;
35245
35246            //Check if Medi-Cal
35247            authService.IsMediCalCheck = _IsMediCalCheck;
35248
35249            //Check CCR Flag
35250            authService.ShowCCRCheck = _ShowCCRCheck;
35251
35252            //Security Questions
35253            authService.getAllQuestions = _getAllQuestions;
35254            authService.submitQuestions = _submitQuestions;
35255            
35256            authService.test = _test;
35257
35258            return authService;
35259
35260        }]);
35261})();
35262
35263(function () {
35264    "use strict";
35265
35266    const app = angular.module("MemberPortalServices");
35267
35268    app.factory("authStatusService", [
35269        "localStorageService",
35270        function (localStorageService) {
35271            const service = {};
35272
35273            var _status = {
35274                isAuth: false,
35275                userName: "", //needed for headerloginout controller prior to log in
35276            };
35277
35278            var _refreshStatus = function () {
35279
35280                _status = _fillAuthData();
35281
35282                return _status;
35283            };
35284
35285            const _clearAll = function () {
35286                //_clearTokenFromCookie();
35287                localStorageService.remove("loginState");
35288                //ID Card Image
35289                localStorageService.remove("IDC");
35290                localStorageService.clearAll();
35291                localStorageService.cookie.clearAll();
35292
35293                _status = {
35294                    isAuth: false,
35295                    userName: "", //needed for headerloginout controller prior to log in
35296                };
35297
35298                //console.log('clear all', _status);
35299            };
35300
35301            const _storeAuthResponse = function (response) {
35302                response.local_issue_time = new Date();
35303                localStorageService.set("loginState", response);
35304                _status = _refreshStatus();
35305                return _status;
35306            };
35307
35308            function checkIfProxied() {
35309
35310                _status = _fillAuthData();
35311
35312                const proxiedMemberNo = _status?.claims?.proxiedmembernumber;
35313
35314                return proxiedMemberNo && proxiedMemberNo !== 'N/A';
35315            }
35316
35317            const isMcrMember = function () {
35318
35319                if (!_status || !_status.claims || !_status.claims.lob) return false;
35320
35321                return ['mcr', 'cmc'].includes(_status.claims.lob.toLowerCase());
35322            };
35323
35324            const isMediCareNoMediCal = function () {
35325                if (!_status || !_status.claims || !_status.claims.lob) return false;
35326                return !isMedical() && isMcrMember();
35327            };
35328
35329            const isCCAMember = function () {
35330
35331                if (!_status || !_status.claims || !_status.claims.lob) return false;
35332
35333                return _status.claims.lob.toLowerCase() === 'cca';
35334            };
35335
35336            const isMedical = function () {
35337
35338                if (!_status || !_status.claims || !_status.claims.lob) return false;
35339
35340                return _status.claims.isMediCal.toLowerCase() === 'true';
35341            };
35342
35343            const isPrimarySubscriber = function () {
35344
35345                if (!_status || !_status.claims) return false;
35346
35347                return _status.claims.isPrimarySubscriber.toLowerCase() === "true";
35348            };
35349
35350            function _retrieveProxiedAuthStatus(claims) {
35351                try {
35352                    if (!Object.hasOwn(claims, 'proxiedmembernumber') ||
35353                        !Object.hasOwn(claims, 'proxiedmemberInfo')) return claims;
35354
35355                    if (claims.proxiedmembernumber !== 'N/A' && claims.proxiedmemberInfo === 'N/A') {
35356                        console.warn('is being proxied but no proxied member info');
35357                        throw new Error('no proxy member info');
35358                    }
35359
35360                    if (claims.proxiedmembernumber !== 'N/A' && claims.proxiedmemberInfo !== 'N/A') {
35361
35362                        const proxied = JSON.parse(atob(claims.proxiedmemberInfo));
35363                        console.info('being proxied, proxied member info', proxied);
35364
35365                        const { proxiedmemberInfo, ...proxiedAuthClaims } = claims;
35366
35367                        proxiedAuthClaims.origOwnerInfo = {};
35368
35369                        //save the orig. owner properties to origOwnerInfo,
35370                        //then overwrite acc. owner properties with proxied info
35371                        Object.keys(proxied).forEach(key => {
35372                            if (Object.hasOwn(proxiedAuthClaims, key)) {
35373                                proxiedAuthClaims.origOwnerInfo[key] = proxiedAuthClaims[key];
35374                                proxiedAuthClaims[key] = proxied[key];
35375                            }
35376                        });
35377
35378                        claims = proxiedAuthClaims;
35379                        // console.info('proxied Auth Status claims', proxiedAuthClaims);
35380                    }
35381
35382                    return claims;
35383
35384                } catch (proxyError) {
35385                    console.warn('process proxy claim error', proxyError.message);
35386                    throw proxyError;
35387                }
35388            }
35389
35390            function _fillAuthData() {
35391
35392                const authData = localStorageService.get("loginState");
35393
35394                //Check for valid loginState value
35395                if (!authData) {
35396
35397                    //Remove ID Card Image
35398                    localStorageService.remove("IDC");
35399
35400                    console.warn('no login state found in session');
35401
35402                    return {
35403                        isAuth: false,
35404                        userName: "",
35405                    };
35406                }
35407
35408                //console.log('has loginstate stored');
35409                try {
35410                    _status.claims = JSON.parse(
35411                        UTF8ArrToStr(base64DecToArr(authData.access_token.split(".")[1]))
35412                    );
35413
35414                    _status.isAuth = true;
35415                    _status.expires_in = authData.expires_in;
35416                    _status.access_token = authData.access_token;
35417                    _status.refresh_token = authData.refresh_token;
35418                    _status.issued = authData[".issued"];
35419                    _status.expires = authData[".expires"];
35420                    _status.local_issue_time = authData.local_issue_time;
35421                    _status.audience = authData.audience;
35422
35423                    //Check if Valid
35424                    if (!_status.claims.subno) { throw "no subscriber number, invalid"; }
35425
35426                    _status.claims = _retrieveProxiedAuthStatus(_status.claims);
35427
35428                    // console.log("_status.claims", _status.claims);
35429
35430                } catch (e) {
35431                    console.warn('fill auth failed', e);
35432                    localStorageService.remove("loginState");
35433                    //ID Card Image
35434                    sessionStorage.removeItem("IDC");
35435
35436                    return {
35437                        isAuth: false,
35438                        userName: "",
35439                    };
35440                }
35441
35442                //check user info which depends on if user proxied or not
35443                _status.isDSNP = isMcrMember();
35444                _status.isMediCal = isMedical();
35445                _status.isCCA = isCCAMember();
35446                _status.isMediCareNoMediCal = isMediCareNoMediCal();
35447                _status.isPrimarySubscriber = isPrimarySubscriber();
35448                // console.log('_status', _status);
35449                return _status;
35450            }
35451
35452            //const _setTokenIntoCookie = function () {
35453            //  const status = _refreshStatus();
35454            //  localStorageService.cookie.set("token", status.access_token);
35455            //  return true;
35456            //};
35457
35458            //function _clearTokenFromCookie() {
35459            //  localStorageService.cookie.remove("token");
35460            //  return true;
35461            //  }
35462
35463            service.storeAuthResponse = _storeAuthResponse;
35464            service.getStatus = _refreshStatus;
35465            service.clearAll = _clearAll;
35466
35467            //service.SetTokenIntoCookie = _setTokenIntoCookie;
35468            service.checkIfProxied = checkIfProxied;
35469
35470            return service;
35471        }
35472    ]);
35473})();
35474
35475(function () {
35476    "use strict";
35477
35478    angular.module("MemberPortalServices")
35479        .service("confirmationModalService",
35480            ["$uibModal", "environmentConfig",
35481                function ($uibModal, environmentConfig) {
35482
35483                    this.show = function (data, onYes, onNo) {
35484
35485                        const template = data.modalType? data.modalType: 'warning-message-modal';
35486
35487                        var modalInstance = $uibModal.open({
35488                            animation: true,
35489                            templateUrl: environmentConfig.appRoot +
35490                                "/App/directives/confirmation-modal/" + template + ".html",
35491                            windowClass: "modalOne",
35492                            controller: "warningMessageModal",
35493                            controllerAs: "vc",
35494                            backdrop: "static",
35495                            keyboard: false,
35496                            size: "md",
35497                            resolve: {
35498                                data: function () {
35499                                    return data;
35500                                }
35501                            }
35502                        });
35503
35504                        // Handle resolve or reject
35505                        modalInstance.result.then(function (result) {
35506                            if (result && result.answer) {
35507                                if (angular.isFunction(onYes)) {
35508                                    onYes(result);
35509                                }
35510                            } else {
35511                                if (angular.isFunction(onNo)) {
35512                                    onNo(result);
35513                                }
35514                            }
35515                        }, function () {
35516                            // Modal dismissed (same as "No")
35517                            if (angular.isFunction(onNo)) {
35518                                onNo();
35519                            }
35520                        });
35521
35522                        return modalInstance;
35523                    };
35524
35525                }]);
35526})();
35527(function () {
35528    'use strict';
35529    const app = angular.module('MemberPortalServices');
35530
35531    app.factory('contentAuthorization', ['localStorageService',
35532        function (localStorageService) {
35533            const contentAuthorization = {};
35534
35535            contentAuthorization.processAuthorizations = function ($scope, pageValidRoles, pageSecureItems) {
35536                $scope.displayThisPage = false;
35537
35538                const PSI = pageSecureItems.split(',');
35539                 
35540
35541                for (let b = 0; b < PSI.length; b++) {
35542                    $scope.item[PSI[b].trim()] = true;
35543                    // console.log( "[" + PSI[i] + ']...' + $scope.item[PSI[i].trim()]);
35544                }
35545                
35546                ////======================= MY AUTHORIZATIONS FROM TOKEN
35547                const myRoles = localStorageService.get('RoleAcc');
35548                const myInclusionAccess = localStorageService.get('IncAcc').split(',');
35549                const myExclusionAccess = localStorageService.get('ExcAcc').split(',');
35550
35551
35552                ////======================= ENABLE AUTHORIZED ROLES FOR THIS PAGE
35553                for (let a = 0; a < myRoles.length; a++) {
35554                    console.log('ACCEPTED ROLES:' + pageValidRoles);
35555                    console.log('YOUR ROLES:' + myRoles );
35556
35557                    if (pageValidRoles.search(myRoles[a]) > 0) {
35558                        $scope.displayThisPage = true;
35559                        console.log('YOUR ROLE IS VALID');
35560                    }
35561                }
35562                ////======================= ENABLE INCLUSIONS  
35563                for (let c = 0;
35563 c < myInclusionAccess.length; c++) {
35564                    if (pageSecureItems.search(myInclusionAccess[c]) > 0) {
35565                         console.log('INCLUSION:' + myInclusionAccess[c]);
35566                        $scope.item[myInclusionAccess[c]] = true;
35567                        if (myInclusionAccess[c].indexOf(('.')) < 0) {
35568                            $scope.displayThisPage = true;
35569                        }
35570                    }
35571                }
35572                ////======================= DISABLE EXCLUSIONS  
35573                for (let d = 0; d < myExclusionAccess.length; d++) {
35574                    if (pageSecureItems.search(myExclusionAccess[d]) > 0) {
35575                             console.log('EXCLUSION:' + myExclusionAccess[d]);
35576                        $scope.item[myExclusionAccess[d]] = false;
35577                    }
35578                } 
35579            };
35580
35581            return contentAuthorization;
35582    }]);
35583})();
35584
35585
35586
35587(function () {
35588    'use strict';
35589
35590
35591    const app = angular.module('MemberPortalDirectives');
35592
35593    app.factory('dataInterceptorService', ['$q', '$location', 'authStatusService', function ($q, $location, authStatusService) {
35594
35595        const authInterceptorServiceFactory = {};
35596
35597        const _request = function (config) {
35598
35599            config.headers = config.headers || {};
35600
35601            //Retrieve Token and set into headers
35602            if (authStatusService.getStatus().isAuth) {
35603                if (config.headers['X-Make-Anonymous']) {
35604                    delete config.headers['X-Make-Anonymous'];
35605                } else {
35606                    config.headers.Authorization = 'Bearer ' + authStatusService.getStatus().access_token;
35607                }
35608            }
35609
35610            return config;
35611        };
35612
35613        const _responseError = function (rejection) {
35614            if (rejection.status === 401) {
35615
35616                if (authStatusService.getStatus().isAuth) {
35617                    const requestedPath = $location.path();
35618                    $location.path('App/NotAuthorized').search('url', requestedPath);
35619                } else {
35620                    $location.path('App/Account/Login');
35621                }
35622            }
35623            return $q.reject(rejection);
35624        };
35625
35626        authInterceptorServiceFactory.request = _request;
35627        authInterceptorServiceFactory.responseError = _responseError;
35628
35629        return authInterceptorServiceFactory;
35630    }]);
35631
35632})();
35633(function () {
35634    'use strict';
35635
35636    angular
35637        .module('MemberPortalServices')
35638        .factory('featureFlagService', featureFlagService);
35639
35640    featureFlagService.$inject = ['$q', '$location', '$timeout'];
35641
35642    function featureFlagService($q, $location, $timeout) {
35643
35644        featureFlagService.isFeatureEnabled = function (flag) {
35645
35646            const flagsEnabled = $('meta[name="featureFlags"]')
35647                .attr('content').toLowerCase().replaceAll(/\s/g, '');
35648
35649            console.log("flags enabled:", flagsEnabled);
35650            return flagsEnabled.split(',').includes(flag.toLowerCase().replaceAll(/\s/g, ''));
35651        }
35652
35653        featureFlagService.redirect = function (flag, redirectToState) {
35654
35655            const deferred = $q.defer();
35656
35657            if (featureFlagService.isFeatureEnabled(flag)) {
35658                console.log("this flag is enabled:", flag);
35659                console.log("redirected to:", redirectToState);
35660
35661                $timeout(function () {
35662                    $location.path(redirectToState);
35663                }, 50);
35664
35665                return $q.reject();
35666
35667            } else {
35668                deferred.resolve();
35669            }
35670
35671            return deferred.promise;
35672        }
35673
35674        return featureFlagService;
35675    }
35676
35677})();
35678(function () {
35679    'use strict';
35680
35681    angular
35682        .module('MemberPortalServices')
35683        .factory('healthDelegateService', healthDelegateService);
35684
35685    healthDelegateService.$inject = ['$filter', 'authStatusService',
35686        'memberDataService', '$q', 'requestedLanguageService'];
35687
35688    function healthDelegateService($filter, authStatusService,
35689        memberDataService, $q, requestedLanguageService) {
35690
35691        var _isCaregiverEligible;
35692        var postUpSertMsg;
35692 //after user submit/accept a request, we may need to display an alert msg.
35693
35694        healthDelegateService.getPostUpSertMsg = function () {
35695            return postUpSertMsg;
35696        }
35697
35698        healthDelegateService.setPostUpSertMsg = function (msg) {
35699            postUpSertMsg = msg;
35700        }
35701
35702        healthDelegateService.getIsCaregiverEligible = function () {
35703            return _isCaregiverEligible;
35704        }
35705
35706        const confirmation = {
35707            withdraw: 'page_delegates_withdraw_confirmation',
35708            approve: 'page_delegates_approve_confirmation',
35709            accept: 'page_delegates_accept_confirmation',
35710            reject: 'page_delegates_reject_confirmation',
35711            revoke: 'page_delegates_revoke_confirmation',
35712            cancel: 'page_delegates_cancel_confirmation'
35713        };
35714
35715        const translateActionMapping = {
35716            withdraw: 'form_withdraw',
35717            approve: 'approve',
35718            accept: 'form_accept',
35719            reject: 'form_reject',
35720            revoke: 'form_revoke_access',
35721            cancel: 'page_delegates_cancel_access'
35722        };
35723
35724        healthDelegateService.getTranslateKey = function (action) {
35725
35726            return translateActionMapping[action];
35727        }
35728        
35729        function assignBgColor(item) {
35730            if (!item.status) {
35731                console.warn('no status item', item);
35732            }
35733
35734            const status = item.status?.toLowerCase();
35735
35736            const cssClass = {
35737                active: 'tag-green',
35738                approved: 'tag-green',
35739                accepted: 'tag-green',
35740                rejected: 'tag-red',
35741                revoked: 'tag-grey',
35742                pending: 'tag-yellow',
35743                expired: 'tag-grey',
35744                withdrawn: 'tag-grey',
35745                'pending acceptance': 'tag-yellow',
35746                'pending approval': 'tag-yellow'
35747            };
35748
35749            if (!(cssClass[status])) return 'tag-default';
35750
35751            return cssClass[status];
35752        }
35753
35754        healthDelegateService.getConfirmation = function (action) {
35755            
35756                if (!action) return null;
35757
35758            action = action.toLowerCase();
35759
35760            return confirmation[action];
35761        }
35762
35763        healthDelegateService.getTransactionObj = function (delegate, actions) {
35764
35765            const options = actions.map(action => {
35766                return {
35767                    label: healthDelegateService.getTranslateKey(action),
35768                    code: action
35769                };
35770            });
35771
35772            //console.log('options', options);
35773
35774            return {
35775                status: delegate.status,
35776                statusForDisplay: delegate.statusForDisplay,
35777                id: delegate.requestId,
35778                memberName: delegate.memberName,
35779                memberNumber: delegate.memberNumber,
35780                options: options,
35781                cssClass: delegate.cssClass
35782            };
35783        }
35784
35785        function getUpdateVerb(action) {
35786
35787            if (!action) throw exception('No action');
35788
35789            const mapping = {
35790                withdraw: 'Withdrawn',
35791                approve: 'Active',
35792                accept: 'Active',
35793                reject: 'Rejected',
35794                revoke: 'Revoked',
35795                cancel: 'Cancelled'
35796            };
35797
35798            return mapping[action];
35799        } 
35800        
35801        healthDelegateService.getUpdateStatusParams = function (request, newStatus) {
35802
35803            const effective = new Date(request.effectiveDate);
35804            const term = new Date(request.terminationDate);
35805
35806
35807            return {
35808                healthDelegateId: request.id,
35809                effectiveDate: effective.toISOString(),
35810                terminationDate: term.toISOString(),
35811                status: getUpdateVerb(newStatus)
35812            };
35813        }
35814
35815        healthDelegateService.mapParentFields = function (request) {
35816            
35817            const parentFields = {};
35818
35819            parentFields.memberField = {
35820                label: request.memberName,
35821                value: request.memberNumber
35822            };
35823
35824            parentFields.eligPeriodStart = {
35825                value: request.eligFrom
35826            };
35827
35828            parentFields.eligPeriodEnd = {
35829                value: request.eligEnd
35830            };
35831
35832            return parentFields;
35833        }
35834
35835        healthDelegateService.mapPendingChildFields = function (request) {
35836
35837            const childFields = [];
35838
35839            const firstField = {
35840                value: request.requestType
35841            };
35842
35843            childFields.push(firstField);
35844
35845            const secondField = {
35846                value: request.requestDate,
35847                additional: request.requestDateNotes.includes(' 0 ') ? '' : request.requestDateNotes
35848            };
35849
35850            childFields.push(secondField);
35851
35852            const thirdField = {
35853                value: request.authDetails
35854            }
35855
35856            childFields.push(thirdField);
35857
35858            const fourthField = {
35859                value: request.authPeriodFrom + ' - ' + request.authPeriodTo
35860            }
35861
35862            childFields.push(fourthField);
35863
35864            return childFields;
35865        }
35866
35867        healthDelegateService.mapChildFields = function (request) {
35868            
35869            const childFields = [];
35870
35871            const firstField = {
35872                value: request.authDetails
35873            }
35874
35875            childFields.push(firstField);
35876
35877            const secondField = {
35878                value: request.authPeriodFrom + ' - ' + request.authPeriodTo
35879            }
35880
35881            childFields.push(secondField);
35882
35883            return childFields;
35884        }
35885
35886        healthDelegateService.isCaregiverEligible = function (language) {
35887
35888            const deferred = $q.defer();
35889
35890            const defaultSettings = {
35891                addCaregiver: false,
35892                requestAccess: false,
35893                message: ''
35894            }; 
35895
35896            const authStatus = authStatusService.getStatus();
35897
35898            if (!authStatus.isAuth) {
35899                
35900                return deferred.resolve(defaultSettings);
35901            }
35902
35903            const claims = authStatus.claims;
35904
35905            const { username } = claims;
35906            
35907            memberDataService.getDelegateFeaturePermission(username, language).then(function (resp) {
35908                console.log('get delegate eligibility resp', resp);
35909                if (!resp || !resp.data) {
35910                    
35911                    return deferred.reject(defaultSettings);
35912                }
35913
35914                return deferred.resolve(resp.data);
35915
35916            }).catch(function () {
35917                deferred.reject(defaultSettings);
35918            });
35919
35920            return deferred.promise;
35921        }
35922
35923        healthDelegateService.loadPendingTotal = function () {
35924
35925            const deferred = $q.defer();
35926
35927            const currentLanguage = requestedLanguageService.getCurrentLanguage();
35928
35929            const claims = authStatusService.getStatus().claims;
35930
35931            if (!claims) deferred.resolve('');
35932
35933            const memberId = claims.username;
35934
35935            memberDataService.getPendingDelegates(memberId, currentLanguage).then(
35936                function (resp) {
35937
35938                    // console.log('iehp badge: pending delegates from api', resp.data);
35939
35940                    if (!resp || !resp.data || resp.data.length === 0) { deferred.resolve(''); }
35941
35942                    console.log('pending total', resp.data.length);
35943
35944                    deferred.resolve(resp.data.length);
35945                }, function () {
35946                 deferred.reject('');
35947                });
35948
35949            return deferred.promise;
35950        }
35951
35952        healthDelegateService.isDelegateEligible = function () {
35953
35954            const authStatus = authStatusService.getStatus();
35955
35956            if (!authStatus || !authStatus.isAuth) {
35957                _isCaregiverEligible = false;
35958
35959                return _isCaregiverEligible;
35960            }
35961
35962            const claims = authStatus.claims;
35963
35964            const { age, isOpenAccess } = claims;
35965
35966            if (!age || !isOpenAccess) return false;
35967
35968            const isFosterChild = (isOpenAccess.toLowerCase() === 'true');
35969
35970            _isCaregiverEligible = age >= 18 && (!isFosterChild || age >= 21);
35971
35972            console.log('member age:' + age + ' foster child:' + isFosterChild +
35973                ' therefore, caregiver eligible:' + _isCaregiverEligible);
35974
35975            return _isCaregiverEligible;
35976        }
35977
35978        healthDelegateService.mapDelegates = function (resp) {
35979
35980            if (!resp) return null;
35981
35982            const mappedDelegates = [];
35983
35984            resp.forEach(function (item) {
35985
35986                const transaction = {
35987                    requestId: item.healthDelegateId,
35988                    requestDateNotes: item.requestDateNotes,
35989                    requestType: item.requestType,
35990                    memberName: item.displayName,
35991                    memberNumber: item.displayMemberId,
35992                    approverName: item.approverName,
35993                    eligFrom: item.coverageStartDate,
35994                    eligEnd: item.coverageTerminationDate,
35995                    authDetails: item.authorizationDetails,
35996                    requestor: item.requesterName,
35997                    requestDate: item.createdDate,
35998                    authPeriodFrom: item.effectiveDate,
35999                    authPeriodTo: item.terminationDate,
36000                    status: item.status,
36001                    statusForDisplay: item.statusForDisplay,
36002                    cssClass: assignBgColor(item),
36003                    maxAllowedDate: item.maxAllowedDate
36004                };
36005
36006                mappedDelegates.push(transaction);
36007            });
36008
36009            // console.log('mappedDelegates', mappedDelegates);
36010            return mappedDelegates;
36011        }
36012
36013        return healthDelegateService;
36014    }
36015
36016})();
36017(function () {
36018    'use strict';
36019
36020    angular
36021        .module('MemberPortalServices')
36022        .factory('iFormioService', iFormioService);
36023
36024    iFormioService.$inject = ['$http', '$sce', '$q', 'authStatusService'];
36025
36026    function iFormioService($http, $sce, $q, authStatusService) {
36027        var gatewayBase = $('meta[name="gatewayBase"]').attr('content');
36028
36029        var iFormioService = {};
36030        var emailAddress = '';
36031
36032        var formioBase = $('meta[name="formioBase"]').attr('content');
36033        var iFormioBase = $('meta[name="iFormioBase"]').attr('content');
36034        var form = '';
36035        var params = '';
36036        var origin = window.location.origin;
36037        var source = '';
36038
36039        var elementId = '';
36040        var iFormioSource = 'iFormio';
36041        var targetOrigin = $('meta[name="iFormioOrigin"]').attr('content');
36042
36043        var formioToken = '';
36044        var formioDecodedToken = '';
36045        var formioTokenExpiration = '';
36046        // Do not send soa token in headers
36047        var formioHeaders = {
36048          headers: {
36049              'X-Make-Anonymous': 'true'
36050          }
36051        };
36052
36053        iFormioService.getEmailAddress = function () {
36054            const claims = authStatusService.getStatus().claims;
36055            return emailAddress ? emailAddress : claims.email;
36056        };
36057
36058        iFormioService.setEmailAddress = function (email) {
36059          emailAddress = email;
36060        };
36061
36062        iFormioService.setElementId = function (iframeId) {
36063          elementId = iframeId;
36064        };
36065
36066        iFormioService.setFormName = function (formName) {
36067          form = formName;
36068        };
36069
36070        iFormioService.setFormParams = function (formParams) {
36071          params = '&' + serialize(formParams);
36072        };
36073
36074        iFormioService.setFormSource = function (appName) {
36075          source = appName;
36076        };
36077
36078        iFormioService.setIFormioParams = function (formName, appName) {
36079          form = formName;
36080          params = '';
36081          source = appName;
36082        };
36083
36084        iFormioService.getIFormioSrc = function (viewOnly) {
36085          var url = iFormioBase + '?form=' + form + params + '&origin=' + origin + '&source=' + source;
36086
36087          if (viewOnly) {
36088            url += '&viewOnly=true'; 
36089          }
36090
36091          return $sce.trustAsResourceUrl(url);
36092        }
36093        
36094        iFormioService.iFormioListener = function (event) {
36095            if (event.origin === targetOrigin && event.data.source === iFormioSource) {
36096              var formioEvent = iFormioService.CustomEvent(iFormioSource + '-' + event.data.event, { detail: event.data.data });
36097              window.dispatchEvent(formioEvent);
36098            }
36099        };
36100
36101        iFormioService.CustomEvent = function (event, params) {
36102            params = params || { bubbles: false, cancelable: false, detail: null };
36103            var evt = document.createEvent('CustomEvent');
36104            evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
36105            return evt;
36106        }
36107
36108        iFormioService.getSubmission = function () {
36109          iFormioService.sendIFormioMessage('getSubmission', null);
36110        }
36111
36112        iFormioService.rebuild = function () {
36113          iFormioService.sendIFormioMessage('rebuild', null);
36114        }
36115
36116        iFormioService.refreshData = function () {
36117          iFormioService.sendIFormioMessage('refreshData', null);
36118        }
36119
36120        iFormioService.scrollToTop = function () {
36121          iFormioService.sendIFormioMessage('scrollToTop', null);
36122        }
36123
36124        iFormioService.setFormioOptions = function (data) {
36125          iFormioService.sendIFormioMessage('setFormioOptions', data);
36126        }
36127
36128        iFormioService.setFormioToken = function (data) {
36129          iFormioService.sendIFormioMessage('setFormioToken', data);
36130        }
36131
36132        iFormioService.setIFormioEvents = function (data) {
36133          iFormioService.sendIFormioMessage('setIFormioEvents', data);
36134        }
36135
36136        iFormioService.setIFormioSettings = function (data) {
36137          iFormioService.sendIFormioMessage('setIFormioSettings', data);
36138        }
36139        
36140        iFormioService.setLanguage = function (data) {
36141          iFormioService.sendIFormioMessage('setLanguage', data);
36142        }
36143
36144        iFormioService.setReadOnly = function (data) {
36145          iFormioService.sendIFormioMessage('setReadOnly', data);
36146        }
36147
36148        iFormioService.setReadOnlyAfterReady = function (data) {
36149          iFormioService.sendIFormioMessage('setReadOnlyAfterReady', data);
36150        }
36151
36152        iFormioService.setShowForm = function (data, auth) {
36153          if (auth) {
36154            var diff = moment.unix(formioTokenExpiration).diff(moment(), 'hours', true);
36155
36156            // Has token and more than an hour before token expires
36157            if (formioToken && diff > 1) {
36158              iFormioService.sendIFormioMessage('setFormioToken', formioToken);
36159              iFormioService.sendIFormioMessage('setShowForm', data);
36160            } else {
36161              iFormioService.getToken().then(function (resp) {
36162                iFormioService.setToken(JSON.parse(resp.data));
36163                iFormioService.sendIFormioMessage('setFormioToken', formioToken);
36164                iFormioService.sendIFormioMessage('setShowForm', data);
36165              }).catch(function(e) {
36166                console.log(e);
36167              });
36168            }
36169          }
36170          else {
36171            iFormioService.sendIFormioMessage('setShowForm', data);
36172          }
36173        }
36174
36175        iFormioService.setShowPrint = function (data) {
36176          iFormioService.sendIFormioMessage('setShowPrint', data);
36177        }
36178
36179        iFormioService.setSubmission = function (data) {
36180          iFormioService.sendIFormioMessage('setSubmission', data);
36181        }
36182
36183        iFormioService.setSubmissionView = function (data) {
36184          iFormioService.sendIFormioMessage('setSubmissionView', data);
36185        }
36186
36187        iFormioService.updateSubmission = function (data) {
36188          iFormioService.sendIFormioMessage('updateSubmission', data);
36189        }
36190
36191        iFormioService.sendIFormioMessage = function (event, data) {
36192            var element = document.getElementById(elementId);
36193            if (element) {
36194              try {
36195                element.contentWindow.postMessage(
36196                  {
36197                    event: event,
36198                    data: data,
36199                    source: source
36200                  },
36201                  targetOrigin
36202                );
36203              } catch (e) {
36204                console.error(e);
36205              }
36206          }
36207        }
36208
36209        iFormioService.getToken = function () {
36210          return $http.get(gatewayBase + 'formio/token');
36211        };
36212
36213        iFormioService.setToken = function (token) {
36214          // parse logic from keep-alive.directive.js
36215          formioDecodedToken = JSON.parse(UTF8ArrToStr(base64DecToArr(token.split('.')[1])));
36216
36217          formioToken = token;
36218          formioHeaders.headers['x-jwt-token'] = token;
36219          formioTokenExpiration = formioDecodedToken.exp;
36220        };
36221
36222         iFormioService.getFormSubmissionsInterpreter = function (params) {
36223          var deferred = $q.defer();
36224          var diff = moment.unix(formioTokenExpiration).diff(moment(), 'hours', true);
36225
36226          // Has token and more than an hour before token expires
36227          if (formioToken && diff > 1) {
36228            deferred.resolve($http.get(formioBase + '/' + form + '/submission' + '?' +params, formioHeaders));
36229          } else {
36230            iFormioService.getToken().then(function (resp) {
36231              iFormioService.setToken(JSON.parse(resp.data));
36232              deferred.resolve($http.get(formioBase + '/' + form + '/submission' + '?' + params, formioHeaders));
36233            }).catch(function(e) {
36234              console.log(e);
36235              deferred.reject(e);
36236            });
36237          }
36238        
36239          return deferred.promise;
36240        };
36241
36242        iFormioService.getFormSubmissions = function (params) {
36243          var deferred = $q.defer();
36244          var diff = moment.unix(formioTokenExpiration).diff(moment(), 'hours', true);
36245
36246          // Has token and more than an hour before token expires
36247          if (formioToken && diff > 1) {
36248            deferred.resolve($http.get(formioBase + '/' + form + '/submission' + '?' + serialize(params), formioHeaders));
36249          } else {
36250            iFormioService.getToken().then(function (resp) {
36251              iFormioService.setToken(JSON.parse(resp.data));
36252              deferred.resolve($http.get(formioBase + '/' + form + '/submission' + '?' + params, formioHeaders));
36253            }).catch(function(e) {
36254              console.log(e);
36255              deferred.reject(e);
36256            });
36257          }
36258        
36259          return deferred.promise;
36260        };
36261
36262        function serialize(params) {
36263          return params
36264            ? Object.keys(params)
36265                .map((key) => key + '=' + params[key])
36266                .join('&')
36267            : '';
36268        }
36269
36270        return iFormioService;
36271    }
36272    
36273})();
36274(function () {
36275  angular.module("MemberPortalServices").factory("inboxService", inboxService);
36276
36277    inboxService.$inject = ['authStatusService'];
36278
36279    function inboxService(authStatusService) {
36280
36281        var readBroadcastedMsgCnt = 0;
36282
36283        function getCookieName() {
36284
36285            const memberId = authStatusService.getStatus().claims.username;
36286
36287            const identifier = memberId ? memberId.slice(-5) : '';
36288
36289            return 'readBcMsgs' + identifier;
36290        }
36291        
36292        function getMessageCategories() {
36293          return [
36294            {
36295              categoryDisplayName: "All",
36296              cssClass: "inbox-category-all inbox-category"
36297            },
36298            {
36299              categoryDisplayName: "PDR",
36300              cssClass: "inbox-category-pdr inbox-category"
36301            },
36302            {
36303              categoryDisplayName: "G & A",
36304              cssClass: "inbox-category-ga inbox-category"
36305            },
36306            {
36307              categoryDisplayName: "Notices",
36308              cssClass: "inbox-category-notices inbox-category"
36309            },
36310            {
36311              categoryDisplayName: "Policy Updates",
36312              cssClass: "inbox-category-policy-updates inbox-category"
36313              },
36314            {
36315                categoryDisplayName: "Unknown",
36316                cssClass: "inbox-category-default inbox-category"
36317            },
36318          ];
36319        }
36320
36321        function getCssClassPerCategoryName(categoryName) {
36322            
36323            const matched = getMessageCategories().find(m => m.categoryDisplayName === categoryName);
36324            
36325            return matched ? matched.cssClass : '';
36326        }
36327
36328        function filters() {
36329          return [
36330            {
36331              label: "Important",
36332              imgSrc: "Content/Images/portal-inbox/warning.svg",
36333              key: "important",
36334              selected: false,
36335            },
36336             {
36337               label: "Reply",
36338               imgSrc: "Content/Images/portal-inbox/reply.svg",
36339               key: "reply",
36340               selected: false,
36341             },
36342            {
36343              label: "Unread",
36344              imgSrc: "Content/Images/portal-inbox/email_unread.svg",
36345              key: "unread",
36346              selected: false,
36347            },
36348            {
36349              label: "Read",
36350              imgSrc: "Content/Images/portal-inbox/email_read.svg",
36351              key: "read",
36352              selected: false,
36353            },
36354            {
36355              label: "Archived",
36356              imgSrc: "Content/Images/portal-inbox/inventory.svg",
36357              key: "archived",
36358              selected: false,
36359            },
36360          ];
36361        }
36362
36363        function addTodayDesc(date) {
36364
36365              if (!date) return '';
36366
36367              var momentDate;
36368
36369              try {
36370                  momentDate = moment(date);
36371
36372                  const today = moment();
36373
36374                  if (momentDate.isSame(today, 'day')) {
36375                      return 'Today';
36376                  }
36377
36378                  return '';
36379
36380              } catch(error) {
36381                  return '';
36382              }
36383          }
36384
36385        function isNeededReply(message) {
36386              //console.log('message', message);
36387              return message &&
36388                  message.currentMessageState &&
36389                  message.currentMessageState.status &&
36390                  message.currentMessageState.status.toLowerCase() === "needs more info"
36391                  ? true
36392                  : false;
36393        }
36394
36395        function getIconSrcByType(type) {
36396          //console.log('uploaded type', type);
36397
36398            let url = "Content/Images/portal-inbox/file-regular.svg";
36399
36400          if (type) {
36401              switch (type.toLowerCase()) {
36402              case "application/pdf":
36403              case "pdf":
36404                url = "Content/Images/portal-inbox/pdf.svg";
36405                break;
36406              case "webpage":
36407                url = "Content/Images/portal-inbox/insert_link.svg";
36408                break;
36409              case "doc":
36410                url = "Content/Images/portal-inbox/file-word-regular.svg";
36411                break;
36412              default:
36413                url = "Content/Images/portal-inbox/file-regular.svg";
36414            }
36415          }
36416
36417          return url;
36418        }
36419
36420        function isBroadcastMessageRead(id) {
36421            const readMessages = getReadBroadcastMessages();
36422            return readMessages.indexOf(id) >= 0;
36423        }
36424
36425        function getReadBroadcastMessageCnt() {
36426
36427            const readMessages = getReadBroadcastMessages();
36428
36429            readBroadcastedMsgCnt = readMessages ? readMessages.length : 0;
36430
36431            //console.log('inbox service: read broadcasted msg', readBroadcastedMsgCnt);
36432
36433            return readBroadcastedMsgCnt;
36434        }
36435
36436        function markReadBroadcastMessage(id) {
36437          const readMessages = getReadBroadcastMessages();
36438          const msgSet = new Set(readMessages);
36439          msgSet.add(id);
36440          setReadBroadcastMessagesCookie(Array.from(msgSet));
36441        }
36442
36443        function setReadBroadcastMessagesCookie(ids = [], days = 365) {
36444          let expires = "";
36445
36446          if (days) {
36447            const date = new Date();
36448            date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
36449            expires = "; expires=" + date.toUTCString();
36450          }
36451
36452          const broadcastCookieName = getCookieName();
36453
36454          document.cookie = broadcastCookieName + "=" + (JSON.stringify(ids) || "") + expires + "; path=/";
36455        }
36456
36457        function getReadBroadcastMessages() {
36458
36459            const broadcastCookieName = getCookieName();
36460
36461            const nameEQ = broadcastCookieName + "=";
36462            const ca = document.cookie.split(';');
36463            for(let i = 0; i < ca.length; i++) {
36464                let c = ca[i];
36465                while (c.charAt(0) === ' ') c = c.substring(1, c.length);
36466                if (c.indexOf(nameEQ) === 0) return JSON.parse(c.substring(nameEQ.length, c.length));
36467            }
36468            return [];
36469        }
36470
36471        return {
36472          getIconSrcByType: getIconSrcByType,
36473          isNeededReply: isNeededReply,
36474          getReadBroadcastMessageCnt: getReadBroadcastMessageCnt,
36475          getFilters: filters,
36476          addTodayDesc: addTodayDesc,
36477          getMessageCategories: getMessageCategories,
36478          getCssClassPerCategoryName: getCssClassPerCategoryName,
36479          markReadBroadcastMessage: markReadBroadcastMessage,
36480          isBroadcastMessageRead: isBroadcastMessageRead
36481    };
36482  }
36483})();
36484
36485(function () {
36486    'use strict';
36487
36488    const app = angular.module('MemberPortalServices');
36489
36490    app.factory('memberDataService', ['$http', '$rootScope', 'utilitiesService', '$filter', '$q',
36491        function($http, $rootScope, utilitiesService, $filter, $q) {
36492
36493            var memberDataService = {};
36494            memberDataService.unreadCount = 0;
36495            memberDataService.counts = {
36496                unreadCount: 0,
36497                broadcastCount: 0,
36498                importantCount: 0,
36499                attentionNeededCount: 0,
36500                totalWithoutBroadcast: 0,
36501                total: 0,
36502            }
36503            var appMode = $('meta[name="appMode"]').attr('content');
36504            var serviceBase = $('meta[name="soaUrl"]').attr('content');
36505            var iehpServiceBase = $('meta[name="iehpServiceBase"]').attr('content');
36506            var gatewayBase = $('meta[name="gatewayBase"]').attr('content');
36507            var healthConnectServiceBase = $('meta[name="healthConnectServiceBase"]').attr('content');
36508            var reverseProxyServeysApiUrl = $('meta[name="reverseProxyServeysApiUrl"]').attr('content');
36509            var geoAddressBase = $('meta[name="geoAddressApiBase"]').attr('content');
36510            var languageServiceBase = $('meta[name="languageServiceApiBase"]').attr('content');
36511            var inboxBaseUrl = $('meta[name="inboxBaseUrl"]').attr('content');
36512            var providerSearchBase= $('meta[name="providerSearchBaseUrl"]').attr('content');
36513            var dmsGateWayBaseUrl = $('meta[name="dmsGateWayBaseUrl"]').attr('content');
36514            var leadRedeterminationRequest = null;
36515
36516            if (appMode === 'dev') {
36517                var cookies = {};
36518
36519                var x = document.cookie;
36520                var c = x.split(';');
36521
36522                for (var i = c.length - 1; i >= 0; i--) {
36523                    var C = c[i].split('=');
36524                    C[0] = C[0].replace(/\s+/g, '');
36525                    //console.log('C', C);
36526                    cookies[C[0]] = C[1];
36527                }
36528                //console.log('cookies are ', cookies['iehpSoaUrl']);
36529                if (cookies['iehpSoaUrl']) {
36530                    serviceBase = cookies['iehpSoaUrl'];
36531                }
36532            }
36533
36534            /*************************************
36535             *********** Eligibility *************
36536             *************************************/
36537
36538            memberDataService.checkLeadRedetermination = function (memberNumber) {
36539
36540                if (leadRedeterminationRequest) {
36541                    return leadRedeterminationRequest;
36542                }
36543
36544                const url = dmsGateWayBaseUrl +
36545                    'dynamics-dataverse-integrator-was/api/dynamics/LeadRedetermination?memberNumber=' +
36546                    memberNumber;
36547
36548                leadRedeterminationRequest = $http.post(url);
36549
36550                return leadRedeterminationRequest;
36551            };
36552
36553            memberDataService.viewMemberLetter = function(scopeData, caseNumber) {
36554
36555                memberDataService.GetMedicalAuthMemberLetter(caseNumber).success(function(data, textStatus) {
36556
36557                    if (textStatus === 200) {
36558
36559                        const fileName = `Medical-Authorization-Letter-${caseNumber}.pdf`;
36560
36561                        const file = new Blob([data], { type: 'application/pdf' });
36562
36563                        if (window.navigator && window.navigator.msSaveOrOpenBlob) {
36564                            window.navigator.msSaveOrOpenBlob(file, fileName);
36565                        } else {
36566                            const fileUrl = URL.createObjectURL(file);
36567                            const link = document.createElement('a');
36568                            document.body.appendChild(link);
36569                            link.href = fileUrl;
36570                            link.download = fileName;
36571                            link.click();
36572                            document.body.removeChild(link);
36573                        }
36574                    } else {
36575                        memberDataService.showNoLetter(scopeData, caseNumber);
36576                    }
36577
36578                }).error(function() {
36579
36580                    memberDataService.showNoLetter(scopeData, caseNumber);
36581                });
36582            }
36583
36584            /**
36585            As of 6/17/2024
36586            Show download buttons IF
36587                AND It's a medical referral OR behavioral health referral
36588                AND is approved OR denied OR partially approved
36589
36590                OR it's a pharmacy referral AND NOT dismissed
36591             */
36592            memberDataService.canShowMemberLetter = function (typeOfService, item) {
36593                const downloadableMedicalStatuses = ["Approved", "Denied", "Partial Approval", "Termination"];
36594                const serviceType = typeOfService(item); //Medical includes BH as well here
36595
36596                const isMedicalAndDownloadable = serviceType === "Medical" &&
36597                    downloadableMedicalStatuses.includes(item.reviewDecision);;
36598                const isPharmacyNotDismissed = serviceType === "Pharmacy" && item.eventStatus !== "Dismissed";
36599
36600                return !item.showNoLetterFound && (isMedicalAndDownloadable || isPharmacyNotDismissed);
36601            }
36602
36603
36604            memberDataService.showNoLetter = function(data, caseNumber) {
36605                const item = $.grep(data,
36606                    function(element) {
36607                        return element.eventNumber === caseNumber;
36608                    })[0];
36609
36610                item.showNoLetterFound = true;
36611            }
36612
36613            memberDataService.activityWrite = function (activityType) {
36614                $http.get(serviceBase + 'Activity/Log?activityType=' + activityType).success(function () { });
36615            };
36616
36617            memberDataService.getCurrentEligibility = function (language) {
36618
36619                return $http.get(languageServiceBase +
36620                    'eligibility-status-current',
36621                    { headers: { 'X-Language': language } });
36622            };
36623
36624            //Get CCA Plan Details
36625            memberDataService.getPlanDetails = function(language) {
36626                return $http.get(languageServiceBase +
36627                    'eligibility-plan-detail',
36628                    { headers: { 'X-Language': language } });
36629            };
36630
36631            //Get Current CCA Member Benefits
36632            memberDataService.getCurrentBenefits = function(language) {
36633
36634                return $http.get(languageServiceBase +
36635                    'eligibility-status-current',
36636                    { headers: { 'X-Language': language } });
36637
36638            };
36639
36640            memberDataService.getLis = function (memberNumber) {
36641                var url = serviceBase + 'Lis/DrugTier?memberNumber=' + memberNumber;
36642                return $http.get(url);
36643            }
36644
36645            memberDataService.getUrgentContactList = function () {
36646
36647                return $http.get(gatewayBase + 'passbook/phones');
36648            }
36649
36650            memberDataService.printIdCard = function () {
36651            };
36652
36653            //we removed DOS param because the endpoint doesn't check DOS, per Istvan.
36654            memberDataService.getEligibilityByDateOfService = function(language) {
36655
36656                const config = {
36657                    headers: { 'X-Language': language }
36658                };
36659
36660                return $http.get(languageServiceBase + 'eligibility-status-current',
36661                    config);
36662            };
36663
36664            memberDataService.getHistoricalEligibility = function (language) {
36665
36666                return $http.get(languageServiceBase + 'eligibility-status-all',
36667                    { headers: { 'X-Language': language } });
36668            };
36669
36670            /************************************************
36671             ******************* ASK IEHP *******************
36672             ************************************************/
36673
36674            memberDataService.askIEHP = function (params) {
36675                return $http.put(serviceBase + 'AskMemberServices/INSQuestion', params);
36676            };
36677
36678            /************************************************
36679             ************* PCP Search/Change ****************
36680             ************************************************/
36681
36682            memberDataService.SearchProviders = function (params) {
36683                return $http.post(serviceBase + 'Member/Pcp/Search', params);
36684            };
36685
36686            memberDataService.getPendingPcpChanges = function () {
36687                return $http.get(serviceBase + 'Member/PcpChange/Pending');
36688            };
36689
36690            memberDataService.getAllIpas = function () {
36691                return $http.get(serviceBase + 'Providers/AllIpas');
36692            };
36693
36694            memberDataService.SubmitPcpChange = function (params) {
36695                return $http.post(serviceBase + 'Member/PcpChange/Request', params);
36696            };
36697
36698            memberDataService.deleteRequest = function (id) {
36699                return $http({
36700                    method: 'DELETE',
36701                    url: serviceBase + 'Member/PcpChange/Void',
36702                    params: { changeRequestId: id }
36703                });
36704            };
36705
36706            /*************************************
36707             ********* Authorizations ************
36708             *************************************/
36709
36710            memberDataService.SearchAllAuthorizations = function (params) {
36711                return $http.post(serviceBase + 'Member/Authorizations/All/Search2', params);
36712            };
36713
36714            memberDataService.GetMedicalAuth = function (params) {
36715                return $http.post(serviceBase + 'Member/Authorizations/Medical/Get', params);
36716            };
36717
36718            memberDataService.GetMedicalAuthMemberLetter = function (authId) {
36719                const config = {
36720                    params: {
36721                        authorizationNumber: authId
36722                    },
36723                    responseType: 'arraybuffer'
36724                };
36725                return $http.get(serviceBase + 'Member/Authorizations/Medical/GetLetter', config);
36726            };
36727
36728            memberDataService.GetRxAuth = function (params) {
36729                return $http.post(serviceBase + 'Member/Authorizations/Rx/Get', params);
36730            };
36731
36732            memberDataService.GetVisionAuth = function (params) {
36733                return $http.post(serviceBase + 'Member/Authorizations/Vision/Get', params);
36734            };
36735
36736            /*************************************************
36737             ********* Rx Prescriptions & Reminders **********
36738             *************************************************/
36739
36740            memberDataService.LoadRxPrescriptions = function (params) {
36741                return $http.post(`${serviceBase}Member/Prescriptions/Load`, params);
36742            };
36743
36744            memberDataService.SaveRxReminder = function (params) {
36745                return $http.post(`${serviceBase}Member/Prescriptions/Save`, params);
36746            };
36747
36748              
36749            /*************************************
36750            ************* Interpreter Request ****************
36751            *************************************/
36752            memberDataService.GetSubmittingProviderListInterpreter = function (params) {
36753                return $http.post(`${providerSearchBase}_search`, params);
36754            };
36755            
36756            /*************************************
36757             ************* CLAIMS ****************
36758             *************************************/
36759
36760            memberDataService.GetClaims = function () {
36761                return $http.get(serviceBase + 'Claims/Member/ClaimHeaders', { cache: true });
36762            };
36763
36764            memberDataService.GetClaim = function (params) {
36765                const config = {
36766                    params: params
36767                };
36768                return $http.get(serviceBase + 'Claims/Member/Claim', config);
36769            };
36770
36771            //------------------------------------------------------------------------------//
36772            //------------------------------------------------------------------------------//
36773
36774            /*************************************
36775             ****** Correspondence ****************
36776             *************************************/
36777            memberDataService.GetCorrespondence = function () {
36778                return $http.get(serviceBase + 'Member/Letter/Search', { cache: false });
36779            };
36780
36781            memberDataService.setIsRead = function (params) {
36782                return $http.post(`${serviceBase}Member/Letter/Document`, params);
36783            };
36784
36785            memberDataService.DownloadMyCorrespondence = function (docId) {
36786                const config = {
36787                    responseType: 'arraybuffer'
36788                };
36789                return $http.get(serviceBase + '/Member/Letter/Download/Document?documentId=' + docId, config);
36790            };
36791
36792            /*************************************
36793            *********** Immunization *************
36794            *************************************/
36795
36796            //Get All Immunizations, verified active (09/15/2021)
36797            memberDataService.GetImmunization = function() {
36798
36799                return $http.get(serviceBase + 'Member/YellowCard');
36800            };
36801
36802            /*************************************
36803             ************** LABS *****************
36804             *************************************/
36805
36806            //Get All Labs, verified active (09/15/2021)
36807            memberDataService.GetLabData = function(language) {
36808                return $http.get(serviceBase + 'Member/LabResultGrouped');
36809            };
36810
36811            memberDataService.GetCarePlans = function() {
36812                return $http.get(serviceBase + "Member/CarePlan");
36813            }
36814
36815            memberDataService.getLimeSurveyList = function (params) {
36816
36817                const method = 'ParticipantSurvey';
36818                return $http.post(reverseProxyServeysApiUrl + method, params);
36819            };
36820
36821            memberDataService.GetHaSurveyToken = function (payload) {
36822
36823                const method = 'ParticipantSurvey';
36824                return $http.post(reverseProxyServeysApiUrl + method, payload);
36825            };
36826
36827            /************** NCQA MediCal/ Medicare
36828            *************** PHARMACY - TARGUS
36829            ***************/
36830
36831            memberDataService.FetchArgusValidToken = function (landingPath, params) {
36832
36833                return $http.get(serviceBase + 'PharmacyBenefits/GetRsoToken?landingpath=' + landingPath, params);
36834            };
36835
36836            memberDataService.PostDrugRequest = function (data) {
36837                const method = 'PharmacyBenefits/PostDrugRequest';
36838                return $http.post(serviceBase + method, data);
36839            };
36840
36841            memberDataService.DrugRequestDisplayPdf = function (params) {
36842                const config = {
36843                    params: params,
36844                    responseType: 'arraybuffer'
36845                };
36846                const urlWebService = serviceBase + 'PharmacyBenefits/DisplayPdf';
36847                return $http.post(urlWebService, params, config);
36848            };
36849
36850            /*************** NOTIFICATIONS *************/
36851
36852            memberDataService.GetAnnouncements = function(params, language, useCached) {
36853
36854                if (useCached === false) {
36855                    return $http.get(iehpServiceBase +
36856                        'api/announcement/announcements?lob=' +
36857                        params.lob +
36858                        "&type=" +
36859                        params.type,
36860                        { headers: { 'X-Language': language } });
36861                }
36862
36863                return $http.get(iehpServiceBase +
36864                    'api/announcement/announcements?lob=' +
36865                    params.lob +
36866                    "&type=" +
36867                    params.type,
36868                    { headers: { 'X-Language': language }, cache: true });
36869            }
36870
36871            memberDataService.GetMemberNotifications = function (language, useCached) {
36872
36873                if (useCached === false) {
36874                    return $http.get(languageServiceBase + 'external-alerts', { headers: { 'X-Language': language } });
36875                }
36876
36877                return $http.get(languageServiceBase + 'external-alerts',
36878                    { headers: { 'X-Language': language }, cache: true });
36879            };
36880
36881            //Update Notification, verified active (09/15/2021)
36882            memberDataService.mdsUpdateNotificationStatus = function(alertId) {
36883                return $http.get(serviceBase + 'ExternalAlerts/UpdateStatus/?alertId=' + alertId);
36884            };
36885
36886            memberDataService.GetHRASurveyStatus = function() {
36887                return $http.get(serviceBase + 'Hra/GetSurveyUrl');
36888            }
36889
36890            memberDataService.GetHRACarePlan = function() {
36891                return $http.get(serviceBase + 'hra/GetHraCarePlanPdf', { responseType: 'arraybuffer' });
36892            }
36893
36894            memberDataService.IsMemberCurrentlySPD = function() {
36895                return $http.get(serviceBase + 'hra/IsMemberCurrentlySPD');
36896            }
36897
36898            /*************************************
36899             ************** NCQA COURSES *****************
36900             *************************************/
36901
36902            memberDataService.GetActivitiesByCategory = function (languageCode, categoryId) {
36903                return $http.get(healthConnectServiceBase + 'activities?languageCode=' + languageCode + '&categoryId=' + categoryId);
36904            };
36905
36906            memberDataService.GetCoursesByCategory = function (languageCode, categoryId) {
36907                return $http.get(healthConnectServiceBase + 'courses?languageCode=' + languageCode + '&categoryId=' + categoryId +
36908                    '&requestType=Eligible&excludePastCourses=true&isActive=true&isPublished=true&RowsPerPage=500');
36909            };
36910
36911            memberDataService.HealthEdOptionsLookUp = function (languageCode) {
36912                return $http.get(healthConnectServiceBase + 'lookups/types?languageCode=' + languageCode);
36913            };
36914
36915            memberDataService.HealthEdHistory = function (languageCode, memberId, excludeDroppedMembers) {
36916                return $http.get(healthConnectServiceBase + 'members/' + memberId +
36917                    '/registrations?MergeMemberIdentities=true&languageCode=' + languageCode + '&excludeDroppedMembers=' + excludeDroppedMembers);
36918            };
36919
36920            memberDataService.HealthEdCourseDocuments = function (languageCode, courseId) {
36921                return $http.get(healthConnectServiceBase + 'courses/' + courseId + '/documents?languageCode=' + languageCode);
36922            };
36923
36924            memberDataService.HealthEdMemberCorrespondence = function (memberId) {
36925                return $http.get(healthConnectServiceBase + 'members/' + memberId + '/correspondence');
36926            };
36927
36928            memberDataService.HealthEdCourseWaitlist = function (languageCode, memberId) {
36929                return $http.get(healthConnectServiceBase + 'activities/waitlists?languageCode=' + languageCode + '&memberNumber=' + memberId);
36930            };
36931
36932            memberDataService.GetHealthEdDocument = function (courseId, fileName) {
36933                return $http.get(healthConnectServiceBase + 'courses/' + courseId + '/files?fileName=' + fileName, { responseType: 'blob' });
36934            };
36935
36936            memberDataService.HealthEdCourseDetail = function (languageCode, courseId) {
36937                return $http.get(healthConnectServiceBase + 'courses/' + courseId + '?languageCode=' + languageCode);
36938            };
36939
36940            memberDataService.HealthEdFacilityDetail = function (facilityId) {
36941                return $http.get(healthConnectServiceBase + 'facilities/' + facilityId);
36942            };
36943
36944            memberDataService.HealthEdDropCourse = function (registrationId, params) {
36945                return $http.patch(healthConnectServiceBase + 'courses/registrations/drop/' + registrationId, params);
36946            };
36947
36948            memberDataService.GetTempCardImage = function () {
36949                return $http.get(serviceBase + 'IdCard/Download');
36950            };
36951
36952            //------------------------------------------------------------------------------//
36953            //------------------------------------------------------------------------------//
36954
36955            /*************************************
36956             ************* Demographics ****************
36957             *************************************/
36958            //for contact info
36959            memberDataService.GetLanguageOptions = function() {
36960                return $http.get(serviceBase + 'Member/Demographics/LanguageOptions');
36961            };
36962
36963            memberDataService.GetNcqaAnswers = function(param) {
36964                return $http.get(languageServiceBase +
36965                    'get-ncqa-answers?memberNumber=' +
36966                    param.memberId,
36967                    { headers: { 'X-Language': param.currentLang } });
36968
36969            };
36970
36971            memberDataService.GetNcQaQuestions = function(param) {
36972                return $http.get(languageServiceBase +
36973                    'get-ncqa-questions?applicationId=5',
36974                    { headers: { 'X-Language': param } });
36975            };
36976
36977            memberDataService.SaveNcqaAnswers = function(params) {
36978                return $http.post(serviceBase + 'Questions/SaveMemberAnswers', params);
36979            }
36980
36981            memberDataService.GetDemographics = function () {
36982                return $http.get(serviceBase + 'Member/Demographics/Get');
36983            };
36984
36985            memberDataService.UpdateDemographics = function (params) {
36986                return $http.post(serviceBase + 'Member/Demographics/Update', params);
36987            };
36988
36989            memberDataService.GetCommPref = function (memberNo, language) {
36990                return $http.get(gatewayBase + 'contact-Preference/' + memberNo,
36991                    { headers: { 'X-Language': language } });
36992            }
36993
36994            memberDataService.SaveCommPref = function (params) {
36995                return $http.post(gatewayBase + 'contact-Preference/preferences', params);
36996            }
36997
36998            memberDataService.SaveMobilePhoneNumber = function (phoneInfo) {
36999                return $http.post(gatewayBase + 'contact-Preference/mobile-Number', phoneInfo);
37000            }
37001
37002            memberDataService.SaveCommPrefEmail = function (email) {
37003                return $http.post(gatewayBase + 'contact-Preference/email', email);
37004            }
37005
37006            memberDataService.UpdateCommPreferences = function (params) {
37007                return $http.post(serviceBase + 'Member/Demographics/Update', params);
37008            }
37009
37010            //verified active (09/15/2021)
37011            memberDataService.ValidateMemberAddress = function(params) {
37012
37013                console.log('user-entered address', params);
37014
37015                return $http.post(geoAddressBase + 'Verify', params);
37016            };
37017
37018            /*************************************
37019************* Caregiver Access/Health Delegates ****************
37020*************************************/
37021
37022            function getHealthDelegateApiVersion(params) {
37023                //needed to incld. version for health delegate gateway and language apis.
37024                const currentVersion = 2;
37025
37026                if (params) {
37027                    params['api-version'] = currentVersion;
37028
37029                    return params;
37030                }
37031
37032                return `api-version=${currentVersion}`;
37033            }
37034
37035            memberDataService.searchCaregiverMember = function (params, language) {
37036
37037                const apiVersion = getHealthDelegateApiVersion();
37038
37039                return $http.post(`${languageServiceBase}health-delegate/member-search?${apiVersion}`, params,
37040                    { headers: { 'X-Language': language } });
37041            }
37042
37043            memberDataService.getDelegateFeaturePermission = function (memberId, language) {
37044
37045                const apiVersion = getHealthDelegateApiVersion();
37046
37047                return $http.get(`${languageServiceBase}health-delegate/eligibility/${memberId}?${apiVersion}`,
37048                    { headers: { 'X-Language': language } });
37049            }
37050
37051            memberDataService.getPotentialDelegates = function (memberId) {
37052
37053                const apiVersion = getHealthDelegateApiVersion();
37054
37055                return $http.get(`${gatewayBase}health-delegate/availablemembers/${memberId}?${apiVersion}`);
37056            }
37057
37058            memberDataService.CreateNewDelegate = function (params, language) {
37059
37060                const apiVersion = getHealthDelegateApiVersion();
37061
37062                return $http.post(`${languageServiceBase}health-delegate?${apiVersion}`, params,
37063                    { headers: { 'X-Language': language } });
37064            }
37065
37066            memberDataService.UpdateDelegateStatus = function (params, language) {
37067
37068                const apiVersion = getHealthDelegateApiVersion();
37069
37070                return $http.post(`${languageServiceBase}health-delegate/updateStatus?${apiVersion}`, params,
37071                    { headers: { 'X-Language': language } });
37072            }
37073
37074            memberDataService.getPendingDelegates = function (memberId, language) {
37075
37076                const apiVersion = getHealthDelegateApiVersion();
37077
37078                return $http.get(`${languageServiceBase}health-delegate?memberId=${memberId}&filter=Pending&${apiVersion}`,
37079                    { headers: { 'X-Language': language } });
37080            }
37081
37082            memberDataService.getProcessedDelegates = function (memberId, language) {
37083
37084                const apiVersion = getHealthDelegateApiVersion();
37085
37086                return $http.get(`${languageServiceBase}health-delegate?memberId=${memberId}&filter=Processed&${apiVersion}`,
37087                    { headers: { 'X-Language': language } });
37088            }
37089
37090            memberDataService.getHistoryDelegates = function (memberId, language) {
37091
37092                const apiVersion = getHealthDelegateApiVersion();
37093
37094                return $http.get(`${languageServiceBase}health-delegate?memberId=${memberId}&filter=History&${apiVersion}`,
37095                    { headers: { 'X-Language': language } });
37096            }
37097            
37098            const delegateDefinitions = {
37099                "en-us": [
37100                    {
37101                        "term": "What is a Caregiver?",
37102                        "definition":
37103                            "A Caregiver is an IEHP member over 18 whom you give access to view your IEHP member account. You can only appoint one Caregiver. *In the future, you may be able to provide access to someone outside of IEHP.",
37104
37105                    },
37106                    {
37107                        "term": "What can my Caregiver Access?",
37108                        "definition":
37109                            "<p>Your Caregiver can see what you can see, including, but not limited to:</p><ul><li>Lab results</li><li>Claims</li><li>Referrals</li></ul><p>If you are a Caregiver for someone else, your Caregiver will not be able to see their information.</p>",
37110
37111                    },
37112                    {
37113                        "term": "Can I make changes to my Caregiver Access?",
37114                        "definition":
37115                            "You can assign a Caregiver and revoke access at any time. Your Caregiver can also cancel access at any time while appointed.",
37116
37117                    },
37118                    {
37119                        "term": "How can my Caregiver Access my information in the secure IEHP Member Portal?",
37120                        "definition":
37121                            "Your Caregiver can choose your name from the dropdown menu at the top right corner of the portal to view your information.",
37122
37123                    },
37124                    {
37125                        "term": "Who cannot have a Caregiver?",
37126                        "definition":
37127                            "<ul><li>Members under 18</li><li>Foster children under 21</li><li>Any IEHP member who has given a separate address to get mail for sensitive services</li></ul>",
37128
37129                    },
37130                    {
37131                        "term": "Is having Caregiver access the same as being able to act on behalf of a family member or to request their PHI?",
37132                        "definition": "<p>No. Caregiver access allows you to view the Member Portal as the member sees it and complete certain portal tasks (for example, requesting an ID card or changing a PCP). It does not authorize you to act for the member in appeals, grievances, coverage decisions, or other benefit matters, and it does not permit you to request or receive the member’s protected health information (PHI) beyond what is displayed in the portal.</p><p>
37132To act on the member’s behalf for benefit matters, certain documents such as an Authorization of Representative (AOR), an Advanced Health Care Directive/Power of Attorney for Healthcare or other court order must be on file. To request or receive PHI outside the portal, a valid HIPAA authorization (PHI authorization) designating you must be on file.</p>"
37133                    }
37134                ],
37135                "es-mx": [
37136                    {
37137                        "term": "¿Qué es un del cuidador?",
37138                        "definition":
37139                            "Un cuidador es un miembro de IEHP mayor de 18 años al que usted le permite consultar su cuenta de miembro de IEHP. Solamente puede designar a un cuidador. *En el futuro, es posible que pueda proporcionarle acceso a alguien que no pertenece a IEHP.",
37140
37141                    },
37142                    {
37143                        "term": "¿A qué puede tener acceso mi cuidador?",
37144                        "definition":
37145                            "<p>Su cuidador puede consultar, entre otras cosas, lo siguiente:</p><ul><li>Resultados de las pruebas de laboratorio</li><li>Quejas</li><li>Referencias</li></ul><p>Si usted es el cuidador de alguien más, su cuidador no podrá consultar la información de esa persona.</p>",
37146
37147                    },
37148                    {
37149                        "term": "¿Puedo hacer cambios en el acceso de mi cuidador?",
37150                        "definition":
37151                            "Puede asignar a un cuidador y revocar su acceso en cualquier momento. Su cuidador también puede cancelar su propio acceso en cualquier momento mientras esté asignado como cuida
37151dor.",
37152
37153                    },
37154                    {
37155                        "term": "¿Cómo puede mi cuidador acceder a mi información en el portal seguro para miembros de IEHP?",
37156                        "definition":
37157                            "Su cuidador puede elegir su nombre del menú desplegable que se encuentra en la esquina superior derecha del portal para consultar su información.",
37158
37159                    },
37160                    {
37161                        "term": "¿Qué personas no pueden ser cuidadores?",
37162                        "definition":
37163                            "<ul><li>Los miembros menores de 18 años</li><li>Los niños en cuidado de crianza temporal menores de 21 años</li><li>Cualquier miembro de IEHP que haya proporcionado un domicilio distinto al suyo para recibir correo relacionado con servicios confidenciales</li></ul>",
37164
37165                    },
37166                    {
37167                        "term": "¿Es lo mismo el acceso de un Cuidador, que actuar en representación de un familiar o solicitar PHI del miembro?",
37168                        "definition": "<p>No. El acceso del Cuidador le permite navegar en el Portal del Miembros, de la misma manera que lo hace el miembro y hacer ciertas actividades (por ejemplo, solicitar una tarjeta de identificación o cambiar de médico de cuidado primario). No le autoriza a actuar en representación del miembro en apelaciones, quejas, decisiones de cobertura o de otros asuntos relacionados con los beneficios, ni a solicitar ni recibir su información médica protegida (protected health information, PHI) que no se muestra en el portal.</p><p>Para actuar en representación del miembro en asuntos relacionados con sus beneficios, se deben tener en los récords ciertos documentos, como una Autorización de Representante (AOR), una Directiva Anticipada de Atención Médica/Poder Notarial para Atención Médica o de otra orden judicial. Para solicitar o recibir PHI fuera del portal, se debe tener en los récords una autorización válida de HIPAA (autorización PHI) que demuestre que usted está designado.</p>"
37169                    }
37170                ],
37171                "zh-tw": [
37172                    {
37173                        "term": "什麼是照顾者?",
37174                        "definition":
37175                            "照顧者是指超過18歲且您授予其訪問權限查看您IEHP會員帳戶的IEHP會員。您僅可指定一位照顧者。*將來,您可能可以向IEHP以外的人士提供訪問權限。",
37176
37177                    },
37178                    {
37179                        "term": "我的照顧者可以查看哪些信息?",
37180                        "definition":
37181                            "<p>您的照顧者可以查看您能夠查看的信息,其中包括但不限於:</p><ul><li>化驗結果</li><li>理賠</li><li>轉介</li></ul><p>如果您是某人士的照顧者,您的照顧者將無法查看該人士的信息。</p>",
37182
37183                    },
37184                    {
37185                        "term": "我可以更改我的照顧者訪問權限嗎?",
37186                        "definition":
37187                            "您可以指定一名照顧者,並隨時撤銷其訪問權限。您的照顧者也可以在接受指定的期間隨時取消其訪問權限",
37188
37189                    },
37190                    {
37191                        "term": "我的照顧者該如何在安全的IEHP會員入口網站查看我的信息?",
37192                        "definition":
37193                            "您的照顧者可以從入口網站右上角的下拉菜單中選擇您的姓名,以查看您的信息。",
37194
37195                    },
37196                    {
37197                        "term": "哪些人不能指定照顧者?",
37198                        "definition":
37199                            "<ul><li>未滿18歲的會員</li><li>未滿21歲的寄養兒童</li><li>已提供另外的地址接收敏感服務相關郵件的任何IEHP會員</li></ul>",
37200
37201                    },
37202                    {
37203                        "term": "擁有照顧者權限是否等同於可以代表家人行事或索取其受保護健康信息 (Protected Health Information, PHI)?",
37204                        "definition": "<p>否。照顧者訪問權限可讓您查看會員入口網站中會員可以查看的信息,並完成某些入口網站的操作(例如:索取ID卡或更換基本保健服務提供者 [Primary Care Provider, PCP])。照顧者權限並未授權您代表會員處理上訴、申訴、承保決定或其他福利事宜,ä¹
37204Ÿæœªå…è¨±æ‚¨ç´¢å–或獲取入口網站未顯示的會員受保護健康信息 (PHI)。</p><p>若要代表會員處理福利事宜,會員檔案中必須要有某些文件,例如代表授權書 (Authorization of Representative, AOR)、預先醫療保健指示/醫療保健授權書或其他法院命令。若要索取或獲取入口網站以外的PHI,會員檔案中必須要有具有效力的健康保險可攜性及問責法案 (Health Insurance Portability and Accountability Act, HIPAA) 授權書(PHI授權書)指定您作為代表。</p>"
37205                    }
37206                ],
37207                "vi-vn": [
37208                    {
37209                        "term": "Người chăm sóc là gì?",
37210                        "definition":
37211                            "Người chăm sóc (caregiver) là một hội viên IEHP trên 18 tuổi mà quý vị cấp quyền xem tài khoản hội viên IEHP của mình. Quý vị chỉ có thể chỉ định một người chăm sóc. *Trong tương lai, quý vị có thể cung cấp quyền truy cập cho một người nào đó bên ngoài IEHP.",
37212
37213                    },
37214                    {
37215                        "term": "Người chăm sóc của tôi có thể truy cập những gì?",
37216                        "definition":
37217                            "<p>Người chăm sóc cá»§a quý vị có thể nhìn thấy những gì quý vá
37217»‹ có thể nhìn thấy, bao gồm nhưng không giới hạn ở:</p><ul><li>Kết quả xét nghiệm</li><li>Yêu cầu bồi thường</li><li>Giới thiệu (referral)</li></ul><p>Nếu quý vị là người chăm sóc cho người khác, người chăm sóc cá»§a quý vị sẽ không thể xem thông tin cá»§a họ.</p>",
37218
37219                    },
37220                    {
37221                        "term": "Tôi có thể thay đổi quyền truy cập của người chăm sóc không?",
37222                        "definition":
37223                            "Quý vị có thể chỉ định người chăm sóc và thu hồi quyền truy cập bất cứ lúc nào. Người chăm sóc của quý vị cũng có thể hủy quyền truy cập bất cứ lúc nào trong thời gian được chỉ định.",
37224
37225                    },
37226                    {
37227                        "term": "Người chăm sóc của tôi có thể truy cập thông tin của tôi trong Cổng Thông Tin Hội Viên IEHP an toàn bằng cách nào?",
37228                        "definition":
37229                            "Người chăm sóc có thể chọn tên quý vị từ thanh danh sách tùy chọn (menu) thả xuống ở góc trên bên phải của cổng thông tin để xem thông tin của quý vị.",
37230
37231                    },
37232                    {
37233                        "term": "Ai không thể có người chăm sóc?",
37234                        "definition":
37235                            "<ul><li>Hội viên dưới 18 tuổi</li><li>Trẻ em được nhận nuôi (foster) dưới 21 tuổi</li><li>Bất kỳ hội viên IEHP nào đã cung cấp địa chỉ riêng để nhận thư cho các dịch vụ nhạy cảm</li></ul>",
37236
37237                    },
37238                    {
37239                        "term": "Khi quý vị có Quyền truy cập của người chăm sóc thì có đồng nghĩa với việc quý vị có thể hành động thay cho một thành viên gia đình hoặc yêu cầu Thông Tin Y Tế Được Bảo Mật (PHI) của họ không?",
37240                        "definition": "<p>Không. Quyền truy cập của người chăm sóc cho phép quý vị xem Trang mạng Thông tin Hội viên (Member Portal) giống như khi hội viên xem và hoàn thành một số yêu cầu nhất định trên trang mạng thông tin (ví dụ: yêu cầu Thẻ ID Hội viên hoặc thay đổi Bác sĩ chăm sóc chính (PCP)). Việc này không cho phép quý vị hành động thay cho hội viên trong các khiếu nại, kháng nghị, quyết định đài thọ hoặc các vấn đề phúc lợi khác và không cho phép quý vị yêu cầu hoặc nhận thông tin y tế được bảo mật (PHI) của hội viên ngoài những gì được hiển thị trên trang mạng thông tin.</p><p>Để thay mặt hội viên cho các vấn đề phúc lợi, một số tài liệu nhất định như Ủy Quyền Của Người Đại Diện (Authorization of Representative, AOR), Bản Chỉ Dẫn Trước về Chăm Sóc Sức Khỏe (Advance Health Care Directive, AHCD)/ Giấy Ủy Quyền Cho Việc Chăm Sóc Sức Khỏe (Power of Attorney for Healthcare) hoặc lệnh tòa án khác phải được lưu trong hồ sơ. Để yêu cầu hoặc nhận PHI không có trên trang mạng thông tin, giấy ủy quyền HIPAA hợp lệ (ủy quyền PHI) chỉ định quý vị phải có trong hồ sơ.</p>"
37241                    }
37242                ]
37243            };
37244
37245            memberDataService.GetDelegateDef = function(language) {
37246
37247                const deferred = $q.defer();
37248
37249                language = language ? language.toLowerCase() : 'en-us';
37250
37251                deferred.resolve(delegateDefinitions[language]);
37252
37253                return deferred.promise;
37254            }
37255            
37256            memberDataService.GetActiveDelegates = function (memberNumber) {
37257
37258                const apiVersion = getHealthDelegateApiVersion();
37259
37260                return $http.get(`${gatewayBase}health-delegate/active/${memberNumber}?${apiVersion}`);
37261            }
37262              
37263            //-----------------------------------------------------------------------------//
37264            //------------------------------------------------------------------------------//
37265
37266            /*************************************
37267            *********** New ID Card **************
37268            *************************************/
37269
37270            memberDataService.GetCurrentIdCardRequests = function (memberNumber) {
37271                return $http.get(serviceBase + "IdCard/Get?memberNumber=" + memberNumber);
37272
37273            };
37274
37275            memberDataService.SubmitNewIdCardRequest = function () {
37276                return $http.put(serviceBase + "IdCard/Member/Add");
37277            };
37278
37279            memberDataService.CancelIdCardRequest = function (payload) {
37280                return $http.post(serviceBase + "IdCard/Update", payload);
37281            };
37282
37283
37284            /*********************************************
37285            *********** New Virtual Id Pass **************
37286            *********************************************/
37287
37288            //for Google
37289            memberDataService.GetGooglePass = function (memberId, dob) {
37290                return $http.get(gatewayBase + 'passbook/android?memberId=' + memberId +
37291                    '&dob=' + dob.split('T')[0]);
37292            };
37293
37294            //for iOS
37295            memberDataService.GetApplePass = function (memberId, dob) {
37296                return $http.get(gatewayBase + 'passbook/ios?memberId=' + memberId +
37297                    '&dob=' + dob.split('T')[0]);
37298            };
37299
37300            memberDataService.GetMobilePass = function (device, memberId, dob) {
37301                if (device === "Android") {
37302                    return memberDataService.GetGooglePass(memberId, dob);
37303
37304                } else if (device === "iOS") {
37305                    return memberDataService.GetApplePass(memberId, dob);
37306                }
37307                else {
37308                    const deferred = $q.defer();
37309                    deferred.reject(`Could not pass. Invalid device: ${device}`);
37310                    return deferred.promise;
37311                }
37312            };
37313
37314            /******************************************
37315            ****** Explanation of Benefits (EOB) ******
37316            ******************************************/
37317            memberDataService.GetMemberEobDocuments = function (params) {
37318                return $http.get(serviceBase + 'Member/Eob/Search', params);
37319            };
37320
37321
37322            memberDataService.DownloadEob = function (docId) {
37323                const config = {
37324                    responseType: 'arraybuffer'
37325                };
37326                return $http.get(serviceBase + 'Member/Eob/Download/Document?documentId=' + docId, config);
37327            };
37328
37329            /******************************************
37330            *********** Member Invoices **************
37331            ******************************************/
37332            memberDataService.GetMemberInvoices = function (params) {
37333                return $http.post(serviceBase + 'Member/Invoice/Search', params);
37334            };
37335
37336
37337            memberDataService.DownloadInvoice = function (invoiceId) {
37338                const config = {
37339                    responseType: 'arraybuffer'
37340                };
37341                return $http.get(serviceBase + 'Member/Invoice/Download/Document?invoiceId=' + invoiceId, config);
37342            };
37343
37344            /******************************************
37345         *********** Inbox **************
37346        ******************************************/
37347
37348            memberDataService.downloadSubmission = function (url) {
37349                return $http.get(url);
37350            };
37351
37352            memberDataService.GetMessageCategoriesCount = function () {
37353                return $http.get(
37354                    inboxBaseUrl + "message/search/count-per-message-category"
37355                );
37356            };
37357
37358            memberDataService.GetMessages = function (params) {
37359                //return $http.get("/Content/_data/inbox-list.json");
37360                return $http.get(inboxBaseUrl + "message/search" + params);
37361            };
37362
37363            memberDataService.GetMessageDetails = function (memberNumber, id) {
37364                //return $http.get("/Content/_data/inbox-message.json");
37365                return $http.get(
37366                    inboxBaseUrl +
37367                    "message/search?ContextIdentityMemberNumber=" +
37368                    memberNumber +
37369                    "&id=" +
37370                    id
37371                );
37372            };
37373            memberDataService.GetChildMessages = function (messageId) {
37374                return $http.get(inboxBaseUrl + "message/" + messageId);
37375            };
37376
37377            memberDataService.downloadAttachment = function (url) {
37378                return $http.get(url);
37379            };
37380
37381            memberDataService.GetMessageCount = function (memberNumber, messageStatus) {
37382                var queryParams = "";
37383                switch (messageStatus) {
37384                    case "NeedsMoreInfo":
37385                        queryParams = "&MessageState=Needs%20More%20Info";
37386                        break;
37387                    case "Important":
37388                        queryParams = "&ImportantOnly=true";
37389                        break;
37390                    case "Unread":
37391                        queryParams = "&UnreadOnly=true";
37392                        break;
37393                    default:
37394                        break;
37395                }
37396                return $http.get(
37397                    inboxBaseUrl +
37398                    "message/search/count?ContextIdentityMemberNumber=" +
37399                    memberNumber +
37400                    queryParams
37401                );
37402            };
37403
37404            memberDataService.GetMessageCountAggregations = function (memberNumber) {
37405                return $http.get(
37406                    inboxBaseUrl +
37407                    "message/search/count/totals?ContextIdentityMemberNumber=" +
37408                    memberNumber
37409                );
37410            };
37411
37412            memberDataService.appendAttachmentToReply = function (parentMessageId, payload) {
37413                payload.generateRmqEvent = true;
37414                return $http.put(
37415                    inboxBaseUrl + "message/" + parentMessageId + "/AppendMessageLink",
37416                    payload
37417                );
37418            };
37419
37420            memberDataService.sendComments = function (parentMessageId, payload) {
37421                payload.generateRmqEvent = true;
37422                return $http.post(
37423                    inboxBaseUrl + "message/" + parentMessageId + "/Reply",
37424                    payload
37425                );
37426            };
37427
37428            memberDataService.InboxMessageMarkImportant = function (messageId, value = true) {
37429                const payload = {};
37430                return $http.put(
37431                    inboxBaseUrl +
37432                    "message/" +
37433                    messageId +
37434                    "/UpdateImportantFlag?importantFlag=" + value.toString(),
37435                    payload
37436                );
37437            };
37438
37439            memberDataService.InboxMessageUpdateReadStatus = function (messageId, value = true) {
37440                const payload = {};
37441                return $http.put(
37442                    inboxBaseUrl +
37443                    "message/" +
37444                     messageId +
37445                    "/UpdateReadState?readState=" + value.toString(),
37446                    payload
37447                );
37448            };
37449
37450            memberDataService.InboxMessageArchive = function (messageId, value = true) {
37451                const params = {
37452                    action: 'archive',
37453                    state: value
37454                };
37455                const messageIds = [messageId];
37456                return memberDataService.InboxMessageBatchUpdate(messageIds, params);
37457            };
37458
37459            memberDataService.InboxMessageBatchUpdate = function (messageIds, params) {
37460                let payload = {
37461                    ids: messageIds,
37462                };
37463
37464                if (params.action === 'read') {
37465                    payload = {
37466                        ids: messageIds,
37467                        readState: params.state
37468                    }
37469                }
37470
37471                if (params.action === 'archive') {
37472                    payload = {
37473                        ids: messageIds,
37474                        archiveFlag: params.state
37475                    }
37476                }
37477
37478                return $http.post(
37479                    inboxBaseUrl +
37480                    "message/UpdateMultiple",
37481                    payload
37482                );
37483            };
37484
37485            memberDataService.TakeMessageAction = function (
37486                action,
37487                value,
37488                messageId
37489            ) {
37490                if (action === "important") {
37491                    return memberDataService.InboxMessageMarkImportant(messageId, value);
37492                } else if (action === "read") {
37493                    return memberDataService.InboxMessageUpdateReadStatus(messageId, value);
37494                } else if (action === "archive") {
37495                    return memberDataService.InboxMessageArchive(messageId, value);
37496                } else {
37497                    const deferred = $q.defer();
37498                    deferred.reject(`Could not update message. Invalid action type: ${action}`);
37499                    return deferred.promise;
37500                }
37501            };
37502
37503            /*************************************
37504             ************* Utility ****************
37505             *************************************/
37506            memberDataService.utility = {};
37507
37508            //Determine Primary Elig Record
37509            memberDataService.utility.getPrimaryEligibilityRecord = function (eligArray) {
37510                //If there is only one elig record, just use that.
37511                if (eligArray.length < 2) {
37512                    return eligArray[0];
37513                } else {
37514                    //sort by lob (ascending), then loop over and pick first active record which should be cmc before med
37515                    eligArray = eligArray.sort(function (a, b) { return a.Lob.localeCompare(b.Lob); });
37516                    for (let i = 0; i < eligArray.length; i++) {
37517                        if (eligArray[i].EligibilityStatusCode === 'A') {
37518                            return eligArray[i];
37519                        }
37520                    }
37521                }
37522            };
37523
37524            memberDataService.utility.formatAddress = function (currElig) {
37525
37526                currElig.PcpAddress1 = utilitiesService.capFirstLetter(currElig.PcpAddress1);
37527
37528                if (currElig.PcpAddress2) {
37529                    currElig.PcpAddress2 = utilitiesService.capFirstLetter(currElig.PcpAddress2);
37530                }
37531
37532                currElig.PcpCity = utilitiesService.capFirstLetter(currElig.PcpCity);
37533
37534                return currElig;
37535            };
37536
37537            memberDataService.utility.DetermineAuthStatus = function (item) {
37538                /**
37539                 * Auth types
37540                 * 0: Medical
37541                 * 1: Pharmacy
37542                 * 2: Vision
37543                 * 3: Medical
37544                 */
37545
37546                if (item.authType === 2 && status !== "Complete") {
37547                    return $filter("translate")("see") + " PCP";
37548                } else {
37549                    return item.eventStatus;
37550                }
37551            };
37552
37553            //------------------------------------------------------------------------------//
37554            //------------------------------------------------------------------------------//
37555            memberDataService.broadcastResults = function () {
37556                $rootScope.$broadcast('resultsUpdated');
37557            };
37558
37559            return memberDataService;
37560        }]);
37561
37562})();
37563(function () {
37564    const app = angular.module('MemberPortalServices');
37565
37566    app.factory('memberIDCardService', ['$rootScope', 'memberDataService','localStorageService','$q',
37567        function ($rootScope, memberDataService, localStorageService, $q) {
37568
37569            const memberIDCardService = {};
37570       
37571            memberIDCardService.GetMemberTempCardImage = function () {
37572                //console.log('@@ GET MEM TEMP CARD_IMAGE... FETCHING IMAGE ');
37573                $rootScope.showLoader = true;
37574            var deferred = $q.defer();
37575
37576            memberDataService.GetTempCardImage().success(
37577                 
37578                     function (IEHPTemporaryIDCardImageObject) {
37579                    
37580                         //console.log('@TEMPCARDIMAGE > SUCCESSFUL');
37581                         const args = {};
37582
37583                         $rootScope.showLoader = false;
37584
37585                         if (IEHPTemporaryIDCardImageObject.ResponseStatus === 'Success') {
37586                             
37587                             //Make available to rendering variable 
37588                             $rootScope.IEHPTemporaryIDCardFrontSide   = IEHPTemporaryIDCardImageObject.FrontSide;
37589                             $rootScope.IEHPTemporaryIDCardBackSide    = IEHPTemporaryIDCardImageObject.BackSide; 
37590                             $rootScope.ShowMemberIDCard = true;
37591                             $rootScope.IEHPTemporaryIDCardFetchStatus = IEHPTemporaryIDCardImageObject.ResponseStatus;
37592                           
37593                             //ID Card stored in session Storage
37594                             localStorageService.set('IDC_front', IEHPTemporaryIDCardImageObject.FrontSide);
37595                             localStorageService.set('IDC_back', IEHPTemporaryIDCardImageObject.BackSide); 
37596                             localStorageService.set('IDC_fetchStatus', IEHPTemporaryIDCardImageObject.ResponseStatus);
37597
37598                            // $scope.$broadcast('asdasd', args);
37599                             $rootScope.$broadcast('IDCardFetched', args
37599);
37600                             deferred.resolve();
37601                         }
37602                         else {//200 Response, but no image 
37603                             $rootScope.IEHPTemporaryIDCardFetchStatus =  IEHPTemporaryIDCardImageObject.ResponseStatus;
37604                             localStorageService.set('IDC_fetchStatus',  IEHPTemporaryIDCardImageObject.ResponseStatus);
37605                             $rootScope.$broadcast('IDCardFetched', args);
37606                
37607                             // console.log('ERROR > NO IMAGE RETURNED');
37608                            //  $rootScope.$broadcast('IDCardFetchingError',args);
37609                         }
37610                    }).error(function () {
37611                        
37612                        $rootScope.IEHPTemporaryIDCardFetchStatus = 'ERROR';
37613                        $rootScope.showLoader = false;
37614                        $rootScope.showErrorMessage = true;
37615                         console.log('@TEMPCARDIMAGE > ERROR!');
37616                         
37617                         //   $rootScope.$broadcast('IDCardFetchingError',args);
37618                         deferred.reject();
37619                     });
37620                return deferred.promise; 
37621            };
37622
37623            return memberIDCardService;
37624    }]);
37625})();
37626
37627
37628
37629(function () {
37630    'use strict';
37631
37632    angular
37633        .module('MemberPortalServices')
37634        .factory('mockDataService', mockDataService);
37635
37636    mockDataService.$inject = ['$http', '$q'];
37637
37638    function mockDataService($http, $q) {
37639
37640        mockDataService.getTransportationRequestsMock = function () {
37641
37642            return $http.get('Content/_data/transportationRequests.json');
37643        }
37644
37645        mockDataService.checkTransportationRequestEligMock = function () {
37646
37647            return $http.get('Content/_data/eligibility.json');
37648        }
37649
37650        return mockDataService;
37651    }
37652
37653})();
37654(function () {
37655    'use strict';
37656
37657    const app = angular.module('MemberPortalServices');
37658    app.factory('notificationService', ['$filter',
37659        function ($filter) {
37660            const notificationService = {};
37661            const ssbci = 'SSBCI-Opt-In';
37662            var isSsbciOptIn = false;
37663
37664            notificationService.shouldShow = function (alertType) {
37665                console.log("alertType", alertType);
37666                const hasReadMessages = localStorage.getItem('hasRead');
37667
37668                if (!hasReadMessages) return true;
37669
37670                const haveRead = hasReadMessages.includes(alertType);
37671
37672                console.log("have read " + alertType + "? ", haveRead);
37673                return !haveRead;
37674            }
37675
37676            notificationService.getAlertTitle = function (alert) {
37677                var title;
37678                switch (alert.AlertType) {
37679                    case 'AER':
37680                        title = $filter('translate')('page_notifications_aer_title');
37681                        break;
37682                    case 'ADDR':
37683                        title = $filter('translate')('page_notifications_addressVerification');
37684                        break;
37685                    case 'FLU':
37686                        title = $filter('translate')('page_notifications_fluShotDue');
37687                        break;
37688                    case 'HRACN':
37689                        title = $filter('translate')('page_notifications_healthRiskAssessment');
37690                        break;
37691                    case 'DIABT':
37692                        title = $filter('translate')('page_notifications_healthDIABT');
37693                        break;
37694                    case 'DRE':
37695                        title = $filter('translate')('page_notifications_healthDRE');
37696                        break;
37697                    case 'MAMMO':
37698                        title = $filter('translate')('page_notifications_healthMammo');
37699                        break;
37700                    case 'MGRX':
37701                        title = alert.AlertTitle;
37702                        break;
37703                    case 'NOA':
37704                        title = $filter('translate')('page_notifications_noa_title');
37705                        break;
37706                    case 'PAP':
37707                        title = $filter('translate')('page_notifications_healthPAP');
37708                        break;
37709                    case 'Prescription':
37710                        title = $filter('translate')('page_notifications_prescriptions');
37711                        break;
37712                    case 'HRA':
37713                        title = $filter('translate')('page_notifications_hra_title');
37714                        break;
37715                    default:
37716                        title = $filter('translate')('page_notifications_panelTitle');
37717                }
37718                return title;
37719            }
37720
37721            notificationService.getAlertDescription = function (alert) {
37722                var desc;
37723                switch (alert.AlertType) {
37724                    case 'HRA':
37725                        desc = $filter('translate')('page_notifications_hra');
37726                        break;
37727                    default:
37728                        desc = $filter('translate')('page_notifications_panelTitle');
37729                }
37730
37731                return desc;
37732            }
37733
37734            notificationService.sortAlerts = function (alerts) {
37735                alerts.sort(function (a, b) {
37736                    return new Date(b.CreateDate) - new Date(a.CreateDate);
37737                });
37738
37739                // if alerts list contains ssbci-opt-in, 
37740                // remove the alert(s) from alerts list
37741                // there isn't a business need to display this for the member, 
37742                // but we want to know the member has opted in. 
37743                if (alerts.filter(_ => _.AlertType != null &&
37744                    _.AlertType.toLowerCase() == ssbci.toLowerCase()).length > 0)
37745                {
37746                    // member has opted in 
37747                    isSsbciOptIn = true;
37748
37749                    // remove ssbci alerts from alerts list
37750                    alerts = alerts.filter(function (_) {
37751                        return _.AlertType != null
37752                            && _.AlertType.toLowerCase() !== ssbci.toLowerCase()
37753                    });
37754                }
37755
37756                alerts.forEach(m => m.AlertTitle = notificationService.getAlertTitle(m));
37757
37758                var alertsCopy = angular.copy(alerts);
37759
37760                alerts.forEach(function (item, index) {
37761                    if(!!item.AlertDescription) {
37762                        if (item.AlertDescription.indexOf('<div class="refillNotification">') > -1) {
37763    
37764                            let copy = angular.copy(item);
37765                            copy.AlertDescription = $filter('translate')('page_notifications_prescriptions_reminderForDrug') +
37766                                item.AlertDescription +
37767                                $filter('translate')('page_notifications_prescriptions_pleaseRefill');
37768    
37769                            alertsCopy.splice(index, 1);
37770                            console.log("alerts after removing reminder", alertsCopy);
37771    
37772                            alertsCopy.unshift(copy);
37773                            console.log("alerts after adding back reminder", alertsCopy);
37774                        }
37775                    }
37776                });
37777
37778                return alertsCopy;
37779            }
37780
37781            notificationService.isSsbciOptIn = function () {
37782                return isSsbciOptIn;
37783            }
37784
37785            notificationService.convertToAlert = function (item) {
37786                return {
37787                    AlertType: item.key,
37788                    AlertDescription: item.content,
37789                    AlertTitle: item.title
37790                }
37791            }
37792
37793            notificationService.saveReadMessages = function (messages) {
37794
37795                const hasReadMessages = messages.map(m => m.AlertType).join();
37796
37797                localStorage.removeItem('hasRead');
37798                localStorage.setItem('hasRead', hasReadMessages);
37799
37800                console.log("read all msgs", localStorage.getItem('hasRead'));
37801            }
37802
37803            notificationService.saveReadMessage = function (message) {
37804
37805                const rawValue = localStorage.getItem('hasRead');
37806                let hasReadMessages = [];
37807
37808                if (rawValue) {
37809                    hasReadMessages = rawValue.split(',');
37810                }
37811
37812                if (hasReadMessages.includes(message.AlertType)) return;
37813
37814                hasReadMessages.push(message.AlertType);
37815
37816                const convertedString = hasReadMessages.join();
37817
37818                console.log("added one read message", convertedString);
37819
37820                localStorage.setItem('hasRead', convertedString);
37821
37822                return hasReadMessages.length;
37823            }
37824
37825            return notificationService;
37826        }]);
37827
37828})();
37829
37830(function () {
37831    'use strict';
37832
37833    const app = angular.module('MemberPortalServices');
37834
37835    app.factory('passwordStrength', [
37836        function () {
37837            const passwordStrength = {};
37838            var metRequirements = [];
37839
37840            var checkRequirement = function(requirements) {
37841
37842                //if it meets a requirement and the requirement is not recorded
37843                //add that requirement
37844                if (requirements.hasRequired &&
37845                    !metRequirements.includes(requirements.rule)) {
37846
37847                    metRequirements.push(requirements.rule);
37848
37849                } else if (!requirements.hasRequired) {
37850
37851                    const index = metRequirements.indexOf(requirements.rule);
37852
37853                    if (index > -1) {
37854
37855                        metRequirements.splice(index, 1);
37856
37857                    }
37858                }
37859            };
37860
37861            const checkPasswordStrength = function(password, minLength) {
37862
37863                const regex = /[#?!@$%^&*-]/g;
37864                const lowerLetters = /[a-z]+/.test(password);
37865                const upperLetters = /[A-Z]+/.test(password);
37866                const numbers = /[0-9]+/.test(password);
37867                const symbols = regex.test(password);
37868                const minLengthPassed = password.length >= minLength;
37869                
37870                const requirements = [
37871                    {
37872                        "hasRequired": lowerLetters,
37873                        "rule": 'lowercase'
37874                    },
37875                    {
37876                        "hasRequired": upperLetters,
37877                        "rule": 'uppercase'
37878                    },
37879                    {
37880                        "hasRequired": numbers,
37881                        "rule": 'number'
37882                    },
37883                    {
37884                        "hasRequired": symbols,
37885                        "rule": 'symbol'
37886                    },
37887                    {
37888                        "hasRequired": minLengthPassed,
37889                        "rule": 'min-length'
37890                    }
37891                ];
37892
37893                requirements.forEach(checkRequirement);
37894
37895                return metRequirements;
37896            };
37897
37898            passwordStrength.CheckPasswordStrength = checkPasswordStrength;
37899
37900            return passwordStrength;
37901        }]);
37902})();
37903
37904
37905
37906(function () {
37907    'use strict';
37908
37909    const app = angular.module('MemberPortalServices');
37910
37911    app.factory('plUploadInboxService', ['$rootScope',
37912        'authStatusService', '$http', 'memberDataService',
37913        function ($rootScope, authStatusService, $http, memberDataService) {
37914
37915            var serviceBase = $('meta[name="inboxBaseUrl"]').attr('content');
37916
37917            var maxFiles = 8;
37918            var maxFileLength = 100;
37919            var hasFiles = false;
37920            var uploadErrors = [];
37921
37922            var specialChars = '\\/:*?"<>|';
37923
37924            function checkString(string) {
37925
37926                for (let i = 0; i < specialChars.length; i++) {
37927
37928                    if (string.indexOf(specialChars[i]) > -1) {
37929                        return true;
37930                    }
37931                }
37932
37933                return false;
37934            }
37935
37936            function checkFileNameLength(fileName) {
37937
37938                return fileName.length > maxFileLength;
37939            }
37940
37941            function uploadFile(file, totalFilesToProcess) {
37942                //mi.io requires uploading base64 instead of binary
37943                //this will call our API manually instead of letting PLUpload takes care of it
37944
37945                const uploader = $('#plupLoadUploader').pluploadQueue();
37946
37947                return new Promise((resolve, reject) => {
37948                    const reader = new window.FileReader();
37949
37950                    reader.onload = function (e) {
37951
37952                        const base64data = e.target.result.split(',')[1];
37953
37954                        const payload =
37955                        {
37956                            displayName: file.name,
37957                            type: file.type,
37958                            base64EncodedDocument: base64data
37959                        };
37960
37961                        $http({
37962                            url: uploader.settings.url,
37963                            method: 'POST',
37964                            headers: {
37965                                'Content-Type': 'application/json',
37966                                Authorization: 'Bearer ' + authStatusService.getStatus().access_token
37967                            },
37968                            data: payload
37969                        })
37970                            .then(response => {
37971                                console.log('Upload Success:', response.data);
37972                                uploader.trigger('FileUploaded', file, { response: response.data });
37973
37974                                const uploaded = response.data;
37975
37976                                const attachment = {
37977                                    type: uploaded.type,
37978                                    url: uploaded.uri,
37979                                    displayName: uploaded.displayName
37980                                };
37981
37982                                console.log('append attachment to reply payload', payload);
37983                               
37984                                resolve({
37985                                    messageLink: attachment,
37986                                    response: response.data,
37987                                    filesToProcess: totalFilesToProcess - 1
37988                                });
37989                            })
37990                            .catch(error => {
37991                                const failedUpload = {
37992                                    messageLink: null,
37993                                    fileName: file.name,
37994                                    code: -1,
37995                                    errorMsg: error.message || 'Upload Failed',
37996                                    filesToProcess: totalFilesToProcess - 1
37997                                };
37998                                //uploader.trigger('Error', failedUpload);
37999                                reject(failedUpload);
38000                            });
38001                    };
38002
38003                    reader.onerror = function (error) {
38004                        console.error('FileReader error:', error);
38005                        reject(error);
38006                    };
38007                    reader.readAsDataURL(file.getNative());
38008                });
38009            }
38010
38011            return {
38012                initializePlupload: function (func) {
38013
38014                    console.log('init plupload');
38015
38016                    if (hasFiles) return;
38017
38018                    uploadErrors = [];
38019
38020                    const headers = {
38021                        "Content-Type": 'application/json',
38022                        Authorization: `Bearer ${authStatusService.getStatus().access_token}`
38023                    };
38024
38025                    const fileUploadUrl = serviceBase + 'documents';
38026
38027                    $('#plupLoadUploader').pluploadQueue({
38028                        runtimes: 'html5,html4',
38029                        url: fileUploadUrl,
38030                        headers,
38031                        chunk_size: '0',
38032                        max_file_size: '10mb',
38033                        max_retries: 0,
38034                        multiple_queues: true,
38035                        dragdrop: true,
38036                        prevent_duplicates: true,
38037                        views: {
38038                            list: true,
38039                            thumbs: true,
38040                            active: 'thumbs'
38041                        },
38042                        filters: {
38043                            mime_types: [
38044                                { title: 'PDF files', extensions: 'pdf' },
38045                                { title: 'Tiff files', extensions: 'tiff' },
38046                                { title: "Word files", extensions: "doc,docx" },
38047                                { title: 'Jpeg files', extensions: 'jpeg,jpg,png,gif' }
38048                            ]
38049                        },
38050                        multipart: false,
38051                        init: {
38052                            FileUploaded: function (up, file, response) {
38053
38054                                hasFiles = false;
38055                                console.log('reset hasFiles after done uploading', hasFiles);
38056                            },
38057                            FilesAdded: function (up, files) {
38058                                //console.log('plupload', plupload);
38059                                plupload.each(files, function (file) {
38060                                    console.log('looping plupload for max files allowed');
38061                                    if (up.files.length > maxFiles) {
38062
38063                                        up.removeFile(file);
38064                                    }
38065
38066                                });
38067
38068                                plupload.each(files, function (file) {
38069                                    console.log('looping plupload for invalid file name');
38070                                    if (checkString(file.name)) {
38071                                        alert('The file name ' +
38072                                            file.name +
38073                                            ' contains one of the following invalid characters and will not be submitted untill renamed: \\ / : * ? " < > |');
38074                                        up.removeFile(file);
38075                                    }
38076                                });
38077
38078                                plupload.each(files, function (file) {
38079                                    console.log('looping plupload for max file name length');
38080                                    if (checkFileNameLength(file.name)) {
38081                                        const diff = file.name.length - maxFileLength;
38082                                        alert(
38083                                            'File name: \n\n' + file.name + ' \n\nis ' + diff + ' characters longer than the maximum allowed ' + maxFileLength + ' characters.'
38084                                            + ' Please rename the file with a shorter name in order to continue the upload.'
38085                                        );
38086                                        up.removeFile(file);
38087                                    }
38088
38089                                });
38090
38091                                if (up.files.length >= maxFiles) {
38092                                    $('#plupLoadUploader_browse').hide('slow');
38093                                }
38094                            },
38095                            FilesRemoved: function (up, files) {
38096                                if (up.files.length < maxFiles) {
38097                                    $('#plupLoadUploader_browse').fadeIn('slow');
38098                                }
38099
38100                                if (up.files.length === 0) {
38101                                    hasFiles = false;
38102                                }
38103                            }
38104                        }
38105                    });
38106
38107                    const uploader = $('#plupLoadUploader').pluploadQueue();
38108
38109                    if (!uploader) return;
38110
38111                    func();
38112                },
38113                getHasFiles: function () {
38114                    const uploader = $('#plupLoadUploader').pluploadQueue();
38115                    return uploader.files.length;
38116                },
38117                getUploadError: function () {
38118                    return uploadErrors.length > 0 ? uploadErrors : null;
38119                },
38120                submitDocuments: function () {
38121                  
38122                    const uploader = $('#plupLoadUploader').pluploadQueue();
38123
38124
38125                    uploader.start();
38126                },
38127                getPluploadInstance: function () {
38128                    //return plupload instance in order to bind events to it
38129
38130                    const uploader = $('#plupLoadUploader').pluploadQueue();
38131                    return uploader;
38132                },
38133                bindFilterObject: function () {
38134
38135                    const uploader = $('#plupLoadUploader').pluploadQueue();
38136
38137                    let totalFilesToProcess = uploader.files.length;
38138
38139                    uploader.bind('BeforeUpload', function () {
38140                        return false; //prevent default upload
38141                    });
38142
38143                    uploader.start = function () {
38144                        processFilesSequentially()
38145                            .then(messageLinks => {
38146                                console.log('All files uploaded successfully:', messageLinks);
38147                                uploader.trigger('UploadComplete', messageLinks);
38148                            });
38149                    };
38150                    
38151                    // Function to process files sequentially
38152                    async function processFilesSequentially() {
38153
38154                        const files = uploader.files;
38155                        const results = [];
38156                        const errors = [];
38157                        const messageLinks = [];
38158
38159                        console.log('upload files', files.length);
38160                        let cnt = 0;
38161                        for (const file of files) {
38162                            cnt = cnt + 1;
38163                            console.log('file no.', cnt);
38164
38165                            try {
38166                                const result = await uploadFile(file, totalFilesToProcess);
38167                                console.log('file upload result', result);
38168                                totalFilesToProcess = result.filesToProcess;
38169                                console.log('after an upload, remaining files to upload', result.filesToProcess);
38170                                results.push(result);
38171                                messageLinks.push(result.messageLink);
38172                            } catch (error) {
38173                                console.log('error', error);
38174                                totalFilesToProcess = error.filesToProcess;
38175                                console.log('after a failed upload, remaining files to upload', error.filesToProcess);
38176                                errors.push({ file, error });
38177                            }
38178                        }
38179
38180                        if (errors.length > 0) {
38181                            console.error('Some files failed to upload:', errors);
38182                            uploadErrors = errors;
38183                            uploader.trigger('UploadComplete', uploader);
38184                        }
38185
38186                        console.log('uploaded: messageLinks', messageLinks);
38187                        return messageLinks;
38188                    }
38189                }
38190            }
38191        }]);
38192})();
38193
38194(function () {
38195    'use strict';
38196
38197    const app = angular.module('MemberPortalServices');
38198    app.factory('requestedLanguageService', ['$rootScope', '$routeParams', '$translate',
38199        function ($rootScope, $routeParams, $translate) {
38200            const requestedLanguageService = {};
38201
38202            init();
38203
38204            function init() {
38205                //========== LANGUAGE
38206                const requestedLanguage = $routeParams.lang;
38207                
38208                //default language, use existing cookies, or proceed w init cookie as normal
38209                //Check if desired language is passed
38210                requestedLanguage ? changeLanguage(requestedLanguage) :
38211                    changeLanguage($.cookie('__APPLICATION_LANGUAGE') || 'en-US');
38212            }
38213
38214            function _setLanguageCookie(key) {
38215                const date = new Date();
38216                const m = 10 * 60 * 24 * 30 * 6;
38217                date.setTime(date.getTime() + (m * 60 * 1000));
38218                $.cookie('__APPLICATION_LANGUAGE', key, {
38219                    expires: date, path: '/'
38220                });
38221            }
38222            
38223            function getCurrentLanguage() {
38224                let storedLanguagePreference = $.cookie('__APPLICATION_LANGUAGE');
38225                //console.log("storedLanguagePreference", storedLanguagePreference);
38226
38227                if (!storedLanguagePreference) {
38228                    storedLanguagePreference = 'en-US';
38229                } else if (storedLanguagePreference.indexOf('-') > -1) {
38230                    const array = storedLanguagePreference.split('-');
38231                    storedLanguagePreference = array[0] + '-' + array[1].toUpperCase();
38232                }
38233                //console.log("storedLanguagePreference", storedLanguagePreference);
38234                return storedLanguagePreference;
38235            };
38236
38237            function getEnglishCode() {
38238                return 'en-US';
38239            }
38240
38241            function getAllLanguages() {
38242                return [
38243                    { desc: 'English', code: 'en-US' },
38244                    { desc: 'Español', code: 'es-MX' },
38245                    { desc: '繁體中文', code: 'zh-TW' },
38246                    { desc: 'Tiếng Việt', code: 'vi-VN' }
38247                ];
38248            };
38249
38250            function getEnglishSpanishLanguages() {
38251                return [
38252                    { desc: 'English', code: 'en-US' },
38253                    { desc: 'Español', code: 'es-MX' },
38254                ];
38255            };
38256
38257            function getLanguageshorthand(language) {
38258
38259                if (!language) { language = 'en' }
38260
38261                switch (language) {
38262                    case 'en-US':
38263                        language = 'en';
38264                        break;
38265                    case 'es-MX':
38266                        language = 'es';
38267                        break;
38268                    case 'zh-TW':
38269                        language = 'zh';
38270                        break;
38271                    case 'vi-VN':
38272                        language = 'vi';
38273                        break;
38274                    default:
38275                        language = 'en';
38276                }
38277
38278                return language;
38279            }
38280
38281            function getPrivacyUrl(language) {
38282
38283                const shorthand = getLanguageshorthand(language);
38284
38285                return 'https://iehp.org/' + shorthand + '/about/privacy-policy';
38286            }
38287
38288            function changeLanguage(key) {
38289
38290                if (!key) { key = 'en-US' };
38291
38292                if (getCurrentLanguage() !== key) {
38293                    $translate.use(key);
38294
38295                    _setLanguageCookie(key);
38296
38297                    $('html').attr('lang', key);
38298
38299                    console.log('set language', key);
38300                    $rootScope.$broadcast('language-change', key);
38301                }
38302            };
38303
38304            requestedLanguageService.getCurrentLanguage = getCurrentLanguage;
38305            requestedLanguageService.getEnglishCode = getEnglishCode;
38306            requestedLanguageService.getAllLanguages = getAllLanguages; 
38307            requestedLanguageService.getEnglishSpanishLanguages = getEnglishSpanishLanguages;
38308            requestedLanguageService.getLanguageshorthand = getLanguageshorthand;
38309            requestedLanguageService.getPrivacyUrl = getPrivacyUrl;
38310            requestedLanguageService.changeLanguage = changeLanguage;
38311
38312            return requestedLanguageService;
38313        }]);
38314
38315})();
38316
38317(function () { 
38318  'use strict'; 
38319
38320  angular
38321    .module('MemberPortalServices')
38322    .factory('toastifyService', toastifyService);
38323
38324  // 1. Explicitly inject $window to prevent minification breaks
38325  toastifyService.$inject = ['$window']; 
38326
38327  // 2. Pass $window as a parameter to the function
38328  function toastifyService($window) { 
38329    var toastifyService = {}; 
38330
38331    toastifyService.showSuccess = function (msg) { 
38332      console.log('show toastify...', msg); 
38333
38334      // This will now successfully look for window.Toastify
38335      if ($window.Toastify) { 
38336        $window.Toastify({ 
38337          text: msg, 
38338          duration: 2000, 
38339          position: "left", 
38340          className: "toast-center", 
38341          style: { 
38342            background: "#093a11", 
38343            color: "#ffffff", 
38344            borderRadius: "5px", 
38345            border: "2px solid #4caf50", 
38346            padding: "12px 20px" 
38347          } 
38348        }).showToast(); 
38349      } else { 
38350        console.error("Toastify CDN files are missing from the HTML script tags."); 
38351      } 
38352    };
38353
38354    
38355 // ERROR TOAST
38356    toastifyService.showError = function (msg) {
38357      console.log('show toastify error...', msg);
38358
38359      if ($window.Toastify) { 
38360        $window.Toastify({ 
38361          text: msg, 
38362          duration: 3000,
38363          position: "left",
38364          className: "toast-center",
38365          style: {
38366            background: "#ffcccc",        // light red
38367            color: "#b00000",             // deep red text
38368            borderRadius: "5px",
38369            border: "1px solid #e60000",  // red border
38370            padding: "12px 20px"
38371          }
38372        }).showToast();
38373      } else {
38374        console.error("Toastify CDN files are missing from the HTML script tags.");
38375      }
38376    };
38377
38378
38379    return toastifyService; 
38380  } 
38381})();
38382(function () {
38383    'use strict';
38384
38385    const app = angular.module('MemberPortalServices');
38386
38387    // I provide a request-transformation method that is used to prepare the outgoing
38388    // request as a FORM post instead of a JSON packet.
38389    app.factory(
38390        'transformRequestAsFormPost',
38391        function () {
38392
38393            // I prepare the request data for the form post.
38394            function transformRequest(data, getHeaders) {
38395
38396                const headers = getHeaders();
38397                delete headers['X-Requested-With'];
38398                headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
38399
38400                return (serializeData(data));
38401
38402            }
38403
38404            // Return the factory value.
38405            return (transformRequest);
38406
38407
38408            // ---
38409            // PRIVATE METHODS.
38410            // ---
38411            // I serialize the given Object into a key-value pair string. This
38412            // method expects an object and will default to the toString() method.
38413            // --
38414            // NOTE: This is an altered version of the jQuery.param() method which
38415            // will serialize a data collection for Form posting.
38416            // --
38417            // https://github.com/jquery/jquery/blob/master/src/serialize.js#L45
38418            function serializeData(data) {
38419
38420                // If this is not an object, defer to native stringification.
38421                if (!angular.isObject(data)) {
38422
38423                    return ((data == null) ? '' : data.toString());
38424
38425                }
38426
38427                const buffer = [];
38428
38429                // Serialize each key in the object.
38430                for (let name in data) {
38431
38432                    if (!data.hasOwnProperty(name)) {
38433
38434                        continue;
38435
38436                    }
38437
38438                    const value = data[name];
38439
38440                    buffer.push(
38441                        encodeURIComponent(name) +
38442                        '=' +
38443                        encodeURIComponent((value == null) ? '' : value)
38444                    );
38445
38446                }
38447
38448                // Serialize the buffer and clean it up for transportation.
38449                const source = buffer
38450                    .join('&')
38451                    .replace(/%20/g, '+');
38452
38453                return (source);
38454            }
38455        }
38456    );
38457
38458})();
38459(function () {
38460    'use strict';
38461    const app = angular.module('pascalprecht.translate');
38462
38463    app.factory('$translateStaticFilesLoader', ['$q', '$http', function($q, $http) {
38464        return function (input) {
38465            if (!input || !angular.isString(input.prefix) || !angular.isString(input.suffix)) {
38466                console.log("Couldn't load static files, no prefix or suffix specified!");
38467                throw new Error("Couldn't load static files, no prefix or suffix specified!");
38468            }
38469
38470            var deferred = $q.defer();
38471            //console.log("input", input);
38472            //console.log("input path", [input.prefix, input.key, input.suffix].join(''));
38473
38474            return $http({ url: [input.prefix, input.key, input.suffix].join(''), method: 'GET', params: '' })
38475                .success(function (result) {
38476                     //console.log("translate successful");
38477                     deferred.resolve(result);
38478                })
38479                .error(function () {
38480                    console.error("translate error");
38481                     deferred.reject(input.key);
38482                }), deferred.promise;
38483        };
38484    }]);
38485})();
38486
38487
38488
38489(function () {
38490    'use strict';
38491
38492    angular
38493        .module('MemberPortalServices')
38494        .factory('transportationService', transportationService);
38495
38496    transportationService.$inject = ['$http', 'utilitiesService'];
38497
38498    function transportationService($http, utilitiesService) {
38499
38500        const transportationService = {};
38501
38502        const dmsGateWayBaseUrl = $('meta[name="dmsGateWayBaseUrl"]').attr('content');
38503
38504        // Helper to format address objects into strings
38505        const formatAddress = (addr) => {
38506                if (!addr || !addr.line1) return '';
38507                const line2 = addr.line2 ? ` ${addr.line2}` : '';
38508                return `${addr.line1}${line2}, ${addr.city}, ${addr.state} ${addr.postal_code}`.trim();
38509        };
38510
38511        function getStatusConfig(status) {
38512
38513                const configs = {
38514               
38515                           'Partially Canceled': {
38516                                statusLabel: 'Partially Canceled',
38517                                cssClass: '',
38518                                icon: 'icon-partially-cancelled.svg'   
38519                            },
38520                            'Canceled': {
38521                                statusLabel: 'Canceled',
38522                                cssClass: 'tag-red',
38523                                icon: 'icon-cancelled.svg'   
38524                            },
38525                            'Scheduled': {
38526                                statusLabel: 'Scheduled',
38527                                cssClass: '',
38528                                icon: 'icon-scheduled.svg'   
38529                            }, 
38530                            'Finished': {
38531                                statusLabel: 'Finished',
38532                                cssClass: 'tag-green',
38533                                icon: 'icon-finished.svg'   
38534                            },
38535                            'Default': {
38536                                statusLabel: status,
38537                                cssClass: 'tag-default',                              
38538                            }
38539                        };
38540                    
38541                    return configs[status]?? configs.Default;
38542
38543        }
38544
38545        transportationService.getSingleTabConfig = function(tabs, tabCode) {
38546
38547            if (!Array.isArray(tabs)) return undefined;
38548        
38549            return tabs.find(m=>m.code === (tabCode || '').toLowerCase());
38550        };
38551
38552        transportationService.getSelectedTabConfig = function(tabs) {
38553            
38554            if (!Array.isArray(tabs)) return undefined;
38555
38556            return tabs.find(m => m.active);
38557        };
38558
38559        transportationService.getTripsByDateRange = function (params) {
38560
38561            const { memberNumber, startDate, endDate, searchType } = params;           
38562
38563            const payload = {
38564                MemberId: memberNumber,
38565                startDate: startDate || null,
38566                endDate: endDate || null
38567            };
38568
38569            const allowedSearchTypes = ['upcoming', 'past'];
38570            
38571            if (!allowedSearchTypes.includes(searchType)) return;
38572
38573            return $http.post(`${dmsGateWayBaseUrl}medidrive-ws/getTrips/${searchType}`, utilitiesService.removeNullProps(payload));
38574
38575        };
38576
38577        transportationService.getTabsConfig = function() {
38578
38579                    const defaultDateRangeInDays = 15;
38580                    const maxTimeWindowInDaysUpcoming = 120; //4 months
38581                    const maxTimeWindowInDaysHistory = 180; //6 months
38582
38583                    const now = moment();
38584                    const todayDate = now.format('MM/DD/YYYY');
38585
38586                    const defaultUpcomingEndDate = now.clone().add(defaultDateRangeInDays, 'days').format('MM/DD/YYYY');
38587                    const defaultHistoryStartDate = now.clone().subtract(defaultDateRangeInDays, 'days').format('MM/DD/YYYY');   
38588                                  
38589                    const maxUpcomingTimeWindow = now.clone().add(maxTimeWindowInDaysUpcoming, 'days').format('MM/DD/YYYY');
38590                    const maxHistoryTimeWindow = now.clone().subtract(1, 'days').format('MM/DD/YYYY');
38591                    const minHistoryTimeWindow = now.clone().subtract(maxTimeWindowInDaysHistory, 'days').format('MM/DD/YYYY');
38592                    const maxAllHistoryTimeWindow = now.clone().add(maxTimeWindowInDaysHistory, 'days').format('MM/DD/YYYY');
38593                    return [
38594                            {
38595                                title: 'Upcoming Rides',
38596                                code: 'upcoming',
38597                                active: true,
38598                                searchResultTitle: 'Your Upcoming Rides',
38599                                minAllowedDate: todayDate,
38600                                maxAllowedDate: maxUpcomingTimeWindow,
38601                                defaultStartDate: todayDate,
38602                                defaultEndDate: defaultUpcomingEndDate
38603                            }, 
38604                            {
38605                                title: 'Ride History',
38606                                code: 'past',
38607                                active: false,
38608                                searchResultTitle: 'Ride History',
38609                                minAllowedDate: minHistoryTimeWindow,
38610                                maxAllowedDate: maxAllHistoryTimeWindow,
38611                                defaultStartDate: defaultHistoryStartDate,
38612                                defaultEndDate: maxHistoryTimeWindow
38613                            },
38614                            {
38615                                title: 'Request a Ride',
38616                                code: 'new',
38617                                active: false
38618                            }
38619                        ];                        
38620        };                   
38621
38622        transportationService.isTransportationEligible = function (memberId) {
38623
38624            const params = {
38625                memberNumber: memberId,
38626                workFlow: 12,
38627                debug: 1
38628            };
38629
38630             return $http.get(dmsGateWayBaseUrl + 'transport-pilot-api/transportation-pilot-check/validate', {params: params});
38631            
38632        };
38633
38634        transportationService.getTransportationEntryFormUrl =  function (params) {
38635
38636                return $http.post(dmsGateWayBaseUrl + 'medidrive-ws/trigger/intake', params);
38637        };
38638
38639        transportationService.cancelTransportationAppt = function(params) {
38640
38641            const {cancelingTripIds} = params;
38642
38643            if (!Array.isArray(cancelingTripIds)) {
38644                console.error('expect trip IDs to be an array');
38645                return;
38646            }
38647
38648            const payload = {
38649                    tripsToCancel: cancelingTripIds
38650            };
38651
38652            return $http.post(`${dmsGateWayBaseUrl}medidrive-ws/cancel`, payload);
38653            
38654        };
38655
38656        transportationService.mapRequestsApiResponseToUi = function (apiResponse) {
38657
38658            if (!apiResponse || !Array.isArray(apiResponse)) {
38659                return [];
38660            }
38661
38662            return apiResponse.map(item => {
38663
38664                const {
38665                    trip_id,
38666                    level_of_service,
38667                    facilityPhone,
38668                    tripType,
38669                    treatment,
38670                    request_by,
38671                    addl_seats,
38672                    requestedDate,
38673                    appointment_time,
38674                    pickupTime,
38675                    linked_trip_ids,
38676                    status,
38677                    pickupAddress,
38678                    dropoffAddress,
38679                    trips
38680                } = item;
38681
38682                const apptTime = appointment_time ? moment(appointment_time) : null;
38683
38684                // ----------------------------
38685                // Handle parent pickupTime
38686                // ----------------------------
38687                const isWillCallParent = typeof pickupTime === "string" &&
38688                                        pickupTime.toLowerCase() === "will call";
38689
38690                const parentPickupTimeValue = isWillCallParent
38691                    ? ""
38692                    : (pickupTime || "");
38693
38694                return {
38695                    // Basic ride info
38696                    tripId: trip_id,
38697                    levelOfService: level_of_service,
38698                    treatment: treatment,
38699                    requestedBy: request_by,
38700                    additionalSeats: addl_seats,
38701                    doctorPhone: facilityPhone,
38702                    tripType: tripType,
38703
38704                    // Dates
38705                    requestedDateTime: requestedDate
38706                        ? moment(requestedDate, ["YYYY-MM-DD"]).format("MM/DD/YYYY")
38707                        : "",
38708
38709                    appointmentDateTime: apptTime
38710                        ? apptTime.format("MM/DD/YYYY hh:mm A")
38711                        : "",
38712
38713                    // PickupTime handling
38714                    pickupTime: moment(parentPickupTimeValue, 'YYYY-MM-DD HH:mm:ss').format("MM/DD/YYYY h:mm A"),
38715                    willCall: isWillCallParent,
38716
38717                    relatedTripIds: linked_trip_ids || [],
38718                    status: status,
38719                    statusConfig: getStatusConfig(status),
38720
38721                    // Addresses (flattened)
38722                    pickupAddress: formatAddress(pickupAddress),
38723                    dropoffAddress: formatAddress(dropoffAddress),
38724
38725                    headerOpen: false,
38726
38727                    // ----------------------------
38728                    // Legs
38729                    // ----------------------------
38730                    legs: (trips || []).map((tripLeg, index) => {
38731
38732                        const legApptTime = tripLeg.appointment_time
38733                            ? moment(tripLeg.appointment_time)
38734                            : null;
38735
38736                        // leg-level will-call detection
38737                        const isWillCallLeg = typeof tripLeg.pickupTime === "string" &&
38738                                            tripLeg.pickupTime.toLowerCase() === "will call";
38739
38740                        const legPickupTimeValue = isWillCallLeg
38741                            ? ""
38742                            : (moment(tripLeg.pickupTime, 'YYYY-MM-DD HH:mm:ss').format("MM/DD/YYYY h:mm A") || '');
38743
38744                        return {
38745                            legId: tripLeg.tripId,
38746                            legOrder: index + 1,
38747                            status: tripLeg.status,
38748                            statusConfig: getStatusConfig(tripLeg.status),
38749                            appointmentDateTime: legApptTime
38750                                ? legApptTime.format("MM/DD/YYYY hh:mm A")
38751                                : "",
38752
38753                            pickupAddress: formatAddress(tripLeg.pickupAddress),
38754                            dropoffAddress: formatAddress(tripLeg.dropoffAddress),
38755
38756                            pickupTime: legPickupTimeValue,
38757                            willCall: isWillCallLeg,
38758
38759                            provider: {
38760                                name: tripLeg.transportation_provider?.name || "",
38761                                phone: tripLeg.transportation_provider?.phone || ""
38762                            }
38763                        };
38764                    })
38765                };
38766            });
38767        };
38768
38769        return transportationService;
38770    }
38771
38772})();
38773(function () {
38774    'use strict';
38775
38776    angular
38777        .module('MemberPortalServices')
38778        .factory('updateInformationService', updateInformationService);
38779
38780    updateInformationService.$inject = ['utilitiesService'];
38781
38782    function updateInformationService(utilitiesService) {
38783        
38784        updateInformationService.mapResponseToVm = function(data) {
38785
38786            const copy = JSON.parse(JSON.stringify(data));
38787
38788            copy.phonePrimaryPart1 = utilitiesService.splitPhoneNumber(data.Phone1, 1);
38789            copy.phonePrimaryPart2 = utilitiesService.splitPhoneNumber(data.Phone1, 2);
38790            copy.phonePrimaryPart3 = utilitiesService.splitPhoneNumber(data.Phone1, 3);
38791
38792            copy.phoneSecondaryPart1 = utilitiesService.splitPhoneNumber(data.Phone2, 1);
38793            copy.phoneSecondaryPart2 = utilitiesService.splitPhoneNumber(data.Phone2, 2);
38794            copy.phoneSecondaryPart3 = utilitiesService.splitPhoneNumber(data.Phone2, 3);
38795
38796            copy.phoneMobilePart1 = utilitiesService.splitPhoneNumber(data.Phone3, 1);
38797            copy.phoneMobilePart2 = utilitiesService.splitPhoneNumber(data.Phone3, 2);
38798            copy.phoneMobilePart3 = utilitiesService.splitPhoneNumber(data.Phone3, 3);
38799
38800            //Data comes in as all uppercase, change to lower, use CSS to Capitalize First Character
38801            copy.firstName = data.FirstName.toLowerCase();
38802            copy.lastName = data.LastName.toLowerCase();
38803
38804            return copy;
38805        }
38806
38807        updateInformationService.getConfiguration = function() {
38808            const config = {
38809                textBoxMinLength: 1,
38810                textBoxMaxLength: 20,
38811                dropdownThreshold: 10
38812            };
38813
38814            return config;
38815        }
38816
38817        return updateInformationService;
38818    }
38819
38820})();
38821(function () {
38822    'use strict';
38823    const app = angular.module('MemberPortalServices');
38824
38825    app.factory('utilitiesService', 
38826        function () {
38827            const utilitiesService = {};
38828
38829            /**
38830 * Detects if the current device is a mobile device
38831 * @return {boolean} True if the device is mobile, false otherwise
38832 */
38833            utilitiesService.isMobile = function () {
38834                // Get the user agent string
38835                const userAgent = navigator.userAgent || window.opera;
38836
38837                // Check if the device is mobile by testing against various pattern groups
38838                return isAndroid(userAgent) ||
38839                    isApple(userAgent) ||
38840                    isOtherMobile(userAgent);
38841            };
38842
38843            /**
38844             * Checks if the user agent indicates an Android mobile device
38845             * @param {string} userAgent - The user agent string
38846             * @return {boolean} True if the device is an Android mobile
38847             */
38848            function isAndroid(userAgent) {
38849                return /android[^m]{0,50}mobile/i.test(userAgent);
38850            }
38851
38852            /**
38853             * Checks if the user agent indicates an Apple mobile device
38854             * @param {string} userAgent - The user agent string
38855             * @return {boolean} True if the device is an Apple mobile
38856             */
38857            function isApple(userAgent) {
38858                return /ip(hone|od)/i.test(userAgent);
38859            }
38860
38861            /**
38862             * Checks if the user agent indicates another type of mobile device
38863             * @param {string} userAgent - The user agent string
38864             * @return {boolean} True if the device is another type of mobile
38865             */
38866            function isOtherMobile(userAgent) {
38867                // BlackBerry
38868                if (/bb\d+[^m]{0,30}mobile/i.test(userAgent)) {
38869                    return true;
38870                }
38871
38872                // MeeGo
38873                if (/meego[^m]{0,30}mobile/i.test(userAgent)) {
38874                    return true;
38875                }
38876
38877                // Series devices
38878                if (/series(46)0/i.test(userAgent)) {
38879                    return true;
38880                }
38881
38882                // Common mobile browsers
38883                if
38884                (/avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|mobile.+firefox|netfront|opera m(ob|in)i|p(ixi|re)\/|plucker|pocket|psp|up\.(browser|link)|vodafone|wap/i
38885                    .test(userAgent)) {
38886                    return true;
38887                }
38888
38889                // General mobile terms
38890                if (/mobile|phone|iris|kindle|lge |midp|mmp|maemo|treo|palm( os)?|symbian|windows ce|xda|xiino/i.test(userAgent)) {
38891                    return true;
38892                }
38893
38894                return false;
38895            }
38896
38897            /**
38898             * Detects the mobile operating system of the current device
38899             * @return {string} The name of the mobile OS ("Windows Phone", "Android", "iOS" or "unknown")
38900             */
38901            utilitiesService.getMobileOperatingSystem = function () {
38902                const userAgent = navigator.userAgent || window.opera;
38903
38904                // Define OS patterns with their corresponding return values
38905                const osPatterns = [
38906                    { pattern: /windows phone/i, name: "Windows Phone" },
38907                    { pattern: /android/i, name: "Android" },
38908                    // iOS detection needs special handling for MSStream
38909                    { pattern: /iPad|iPhone|iPod/i, name: "iOS", extraCheck: () => !window.MSStream }
38910                ];
38911
38912                // Test each pattern in order (order matters for some UAs)
38913                for (const os of osPatterns) {
38914                    if (os.pattern.test(userAgent) && (!os.extraCheck || os.extraCheck())) {
38915                        return os.name;
38916                    }
38917                }
38918
38919                return "unknown";
38920            };
38921
38922            utilitiesService.removeNullProps = function (obj) {
38923                
38924                return Object.fromEntries(
38925                    Object.entries(obj).filter(([_, value]) => value != null)
38926                );
38927            }
38928
38929            utilitiesService.getTimeStamp = function() {
38930                const now = new Date();
38931
38932                const MM = String(now.getMonth() + 1).padStart(2, '0');
38933                const DD = String(now.getDate()).padStart(2, '0');
38934                const YYYY = now.getFullYear();
38935
38936                const HH = String(now.getHours()).padStart(2, '0');
38937                const MIN = String(now.getMinutes()).padStart(2, '0');
38938                const SS = String(now.getSeconds()).padStart(2, '0');
38939
38940                return `${MM}${DD}${YYYY}${HH}${MIN}${SS}`;
38941            }
38942
38943            utilitiesService.capFirstLetter = function (string) {
38944                if (typeof string !== 'string' || string.length === 0) return '';
38945
38946                const words = string.split(' ');
38947                for (let cnt = 0; cnt < words.length; cnt++) {
38948
38949                    words[cnt] = words[cnt][0].toUpperCase() + words[cnt].substr(1).toLowerCase();
38950
38951                }
38952                return words.join(' ');
38953            };
38954
38955            utilitiesService.formatMemberId = function(unformatted) {
38956                if (!unformatted || unformatted.length !== 12) return unformatted;
38957
38958                return unformatted + '00';
38959            }
38960
38961            utilitiesService.determineAge = function(dobString) {
38962
38963                const dobToDate = function (dob) {
38964                    var dobParts = [],
38965                        dobDate = {};
38966
38967                    //Dob is expected in d/M/yyyy format
38968                    if (dob.indexOf('T') > 0) {
38969                        dobDate = new Date(dob.concat('-08:00'));
38970                    } else {
38971                        dobParts = dob.split('/');
38972                        dobDate = new Date(dobParts[2], dobParts[1], dobParts[0]);
38973                    }
38974                    return dobDate;
38975                };
38976
38977                const calculateAge = function (birthday) { // birthday is a date
38978                    const ageDifMs = Date.now() - birthday.getTime();
38979                    const ageDate = new Date(ageDifMs); // milliseconds from epoch
38980                    return Math.abs(ageDate.getUTCFullYear() - 1970);
38981                };
38982
38983                return calculateAge(dobToDate(dobString));
38984
38985            };
38986
38987            utilitiesService.splitPhoneNumber = function (value, position) {
38988                if (value && value.length === 10) {
38989                    const _val = value;
38990                    switch (position) {
38991                    case 1:
38992                        return _val.substring(0, 3);
38993                    case 2:
38994                        return _val.substring(3, 6);
38995                    case 3:
38996                        return _val.substring(6, 11);
38997                    default:
38998                        return '';
38999                    }
39000                }
39001                return '';
39002            }; 
39003
39004            // Function to check if the current date is within the config window
39005            utilitiesService.isWithinDateTimeWindow = function (config) {
39006                if (!config) {
39007                    return false; // No config provided
39008                }
39009
39010                // Define the date format
39011                const dateFormat = "M/D/YYYY h:mm a";
39012
39013                // Extract and parse start and end dates from the maintenance config
39014                const startDate = moment(config.from, dateFormat, true);
39015                const endDate = moment(config.to, dateFormat, true);
39016
39017                // Check if the dates are valid
39018                if (!startDate.isValid() && !endDate.isValid()) {
39019                    return false; // Both dates are invalid
39020                }
39021
39022                // Get the current date
39023                const now = moment();
39024
39025                // Determine if the current date is within the maintenance window
39026                if (startDate.isValid() && endDate.isValid()) {
39027                    return now.isBetween(startDate, endDate, null, '[]');
39028                } else if (startDate.isValid()) {
39029                    return now.isSameOrAfter(startDate);
39030                } else if (endDate.isValid()) {
39031                    return now.isSameOrBefore(endDate);
39032                } else {
39033                    return false;
39034                }
39035            }
39036
39037            utilitiesService.containsHtml = function (text) {
39038                return /<\/?[a-z][\s\S]*>/i.test(text);
39039            }
39040
39041            return utilitiesService;
39042        });
39043})();
39044

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.