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 = "✖", 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:""" 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"> </div>' + 14042 '<div class="plupload_file_status"><span>' + _('Status') + '</span></div>' + 14043 '<div class="plupload_file_size">' + _('Size') + '</div>' + 14044 '<div class="plupload_clearer"> </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"> </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"> </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()">×</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. "	" â tab, "&" â "&") 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(/&/g, '&') 34427 .replace(/</g, '<') 34428 .replace(/>/g, '>') 34429 .replace(/ /g, ' '); 34430 } 34431 34432 /** 34433 * Normalize metadata values for case-insensitive matching against AEM DAM asset tags. 34434 * Decodes HTML entities (e.g. "	"), 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.