From eb9bbb00a573730a3b32f5939ce4bcdfca16bd75 Mon Sep 17 00:00:00 2001 From: Zachary Klosko Date: Wed, 30 Dec 2020 22:16:07 -0500 Subject: [PATCH 01/17] Removing TableTools --- .../js/airtime/playouthistory/historytable.js | 61 +- .../TableTools-2.1.5/as3/ZeroClipboard.as | 221 -- .../TableTools-2.1.5/as3/ZeroClipboardPdf.as | 310 --- .../TableTools-2.1.5/as3/lib/AlivePDF.swc | Bin 131230 -> 0 bytes .../TableTools-2.1.5/css/TableTools.css | 321 --- .../TableTools-2.1.5/css/TableTools_JUI.css | 185 -- .../TableTools-2.1.5/images/background.png | Bin 944 -> 0 bytes .../TableTools-2.1.5/images/collection.png | Bin 1166 -> 0 bytes .../images/collection_hover.png | Bin 1194 -> 0 bytes .../plugin/TableTools-2.1.5/images/copy.png | Bin 2184 -> 0 bytes .../TableTools-2.1.5/images/copy_hover.png | Bin 2797 -> 0 bytes .../plugin/TableTools-2.1.5/images/csv.png | Bin 1607 -> 0 bytes .../TableTools-2.1.5/images/csv_hover.png | Bin 1854 -> 0 bytes .../plugin/TableTools-2.1.5/images/pdf.png | Bin 4325 -> 0 bytes .../TableTools-2.1.5/images/pdf_hover.png | Bin 2786 -> 0 bytes .../plugin/TableTools-2.1.5/images/print.png | Bin 2123 -> 0 bytes .../TableTools-2.1.5/images/print_hover.png | Bin 2230 -> 0 bytes .../images/psd/collection.psd | Bin 25792 -> 0 bytes .../images/psd/copy document.psd | Bin 104729 -> 0 bytes .../images/psd/file_types.psd | Bin 1090645 -> 0 bytes .../TableTools-2.1.5/images/psd/printer.psd | Bin 119952 -> 0 bytes .../plugin/TableTools-2.1.5/images/xls.png | Bin 1641 -> 0 bytes .../TableTools-2.1.5/images/xls_hover.png | Bin 2061 -> 0 bytes .../plugin/TableTools-2.1.5/js/TableTools.js | 2476 ----------------- .../TableTools-2.1.5/js/TableTools.min.js | 77 - .../TableTools-2.1.5/js/TableTools.min.js.gz | Bin 8785 -> 0 bytes .../TableTools-2.1.5/js/ZeroClipboard.js | 367 --- .../TableTools-2.1.5/swf/copy_csv_xls.swf | Bin 2165 -> 0 bytes .../TableTools-2.1.5/swf/copy_csv_xls_pdf.swf | Bin 58824 -> 0 bytes 29 files changed, 2 insertions(+), 4016 deletions(-) delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/as3/ZeroClipboard.as delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/as3/ZeroClipboardPdf.as delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/as3/lib/AlivePDF.swc delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/css/TableTools.css delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/css/TableTools_JUI.css delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/background.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/collection.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/collection_hover.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/copy.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/copy_hover.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/csv.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/csv_hover.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/pdf.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/pdf_hover.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/print.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/print_hover.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/psd/collection.psd delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/psd/copy document.psd delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/psd/file_types.psd delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/psd/printer.psd delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/xls.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/xls_hover.png delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/TableTools.js delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/TableTools.min.js delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/TableTools.min.js.gz delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/ZeroClipboard.js delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/swf/copy_csv_xls.swf delete mode 100644 airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/swf/copy_csv_xls_pdf.swf diff --git a/airtime_mvc/public/js/airtime/playouthistory/historytable.js b/airtime_mvc/public/js/airtime/playouthistory/historytable.js index 3acd8648f..023b11e99 100644 --- a/airtime_mvc/public/js/airtime/playouthistory/historytable.js +++ b/airtime_mvc/public/js/airtime/playouthistory/historytable.js @@ -7,46 +7,10 @@ var AIRTIME = (function(AIRTIME) { mod = AIRTIME.history; var $historyContentDiv; - - var oTableTools = { - "sSwfPath": baseUrl+"js/datatables/plugin/TableTools-2.1.5/swf/copy_csv_xls_pdf.swf", - "aButtons": [ - { - "sExtends": "copy", - "fnComplete": function(nButton, oConfig, oFlash, text) { - var lines = text.split('\n').length, - len = this.s.dt.nTFoot === null ? lines-1 : lines-2, - plural = (len==1) ? "" : "s"; - alert(sprintf($.i18n._('Copied %s row%s to the clipboard'), len, plural)); - }, - //set because only the checkbox row is not sortable. - "mColumns": "sortable" - }, - { - "sExtends": "csv", - "fnClick": setFlashFileName, - //set because only the checkbox row is not sortable. - "mColumns": "sortable" - }, - { - "sExtends": "pdf", - "fnClick": setFlashFileName, - "sPdfOrientation": "landscape", - //set because only the checkbox row is not sortable. - "mColumns": "sortable" - }, - { - "sExtends": "print", - "sInfo" : sprintf($.i18n._("%sPrint view%sPlease use your browser's print function to print this table. Press escape when finished."), "
", "

"), - //set because only the checkbox row is not sortable. - "mColumns": "sortable" - } - ] - }; - + var lengthMenu = [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, $.i18n._("All")]]; - var sDom = 'l<"dt-process-rel"r><"H"T><"dataTables_scrolling"t><"F"ip>'; + var sDom = 'l<"dt-process-rel"r><"H"><"dataTables_scrolling"t><"F"ip>'; var selectedLogItems = {}; @@ -155,25 +119,6 @@ var AIRTIME = (function(AIRTIME) { } return filename; } - - function setFlashFileName( nButton, oConfig, oFlash ) { - var filename = getFileName(oConfig.sExtends); - oFlash.setFileName( filename ); - - if (oConfig.sExtends == "pdf") { - this.fnSetText( oFlash, - "title:"+ this.fnGetTitle(oConfig) +"\n"+ - "message:"+ oConfig.sPdfMessage +"\n"+ - "colWidth:"+ this.fnCalcColRatios(oConfig) +"\n"+ - "orientation:"+ oConfig.sPdfOrientation +"\n"+ - "size:"+ oConfig.sPdfSize +"\n"+ - "--/TableToolsOpts--\n" + - this.fnGetTableData(oConfig)); - } - else { - this.fnSetText(oFlash, this.fnGetTableData(oConfig)); - } - } /* This callback can be used for all history tables */ function fnServerData( sSource, aoData, fnCallback ) { @@ -357,7 +302,6 @@ var AIRTIME = (function(AIRTIME) { "bJQueryUI": true, "bAutoWidth": true, "sDom": sDom, - "oTableTools": oTableTools }); oTable.fnSetFilteringDelay(350); @@ -430,7 +374,6 @@ var AIRTIME = (function(AIRTIME) { "bJQueryUI": true, "bAutoWidth": true, "sDom": sDom, - "oTableTools": oTableTools }); oTable.fnSetFilteringDelay(350); diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/as3/ZeroClipboard.as b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/as3/ZeroClipboard.as deleted file mode 100644 index d6b9c072d..000000000 --- a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/as3/ZeroClipboard.as +++ /dev/null @@ -1,221 +0,0 @@ -/* Compile using: mxmlc --target-player=10.0.0 ZeroClipboard.as */ -package { - import flash.display.Stage; - import flash.display.Sprite; - import flash.display.LoaderInfo; - import flash.display.StageScaleMode; - import flash.events.*; - import flash.display.StageAlign; - import flash.display.StageScaleMode; - import flash.external.ExternalInterface; - import flash.system.Security; - import flash.utils.*; - import flash.system.System; - import flash.net.FileReference; - import flash.net.FileFilter; - - public class ZeroClipboard extends Sprite { - - private var domId:String = ''; - private var button:Sprite; - private var clipText:String = 'blank'; - private var fileName:String = ''; - private var action:String = 'copy'; - private var incBom:Boolean = true; - private var charSet:String = 'utf8'; - - - public function ZeroClipboard() { - // constructor, setup event listeners and external interfaces - stage.scaleMode = StageScaleMode.EXACT_FIT; - flash.system.Security.allowDomain("*"); - - // import flashvars - var flashvars:Object = LoaderInfo( this.root.loaderInfo ).parameters; - domId = flashvars.id.split("\\").join("\\\\"); - - // invisible button covers entire stage - button = new Sprite(); - button.buttonMode = true; - button.useHandCursor = true; - button.graphics.beginFill(0x00FF00); - button.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); - button.alpha = 0.0; - addChild(button); - - button.addEventListener(MouseEvent.CLICK, clickHandler); - button.addEventListener(MouseEvent.MOUSE_OVER, function(event:Event):void { - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'mouseOver', null ); - } ); - button.addEventListener(MouseEvent.MOUSE_OUT, function(event:Event):void { - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'mouseOut', null ); - } ); - button.addEventListener(MouseEvent.MOUSE_DOWN, function(event:Event):void { - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'mouseDown', null ); - } ); - button.addEventListener(MouseEvent.MOUSE_UP, function(event:Event):void { - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'mouseUp', null ); - } ); - - // External functions - readd whenever the stage is made active for IE - addCallbacks(); - stage.addEventListener(Event.ACTIVATE, addCallbacks); - - // signal to the browser that we are ready - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'load', null ); - } - - public function addCallbacks (evt:Event = null):void { - ExternalInterface.addCallback("setHandCursor", setHandCursor); - ExternalInterface.addCallback("clearText", clearText); - ExternalInterface.addCallback("setText", setText); - ExternalInterface.addCallback("appendText", appendText); - ExternalInterface.addCallback("setFileName", setFileName); - ExternalInterface.addCallback("setAction", setAction); - ExternalInterface.addCallback("setCharSet", setCharSet); - ExternalInterface.addCallback("setBomInc", setBomInc); - } - - - public function setCharSet(newCharSet:String):void { - if ( newCharSet == 'UTF16LE' ) { - charSet = newCharSet; - } else { - charSet = 'UTF8'; - } - } - - public function setBomInc(newBomInc:Boolean):void { - incBom = newBomInc; - } - - public function clearText():void { - clipText = ''; - } - - public function appendText(newText:String):void { - clipText += newText; - } - - public function setText(newText:String):void { - clipText = newText; - } - - public function setFileName(newFileName:String):void { - fileName = newFileName; - } - - public function setAction(newAction:String):void { - action = newAction; - } - - public function setHandCursor(enabled:Boolean):void { - // control whether the hand cursor is shown on rollover (true) - // or the default arrow cursor (false) - button.useHandCursor = enabled; - } - - - private function clickHandler(event:Event):void { - var fileRef:FileReference = new FileReference(); - fileRef.addEventListener(Event.COMPLETE, saveComplete); - - if ( action == "save" ) { - /* Save as a file */ - if ( charSet == 'UTF16LE' ) { - fileRef.save( strToUTF16LE(clipText), fileName ); - } else { - fileRef.save( strToUTF8(clipText), fileName ); - } - } else if ( action == "pdf" ) { - fileRef.save( "This instance of ZeroClipboard is not configured for PDF export. "+ - "Please use the PDF export version.", fileName+".txt" ); - } else { - /* Copy the text to the clipboard. Note charset and BOM have no effect here */ - System.setClipboard( clipText ); - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'complete', clipText ); - } - } - - - private function saveComplete(event:Event):void { - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'complete', clipText ); - } - - - private function getProp( prop:String, opts:Array ):String - { - var i:int, iLen:int; - for ( i=0, iLen=opts.length ; i> 8 ); - } - - i++; - } - - return utf16; - } - } -} diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/as3/ZeroClipboardPdf.as b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/as3/ZeroClipboardPdf.as deleted file mode 100644 index 158d5f188..000000000 --- a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/as3/ZeroClipboardPdf.as +++ /dev/null @@ -1,310 +0,0 @@ -/* Compile using: mxmlc --target-player=10.0.0 -static-link-runtime-shared-libraries=true -library-path+=lib ZeroClipboardPdf.as */ -package { - import flash.display.Stage; - import flash.display.Sprite; - import flash.display.LoaderInfo; - import flash.display.StageScaleMode; - import flash.events.*; - import flash.display.StageAlign; - import flash.display.StageScaleMode; - import flash.external.ExternalInterface; - import flash.system.Security; - import flash.utils.*; - import flash.system.System; - import flash.net.FileReference; - import flash.net.FileFilter; - - /* PDF imports */ - import org.alivepdf.pdf.PDF; - import org.alivepdf.data.Grid; - import org.alivepdf.data.GridColumn; - import org.alivepdf.layout.Orientation; - import org.alivepdf.layout.Size; - import org.alivepdf.layout.Unit; - import org.alivepdf.display.Display; - import org.alivepdf.saving.Method; - import org.alivepdf.fonts.FontFamily; - import org.alivepdf.fonts.Style; - import org.alivepdf.fonts.CoreFont; - import org.alivepdf.colors.RGBColor; - - public class ZeroClipboard extends Sprite { - - private var domId:String = ''; - private var button:Sprite; - private var clipText:String = 'blank'; - private var fileName:String = ''; - private var action:String = 'copy'; - private var incBom:Boolean = true; - private var charSet:String = 'utf8'; - - - public function ZeroClipboard() { - // constructor, setup event listeners and external interfaces - stage.scaleMode = StageScaleMode.EXACT_FIT; - flash.system.Security.allowDomain("*"); - - // import flashvars - var flashvars:Object = LoaderInfo( this.root.loaderInfo ).parameters; - domId = flashvars.id.split("\\").join("\\\\"); - - // invisible button covers entire stage - button = new Sprite(); - button.buttonMode = true; - button.useHandCursor = true; - button.graphics.beginFill(0x00FF00); - button.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); - button.alpha = 0.0; - addChild(button); - - button.addEventListener(MouseEvent.CLICK, function(event:Event):void { - clickHandler(event); - } ); - button.addEventListener(MouseEvent.MOUSE_OVER, function(event:Event):void { - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'mouseOver', null ); - } ); - button.addEventListener(MouseEvent.MOUSE_OUT, function(event:Event):void { - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'mouseOut', null ); - } ); - button.addEventListener(MouseEvent.MOUSE_DOWN, function(event:Event):void { - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'mouseDown', null ); - } ); - button.addEventListener(MouseEvent.MOUSE_UP, function(event:Event):void { - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'mouseUp', null ); - } ); - - // External functions - readd whenever the stage is made active for IE - addCallbacks(); - stage.addEventListener(Event.ACTIVATE, addCallbacks); - - // signal to the browser that we are ready - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'load', null ); - } - - public function addCallbacks (evt:Event = null):void { - ExternalInterface.addCallback("setHandCursor", setHandCursor); - ExternalInterface.addCallback("clearText", clearText); - ExternalInterface.addCallback("setText", setText); - ExternalInterface.addCallback("appendText", appendText); - ExternalInterface.addCallback("setFileName", setFileName); - ExternalInterface.addCallback("setAction", setAction); - ExternalInterface.addCallback("setCharSet", setCharSet); - ExternalInterface.addCallback("setBomInc", setBomInc); - } - - - public function setCharSet(newCharSet:String):void { - if ( newCharSet == 'UTF16LE' ) { - charSet = newCharSet; - } else { - charSet = 'UTF8'; - } - } - - public function setBomInc(newBomInc:Boolean):void { - incBom = newBomInc; - } - - public function clearText():void { - clipText = ''; - } - - public function appendText(newText:String):void { - clipText += newText; - } - - public function setText(newText:String):void { - clipText = newText; - } - - public function setFileName(newFileName:String):void { - fileName = newFileName; - } - - public function setAction(newAction:String):void { - action = newAction; - } - - public function setHandCursor(enabled:Boolean):void { - // control whether the hand cursor is shown on rollover (true) - // or the default arrow cursor (false) - button.useHandCursor = enabled; - } - - - private function clickHandler(event:Event):void { - var fileRef:FileReference = new FileReference(); - fileRef.addEventListener(Event.COMPLETE, saveComplete); - - if ( action == "save" ) { - /* Save as a file */ - if ( charSet == 'UTF16LE' ) { - fileRef.save( strToUTF16LE(clipText), fileName ); - } else { - fileRef.save( strToUTF8(clipText), fileName ); - } - } else if ( action == "pdf" ) { - /* Save as a PDF */ - var pdf:PDF = configPdf(); - fileRef.save( pdf.save( Method.LOCAL ), fileName ); - } else { - /* Copy the text to the clipboard. Note charset and BOM have no effect here */ - System.setClipboard( clipText ); - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'complete', clipText ); - } - } - - - private function saveComplete(event:Event):void { - ExternalInterface.call( 'ZeroClipboard_TableTools.dispatch', domId, 'complete', clipText ); - } - - - private function getProp( prop:String, opts:Array ):String - { - var i:int, iLen:int; - for ( i=0, iLen=opts.length ; i> 8 ); - } - - i++; - } - - return utf16; - } - } -} diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/as3/lib/AlivePDF.swc b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/as3/lib/AlivePDF.swc deleted file mode 100644 index ee0f3f8f4ca49ccea24defe4ac6bd4b9c3e65a05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131230 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%0DpCJ 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 z�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%0DJQR7sG>Xt#p#V(I8GKf74o* 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

{ zRq=$F4L{F5bE$64l>HvObE*E^Z_^VHNbZ7%k_GLJoZc932=GmRx3sXwvPgj_5Zhb9 zvqs&ZFz2;SZM{|5UbC;0*Y9nwx4*XN(9nmR>+|N-t#sU3+FC=gwstVVUC2gLurNKd z@UQju$NIJV*qximsxN?HVf99B5q3YT(oz))YZI+PkpX2_T8(faMgu8s(9f2?VSS?R zyR>HGvx=$z{F^hpYM*h$>b1=mM>1+8tKeq&IS$A?x*citFyP{jKsTzqp<{MVEROHpBx~5SOp1mgHWc2s4 zT3OYAKK-v2eY)y@Jl2psJ*h2!EVO7>D;T!z0YnB)j|~K;Bo>Qn`Dh{)!L?xirq?7T0Sv=2eY_rElu*w zDTaD7Ogeq5hoD_JQ7vyDg0bi*P$4^uta_qBE0^7_Yf&!gyO?+*7$rKh;AnO@W!QJ} zlGjwR&nXohNKE%{k)#iqmK8yL|=5wW0E@L8s%>%OB zJjX)*(K#QDO4uPU=W5(PClcj!M`h$D7jkiPmn9)Uyj{2%S5kVD=746E&5pu3pS%bj zepc{+mh`47qPKs=1rAiyL6jECB+~Zu3@@+uDt{)ZqYJzoJNNMKZ!fQdCqm&W7u0&I zY~vAKXPMNdXlfY)qR!zbA|u|04dMblVJu9IcqZ(!1c5h6s|e~^P6{_{)>1RMse-q9 z>}0C59bK@g^8cX}N3eL#lZ#p%A;=P&qyWn-oX>rD*Kqr7rL%qHO&LgAtmiSBH4?L^ zYC@Y9rYreJ5Jk2ol-Kkm6%f?_ZTTp3q9=p3t#OsYsu5$HRz1s@qs>3hSpYA2l$|k) z5)q;voq93^$!R4IwrfDriCuV`M!TvD&=#h-vl@mX7Q4O()QH-{2QtCFq%L+_sOn$#; z8`2iQ;MK_|7Z+bva6c}qav?rinde{u7Kz$h@~|!?6Dp)n`@m37B>s|tqL3)XQX~fywFFwuf&ys4Z~*YoVFma6elTriY?!q47QjLZk9JEfSV>) z3q)Rd+}b^5suro#2rD8`c7g|;>hLv*+`^xYRrj`+$KdXX;6Lh3% z$k7(!IAu4)jrKZR!$#+X^j^XuMN&-^0v?jhJUYOjy1oU9i-S2m0`6fgx3i-=9IjU) z3zye)CEQX>X7>Iirs1dnO4{9IlK7c3J)s{FmjzKhD~H|u)fTsrmb_I2Z(J#qh^PQu4WA%t6PileJ{*Ws z#^D7aMZdr*U&diY-2OXq@?Hdw|I3L7EZuNHrZ#p&?r;>R@!z@F`X?aQ8fT8JU@e3G znv>nr!B%gdu1mN0W*wG%Oy8MvjF#6J*LU)a{@>b9S9j-m`|XYmxCe?ON9An_Fsp?s zAM?#@)G6Nfj@z>bOF*tDKn-!>T9`~-jNA1uGhg9)%uzQ*r(b?NS$4m!{VT%0uP_Eh z_Uao9>ynyRPiiNLuP(=RGfk7FepQOxl*k}{h0KUqK{Et}%*e2v-uPU#j(jVe?fU8u zY>T^o!nDmUv-YiroQ5x6>Z-w{nR|jYE!ehAhZ}izx`OV%Db^KL8O;nuG9RPuE~_iz z(Qu`$c1%g-j< zH5;p`RR=uaMW}U-(QI#?!d%O3Xr=LSU;4P-l03nI-XRcUm$G)=Ynp7cTH!1j+U9I# zY0d6AEi{y!R_&@d0$CfGDV&m#sWXd1cRvo`mh~3{4=dZMz?;VuGo!0*-B$uH>ZHzq zV6{G<9P;e$i#*OUgmFk`i`pCm=xuL`wbR3loFZ5F>Cb+1&K3)D9b5(MfCm zL{zWO*42kp)^D<0=(&+g2OL-s6V9!)Tie&?%^3uObXg-{LD<4H01xh(YU<2-fyxKP z@^T^lkwcEz<=6;i2XQZkIq1%$f%lGre!!lZ0-A*W+js8EnZh$%d4xBw{uf?Pb^G)z z|F)dGA7S7o3Za6#IOOpk-$+>Rp?}2}MlF@LyAMi?BQD-pjbLwZ?%s1CUhZ@S^p|D| z^>fiV#lTbudUmm2?u&TLSX#07 z@t(QI=ODXAMpg7UPeX7_X@PDm>c71GJvY&X6)Vv%OgrPxhs%jH0{&Kg->lX6S zvJ7d08{(t5EuIAUfj0ks-6>`Qxfi-ZoLaIeUV``$XuPv|@i^YSA9i(;x;9?dcC~BZt%t3bV z^2MfX9d?52o43j=aCRdX|exGuJqch-6uoON5(x@o%Z zN`35EGWR9D*Y*9&Gzl{ zrO3-$V-SF&Ul4G3E3l=Mr)+FpRFu8T!vmk0Q2kTw0#i08V7YowQo6;&z!U4J70B6QWozKelwU42b$_GOrdtG#_y zhDr;!Mn&X85r603KOOKH+LOk*dW4bsMrfVa%rqq~H9{9&LO9Kw6<_XYPX%jlX;dBZsDZ_8iV(+G^B_EpzjB{!M6WG;@y_B}8a2#|zLqx2cJY5!vD#*x$5RbW zVc9yAOk>$F7PYb`Fl_CjCo*j8vM0KnakR8kHEbSfY3&s^Ke2VFnx5O81EF zl>dTS-i_G5efP&36n)0$ouJNQ8vCXA4g;a9n=mBc{`oS)JzsiTS~J#Rq%!bN%`(5hWJZUUtoM4X}eeuqEc zj5357K3T;o{%RC%joqehN?e#vXqs9rIs!3Ep@)y7v+O2iHDNu03A- z``v^$)Py&KN#DU^{~>_De<`P4g126TE`5d0eTD9Qg|2;to_&bCdy;tfnEwHv@eada zP*Va9I3PHeMe%$kXrk1RIHs0F0QltpSbq;t~!geM+rSeR$Kkj1C!QV9AgJ31>w>I z3)fs+V>fF9<7c)PP54ko9`@AHFh5|?n;f*#dWDc95Lg}XFpwGLg zsiBhkV>FVooOrcss&q*$OEITWAp$MIgUzZ52(n>ve2WwgnH1VKY~Fmb9B1H^skE>= zK$L2VAF%^j@~(LwqO;p~+b4v{D_lH;Cxc)F*ti|l{KP+_uWB(Y7LQ47#yjmv^{bBq zfl;Q!PofX@yEmc`+@8(t1m9H-pW*9|L@wRFN<}t_4J%)S)7`AmlFF2^eS0cXKa{LvJAJSXhvz0#MfeWRx(ya7*d}O;QQbw_zQY z2GI?;u-&M0IX=e`SBwHq)w@t|BfDI0fvAT~sKM}f25AgjLKA_Z;iZGa1K+SyMta1C z3)2Z<3`^O?lWvCTF7zJ*XHefa#?AXI&jc<_qo=TskPA#TX_X!3xWD>L1bwa_%5Z8@ zU9s4OLgD`nUbIZVX|xl1bHUGgSAMlK@g4OHrXh(DM)@~&c**}CJHm-4z)W|QLzV(S{slaj~Hp|jGoGE|kT(8J?F2}VrO}O&11q(Z1 zj5Uw^D+;@j2=vKh?-1g>w5uKK$ub&%Q|^J<$sD~hcwD`J9uOJb3Q=m=CrldJtztBv zYqB8*qHWU;@SW*j@k$Lo?wWfCRiPK(lw#9)O^b>WBr(gzHyQd#1yEio6aX-57DV@r zkzQ^wZ||ElHW+ar$CjpC{4Qw4u2J=dYuB!HLo8c!T~^r{0No0@#mjEyQ>q{P>jC95-H zdq#DI_lL;Y>{37Kw#WCrs{+IU?g_1)w4SjOyGO_;G1Ie`8nc>X?Plo!qL!|& z@Ggd-_cX4U@tYVm*14HTF~2-KZcU9JIngI@S1UKTL|nlKlit4f!=7=!Ls+aV*v(@n zMwCIYPW^u@I|v`pUHLPb2OU+0;hzS#)JtA)FXg`_YD7(2M1-y4_V!;Csg_3XWz&L3Y6FXR?$}o zf_Bb+)9pAcxH|3Me&<;6(I1t3wq(UlY?>!Laak2v5+s@2#&dt z@f^MzvUtx1u9fCcpLX1-R!`XnC?@pS@Oxb18cXfdoAbny)?y(xaGoYs6z{2nl%bDt zP3B~8e=D$Nhu@e7;_X(jo=MJPtjB8s%?3l0pr$;SZ>Q9QrdYrC*~g7^<(u9O4=6pY z5&nb@B7WF+zU73`6S7rkMOaUdpF5d}!r#IC%$V4We~J3dzM>M-Z+bHlS1mr!m%PUD zObk;XZ&eIX+om?pz*(vBQWzP`5z-=KLHMlZL_1_x-*_t+kr>B0yszs_?DQkfs%^8 zo7*J-&9qx+x&xnWT{j9~5zThnRKLolQ<1p#u*_n;fXIO}nuefbbF?pkREbmC=s1GFy(my8aF zS_P8Gjg%Z$p6=w3GuylCHx$L^6m+IcPo}>IU85py+uM&j@i!~1J_r*4XuC(C@uWuN z6WcT2@#SQ&$o~e=ZGiR0d~!uzIrfkNnC) z;oFbp8yWrHClCDioQ*_q_7a#WB_m*;__oLxT@i^#97iUw>o}lV)q%1Voe8gcF<XA{vsei(47hI#etR5-SRe+=<=zP)`%+^=d}22qp&VMK0Ay+MLTUH53;~)!%sKCTC+;)abwrj}-0%gig%1^G zebnuo2e`-EAa;oqBe=LH$q}U!gk*LE1yeS0H-%KR4njf6@Mhn*@n^hh0!E_L#Hiyt zYFb8Z-*xep(9<#~zukHg8Q4k4CpZ-;%3!!FxG1Noo+fUizmB0FWt;uLmJ-Ne_BoZ=7y{1eg4oTh zvwT@3o7IV3a>v=u0FZjn8E%DvbgzN~?_}xr_I^ZBg(vHI#Jnp!tCXuTpPnu{hpS$KUR^v+q=39p~m z8S5X(oIQ@iV;lfI5AyB~$aZj%cGWX!GuNBjj!Iug?CIL;y4ki=N{=_*ipLX&ER+R< z8%j*m&IFBk2c0?5>`HyX)I{Icf6cV=fyTa@YHOO&?W-g=29Y$^6x#6Y>?Z2?oOzlm zMQG_5T`}BCO=N$a2<{-tWe=P=t%#Fvgh=y78G|5$7Z>h>7?)th@p6PbQ`44_wNc!p za|Bsj*X&?aF=Jw9Kw`Ul!+K@Ad+oTx8xMY5D7@XWmQq*P-S~L z6x5kb2hVV)e8FQM;EwE@r7ugKJ;zMt88GVyDT!bbr7eR)A)UF^BAq>3{l^}nx$IKW zEY@}Yb*iAv{-GQZyzcGs$FKM#dCnL)S-h|P7kS(66mW(LwEdO_YdZA6@CJ~4Wwski{`kQhhP#$BSlp{qNg5`T&S<)Wq>wR*R6a2s zZM*L_KeS>}o?U26r?Db8w2J5jf#;ac1fsp2$p;reQ0A{uWwu@uJ+8*>K&$e&(gRWyr=BT?_(h$BG2IX@@FYt~baJ#=iQL+78nNEizN z4|E5nmA>bfwkF3%0`t*KREr=hge+$n|-F(To8A)z-+)W z%!e;Pm$`?s(SQ(LU@oKFC#DM~=2}r+`OaWm9WZEzPw+4h9Bc}7eDIpqdVeo)yzu-) z?#(G8d8D8kQYUg%FiU)->@YtENZj#LB<>KNBbTTNQJ1o@qXGxdNlLmx9cRFI3mqEL zd&K{H(C)Nj0~}Xm0UDlsOQl+EW`R?N6;zYeYa}ipi}6NbSSf5!cxps zm%V0_*1qAwz$W^g_HEa>KM-+hw^mTkVevX;V?X{7)UEMriCJaMPP0o69gpnXYDk6U zwux@?y{bSZX?vEltWtVCH;j9F;fHK!BXsts9D=|ym}0ocG1@@Rh72z|Q%&TId`_Z| zHXDln4Ko8pM=l>Tu!IiSj#P7uyi);r?J7>^U zl9mW3ywoCX7_o?L>o_PVm{-X#9a|fuvq~w_-(?~JT7Q`aVW~)Z*-#}LOzF@hY_ZtD zmJ6&3MDr^+3LBL6-6bzi{W=!3YnTxYb9?)$T3Y1VE0{G&+Uw)?W-ZUYmf98uoI9D7 zN!y;0=6_D=O*8~@YT?(ssBXvJmF9Dut>`W+qt;nRg7<`KKb4z;6$deaa?K5ETnuy} zYz6N8&NhZHK9K}ao(Ou&Y>SxS27ttBtgYfp8l8WgoeoDL?K+%|tm;#yy8l#Hdb&1~ zf&!u>pkMs*%8x~rseo>xbBW2d0oO)97_K1Za+?@6YTBU4A2USR@j=0F zC^vZItYBE-ne5W#y_iDKDSbi$PMt{+u-IiN^7N}G**Sc&sg|xLNJXqaZL`gpdh>57 zc+BgWnG3rk+#`BExs&^6tOs^t1S(eNNNPumsB;}1E(Q`KKXsQ7w!FuEpZSATsoB2DIBrZDPTO$8f z5}?6pDW~LlA;-+S`$(WamVjela@!0z2@BlGDr<7k)KPZbeUP9u^HqX&YHViA+Ei!K z6c;VnQc)`vsWvTAs8qL9|Qm zDu$Y1x1M=@dm>YL_;8QPR3f2_L`l>Hx`HaIJ(9{Dh!6}@GH3;CaZ*P!dd8iyLrOQ1 zxI(iE1@xxa{H3*$Eul1PdOxX8x%)oZTn$DEcWx3c@K$<~J5@njjya!Mnh@bEo@M<-GUo(%B8 zA!JQEitz&f8uu0^-ALS$ zIh3N(qdkNJG)Dp)9ITvfS1X_zN%j6)Rw5r5R0L|0F*$S{ z<>UtXu6k2^g)*04qXh$gn|=LiH(_f_sY(O4S8-L$Tmc?a%(2g2UPzRD{%@td%VUm( z_JV+{{-I*8qRw1jv#5hgGZ+=4Ut>?gPLL((ZhD)AhoK^@Yh*}YI)PA_7{wCH#+(s# z1AgCKE1AAbM~vh~u<1Xa9Mr2UK7y&}N&3-|2HcELFk*g@WglmH<~|e$ z65#VbpiXC{z~)CSnXbMl=!#Gu zvmHydnZxbWacv#$;_Ko~2U8oH9}+&xR?rb}29kk(|fGNB`zpjiN8 zS*o&}n_|6%EA+B_rN43JLlu&^K5vuUoo@cNgAhT4mM`m4h+)6Nj_URwst-qy!EZom znACrLd3os)h+tC+S=A#hN9dtaU*v^8G{AYiK?#&RmG2Bka6dC zQ?{X!hL;J}fT^xf>gd5Ct40)+E+2@%!bm!}`Gxol5h=&42|cIL0Ct80XvIVAEsG_r5!eEK31jUr z?+@iA+lKp(L0!qG8h%YQn_Np9lv7QV!46}%zCH{_t}8~(s7AzH$+x-zhPb1rz=YXP zDY&}6QEab=WO!ias?pH9fuiA%dkI z8fUASfppUe@Xi@TN}V~L^lmA$GO0<)+-=&h910XDLHudbd|X~*vq(I;b)6SSXj>G; z!cw2!g7}#a^COHr17&$uUXsDIRHy`ktxGzErY*~}NfDZB0!3kMGkcD0{X`DiT3vG# zMdP|w>;#9+jXnI$6_Q91bV% z#)((!n|-#|dz??i_-{ga0^!}vqfSD4zMy^dcYWyZ!dfH``mgn2!y;>7`Ci}wTtF}M zpnHx-M#V1NJ|v(Qd=QbCAI%x-f3Y$7z#F!oPj!r1V`SlaGZwTNG-Y+M)VE8AFqzoww zQ&NaD{who~#BWyC2z4UJoI zR9JRRaCFRabA7}0=9=#IJL34e;`lq`_`BoSe-AKl;5B&cW$4zo@7`CM;5i2%@aU7o zyMM#GSDy5p0}Pya4c>bN9(euE0tRlp0x!G*PrL&E6GvW#&V7wMdxboE>v;DI{{jD= z5$~Q6|Gp9LK9F~B{Xc%djHCiZv0y@aG^S{>q*pdwsR15dy{NIP>Yr(>TY8{-5YeLqTxhGVJqW(dW5 zy^H`dKUCWDgypsGrV}hbS>0GVDj>P)Z4z0;O`EOnrt=~{S+KyQ@HLF|QqUNSe!G7E zzRz5RfzpLSZHcw^&;~C=_`or^aywgY@P4%X7y{|44-(4{B1;8lhwvq5PW^3#0s2dd zX#0=A%hNjCQaswVdx|{T=ugs=v{)qhbxMerX6hqH`MKedQk1nc;yl``(#z8|)s*>l zTR8uTP>0OKRn8Oz4N=TXvw8=j{JK_;6os|T#W@;_^SpJa%MI84ZK|{u_H#}cR;^=S zobYVB+wu4jx{kXL31bdh5#BYXs|=zB-dw1y=8v@LZuI zhlDwCN3svRlvw2OWdsbQlEskZjXVVAp%Rj?#xZbwB>Y5?A+2N#_o>?%3Gt|zu@ObL zDs$4GDwrJBp@zCSuTl9PEs2jtXHfjPQj-`v+($LgLrcp!k=@yT^R7V16DnUv+B(${au~NTCA;pit z+#d`~zL+qG#!?u@y}a`vN9P9_(uI+|S^-#hD5uC>va6EW0PQsctRNV1_9xN9IrhBozC8W3;S+zSrq+#~hrPpo6 z_{u~GS(M?2iL9Ied=2;;3=1=kdYl@J?+=2hnjVW8Hv((ijH{oT!AQ|i!V$?Kw;OLz zJ&Gt%0H{Ouvwc_W>G>gpx`ElmdNt-`oa1t3+&tUB?K8nYJvP3TPt1e-@s}NglY9+- z-9OGslG)5Q@PA;yup>9MdOw-|1<)N?MC-+!BhSR1)ku=K5}gTLxd}1+An-hp$VnDS z+Q^9~5(o`38*J)b==6`Rj-U8dQCD4TA=V+@5mMhQXLxikPNqHwB4xxH?m25vej)kS zSvn5eBmF{nwm*AzSx!5$9!IhXTZ*3@j}o#6#KC+66ThKmbvHTApjUGpIM0ZEbklnSvAq%q^wi_N(0Rxp3?%tPhh{p&sBdUUfeDE6x8WY z)P-3u)-M9gl7&`(GBn9C>dVTm*+P@gbV!7*xPgs36(!K4TXuVkqeOY^#7m>cvkYa6 zmfkT2s023mwT8k0-@JqYn_jts=!8rZbBpi|sB>JyCNP&M@j_2#*2C+I{oi`NR@!_1 z4~7gW9KnwG{vEYrL2|4=HcOuP{f4w6h~W3@%I~rhFC~tg;QK&MqQjgj&$1KpC5@oN z1F%Y4zyPPrg)S6i=6yPKq!}=$-jDvm`5jzeqnOi&8j%`6$pED;EhsCZ$fE4!*IQ`&+(3 z;?t8l{1wRF4~f;&A<(`-82^bJiZ6L7+aXepKzoaE+r-6~Qu%x|TcNk1m#zZ6oGzJk z(Uy=;7(2s}dr8-}LX!8*$$B+Dv_P5HEJ?wy0TRcV1C2qi{K3DY*Kit1`VbD4u3}bT zAz__zh};{bKu4tfVRQTp>Oz!zbXdB#BuU?JFX9hPK2L0pJ*Ap((9fvGTOMN)aDJ!K zTcA#+->Ki13V`JI{)R%0eLl=iB3=#Yhfi)Y94=rjk$uAM5C9sT_9^?p-%LInoqW-k z->y{mBeom+K|P1uLGFqKLhRqZ?_>`l_&yH{PW$2Rl;%&JRJ$P$5YF|1Jv;AZx_loS zLwu!&S5p9%%uXOV0)fJrMWx+040Ttw#R{s%SE9!o;Np2PW7?6;^~-X71~~SNd~vrl z+QveEXq`I-p@yMIzrkPxa%=taSq0wlLemE4UocP0RUtQsXPg=U#MW#r5=GuVNHkW> zeZKE7sU|EcMup4*A_f2?jnDdJtop8PGC9i_)%ZjegcpJ!;Vm);O8;_(#Kyce@aG8> z%g$a?0z1$REf@HzMDqD&5EBBh9foXLpudI;0lse~ZoJ@oFGAONZ=a8I>VK=+o5ZxU^rwtg9q zXGY7|g{gkL$;VH9Wgt~$<1X942zpk&!25p>7I_l8?}v#_a14c(fHC_Vj+Lo39?-TN z{gB7`iwb&Q0)UVEO$ETAFM&b*1$%D9h4BbD8HFVzT;MF%LNIY*qBrG^jAab?1Vg(p32*ZTI=P%M zto!1g8p4D&@rIE3HjNwIm)b>u_?+$w*~Ur^hP;Qo>d7fnA>e}|%93z2qnC@q^ z9wdzR!ipwz9}#AJqobTnJ&7&7TK|XaZVt}md%lisCmY+&#3gT|+|%duv}yMXE*;g;5~>Q4OsgVm4?K}zZLFnVBg^=N z+#}5}DjsU_$Q884LC7ut;5_^gQImI?QF5@Y@;ivMH>S)%3ERem`?K%N-X)c)hS6H& zD6(YvJ7yKnlIPN$m9E*w3$DI0%%(o#Iq|gmgVl28r3z_*aqyWlr0+9dU>Tv~ZP~vdOGcgtfJ{8gmDv!i@8XKr6JqtXw>x~#FCR9)5{8GA z7C4oHdiey7UA^EvtZjyJdn4Wqayv|2`vUpE{4b=w%W$wdTDm_abyhnzBBvVxfhM>k zYY`bYrM6u(OlvNS(2W!*SI`AyLh{LR#-ZG!;gB&QJaR7!Ub}CM@Y> zc-9pD&>(iCLer~w>5*Rehu6gkH!&U*LI-9hO+`%(bsc9rd@pt3YiLO$@_~t zo7;7VVsJzI98~dk;^bfKc;a0=Yd=yh!1o%A ztM`qS5r71~NT8J8M+l%7^B3r^Q?EbOBAFE46pw3*W(-5|XgC@KMgp7v*1p)6;9V(U zU2|LGt4y{#x9)Yuh+L~(l(^>Y`3PEHi6T0Tsorlm=kw{^;z1ZxLSPl2myivOr1V_dz;Z z0ss41989vqvvfTo`4uB#`9vW1(q-(6MAi&N_U!cuG5u7}%7c~e5+KyYS@9zt#AHM{ znUR8-P)!0H!6;rCZ1oq)HxvKS#M6N|U!#Iubqb-pqIHAp=~?=&(LTXewxv%Z@QFpd zlzB~guf`|9D^{GoP2O~J6Ta2@D%fm=aFE*}yU z#{N?hlk#_hRE(jYABw_q{r=P_K-lV<{)>EL6^P#JF&F+^Xj60|wn%y8IOvN!GKpN& zTm;lCjyC#kI@1O_=EOWmIdM7TRGMTlK)S(y%Y04{)@#gN%&c2ESBB4uG1#Y=OGdb>aLU$SrU(fS8r&)Z!oN~^gAc)2erEZy#|jcN_QJoR`wAC> zi8xQbG(Glv9JQVd8HoMs{ku@)2^L!mO_ESOty>u<@e60@!*d?36G4&pDv+n+y(ILx zg7H;KF3wxHIx(hdaqbD1OJWzH*>oR)hI^t-LR`m!B0Aa#%y=$dl)m#XgLlF0b38a(G0E-Q1vY^QG-j z$7mC%EA$?Z=TU`AM#yocwqSzKc2#tcUa>X&ky^L%LaxRZa&%_Kt|1uW4F;AmcwTpgovpz;>%EqOM{f1mdS=cSiUSdCfH_y;Dgd7OUn3n_|Qk8Ca%!cj~` z;LEU4HWQ0*TseNq8)382PZDDz)jNaoLHtM}9q`-n!Kp8j%r9EA9PmcE*4Uk&aO)ne zUxF8Yu)ex(&0p_VtR5}oN&NB)Kkcqvx5E;+Y)X+@ zcafiYrTmv-Xf_w*jyg=2f5|R!6G*H?O6}6hUl%>7hB^rX)WYcKX6GkV; z`hQU*SXUPO!^qID*s{Ss!EnpX*VnW;vv4)w$rImY!-$=fk&NKH#f#Rva}&wlM1KWhnGK-O4w zLsFjx0E*b(cKpoS&f6W%CZjt~LKJ36p>YCNE6g)HT3vMUE4 z{TDsvVM;0%)*_+xP$0r7M*4EEf!-m_tu7Z@crHQ4)D1=yUq+ikuolSbWba28_E9=} zI*!_VCdOI3}+m#T`{42!l(#)eebgiVzKexBu!9zOaYZhwD2cflMbrmWY3kgEaL)(#mGQ3yWbX981dUy@Pxh6<-JLRR%h*?{m z=VROZgVQVT$YC8(TAmIxZDCcFKQx2;&#$)XH@r3ZpBW zH@mZtfVj5;XESmravT^+ zzI?DwDeqGZo!VL2JS%n%Tr-U#YNB(e(D#O=Xf7C z1R&?AWMs3;E*^x$OV~9{@t6iO7XP?HiW1^3FSQUC5XrjZuz*=yl^%Cvio9V(GNOPPzEct_Px zrNY#g!${V&aG%I={3Wa;an;8UqoOih@i5c^^oa#`q)miJ9{Le*x@BhQlKpVz>9r&a z=u24uDx+PBrAHCXQTPl>`6X+VG_E6etu1m3siGD7fYld|e6gg}msa&s>BCIb0?2-C z;zCGTjtcFhGj_Y~Qrm}Gkvm+x*zR=TR^F;Pg(1>jSD_lAJ9H)hKv-jpg9>a!hUq?)zWewZ;I7>DH!Jk^# zzZ|}!{>BYjN=hr`Us9N4Exr6;xKOh=36fL}k~BUQN1q67pwKvu`$zHIt_0B$ zn0T>=R7qictu$oL`lTm;!yQ1}YQtHr1p^Wfv6BMoJ8pkZeOU;Lr53r**vsllYeBcV zXMmv7`N84_sqpF~mhh!#yYxigr>+tlhUWqM%RE}L-Sn1P5#&HtSaU8too}7?#~J~i zm9Mcu1ui!=(u20zbDFBycu1$LZmDrub>|5_s&vxh4Y0IoxBw! zbqxq~m9B+qYZ)N?K_s@cO3Q;pfvNkcs~Y~A0_RS z90c@q@@_J2@~PTWov)LL&6C8xjsuYB?7kxzk^-YQ#;y&VwKKh6RMpN_jd9z*Okkox zZ4_G~p#Emg~8-t*RrXt72MH8KoT>3}K-5&DIfWQ9uBsmM9_o^LoH9RX^twk_` z5Fga6)2&G@nJ)wi`w*u+Rs8L+%DcL`^d|!sOaI;74C@{TU}XC=V(Q%I^AA(0zsaf~i&R4jIrpx`Bo-4kfW~DYk;iW)5m7Z~~|fRZ~OrR7V~TCTMN+nD>LHRU_r; z2{w=5f}dakZpa!`DUg6KpiLz1@y(hJzng>c)ULD_rMc6KZVTmwlzfgLjnMqvm~L_I z9zG#sT|d75We{QjC?Ewb8EYQCu^u+473rdrKXY$kk$nnLFEqc@%(`Bu;xM5Z405>m z$clAR3e(5PS4gXTESfoIhj}#YYCyeM*B^f{q!2^O

LlCHqghW6o`YlXts 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 A0{{R3 diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/copy.png b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/copy.png deleted file mode 100644 index 5b01ab165a9d1adeee1142b4ea5a0c47278c0689..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2184 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 KfL+QIrTrJRQ-Rt5 diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/csv.png b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/csv.png deleted file mode 100644 index 43df1559f7cf9546065ef4864e605a834df47b51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1607 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI1#=yXM&$lK5$YDu$^mSxlxOU@;e}oZ`FInOm zQ4*Y=R#Ki=l*-_klAn~S;F+74o*I;zm{M7IGSvpC$O5Fu(I+G%F`dC)!8b7_RUto5 zArZ(*R^)Z9NX{uO&MZq+NXsu$2+mC`&dAJ52g;=9c{FoijrsN?cNllZ!G7 zN;32FfI+yXM}5RnejUS+Wl7>dU|T^vIyZYBNs|KFb3 zD1k}v^Ru(R|9^db{eJ;ta&mIp{`&t)Au+ql-rl&jHoCv@1_R@fjvThn>*M!N+gJCu zN;hCbqi0Nv&8?FsJ0ITOp3kG>ecgrAxJ1HVNM7J!W^?A=z4@(A z^5i{##6PVszG~a@VcC=>L$>EKUYY_<6Qa-k-1*b}hv@-Jd(){w{MowG1d@6bTIQ^X zdi{|nnPQT$IzbLn-bEqiyquXz7aut6tld!hu(BJs{?T!E5h z3zC?WEqSYVB&pm`l5Ar0mOC(ad%+*SLt&G&EfnwW{%$#KcJZ^7g);mz-r9WFnk&8a z+Pvq>%DrCR+xGtV!YPYNV#b4&KN$t7MH4jesUanDhyefFE zGUEDGp2G_762EWK>3v?wwx%lK9dpqg#ecCpEm~^6!7Bw^Cnh|yE{&R&s^&R~Ij7>E zB!9yEUF)hh=l+&^w=Z?N0Z*%=(4o!me;+HH!~IRJ@%WL#nx+Nre?7D(TIS4|v%MwK zQJ^Dqg&_0oZKA!?QVl~xCH3z2*@hinbxP`ERx+<>23-~Uc8 zOPSDjIQ@>f!Xww@{VXrG^n^-vG->P#P1<=>sN+V<1UC;sMb~Tmf;u@z?_D}CVDHL( zP;G+9&n6K?0Vl21xm!QXJGk3s@|upuv$x-zt1eD|c5d#UjPlJ3u4dhRURl>{R*@W~ zeZfNJ>PMF=)mMKh>^`5bZtDO2qU|d!(Q?%(@=@ov&Qp7#q%L;jWQvgX&Q+_lZs%_mR_e_p;m{9rn<7PmN^r`oXxdYf;B1rE>;FX0u(7@Cbiqk(TjFEc$wC z^PGLFZ{90;bG1=3-IJrZ)vyUCI0#j&)qp z^9c;B;Bd0qu_K&!QC#yu1rc^#gAITG_Fh&h^(eXg(nLvBi0}3H-#064__p0_wElfP z@#2#fW{W+SUtZbVyT#z=#nAt%8$2wHj~|OztSl~iWLC)5Khgaw9IIwXKELwUQYk?3 whM0}@N5#Gw-!;Gfzopr0IJ)>1ONa4 diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/csv_hover.png b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/csv_hover.png deleted file mode 100644 index 10b34d3b90b3f7712f6de998fbc98a051b38a661..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1854 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 TzzMz{00000NkvXXu0mjfWhV~A diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/pdf_hover.png b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/pdf_hover.png deleted file mode 100644 index eb06855f4fc6bb57036cf61324149e3f8e27c902..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2786 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 diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/print_hover.png b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/print_hover.png deleted file mode 100644 index 9808a9cc9c59c3968494faea0399864448fdb3f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2230 zcmb7_c|6p67sr3r1~ZnfWk?xIiiEL+$`}SCOJWGwh8bgQ6DC_@D_e+>r9_Ik;r5_h zYf9sy!DSlRsf!{dvZjfVdB$J&-{(26*Li)nNZ3TTM{wuWE4IsAd+~MOeBT#MgXuzqyN8NjL&DVc}k-L+`nE9U|s*Cp;J$c zUje`&BU=;_6Fcp{$K@gf^!)gxVpfy(xZBH@pMhPlaS#C|_QiB1M4~QErcg6QNXNIG z{9RO1U}_NH>&XlicISTFW+7F&a##-$To#kbT;JTjp80)j;BKC&iHSTQC^_GqA@LW# zg~bgnm&+Mg|GuOzY!oeSO@*qyph{a(Q0wzKg(!JzYinWZj-sL>SlG=mM=RY_0qht( zu4iRs1@@&U1o#Euv1nUc{LmN%&JTiFlh-;QY=3#HnjX-b3#a_rUE;PreaO}EYFq!> zah(l?nw!2#;?6WvwxVELy zUsQfeZmyv@0N3_zmtO6mCvhW9O-&Oy>@}sydY(#m<^A(5h?AEz4g!AkQwpjvvWnQr zk6T!|{79Wjg33$(?hX|+QR*;+!`}j$aRSc0U43s@os9ne_2qfHbRH*ZUQpo+mO0qj zHP{9GxzWqg0Z%;b*gGOC!%mo(=>RJs00{YqsGp>ajCQTo;5W|Z_Nhsa!NEa4aHJ4g zwU6dY=LR}qsDW44L4LUWqGErL*d~d42-C@8u{!yhihhH}t*nI3WePr~JwznwQTF%b zq2yWlm0IR)bLAdfF!4IZ!X9Jk<5PTGL%IC(JckXox3>qwX7@RqyD37@nbSwTNy+IA zq<8JEaxg&YQ|mNJGHrtteTGxLs;++JhCvn#nmS&J*6f;Q$O?XY%z|b5ZCgtzRmy#{ z^66w~X6_35fMiA=ooRu$33VIUX-D&_p;~x($vr3J*)!epi-TqQ^tDoMms#-1q6>IM zT;o&+#f)@&y}dp=xY;!1{vxM4<5cc^j=;2Z;IKx1&s*7Sp`M66#}R|#flvGW#Lbaj ziDoA2MCkC7w*H~x@7wPGZmwPMCn=9)ig^-?jk37$GhgDAZy!5J-)-?NFssQp+ zuIm$RIeqAu?|h5m-bxqy9q8szW~JnXI=AleA1yU>FYTLGFom`jpKd9oK@#3MJf{DAuEj1r*+DItbKWWMrD~$T;qJqHv>SvUkSrf^Ga-mOvujy$8DDiiYh_7QKW_fK1|FX?j(kfw+=TFz-fm8$-?4aB;ksc=82 zx||azjKp@^ss9?h9<_R&{XWXZXLhxzVc03}m(Zq`p{wcwzE%sARe8kez8PvNrQ=3t zor3wIK$Gn%wqlK|i8%aQ`e(yXvOBR_--bpD*znVx%mvCnhJU5!v;+}OsBHDS*(DM^ zXn$Q5eLdVOq*JOTGUu}G7%AhSQBsV)R0u8`I|Tb&Wxj?0XMOajqn>pn42d_VBrit1 zFpw{69I?}Ns2b3^Bwm7ZR)9Dx{82HJGA%7wUAl5p(ZL7^>OXbRmZjv*Ra6 zY(Po(+4g|=VI6_O{>3lI3Bj?NW2OmY2}-w>j_-DA{OpgT8IT2h-aDqVncHKxafW|` zp~%yudm`%UeWrImCQw9nRJ+H;GE)7ZnCxES)7HI5mB##_Nc3V#^6fOVnlt*u<H}ZllwAh0Ka*zPT7YdQLphI{Gzo#AU?boT6~eD$@wCGGnbK!#yhz zNg&x3Mu?|;y{KFJfJ^1f>dR|u6qf>T3j@W7!{WP7AtLcp8qccR+U~WtzZeM%`!?~h o;Us;q!HUvr$byH37?TLU_*S9#_)UUT%E868wQ@w&Tln4hFFz3kD*ylh diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/psd/collection.psd b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/psd/collection.psd deleted file mode 100644 index 7eb7caf2cc5c98e1d9ac04de50b2009b344bbe85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25792 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 diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/psd/copy document.psd b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/psd/copy document.psd deleted file mode 100644 index ca207adc608751b51eb5265bda2b7392889305a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 104729 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 -z29Kyi71;_?78Nx8T@Kyi71;_?8+RhFJ*IS|(R zV!bqG3$m6Y9v^AF3fsCthdKb8O7xR9@|=69)Pv@ zipvA2wFHaH1F+0cad`k1%V$L^SsYCE>MAY|kaByTV_~l1@&H?95wPO&04xw!Tpoa> z30Ww-xI6&-vA8?{E7KL12XJIxhvM=8tj8()?Xbs4ae06YhIVmzfQT&i>J^sWuE$@eH70Ukd8Mek_dn}y|5LtL%HXcf`TY{_z%vJ`nxdfXp%Oz3E zke;z{b+Hc|`__Og)Ixrv&xX$IR`^vOop z(RFdz*IZwxwr-QXd2kiTOO9-Z$=1QdM%mL#b|6ZzxnFU4fFyg*;aw~)53r35>x;_+ z$hJ_z9b{+uSlZ}jx1FRHW;3FoeJdL(>2`$u?-ns`eZ20^Pn;Xg=BKD?7eb_|G4etGJ=9#Tg zww_^^NcOr)T49SR3IP*Q*u$DlpP23d^>+luR@q;W*$V72BU>Y?rFYrBhwYjIwtZsz zCgyn-mj@^=4^UhlptwAM6LdUZTppmfJV0@Ifa3B1#pMBt%L5db2k7n%vol|>7|-mA z$xg0G_F|PSx7nB0^Lu){cn_P(vT0!ow*s4n%l@Kv8n-~AH`yL_66_hsPKM~+GG~|> z4Z4|#$&RD4AKzp8)*kkR#3|sg>#WWG=#Y^SVn|;FfM3B zH+ybJEFwmRnWQOfk;oKh!^r4tTg#@!?1(8fF(ulM~Upj z%#RU`2H(-GsOg3~LTuw1V;@Ff%Pk{pM7hu8^k~#h>l@hhol326G9G0=^T4~0eUaG> z*zaWv$Fvp3f^(7)wg?a6>~k+w6qg4mE)P -z29Kyi71;_?8+*%Z6t|PmTavb|hC)Rb}+9$jh zUzN*Ixyz2X$f&J7`s$HgS+c&ld+bY2Jhb+?hZC7!zO{Q)?xN#uGEG?f>Z`kU?cTlX zmoLA%`?aHcCcL(LSD%wuyKCKpvCOMS_l)ho-gboSC2Of>Bx$d{y8EbP-@R+s4sY#& zSDt-P@00BP*X>8iK3+=S{Tlgq@7a4~&+qMhZTD-h?pV8L&x_CeB>B>7dj?%^C3|-G zTAK3qYuP<-?LE4uWbdnQtsU{R)b727i;_{=SYAHPTTX?#oe z?mMz~)T^o31BsXR9?e~jkW1CF*Y@l=x_8WOO5DGH-`>4%@6YZ#x_9JjnYHm3_v9{y zb3xXT^l$H8ZkFsnaCE=6@96&Qfh@VRd$W5kSQ~wD@BaM<4p0V#?Aap^3|~wBqx-V^ z-#(!2e=DDN#GbX0o%`O-Uky_k8PUDN_w7Hh|IGtO50aVYj$GTjv#Kh8QIfBo+NGR> zM-RPypk#k8*Gp9oRUOG)scfU^JzY6KN!bIX2M-45o=$24QL+F4jwr?;`PI?zj62| znf5IkQw0?BRgO??%jFq!=)j>j4pKb@l~hq?pgckORcbVjzE*M%?H_Z`?%m)7ojY=H#DRSWUO({GzHy+*?t3CTs+KLQdVAkUbw$>a zd!T}`d&qiF!R6?oEJMgp?j1+=C_t1(%Jp!rd0^r_6iwOt_Nxbu(2@_+PspOG>Ymt3 zj3oKh^=_4aEcqFyy#OBI9MS|t`)Leh#(2hFmel=Y)OGHG59AsTUJITGY!B=MO0ON= zJwZnF#eMnfw|A?)js+&tBKmuD|1dCH#`Ap-Rqa&>O1coQG+evq=&q98djQcMAV|)t z|JTmhw6qa~aeQ}u+mK?sRm~inCkjrG$h$JmjBepPB#8^J6bjepimz|Pva2rw;U-Ya<8F7}WWxDaRy%g6YR1Cz6u_c5n-7CdBD+Op~Jm<^x+-e@3@ zLUNmN!F=cNOeAUE-^sDSg2S1gc7V35AVjYTPguY2yg$X|I+^9Gy(l=$_nFd)nD?L& zD4z(<8S|T|8oB+DLvK6yhN<#*yr*$`(To|IPW*o7!&&`zihVQNw{^#Q{pRhhW3%+BO3g{ z1*jg!AKbKv27jny5H0>-W{Kp$pYk&Y{@`au{K3vH{<`?dHq+&-EWf7Eqp4*XG9HJJ&2 zlmdTnGBf_*XFmKXNA>uFpF;=So?waRT|Ik5Wh&{`8}d5jTbTEXKB1l5I;%lm4WzC znrjFrQYHVxr)d|l47+R>OXcB_(XsJ~$*Gyyx%q{~rR9~?_-T1{WqE0FVSa9QW@>U` Td~9@NxLhjQ$40|Q%*gl&pqPNb diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/psd/file_types.psd b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/psd/file_types.psd deleted file mode 100644 index 0f280ad520ae61fc8bf3f6d0781d878c28e62a73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1090645 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%0D!*7gJ&xXg{)WOAPknlyJ>Sq1bGcb~zFrJobK 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 NIbZ(A+IS-N{{g?^6wUwu diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/psd/printer.psd b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/psd/printer.psd deleted file mode 100644 index 8c33f7aaadcb2e4932ec714f29a6717f05de2bd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 119952 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 RPsfkiB~w3|a8^@5`)|Mpx1|68 diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/xls.png b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/xls.png deleted file mode 100644 index 5aaf40d0e3ebe984398903d89d11cf9bc21385c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1641 zcmb7^dpy&77{`BDv)GzzO&$7?vb|XDW#u*@w{hBBMyY<7SuS&#>*Tr#-4H5`LnI;B zmXcUmvE)c3Md*H+qC*m8Xa4HnbDr1hd3~Sf`~5uc*XQ;9`zng}=m$V`xusnP~UCz6N&FfuyqK{b)~8Y~AYi^2?M#Y53FfEdIKpn=Ze5S>P* zK|zU8BATUCWU(qDx_Np+bif8AK|wT-8397lDo`8ckPsLa8^egBfx*mZkP;5XgfJrL z(lKf!;{Lgr^N(DGa^|GW`y(#0A?z?{;wAct7G4#lRH*%zrC!&?*2zZ`n49F z(qH`OY)|w|cpmUmBC(M(IV`I@_pX0g@=ffMjdZ%#l`2`8dDi;gCc9r&x@m$Kii^XSXk@g6-y6x-KkUF zbLSB7;04aKx$6gPD&s^rMghVurr7Th$sz&8W)1WGRRKBmw#0N87Nz1OT*2n5ZD>fo zKI1RhHK{VOjK3u0ss2qE_N(A`YT+*FeIz2H8a-V!bvbF|2M0sLFD_tSTRvjHw^a)4 zz?rN#^jP1=TpP)YvF^D!SZ7$0Vk&Z`hJIB}Bbi)J$8aiYn1CG@TcECk$#^GRUuaNd z{H5KjPz`CTN|Q?xLay4oaK$y#rzNSjac$w1xInk=`&{)>tsWVx#aqWaOCKH`ODyXr z?3F9UIpMT9zy#Z+oWCN(!Jyq*PCan>jeqE)Y~BB7JwX6|iRpO(%I@ zkF-km@8*tO|4b>VmX8Gx$m}d=vFv>!8uaq|m3yPk&>-QB5qZe$O`;aQ&yMEn5+3&L zl<4Tm*NcMhHe4-idY-twO1z5;VVlnwzpUEooS`;N$nr>C7`YY7Zd1$j7ds7&an|si znfQVsH7qje%!P?!_DuXr@+2Xw_l0kHjTHwz zR?o4<+V%Qfedn*ZcA5`_^8JQl#N2G+($cR^D-oiF0(Imq$2%8{f0!PN%gRmbyI5v1 zNZFcQiEWQ)8zaG5s0K*jSHRn3>o(abPNv>;-BWS0Vo|xzRU6ho->Ie<)dke-xckbW4_P(#*&2PUn=OE667#nV zY!G0)S;B%Z2!_{(Q&u-?^{XR2w$)DMb+J9Ls@;=EZ{xpcf-nL66T0|S5v3@$q#8bc zgm2KX45yF!jC_Y{%=Q{Cd>k=8+jr@$!NA;MORWuE-R*-RY7c`=6)&@W-aZ-B7V}1; z{@4S}og<@`0ivkXU2k@KN{FW{l+PB?R17uEL(DdM%gXTFws}|0`}CxN1)F@{?xRWO zgvIEJ&6X9*vW{N!C0y8Ir(qa79T2!|*I8&nHFnTc-(Yt*<6piT&6xlI diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/xls_hover.png b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/images/xls_hover.png deleted file mode 100644 index 5b1930afd86e7ef439026ee89e75520c086471f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2061 zcmb7_dpHvcAIFzDGtT5v)~3lUrG?ta>SWj$DJBfDl-bNoW0{#vB)8o1qI#?0xK-YC zKaNWzm-M2@r94F{&E!&N?+A^#?47^Pzwi5dp5ODkf1dAuzie->Bf2`qIsgDb_ozF` zR~?Nv91H>gHhm^12C5597LiN@0NxD`9S^io*KodTAfLhw=f{Wgm;hoJm(D~UjS6Kk zeVL(QqL`OVyn5--#uCxf&o7h(a6*zp!pfU z1Us6gp0Pp`*wIWg&w@fdH3PfBaN(X6@z|^gKA;-;x9K8nFqq$vbP@76N6q0O{nTQ@ zxbd-P*gxx7#p+`hwqGEh#EfO|*k}1{ZnU}u0LX8){=ZrdIboA@B@8SrE&t=&@>EgKRCFayrwil0e$CWJqi^?abQ*Jk03(ocH9G} z-QDG&0DwN>Qg`@c*txQxZ}L~BM$@ZlDd;{~!~&~Wc#%8Sf_;p^VzDJQo$#TtwViT8te0B1+9Jb!J|Fq^wJ;9f! z@p9Pt)undKn!9z!&6WMImgJSq^(M#*u)$|Q;HANTH>}U|Y;9MUmh(e-b$dFVDoo)M z6B96TDPH*`?Yhi94JM2-xb*4;-%mN>oH9({g2%9rIpgH|ALF=P`y{P@2Zkh1YB`WZ9?#;XSZLD;`7zeHM zI((Revkmpq0i`XFKmJ+SrA1EwPjk=&R28G#-ZZnz09P<28kPx2D1%?I?{pgFG*|4d zMtGAq_m)NDgNL2eXDz!bZlX<{H}@}8-f;Cve;K9>%b&nA=^9&H8XT|Ri^&nNXxaa~ zbBFE0bQoIyxqaw_Tei~enm$i+^4bY&F?wl7aN&$$yu$EPaV@Yp*LD`X<#46FU$%+BHgE%rP0%o7sbuXs#96$LL;&$VRPm19mTT6Vp7QSdeg1;$H z{{H=I=T-Z*_rqfcitVo6V-_x<|cPN6Mw=sJBxECJf& za>b7@n-TK&IOV~Un&`!ukio)a3+t6EA4cHljj{p8=y%lmjmqxvsrzj^1%RF;n?93! z<^Gk#?hXtjqNsj!YQC;ZI``)gsb3frumEhGiEW&$uI9u>2L|TEJfjhe${O_Gp9}QHtn)HN8wODZZQdRWnTydB|GGYF~xTqhZ_G0w}$z2b?CL>1nN{iSl2 zDy(-{W4{z>uM!u`Q)zDW+km){p|7&gydO(55GHq@q1{a$(v{B-z^+N0Fru0oRBW2{_7|YRA zd#MbS;H9Tm^}Rg`znr+OmHa#3ojTK6gtKz8D`^5AQIu$`1mx9-sfmMQDKtl%aWg2H+sFotDXNROl$ ax&mAY+hYDES`WPO$d0;tksdnJQvVCjG_(i+ diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/TableTools.js b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/TableTools.js deleted file mode 100644 index 3c3ae9115..000000000 --- a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/TableTools.js +++ /dev/null @@ -1,2476 +0,0 @@ -/* - * File: TableTools.js - * Version: 2.1.5 - * Description: Tools and buttons for DataTables - * Author: Allan Jardine (www.sprymedia.co.uk) - * Language: Javascript - * License: GPL v2 or BSD 3 point style - * Project: DataTables - * - * Copyright 2009-2013 Allan Jardine, all rights reserved. - * - * This source file is free software, under either the GPL v2 license or a - * BSD style license, available at: - * http://datatables.net/license_gpl2 - * http://datatables.net/license_bsd - */ - -/* Global scope for TableTools */ -var TableTools; - -(function($, window, document) { - -/** - * TableTools provides flexible buttons and other tools for a DataTables enhanced table - * @class TableTools - * @constructor - * @param {Object} oDT DataTables instance - * @param {Object} oOpts TableTools options - * @param {String} oOpts.sSwfPath ZeroClipboard SWF path - * @param {String} oOpts.sRowSelect Row selection options - 'none', 'single' or 'multi' - * @param {Function} oOpts.fnPreRowSelect Callback function just prior to row selection - * @param {Function} oOpts.fnRowSelected Callback function just after row selection - * @param {Function} oOpts.fnRowDeselected Callback function when row is deselected - * @param {Array} oOpts.aButtons List of buttons to be used - */ -TableTools = function( oDT, oOpts ) -{ - /* Santiy check that we are a new instance */ - if ( ! this instanceof TableTools ) - { - alert( "Warning: TableTools must be initialised with the keyword 'new'" ); - } - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public class variables - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - /** - * @namespace Settings object which contains customisable information for TableTools instance - */ - this.s = { - /** - * Store 'this' so the instance can be retrieved from the settings object - * @property that - * @type object - * @default this - */ - "that": this, - - /** - * DataTables settings objects - * @property dt - * @type object - * @default From the oDT init option - */ - "dt": oDT.fnSettings(), - - /** - * @namespace Print specific information - */ - "print": { - /** - * DataTables draw 'start' point before the printing display was shown - * @property saveStart - * @type int - * @default -1 - */ - "saveStart": -1, - - /** - * DataTables draw 'length' point before the printing display was shown - * @property saveLength - * @type int - * @default -1 - */ - "saveLength": -1, - - /** - * Page scrolling point before the printing display was shown so it can be restored - * @property saveScroll - * @type int - * @default -1 - */ - "saveScroll": -1, - - /** - * Wrapped function to end the print display (to maintain scope) - * @property funcEnd - * @type Function - * @default function () {} - */ - "funcEnd": function () {} - }, - - /** - * A unique ID is assigned to each button in each instance - * @property buttonCounter - * @type int - * @default 0 - */ - "buttonCounter": 0, - - /** - * @namespace Select rows specific information - */ - "select": { - /** - * Select type - can be 'none', 'single' or 'multi' - * @property type - * @type string - * @default "" - */ - "type": "", - - /** - * Array of nodes which are currently selected - * @property selected - * @type array - * @default [] - */ - "selected": [], - - /** - * Function to run before the selection can take place. Will cancel the select if the - * function returns false - * @property preRowSelect - * @type Function - * @default null - */ - "preRowSelect": null, - - /** - * Function to run when a row is selected - * @property postSelected - * @type Function - * @default null - */ - "postSelected": null, - - /** - * Function to run when a row is deselected - * @property postDeselected - * @type Function - * @default null - */ - "postDeselected": null, - - /** - * Indicate if all rows are selected (needed for server-side processing) - * @property all - * @type boolean - * @default false - */ - "all": false, - - /** - * Class name to add to selected TR nodes - * @property selectedClass - * @type String - * @default "" - */ - "selectedClass": "" - }, - - /** - * Store of the user input customisation object - * @property custom - * @type object - * @default {} - */ - "custom": {}, - - /** - * SWF movie path - * @property swfPath - * @type string - * @default "" - */ - "swfPath": "", - - /** - * Default button set - * @property buttonSet - * @type array - * @default [] - */ - "buttonSet": [], - - /** - * When there is more than one TableTools instance for a DataTable, there must be a - * master which controls events (row selection etc) - * @property master - * @type boolean - * @default false - */ - "master": false, - - /** - * Tag names that are used for creating collections and buttons - * @namesapce - */ - "tags": {} - }; - - - /** - * @namespace Common and useful DOM elements for the class instance - */ - this.dom = { - /** - * DIV element that is create and all TableTools buttons (and their children) put into - * @property container - * @type node - * @default null - */ - "container": null, - - /** - * The table node to which TableTools will be applied - * @property table - * @type node - * @default null - */ - "table": null, - - /** - * @namespace Nodes used for the print display - */ - "print": { - /** - * Nodes which have been removed from the display by setting them to display none - * @property hidden - * @type array - * @default [] - */ - "hidden": [], - - /** - * The information display saying telling the user about the print display - * @property message - * @type node - * @default null - */ - "message": null - }, - - /** - * @namespace Nodes used for a collection display. This contains the currently used collection - */ - "collection": { - /** - * The div wrapper containing the buttons in the collection (i.e. the menu) - * @property collection - * @type node - * @default null - */ - "collection": null, - - /** - * Background display to provide focus and capture events - * @property background - * @type node - * @default null - */ - "background": null - } - }; - - /** - * @namespace Name space for the classes that this TableTools instance will use - * @extends TableTools.classes - */ - this.classes = $.extend( true, {}, TableTools.classes ); - if ( this.s.dt.bJUI ) - { - $.extend( true, this.classes, TableTools.classes_themeroller ); - } - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public class methods - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - /** - * Retreieve the settings object from an instance - * @method fnSettings - * @returns {object} TableTools settings object - */ - this.fnSettings = function () { - return this.s; - }; - - - /* Constructor logic */ - if ( typeof oOpts == 'undefined' ) - { - oOpts = {}; - } - - this._fnConstruct( oOpts ); - - return this; -}; - - - -TableTools.prototype = { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - /** - * Retreieve the settings object from an instance - * @returns {array} List of TR nodes which are currently selected - * @param {boolean} [filtered=false] Get only selected rows which are - * available given the filtering applied to the table. By default - * this is false - i.e. all rows, regardless of filtering are - selected. - */ - "fnGetSelected": function ( filtered ) - { - var - out = [], - data = this.s.dt.aoData, - displayed = this.s.dt.aiDisplay, - i, iLen; - - if ( filtered ) - { - // Only consider filtered rows - for ( i=0, iLen=displayed.length ; i 0 ) - { - sTitle = anTitle[0].innerHTML; - } - } - - /* Strip characters which the OS will object to - checking for UTF8 support in the scripting - * engine - */ - if ( "\u00A1".toString().length < 4 ) { - return sTitle.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, ""); - } else { - return sTitle.replace(/[^a-zA-Z0-9_\.,\-_ !\(\)]/g, ""); - } - }, - - - /** - * Calculate a unity array with the column width by proportion for a set of columns to be - * included for a button. This is particularly useful for PDF creation, where we can use the - * column widths calculated by the browser to size the columns in the PDF. - * @param {Object} oConfig Button configuration object - * @returns {Array} Unity array of column ratios - */ - "fnCalcColRatios": function ( oConfig ) - { - var - aoCols = this.s.dt.aoColumns, - aColumnsInc = this._fnColumnTargets( oConfig.mColumns ), - aColWidths = [], - iWidth = 0, iTotal = 0, i, iLen; - - for ( i=0, iLen=aColumnsInc.length ; i

Copied '+len+' row'+plural+' to the clipboard.

', - 1500 - ); - } - } ), - - "pdf": $.extend( {}, TableTools.buttonBase, { - "sAction": "flash_pdf", - "sNewLine": "\n", - "sFileName": "*.pdf", - "sButtonClass": "DTTT_button_pdf", - "sButtonText": "PDF", - "sPdfOrientation": "portrait", - "sPdfSize": "A4", - "sPdfMessage": "", - "fnClick": function( nButton, oConfig, flash ) { - this.fnSetText( flash, - "title:"+ this.fnGetTitle(oConfig) +"\n"+ - "message:"+ oConfig.sPdfMessage +"\n"+ - "colWidth:"+ this.fnCalcColRatios(oConfig) +"\n"+ - "orientation:"+ oConfig.sPdfOrientation +"\n"+ - "size:"+ oConfig.sPdfSize +"\n"+ - "--/TableToolsOpts--\n" + - this.fnGetTableData(oConfig) - ); - } - } ), - - "print": $.extend( {}, TableTools.buttonBase, { - "sInfo": "
Print view

Please use your browser's print function to "+ - "print this table. Press escape when finished.", - "sMessage": null, - "bShowAll": true, - "sToolTip": "View print view", - "sButtonClass": "DTTT_button_print", - "sButtonText": "Print", - "fnClick": function ( nButton, oConfig ) { - this.fnPrint( true, oConfig ); - } - } ), - - "text": $.extend( {}, TableTools.buttonBase ), - - "select": $.extend( {}, TableTools.buttonBase, { - "sButtonText": "Select button", - "fnSelect": function( nButton, oConfig ) { - if ( this.fnGetSelected().length !== 0 ) { - $(nButton).removeClass( this.classes.buttons.disabled ); - } else { - $(nButton).addClass( this.classes.buttons.disabled ); - } - }, - "fnInit": function( nButton, oConfig ) { - $(nButton).addClass( this.classes.buttons.disabled ); - } - } ), - - "select_single": $.extend( {}, TableTools.buttonBase, { - "sButtonText": "Select button", - "fnSelect": function( nButton, oConfig ) { - var iSelected = this.fnGetSelected().length; - if ( iSelected == 1 ) { - $(nButton).removeClass( this.classes.buttons.disabled ); - } else { - $(nButton).addClass( this.classes.buttons.disabled ); - } - }, - "fnInit": function( nButton, oConfig ) { - $(nButton).addClass( this.classes.buttons.disabled ); - } - } ), - - "select_all": $.extend( {}, TableTools.buttonBase, { - "sButtonText": "Select all", - "fnClick": function( nButton, oConfig ) { - this.fnSelectAll(); - }, - "fnSelect": function( nButton, oConfig ) { - if ( this.fnGetSelected().length == this.s.dt.fnRecordsDisplay() ) { - $(nButton).addClass( this.classes.buttons.disabled ); - } else { - $(nButton).removeClass( this.classes.buttons.disabled ); - } - } - } ), - - "select_none": $.extend( {}, TableTools.buttonBase, { - "sButtonText": "Deselect all", - "fnClick": function( nButton, oConfig ) { - this.fnSelectNone(); - }, - "fnSelect": function( nButton, oConfig ) { - if ( this.fnGetSelected().length !== 0 ) { - $(nButton).removeClass( this.classes.buttons.disabled ); - } else { - $(nButton).addClass( this.classes.buttons.disabled ); - } - }, - "fnInit": function( nButton, oConfig ) { - $(nButton).addClass( this.classes.buttons.disabled ); - } - } ), - - "ajax": $.extend( {}, TableTools.buttonBase, { - "sAjaxUrl": "/xhr.php", - "sButtonText": "Ajax button", - "fnClick": function( nButton, oConfig ) { - var sData = this.fnGetTableData(oConfig); - $.ajax( { - "url": oConfig.sAjaxUrl, - "data": [ - { "name": "tableData", "value": sData } - ], - "success": oConfig.fnAjaxComplete, - "dataType": "json", - "type": "POST", - "cache": false, - "error": function () { - alert( "Error detected when sending table data to server" ); - } - } ); - }, - "fnAjaxComplete": function( json ) { - alert( 'Ajax complete' ); - } - } ), - - "div": $.extend( {}, TableTools.buttonBase, { - "sAction": "div", - "sTag": "div", - "sButtonClass": "DTTT_nonbutton", - "sButtonText": "Text button" - } ), - - "collection": $.extend( {}, TableTools.buttonBase, { - "sAction": "collection", - "sButtonClass": "DTTT_button_collection", - "sButtonText": "Collection", - "fnClick": function( nButton, oConfig ) { - this._fnCollectionShow(nButton, oConfig); - } - } ) -}; -/* - * on* callback parameters: - * 1. node - button element - * 2. object - configuration object for this button - * 3. object - ZeroClipboard reference (flash button only) - * 4. string - Returned string from Flash (flash button only - and only on 'complete') - */ - - - -/** - * @namespace Classes used by TableTools - allows the styles to be override easily. - * Note that when TableTools initialises it will take a copy of the classes object - * and will use its internal copy for the remainder of its run time. - */ -TableTools.classes = { - "container": "DTTT_container", - "buttons": { - "normal": "DTTT_button", - "disabled": "DTTT_disabled" - }, - "collection": { - "container": "DTTT_collection", - "background": "DTTT_collection_background", - "buttons": { - "normal": "DTTT_button", - "disabled": "DTTT_disabled" - } - }, - "select": { - "table": "DTTT_selectable", - "row": "DTTT_selected" - }, - "print": { - "body": "DTTT_Print", - "info": "DTTT_print_info", - "message": "DTTT_PrintMessage" - } -}; - - -/** - * @namespace ThemeRoller classes - built in for compatibility with DataTables' - * bJQueryUI option. - */ -TableTools.classes_themeroller = { - "container": "DTTT_container ui-buttonset ui-buttonset-multi", - "buttons": { - "normal": "DTTT_button ui-button ui-state-default" - }, - "collection": { - "container": "DTTT_collection ui-buttonset ui-buttonset-multi" - } -}; - - -/** - * @namespace TableTools default settings for initialisation - */ -TableTools.DEFAULTS = { - "sSwfPath": "media/swf/copy_csv_xls_pdf.swf", - "sRowSelect": "none", - "sSelectedClass": null, - "fnPreRowSelect": null, - "fnRowSelected": null, - "fnRowDeselected": null, - "aButtons": [ "copy", "csv", "xls", "pdf", "print" ], - "oTags": { - "container": "div", - "button": "a", // We really want to use buttons here, but Firefox and IE ignore the - // click on the Flash element in the button (but not mouse[in|out]). - "liner": "span", - "collection": { - "container": "div", - "button": "a", - "liner": "span" - } - } -}; - - -/** - * Name of this class - * @constant CLASS - * @type String - * @default TableTools - */ -TableTools.prototype.CLASS = "TableTools"; - - -/** - * TableTools version - * @constant VERSION - * @type String - * @default See code - */ -TableTools.VERSION = "2.1.5"; -TableTools.prototype.VERSION = TableTools.VERSION; - - - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Initialisation - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/* - * Register a new feature with DataTables - */ -if ( typeof $.fn.dataTable == "function" && - typeof $.fn.dataTableExt.fnVersionCheck == "function" && - $.fn.dataTableExt.fnVersionCheck('1.9.0') ) -{ - $.fn.dataTableExt.aoFeatures.push( { - "fnInit": function( oDTSettings ) { - var oOpts = typeof oDTSettings.oInit.oTableTools != 'undefined' ? - oDTSettings.oInit.oTableTools : {}; - - var oTT = new TableTools( oDTSettings.oInstance, oOpts ); - TableTools._aInstances.push( oTT ); - - return oTT.dom.container; - }, - "cFeature": "T", - "sFeature": "TableTools" - } ); -} -else -{ - alert( "Warning: TableTools 2 requires DataTables 1.9.0 or newer - www.datatables.net/download"); -} - -$.fn.DataTable.TableTools = TableTools; - -})(jQuery, window, document); diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/TableTools.min.js b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/TableTools.min.js deleted file mode 100644 index e580c9936..000000000 --- a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/TableTools.min.js +++ /dev/null @@ -1,77 +0,0 @@ -// Simple Set Clipboard System -// Author: Joseph Huckaby -var ZeroClipboard_TableTools={version:"1.0.4-TableTools2",clients:{},moviePath:"",nextId:1,$:function(a){"string"==typeof a&&(a=document.getElementById(a));a.addClass||(a.hide=function(){this.style.display="none"},a.show=function(){this.style.display=""},a.addClass=function(a){this.removeClass(a);this.className+=" "+a},a.removeClass=function(a){this.className=this.className.replace(RegExp("\\s*"+a+"\\s*")," ").replace(/^\s+/,"").replace(/\s+$/,"")},a.hasClass=function(a){return!!this.className.match(RegExp("\\s*"+ -a+"\\s*"))});return a},setMoviePath:function(a){this.moviePath=a},dispatch:function(a,b,c){(a=this.clients[a])&&a.receiveEvent(b,c)},register:function(a,b){this.clients[a]=b},getDOMObjectPosition:function(a){var b={left:0,top:0,width:a.width?a.width:a.offsetWidth,height:a.height?a.height:a.offsetHeight};""!=a.style.width&&(b.width=a.style.width.replace("px",""));""!=a.style.height&&(b.height=a.style.height.replace("px",""));for(;a;)b.left+=a.offsetLeft,b.top+=a.offsetTop,a=a.offsetParent;return b}, -Client:function(a){this.handlers={};this.id=ZeroClipboard_TableTools.nextId++;this.movieId="ZeroClipboard_TableToolsMovie_"+this.id;ZeroClipboard_TableTools.register(this.id,this);a&&this.glue(a)}}; -ZeroClipboard_TableTools.Client.prototype={id:0,ready:!1,movie:null,clipText:"",fileName:"",action:"copy",handCursorEnabled:!0,cssEffects:!0,handlers:null,sized:!1,glue:function(a,b){this.domElement=ZeroClipboard_TableTools.$(a);var c=99;this.domElement.style.zIndex&&(c=parseInt(this.domElement.style.zIndex)+1);var d=ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement);this.div=document.createElement("div");var e=this.div.style;e.position="absolute";e.left="0px";e.top="0px";e.width=d.width+ -"px";e.height=d.height+"px";e.zIndex=c;"undefined"!=typeof b&&""!=b&&(this.div.title=b);0!=d.width&&0!=d.height&&(this.sized=!0);this.domElement&&(this.domElement.appendChild(this.div),this.div.innerHTML=this.getHTML(d.width,d.height))},positionElement:function(){var a=ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement),b=this.div.style;b.position="absolute";b.width=a.width+"px";b.height=a.height+"px";0!=a.width&&0!=a.height&&(this.sized=!0,b=this.div.childNodes[0],b.width=a.width,b.height= -a.height)},getHTML:function(a,b){var c="",d="id="+this.id+"&width="+a+"&height="+b;if(navigator.userAgent.match(/MSIE/))var e=location.href.match(/^https/i)?"https://":"http://",c=c+('');else c+='';return c},hide:function(){this.div&&(this.div.style.left="-2000px")},show:function(){this.reposition()},destroy:function(){if(this.domElement&&this.div){this.hide();this.div.innerHTML="";var a=document.getElementsByTagName("body")[0];try{a.removeChild(this.div)}catch(b){}this.div=this.domElement=null}},reposition:function(a){a&&((this.domElement=ZeroClipboard_TableTools.$(a))||this.hide());if(this.domElement&&this.div){var a=ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement), -b=this.div.style;b.left=""+a.left+"px";b.top=""+a.top+"px"}},clearText:function(){this.clipText="";this.ready&&this.movie.clearText()},appendText:function(a){this.clipText+=a;this.ready&&this.movie.appendText(a)},setText:function(a){this.clipText=a;this.ready&&this.movie.setText(a)},setCharSet:function(a){this.charSet=a;this.ready&&this.movie.setCharSet(a)},setBomInc:function(a){this.incBom=a;this.ready&&this.movie.setBomInc(a)},setFileName:function(a){this.fileName=a;this.ready&&this.movie.setFileName(a)}, -setAction:function(a){this.action=a;this.ready&&this.movie.setAction(a)},addEventListener:function(a,b){a=a.toString().toLowerCase().replace(/^on/,"");this.handlers[a]||(this.handlers[a]=[]);this.handlers[a].push(b)},setHandCursor:function(a){this.handCursorEnabled=a;this.ready&&this.movie.setHandCursor(a)},setCSSEffects:function(a){this.cssEffects=!!a},receiveEvent:function(a,b){a=a.toString().toLowerCase().replace(/^on/,"");switch(a){case "load":this.movie=document.getElementById(this.movieId); -if(!this.movie){var c=this;setTimeout(function(){c.receiveEvent("load",null)},1);return}if(!this.ready&&navigator.userAgent.match(/Firefox/)&&navigator.userAgent.match(/Windows/)){c=this;setTimeout(function(){c.receiveEvent("load",null)},100);this.ready=!0;return}this.ready=!0;this.movie.clearText();this.movie.appendText(this.clipText);this.movie.setFileName(this.fileName);this.movie.setAction(this.action);this.movie.setCharSet(this.charSet);this.movie.setBomInc(this.incBom);this.movie.setHandCursor(this.handCursorEnabled); -break;case "mouseover":this.domElement&&this.cssEffects&&this.recoverActive&&this.domElement.addClass("active");break;case "mouseout":this.domElement&&this.cssEffects&&(this.recoverActive=!1,this.domElement.hasClass("active")&&(this.domElement.removeClass("active"),this.recoverActive=!0));break;case "mousedown":this.domElement&&this.cssEffects&&this.domElement.addClass("active");break;case "mouseup":this.domElement&&this.cssEffects&&(this.domElement.removeClass("active"),this.recoverActive=!1)}if(this.handlers[a])for(var d= -0,e=this.handlers[a].length;d"\u00a1".toString().length?b.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g,""):b.replace(/[^a-zA-Z0-9_\.,\-_ !\(\)]/g,"")},fnCalcColRatios:function(a){var b=this.s.dt.aoColumns, -a=this._fnColumnTargets(a.mColumns),c=[],d=0,f=0,e,g;e=0;for(g=a.length;eh?m:h)+"px";l.style.width=(k>o?k:o)+"px";l.className=this.classes.collection.background;e(l).css("opacity",0);g.body.appendChild(l);g.body.appendChild(f);m=e(f).outerWidth();k=e(f).outerHeight(); -j+m>o&&(f.style.left=o-m+"px");d+k>h&&(f.style.top=d-k-e(a).outerHeight()+"px");this.dom.collection.collection=f;this.dom.collection.background=l;setTimeout(function(){e(f).animate({opacity:1},500);e(l).animate({opacity:0.25},500)},10);this.fnResizeButtons();e(l).click(function(){c._fnCollectionHide.call(c,null,null)})},_fnCollectionHide:function(a,b){!(null!==b&&"collection"==b.sExtends)&&null!==this.dom.collection.collection&&(e(this.dom.collection.collection).animate({opacity:0},500,function(){this.style.display= -"none"}),e(this.dom.collection.background).animate({opacity:0},500,function(){this.parentNode.removeChild(this)}),this.dom.collection.collection=null,this.dom.collection.background=null)},_fnRowSelectConfig:function(){if(this.s.master){var a=this,b=this.s.dt;e(b.nTable).addClass(this.classes.select.table);e(b.nTBody).on("click.DTTT_Select","tr",function(c){this.parentNode==b.nTBody&&null!==b.oInstance.fnGetData(this)&&(a.fnIsSelected(this)?a._fnRowDeselect(this,c):"single"==a.s.select.type?(a.fnSelectNone(), -a._fnRowSelect(this,c)):"multi"==a.s.select.type&&a._fnRowSelect(this,c))});b.oApi._fnCallbackReg(b,"aoRowCreatedCallback",function(c,d,f){b.aoData[f]._DTTT_selected&&e(c).addClass(a.classes.select.row)},"TableTools-SelectAll")}},_fnRowSelect:function(a,b){var c=this._fnSelectData(a),d=[],f,j;f=0;for(j=c.length;f/g, -"").replace(/^\s+|\s+$/g,""),h=this._fnHtmlDecode(h),j.push(this._fnBoundData(h,a.sFieldBoundary,l)));g.push(j.join(a.sFieldSeperator))}var p=k.aiDisplay;f=this.fnGetSelected();if("none"!==this.s.select.type&&d&&0!==f.length){p=[];b=0;for(c=f.length;b]+)).*?>/gi,"$1$2$3"),h=h.replace(/<.*?>/g,"")):h+="",h=h.replace(/^\s+/,"").replace(/\s+$/,""),h=this._fnHtmlDecode(h),j.push(this._fnBoundData(h,a.sFieldBoundary,l)));g.push(j.join(a.sFieldSeperator));a.bOpenRows&&(b=e.grep(k.aoOpenRows,function(a){return a.nParent===o}),1===b.length&&(h=this._fnBoundData(e("td",b[0].nTr).html(),a.sFieldBoundary,l),g.push(h)))}if(a.bFooter&&null!==k.nTFoot){j=[];b=0;for(c=k.aoColumns.length;b< -c;b++)n[b]&&null!==k.aoColumns[b].nTf&&(h=k.aoColumns[b].nTf.innerHTML.replace(/\n/g," ").replace(/<.*?>/g,""),h=this._fnHtmlDecode(h),j.push(this._fnBoundData(h,a.sFieldBoundary,l)));g.push(j.join(a.sFieldSeperator))}return _sLastData=g.join(this._fnNewline(a))},_fnBoundData:function(a,b,c){return""===b?a:b+a.replace(c,b+b)+b},_fnChunkData:function(a,b){for(var c=[],d=a.length,f=0;fTable copied

Copied "+a+" row"+(1==a?"":"s")+ -" to the clipboard.

",1500)}}),pdf:e.extend({},TableTools.buttonBase,{sAction:"flash_pdf",sNewLine:"\n",sFileName:"*.pdf",sButtonClass:"DTTT_button_pdf",sButtonText:"PDF",sPdfOrientation:"portrait",sPdfSize:"A4",sPdfMessage:"",fnClick:function(a,b,c){this.fnSetText(c,"title:"+this.fnGetTitle(b)+"\nmessage:"+b.sPdfMessage+"\ncolWidth:"+this.fnCalcColRatios(b)+"\norientation:"+b.sPdfOrientation+"\nsize:"+b.sPdfSize+"\n--/TableToolsOpts--\n"+this.fnGetTableData(b))}}),print:e.extend({},TableTools.buttonBase, -{sInfo:"
Print view

Please use your browser's print function to print this table. Press escape when finished.",sMessage:null,bShowAll:!0,sToolTip:"View print view",sButtonClass:"DTTT_button_print",sButtonText:"Print",fnClick:function(a,b){this.fnPrint(!0,b)}}),text:e.extend({},TableTools.buttonBase),select:e.extend({},TableTools.buttonBase,{sButtonText:"Select button",fnSelect:function(a){0!==this.fnGetSelected().length?e(a).removeClass(this.classes.buttons.disabled):e(a).addClass(this.classes.buttons.disabled)}, -fnInit:function(a){e(a).addClass(this.classes.buttons.disabled)}}),select_single:e.extend({},TableTools.buttonBase,{sButtonText:"Select button",fnSelect:function(a){1==this.fnGetSelected().length?e(a).removeClass(this.classes.buttons.disabled):e(a).addClass(this.classes.buttons.disabled)},fnInit:function(a){e(a).addClass(this.classes.buttons.disabled)}}),select_all:e.extend({},TableTools.buttonBase,{sButtonText:"Select all",fnClick:function(){this.fnSelectAll()},fnSelect:function(a){this.fnGetSelected().length== -this.s.dt.fnRecordsDisplay()?e(a).addClass(this.classes.buttons.disabled):e(a).removeClass(this.classes.buttons.disabled)}}),select_none:e.extend({},TableTools.buttonBase,{sButtonText:"Deselect all",fnClick:function(){this.fnSelectNone()},fnSelect:function(a){0!==this.fnGetSelected().length?e(a).removeClass(this.classes.buttons.disabled):e(a).addClass(this.classes.buttons.disabled)},fnInit:function(a){e(a).addClass(this.classes.buttons.disabled)}}),ajax:e.extend({},TableTools.buttonBase,{sAjaxUrl:"/xhr.php", -sButtonText:"Ajax button",fnClick:function(a,b){var c=this.fnGetTableData(b);e.ajax({url:b.sAjaxUrl,data:[{name:"tableData",value:c}],success:b.fnAjaxComplete,dataType:"json",type:"POST",cache:!1,error:function(){alert("Error detected when sending table data to server")}})},fnAjaxComplete:function(){alert("Ajax complete")}}),div:e.extend({},TableTools.buttonBase,{sAction:"div",sTag:"div",sButtonClass:"DTTT_nonbutton",sButtonText:"Text button"}),collection:e.extend({},TableTools.buttonBase,{sAction:"collection", -sButtonClass:"DTTT_button_collection",sButtonText:"Collection",fnClick:function(a,b){this._fnCollectionShow(a,b)}})};TableTools.classes={container:"DTTT_container",buttons:{normal:"DTTT_button",disabled:"DTTT_disabled"},collection:{container:"DTTT_collection",background:"DTTT_collection_background",buttons:{normal:"DTTT_button",disabled:"DTTT_disabled"}},select:{table:"DTTT_selectable",row:"DTTT_selected"},print:{body:"DTTT_Print",info:"DTTT_print_info",message:"DTTT_PrintMessage"}};TableTools.classes_themeroller= -{container:"DTTT_container ui-buttonset ui-buttonset-multi",buttons:{normal:"DTTT_button ui-button ui-state-default"},collection:{container:"DTTT_collection ui-buttonset ui-buttonset-multi"}};TableTools.DEFAULTS={sSwfPath:"media/swf/copy_csv_xls_pdf.swf",sRowSelect:"none",sSelectedClass:null,fnPreRowSelect:null,fnRowSelected:null,fnRowDeselected:null,aButtons:["copy","csv","xls","pdf","print"],oTags:{container:"div",button:"a",liner:"span",collection:{container:"div",button:"a",liner:"span"}}};TableTools.prototype.CLASS= -"TableTools";TableTools.VERSION="2.1.5";TableTools.prototype.VERSION=TableTools.VERSION;"function"==typeof e.fn.dataTable&&"function"==typeof e.fn.dataTableExt.fnVersionCheck&&e.fn.dataTableExt.fnVersionCheck("1.9.0")?e.fn.dataTableExt.aoFeatures.push({fnInit:function(a){a=new TableTools(a.oInstance,"undefined"!=typeof a.oInit.oTableTools?a.oInit.oTableTools:{});TableTools._aInstances.push(a);return a.dom.container},cFeature:"T",sFeature:"TableTools"}):alert("Warning: TableTools 2 requires DataTables 1.9.0 or newer - www.datatables.net/download"); -e.fn.DataTable.TableTools=TableTools})(jQuery,window,document); diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/TableTools.min.js.gz b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/TableTools.min.js.gz deleted file mode 100644 index 01ce7c041bcdb1df5cec281dd4dc639362beda8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8785 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 diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/ZeroClipboard.js b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/ZeroClipboard.js deleted file mode 100644 index de0f6b67b..000000000 --- a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/js/ZeroClipboard.js +++ /dev/null @@ -1,367 +0,0 @@ -// Simple Set Clipboard System -// Author: Joseph Huckaby - -var ZeroClipboard_TableTools = { - - version: "1.0.4-TableTools2", - clients: {}, // registered upload clients on page, indexed by id - moviePath: '', // URL to movie - nextId: 1, // ID of next movie - - $: function(thingy) { - // simple DOM lookup utility function - if (typeof(thingy) == 'string') thingy = document.getElementById(thingy); - if (!thingy.addClass) { - // extend element with a few useful methods - thingy.hide = function() { this.style.display = 'none'; }; - thingy.show = function() { this.style.display = ''; }; - thingy.addClass = function(name) { this.removeClass(name); this.className += ' ' + name; }; - thingy.removeClass = function(name) { - this.className = this.className.replace( new RegExp("\\s*" + name + "\\s*"), " ").replace(/^\s+/, '').replace(/\s+$/, ''); - }; - thingy.hasClass = function(name) { - return !!this.className.match( new RegExp("\\s*" + name + "\\s*") ); - } - } - return thingy; - }, - - setMoviePath: function(path) { - // set path to ZeroClipboard.swf - this.moviePath = path; - }, - - dispatch: function(id, eventName, args) { - // receive event from flash movie, send to client - var client = this.clients[id]; - if (client) { - client.receiveEvent(eventName, args); - } - }, - - register: function(id, client) { - // register new client to receive events - this.clients[id] = client; - }, - - getDOMObjectPosition: function(obj) { - // get absolute coordinates for dom element - var info = { - left: 0, - top: 0, - width: obj.width ? obj.width : obj.offsetWidth, - height: obj.height ? obj.height : obj.offsetHeight - }; - - if ( obj.style.width != "" ) - info.width = obj.style.width.replace("px",""); - - if ( obj.style.height != "" ) - info.height = obj.style.height.replace("px",""); - - while (obj) { - info.left += obj.offsetLeft; - info.top += obj.offsetTop; - obj = obj.offsetParent; - } - - return info; - }, - - Client: function(elem) { - // constructor for new simple upload client - this.handlers = {}; - - // unique ID - this.id = ZeroClipboard_TableTools.nextId++; - this.movieId = 'ZeroClipboard_TableToolsMovie_' + this.id; - - // register client with singleton to receive flash events - ZeroClipboard_TableTools.register(this.id, this); - - // create movie - if (elem) this.glue(elem); - } -}; - -ZeroClipboard_TableTools.Client.prototype = { - - id: 0, // unique ID for us - ready: false, // whether movie is ready to receive events or not - movie: null, // reference to movie object - clipText: '', // text to copy to clipboard - fileName: '', // default file save name - action: 'copy', // action to perform - handCursorEnabled: true, // whether to show hand cursor, or default pointer cursor - cssEffects: true, // enable CSS mouse effects on dom container - handlers: null, // user event handlers - sized: false, - - glue: function(elem, title) { - // glue to DOM element - // elem can be ID or actual DOM element object - this.domElement = ZeroClipboard_TableTools.$(elem); - - // float just above object, or zIndex 99 if dom element isn't set - var zIndex = 99; - if (this.domElement.style.zIndex) { - zIndex = parseInt(this.domElement.style.zIndex) + 1; - } - - // find X/Y position of domElement - var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement); - - // create floating DIV above element - this.div = document.createElement('div'); - var style = this.div.style; - style.position = 'absolute'; - style.left = '0px'; - style.top = '0px'; - style.width = (box.width) + 'px'; - style.height = box.height + 'px'; - style.zIndex = zIndex; - - if ( typeof title != "undefined" && title != "" ) { - this.div.title = title; - } - if ( box.width != 0 && box.height != 0 ) { - this.sized = true; - } - - // style.backgroundColor = '#f00'; // debug - if ( this.domElement ) { - this.domElement.appendChild(this.div); - this.div.innerHTML = this.getHTML( box.width, box.height ); - } - }, - - positionElement: function() { - var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement); - var style = this.div.style; - - style.position = 'absolute'; - //style.left = (this.domElement.offsetLeft)+'px'; - //style.top = this.domElement.offsetTop+'px'; - style.width = box.width + 'px'; - style.height = box.height + 'px'; - - if ( box.width != 0 && box.height != 0 ) { - this.sized = true; - } else { - return; - } - - var flash = this.div.childNodes[0]; - flash.width = box.width; - flash.height = box.height; - }, - - getHTML: function(width, height) { - // return HTML for movie - var html = ''; - var flashvars = 'id=' + this.id + - '&width=' + width + - '&height=' + height; - - if (navigator.userAgent.match(/MSIE/)) { - // IE gets an OBJECT tag - var protocol = location.href.match(/^https/i) ? 'https://' : 'http://'; - html += ''; - } - else { - // all other browsers get an EMBED tag - html += ''; - } - return html; - }, - - hide: function() { - // temporarily hide floater offscreen - if (this.div) { - this.div.style.left = '-2000px'; - } - }, - - show: function() { - // show ourselves after a call to hide() - this.reposition(); - }, - - destroy: function() { - // destroy control and floater - if (this.domElement && this.div) { - this.hide(); - this.div.innerHTML = ''; - - var body = document.getElementsByTagName('body')[0]; - try { body.removeChild( this.div ); } catch(e) {;} - - this.domElement = null; - this.div = null; - } - }, - - reposition: function(elem) { - // reposition our floating div, optionally to new container - // warning: container CANNOT change size, only position - if (elem) { - this.domElement = ZeroClipboard_TableTools.$(elem); - if (!this.domElement) this.hide(); - } - - if (this.domElement && this.div) { - var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement); - var style = this.div.style; - style.left = '' + box.left + 'px'; - style.top = '' + box.top + 'px'; - } - }, - - clearText: function() { - // clear the text to be copy / saved - this.clipText = ''; - if (this.ready) this.movie.clearText(); - }, - - appendText: function(newText) { - // append text to that which is to be copied / saved - this.clipText += newText; - if (this.ready) { this.movie.appendText(newText) ;} - }, - - setText: function(newText) { - // set text to be copied to be copied / saved - this.clipText = newText; - if (this.ready) { this.movie.setText(newText) ;} - }, - - setCharSet: function(charSet) { - // set the character set (UTF16LE or UTF8) - this.charSet = charSet; - if (this.ready) { this.movie.setCharSet(charSet) ;} - }, - - setBomInc: function(bomInc) { - // set if the BOM should be included or not - this.incBom = bomInc; - if (this.ready) { this.movie.setBomInc(bomInc) ;} - }, - - setFileName: function(newText) { - // set the file name - this.fileName = newText; - if (this.ready) this.movie.setFileName(newText); - }, - - setAction: function(newText) { - // set action (save or copy) - this.action = newText; - if (this.ready) this.movie.setAction(newText); - }, - - addEventListener: function(eventName, func) { - // add user event listener for event - // event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel - eventName = eventName.toString().toLowerCase().replace(/^on/, ''); - if (!this.handlers[eventName]) this.handlers[eventName] = []; - this.handlers[eventName].push(func); - }, - - setHandCursor: function(enabled) { - // enable hand cursor (true), or default arrow cursor (false) - this.handCursorEnabled = enabled; - if (this.ready) this.movie.setHandCursor(enabled); - }, - - setCSSEffects: function(enabled) { - // enable or disable CSS effects on DOM container - this.cssEffects = !!enabled; - }, - - receiveEvent: function(eventName, args) { - // receive event from flash - eventName = eventName.toString().toLowerCase().replace(/^on/, ''); - - // special behavior for certain events - switch (eventName) { - case 'load': - // movie claims it is ready, but in IE this isn't always the case... - // bug fix: Cannot extend EMBED DOM elements in Firefox, must use traditional function - this.movie = document.getElementById(this.movieId); - if (!this.movie) { - var self = this; - setTimeout( function() { self.receiveEvent('load', null); }, 1 ); - return; - } - - // firefox on pc needs a "kick" in order to set these in certain cases - if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) { - var self = this; - setTimeout( function() { self.receiveEvent('load', null); }, 100 ); - this.ready = true; - return; - } - - this.ready = true; - this.movie.clearText(); - this.movie.appendText( this.clipText ); - this.movie.setFileName( this.fileName ); - this.movie.setAction( this.action ); - this.movie.setCharSet( this.charSet ); - this.movie.setBomInc( this.incBom ); - this.movie.setHandCursor( this.handCursorEnabled ); - break; - - case 'mouseover': - if (this.domElement && this.cssEffects) { - //this.domElement.addClass('hover'); - if (this.recoverActive) this.domElement.addClass('active'); - } - break; - - case 'mouseout': - if (this.domElement && this.cssEffects) { - this.recoverActive = false; - if (this.domElement.hasClass('active')) { - this.domElement.removeClass('active'); - this.recoverActive = true; - } - //this.domElement.removeClass('hover'); - } - break; - - case 'mousedown': - if (this.domElement && this.cssEffects) { - this.domElement.addClass('active'); - } - break; - - case 'mouseup': - if (this.domElement && this.cssEffects) { - this.domElement.removeClass('active'); - this.recoverActive = false; - } - break; - } // switch eventName - - if (this.handlers[eventName]) { - for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) { - var func = this.handlers[eventName][idx]; - - if (typeof(func) == 'function') { - // actual function reference - func(this, args); - } - else if ((typeof(func) == 'object') && (func.length == 2)) { - // PHP style object + method, i.e. [myObject, 'myMethod'] - func[0][ func[1] ](this, args); - } - else if (typeof(func) == 'string') { - // name of function - window[func](this, args); - } - } // foreach event handler defined - } // user defined handler for event - } - -}; diff --git a/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/swf/copy_csv_xls.swf b/airtime_mvc/public/js/datatables/plugin/TableTools-2.1.5/swf/copy_csv_xls.swf deleted file mode 100644 index 082c7acbfd0e1368e3a1ef4b10a218bb1dbe7d60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2165 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≧q1$SHJKl= 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 From 18d567c1fa6b5e6c06c4784a831972310a06ebe3 Mon Sep 17 00:00:00 2001 From: Zachary Klosko Date: Wed, 30 Dec 2020 22:35:45 -0500 Subject: [PATCH 02/17] Removing uncalled scripts --- airtime_mvc/public/js/libs/angular.js | 28688 ---------------- .../public/js/libs/jquery-migrate-1.2.1.js | 521 - 2 files changed, 29209 deletions(-) delete mode 100644 airtime_mvc/public/js/libs/angular.js delete mode 100644 airtime_mvc/public/js/libs/jquery-migrate-1.2.1.js diff --git a/airtime_mvc/public/js/libs/angular.js b/airtime_mvc/public/js/libs/angular.js deleted file mode 100644 index a6aafd5b3..000000000 --- a/airtime_mvc/public/js/libs/angular.js +++ /dev/null @@ -1,28688 +0,0 @@ -/** - * @license AngularJS v1.4.5 - * (c) 2010-2015 Google, Inc. http://angularjs.org - * License: MIT - */ -(function(window, document, undefined) {'use strict'; - -/** - * @description - * - * This object provides a utility for producing rich Error messages within - * Angular. It can be called as follows: - * - * var exampleMinErr = minErr('example'); - * throw exampleMinErr('one', 'This {0} is {1}', foo, bar); - * - * The above creates an instance of minErr in the example namespace. The - * resulting error will have a namespaced error code of example.one. The - * resulting error will replace {0} with the value of foo, and {1} with the - * value of bar. The object is not restricted in the number of arguments it can - * take. - * - * If fewer arguments are specified than necessary for interpolation, the extra - * interpolation markers will be preserved in the final string. - * - * Since data will be parsed statically during a build step, some restrictions - * are applied with respect to how minErr instances are created and called. - * Instances should have names of the form namespaceMinErr for a minErr created - * using minErr('namespace') . Error codes, namespaces and template strings - * should all be static strings, not variables or general expressions. - * - * @param {string} module The namespace to use for the new minErr instance. - * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning - * error from returned function, for cases when a particular type of error is useful. - * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance - */ - -function minErr(module, ErrorConstructor) { - ErrorConstructor = ErrorConstructor || Error; - return function() { - var SKIP_INDEXES = 2; - - var templateArgs = arguments, - code = templateArgs[0], - message = '[' + (module ? module + ':' : '') + code + '] ', - template = templateArgs[1], - paramPrefix, i; - - message += template.replace(/\{\d+\}/g, function(match) { - var index = +match.slice(1, -1), - shiftedIndex = index + SKIP_INDEXES; - - if (shiftedIndex < templateArgs.length) { - return toDebugString(templateArgs[shiftedIndex]); - } - - return match; - }); - - message += '\nhttp://errors.angularjs.org/1.4.5/' + - (module ? module + '/' : '') + code; - - for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') { - message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' + - encodeURIComponent(toDebugString(templateArgs[i])); - } - - return new ErrorConstructor(message); - }; -} - -/* We need to tell jshint what variables are being exported */ -/* global angular: true, - msie: true, - jqLite: true, - jQuery: true, - slice: true, - splice: true, - push: true, - toString: true, - ngMinErr: true, - angularModule: true, - uid: true, - REGEX_STRING_REGEXP: true, - VALIDITY_STATE_PROPERTY: true, - - lowercase: true, - uppercase: true, - manualLowercase: true, - manualUppercase: true, - nodeName_: true, - isArrayLike: true, - forEach: true, - forEachSorted: true, - reverseParams: true, - nextUid: true, - setHashKey: true, - extend: true, - toInt: true, - inherit: true, - merge: true, - noop: true, - identity: true, - valueFn: true, - isUndefined: true, - isDefined: true, - isObject: true, - isBlankObject: true, - isString: true, - isNumber: true, - isDate: true, - isArray: true, - isFunction: true, - isRegExp: true, - isWindow: true, - isScope: true, - isFile: true, - isFormData: true, - isBlob: true, - isBoolean: true, - isPromiseLike: true, - trim: true, - escapeForRegexp: true, - isElement: true, - makeMap: true, - includes: true, - arrayRemove: true, - copy: true, - shallowCopy: true, - equals: true, - csp: true, - jq: true, - concat: true, - sliceArgs: true, - bind: true, - toJsonReplacer: true, - toJson: true, - fromJson: true, - convertTimezoneToLocal: true, - timezoneToOffset: true, - startingTag: true, - tryDecodeURIComponent: true, - parseKeyValue: true, - toKeyValue: true, - encodeUriSegment: true, - encodeUriQuery: true, - angularInit: true, - bootstrap: true, - getTestability: true, - snake_case: true, - bindJQuery: true, - assertArg: true, - assertArgFn: true, - assertNotHasOwnProperty: true, - getter: true, - getBlockNodes: true, - hasOwnProperty: true, - createMap: true, - - NODE_TYPE_ELEMENT: true, - NODE_TYPE_ATTRIBUTE: true, - NODE_TYPE_TEXT: true, - NODE_TYPE_COMMENT: true, - NODE_TYPE_DOCUMENT: true, - NODE_TYPE_DOCUMENT_FRAGMENT: true, -*/ - -//////////////////////////////////// - -/** - * @ngdoc module - * @name ng - * @module ng - * @description - * - * # ng (core module) - * The ng module is loaded by default when an AngularJS application is started. The module itself - * contains the essential components for an AngularJS application to function. The table below - * lists a high level breakdown of each of the services/factories, filters, directives and testing - * components available within this core module. - * - *

- */ - -var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/; - -// The name of a form control's ValidityState property. -// This is used so that it's possible for internal tests to create mock ValidityStates. -var VALIDITY_STATE_PROPERTY = 'validity'; - -/** - * @ngdoc function - * @name angular.lowercase - * @module ng - * @kind function - * - * @description Converts the specified string to lowercase. - * @param {string} string String to be converted to lowercase. - * @returns {string} Lowercased string. - */ -var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;}; -var hasOwnProperty = Object.prototype.hasOwnProperty; - -/** - * @ngdoc function - * @name angular.uppercase - * @module ng - * @kind function - * - * @description Converts the specified string to uppercase. - * @param {string} string String to be converted to uppercase. - * @returns {string} Uppercased string. - */ -var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;}; - - -var manualLowercase = function(s) { - /* jshint bitwise: false */ - return isString(s) - ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) - : s; -}; -var manualUppercase = function(s) { - /* jshint bitwise: false */ - return isString(s) - ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) - : s; -}; - - -// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish -// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods -// with correct but slower alternatives. -if ('i' !== 'I'.toLowerCase()) { - lowercase = manualLowercase; - uppercase = manualUppercase; -} - - -var - msie, // holds major version number for IE, or NaN if UA is not IE. - jqLite, // delay binding since jQuery could be loaded after us. - jQuery, // delay binding - slice = [].slice, - splice = [].splice, - push = [].push, - toString = Object.prototype.toString, - getPrototypeOf = Object.getPrototypeOf, - ngMinErr = minErr('ng'), - - /** @name angular */ - angular = window.angular || (window.angular = {}), - angularModule, - uid = 0; - -/** - * documentMode is an IE-only property - * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx - */ -msie = document.documentMode; - - -/** - * @private - * @param {*} obj - * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, - * String ...) - */ -function isArrayLike(obj) { - if (obj == null || isWindow(obj)) { - return false; - } - - // Support: iOS 8.2 (not reproducible in simulator) - // "length" in obj used to prevent JIT error (gh-11508) - var length = "length" in Object(obj) && obj.length; - - if (obj.nodeType === NODE_TYPE_ELEMENT && length) { - return true; - } - - return isString(obj) || isArray(obj) || length === 0 || - typeof length === 'number' && length > 0 && (length - 1) in obj; -} - -/** - * @ngdoc function - * @name angular.forEach - * @module ng - * @kind function - * - * @description - * Invokes the `iterator` function once for each item in `obj` collection, which can be either an - * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value` - * is the value of an object property or an array element, `key` is the object property key or - * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional. - * - * It is worth noting that `.forEach` does not iterate over inherited properties because it filters - * using the `hasOwnProperty` method. - * - * Unlike ES262's - * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18), - * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just - * return the value provided. - * - ```js - var values = {name: 'misko', gender: 'male'}; - var log = []; - angular.forEach(values, function(value, key) { - this.push(key + ': ' + value); - }, log); - expect(log).toEqual(['name: misko', 'gender: male']); - ``` - * - * @param {Object|Array} obj Object to iterate over. - * @param {Function} iterator Iterator function. - * @param {Object=} context Object to become context (`this`) for the iterator function. - * @returns {Object|Array} Reference to `obj`. - */ - -function forEach(obj, iterator, context) { - var key, length; - if (obj) { - if (isFunction(obj)) { - for (key in obj) { - // Need to check if hasOwnProperty exists, - // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function - if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) { - iterator.call(context, obj[key], key, obj); - } - } - } else if (isArray(obj) || isArrayLike(obj)) { - var isPrimitive = typeof obj !== 'object'; - for (key = 0, length = obj.length; key < length; key++) { - if (isPrimitive || key in obj) { - iterator.call(context, obj[key], key, obj); - } - } - } else if (obj.forEach && obj.forEach !== forEach) { - obj.forEach(iterator, context, obj); - } else if (isBlankObject(obj)) { - // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty - for (key in obj) { - iterator.call(context, obj[key], key, obj); - } - } else if (typeof obj.hasOwnProperty === 'function') { - // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed - for (key in obj) { - if (obj.hasOwnProperty(key)) { - iterator.call(context, obj[key], key, obj); - } - } - } else { - // Slow path for objects which do not have a method `hasOwnProperty` - for (key in obj) { - if (hasOwnProperty.call(obj, key)) { - iterator.call(context, obj[key], key, obj); - } - } - } - } - return obj; -} - -function forEachSorted(obj, iterator, context) { - var keys = Object.keys(obj).sort(); - for (var i = 0; i < keys.length; i++) { - iterator.call(context, obj[keys[i]], keys[i]); - } - return keys; -} - - -/** - * when using forEach the params are value, key, but it is often useful to have key, value. - * @param {function(string, *)} iteratorFn - * @returns {function(*, string)} - */ -function reverseParams(iteratorFn) { - return function(value, key) { iteratorFn(key, value); }; -} - -/** - * A consistent way of creating unique IDs in angular. - * - * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before - * we hit number precision issues in JavaScript. - * - * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M - * - * @returns {number} an unique alpha-numeric string - */ -function nextUid() { - return ++uid; -} - - -/** - * Set or clear the hashkey for an object. - * @param obj object - * @param h the hashkey (!truthy to delete the hashkey) - */ -function setHashKey(obj, h) { - if (h) { - obj.$$hashKey = h; - } else { - delete obj.$$hashKey; - } -} - - -function baseExtend(dst, objs, deep) { - var h = dst.$$hashKey; - - for (var i = 0, ii = objs.length; i < ii; ++i) { - var obj = objs[i]; - if (!isObject(obj) && !isFunction(obj)) continue; - var keys = Object.keys(obj); - for (var j = 0, jj = keys.length; j < jj; j++) { - var key = keys[j]; - var src = obj[key]; - - if (deep && isObject(src)) { - if (isDate(src)) { - dst[key] = new Date(src.valueOf()); - } else if (isRegExp(src)) { - dst[key] = new RegExp(src); - } else { - if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {}; - baseExtend(dst[key], [src], true); - } - } else { - dst[key] = src; - } - } - } - - setHashKey(dst, h); - return dst; -} - -/** - * @ngdoc function - * @name angular.extend - * @module ng - * @kind function - * - * @description - * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s) - * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so - * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`. - * - * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use - * {@link angular.merge} for this. - * - * @param {Object} dst Destination object. - * @param {...Object} src Source object(s). - * @returns {Object} Reference to `dst`. - */ -function extend(dst) { - return baseExtend(dst, slice.call(arguments, 1), false); -} - - -/** -* @ngdoc function -* @name angular.merge -* @module ng -* @kind function -* -* @description -* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s) -* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so -* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`. -* -* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source -* objects, performing a deep copy. -* -* @param {Object} dst Destination object. -* @param {...Object} src Source object(s). -* @returns {Object} Reference to `dst`. -*/ -function merge(dst) { - return baseExtend(dst, slice.call(arguments, 1), true); -} - - - -function toInt(str) { - return parseInt(str, 10); -} - - -function inherit(parent, extra) { - return extend(Object.create(parent), extra); -} - -/** - * @ngdoc function - * @name angular.noop - * @module ng - * @kind function - * - * @description - * A function that performs no operations. This function can be useful when writing code in the - * functional style. - ```js - function foo(callback) { - var result = calculateResult(); - (callback || angular.noop)(result); - } - ``` - */ -function noop() {} -noop.$inject = []; - - -/** - * @ngdoc function - * @name angular.identity - * @module ng - * @kind function - * - * @description - * A function that returns its first argument. This function is useful when writing code in the - * functional style. - * - ```js - function transformer(transformationFn, value) { - return (transformationFn || angular.identity)(value); - }; - ``` - * @param {*} value to be returned. - * @returns {*} the value passed in. - */ -function identity($) {return $;} -identity.$inject = []; - - -function valueFn(value) {return function() {return value;};} - -function hasCustomToString(obj) { - return isFunction(obj.toString) && obj.toString !== Object.prototype.toString; -} - - -/** - * @ngdoc function - * @name angular.isUndefined - * @module ng - * @kind function - * - * @description - * Determines if a reference is undefined. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is undefined. - */ -function isUndefined(value) {return typeof value === 'undefined';} - - -/** - * @ngdoc function - * @name angular.isDefined - * @module ng - * @kind function - * - * @description - * Determines if a reference is defined. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is defined. - */ -function isDefined(value) {return typeof value !== 'undefined';} - - -/** - * @ngdoc function - * @name angular.isObject - * @module ng - * @kind function - * - * @description - * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not - * considered to be objects. Note that JavaScript arrays are objects. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is an `Object` but not `null`. - */ -function isObject(value) { - // http://jsperf.com/isobject4 - return value !== null && typeof value === 'object'; -} - - -/** - * Determine if a value is an object with a null prototype - * - * @returns {boolean} True if `value` is an `Object` with a null prototype - */ -function isBlankObject(value) { - return value !== null && typeof value === 'object' && !getPrototypeOf(value); -} - - -/** - * @ngdoc function - * @name angular.isString - * @module ng - * @kind function - * - * @description - * Determines if a reference is a `String`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `String`. - */ -function isString(value) {return typeof value === 'string';} - - -/** - * @ngdoc function - * @name angular.isNumber - * @module ng - * @kind function - * - * @description - * Determines if a reference is a `Number`. - * - * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`. - * - * If you wish to exclude these then you can use the native - * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite) - * method. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `Number`. - */ -function isNumber(value) {return typeof value === 'number';} - - -/** - * @ngdoc function - * @name angular.isDate - * @module ng - * @kind function - * - * @description - * Determines if a value is a date. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `Date`. - */ -function isDate(value) { - return toString.call(value) === '[object Date]'; -} - - -/** - * @ngdoc function - * @name angular.isArray - * @module ng - * @kind function - * - * @description - * Determines if a reference is an `Array`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is an `Array`. - */ -var isArray = Array.isArray; - -/** - * @ngdoc function - * @name angular.isFunction - * @module ng - * @kind function - * - * @description - * Determines if a reference is a `Function`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `Function`. - */ -function isFunction(value) {return typeof value === 'function';} - - -/** - * Determines if a value is a regular expression object. - * - * @private - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `RegExp`. - */ -function isRegExp(value) { - return toString.call(value) === '[object RegExp]'; -} - - -/** - * Checks if `obj` is a window object. - * - * @private - * @param {*} obj Object to check - * @returns {boolean} True if `obj` is a window obj. - */ -function isWindow(obj) { - return obj && obj.window === obj; -} - - -function isScope(obj) { - return obj && obj.$evalAsync && obj.$watch; -} - - -function isFile(obj) { - return toString.call(obj) === '[object File]'; -} - - -function isFormData(obj) { - return toString.call(obj) === '[object FormData]'; -} - - -function isBlob(obj) { - return toString.call(obj) === '[object Blob]'; -} - - -function isBoolean(value) { - return typeof value === 'boolean'; -} - - -function isPromiseLike(obj) { - return obj && isFunction(obj.then); -} - - -var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/; -function isTypedArray(value) { - return TYPED_ARRAY_REGEXP.test(toString.call(value)); -} - - -var trim = function(value) { - return isString(value) ? value.trim() : value; -}; - -// Copied from: -// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021 -// Prereq: s is a string. -var escapeForRegexp = function(s) { - return s.replace(/([-()\[\]{}+?*.$\^|,:#= 0) { - array.splice(index, 1); - } - return index; -} - -/** - * @ngdoc function - * @name angular.copy - * @module ng - * @kind function - * - * @description - * Creates a deep copy of `source`, which should be an object or an array. - * - * * If no destination is supplied, a copy of the object or array is created. - * * If a destination is provided, all of its elements (for arrays) or properties (for objects) - * are deleted and then all elements/properties from the source are copied to it. - * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. - * * If `source` is identical to 'destination' an exception will be thrown. - * - * @param {*} source The source that will be used to make a copy. - * Can be any type, including primitives, `null`, and `undefined`. - * @param {(Object|Array)=} destination Destination into which the source is copied. If - * provided, must be of the same type as `source`. - * @returns {*} The copy or updated `destination`, if `destination` was specified. - * - * @example - - -
-
- Name:
- E-mail:
- Gender: male - female
- - -
-
form = {{user | json}}
-
master = {{master | json}}
-
- - -
-
- */ -function copy(source, destination, stackSource, stackDest) { - if (isWindow(source) || isScope(source)) { - throw ngMinErr('cpws', - "Can't copy! Making copies of Window or Scope instances is not supported."); - } - if (isTypedArray(destination)) { - throw ngMinErr('cpta', - "Can't copy! TypedArray destination cannot be mutated."); - } - - if (!destination) { - destination = source; - if (isObject(source)) { - var index; - if (stackSource && (index = stackSource.indexOf(source)) !== -1) { - return stackDest[index]; - } - - // TypedArray, Date and RegExp have specific copy functionality and must be - // pushed onto the stack before returning. - // Array and other objects create the base object and recurse to copy child - // objects. The array/object will be pushed onto the stack when recursed. - if (isArray(source)) { - return copy(source, [], stackSource, stackDest); - } else if (isTypedArray(source)) { - destination = new source.constructor(source); - } else if (isDate(source)) { - destination = new Date(source.getTime()); - } else if (isRegExp(source)) { - destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); - destination.lastIndex = source.lastIndex; - } else { - var emptyObject = Object.create(getPrototypeOf(source)); - return copy(source, emptyObject, stackSource, stackDest); - } - - if (stackDest) { - stackSource.push(source); - stackDest.push(destination); - } - } - } else { - if (source === destination) throw ngMinErr('cpi', - "Can't copy! Source and destination are identical."); - - stackSource = stackSource || []; - stackDest = stackDest || []; - - if (isObject(source)) { - stackSource.push(source); - stackDest.push(destination); - } - - var result, key; - if (isArray(source)) { - destination.length = 0; - for (var i = 0; i < source.length; i++) { - destination.push(copy(source[i], null, stackSource, stackDest)); - } - } else { - var h = destination.$$hashKey; - if (isArray(destination)) { - destination.length = 0; - } else { - forEach(destination, function(value, key) { - delete destination[key]; - }); - } - if (isBlankObject(source)) { - // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty - for (key in source) { - destination[key] = copy(source[key], null, stackSource, stackDest); - } - } else if (source && typeof source.hasOwnProperty === 'function') { - // Slow path, which must rely on hasOwnProperty - for (key in source) { - if (source.hasOwnProperty(key)) { - destination[key] = copy(source[key], null, stackSource, stackDest); - } - } - } else { - // Slowest path --- hasOwnProperty can't be called as a method - for (key in source) { - if (hasOwnProperty.call(source, key)) { - destination[key] = copy(source[key], null, stackSource, stackDest); - } - } - } - setHashKey(destination,h); - } - } - return destination; -} - -/** - * Creates a shallow copy of an object, an array or a primitive. - * - * Assumes that there are no proto properties for objects. - */ -function shallowCopy(src, dst) { - if (isArray(src)) { - dst = dst || []; - - for (var i = 0, ii = src.length; i < ii; i++) { - dst[i] = src[i]; - } - } else if (isObject(src)) { - dst = dst || {}; - - for (var key in src) { - if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) { - dst[key] = src[key]; - } - } - } - - return dst || src; -} - - -/** - * @ngdoc function - * @name angular.equals - * @module ng - * @kind function - * - * @description - * Determines if two objects or two values are equivalent. Supports value types, regular - * expressions, arrays and objects. - * - * Two objects or values are considered equivalent if at least one of the following is true: - * - * * Both objects or values pass `===` comparison. - * * Both objects or values are of the same type and all of their properties are equal by - * comparing them with `angular.equals`. - * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal) - * * Both values represent the same regular expression (In JavaScript, - * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual - * representation matches). - * - * During a property comparison, properties of `function` type and properties with names - * that begin with `$` are ignored. - * - * Scope and DOMWindow objects are being compared only by identify (`===`). - * - * @param {*} o1 Object or value to compare. - * @param {*} o2 Object or value to compare. - * @returns {boolean} True if arguments are equal. - */ -function equals(o1, o2) { - if (o1 === o2) return true; - if (o1 === null || o2 === null) return false; - if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN - var t1 = typeof o1, t2 = typeof o2, length, key, keySet; - if (t1 == t2) { - if (t1 == 'object') { - if (isArray(o1)) { - if (!isArray(o2)) return false; - if ((length = o1.length) == o2.length) { - for (key = 0; key < length; key++) { - if (!equals(o1[key], o2[key])) return false; - } - return true; - } - } else if (isDate(o1)) { - if (!isDate(o2)) return false; - return equals(o1.getTime(), o2.getTime()); - } else if (isRegExp(o1)) { - return isRegExp(o2) ? o1.toString() == o2.toString() : false; - } else { - if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || - isArray(o2) || isDate(o2) || isRegExp(o2)) return false; - keySet = createMap(); - for (key in o1) { - if (key.charAt(0) === '$' || isFunction(o1[key])) continue; - if (!equals(o1[key], o2[key])) return false; - keySet[key] = true; - } - for (key in o2) { - if (!(key in keySet) && - key.charAt(0) !== '$' && - o2[key] !== undefined && - !isFunction(o2[key])) return false; - } - return true; - } - } - } - return false; -} - -var csp = function() { - if (!isDefined(csp.rules)) { - - - var ngCspElement = (document.querySelector('[ng-csp]') || - document.querySelector('[data-ng-csp]')); - - if (ngCspElement) { - var ngCspAttribute = ngCspElement.getAttribute('ng-csp') || - ngCspElement.getAttribute('data-ng-csp'); - csp.rules = { - noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1), - noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1) - }; - } else { - csp.rules = { - noUnsafeEval: noUnsafeEval(), - noInlineStyle: false - }; - } - } - - return csp.rules; - - function noUnsafeEval() { - try { - /* jshint -W031, -W054 */ - new Function(''); - /* jshint +W031, +W054 */ - return false; - } catch (e) { - return true; - } - } -}; - -/** - * @ngdoc directive - * @module ng - * @name ngJq - * - * @element ANY - * @param {string=} ngJq the name of the library available under `window` - * to be used for angular.element - * @description - * Use this directive to force the angular.element library. This should be - * used to force either jqLite by leaving ng-jq blank or setting the name of - * the jquery variable under window (eg. jQuery). - * - * Since angular looks for this directive when it is loaded (doesn't wait for the - * DOMContentLoaded event), it must be placed on an element that comes before the script - * which loads angular. Also, only the first instance of `ng-jq` will be used and all - * others ignored. - * - * @example - * This example shows how to force jqLite using the `ngJq` directive to the `html` tag. - ```html - - - ... - ... - - ``` - * @example - * This example shows how to use a jQuery based library of a different name. - * The library name must be available at the top most 'window'. - ```html - - - ... - ... - - ``` - */ -var jq = function() { - if (isDefined(jq.name_)) return jq.name_; - var el; - var i, ii = ngAttrPrefixes.length, prefix, name; - for (i = 0; i < ii; ++i) { - prefix = ngAttrPrefixes[i]; - if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) { - name = el.getAttribute(prefix + 'jq'); - break; - } - } - - return (jq.name_ = name); -}; - -function concat(array1, array2, index) { - return array1.concat(slice.call(array2, index)); -} - -function sliceArgs(args, startIndex) { - return slice.call(args, startIndex || 0); -} - - -/* jshint -W101 */ -/** - * @ngdoc function - * @name angular.bind - * @module ng - * @kind function - * - * @description - * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for - * `fn`). You can supply optional `args` that are prebound to the function. This feature is also - * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as - * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application). - * - * @param {Object} self Context which `fn` should be evaluated in. - * @param {function()} fn Function to be bound. - * @param {...*} args Optional arguments to be prebound to the `fn` function call. - * @returns {function()} Function that wraps the `fn` with all the specified bindings. - */ -/* jshint +W101 */ -function bind(self, fn) { - var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; - if (isFunction(fn) && !(fn instanceof RegExp)) { - return curryArgs.length - ? function() { - return arguments.length - ? fn.apply(self, concat(curryArgs, arguments, 0)) - : fn.apply(self, curryArgs); - } - : function() { - return arguments.length - ? fn.apply(self, arguments) - : fn.call(self); - }; - } else { - // in IE, native methods are not functions so they cannot be bound (note: they don't need to be) - return fn; - } -} - - -function toJsonReplacer(key, value) { - var val = value; - - if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') { - val = undefined; - } else if (isWindow(value)) { - val = '$WINDOW'; - } else if (value && document === value) { - val = '$DOCUMENT'; - } else if (isScope(value)) { - val = '$SCOPE'; - } - - return val; -} - - -/** - * @ngdoc function - * @name angular.toJson - * @module ng - * @kind function - * - * @description - * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be - * stripped since angular uses this notation internally. - * - * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. - * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace. - * If set to an integer, the JSON output will contain that many spaces per indentation. - * @returns {string|undefined} JSON-ified string representing `obj`. - */ -function toJson(obj, pretty) { - if (typeof obj === 'undefined') return undefined; - if (!isNumber(pretty)) { - pretty = pretty ? 2 : null; - } - return JSON.stringify(obj, toJsonReplacer, pretty); -} - - -/** - * @ngdoc function - * @name angular.fromJson - * @module ng - * @kind function - * - * @description - * Deserializes a JSON string. - * - * @param {string} json JSON string to deserialize. - * @returns {Object|Array|string|number} Deserialized JSON string. - */ -function fromJson(json) { - return isString(json) - ? JSON.parse(json) - : json; -} - - -function timezoneToOffset(timezone, fallback) { - var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; - return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; -} - - -function addDateMinutes(date, minutes) { - date = new Date(date.getTime()); - date.setMinutes(date.getMinutes() + minutes); - return date; -} - - -function convertTimezoneToLocal(date, timezone, reverse) { - reverse = reverse ? -1 : 1; - var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset()); - return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset())); -} - - -/** - * @returns {string} Returns the string representation of the element. - */ -function startingTag(element) { - element = jqLite(element).clone(); - try { - // turns out IE does not let you set .html() on elements which - // are not allowed to have children. So we just ignore it. - element.empty(); - } catch (e) {} - var elemHtml = jqLite('
').append(element).html(); - try { - return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) : - elemHtml. - match(/^(<[^>]+>)/)[1]. - replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); - } catch (e) { - return lowercase(elemHtml); - } - -} - - -///////////////////////////////////////////////// - -/** - * Tries to decode the URI component without throwing an exception. - * - * @private - * @param str value potential URI component to check. - * @returns {boolean} True if `value` can be decoded - * with the decodeURIComponent function. - */ -function tryDecodeURIComponent(value) { - try { - return decodeURIComponent(value); - } catch (e) { - // Ignore any invalid uri component - } -} - - -/** - * Parses an escaped url query string into key-value pairs. - * @returns {Object.} - */ -function parseKeyValue(/**string*/keyValue) { - var obj = {}; - forEach((keyValue || "").split('&'), function(keyValue) { - var splitPoint, key, val; - if (keyValue) { - key = keyValue = keyValue.replace(/\+/g,'%20'); - splitPoint = keyValue.indexOf('='); - if (splitPoint !== -1) { - key = keyValue.substring(0, splitPoint); - val = keyValue.substring(splitPoint + 1); - } - key = tryDecodeURIComponent(key); - if (isDefined(key)) { - val = isDefined(val) ? tryDecodeURIComponent(val) : true; - if (!hasOwnProperty.call(obj, key)) { - obj[key] = val; - } else if (isArray(obj[key])) { - obj[key].push(val); - } else { - obj[key] = [obj[key],val]; - } - } - } - }); - return obj; -} - -function toKeyValue(obj) { - var parts = []; - forEach(obj, function(value, key) { - if (isArray(value)) { - forEach(value, function(arrayValue) { - parts.push(encodeUriQuery(key, true) + - (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); - }); - } else { - parts.push(encodeUriQuery(key, true) + - (value === true ? '' : '=' + encodeUriQuery(value, true))); - } - }); - return parts.length ? parts.join('&') : ''; -} - - -/** - * We need our custom method because encodeURIComponent is too aggressive and doesn't follow - * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path - * segments: - * segment = *pchar - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * pct-encoded = "%" HEXDIG HEXDIG - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ -function encodeUriSegment(val) { - return encodeUriQuery(val, true). - replace(/%26/gi, '&'). - replace(/%3D/gi, '='). - replace(/%2B/gi, '+'); -} - - -/** - * This method is intended for encoding *key* or *value* parts of query component. We need a custom - * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be - * encoded per http://tools.ietf.org/html/rfc3986: - * query = *( pchar / "/" / "?" ) - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * pct-encoded = "%" HEXDIG HEXDIG - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ -function encodeUriQuery(val, pctEncodeSpaces) { - return encodeURIComponent(val). - replace(/%40/gi, '@'). - replace(/%3A/gi, ':'). - replace(/%24/g, '$'). - replace(/%2C/gi, ','). - replace(/%3B/gi, ';'). - replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); -} - -var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-']; - -function getNgAttribute(element, ngAttr) { - var attr, i, ii = ngAttrPrefixes.length; - for (i = 0; i < ii; ++i) { - attr = ngAttrPrefixes[i] + ngAttr; - if (isString(attr = element.getAttribute(attr))) { - return attr; - } - } - return null; -} - -/** - * @ngdoc directive - * @name ngApp - * @module ng - * - * @element ANY - * @param {angular.Module} ngApp an optional application - * {@link angular.module module} name to load. - * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be - * created in "strict-di" mode. This means that the application will fail to invoke functions which - * do not use explicit function annotation (and are thus unsuitable for minification), as described - * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in - * tracking down the root of these bugs. - * - * @description - * - * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive - * designates the **root element** of the application and is typically placed near the root element - * of the page - e.g. on the `` or `` tags. - * - * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp` - * found in the document will be used to define the root element to auto-bootstrap as an - * application. To run multiple applications in an HTML document you must manually bootstrap them using - * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other. - * - * You can specify an **AngularJS module** to be used as the root module for the application. This - * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It - * should contain the application code needed or have dependencies on other modules that will - * contain the code. See {@link angular.module} for more information. - * - * In the example below if the `ngApp` directive were not placed on the `html` element then the - * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` - * would not be resolved to `3`. - * - * `ngApp` is the easiest, and most common way to bootstrap an application. - * - - -
- I can add: {{a}} + {{b}} = {{ a+b }} -
-
- - angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) { - $scope.a = 1; - $scope.b = 2; - }); - -
- * - * Using `ngStrictDi`, you would see something like this: - * - - -
-
- I can add: {{a}} + {{b}} = {{ a+b }} - -

This renders because the controller does not fail to - instantiate, by using explicit annotation style (see - script.js for details) -

-
- -
- Name:
- Hello, {{name}}! - -

This renders because the controller does not fail to - instantiate, by using explicit annotation style - (see script.js for details) -

-
- -
- I can add: {{a}} + {{b}} = {{ a+b }} - -

The controller could not be instantiated, due to relying - on automatic function annotations (which are disabled in - strict mode). As such, the content of this section is not - interpolated, and there should be an error in your web console. -

-
-
-
- - angular.module('ngAppStrictDemo', []) - // BadController will fail to instantiate, due to relying on automatic function annotation, - // rather than an explicit annotation - .controller('BadController', function($scope) { - $scope.a = 1; - $scope.b = 2; - }) - // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated, - // due to using explicit annotations using the array style and $inject property, respectively. - .controller('GoodController1', ['$scope', function($scope) { - $scope.a = 1; - $scope.b = 2; - }]) - .controller('GoodController2', GoodController2); - function GoodController2($scope) { - $scope.name = "World"; - } - GoodController2.$inject = ['$scope']; - - - div[ng-controller] { - margin-bottom: 1em; - -webkit-border-radius: 4px; - border-radius: 4px; - border: 1px solid; - padding: .5em; - } - div[ng-controller^=Good] { - border-color: #d6e9c6; - background-color: #dff0d8; - color: #3c763d; - } - div[ng-controller^=Bad] { - border-color: #ebccd1; - background-color: #f2dede; - color: #a94442; - margin-bottom: 0; - } - -
- */ -function angularInit(element, bootstrap) { - var appElement, - module, - config = {}; - - // The element `element` has priority over any other element - forEach(ngAttrPrefixes, function(prefix) { - var name = prefix + 'app'; - - if (!appElement && element.hasAttribute && element.hasAttribute(name)) { - appElement = element; - module = element.getAttribute(name); - } - }); - forEach(ngAttrPrefixes, function(prefix) { - var name = prefix + 'app'; - var candidate; - - if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) { - appElement = candidate; - module = candidate.getAttribute(name); - } - }); - if (appElement) { - config.strictDi = getNgAttribute(appElement, "strict-di") !== null; - bootstrap(appElement, module ? [module] : [], config); - } -} - -/** - * @ngdoc function - * @name angular.bootstrap - * @module ng - * @description - * Use this function to manually start up angular application. - * - * See: {@link guide/bootstrap Bootstrap} - * - * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually. - * They must use {@link ng.directive:ngApp ngApp}. - * - * Angular will detect if it has been loaded into the browser more than once and only allow the - * first loaded script to be bootstrapped and will report a warning to the browser console for - * each of the subsequent scripts. This prevents strange results in applications, where otherwise - * multiple instances of Angular try to work on the DOM. - * - * ```html - * - * - * - *
- * {{greeting}} - *
- * - * - * - * - * - * ``` - * - * @param {DOMElement} element DOM element which is the root of angular application. - * @param {Array=} modules an array of modules to load into the application. - * Each item in the array should be the name of a predefined module or a (DI annotated) - * function that will be invoked by the injector as a `config` block. - * See: {@link angular.module modules} - * @param {Object=} config an object for defining configuration options for the application. The - * following keys are supported: - * - * * `strictDi` - disable automatic function annotation for the application. This is meant to - * assist in finding bugs which break minified code. Defaults to `false`. - * - * @returns {auto.$injector} Returns the newly created injector for this app. - */ -function bootstrap(element, modules, config) { - if (!isObject(config)) config = {}; - var defaultConfig = { - strictDi: false - }; - config = extend(defaultConfig, config); - var doBootstrap = function() { - element = jqLite(element); - - if (element.injector()) { - var tag = (element[0] === document) ? 'document' : startingTag(element); - //Encode angle brackets to prevent input from being sanitized to empty string #8683 - throw ngMinErr( - 'btstrpd', - "App Already Bootstrapped with this Element '{0}'", - tag.replace(//,'>')); - } - - modules = modules || []; - modules.unshift(['$provide', function($provide) { - $provide.value('$rootElement', element); - }]); - - if (config.debugInfoEnabled) { - // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`. - modules.push(['$compileProvider', function($compileProvider) { - $compileProvider.debugInfoEnabled(true); - }]); - } - - modules.unshift('ng'); - var injector = createInjector(modules, config.strictDi); - injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', - function bootstrapApply(scope, element, compile, injector) { - scope.$apply(function() { - element.data('$injector', injector); - compile(element)(scope); - }); - }] - ); - return injector; - }; - - var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/; - var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; - - if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) { - config.debugInfoEnabled = true; - window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, ''); - } - - if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { - return doBootstrap(); - } - - window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); - angular.resumeBootstrap = function(extraModules) { - forEach(extraModules, function(module) { - modules.push(module); - }); - return doBootstrap(); - }; - - if (isFunction(angular.resumeDeferredBootstrap)) { - angular.resumeDeferredBootstrap(); - } -} - -/** - * @ngdoc function - * @name angular.reloadWithDebugInfo - * @module ng - * @description - * Use this function to reload the current application with debug information turned on. - * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`. - * - * See {@link ng.$compileProvider#debugInfoEnabled} for more. - */ -function reloadWithDebugInfo() { - window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name; - window.location.reload(); -} - -/** - * @name angular.getTestability - * @module ng - * @description - * Get the testability service for the instance of Angular on the given - * element. - * @param {DOMElement} element DOM element which is the root of angular application. - */ -function getTestability(rootElement) { - var injector = angular.element(rootElement).injector(); - if (!injector) { - throw ngMinErr('test', - 'no injector found for element argument to getTestability'); - } - return injector.get('$$testability'); -} - -var SNAKE_CASE_REGEXP = /[A-Z]/g; -function snake_case(name, separator) { - separator = separator || '_'; - return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { - return (pos ? separator : '') + letter.toLowerCase(); - }); -} - -var bindJQueryFired = false; -var skipDestroyOnNextJQueryCleanData; -function bindJQuery() { - var originalCleanData; - - if (bindJQueryFired) { - return; - } - - // bind to jQuery if present; - var jqName = jq(); - jQuery = window.jQuery; // use default jQuery. - if (isDefined(jqName)) { // `ngJq` present - jQuery = jqName === null ? undefined : window[jqName]; // if empty; use jqLite. if not empty, use jQuery specified by `ngJq`. - } - - // Use jQuery if it exists with proper functionality, otherwise default to us. - // Angular 1.2+ requires jQuery 1.7+ for on()/off() support. - // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older - // versions. It will not work for sure with jQuery <1.7, though. - if (jQuery && jQuery.fn.on) { - jqLite = jQuery; - extend(jQuery.fn, { - scope: JQLitePrototype.scope, - isolateScope: JQLitePrototype.isolateScope, - controller: JQLitePrototype.controller, - injector: JQLitePrototype.injector, - inheritedData: JQLitePrototype.inheritedData - }); - - // All nodes removed from the DOM via various jQuery APIs like .remove() - // are passed through jQuery.cleanData. Monkey-patch this method to fire - // the $destroy event on all removed nodes. - originalCleanData = jQuery.cleanData; - jQuery.cleanData = function(elems) { - var events; - if (!skipDestroyOnNextJQueryCleanData) { - for (var i = 0, elem; (elem = elems[i]) != null; i++) { - events = jQuery._data(elem, "events"); - if (events && events.$destroy) { - jQuery(elem).triggerHandler('$destroy'); - } - } - } else { - skipDestroyOnNextJQueryCleanData = false; - } - originalCleanData(elems); - }; - } else { - jqLite = JQLite; - } - - angular.element = jqLite; - - // Prevent double-proxying. - bindJQueryFired = true; -} - -/** - * throw error if the argument is falsy. - */ -function assertArg(arg, name, reason) { - if (!arg) { - throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required")); - } - return arg; -} - -function assertArgFn(arg, name, acceptArrayAnnotation) { - if (acceptArrayAnnotation && isArray(arg)) { - arg = arg[arg.length - 1]; - } - - assertArg(isFunction(arg), name, 'not a function, got ' + - (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg)); - return arg; -} - -/** - * throw error if the name given is hasOwnProperty - * @param {String} name the name to test - * @param {String} context the context in which the name is used, such as module or directive - */ -function assertNotHasOwnProperty(name, context) { - if (name === 'hasOwnProperty') { - throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context); - } -} - -/** - * Return the value accessible from the object by path. Any undefined traversals are ignored - * @param {Object} obj starting object - * @param {String} path path to traverse - * @param {boolean} [bindFnToScope=true] - * @returns {Object} value as accessible by path - */ -//TODO(misko): this function needs to be removed -function getter(obj, path, bindFnToScope) { - if (!path) return obj; - var keys = path.split('.'); - var key; - var lastInstance = obj; - var len = keys.length; - - for (var i = 0; i < len; i++) { - key = keys[i]; - if (obj) { - obj = (lastInstance = obj)[key]; - } - } - if (!bindFnToScope && isFunction(obj)) { - return bind(lastInstance, obj); - } - return obj; -} - -/** - * Return the DOM siblings between the first and last node in the given array. - * @param {Array} array like object - * @returns {jqLite} jqLite collection containing the nodes - */ -function getBlockNodes(nodes) { - // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original - // collection, otherwise update the original collection. - var node = nodes[0]; - var endNode = nodes[nodes.length - 1]; - var blockNodes = [node]; - - do { - node = node.nextSibling; - if (!node) break; - blockNodes.push(node); - } while (node !== endNode); - - return jqLite(blockNodes); -} - - -/** - * Creates a new object without a prototype. This object is useful for lookup without having to - * guard against prototypically inherited properties via hasOwnProperty. - * - * Related micro-benchmarks: - * - http://jsperf.com/object-create2 - * - http://jsperf.com/proto-map-lookup/2 - * - http://jsperf.com/for-in-vs-object-keys2 - * - * @returns {Object} - */ -function createMap() { - return Object.create(null); -} - -var NODE_TYPE_ELEMENT = 1; -var NODE_TYPE_ATTRIBUTE = 2; -var NODE_TYPE_TEXT = 3; -var NODE_TYPE_COMMENT = 8; -var NODE_TYPE_DOCUMENT = 9; -var NODE_TYPE_DOCUMENT_FRAGMENT = 11; - -/** - * @ngdoc type - * @name angular.Module - * @module ng - * @description - * - * Interface for configuring angular {@link angular.module modules}. - */ - -function setupModuleLoader(window) { - - var $injectorMinErr = minErr('$injector'); - var ngMinErr = minErr('ng'); - - function ensure(obj, name, factory) { - return obj[name] || (obj[name] = factory()); - } - - var angular = ensure(window, 'angular', Object); - - // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap - angular.$$minErr = angular.$$minErr || minErr; - - return ensure(angular, 'module', function() { - /** @type {Object.} */ - var modules = {}; - - /** - * @ngdoc function - * @name angular.module - * @module ng - * @description - * - * The `angular.module` is a global place for creating, registering and retrieving Angular - * modules. - * All modules (angular core or 3rd party) that should be available to an application must be - * registered using this mechanism. - * - * Passing one argument retrieves an existing {@link angular.Module}, - * whereas passing more than one argument creates a new {@link angular.Module} - * - * - * # Module - * - * A module is a collection of services, directives, controllers, filters, and configuration information. - * `angular.module` is used to configure the {@link auto.$injector $injector}. - * - * ```js - * // Create a new module - * var myModule = angular.module('myModule', []); - * - * // register a new service - * myModule.value('appName', 'MyCoolApp'); - * - * // configure existing services inside initialization blocks. - * myModule.config(['$locationProvider', function($locationProvider) { - * // Configure existing providers - * $locationProvider.hashPrefix('!'); - * }]); - * ``` - * - * Then you can create an injector and load your modules like this: - * - * ```js - * var injector = angular.injector(['ng', 'myModule']) - * ``` - * - * However it's more likely that you'll just use - * {@link ng.directive:ngApp ngApp} or - * {@link angular.bootstrap} to simplify this process for you. - * - * @param {!string} name The name of the module to create or retrieve. - * @param {!Array.=} requires If specified then new module is being created. If - * unspecified then the module is being retrieved for further configuration. - * @param {Function=} configFn Optional configuration function for the module. Same as - * {@link angular.Module#config Module#config()}. - * @returns {module} new module with the {@link angular.Module} api. - */ - return function module(name, requires, configFn) { - var assertNotHasOwnProperty = function(name, context) { - if (name === 'hasOwnProperty') { - throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); - } - }; - - assertNotHasOwnProperty(name, 'module'); - if (requires && modules.hasOwnProperty(name)) { - modules[name] = null; - } - return ensure(modules, name, function() { - if (!requires) { - throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " + - "the module name or forgot to load it. If registering a module ensure that you " + - "specify the dependencies as the second argument.", name); - } - - /** @type {!Array.>} */ - var invokeQueue = []; - - /** @type {!Array.} */ - var configBlocks = []; - - /** @type {!Array.} */ - var runBlocks = []; - - var config = invokeLater('$injector', 'invoke', 'push', configBlocks); - - /** @type {angular.Module} */ - var moduleInstance = { - // Private state - _invokeQueue: invokeQueue, - _configBlocks: configBlocks, - _runBlocks: runBlocks, - - /** - * @ngdoc property - * @name angular.Module#requires - * @module ng - * - * @description - * Holds the list of modules which the injector will load before the current module is - * loaded. - */ - requires: requires, - - /** - * @ngdoc property - * @name angular.Module#name - * @module ng - * - * @description - * Name of the module. - */ - name: name, - - - /** - * @ngdoc method - * @name angular.Module#provider - * @module ng - * @param {string} name service name - * @param {Function} providerType Construction function for creating new instance of the - * service. - * @description - * See {@link auto.$provide#provider $provide.provider()}. - */ - provider: invokeLaterAndSetModuleName('$provide', 'provider'), - - /** - * @ngdoc method - * @name angular.Module#factory - * @module ng - * @param {string} name service name - * @param {Function} providerFunction Function for creating new instance of the service. - * @description - * See {@link auto.$provide#factory $provide.factory()}. - */ - factory: invokeLaterAndSetModuleName('$provide', 'factory'), - - /** - * @ngdoc method - * @name angular.Module#service - * @module ng - * @param {string} name service name - * @param {Function} constructor A constructor function that will be instantiated. - * @description - * See {@link auto.$provide#service $provide.service()}. - */ - service: invokeLaterAndSetModuleName('$provide', 'service'), - - /** - * @ngdoc method - * @name angular.Module#value - * @module ng - * @param {string} name service name - * @param {*} object Service instance object. - * @description - * See {@link auto.$provide#value $provide.value()}. - */ - value: invokeLater('$provide', 'value'), - - /** - * @ngdoc method - * @name angular.Module#constant - * @module ng - * @param {string} name constant name - * @param {*} object Constant value. - * @description - * Because the constant are fixed, they get applied before other provide methods. - * See {@link auto.$provide#constant $provide.constant()}. - */ - constant: invokeLater('$provide', 'constant', 'unshift'), - - /** - * @ngdoc method - * @name angular.Module#decorator - * @module ng - * @param {string} The name of the service to decorate. - * @param {Function} This function will be invoked when the service needs to be - * instantiated and should return the decorated service instance. - * @description - * See {@link auto.$provide#decorator $provide.decorator()}. - */ - decorator: invokeLaterAndSetModuleName('$provide', 'decorator'), - - /** - * @ngdoc method - * @name angular.Module#animation - * @module ng - * @param {string} name animation name - * @param {Function} animationFactory Factory function for creating new instance of an - * animation. - * @description - * - * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. - * - * - * Defines an animation hook that can be later used with - * {@link $animate $animate} service and directives that use this service. - * - * ```js - * module.animation('.animation-name', function($inject1, $inject2) { - * return { - * eventName : function(element, done) { - * //code to run the animation - * //once complete, then run done() - * return function cancellationFunction(element) { - * //code to cancel the animation - * } - * } - * } - * }) - * ``` - * - * See {@link ng.$animateProvider#register $animateProvider.register()} and - * {@link ngAnimate ngAnimate module} for more information. - */ - animation: invokeLaterAndSetModuleName('$animateProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#filter - * @module ng - * @param {string} name Filter name - this must be a valid angular expression identifier - * @param {Function} filterFactory Factory function for creating new instance of filter. - * @description - * See {@link ng.$filterProvider#register $filterProvider.register()}. - * - *
- * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. - * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace - * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores - * (`myapp_subsection_filterx`). - *
- */ - filter: invokeLaterAndSetModuleName('$filterProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#controller - * @module ng - * @param {string|Object} name Controller name, or an object map of controllers where the - * keys are the names and the values are the constructors. - * @param {Function} constructor Controller constructor function. - * @description - * See {@link ng.$controllerProvider#register $controllerProvider.register()}. - */ - controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#directive - * @module ng - * @param {string|Object} name Directive name, or an object map of directives where the - * keys are the names and the values are the factories. - * @param {Function} directiveFactory Factory function for creating new instance of - * directives. - * @description - * See {@link ng.$compileProvider#directive $compileProvider.directive()}. - */ - directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'), - - /** - * @ngdoc method - * @name angular.Module#config - * @module ng - * @param {Function} configFn Execute this function on module load. Useful for service - * configuration. - * @description - * Use this method to register work which needs to be performed on module loading. - * For more about how to configure services, see - * {@link providers#provider-recipe Provider Recipe}. - */ - config: config, - - /** - * @ngdoc method - * @name angular.Module#run - * @module ng - * @param {Function} initializationFn Execute this function after injector creation. - * Useful for application initialization. - * @description - * Use this method to register work which should be performed when the injector is done - * loading all modules. - */ - run: function(block) { - runBlocks.push(block); - return this; - } - }; - - if (configFn) { - config(configFn); - } - - return moduleInstance; - - /** - * @param {string} provider - * @param {string} method - * @param {String=} insertMethod - * @returns {angular.Module} - */ - function invokeLater(provider, method, insertMethod, queue) { - if (!queue) queue = invokeQueue; - return function() { - queue[insertMethod || 'push']([provider, method, arguments]); - return moduleInstance; - }; - } - - /** - * @param {string} provider - * @param {string} method - * @returns {angular.Module} - */ - function invokeLaterAndSetModuleName(provider, method) { - return function(recipeName, factoryFunction) { - if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name; - invokeQueue.push([provider, method, arguments]); - return moduleInstance; - }; - } - }); - }; - }); - -} - -/* global: toDebugString: true */ - -function serializeObject(obj) { - var seen = []; - - return JSON.stringify(obj, function(key, val) { - val = toJsonReplacer(key, val); - if (isObject(val)) { - - if (seen.indexOf(val) >= 0) return '<>'; - - seen.push(val); - } - return val; - }); -} - -function toDebugString(obj) { - if (typeof obj === 'function') { - return obj.toString().replace(/ \{[\s\S]*$/, ''); - } else if (typeof obj === 'undefined') { - return 'undefined'; - } else if (typeof obj !== 'string') { - return serializeObject(obj); - } - return obj; -} - -/* global angularModule: true, - version: true, - - $CompileProvider, - - htmlAnchorDirective, - inputDirective, - inputDirective, - formDirective, - scriptDirective, - selectDirective, - styleDirective, - optionDirective, - ngBindDirective, - ngBindHtmlDirective, - ngBindTemplateDirective, - ngClassDirective, - ngClassEvenDirective, - ngClassOddDirective, - ngCloakDirective, - ngControllerDirective, - ngFormDirective, - ngHideDirective, - ngIfDirective, - ngIncludeDirective, - ngIncludeFillContentDirective, - ngInitDirective, - ngNonBindableDirective, - ngPluralizeDirective, - ngRepeatDirective, - ngShowDirective, - ngStyleDirective, - ngSwitchDirective, - ngSwitchWhenDirective, - ngSwitchDefaultDirective, - ngOptionsDirective, - ngTranscludeDirective, - ngModelDirective, - ngListDirective, - ngChangeDirective, - patternDirective, - patternDirective, - requiredDirective, - requiredDirective, - minlengthDirective, - minlengthDirective, - maxlengthDirective, - maxlengthDirective, - ngValueDirective, - ngModelOptionsDirective, - ngAttributeAliasDirectives, - ngEventDirectives, - - $AnchorScrollProvider, - $AnimateProvider, - $CoreAnimateCssProvider, - $$CoreAnimateQueueProvider, - $$CoreAnimateRunnerProvider, - $BrowserProvider, - $CacheFactoryProvider, - $ControllerProvider, - $DocumentProvider, - $ExceptionHandlerProvider, - $FilterProvider, - $$ForceReflowProvider, - $InterpolateProvider, - $IntervalProvider, - $$HashMapProvider, - $HttpProvider, - $HttpParamSerializerProvider, - $HttpParamSerializerJQLikeProvider, - $HttpBackendProvider, - $LocationProvider, - $LogProvider, - $ParseProvider, - $RootScopeProvider, - $QProvider, - $$QProvider, - $$SanitizeUriProvider, - $SceProvider, - $SceDelegateProvider, - $SnifferProvider, - $TemplateCacheProvider, - $TemplateRequestProvider, - $$TestabilityProvider, - $TimeoutProvider, - $$RAFProvider, - $WindowProvider, - $$jqLiteProvider, - $$CookieReaderProvider -*/ - - -/** - * @ngdoc object - * @name angular.version - * @module ng - * @description - * An object that contains information about the current AngularJS version. This object has the - * following properties: - * - * - `full` – `{string}` – Full version string, such as "0.9.18". - * - `major` – `{number}` – Major version number, such as "0". - * - `minor` – `{number}` – Minor version number, such as "9". - * - `dot` – `{number}` – Dot version number, such as "18". - * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". - */ -var version = { - full: '1.4.5', // all of these placeholder strings will be replaced by grunt's - major: 1, // package task - minor: 4, - dot: 5, - codeName: 'permanent-internship' -}; - - -function publishExternalAPI(angular) { - extend(angular, { - 'bootstrap': bootstrap, - 'copy': copy, - 'extend': extend, - 'merge': merge, - 'equals': equals, - 'element': jqLite, - 'forEach': forEach, - 'injector': createInjector, - 'noop': noop, - 'bind': bind, - 'toJson': toJson, - 'fromJson': fromJson, - 'identity': identity, - 'isUndefined': isUndefined, - 'isDefined': isDefined, - 'isString': isString, - 'isFunction': isFunction, - 'isObject': isObject, - 'isNumber': isNumber, - 'isElement': isElement, - 'isArray': isArray, - 'version': version, - 'isDate': isDate, - 'lowercase': lowercase, - 'uppercase': uppercase, - 'callbacks': {counter: 0}, - 'getTestability': getTestability, - '$$minErr': minErr, - '$$csp': csp, - 'reloadWithDebugInfo': reloadWithDebugInfo - }); - - angularModule = setupModuleLoader(window); - - angularModule('ng', ['ngLocale'], ['$provide', - function ngModule($provide) { - // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. - $provide.provider({ - $$sanitizeUri: $$SanitizeUriProvider - }); - $provide.provider('$compile', $CompileProvider). - directive({ - a: htmlAnchorDirective, - input: inputDirective, - textarea: inputDirective, - form: formDirective, - script: scriptDirective, - select: selectDirective, - style: styleDirective, - option: optionDirective, - ngBind: ngBindDirective, - ngBindHtml: ngBindHtmlDirective, - ngBindTemplate: ngBindTemplateDirective, - ngClass: ngClassDirective, - ngClassEven: ngClassEvenDirective, - ngClassOdd: ngClassOddDirective, - ngCloak: ngCloakDirective, - ngController: ngControllerDirective, - ngForm: ngFormDirective, - ngHide: ngHideDirective, - ngIf: ngIfDirective, - ngInclude: ngIncludeDirective, - ngInit: ngInitDirective, - ngNonBindable: ngNonBindableDirective, - ngPluralize: ngPluralizeDirective, - ngRepeat: ngRepeatDirective, - ngShow: ngShowDirective, - ngStyle: ngStyleDirective, - ngSwitch: ngSwitchDirective, - ngSwitchWhen: ngSwitchWhenDirective, - ngSwitchDefault: ngSwitchDefaultDirective, - ngOptions: ngOptionsDirective, - ngTransclude: ngTranscludeDirective, - ngModel: ngModelDirective, - ngList: ngListDirective, - ngChange: ngChangeDirective, - pattern: patternDirective, - ngPattern: patternDirective, - required: requiredDirective, - ngRequired: requiredDirective, - minlength: minlengthDirective, - ngMinlength: minlengthDirective, - maxlength: maxlengthDirective, - ngMaxlength: maxlengthDirective, - ngValue: ngValueDirective, - ngModelOptions: ngModelOptionsDirective - }). - directive({ - ngInclude: ngIncludeFillContentDirective - }). - directive(ngAttributeAliasDirectives). - directive(ngEventDirectives); - $provide.provider({ - $anchorScroll: $AnchorScrollProvider, - $animate: $AnimateProvider, - $animateCss: $CoreAnimateCssProvider, - $$animateQueue: $$CoreAnimateQueueProvider, - $$AnimateRunner: $$CoreAnimateRunnerProvider, - $browser: $BrowserProvider, - $cacheFactory: $CacheFactoryProvider, - $controller: $ControllerProvider, - $document: $DocumentProvider, - $exceptionHandler: $ExceptionHandlerProvider, - $filter: $FilterProvider, - $$forceReflow: $$ForceReflowProvider, - $interpolate: $InterpolateProvider, - $interval: $IntervalProvider, - $http: $HttpProvider, - $httpParamSerializer: $HttpParamSerializerProvider, - $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider, - $httpBackend: $HttpBackendProvider, - $location: $LocationProvider, - $log: $LogProvider, - $parse: $ParseProvider, - $rootScope: $RootScopeProvider, - $q: $QProvider, - $$q: $$QProvider, - $sce: $SceProvider, - $sceDelegate: $SceDelegateProvider, - $sniffer: $SnifferProvider, - $templateCache: $TemplateCacheProvider, - $templateRequest: $TemplateRequestProvider, - $$testability: $$TestabilityProvider, - $timeout: $TimeoutProvider, - $window: $WindowProvider, - $$rAF: $$RAFProvider, - $$jqLite: $$jqLiteProvider, - $$HashMap: $$HashMapProvider, - $$cookieReader: $$CookieReaderProvider - }); - } - ]); -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Any commits to this file should be reviewed with security in mind. * - * Changes to this file can potentially create security vulnerabilities. * - * An approval from 2 Core members with history of modifying * - * this file is required. * - * * - * Does the change somehow allow for arbitrary javascript to be executed? * - * Or allows for someone to change the prototype of built-in objects? * - * Or gives undesired access to variables likes document or window? * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/* global JQLitePrototype: true, - addEventListenerFn: true, - removeEventListenerFn: true, - BOOLEAN_ATTR: true, - ALIASED_ATTR: true, -*/ - -////////////////////////////////// -//JQLite -////////////////////////////////// - -/** - * @ngdoc function - * @name angular.element - * @module ng - * @kind function - * - * @description - * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. - * - * If jQuery is available, `angular.element` is an alias for the - * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` - * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite." - * - *
jqLite is a tiny, API-compatible subset of jQuery that allows - * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most - * commonly needed functionality with the goal of having a very small footprint.
- * - * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. - * - *
**Note:** all element references in Angular are always wrapped with jQuery or - * jqLite; they are never raw DOM references.
- * - * ## Angular's jqLite - * jqLite provides only the following jQuery methods: - * - * - [`addClass()`](http://api.jquery.com/addClass/) - * - [`after()`](http://api.jquery.com/after/) - * - [`append()`](http://api.jquery.com/append/) - * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters - * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData - * - [`children()`](http://api.jquery.com/children/) - Does not support selectors - * - [`clone()`](http://api.jquery.com/clone/) - * - [`contents()`](http://api.jquery.com/contents/) - * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'. - * - [`data()`](http://api.jquery.com/data/) - * - [`detach()`](http://api.jquery.com/detach/) - * - [`empty()`](http://api.jquery.com/empty/) - * - [`eq()`](http://api.jquery.com/eq/) - * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name - * - [`hasClass()`](http://api.jquery.com/hasClass/) - * - [`html()`](http://api.jquery.com/html/) - * - [`next()`](http://api.jquery.com/next/) - Does not support selectors - * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData - * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter - * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors - * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors - * - [`prepend()`](http://api.jquery.com/prepend/) - * - [`prop()`](http://api.jquery.com/prop/) - * - [`ready()`](http://api.jquery.com/ready/) - * - [`remove()`](http://api.jquery.com/remove/) - * - [`removeAttr()`](http://api.jquery.com/removeAttr/) - * - [`removeClass()`](http://api.jquery.com/removeClass/) - * - [`removeData()`](http://api.jquery.com/removeData/) - * - [`replaceWith()`](http://api.jquery.com/replaceWith/) - * - [`text()`](http://api.jquery.com/text/) - * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers. - * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter - * - [`val()`](http://api.jquery.com/val/) - * - [`wrap()`](http://api.jquery.com/wrap/) - * - * ## jQuery/jqLite Extras - * Angular also provides the following additional methods and events to both jQuery and jqLite: - * - * ### Events - * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event - * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM - * element before it is removed. - * - * ### Methods - * - `controller(name)` - retrieves the controller of the current element or its parent. By default - * retrieves controller associated with the `ngController` directive. If `name` is provided as - * camelCase directive name, then the controller for this directive will be retrieved (e.g. - * `'ngModel'`). - * - `injector()` - retrieves the injector of the current element or its parent. - * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current - * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to - * be enabled. - * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the - * current element. This getter should be used only on elements that contain a directive which starts a new isolate - * scope. Calling `scope()` on this element always returns the original non-isolate scope. - * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled. - * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top - * parent element is reached. - * - * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. - * @returns {Object} jQuery object. - */ - -JQLite.expando = 'ng339'; - -var jqCache = JQLite.cache = {}, - jqId = 1, - addEventListenerFn = function(element, type, fn) { - element.addEventListener(type, fn, false); - }, - removeEventListenerFn = function(element, type, fn) { - element.removeEventListener(type, fn, false); - }; - -/* - * !!! This is an undocumented "private" function !!! - */ -JQLite._data = function(node) { - //jQuery always returns an object on cache miss - return this.cache[node[this.expando]] || {}; -}; - -function jqNextId() { return ++jqId; } - - -var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; -var MOZ_HACK_REGEXP = /^moz([A-Z])/; -var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"}; -var jqLiteMinErr = minErr('jqLite'); - -/** - * Converts snake_case to camelCase. - * Also there is special case for Moz prefix starting with upper case letter. - * @param name Name to normalize - */ -function camelCase(name) { - return name. - replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { - return offset ? letter.toUpperCase() : letter; - }). - replace(MOZ_HACK_REGEXP, 'Moz$1'); -} - -var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/; -var HTML_REGEXP = /<|&#?\w+;/; -var TAG_NAME_REGEXP = /<([\w:]+)/; -var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi; - -var wrapMap = { - 'option': [1, ''], - - 'thead': [1, '', '
'], - 'col': [2, '', '
'], - 'tr': [2, '', '
'], - 'td': [3, '', '
'], - '_default': [0, "", ""] -}; - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - - -function jqLiteIsTextNode(html) { - return !HTML_REGEXP.test(html); -} - -function jqLiteAcceptsData(node) { - // The window object can accept data but has no nodeType - // Otherwise we are only interested in elements (1) and documents (9) - var nodeType = node.nodeType; - return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT; -} - -function jqLiteHasData(node) { - for (var key in jqCache[node.ng339]) { - return true; - } - return false; -} - -function jqLiteBuildFragment(html, context) { - var tmp, tag, wrap, - fragment = context.createDocumentFragment(), - nodes = [], i; - - if (jqLiteIsTextNode(html)) { - // Convert non-html into a text node - nodes.push(context.createTextNode(html)); - } else { - // Convert html into DOM nodes - tmp = tmp || fragment.appendChild(context.createElement("div")); - tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase(); - wrap = wrapMap[tag] || wrapMap._default; - tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1>") + wrap[2]; - - // Descend through wrappers to the right content - i = wrap[0]; - while (i--) { - tmp = tmp.lastChild; - } - - nodes = concat(nodes, tmp.childNodes); - - tmp = fragment.firstChild; - tmp.textContent = ""; - } - - // Remove wrapper from fragment - fragment.textContent = ""; - fragment.innerHTML = ""; // Clear inner HTML - forEach(nodes, function(node) { - fragment.appendChild(node); - }); - - return fragment; -} - -function jqLiteParseHTML(html, context) { - context = context || document; - var parsed; - - if ((parsed = SINGLE_TAG_REGEXP.exec(html))) { - return [context.createElement(parsed[1])]; - } - - if ((parsed = jqLiteBuildFragment(html, context))) { - return parsed.childNodes; - } - - return []; -} - -///////////////////////////////////////////// -function JQLite(element) { - if (element instanceof JQLite) { - return element; - } - - var argIsString; - - if (isString(element)) { - element = trim(element); - argIsString = true; - } - if (!(this instanceof JQLite)) { - if (argIsString && element.charAt(0) != '<') { - throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element'); - } - return new JQLite(element); - } - - if (argIsString) { - jqLiteAddNodes(this, jqLiteParseHTML(element)); - } else { - jqLiteAddNodes(this, element); - } -} - -function jqLiteClone(element) { - return element.cloneNode(true); -} - -function jqLiteDealoc(element, onlyDescendants) { - if (!onlyDescendants) jqLiteRemoveData(element); - - if (element.querySelectorAll) { - var descendants = element.querySelectorAll('*'); - for (var i = 0, l = descendants.length; i < l; i++) { - jqLiteRemoveData(descendants[i]); - } - } -} - -function jqLiteOff(element, type, fn, unsupported) { - if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument'); - - var expandoStore = jqLiteExpandoStore(element); - var events = expandoStore && expandoStore.events; - var handle = expandoStore && expandoStore.handle; - - if (!handle) return; //no listeners registered - - if (!type) { - for (type in events) { - if (type !== '$destroy') { - removeEventListenerFn(element, type, handle); - } - delete events[type]; - } - } else { - forEach(type.split(' '), function(type) { - if (isDefined(fn)) { - var listenerFns = events[type]; - arrayRemove(listenerFns || [], fn); - if (listenerFns && listenerFns.length > 0) { - return; - } - } - - removeEventListenerFn(element, type, handle); - delete events[type]; - }); - } -} - -function jqLiteRemoveData(element, name) { - var expandoId = element.ng339; - var expandoStore = expandoId && jqCache[expandoId]; - - if (expandoStore) { - if (name) { - delete expandoStore.data[name]; - return; - } - - if (expandoStore.handle) { - if (expandoStore.events.$destroy) { - expandoStore.handle({}, '$destroy'); - } - jqLiteOff(element); - } - delete jqCache[expandoId]; - element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it - } -} - - -function jqLiteExpandoStore(element, createIfNecessary) { - var expandoId = element.ng339, - expandoStore = expandoId && jqCache[expandoId]; - - if (createIfNecessary && !expandoStore) { - element.ng339 = expandoId = jqNextId(); - expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined}; - } - - return expandoStore; -} - - -function jqLiteData(element, key, value) { - if (jqLiteAcceptsData(element)) { - - var isSimpleSetter = isDefined(value); - var isSimpleGetter = !isSimpleSetter && key && !isObject(key); - var massGetter = !key; - var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter); - var data = expandoStore && expandoStore.data; - - if (isSimpleSetter) { // data('key', value) - data[key] = value; - } else { - if (massGetter) { // data() - return data; - } else { - if (isSimpleGetter) { // data('key') - // don't force creation of expandoStore if it doesn't exist yet - return data && data[key]; - } else { // mass-setter: data({key1: val1, key2: val2}) - extend(data, key); - } - } - } - } -} - -function jqLiteHasClass(element, selector) { - if (!element.getAttribute) return false; - return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " "). - indexOf(" " + selector + " ") > -1); -} - -function jqLiteRemoveClass(element, cssClasses) { - if (cssClasses && element.setAttribute) { - forEach(cssClasses.split(' '), function(cssClass) { - element.setAttribute('class', trim( - (" " + (element.getAttribute('class') || '') + " ") - .replace(/[\n\t]/g, " ") - .replace(" " + trim(cssClass) + " ", " ")) - ); - }); - } -} - -function jqLiteAddClass(element, cssClasses) { - if (cssClasses && element.setAttribute) { - var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') - .replace(/[\n\t]/g, " "); - - forEach(cssClasses.split(' '), function(cssClass) { - cssClass = trim(cssClass); - if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) { - existingClasses += cssClass + ' '; - } - }); - - element.setAttribute('class', trim(existingClasses)); - } -} - - -function jqLiteAddNodes(root, elements) { - // THIS CODE IS VERY HOT. Don't make changes without benchmarking. - - if (elements) { - - // if a Node (the most common case) - if (elements.nodeType) { - root[root.length++] = elements; - } else { - var length = elements.length; - - // if an Array or NodeList and not a Window - if (typeof length === 'number' && elements.window !== elements) { - if (length) { - for (var i = 0; i < length; i++) { - root[root.length++] = elements[i]; - } - } - } else { - root[root.length++] = elements; - } - } - } -} - - -function jqLiteController(element, name) { - return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller'); -} - -function jqLiteInheritedData(element, name, value) { - // if element is the document object work with the html element instead - // this makes $(document).scope() possible - if (element.nodeType == NODE_TYPE_DOCUMENT) { - element = element.documentElement; - } - var names = isArray(name) ? name : [name]; - - while (element) { - for (var i = 0, ii = names.length; i < ii; i++) { - if ((value = jqLite.data(element, names[i])) !== undefined) return value; - } - - // If dealing with a document fragment node with a host element, and no parent, use the host - // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM - // to lookup parent controllers. - element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host); - } -} - -function jqLiteEmpty(element) { - jqLiteDealoc(element, true); - while (element.firstChild) { - element.removeChild(element.firstChild); - } -} - -function jqLiteRemove(element, keepData) { - if (!keepData) jqLiteDealoc(element); - var parent = element.parentNode; - if (parent) parent.removeChild(element); -} - - -function jqLiteDocumentLoaded(action, win) { - win = win || window; - if (win.document.readyState === 'complete') { - // Force the action to be run async for consistent behaviour - // from the action's point of view - // i.e. it will definitely not be in a $apply - win.setTimeout(action); - } else { - // No need to unbind this handler as load is only ever called once - jqLite(win).on('load', action); - } -} - -////////////////////////////////////////// -// Functions which are declared directly. -////////////////////////////////////////// -var JQLitePrototype = JQLite.prototype = { - ready: function(fn) { - var fired = false; - - function trigger() { - if (fired) return; - fired = true; - fn(); - } - - // check if document is already loaded - if (document.readyState === 'complete') { - setTimeout(trigger); - } else { - this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 - // we can not use jqLite since we are not done loading and jQuery could be loaded later. - // jshint -W064 - JQLite(window).on('load', trigger); // fallback to window.onload for others - // jshint +W064 - } - }, - toString: function() { - var value = []; - forEach(this, function(e) { value.push('' + e);}); - return '[' + value.join(', ') + ']'; - }, - - eq: function(index) { - return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); - }, - - length: 0, - push: push, - sort: [].sort, - splice: [].splice -}; - -////////////////////////////////////////// -// Functions iterating getter/setters. -// these functions return self on setter and -// value on get. -////////////////////////////////////////// -var BOOLEAN_ATTR = {}; -forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { - BOOLEAN_ATTR[lowercase(value)] = value; -}); -var BOOLEAN_ELEMENTS = {}; -forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { - BOOLEAN_ELEMENTS[value] = true; -}); -var ALIASED_ATTR = { - 'ngMinlength': 'minlength', - 'ngMaxlength': 'maxlength', - 'ngMin': 'min', - 'ngMax': 'max', - 'ngPattern': 'pattern' -}; - -function getBooleanAttrName(element, name) { - // check dom last since we will most likely fail on name - var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; - - // booleanAttr is here twice to minimize DOM access - return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr; -} - -function getAliasedAttrName(element, name) { - var nodeName = element.nodeName; - return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name]; -} - -forEach({ - data: jqLiteData, - removeData: jqLiteRemoveData, - hasData: jqLiteHasData -}, function(fn, name) { - JQLite[name] = fn; -}); - -forEach({ - data: jqLiteData, - inheritedData: jqLiteInheritedData, - - scope: function(element) { - // Can't use jqLiteData here directly so we stay compatible with jQuery! - return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']); - }, - - isolateScope: function(element) { - // Can't use jqLiteData here directly so we stay compatible with jQuery! - return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate'); - }, - - controller: jqLiteController, - - injector: function(element) { - return jqLiteInheritedData(element, '$injector'); - }, - - removeAttr: function(element, name) { - element.removeAttribute(name); - }, - - hasClass: jqLiteHasClass, - - css: function(element, name, value) { - name = camelCase(name); - - if (isDefined(value)) { - element.style[name] = value; - } else { - return element.style[name]; - } - }, - - attr: function(element, name, value) { - var nodeType = element.nodeType; - if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) { - return; - } - var lowercasedName = lowercase(name); - if (BOOLEAN_ATTR[lowercasedName]) { - if (isDefined(value)) { - if (!!value) { - element[name] = true; - element.setAttribute(name, lowercasedName); - } else { - element[name] = false; - element.removeAttribute(lowercasedName); - } - } else { - return (element[name] || - (element.attributes.getNamedItem(name) || noop).specified) - ? lowercasedName - : undefined; - } - } else if (isDefined(value)) { - element.setAttribute(name, value); - } else if (element.getAttribute) { - // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code - // some elements (e.g. Document) don't have get attribute, so return undefined - var ret = element.getAttribute(name, 2); - // normalize non-existing attributes to undefined (as jQuery) - return ret === null ? undefined : ret; - } - }, - - prop: function(element, name, value) { - if (isDefined(value)) { - element[name] = value; - } else { - return element[name]; - } - }, - - text: (function() { - getText.$dv = ''; - return getText; - - function getText(element, value) { - if (isUndefined(value)) { - var nodeType = element.nodeType; - return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : ''; - } - element.textContent = value; - } - })(), - - val: function(element, value) { - if (isUndefined(value)) { - if (element.multiple && nodeName_(element) === 'select') { - var result = []; - forEach(element.options, function(option) { - if (option.selected) { - result.push(option.value || option.text); - } - }); - return result.length === 0 ? null : result; - } - return element.value; - } - element.value = value; - }, - - html: function(element, value) { - if (isUndefined(value)) { - return element.innerHTML; - } - jqLiteDealoc(element, true); - element.innerHTML = value; - }, - - empty: jqLiteEmpty -}, function(fn, name) { - /** - * Properties: writes return selection, reads return first value - */ - JQLite.prototype[name] = function(arg1, arg2) { - var i, key; - var nodeCount = this.length; - - // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it - // in a way that survives minification. - // jqLiteEmpty takes no arguments but is a setter. - if (fn !== jqLiteEmpty && - (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) { - if (isObject(arg1)) { - - // we are a write, but the object properties are the key/values - for (i = 0; i < nodeCount; i++) { - if (fn === jqLiteData) { - // data() takes the whole object in jQuery - fn(this[i], arg1); - } else { - for (key in arg1) { - fn(this[i], key, arg1[key]); - } - } - } - // return self for chaining - return this; - } else { - // we are a read, so read the first child. - // TODO: do we still need this? - var value = fn.$dv; - // Only if we have $dv do we iterate over all, otherwise it is just the first element. - var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount; - for (var j = 0; j < jj; j++) { - var nodeValue = fn(this[j], arg1, arg2); - value = value ? value + nodeValue : nodeValue; - } - return value; - } - } else { - // we are a write, so apply to all children - for (i = 0; i < nodeCount; i++) { - fn(this[i], arg1, arg2); - } - // return self for chaining - return this; - } - }; -}); - -function createEventHandler(element, events) { - var eventHandler = function(event, type) { - // jQuery specific api - event.isDefaultPrevented = function() { - return event.defaultPrevented; - }; - - var eventFns = events[type || event.type]; - var eventFnsLength = eventFns ? eventFns.length : 0; - - if (!eventFnsLength) return; - - if (isUndefined(event.immediatePropagationStopped)) { - var originalStopImmediatePropagation = event.stopImmediatePropagation; - event.stopImmediatePropagation = function() { - event.immediatePropagationStopped = true; - - if (event.stopPropagation) { - event.stopPropagation(); - } - - if (originalStopImmediatePropagation) { - originalStopImmediatePropagation.call(event); - } - }; - } - - event.isImmediatePropagationStopped = function() { - return event.immediatePropagationStopped === true; - }; - - // Copy event handlers in case event handlers array is modified during execution. - if ((eventFnsLength > 1)) { - eventFns = shallowCopy(eventFns); - } - - for (var i = 0; i < eventFnsLength; i++) { - if (!event.isImmediatePropagationStopped()) { - eventFns[i].call(element, event); - } - } - }; - - // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all - // events on `element` - eventHandler.elem = element; - return eventHandler; -} - -////////////////////////////////////////// -// Functions iterating traversal. -// These functions chain results into a single -// selector. -////////////////////////////////////////// -forEach({ - removeData: jqLiteRemoveData, - - on: function jqLiteOn(element, type, fn, unsupported) { - if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); - - // Do not add event handlers to non-elements because they will not be cleaned up. - if (!jqLiteAcceptsData(element)) { - return; - } - - var expandoStore = jqLiteExpandoStore(element, true); - var events = expandoStore.events; - var handle = expandoStore.handle; - - if (!handle) { - handle = expandoStore.handle = createEventHandler(element, events); - } - - // http://jsperf.com/string-indexof-vs-split - var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type]; - var i = types.length; - - while (i--) { - type = types[i]; - var eventFns = events[type]; - - if (!eventFns) { - events[type] = []; - - if (type === 'mouseenter' || type === 'mouseleave') { - // Refer to jQuery's implementation of mouseenter & mouseleave - // Read about mouseenter and mouseleave: - // http://www.quirksmode.org/js/events_mouse.html#link8 - - jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) { - var target = this, related = event.relatedTarget; - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if (!related || (related !== target && !target.contains(related))) { - handle(event, type); - } - }); - - } else { - if (type !== '$destroy') { - addEventListenerFn(element, type, handle); - } - } - eventFns = events[type]; - } - eventFns.push(fn); - } - }, - - off: jqLiteOff, - - one: function(element, type, fn) { - element = jqLite(element); - - //add the listener twice so that when it is called - //you can remove the original function and still be - //able to call element.off(ev, fn) normally - element.on(type, function onFn() { - element.off(type, fn); - element.off(type, onFn); - }); - element.on(type, fn); - }, - - replaceWith: function(element, replaceNode) { - var index, parent = element.parentNode; - jqLiteDealoc(element); - forEach(new JQLite(replaceNode), function(node) { - if (index) { - parent.insertBefore(node, index.nextSibling); - } else { - parent.replaceChild(node, element); - } - index = node; - }); - }, - - children: function(element) { - var children = []; - forEach(element.childNodes, function(element) { - if (element.nodeType === NODE_TYPE_ELEMENT) { - children.push(element); - } - }); - return children; - }, - - contents: function(element) { - return element.contentDocument || element.childNodes || []; - }, - - append: function(element, node) { - var nodeType = element.nodeType; - if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return; - - node = new JQLite(node); - - for (var i = 0, ii = node.length; i < ii; i++) { - var child = node[i]; - element.appendChild(child); - } - }, - - prepend: function(element, node) { - if (element.nodeType === NODE_TYPE_ELEMENT) { - var index = element.firstChild; - forEach(new JQLite(node), function(child) { - element.insertBefore(child, index); - }); - } - }, - - wrap: function(element, wrapNode) { - wrapNode = jqLite(wrapNode).eq(0).clone()[0]; - var parent = element.parentNode; - if (parent) { - parent.replaceChild(wrapNode, element); - } - wrapNode.appendChild(element); - }, - - remove: jqLiteRemove, - - detach: function(element) { - jqLiteRemove(element, true); - }, - - after: function(element, newElement) { - var index = element, parent = element.parentNode; - newElement = new JQLite(newElement); - - for (var i = 0, ii = newElement.length; i < ii; i++) { - var node = newElement[i]; - parent.insertBefore(node, index.nextSibling); - index = node; - } - }, - - addClass: jqLiteAddClass, - removeClass: jqLiteRemoveClass, - - toggleClass: function(element, selector, condition) { - if (selector) { - forEach(selector.split(' '), function(className) { - var classCondition = condition; - if (isUndefined(classCondition)) { - classCondition = !jqLiteHasClass(element, className); - } - (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); - }); - } - }, - - parent: function(element) { - var parent = element.parentNode; - return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null; - }, - - next: function(element) { - return element.nextElementSibling; - }, - - find: function(element, selector) { - if (element.getElementsByTagName) { - return element.getElementsByTagName(selector); - } else { - return []; - } - }, - - clone: jqLiteClone, - - triggerHandler: function(element, event, extraParameters) { - - var dummyEvent, eventFnsCopy, handlerArgs; - var eventName = event.type || event; - var expandoStore = jqLiteExpandoStore(element); - var events = expandoStore && expandoStore.events; - var eventFns = events && events[eventName]; - - if (eventFns) { - // Create a dummy event to pass to the handlers - dummyEvent = { - preventDefault: function() { this.defaultPrevented = true; }, - isDefaultPrevented: function() { return this.defaultPrevented === true; }, - stopImmediatePropagation: function() { this.immediatePropagationStopped = true; }, - isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; }, - stopPropagation: noop, - type: eventName, - target: element - }; - - // If a custom event was provided then extend our dummy event with it - if (event.type) { - dummyEvent = extend(dummyEvent, event); - } - - // Copy event handlers in case event handlers array is modified during execution. - eventFnsCopy = shallowCopy(eventFns); - handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent]; - - forEach(eventFnsCopy, function(fn) { - if (!dummyEvent.isImmediatePropagationStopped()) { - fn.apply(element, handlerArgs); - } - }); - } - } -}, function(fn, name) { - /** - * chaining functions - */ - JQLite.prototype[name] = function(arg1, arg2, arg3) { - var value; - - for (var i = 0, ii = this.length; i < ii; i++) { - if (isUndefined(value)) { - value = fn(this[i], arg1, arg2, arg3); - if (isDefined(value)) { - // any function which returns a value needs to be wrapped - value = jqLite(value); - } - } else { - jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); - } - } - return isDefined(value) ? value : this; - }; - - // bind legacy bind/unbind to on/off - JQLite.prototype.bind = JQLite.prototype.on; - JQLite.prototype.unbind = JQLite.prototype.off; -}); - - -// Provider for private $$jqLite service -function $$jqLiteProvider() { - this.$get = function $$jqLite() { - return extend(JQLite, { - hasClass: function(node, classes) { - if (node.attr) node = node[0]; - return jqLiteHasClass(node, classes); - }, - addClass: function(node, classes) { - if (node.attr) node = node[0]; - return jqLiteAddClass(node, classes); - }, - removeClass: function(node, classes) { - if (node.attr) node = node[0]; - return jqLiteRemoveClass(node, classes); - } - }); - }; -} - -/** - * Computes a hash of an 'obj'. - * Hash of a: - * string is string - * number is number as string - * object is either result of calling $$hashKey function on the object or uniquely generated id, - * that is also assigned to the $$hashKey property of the object. - * - * @param obj - * @returns {string} hash string such that the same input will have the same hash string. - * The resulting string key is in 'type:hashKey' format. - */ -function hashKey(obj, nextUidFn) { - var key = obj && obj.$$hashKey; - - if (key) { - if (typeof key === 'function') { - key = obj.$$hashKey(); - } - return key; - } - - var objType = typeof obj; - if (objType == 'function' || (objType == 'object' && obj !== null)) { - key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)(); - } else { - key = objType + ':' + obj; - } - - return key; -} - -/** - * HashMap which can use objects as keys - */ -function HashMap(array, isolatedUid) { - if (isolatedUid) { - var uid = 0; - this.nextUid = function() { - return ++uid; - }; - } - forEach(array, this.put, this); -} -HashMap.prototype = { - /** - * Store key value pair - * @param key key to store can be any type - * @param value value to store can be any type - */ - put: function(key, value) { - this[hashKey(key, this.nextUid)] = value; - }, - - /** - * @param key - * @returns {Object} the value for the key - */ - get: function(key) { - return this[hashKey(key, this.nextUid)]; - }, - - /** - * Remove the key/value pair - * @param key - */ - remove: function(key) { - var value = this[key = hashKey(key, this.nextUid)]; - delete this[key]; - return value; - } -}; - -var $$HashMapProvider = [function() { - this.$get = [function() { - return HashMap; - }]; -}]; - -/** - * @ngdoc function - * @module ng - * @name angular.injector - * @kind function - * - * @description - * Creates an injector object that can be used for retrieving services as well as for - * dependency injection (see {@link guide/di dependency injection}). - * - * @param {Array.} modules A list of module functions or their aliases. See - * {@link angular.module}. The `ng` module must be explicitly added. - * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which - * disallows argument name annotation inference. - * @returns {injector} Injector object. See {@link auto.$injector $injector}. - * - * @example - * Typical usage - * ```js - * // create an injector - * var $injector = angular.injector(['ng']); - * - * // use the injector to kick off your application - * // use the type inference to auto inject arguments, or use implicit injection - * $injector.invoke(function($rootScope, $compile, $document) { - * $compile($document)($rootScope); - * $rootScope.$digest(); - * }); - * ``` - * - * Sometimes you want to get access to the injector of a currently running Angular app - * from outside Angular. Perhaps, you want to inject and compile some markup after the - * application has been bootstrapped. You can do this using the extra `injector()` added - * to JQuery/jqLite elements. See {@link angular.element}. - * - * *This is fairly rare but could be the case if a third party library is injecting the - * markup.* - * - * In the following example a new block of HTML containing a `ng-controller` - * directive is added to the end of the document body by JQuery. We then compile and link - * it into the current AngularJS scope. - * - * ```js - * var $div = $('
{{content.label}}
'); - * $(document.body).append($div); - * - * angular.element(document).injector().invoke(function($compile) { - * var scope = angular.element($div).scope(); - * $compile($div)(scope); - * }); - * ``` - */ - - -/** - * @ngdoc module - * @name auto - * @description - * - * Implicit module which gets automatically added to each {@link auto.$injector $injector}. - */ - -var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m; -var FN_ARG_SPLIT = /,/; -var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; -var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; -var $injectorMinErr = minErr('$injector'); - -function anonFn(fn) { - // For anonymous functions, showing at the very least the function signature can help in - // debugging. - var fnText = fn.toString().replace(STRIP_COMMENTS, ''), - args = fnText.match(FN_ARGS); - if (args) { - return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')'; - } - return 'fn'; -} - -function annotate(fn, strictDi, name) { - var $inject, - fnText, - argDecl, - last; - - if (typeof fn === 'function') { - if (!($inject = fn.$inject)) { - $inject = []; - if (fn.length) { - if (strictDi) { - if (!isString(name) || !name) { - name = fn.name || anonFn(fn); - } - throw $injectorMinErr('strictdi', - '{0} is not using explicit annotation and cannot be invoked in strict mode', name); - } - fnText = fn.toString().replace(STRIP_COMMENTS, ''); - argDecl = fnText.match(FN_ARGS); - forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) { - arg.replace(FN_ARG, function(all, underscore, name) { - $inject.push(name); - }); - }); - } - fn.$inject = $inject; - } - } else if (isArray(fn)) { - last = fn.length - 1; - assertArgFn(fn[last], 'fn'); - $inject = fn.slice(0, last); - } else { - assertArgFn(fn, 'fn', true); - } - return $inject; -} - -/////////////////////////////////////// - -/** - * @ngdoc service - * @name $injector - * - * @description - * - * `$injector` is used to retrieve object instances as defined by - * {@link auto.$provide provider}, instantiate types, invoke methods, - * and load modules. - * - * The following always holds true: - * - * ```js - * var $injector = angular.injector(); - * expect($injector.get('$injector')).toBe($injector); - * expect($injector.invoke(function($injector) { - * return $injector; - * })).toBe($injector); - * ``` - * - * # Injection Function Annotation - * - * JavaScript does not have annotations, and annotations are needed for dependency injection. The - * following are all valid ways of annotating function with injection arguments and are equivalent. - * - * ```js - * // inferred (only works if code not minified/obfuscated) - * $injector.invoke(function(serviceA){}); - * - * // annotated - * function explicit(serviceA) {}; - * explicit.$inject = ['serviceA']; - * $injector.invoke(explicit); - * - * // inline - * $injector.invoke(['serviceA', function(serviceA){}]); - * ``` - * - * ## Inference - * - * In JavaScript calling `toString()` on a function returns the function definition. The definition - * can then be parsed and the function arguments can be extracted. This method of discovering - * annotations is disallowed when the injector is in strict mode. - * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the - * argument names. - * - * ## `$inject` Annotation - * By adding an `$inject` property onto a function the injection parameters can be specified. - * - * ## Inline - * As an array of injection names, where the last item in the array is the function to call. - */ - -/** - * @ngdoc method - * @name $injector#get - * - * @description - * Return an instance of the service. - * - * @param {string} name The name of the instance to retrieve. - * @param {string=} caller An optional string to provide the origin of the function call for error messages. - * @return {*} The instance. - */ - -/** - * @ngdoc method - * @name $injector#invoke - * - * @description - * Invoke the method and supply the method arguments from the `$injector`. - * - * @param {Function|Array.} fn The injectable function to invoke. Function parameters are - * injected according to the {@link guide/di $inject Annotation} rules. - * @param {Object=} self The `this` for the invoked method. - * @param {Object=} locals Optional object. If preset then any argument names are read from this - * object first, before the `$injector` is consulted. - * @returns {*} the value returned by the invoked `fn` function. - */ - -/** - * @ngdoc method - * @name $injector#has - * - * @description - * Allows the user to query if the particular service exists. - * - * @param {string} name Name of the service to query. - * @returns {boolean} `true` if injector has given service. - */ - -/** - * @ngdoc method - * @name $injector#instantiate - * @description - * Create a new instance of JS type. The method takes a constructor function, invokes the new - * operator, and supplies all of the arguments to the constructor function as specified by the - * constructor annotation. - * - * @param {Function} Type Annotated constructor function. - * @param {Object=} locals Optional object. If preset then any argument names are read from this - * object first, before the `$injector` is consulted. - * @returns {Object} new instance of `Type`. - */ - -/** - * @ngdoc method - * @name $injector#annotate - * - * @description - * Returns an array of service names which the function is requesting for injection. This API is - * used by the injector to determine which services need to be injected into the function when the - * function is invoked. There are three ways in which the function can be annotated with the needed - * dependencies. - * - * # Argument names - * - * The simplest form is to extract the dependencies from the arguments of the function. This is done - * by converting the function into a string using `toString()` method and extracting the argument - * names. - * ```js - * // Given - * function MyController($scope, $route) { - * // ... - * } - * - * // Then - * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); - * ``` - * - * You can disallow this method by using strict injection mode. - * - * This method does not work with code minification / obfuscation. For this reason the following - * annotation strategies are supported. - * - * # The `$inject` property - * - * If a function has an `$inject` property and its value is an array of strings, then the strings - * represent names of services to be injected into the function. - * ```js - * // Given - * var MyController = function(obfuscatedScope, obfuscatedRoute) { - * // ... - * } - * // Define function dependencies - * MyController['$inject'] = ['$scope', '$route']; - * - * // Then - * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); - * ``` - * - * # The array notation - * - * It is often desirable to inline Injected functions and that's when setting the `$inject` property - * is very inconvenient. In these situations using the array notation to specify the dependencies in - * a way that survives minification is a better choice: - * - * ```js - * // We wish to write this (not minification / obfuscation safe) - * injector.invoke(function($compile, $rootScope) { - * // ... - * }); - * - * // We are forced to write break inlining - * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { - * // ... - * }; - * tmpFn.$inject = ['$compile', '$rootScope']; - * injector.invoke(tmpFn); - * - * // To better support inline function the inline annotation is supported - * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { - * // ... - * }]); - * - * // Therefore - * expect(injector.annotate( - * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) - * ).toEqual(['$compile', '$rootScope']); - * ``` - * - * @param {Function|Array.} fn Function for which dependent service names need to - * be retrieved as described above. - * - * @param {boolean=} [strictDi=false] Disallow argument name annotation inference. - * - * @returns {Array.} The names of the services which the function requires. - */ - - - - -/** - * @ngdoc service - * @name $provide - * - * @description - * - * The {@link auto.$provide $provide} service has a number of methods for registering components - * with the {@link auto.$injector $injector}. Many of these functions are also exposed on - * {@link angular.Module}. - * - * An Angular **service** is a singleton object created by a **service factory**. These **service - * factories** are functions which, in turn, are created by a **service provider**. - * The **service providers** are constructor functions. When instantiated they must contain a - * property called `$get`, which holds the **service factory** function. - * - * When you request a service, the {@link auto.$injector $injector} is responsible for finding the - * correct **service provider**, instantiating it and then calling its `$get` **service factory** - * function to get the instance of the **service**. - * - * Often services have no configuration options and there is no need to add methods to the service - * provider. The provider will be no more than a constructor function with a `$get` property. For - * these cases the {@link auto.$provide $provide} service has additional helper methods to register - * services without specifying a provider. - * - * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the - * {@link auto.$injector $injector} - * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by - * providers and services. - * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by - * services, not providers. - * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`, - * that will be wrapped in a **service provider** object, whose `$get` property will contain the - * given factory function. - * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class` - * that will be wrapped in a **service provider** object, whose `$get` property will instantiate - * a new object using the given constructor function. - * - * See the individual methods for more information and examples. - */ - -/** - * @ngdoc method - * @name $provide#provider - * @description - * - * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions - * are constructor functions, whose instances are responsible for "providing" a factory for a - * service. - * - * Service provider names start with the name of the service they provide followed by `Provider`. - * For example, the {@link ng.$log $log} service has a provider called - * {@link ng.$logProvider $logProvider}. - * - * Service provider objects can have additional methods which allow configuration of the provider - * and its service. Importantly, you can configure what kind of service is created by the `$get` - * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a - * method {@link ng.$logProvider#debugEnabled debugEnabled} - * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the - * console or not. - * - * @param {string} name The name of the instance. NOTE: the provider will be available under `name + - 'Provider'` key. - * @param {(Object|function())} provider If the provider is: - * - * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using - * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created. - * - `Constructor`: a new instance of the provider will be created using - * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`. - * - * @returns {Object} registered provider instance - - * @example - * - * The following example shows how to create a simple event tracking service and register it using - * {@link auto.$provide#provider $provide.provider()}. - * - * ```js - * // Define the eventTracker provider - * function EventTrackerProvider() { - * var trackingUrl = '/track'; - * - * // A provider method for configuring where the tracked events should been saved - * this.setTrackingUrl = function(url) { - * trackingUrl = url; - * }; - * - * // The service factory function - * this.$get = ['$http', function($http) { - * var trackedEvents = {}; - * return { - * // Call this to track an event - * event: function(event) { - * var count = trackedEvents[event] || 0; - * count += 1; - * trackedEvents[event] = count; - * return count; - * }, - * // Call this to save the tracked events to the trackingUrl - * save: function() { - * $http.post(trackingUrl, trackedEvents); - * } - * }; - * }]; - * } - * - * describe('eventTracker', function() { - * var postSpy; - * - * beforeEach(module(function($provide) { - * // Register the eventTracker provider - * $provide.provider('eventTracker', EventTrackerProvider); - * })); - * - * beforeEach(module(function(eventTrackerProvider) { - * // Configure eventTracker provider - * eventTrackerProvider.setTrackingUrl('/custom-track'); - * })); - * - * it('tracks events', inject(function(eventTracker) { - * expect(eventTracker.event('login')).toEqual(1); - * expect(eventTracker.event('login')).toEqual(2); - * })); - * - * it('saves to the tracking url', inject(function(eventTracker, $http) { - * postSpy = spyOn($http, 'post'); - * eventTracker.event('login'); - * eventTracker.save(); - * expect(postSpy).toHaveBeenCalled(); - * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); - * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); - * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); - * })); - * }); - * ``` - */ - -/** - * @ngdoc method - * @name $provide#factory - * @description - * - * Register a **service factory**, which will be called to return the service instance. - * This is short for registering a service where its provider consists of only a `$get` property, - * which is the given service factory function. - * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to - * configure your service in a provider. - * - * @param {string} name The name of the instance. - * @param {Function|Array.} $getFn The injectable $getFn for the instance creation. - * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`. - * @returns {Object} registered provider instance - * - * @example - * Here is an example of registering a service - * ```js - * $provide.factory('ping', ['$http', function($http) { - * return function ping() { - * return $http.send('/ping'); - * }; - * }]); - * ``` - * You would then inject and use this service like this: - * ```js - * someModule.controller('Ctrl', ['ping', function(ping) { - * ping(); - * }]); - * ``` - */ - - -/** - * @ngdoc method - * @name $provide#service - * @description - * - * Register a **service constructor**, which will be invoked with `new` to create the service - * instance. - * This is short for registering a service where its provider's `$get` property is the service - * constructor function that will be used to instantiate the service instance. - * - * You should use {@link auto.$provide#service $provide.service(class)} if you define your service - * as a type/class. - * - * @param {string} name The name of the instance. - * @param {Function|Array.} constructor An injectable class (constructor function) - * that will be instantiated. - * @returns {Object} registered provider instance - * - * @example - * Here is an example of registering a service using - * {@link auto.$provide#service $provide.service(class)}. - * ```js - * var Ping = function($http) { - * this.$http = $http; - * }; - * - * Ping.$inject = ['$http']; - * - * Ping.prototype.send = function() { - * return this.$http.get('/ping'); - * }; - * $provide.service('ping', Ping); - * ``` - * You would then inject and use this service like this: - * ```js - * someModule.controller('Ctrl', ['ping', function(ping) { - * ping.send(); - * }]); - * ``` - */ - - -/** - * @ngdoc method - * @name $provide#value - * @description - * - * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a - * number, an array, an object or a function. This is short for registering a service where its - * provider's `$get` property is a factory function that takes no arguments and returns the **value - * service**. - * - * Value services are similar to constant services, except that they cannot be injected into a - * module configuration function (see {@link angular.Module#config}) but they can be overridden by - * an Angular - * {@link auto.$provide#decorator decorator}. - * - * @param {string} name The name of the instance. - * @param {*} value The value. - * @returns {Object} registered provider instance - * - * @example - * Here are some examples of creating value services. - * ```js - * $provide.value('ADMIN_USER', 'admin'); - * - * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); - * - * $provide.value('halfOf', function(value) { - * return value / 2; - * }); - * ``` - */ - - -/** - * @ngdoc method - * @name $provide#constant - * @description - * - * Register a **constant service**, such as a string, a number, an array, an object or a function, - * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be - * injected into a module configuration function (see {@link angular.Module#config}) and it cannot - * be overridden by an Angular {@link auto.$provide#decorator decorator}. - * - * @param {string} name The name of the constant. - * @param {*} value The constant value. - * @returns {Object} registered instance - * - * @example - * Here a some examples of creating constants: - * ```js - * $provide.constant('SHARD_HEIGHT', 306); - * - * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); - * - * $provide.constant('double', function(value) { - * return value * 2; - * }); - * ``` - */ - - -/** - * @ngdoc method - * @name $provide#decorator - * @description - * - * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator - * intercepts the creation of a service, allowing it to override or modify the behaviour of the - * service. The object returned by the decorator may be the original service, or a new service - * object which replaces or wraps and delegates to the original service. - * - * @param {string} name The name of the service to decorate. - * @param {Function|Array.} decorator This function will be invoked when the service needs to be - * instantiated and should return the decorated service instance. The function is called using - * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. - * Local injection arguments: - * - * * `$delegate` - The original service instance, which can be monkey patched, configured, - * decorated or delegated to. - * - * @example - * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting - * calls to {@link ng.$log#error $log.warn()}. - * ```js - * $provide.decorator('$log', ['$delegate', function($delegate) { - * $delegate.warn = $delegate.error; - * return $delegate; - * }]); - * ``` - */ - - -function createInjector(modulesToLoad, strictDi) { - strictDi = (strictDi === true); - var INSTANTIATING = {}, - providerSuffix = 'Provider', - path = [], - loadedModules = new HashMap([], true), - providerCache = { - $provide: { - provider: supportObject(provider), - factory: supportObject(factory), - service: supportObject(service), - value: supportObject(value), - constant: supportObject(constant), - decorator: decorator - } - }, - providerInjector = (providerCache.$injector = - createInternalInjector(providerCache, function(serviceName, caller) { - if (angular.isString(caller)) { - path.push(caller); - } - throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); - })), - instanceCache = {}, - instanceInjector = (instanceCache.$injector = - createInternalInjector(instanceCache, function(serviceName, caller) { - var provider = providerInjector.get(serviceName + providerSuffix, caller); - return instanceInjector.invoke(provider.$get, provider, undefined, serviceName); - })); - - - forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); }); - - return instanceInjector; - - //////////////////////////////////// - // $provider - //////////////////////////////////// - - function supportObject(delegate) { - return function(key, value) { - if (isObject(key)) { - forEach(key, reverseParams(delegate)); - } else { - return delegate(key, value); - } - }; - } - - function provider(name, provider_) { - assertNotHasOwnProperty(name, 'service'); - if (isFunction(provider_) || isArray(provider_)) { - provider_ = providerInjector.instantiate(provider_); - } - if (!provider_.$get) { - throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); - } - return providerCache[name + providerSuffix] = provider_; - } - - function enforceReturnValue(name, factory) { - return function enforcedReturnValue() { - var result = instanceInjector.invoke(factory, this); - if (isUndefined(result)) { - throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name); - } - return result; - }; - } - - function factory(name, factoryFn, enforce) { - return provider(name, { - $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn - }); - } - - function service(name, constructor) { - return factory(name, ['$injector', function($injector) { - return $injector.instantiate(constructor); - }]); - } - - function value(name, val) { return factory(name, valueFn(val), false); } - - function constant(name, value) { - assertNotHasOwnProperty(name, 'constant'); - providerCache[name] = value; - instanceCache[name] = value; - } - - function decorator(serviceName, decorFn) { - var origProvider = providerInjector.get(serviceName + providerSuffix), - orig$get = origProvider.$get; - - origProvider.$get = function() { - var origInstance = instanceInjector.invoke(orig$get, origProvider); - return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); - }; - } - - //////////////////////////////////// - // Module Loading - //////////////////////////////////// - function loadModules(modulesToLoad) { - assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array'); - var runBlocks = [], moduleFn; - forEach(modulesToLoad, function(module) { - if (loadedModules.get(module)) return; - loadedModules.put(module, true); - - function runInvokeQueue(queue) { - var i, ii; - for (i = 0, ii = queue.length; i < ii; i++) { - var invokeArgs = queue[i], - provider = providerInjector.get(invokeArgs[0]); - - provider[invokeArgs[1]].apply(provider, invokeArgs[2]); - } - } - - try { - if (isString(module)) { - moduleFn = angularModule(module); - runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); - runInvokeQueue(moduleFn._invokeQueue); - runInvokeQueue(moduleFn._configBlocks); - } else if (isFunction(module)) { - runBlocks.push(providerInjector.invoke(module)); - } else if (isArray(module)) { - runBlocks.push(providerInjector.invoke(module)); - } else { - assertArgFn(module, 'module'); - } - } catch (e) { - if (isArray(module)) { - module = module[module.length - 1]; - } - if (e.message && e.stack && e.stack.indexOf(e.message) == -1) { - // Safari & FF's stack traces don't contain error.message content - // unlike those of Chrome and IE - // So if stack doesn't contain message, we create a new string that contains both. - // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. - /* jshint -W022 */ - e = e.message + '\n' + e.stack; - } - throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", - module, e.stack || e.message || e); - } - }); - return runBlocks; - } - - //////////////////////////////////// - // internal Injector - //////////////////////////////////// - - function createInternalInjector(cache, factory) { - - function getService(serviceName, caller) { - if (cache.hasOwnProperty(serviceName)) { - if (cache[serviceName] === INSTANTIATING) { - throw $injectorMinErr('cdep', 'Circular dependency found: {0}', - serviceName + ' <- ' + path.join(' <- ')); - } - return cache[serviceName]; - } else { - try { - path.unshift(serviceName); - cache[serviceName] = INSTANTIATING; - return cache[serviceName] = factory(serviceName, caller); - } catch (err) { - if (cache[serviceName] === INSTANTIATING) { - delete cache[serviceName]; - } - throw err; - } finally { - path.shift(); - } - } - } - - function invoke(fn, self, locals, serviceName) { - if (typeof locals === 'string') { - serviceName = locals; - locals = null; - } - - var args = [], - $inject = createInjector.$$annotate(fn, strictDi, serviceName), - length, i, - key; - - for (i = 0, length = $inject.length; i < length; i++) { - key = $inject[i]; - if (typeof key !== 'string') { - throw $injectorMinErr('itkn', - 'Incorrect injection token! Expected service name as string, got {0}', key); - } - args.push( - locals && locals.hasOwnProperty(key) - ? locals[key] - : getService(key, serviceName) - ); - } - if (isArray(fn)) { - fn = fn[length]; - } - - // http://jsperf.com/angularjs-invoke-apply-vs-switch - // #5388 - return fn.apply(self, args); - } - - function instantiate(Type, locals, serviceName) { - // Check if Type is annotated and use just the given function at n-1 as parameter - // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); - // Object creation: http://jsperf.com/create-constructor/2 - var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null); - var returnedValue = invoke(Type, instance, locals, serviceName); - - return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; - } - - return { - invoke: invoke, - instantiate: instantiate, - get: getService, - annotate: createInjector.$$annotate, - has: function(name) { - return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); - } - }; - } -} - -createInjector.$$annotate = annotate; - -/** - * @ngdoc provider - * @name $anchorScrollProvider - * - * @description - * Use `$anchorScrollProvider` to disable automatic scrolling whenever - * {@link ng.$location#hash $location.hash()} changes. - */ -function $AnchorScrollProvider() { - - var autoScrollingEnabled = true; - - /** - * @ngdoc method - * @name $anchorScrollProvider#disableAutoScrolling - * - * @description - * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to - * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.
- * Use this method to disable automatic scrolling. - * - * If automatic scrolling is disabled, one must explicitly call - * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the - * current hash. - */ - this.disableAutoScrolling = function() { - autoScrollingEnabled = false; - }; - - /** - * @ngdoc service - * @name $anchorScroll - * @kind function - * @requires $window - * @requires $location - * @requires $rootScope - * - * @description - * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the - * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified - * in the - * [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document). - * - * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to - * match any anchor whenever it changes. This can be disabled by calling - * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}. - * - * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a - * vertical scroll-offset (either fixed or dynamic). - * - * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of - * {@link ng.$location#hash $location.hash()} will be used. - * - * @property {(number|function|jqLite)} yOffset - * If set, specifies a vertical scroll-offset. This is often useful when there are fixed - * positioned elements at the top of the page, such as navbars, headers etc. - * - * `yOffset` can be specified in various ways: - * - **number**: A fixed number of pixels to be used as offset.

- * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return - * a number representing the offset (in pixels).

- * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from - * the top of the page to the element's bottom will be used as offset.
- * **Note**: The element will be taken into account only as long as its `position` is set to - * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust - * their height and/or positioning according to the viewport's size. - * - *
- *
- * In order for `yOffset` to work properly, scrolling should take place on the document's root and - * not some child element. - *
- * - * @example - - -
- Go to bottom - You're at the bottom! -
- - - angular.module('anchorScrollExample', []) - .controller('ScrollController', ['$scope', '$location', '$anchorScroll', - function ($scope, $location, $anchorScroll) { - $scope.gotoBottom = function() { - // set the location.hash to the id of - // the element you wish to scroll to. - $location.hash('bottom'); - - // call $anchorScroll() - $anchorScroll(); - }; - }]); - - - #scrollArea { - height: 280px; - overflow: auto; - } - - #bottom { - display: block; - margin-top: 2000px; - } - - - * - *
- * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value). - * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details. - * - * @example - - - -
- Anchor {{x}} of 5 -
-
- - angular.module('anchorScrollOffsetExample', []) - .run(['$anchorScroll', function($anchorScroll) { - $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels - }]) - .controller('headerCtrl', ['$anchorScroll', '$location', '$scope', - function ($anchorScroll, $location, $scope) { - $scope.gotoAnchor = function(x) { - var newHash = 'anchor' + x; - if ($location.hash() !== newHash) { - // set the $location.hash to `newHash` and - // $anchorScroll will automatically scroll to it - $location.hash('anchor' + x); - } else { - // call $anchorScroll() explicitly, - // since $location.hash hasn't changed - $anchorScroll(); - } - }; - } - ]); - - - body { - padding-top: 50px; - } - - .anchor { - border: 2px dashed DarkOrchid; - padding: 10px 10px 200px 10px; - } - - .fixed-header { - background-color: rgba(0, 0, 0, 0.2); - height: 50px; - position: fixed; - top: 0; left: 0; right: 0; - } - - .fixed-header > a { - display: inline-block; - margin: 5px 15px; - } - -
- */ - this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { - var document = $window.document; - - // Helper function to get first anchor from a NodeList - // (using `Array#some()` instead of `angular#forEach()` since it's more performant - // and working in all supported browsers.) - function getFirstAnchor(list) { - var result = null; - Array.prototype.some.call(list, function(element) { - if (nodeName_(element) === 'a') { - result = element; - return true; - } - }); - return result; - } - - function getYOffset() { - - var offset = scroll.yOffset; - - if (isFunction(offset)) { - offset = offset(); - } else if (isElement(offset)) { - var elem = offset[0]; - var style = $window.getComputedStyle(elem); - if (style.position !== 'fixed') { - offset = 0; - } else { - offset = elem.getBoundingClientRect().bottom; - } - } else if (!isNumber(offset)) { - offset = 0; - } - - return offset; - } - - function scrollTo(elem) { - if (elem) { - elem.scrollIntoView(); - - var offset = getYOffset(); - - if (offset) { - // `offset` is the number of pixels we should scroll UP in order to align `elem` properly. - // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the - // top of the viewport. - // - // IF the number of pixels from the top of `elem` to the end of the page's content is less - // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some - // way down the page. - // - // This is often the case for elements near the bottom of the page. - // - // In such cases we do not need to scroll the whole `offset` up, just the difference between - // the top of the element and the offset, which is enough to align the top of `elem` at the - // desired position. - var elemTop = elem.getBoundingClientRect().top; - $window.scrollBy(0, elemTop - offset); - } - } else { - $window.scrollTo(0, 0); - } - } - - function scroll(hash) { - hash = isString(hash) ? hash : $location.hash(); - var elm; - - // empty hash, scroll to the top of the page - if (!hash) scrollTo(null); - - // element with given id - else if ((elm = document.getElementById(hash))) scrollTo(elm); - - // first anchor with given name :-D - else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm); - - // no element and hash == 'top', scroll to the top of the page - else if (hash === 'top') scrollTo(null); - } - - // does not scroll when user clicks on anchor link that is currently on - // (no url change, no $location.hash() change), browser native does scroll - if (autoScrollingEnabled) { - $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, - function autoScrollWatchAction(newVal, oldVal) { - // skip the initial scroll if $location.hash is empty - if (newVal === oldVal && newVal === '') return; - - jqLiteDocumentLoaded(function() { - $rootScope.$evalAsync(scroll); - }); - }); - } - - return scroll; - }]; -} - -var $animateMinErr = minErr('$animate'); -var ELEMENT_NODE = 1; -var NG_ANIMATE_CLASSNAME = 'ng-animate'; - -function mergeClasses(a,b) { - if (!a && !b) return ''; - if (!a) return b; - if (!b) return a; - if (isArray(a)) a = a.join(' '); - if (isArray(b)) b = b.join(' '); - return a + ' ' + b; -} - -function extractElementNode(element) { - for (var i = 0; i < element.length; i++) { - var elm = element[i]; - if (elm.nodeType === ELEMENT_NODE) { - return elm; - } - } -} - -function splitClasses(classes) { - if (isString(classes)) { - classes = classes.split(' '); - } - - // Use createMap() to prevent class assumptions involving property names in - // Object.prototype - var obj = createMap(); - forEach(classes, function(klass) { - // sometimes the split leaves empty string values - // incase extra spaces were applied to the options - if (klass.length) { - obj[klass] = true; - } - }); - return obj; -} - -// if any other type of options value besides an Object value is -// passed into the $animate.method() animation then this helper code -// will be run which will ignore it. While this patch is not the -// greatest solution to this, a lot of existing plugins depend on -// $animate to either call the callback (< 1.2) or return a promise -// that can be changed. This helper function ensures that the options -// are wiped clean incase a callback function is provided. -function prepareAnimateOptions(options) { - return isObject(options) - ? options - : {}; -} - -var $$CoreAnimateRunnerProvider = function() { - this.$get = ['$q', '$$rAF', function($q, $$rAF) { - function AnimateRunner() {} - AnimateRunner.all = noop; - AnimateRunner.chain = noop; - AnimateRunner.prototype = { - end: noop, - cancel: noop, - resume: noop, - pause: noop, - complete: noop, - then: function(pass, fail) { - return $q(function(resolve) { - $$rAF(function() { - resolve(); - }); - }).then(pass, fail); - } - }; - return AnimateRunner; - }]; -}; - -// this is prefixed with Core since it conflicts with -// the animateQueueProvider defined in ngAnimate/animateQueue.js -var $$CoreAnimateQueueProvider = function() { - var postDigestQueue = new HashMap(); - var postDigestElements = []; - - this.$get = ['$$AnimateRunner', '$rootScope', - function($$AnimateRunner, $rootScope) { - return { - enabled: noop, - on: noop, - off: noop, - pin: noop, - - push: function(element, event, options, domOperation) { - domOperation && domOperation(); - - options = options || {}; - options.from && element.css(options.from); - options.to && element.css(options.to); - - if (options.addClass || options.removeClass) { - addRemoveClassesPostDigest(element, options.addClass, options.removeClass); - } - - return new $$AnimateRunner(); // jshint ignore:line - } - }; - - function addRemoveClassesPostDigest(element, add, remove) { - var classVal, data = postDigestQueue.get(element); - - if (!data) { - postDigestQueue.put(element, data = {}); - postDigestElements.push(element); - } - - var updateData = function(classes, value) { - var changed = false; - if (classes) { - classes = isString(classes) ? classes.split(' ') : - isArray(classes) ? classes : []; - forEach(classes, function(className) { - if (className) { - changed = true; - data[className] = value; - } - }); - } - return changed; - }; - - var classesAdded = updateData(add, true); - var classesRemoved = updateData(remove, false); - if ((!classesAdded && !classesRemoved) || postDigestElements.length > 1) return; - - $rootScope.$$postDigest(function() { - forEach(postDigestElements, function(element) { - var data = postDigestQueue.get(element); - if (data) { - var existing = splitClasses(element.attr('class')); - var toAdd = ''; - var toRemove = ''; - forEach(data, function(status, className) { - var hasClass = !!existing[className]; - if (status !== hasClass) { - if (status) { - toAdd += (toAdd.length ? ' ' : '') + className; - } else { - toRemove += (toRemove.length ? ' ' : '') + className; - } - } - }); - - forEach(element, function(elm) { - toAdd && jqLiteAddClass(elm, toAdd); - toRemove && jqLiteRemoveClass(elm, toRemove); - }); - postDigestQueue.remove(element); - } - }); - - postDigestElements.length = 0; - }); - } - }]; -}; - -/** - * @ngdoc provider - * @name $animateProvider - * - * @description - * Default implementation of $animate that doesn't perform any animations, instead just - * synchronously performs DOM updates and resolves the returned runner promise. - * - * In order to enable animations the `ngAnimate` module has to be loaded. - * - * To see the functional implementation check out `src/ngAnimate/animate.js`. - */ -var $AnimateProvider = ['$provide', function($provide) { - var provider = this; - - this.$$registeredAnimations = Object.create(null); - - /** - * @ngdoc method - * @name $animateProvider#register - * - * @description - * Registers a new injectable animation factory function. The factory function produces the - * animation object which contains callback functions for each event that is expected to be - * animated. - * - * * `eventFn`: `function(element, ... , doneFunction, options)` - * The element to animate, the `doneFunction` and the options fed into the animation. Depending - * on the type of animation additional arguments will be injected into the animation function. The - * list below explains the function signatures for the different animation methods: - * - * - setClass: function(element, addedClasses, removedClasses, doneFunction, options) - * - addClass: function(element, addedClasses, doneFunction, options) - * - removeClass: function(element, removedClasses, doneFunction, options) - * - enter, leave, move: function(element, doneFunction, options) - * - animate: function(element, fromStyles, toStyles, doneFunction, options) - * - * Make sure to trigger the `doneFunction` once the animation is fully complete. - * - * ```js - * return { - * //enter, leave, move signature - * eventFn : function(element, done, options) { - * //code to run the animation - * //once complete, then run done() - * return function endFunction(wasCancelled) { - * //code to cancel the animation - * } - * } - * } - * ``` - * - * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to). - * @param {Function} factory The factory function that will be executed to return the animation - * object. - */ - this.register = function(name, factory) { - if (name && name.charAt(0) !== '.') { - throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name); - } - - var key = name + '-animation'; - provider.$$registeredAnimations[name.substr(1)] = key; - $provide.factory(key, factory); - }; - - /** - * @ngdoc method - * @name $animateProvider#classNameFilter - * - * @description - * Sets and/or returns the CSS class regular expression that is checked when performing - * an animation. Upon bootstrap the classNameFilter value is not set at all and will - * therefore enable $animate to attempt to perform an animation on any element that is triggered. - * When setting the `classNameFilter` value, animations will only be performed on elements - * that successfully match the filter expression. This in turn can boost performance - * for low-powered devices as well as applications containing a lot of structural operations. - * @param {RegExp=} expression The className expression which will be checked against all animations - * @return {RegExp} The current CSS className expression value. If null then there is no expression value - */ - this.classNameFilter = function(expression) { - if (arguments.length === 1) { - this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; - if (this.$$classNameFilter) { - var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)"); - if (reservedRegex.test(this.$$classNameFilter.toString())) { - throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME); - - } - } - } - return this.$$classNameFilter; - }; - - this.$get = ['$$animateQueue', function($$animateQueue) { - function domInsert(element, parentElement, afterElement) { - // if for some reason the previous element was removed - // from the dom sometime before this code runs then let's - // just stick to using the parent element as the anchor - if (afterElement) { - var afterNode = extractElementNode(afterElement); - if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) { - afterElement = null; - } - } - afterElement ? afterElement.after(element) : parentElement.prepend(element); - } - - /** - * @ngdoc service - * @name $animate - * @description The $animate service exposes a series of DOM utility methods that provide support - * for animation hooks. The default behavior is the application of DOM operations, however, - * when an animation is detected (and animations are enabled), $animate will do the heavy lifting - * to ensure that animation runs with the triggered DOM operation. - * - * By default $animate doesn't trigger an animations. This is because the `ngAnimate` module isn't - * included and only when it is active then the animation hooks that `$animate` triggers will be - * functional. Once active then all structural `ng-` directives will trigger animations as they perform - * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`, - * `ngShow`, `ngHide` and `ngMessages` also provide support for animations. - * - * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives. - * - * To learn more about enabling animation support, click here to visit the - * {@link ngAnimate ngAnimate module page}. - */ - return { - // we don't call it directly since non-existant arguments may - // be interpreted as null within the sub enabled function - - /** - * - * @ngdoc method - * @name $animate#on - * @kind function - * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...) - * has fired on the given element or among any of its children. Once the listener is fired, the provided callback - * is fired with the following params: - * - * ```js - * $animate.on('enter', container, - * function callback(element, phase) { - * // cool we detected an enter animation within the container - * } - * ); - * ``` - * - * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...) - * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself - * as well as among its children - * @param {Function} callback the callback function that will be fired when the listener is triggered - * - * The arguments present in the callback function are: - * * `element` - The captured DOM element that the animation was fired on. - * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends). - */ - on: $$animateQueue.on, - - /** - * - * @ngdoc method - * @name $animate#off - * @kind function - * @description Deregisters an event listener based on the event which has been associated with the provided element. This method - * can be used in three different ways depending on the arguments: - * - * ```js - * // remove all the animation event listeners listening for `enter` - * $animate.off('enter'); - * - * // remove all the animation event listeners listening for `enter` on the given element and its children - * $animate.off('enter', container); - * - * // remove the event listener function provided by `listenerFn` that is set - * // to listen for `enter` on the given `element` as well as its children - * $animate.off('enter', container, callback); - * ``` - * - * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...) - * @param {DOMElement=} container the container element the event listener was placed on - * @param {Function=} callback the callback function that was registered as the listener - */ - off: $$animateQueue.off, - - /** - * @ngdoc method - * @name $animate#pin - * @kind function - * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists - * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the - * element despite being outside the realm of the application or within another application. Say for example if the application - * was bootstrapped on an element that is somewhere inside of the `` tag, but we wanted to allow for an element to be situated - * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind - * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association. - * - * Note that this feature is only active when the `ngAnimate` module is used. - * - * @param {DOMElement} element the external element that will be pinned - * @param {DOMElement} parentElement the host parent element that will be associated with the external element - */ - pin: $$animateQueue.pin, - - /** - * - * @ngdoc method - * @name $animate#enabled - * @kind function - * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This - * function can be called in four ways: - * - * ```js - * // returns true or false - * $animate.enabled(); - * - * // changes the enabled state for all animations - * $animate.enabled(false); - * $animate.enabled(true); - * - * // returns true or false if animations are enabled for an element - * $animate.enabled(element); - * - * // changes the enabled state for an element and its children - * $animate.enabled(element, true); - * $animate.enabled(element, false); - * ``` - * - * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state - * @param {boolean=} enabled whether or not the animations will be enabled for the element - * - * @return {boolean} whether or not animations are enabled - */ - enabled: $$animateQueue.enabled, - - /** - * @ngdoc method - * @name $animate#cancel - * @kind function - * @description Cancels the provided animation. - * - * @param {Promise} animationPromise The animation promise that is returned when an animation is started. - */ - cancel: function(runner) { - runner.end && runner.end(); - }, - - /** - * - * @ngdoc method - * @name $animate#enter - * @kind function - * @description Inserts the element into the DOM either after the `after` element (if provided) or - * as the first child within the `parent` element and then triggers an animation. - * A promise is returned that will be resolved during the next digest once the animation - * has completed. - * - * @param {DOMElement} element the element which will be inserted into the DOM - * @param {DOMElement} parent the parent element which will append the element as - * a child (so long as the after element is not present) - * @param {DOMElement=} after the sibling element after which the element will be appended - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - enter: function(element, parent, after, options) { - parent = parent && jqLite(parent); - after = after && jqLite(after); - parent = parent || after.parent(); - domInsert(element, parent, after); - return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options)); - }, - - /** - * - * @ngdoc method - * @name $animate#move - * @kind function - * @description Inserts (moves) the element into its new position in the DOM either after - * the `after` element (if provided) or as the first child within the `parent` element - * and then triggers an animation. A promise is returned that will be resolved - * during the next digest once the animation has completed. - * - * @param {DOMElement} element the element which will be moved into the new DOM position - * @param {DOMElement} parent the parent element which will append the element as - * a child (so long as the after element is not present) - * @param {DOMElement=} after the sibling element after which the element will be appended - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - move: function(element, parent, after, options) { - parent = parent && jqLite(parent); - after = after && jqLite(after); - parent = parent || after.parent(); - domInsert(element, parent, after); - return $$animateQueue.push(element, 'move', prepareAnimateOptions(options)); - }, - - /** - * @ngdoc method - * @name $animate#leave - * @kind function - * @description Triggers an animation and then removes the element from the DOM. - * When the function is called a promise is returned that will be resolved during the next - * digest once the animation has completed. - * - * @param {DOMElement} element the element which will be removed from the DOM - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - leave: function(element, options) { - return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() { - element.remove(); - }); - }, - - /** - * @ngdoc method - * @name $animate#addClass - * @kind function - * - * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon - * execution, the addClass operation will only be handled after the next digest and it will not trigger an - * animation if element already contains the CSS class or if the class is removed at a later step. - * Note that class-based animations are treated differently compared to structural animations - * (like enter, move and leave) since the CSS classes may be added/removed at different points - * depending if CSS or JavaScript animations are used. - * - * @param {DOMElement} element the element which the CSS classes will be applied to - * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces) - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - addClass: function(element, className, options) { - options = prepareAnimateOptions(options); - options.addClass = mergeClasses(options.addclass, className); - return $$animateQueue.push(element, 'addClass', options); - }, - - /** - * @ngdoc method - * @name $animate#removeClass - * @kind function - * - * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon - * execution, the removeClass operation will only be handled after the next digest and it will not trigger an - * animation if element does not contain the CSS class or if the class is added at a later step. - * Note that class-based animations are treated differently compared to structural animations - * (like enter, move and leave) since the CSS classes may be added/removed at different points - * depending if CSS or JavaScript animations are used. - * - * @param {DOMElement} element the element which the CSS classes will be applied to - * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces) - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - removeClass: function(element, className, options) { - options = prepareAnimateOptions(options); - options.removeClass = mergeClasses(options.removeClass, className); - return $$animateQueue.push(element, 'removeClass', options); - }, - - /** - * @ngdoc method - * @name $animate#setClass - * @kind function - * - * @description Performs both the addition and removal of a CSS classes on an element and (during the process) - * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and - * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has - * passed. Note that class-based animations are treated differently compared to structural animations - * (like enter, move and leave) since the CSS classes may be added/removed at different points - * depending if CSS or JavaScript animations are used. - * - * @param {DOMElement} element the element which the CSS classes will be applied to - * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces) - * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces) - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - setClass: function(element, add, remove, options) { - options = prepareAnimateOptions(options); - options.addClass = mergeClasses(options.addClass, add); - options.removeClass = mergeClasses(options.removeClass, remove); - return $$animateQueue.push(element, 'setClass', options); - }, - - /** - * @ngdoc method - * @name $animate#animate - * @kind function - * - * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element. - * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take - * on the provided styles. For example, if a transition animation is set for the given className then the provided from and - * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles - * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter). - * - * @param {DOMElement} element the element which the CSS styles will be applied to - * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation. - * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation. - * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If - * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element. - * (Note that if no animation is detected then this value will not be appplied to the element.) - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - animate: function(element, from, to, className, options) { - options = prepareAnimateOptions(options); - options.from = options.from ? extend(options.from, from) : from; - options.to = options.to ? extend(options.to, to) : to; - - className = className || 'ng-inline-animate'; - options.tempClasses = mergeClasses(options.tempClasses, className); - return $$animateQueue.push(element, 'animate', options); - } - }; - }]; -}]; - -/** - * @ngdoc service - * @name $animateCss - * @kind object - * - * @description - * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included, - * then the `$animateCss` service will actually perform animations. - * - * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}. - */ -var $CoreAnimateCssProvider = function() { - this.$get = ['$$rAF', '$q', function($$rAF, $q) { - - var RAFPromise = function() {}; - RAFPromise.prototype = { - done: function(cancel) { - this.defer && this.defer[cancel === true ? 'reject' : 'resolve'](); - }, - end: function() { - this.done(); - }, - cancel: function() { - this.done(true); - }, - getPromise: function() { - if (!this.defer) { - this.defer = $q.defer(); - } - return this.defer.promise; - }, - then: function(f1,f2) { - return this.getPromise().then(f1,f2); - }, - 'catch': function(f1) { - return this.getPromise()['catch'](f1); - }, - 'finally': function(f1) { - return this.getPromise()['finally'](f1); - } - }; - - return function(element, options) { - if (options.from) { - element.css(options.from); - options.from = null; - } - - var closed, runner = new RAFPromise(); - return { - start: run, - end: run - }; - - function run() { - $$rAF(function() { - close(); - if (!closed) { - runner.done(); - } - closed = true; - }); - return runner; - } - - function close() { - if (options.addClass) { - element.addClass(options.addClass); - options.addClass = null; - } - if (options.removeClass) { - element.removeClass(options.removeClass); - options.removeClass = null; - } - if (options.to) { - element.css(options.to); - options.to = null; - } - } - }; - }]; -}; - -/* global stripHash: true */ - -/** - * ! This is a private undocumented service ! - * - * @name $browser - * @requires $log - * @description - * This object has two goals: - * - * - hide all the global state in the browser caused by the window object - * - abstract away all the browser specific features and inconsistencies - * - * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` - * service, which can be used for convenient testing of the application without the interaction with - * the real browser apis. - */ -/** - * @param {object} window The global window object. - * @param {object} document jQuery wrapped document. - * @param {object} $log window.console or an object with the same interface. - * @param {object} $sniffer $sniffer service - */ -function Browser(window, document, $log, $sniffer) { - var self = this, - rawDocument = document[0], - location = window.location, - history = window.history, - setTimeout = window.setTimeout, - clearTimeout = window.clearTimeout, - pendingDeferIds = {}; - - self.isMock = false; - - var outstandingRequestCount = 0; - var outstandingRequestCallbacks = []; - - // TODO(vojta): remove this temporary api - self.$$completeOutstandingRequest = completeOutstandingRequest; - self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; - - /** - * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` - * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. - */ - function completeOutstandingRequest(fn) { - try { - fn.apply(null, sliceArgs(arguments, 1)); - } finally { - outstandingRequestCount--; - if (outstandingRequestCount === 0) { - while (outstandingRequestCallbacks.length) { - try { - outstandingRequestCallbacks.pop()(); - } catch (e) { - $log.error(e); - } - } - } - } - } - - function getHash(url) { - var index = url.indexOf('#'); - return index === -1 ? '' : url.substr(index); - } - - /** - * @private - * Note: this method is used only by scenario runner - * TODO(vojta): prefix this method with $$ ? - * @param {function()} callback Function that will be called when no outstanding request - */ - self.notifyWhenNoOutstandingRequests = function(callback) { - if (outstandingRequestCount === 0) { - callback(); - } else { - outstandingRequestCallbacks.push(callback); - } - }; - - ////////////////////////////////////////////////////////////// - // URL API - ////////////////////////////////////////////////////////////// - - var cachedState, lastHistoryState, - lastBrowserUrl = location.href, - baseElement = document.find('base'), - reloadLocation = null; - - cacheState(); - lastHistoryState = cachedState; - - /** - * @name $browser#url - * - * @description - * GETTER: - * Without any argument, this method just returns current value of location.href. - * - * SETTER: - * With at least one argument, this method sets url to new value. - * If html5 history api supported, pushState/replaceState is used, otherwise - * location.href/location.replace is used. - * Returns its own instance to allow chaining - * - * NOTE: this api is intended for use only by the $location service. Please use the - * {@link ng.$location $location service} to change url. - * - * @param {string} url New url (when used as setter) - * @param {boolean=} replace Should new url replace current history record? - * @param {object=} state object to use with pushState/replaceState - */ - self.url = function(url, replace, state) { - // In modern browsers `history.state` is `null` by default; treating it separately - // from `undefined` would cause `$browser.url('/foo')` to change `history.state` - // to undefined via `pushState`. Instead, let's change `undefined` to `null` here. - if (isUndefined(state)) { - state = null; - } - - // Android Browser BFCache causes location, history reference to become stale. - if (location !== window.location) location = window.location; - if (history !== window.history) history = window.history; - - // setter - if (url) { - var sameState = lastHistoryState === state; - - // Don't change anything if previous and current URLs and states match. This also prevents - // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode. - // See https://github.com/angular/angular.js/commit/ffb2701 - if (lastBrowserUrl === url && (!$sniffer.history || sameState)) { - return self; - } - var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url); - lastBrowserUrl = url; - lastHistoryState = state; - // Don't use history API if only the hash changed - // due to a bug in IE10/IE11 which leads - // to not firing a `hashchange` nor `popstate` event - // in some cases (see #9143). - if ($sniffer.history && (!sameBase || !sameState)) { - history[replace ? 'replaceState' : 'pushState'](state, '', url); - cacheState(); - // Do the assignment again so that those two variables are referentially identical. - lastHistoryState = cachedState; - } else { - if (!sameBase || reloadLocation) { - reloadLocation = url; - } - if (replace) { - location.replace(url); - } else if (!sameBase) { - location.href = url; - } else { - location.hash = getHash(url); - } - } - return self; - // getter - } else { - // - reloadLocation is needed as browsers don't allow to read out - // the new location.href if a reload happened. - // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 - return reloadLocation || location.href.replace(/%27/g,"'"); - } - }; - - /** - * @name $browser#state - * - * @description - * This method is a getter. - * - * Return history.state or null if history.state is undefined. - * - * @returns {object} state - */ - self.state = function() { - return cachedState; - }; - - var urlChangeListeners = [], - urlChangeInit = false; - - function cacheStateAndFireUrlChange() { - cacheState(); - fireUrlChange(); - } - - function getCurrentState() { - try { - return history.state; - } catch (e) { - // MSIE can reportedly throw when there is no state (UNCONFIRMED). - } - } - - // This variable should be used *only* inside the cacheState function. - var lastCachedState = null; - function cacheState() { - // This should be the only place in $browser where `history.state` is read. - cachedState = getCurrentState(); - cachedState = isUndefined(cachedState) ? null : cachedState; - - // Prevent callbacks fo fire twice if both hashchange & popstate were fired. - if (equals(cachedState, lastCachedState)) { - cachedState = lastCachedState; - } - lastCachedState = cachedState; - } - - function fireUrlChange() { - if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) { - return; - } - - lastBrowserUrl = self.url(); - lastHistoryState = cachedState; - forEach(urlChangeListeners, function(listener) { - listener(self.url(), cachedState); - }); - } - - /** - * @name $browser#onUrlChange - * - * @description - * Register callback function that will be called, when url changes. - * - * It's only called when the url is changed from outside of angular: - * - user types different url into address bar - * - user clicks on history (forward/back) button - * - user clicks on a link - * - * It's not called when url is changed by $browser.url() method - * - * The listener gets called with new url as parameter. - * - * NOTE: this api is intended for use only by the $location service. Please use the - * {@link ng.$location $location service} to monitor url changes in angular apps. - * - * @param {function(string)} listener Listener function to be called when url changes. - * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. - */ - self.onUrlChange = function(callback) { - // TODO(vojta): refactor to use node's syntax for events - if (!urlChangeInit) { - // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) - // don't fire popstate when user change the address bar and don't fire hashchange when url - // changed by push/replaceState - - // html5 history api - popstate event - if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange); - // hashchange event - jqLite(window).on('hashchange', cacheStateAndFireUrlChange); - - urlChangeInit = true; - } - - urlChangeListeners.push(callback); - return callback; - }; - - /** - * @private - * Remove popstate and hashchange handler from window. - * - * NOTE: this api is intended for use only by $rootScope. - */ - self.$$applicationDestroyed = function() { - jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange); - }; - - /** - * Checks whether the url has changed outside of Angular. - * Needs to be exported to be able to check for changes that have been done in sync, - * as hashchange/popstate events fire in async. - */ - self.$$checkUrlChange = fireUrlChange; - - ////////////////////////////////////////////////////////////// - // Misc API - ////////////////////////////////////////////////////////////// - - /** - * @name $browser#baseHref - * - * @description - * Returns current - * (always relative - without domain) - * - * @returns {string} The current base href - */ - self.baseHref = function() { - var href = baseElement.attr('href'); - return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : ''; - }; - - /** - * @name $browser#defer - * @param {function()} fn A function, who's execution should be deferred. - * @param {number=} [delay=0] of milliseconds to defer the function execution. - * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. - * - * @description - * Executes a fn asynchronously via `setTimeout(fn, delay)`. - * - * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using - * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed - * via `$browser.defer.flush()`. - * - */ - self.defer = function(fn, delay) { - var timeoutId; - outstandingRequestCount++; - timeoutId = setTimeout(function() { - delete pendingDeferIds[timeoutId]; - completeOutstandingRequest(fn); - }, delay || 0); - pendingDeferIds[timeoutId] = true; - return timeoutId; - }; - - - /** - * @name $browser#defer.cancel - * - * @description - * Cancels a deferred task identified with `deferId`. - * - * @param {*} deferId Token returned by the `$browser.defer` function. - * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully - * canceled. - */ - self.defer.cancel = function(deferId) { - if (pendingDeferIds[deferId]) { - delete pendingDeferIds[deferId]; - clearTimeout(deferId); - completeOutstandingRequest(noop); - return true; - } - return false; - }; - -} - -function $BrowserProvider() { - this.$get = ['$window', '$log', '$sniffer', '$document', - function($window, $log, $sniffer, $document) { - return new Browser($window, $document, $log, $sniffer); - }]; -} - -/** - * @ngdoc service - * @name $cacheFactory - * - * @description - * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to - * them. - * - * ```js - * - * var cache = $cacheFactory('cacheId'); - * expect($cacheFactory.get('cacheId')).toBe(cache); - * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined(); - * - * cache.put("key", "value"); - * cache.put("another key", "another value"); - * - * // We've specified no options on creation - * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); - * - * ``` - * - * - * @param {string} cacheId Name or id of the newly created cache. - * @param {object=} options Options object that specifies the cache behavior. Properties: - * - * - `{number=}` `capacity` — turns the cache into LRU cache. - * - * @returns {object} Newly created cache object with the following set of methods: - * - * - `{object}` `info()` — Returns id, size, and options of cache. - * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns - * it. - * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. - * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. - * - `{void}` `removeAll()` — Removes all cached values. - * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. - * - * @example - - -
- - - - -

Cached Values

-
- - : - -
- -

Cache Info

-
- - : - -
-
-
- - angular.module('cacheExampleApp', []). - controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) { - $scope.keys = []; - $scope.cache = $cacheFactory('cacheId'); - $scope.put = function(key, value) { - if ($scope.cache.get(key) === undefined) { - $scope.keys.push(key); - } - $scope.cache.put(key, value === undefined ? null : value); - }; - }]); - - - p { - margin: 10px 0 3px; - } - -
- */ -function $CacheFactoryProvider() { - - this.$get = function() { - var caches = {}; - - function cacheFactory(cacheId, options) { - if (cacheId in caches) { - throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId); - } - - var size = 0, - stats = extend({}, options, {id: cacheId}), - data = {}, - capacity = (options && options.capacity) || Number.MAX_VALUE, - lruHash = {}, - freshEnd = null, - staleEnd = null; - - /** - * @ngdoc type - * @name $cacheFactory.Cache - * - * @description - * A cache object used to store and retrieve data, primarily used by - * {@link $http $http} and the {@link ng.directive:script script} directive to cache - * templates and other data. - * - * ```js - * angular.module('superCache') - * .factory('superCache', ['$cacheFactory', function($cacheFactory) { - * return $cacheFactory('super-cache'); - * }]); - * ``` - * - * Example test: - * - * ```js - * it('should behave like a cache', inject(function(superCache) { - * superCache.put('key', 'value'); - * superCache.put('another key', 'another value'); - * - * expect(superCache.info()).toEqual({ - * id: 'super-cache', - * size: 2 - * }); - * - * superCache.remove('another key'); - * expect(superCache.get('another key')).toBeUndefined(); - * - * superCache.removeAll(); - * expect(superCache.info()).toEqual({ - * id: 'super-cache', - * size: 0 - * }); - * })); - * ``` - */ - return caches[cacheId] = { - - /** - * @ngdoc method - * @name $cacheFactory.Cache#put - * @kind function - * - * @description - * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be - * retrieved later, and incrementing the size of the cache if the key was not already - * present in the cache. If behaving like an LRU cache, it will also remove stale - * entries from the set. - * - * It will not insert undefined values into the cache. - * - * @param {string} key the key under which the cached data is stored. - * @param {*} value the value to store alongside the key. If it is undefined, the key - * will not be stored. - * @returns {*} the value stored. - */ - put: function(key, value) { - if (isUndefined(value)) return; - if (capacity < Number.MAX_VALUE) { - var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); - - refresh(lruEntry); - } - - if (!(key in data)) size++; - data[key] = value; - - if (size > capacity) { - this.remove(staleEnd.key); - } - - return value; - }, - - /** - * @ngdoc method - * @name $cacheFactory.Cache#get - * @kind function - * - * @description - * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object. - * - * @param {string} key the key of the data to be retrieved - * @returns {*} the value stored. - */ - get: function(key) { - if (capacity < Number.MAX_VALUE) { - var lruEntry = lruHash[key]; - - if (!lruEntry) return; - - refresh(lruEntry); - } - - return data[key]; - }, - - - /** - * @ngdoc method - * @name $cacheFactory.Cache#remove - * @kind function - * - * @description - * Removes an entry from the {@link $cacheFactory.Cache Cache} object. - * - * @param {string} key the key of the entry to be removed - */ - remove: function(key) { - if (capacity < Number.MAX_VALUE) { - var lruEntry = lruHash[key]; - - if (!lruEntry) return; - - if (lruEntry == freshEnd) freshEnd = lruEntry.p; - if (lruEntry == staleEnd) staleEnd = lruEntry.n; - link(lruEntry.n,lruEntry.p); - - delete lruHash[key]; - } - - delete data[key]; - size--; - }, - - - /** - * @ngdoc method - * @name $cacheFactory.Cache#removeAll - * @kind function - * - * @description - * Clears the cache object of any entries. - */ - removeAll: function() { - data = {}; - size = 0; - lruHash = {}; - freshEnd = staleEnd = null; - }, - - - /** - * @ngdoc method - * @name $cacheFactory.Cache#destroy - * @kind function - * - * @description - * Destroys the {@link $cacheFactory.Cache Cache} object entirely, - * removing it from the {@link $cacheFactory $cacheFactory} set. - */ - destroy: function() { - data = null; - stats = null; - lruHash = null; - delete caches[cacheId]; - }, - - - /** - * @ngdoc method - * @name $cacheFactory.Cache#info - * @kind function - * - * @description - * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}. - * - * @returns {object} an object with the following properties: - *
    - *
  • **id**: the id of the cache instance
  • - *
  • **size**: the number of entries kept in the cache instance
  • - *
  • **...**: any additional properties from the options object when creating the - * cache.
  • - *
- */ - info: function() { - return extend({}, stats, {size: size}); - } - }; - - - /** - * makes the `entry` the freshEnd of the LRU linked list - */ - function refresh(entry) { - if (entry != freshEnd) { - if (!staleEnd) { - staleEnd = entry; - } else if (staleEnd == entry) { - staleEnd = entry.n; - } - - link(entry.n, entry.p); - link(entry, freshEnd); - freshEnd = entry; - freshEnd.n = null; - } - } - - - /** - * bidirectionally links two entries of the LRU linked list - */ - function link(nextEntry, prevEntry) { - if (nextEntry != prevEntry) { - if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify - if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify - } - } - } - - - /** - * @ngdoc method - * @name $cacheFactory#info - * - * @description - * Get information about all the caches that have been created - * - * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info` - */ - cacheFactory.info = function() { - var info = {}; - forEach(caches, function(cache, cacheId) { - info[cacheId] = cache.info(); - }); - return info; - }; - - - /** - * @ngdoc method - * @name $cacheFactory#get - * - * @description - * Get access to a cache object by the `cacheId` used when it was created. - * - * @param {string} cacheId Name or id of a cache to access. - * @returns {object} Cache object identified by the cacheId or undefined if no such cache. - */ - cacheFactory.get = function(cacheId) { - return caches[cacheId]; - }; - - - return cacheFactory; - }; -} - -/** - * @ngdoc service - * @name $templateCache - * - * @description - * The first time a template is used, it is loaded in the template cache for quick retrieval. You - * can load templates directly into the cache in a `script` tag, or by consuming the - * `$templateCache` service directly. - * - * Adding via the `script` tag: - * - * ```html - * - * ``` - * - * **Note:** the `script` tag containing the template does not need to be included in the `head` of - * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE, - * element with ng-app attribute), otherwise the template will be ignored. - * - * Adding via the `$templateCache` service: - * - * ```js - * var myApp = angular.module('myApp', []); - * myApp.run(function($templateCache) { - * $templateCache.put('templateId.html', 'This is the content of the template'); - * }); - * ``` - * - * To retrieve the template later, simply use it in your HTML: - * ```html - *
- * ``` - * - * or get it via Javascript: - * ```js - * $templateCache.get('templateId.html') - * ``` - * - * See {@link ng.$cacheFactory $cacheFactory}. - * - */ -function $TemplateCacheProvider() { - this.$get = ['$cacheFactory', function($cacheFactory) { - return $cacheFactory('templates'); - }]; -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Any commits to this file should be reviewed with security in mind. * - * Changes to this file can potentially create security vulnerabilities. * - * An approval from 2 Core members with history of modifying * - * this file is required. * - * * - * Does the change somehow allow for arbitrary javascript to be executed? * - * Or allows for someone to change the prototype of built-in objects? * - * Or gives undesired access to variables likes document or window? * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! - * - * DOM-related variables: - * - * - "node" - DOM Node - * - "element" - DOM Element or Node - * - "$node" or "$element" - jqLite-wrapped node or element - * - * - * Compiler related stuff: - * - * - "linkFn" - linking fn of a single directive - * - "nodeLinkFn" - function that aggregates all linking fns for a particular node - * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node - * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) - */ - - -/** - * @ngdoc service - * @name $compile - * @kind function - * - * @description - * Compiles an HTML string or DOM into a template and produces a template function, which - * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together. - * - * The compilation is a process of walking the DOM tree and matching DOM elements to - * {@link ng.$compileProvider#directive directives}. - * - *
- * **Note:** This document is an in-depth reference of all directive options. - * For a gentle introduction to directives with examples of common use cases, - * see the {@link guide/directive directive guide}. - *
- * - * ## Comprehensive Directive API - * - * There are many different options for a directive. - * - * The difference resides in the return value of the factory function. - * You can either return a "Directive Definition Object" (see below) that defines the directive properties, - * or just the `postLink` function (all other properties will have the default values). - * - *
- * **Best Practice:** It's recommended to use the "directive definition object" form. - *
- * - * Here's an example directive declared with a Directive Definition Object: - * - * ```js - * var myModule = angular.module(...); - * - * myModule.directive('directiveName', function factory(injectables) { - * var directiveDefinitionObject = { - * priority: 0, - * template: '
', // or // function(tElement, tAttrs) { ... }, - * // or - * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, - * transclude: false, - * restrict: 'A', - * templateNamespace: 'html', - * scope: false, - * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, - * controllerAs: 'stringIdentifier', - * bindToController: false, - * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], - * compile: function compile(tElement, tAttrs, transclude) { - * return { - * pre: function preLink(scope, iElement, iAttrs, controller) { ... }, - * post: function postLink(scope, iElement, iAttrs, controller) { ... } - * } - * // or - * // return function postLink( ... ) { ... } - * }, - * // or - * // link: { - * // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, - * // post: function postLink(scope, iElement, iAttrs, controller) { ... } - * // } - * // or - * // link: function postLink( ... ) { ... } - * }; - * return directiveDefinitionObject; - * }); - * ``` - * - *
- * **Note:** Any unspecified options will use the default value. You can see the default values below. - *
- * - * Therefore the above can be simplified as: - * - * ```js - * var myModule = angular.module(...); - * - * myModule.directive('directiveName', function factory(injectables) { - * var directiveDefinitionObject = { - * link: function postLink(scope, iElement, iAttrs) { ... } - * }; - * return directiveDefinitionObject; - * // or - * // return function postLink(scope, iElement, iAttrs) { ... } - * }); - * ``` - * - * - * - * ### Directive Definition Object - * - * The directive definition object provides instructions to the {@link ng.$compile - * compiler}. The attributes are: - * - * #### `multiElement` - * When this property is set to true, the HTML compiler will collect DOM nodes between - * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them - * together as the directive elements. It is recommended that this feature be used on directives - * which are not strictly behavioural (such as {@link ngClick}), and which - * do not manipulate or replace child nodes (such as {@link ngInclude}). - * - * #### `priority` - * When there are multiple directives defined on a single DOM element, sometimes it - * is necessary to specify the order in which the directives are applied. The `priority` is used - * to sort the directives before their `compile` functions get called. Priority is defined as a - * number. Directives with greater numerical `priority` are compiled first. Pre-link functions - * are also run in priority order, but post-link functions are run in reverse order. The order - * of directives with the same priority is undefined. The default priority is `0`. - * - * #### `terminal` - * If set to true then the current `priority` will be the last set of directives - * which will execute (any directives at the current priority will still execute - * as the order of execution on same `priority` is undefined). Note that expressions - * and other directives used in the directive's template will also be excluded from execution. - * - * #### `scope` - * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the - * same element request a new scope, only one new scope is created. The new scope rule does not - * apply for the root of the template since the root of the template always gets a new scope. - * - * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from - * normal scope in that it does not prototypically inherit from the parent scope. This is useful - * when creating reusable components, which should not accidentally read or modify data in the - * parent scope. - * - * The 'isolate' scope takes an object hash which defines a set of local scope properties - * derived from the parent scope. These local properties are useful for aliasing values for - * templates. Locals definition is a hash of local scope property to its source: - * - * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is - * always a string since DOM attributes are strings. If no `attr` name is specified then the - * attribute name is assumed to be the same as the local name. - * Given `` and widget definition - * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect - * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the - * `localName` property on the widget scope. The `name` is read from the parent scope (not - * component scope). - * - * * `=` or `=attr` - set up bi-directional binding between a local scope property and the - * parent scope property of name defined via the value of the `attr` attribute. If no `attr` - * name is specified then the attribute name is assumed to be the same as the local name. - * Given `` and widget definition of - * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the - * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected - * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent - * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You - * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If - * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use - * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional). - * - * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. - * If no `attr` name is specified then the attribute name is assumed to be the same as the - * local name. Given `` and widget definition of - * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to - * a function wrapper for the `count = count + value` expression. Often it's desirable to - * pass data from the isolated scope via an expression to the parent scope, this can be - * done by passing a map of local variable names and values into the expression wrapper fn. - * For example, if the expression is `increment(amount)` then we can specify the amount value - * by calling the `localFn` as `localFn({amount: 22})`. - * - * - * #### `bindToController` - * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will - * allow a component to have its properties bound to the controller, rather than to scope. When the controller - * is instantiated, the initial values of the isolate scope bindings are already available. - * - * #### `controller` - * Controller constructor function. The controller is instantiated before the - * pre-linking phase and it is shared with other directives (see - * `require` attribute). This allows the directives to communicate with each other and augment - * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals: - * - * * `$scope` - Current scope associated with the element - * * `$element` - Current element - * * `$attrs` - Current attributes object for the element - * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope: - * `function([scope], cloneLinkingFn, futureParentElement)`. - * * `scope`: optional argument to override the scope. - * * `cloneLinkingFn`: optional argument to create clones of the original transcluded content. - * * `futureParentElement`: - * * defines the parent to which the `cloneLinkingFn` will add the cloned elements. - * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`. - * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements) - * and when the `cloneLinkinFn` is passed, - * as those elements need to created and cloned in a special way when they are defined outside their - * usual containers (e.g. like ``). - * * See also the `directive.templateNamespace` property. - * - * - * #### `require` - * Require another directive and inject its controller as the fourth argument to the linking function. The - * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the - * injected argument will be an array in corresponding order. If no such directive can be - * found, or if the directive does not have a controller, then an error is raised (unless no link function - * is specified, in which case error checking is skipped). The name can be prefixed with: - * - * * (no prefix) - Locate the required controller on the current element. Throw an error if not found. - * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found. - * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found. - * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found. - * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass - * `null` to the `link` fn if not found. - * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass - * `null` to the `link` fn if not found. - * - * - * #### `controllerAs` - * Identifier name for a reference to the controller in the directive's scope. - * This allows the controller to be referenced from the directive template. The directive - * needs to define a scope for this configuration to be used. Useful in the case when - * directive is used as component. - * - * - * #### `restrict` - * String of subset of `EACM` which restricts the directive to a specific directive - * declaration style. If omitted, the defaults (elements and attributes) are used. - * - * * `E` - Element name (default): `` - * * `A` - Attribute (default): `
` - * * `C` - Class: `
` - * * `M` - Comment: `` - * - * - * #### `templateNamespace` - * String representing the document type used by the markup in the template. - * AngularJS needs this information as those elements need to be created and cloned - * in a special way when they are defined outside their usual containers like `` and ``. - * - * * `html` - All root nodes in the template are HTML. Root nodes may also be - * top-level elements such as `` or ``. - * * `svg` - The root nodes in the template are SVG elements (excluding ``). - * * `math` - The root nodes in the template are MathML elements (excluding ``). - * - * If no `templateNamespace` is specified, then the namespace is considered to be `html`. - * - * #### `template` - * HTML markup that may: - * * Replace the contents of the directive's element (default). - * * Replace the directive's element itself (if `replace` is true - DEPRECATED). - * * Wrap the contents of the directive's element (if `transclude` is true). - * - * Value may be: - * - * * A string. For example `
{{delete_str}}
`. - * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile` - * function api below) and returns a string value. - * - * - * #### `templateUrl` - * This is similar to `template` but the template is loaded from the specified URL, asynchronously. - * - * Because template loading is asynchronous the compiler will suspend compilation of directives on that element - * for later when the template has been resolved. In the meantime it will continue to compile and link - * sibling and parent elements as though this element had not contained any directives. - * - * The compiler does not suspend the entire compilation to wait for templates to be loaded because this - * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the - * case when only one deeply nested directive has `templateUrl`. - * - * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache} - * - * You can specify `templateUrl` as a string representing the URL or as a function which takes two - * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns - * a string value representing the url. In either case, the template URL is passed through {@link - * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}. - * - * - * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0) - * specify what the template should replace. Defaults to `false`. - * - * * `true` - the template will replace the directive's element. - * * `false` - the template will replace the contents of the directive's element. - * - * The replacement process migrates all of the attributes / classes from the old element to the new - * one. See the {@link guide/directive#template-expanding-directive - * Directives Guide} for an example. - * - * There are very few scenarios where element replacement is required for the application function, - * the main one being reusable custom components that are used within SVG contexts - * (because SVG doesn't work with custom elements in the DOM tree). - * - * #### `transclude` - * Extract the contents of the element where the directive appears and make it available to the directive. - * The contents are compiled and provided to the directive as a **transclusion function**. See the - * {@link $compile#transclusion Transclusion} section below. - * - * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the - * directive's element or the entire element: - * - * * `true` - transclude the content (i.e. the child nodes) of the directive's element. - * * `'element'` - transclude the whole of the directive's element including any directives on this - * element that defined at a lower priority than this directive. When used, the `template` - * property is ignored. - * - * - * #### `compile` - * - * ```js - * function compile(tElement, tAttrs, transclude) { ... } - * ``` - * - * The compile function deals with transforming the template DOM. Since most directives do not do - * template transformation, it is not used often. The compile function takes the following arguments: - * - * * `tElement` - template element - The element where the directive has been declared. It is - * safe to do template transformation on the element and child elements only. - * - * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared - * between all directive compile functions. - * - * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)` - * - *
- * **Note:** The template instance and the link instance may be different objects if the template has - * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that - * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration - * should be done in a linking function rather than in a compile function. - *
- - *
- * **Note:** The compile function cannot handle directives that recursively use themselves in their - * own templates or compile functions. Compiling these directives results in an infinite loop and a - * stack overflow errors. - * - * This can be avoided by manually using $compile in the postLink function to imperatively compile - * a directive's template instead of relying on automatic template compilation via `template` or - * `templateUrl` declaration or manual compilation inside the compile function. - *
- * - *
- * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it - * e.g. does not know about the right outer scope. Please use the transclude function that is passed - * to the link function instead. - *
- - * A compile function can have a return value which can be either a function or an object. - * - * * returning a (post-link) function - is equivalent to registering the linking function via the - * `link` property of the config object when the compile function is empty. - * - * * returning an object with function(s) registered via `pre` and `post` properties - allows you to - * control when a linking function should be called during the linking phase. See info about - * pre-linking and post-linking functions below. - * - * - * #### `link` - * This property is used only if the `compile` property is not defined. - * - * ```js - * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... } - * ``` - * - * The link function is responsible for registering DOM listeners as well as updating the DOM. It is - * executed after the template has been cloned. This is where most of the directive logic will be - * put. - * - * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the - * directive for registering {@link ng.$rootScope.Scope#$watch watches}. - * - * * `iElement` - instance element - The element where the directive is to be used. It is safe to - * manipulate the children of the element only in `postLink` function since the children have - * already been linked. - * - * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared - * between all directive linking functions. - * - * * `controller` - the directive's required controller instance(s) - Instances are shared - * among all directives, which allows the directives to use the controllers as a communication - * channel. The exact value depends on the directive's `require` property: - * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one - * * `string`: the controller instance - * * `array`: array of controller instances - * - * If a required controller cannot be found, and it is optional, the instance is `null`, - * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown. - * - * Note that you can also require the directive's own controller - it will be made available like - * any other controller. - * - * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. - * This is the same as the `$transclude` - * parameter of directive controllers, see there for details. - * `function([scope], cloneLinkingFn, futureParentElement)`. - * - * #### Pre-linking function - * - * Executed before the child elements are linked. Not safe to do DOM transformation since the - * compiler linking function will fail to locate the correct elements for linking. - * - * #### Post-linking function - * - * Executed after the child elements are linked. - * - * Note that child elements that contain `templateUrl` directives will not have been compiled - * and linked since they are waiting for their template to load asynchronously and their own - * compilation and linking has been suspended until that occurs. - * - * It is safe to do DOM transformation in the post-linking function on elements that are not waiting - * for their async templates to be resolved. - * - * - * ### Transclusion - * - * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and - * copying them to another part of the DOM, while maintaining their connection to the original AngularJS - * scope from where they were taken. - * - * Transclusion is used (often with {@link ngTransclude}) to insert the - * original contents of a directive's element into a specified place in the template of the directive. - * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded - * content has access to the properties on the scope from which it was taken, even if the directive - * has isolated scope. - * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}. - * - * This makes it possible for the widget to have private state for its template, while the transcluded - * content has access to its originating scope. - * - *
- * **Note:** When testing an element transclude directive you must not place the directive at the root of the - * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives - * Testing Transclusion Directives}. - *
- * - * #### Transclusion Functions - * - * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion - * function** to the directive's `link` function and `controller`. This transclusion function is a special - * **linking function** that will return the compiled contents linked to a new transclusion scope. - * - *
- * If you are just using {@link ngTransclude} then you don't need to worry about this function, since - * ngTransclude will deal with it for us. - *
- * - * If you want to manually control the insertion and removal of the transcluded content in your directive - * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery - * object that contains the compiled DOM, which is linked to the correct transclusion scope. - * - * When you call a transclusion function you can pass in a **clone attach function**. This function accepts - * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded - * content and the `scope` is the newly created transclusion scope, to which the clone is bound. - * - *
- * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function - * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope. - *
- * - * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone - * attach function**: - * - * ```js - * var transcludedContent, transclusionScope; - * - * $transclude(function(clone, scope) { - * element.append(clone); - * transcludedContent = clone; - * transclusionScope = scope; - * }); - * ``` - * - * Later, if you want to remove the transcluded content from your DOM then you should also destroy the - * associated transclusion scope: - * - * ```js - * transcludedContent.remove(); - * transclusionScope.$destroy(); - * ``` - * - *
- * **Best Practice**: if you intend to add and remove transcluded content manually in your directive - * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it), - * then you are also responsible for calling `$destroy` on the transclusion scope. - *
- * - * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat} - * automatically destroy their transluded clones as necessary so you do not need to worry about this if - * you are simply using {@link ngTransclude} to inject the transclusion into your directive. - * - * - * #### Transclusion Scopes - * - * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion - * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed - * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it - * was taken. - * - * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look - * like this: - * - * ```html - *
- *
- *
- *
- *
- *
- * ``` - * - * The `$parent` scope hierarchy will look like this: - * - * ``` - * - $rootScope - * - isolate - * - transclusion - * ``` - * - * but the scopes will inherit prototypically from different scopes to their `$parent`. - * - * ``` - * - $rootScope - * - transclusion - * - isolate - * ``` - * - * - * ### Attributes - * - * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the - * `link()` or `compile()` functions. It has a variety of uses. - * - * accessing *Normalized attribute names:* - * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. - * the attributes object allows for normalized access to - * the attributes. - * - * * *Directive inter-communication:* All directives share the same instance of the attributes - * object which allows the directives to use the attributes object as inter directive - * communication. - * - * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object - * allowing other directives to read the interpolated value. - * - * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes - * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also - * the only way to easily get the actual value because during the linking phase the interpolation - * hasn't been evaluated yet and so the value is at this time set to `undefined`. - * - * ```js - * function linkingFn(scope, elm, attrs, ctrl) { - * // get the attribute value - * console.log(attrs.ngModel); - * - * // change the attribute - * attrs.$set('ngModel', 'new value'); - * - * // observe changes to interpolated attribute - * attrs.$observe('ngModel', function(value) { - * console.log('ngModel has changed value to ' + value); - * }); - * } - * ``` - * - * ## Example - * - *
- * **Note**: Typically directives are registered with `module.directive`. The example below is - * to illustrate how `$compile` works. - *
- * - - - -
-
-
-
-
-
- - it('should auto compile', function() { - var textarea = $('textarea'); - var output = $('div[compile]'); - // The initial state reads 'Hello Angular'. - expect(output.getText()).toBe('Hello Angular'); - textarea.clear(); - textarea.sendKeys('{{name}}!'); - expect(output.getText()).toBe('Angular!'); - }); - -
- - * - * - * @param {string|DOMElement} element Element or HTML string to compile into a template function. - * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED. - * - *
- * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it - * e.g. will not use the right outer scope. Please pass the transclude function as a - * `parentBoundTranscludeFn` to the link function instead. - *
- * - * @param {number} maxPriority only apply directives lower than given priority (Only effects the - * root element(s), not their children) - * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template - * (a DOM element/tree) to a scope. Where: - * - * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. - * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the - * `template` and call the `cloneAttachFn` function allowing the caller to attach the - * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is - * called as:
`cloneAttachFn(clonedElement, scope)` where: - * - * * `clonedElement` - is a clone of the original `element` passed into the compiler. - * * `scope` - is the current scope with which the linking function is working with. - * - * * `options` - An optional object hash with linking options. If `options` is provided, then the following - * keys may be used to control linking behavior: - * - * * `parentBoundTranscludeFn` - the transclude function made available to - * directives; if given, it will be passed through to the link functions of - * directives found in `element` during compilation. - * * `transcludeControllers` - an object hash with keys that map controller names - * to controller instances; if given, it will make the controllers - * available to directives. - * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add - * the cloned elements; only needed for transcludes that are allowed to contain non html - * elements (e.g. SVG elements). See also the directive.controller property. - * - * Calling the linking function returns the element of the template. It is either the original - * element passed in, or the clone of the element if the `cloneAttachFn` is provided. - * - * After linking the view is not updated until after a call to $digest which typically is done by - * Angular automatically. - * - * If you need access to the bound view, there are two ways to do it: - * - * - If you are not asking the linking function to clone the template, create the DOM element(s) - * before you send them to the compiler and keep this reference around. - * ```js - * var element = $compile('

{{total}}

')(scope); - * ``` - * - * - if on the other hand, you need the element to be cloned, the view reference from the original - * example would not point to the clone, but rather to the original template that was cloned. In - * this case, you can access the clone via the cloneAttachFn: - * ```js - * var templateElement = angular.element('

{{total}}

'), - * scope = ....; - * - * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) { - * //attach the clone to DOM document at the right place - * }); - * - * //now we have reference to the cloned DOM via `clonedElement` - * ``` - * - * - * For information on how the compiler works, see the - * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. - */ - -var $compileMinErr = minErr('$compile'); - -/** - * @ngdoc provider - * @name $compileProvider - * - * @description - */ -$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider']; -function $CompileProvider($provide, $$sanitizeUriProvider) { - var hasDirectives = {}, - Suffix = 'Directive', - COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/, - CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/, - ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'), - REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/; - - // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes - // The assumption is that future DOM event attribute names will begin with - // 'on' and be composed of only English letters. - var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; - - function parseIsolateBindings(scope, directiveName, isController) { - var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/; - - var bindings = {}; - - forEach(scope, function(definition, scopeName) { - var match = definition.match(LOCAL_REGEXP); - - if (!match) { - throw $compileMinErr('iscp', - "Invalid {3} for directive '{0}'." + - " Definition: {... {1}: '{2}' ...}", - directiveName, scopeName, definition, - (isController ? "controller bindings definition" : - "isolate scope definition")); - } - - bindings[scopeName] = { - mode: match[1][0], - collection: match[2] === '*', - optional: match[3] === '?', - attrName: match[4] || scopeName - }; - }); - - return bindings; - } - - function parseDirectiveBindings(directive, directiveName) { - var bindings = { - isolateScope: null, - bindToController: null - }; - if (isObject(directive.scope)) { - if (directive.bindToController === true) { - bindings.bindToController = parseIsolateBindings(directive.scope, - directiveName, true); - bindings.isolateScope = {}; - } else { - bindings.isolateScope = parseIsolateBindings(directive.scope, - directiveName, false); - } - } - if (isObject(directive.bindToController)) { - bindings.bindToController = - parseIsolateBindings(directive.bindToController, directiveName, true); - } - if (isObject(bindings.bindToController)) { - var controller = directive.controller; - var controllerAs = directive.controllerAs; - if (!controller) { - // There is no controller, there may or may not be a controllerAs property - throw $compileMinErr('noctrl', - "Cannot bind to controller without directive '{0}'s controller.", - directiveName); - } else if (!identifierForController(controller, controllerAs)) { - // There is a controller, but no identifier or controllerAs property - throw $compileMinErr('noident', - "Cannot bind to controller without identifier for directive '{0}'.", - directiveName); - } - } - return bindings; - } - - function assertValidDirectiveName(name) { - var letter = name.charAt(0); - if (!letter || letter !== lowercase(letter)) { - throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name); - } - if (name !== name.trim()) { - throw $compileMinErr('baddir', - "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces", - name); - } - } - - /** - * @ngdoc method - * @name $compileProvider#directive - * @kind function - * - * @description - * Register a new directive with the compiler. - * - * @param {string|Object} name Name of the directive in camel-case (i.e. ngBind which - * will match as ng-bind), or an object map of directives where the keys are the - * names and the values are the factories. - * @param {Function|Array} directiveFactory An injectable directive factory function. See - * {@link guide/directive} for more info. - * @returns {ng.$compileProvider} Self for chaining. - */ - this.directive = function registerDirective(name, directiveFactory) { - assertNotHasOwnProperty(name, 'directive'); - if (isString(name)) { - assertValidDirectiveName(name); - assertArg(directiveFactory, 'directiveFactory'); - if (!hasDirectives.hasOwnProperty(name)) { - hasDirectives[name] = []; - $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', - function($injector, $exceptionHandler) { - var directives = []; - forEach(hasDirectives[name], function(directiveFactory, index) { - try { - var directive = $injector.invoke(directiveFactory); - if (isFunction(directive)) { - directive = { compile: valueFn(directive) }; - } else if (!directive.compile && directive.link) { - directive.compile = valueFn(directive.link); - } - directive.priority = directive.priority || 0; - directive.index = index; - directive.name = directive.name || name; - directive.require = directive.require || (directive.controller && directive.name); - directive.restrict = directive.restrict || 'EA'; - var bindings = directive.$$bindings = - parseDirectiveBindings(directive, directive.name); - if (isObject(bindings.isolateScope)) { - directive.$$isolateBindings = bindings.isolateScope; - } - directive.$$moduleName = directiveFactory.$$moduleName; - directives.push(directive); - } catch (e) { - $exceptionHandler(e); - } - }); - return directives; - }]); - } - hasDirectives[name].push(directiveFactory); - } else { - forEach(name, reverseParams(registerDirective)); - } - return this; - }; - - - /** - * @ngdoc method - * @name $compileProvider#aHrefSanitizationWhitelist - * @kind function - * - * @description - * Retrieves or overrides the default regular expression that is used for whitelisting of safe - * urls during a[href] sanitization. - * - * The sanitization is a security measure aimed at preventing XSS attacks via html links. - * - * Any url about to be assigned to a[href] via data-binding is first normalized and turned into - * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` - * regular expression. If a match is found, the original url is written into the dom. Otherwise, - * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. - * - * @param {RegExp=} regexp New regexp to whitelist urls with. - * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for - * chaining otherwise. - */ - this.aHrefSanitizationWhitelist = function(regexp) { - if (isDefined(regexp)) { - $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp); - return this; - } else { - return $$sanitizeUriProvider.aHrefSanitizationWhitelist(); - } - }; - - - /** - * @ngdoc method - * @name $compileProvider#imgSrcSanitizationWhitelist - * @kind function - * - * @description - * Retrieves or overrides the default regular expression that is used for whitelisting of safe - * urls during img[src] sanitization. - * - * The sanitization is a security measure aimed at prevent XSS attacks via html links. - * - * Any url about to be assigned to img[src] via data-binding is first normalized and turned into - * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` - * regular expression. If a match is found, the original url is written into the dom. Otherwise, - * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. - * - * @param {RegExp=} regexp New regexp to whitelist urls with. - * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for - * chaining otherwise. - */ - this.imgSrcSanitizationWhitelist = function(regexp) { - if (isDefined(regexp)) { - $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp); - return this; - } else { - return $$sanitizeUriProvider.imgSrcSanitizationWhitelist(); - } - }; - - /** - * @ngdoc method - * @name $compileProvider#debugInfoEnabled - * - * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the - * current debugInfoEnabled state - * @returns {*} current value if used as getter or itself (chaining) if used as setter - * - * @kind function - * - * @description - * Call this method to enable/disable various debug runtime information in the compiler such as adding - * binding information and a reference to the current scope on to DOM elements. - * If enabled, the compiler will add the following to DOM elements that have been bound to the scope - * * `ng-binding` CSS class - * * `$binding` data property containing an array of the binding expressions - * - * You may want to disable this in production for a significant performance boost. See - * {@link guide/production#disabling-debug-data Disabling Debug Data} for more. - * - * The default value is true. - */ - var debugInfoEnabled = true; - this.debugInfoEnabled = function(enabled) { - if (isDefined(enabled)) { - debugInfoEnabled = enabled; - return this; - } - return debugInfoEnabled; - }; - - this.$get = [ - '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse', - '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri', - function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse, - $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) { - - var Attributes = function(element, attributesToCopy) { - if (attributesToCopy) { - var keys = Object.keys(attributesToCopy); - var i, l, key; - - for (i = 0, l = keys.length; i < l; i++) { - key = keys[i]; - this[key] = attributesToCopy[key]; - } - } else { - this.$attr = {}; - } - - this.$$element = element; - }; - - Attributes.prototype = { - /** - * @ngdoc method - * @name $compile.directive.Attributes#$normalize - * @kind function - * - * @description - * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or - * `data-`) to its normalized, camelCase form. - * - * Also there is special case for Moz prefix starting with upper case letter. - * - * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives} - * - * @param {string} name Name to normalize - */ - $normalize: directiveNormalize, - - - /** - * @ngdoc method - * @name $compile.directive.Attributes#$addClass - * @kind function - * - * @description - * Adds the CSS class value specified by the classVal parameter to the element. If animations - * are enabled then an animation will be triggered for the class addition. - * - * @param {string} classVal The className value that will be added to the element - */ - $addClass: function(classVal) { - if (classVal && classVal.length > 0) { - $animate.addClass(this.$$element, classVal); - } - }, - - /** - * @ngdoc method - * @name $compile.directive.Attributes#$removeClass - * @kind function - * - * @description - * Removes the CSS class value specified by the classVal parameter from the element. If - * animations are enabled then an animation will be triggered for the class removal. - * - * @param {string} classVal The className value that will be removed from the element - */ - $removeClass: function(classVal) { - if (classVal && classVal.length > 0) { - $animate.removeClass(this.$$element, classVal); - } - }, - - /** - * @ngdoc method - * @name $compile.directive.Attributes#$updateClass - * @kind function - * - * @description - * Adds and removes the appropriate CSS class values to the element based on the difference - * between the new and old CSS class values (specified as newClasses and oldClasses). - * - * @param {string} newClasses The current CSS className value - * @param {string} oldClasses The former CSS className value - */ - $updateClass: function(newClasses, oldClasses) { - var toAdd = tokenDifference(newClasses, oldClasses); - if (toAdd && toAdd.length) { - $animate.addClass(this.$$element, toAdd); - } - - var toRemove = tokenDifference(oldClasses, newClasses); - if (toRemove && toRemove.length) { - $animate.removeClass(this.$$element, toRemove); - } - }, - - /** - * Set a normalized attribute on the element in a way such that all directives - * can share the attribute. This function properly handles boolean attributes. - * @param {string} key Normalized key. (ie ngAttribute) - * @param {string|boolean} value The value to set. If `null` attribute will be deleted. - * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. - * Defaults to true. - * @param {string=} attrName Optional none normalized name. Defaults to key. - */ - $set: function(key, value, writeAttr, attrName) { - // TODO: decide whether or not to throw an error if "class" - //is set through this function since it may cause $updateClass to - //become unstable. - - var node = this.$$element[0], - booleanKey = getBooleanAttrName(node, key), - aliasedKey = getAliasedAttrName(node, key), - observer = key, - nodeName; - - if (booleanKey) { - this.$$element.prop(key, value); - attrName = booleanKey; - } else if (aliasedKey) { - this[aliasedKey] = value; - observer = aliasedKey; - } - - this[key] = value; - - // translate normalized key to actual key - if (attrName) { - this.$attr[key] = attrName; - } else { - attrName = this.$attr[key]; - if (!attrName) { - this.$attr[key] = attrName = snake_case(key, '-'); - } - } - - nodeName = nodeName_(this.$$element); - - if ((nodeName === 'a' && key === 'href') || - (nodeName === 'img' && key === 'src')) { - // sanitize a[href] and img[src] values - this[key] = value = $$sanitizeUri(value, key === 'src'); - } else if (nodeName === 'img' && key === 'srcset') { - // sanitize img[srcset] values - var result = ""; - - // first check if there are spaces because it's not the same pattern - var trimmedSrcset = trim(value); - // ( 999x ,| 999w ,| ,|, ) - var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/; - var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/; - - // split srcset into tuple of uri and descriptor except for the last item - var rawUris = trimmedSrcset.split(pattern); - - // for each tuples - var nbrUrisWith2parts = Math.floor(rawUris.length / 2); - for (var i = 0; i < nbrUrisWith2parts; i++) { - var innerIdx = i * 2; - // sanitize the uri - result += $$sanitizeUri(trim(rawUris[innerIdx]), true); - // add the descriptor - result += (" " + trim(rawUris[innerIdx + 1])); - } - - // split the last item into uri and descriptor - var lastTuple = trim(rawUris[i * 2]).split(/\s/); - - // sanitize the last uri - result += $$sanitizeUri(trim(lastTuple[0]), true); - - // and add the last descriptor if any - if (lastTuple.length === 2) { - result += (" " + trim(lastTuple[1])); - } - this[key] = value = result; - } - - if (writeAttr !== false) { - if (value === null || value === undefined) { - this.$$element.removeAttr(attrName); - } else { - this.$$element.attr(attrName, value); - } - } - - // fire observers - var $$observers = this.$$observers; - $$observers && forEach($$observers[observer], function(fn) { - try { - fn(value); - } catch (e) { - $exceptionHandler(e); - } - }); - }, - - - /** - * @ngdoc method - * @name $compile.directive.Attributes#$observe - * @kind function - * - * @description - * Observes an interpolated attribute. - * - * The observer function will be invoked once during the next `$digest` following - * compilation. The observer is then invoked whenever the interpolated value - * changes. - * - * @param {string} key Normalized key. (ie ngAttribute) . - * @param {function(interpolatedValue)} fn Function that will be called whenever - the interpolated value of the attribute changes. - * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info. - * @returns {function()} Returns a deregistration function for this observer. - */ - $observe: function(key, fn) { - var attrs = this, - $$observers = (attrs.$$observers || (attrs.$$observers = createMap())), - listeners = ($$observers[key] || ($$observers[key] = [])); - - listeners.push(fn); - $rootScope.$evalAsync(function() { - if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) { - // no one registered attribute interpolation function, so lets call it manually - fn(attrs[key]); - } - }); - - return function() { - arrayRemove(listeners, fn); - }; - } - }; - - - function safeAddClass($element, className) { - try { - $element.addClass(className); - } catch (e) { - // ignore, since it means that we are trying to set class on - // SVG element, where class name is read-only. - } - } - - - var startSymbol = $interpolate.startSymbol(), - endSymbol = $interpolate.endSymbol(), - denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}') - ? identity - : function denormalizeTemplate(template) { - return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); - }, - NG_ATTR_BINDING = /^ngAttr[A-Z]/; - - compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) { - var bindings = $element.data('$binding') || []; - - if (isArray(binding)) { - bindings = bindings.concat(binding); - } else { - bindings.push(binding); - } - - $element.data('$binding', bindings); - } : noop; - - compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) { - safeAddClass($element, 'ng-binding'); - } : noop; - - compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) { - var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope'; - $element.data(dataName, scope); - } : noop; - - compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) { - safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope'); - } : noop; - - return compile; - - //================================ - - function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, - previousCompileContext) { - if (!($compileNodes instanceof jqLite)) { - // jquery always rewraps, whereas we need to preserve the original selector so that we can - // modify it. - $compileNodes = jqLite($compileNodes); - } - // We can not compile top level text elements since text nodes can be merged and we will - // not be able to attach scope data to them, so we will wrap them in - forEach($compileNodes, function(node, index) { - if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) { - $compileNodes[index] = jqLite(node).wrap('').parent()[0]; - } - }); - var compositeLinkFn = - compileNodes($compileNodes, transcludeFn, $compileNodes, - maxPriority, ignoreDirective, previousCompileContext); - compile.$$addScopeClass($compileNodes); - var namespace = null; - return function publicLinkFn(scope, cloneConnectFn, options) { - assertArg(scope, 'scope'); - - options = options || {}; - var parentBoundTranscludeFn = options.parentBoundTranscludeFn, - transcludeControllers = options.transcludeControllers, - futureParentElement = options.futureParentElement; - - // When `parentBoundTranscludeFn` is passed, it is a - // `controllersBoundTransclude` function (it was previously passed - // as `transclude` to directive.link) so we must unwrap it to get - // its `boundTranscludeFn` - if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) { - parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude; - } - - if (!namespace) { - namespace = detectNamespaceForChildElements(futureParentElement); - } - var $linkNode; - if (namespace !== 'html') { - // When using a directive with replace:true and templateUrl the $compileNodes - // (or a child element inside of them) - // might change, so we need to recreate the namespace adapted compileNodes - // for call to the link function. - // Note: This will already clone the nodes... - $linkNode = jqLite( - wrapTemplate(namespace, jqLite('
').append($compileNodes).html()) - ); - } else if (cloneConnectFn) { - // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart - // and sometimes changes the structure of the DOM. - $linkNode = JQLitePrototype.clone.call($compileNodes); - } else { - $linkNode = $compileNodes; - } - - if (transcludeControllers) { - for (var controllerName in transcludeControllers) { - $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance); - } - } - - compile.$$addScopeInfo($linkNode, scope); - - if (cloneConnectFn) cloneConnectFn($linkNode, scope); - if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn); - return $linkNode; - }; - } - - function detectNamespaceForChildElements(parentElement) { - // TODO: Make this detect MathML as well... - var node = parentElement && parentElement[0]; - if (!node) { - return 'html'; - } else { - return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html'; - } - } - - /** - * Compile function matches each node in nodeList against the directives. Once all directives - * for a particular node are collected their compile functions are executed. The compile - * functions return values - the linking functions - are combined into a composite linking - * function, which is the a linking function for the node. - * - * @param {NodeList} nodeList an array of nodes or NodeList to compile - * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the - * scope argument is auto-generated to the new child of the transcluded parent scope. - * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then - * the rootElement must be set the jqLite collection of the compile root. This is - * needed so that the jqLite collection items can be replaced with widgets. - * @param {number=} maxPriority Max directive priority. - * @returns {Function} A composite linking function of all of the matched directives or null. - */ - function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective, - previousCompileContext) { - var linkFns = [], - attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound; - - for (var i = 0; i < nodeList.length; i++) { - attrs = new Attributes(); - - // we must always refer to nodeList[i] since the nodes can be replaced underneath us. - directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined, - ignoreDirective); - - nodeLinkFn = (directives.length) - ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement, - null, [], [], previousCompileContext) - : null; - - if (nodeLinkFn && nodeLinkFn.scope) { - compile.$$addScopeClass(attrs.$$element); - } - - childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || - !(childNodes = nodeList[i].childNodes) || - !childNodes.length) - ? null - : compileNodes(childNodes, - nodeLinkFn ? ( - (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement) - && nodeLinkFn.transclude) : transcludeFn); - - if (nodeLinkFn || childLinkFn) { - linkFns.push(i, nodeLinkFn, childLinkFn); - linkFnFound = true; - nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn; - } - - //use the previous context only for the first element in the virtual group - previousCompileContext = null; - } - - // return a linking function if we have found anything, null otherwise - return linkFnFound ? compositeLinkFn : null; - - function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) { - var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn; - var stableNodeList; - - - if (nodeLinkFnFound) { - // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our - // offsets don't get screwed up - var nodeListLength = nodeList.length; - stableNodeList = new Array(nodeListLength); - - // create a sparse array by only copying the elements which have a linkFn - for (i = 0; i < linkFns.length; i+=3) { - idx = linkFns[i]; - stableNodeList[idx] = nodeList[idx]; - } - } else { - stableNodeList = nodeList; - } - - for (i = 0, ii = linkFns.length; i < ii;) { - node = stableNodeList[linkFns[i++]]; - nodeLinkFn = linkFns[i++]; - childLinkFn = linkFns[i++]; - - if (nodeLinkFn) { - if (nodeLinkFn.scope) { - childScope = scope.$new(); - compile.$$addScopeInfo(jqLite(node), childScope); - var destroyBindings = nodeLinkFn.$$destroyBindings; - if (destroyBindings) { - nodeLinkFn.$$destroyBindings = null; - childScope.$on('$destroyed', destroyBindings); - } - } else { - childScope = scope; - } - - if (nodeLinkFn.transcludeOnThisElement) { - childBoundTranscludeFn = createBoundTranscludeFn( - scope, nodeLinkFn.transclude, parentBoundTranscludeFn); - - } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) { - childBoundTranscludeFn = parentBoundTranscludeFn; - - } else if (!parentBoundTranscludeFn && transcludeFn) { - childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn); - - } else { - childBoundTranscludeFn = null; - } - - nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn, - nodeLinkFn); - - } else if (childLinkFn) { - childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn); - } - } - } - } - - function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) { - - var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) { - - if (!transcludedScope) { - transcludedScope = scope.$new(false, containingScope); - transcludedScope.$$transcluded = true; - } - - return transcludeFn(transcludedScope, cloneFn, { - parentBoundTranscludeFn: previousBoundTranscludeFn, - transcludeControllers: controllers, - futureParentElement: futureParentElement - }); - }; - - return boundTranscludeFn; - } - - /** - * Looks for directives on the given node and adds them to the directive collection which is - * sorted. - * - * @param node Node to search. - * @param directives An array to which the directives are added to. This array is sorted before - * the function returns. - * @param attrs The shared attrs object which is used to populate the normalized attributes. - * @param {number=} maxPriority Max directive priority. - */ - function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) { - var nodeType = node.nodeType, - attrsMap = attrs.$attr, - match, - className; - - switch (nodeType) { - case NODE_TYPE_ELEMENT: /* Element */ - // use the node name: - addDirective(directives, - directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective); - - // iterate over the attributes - for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes, - j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { - var attrStartName = false; - var attrEndName = false; - - attr = nAttrs[j]; - name = attr.name; - value = trim(attr.value); - - // support ngAttr attribute binding - ngAttrName = directiveNormalize(name); - if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) { - name = name.replace(PREFIX_REGEXP, '') - .substr(8).replace(/_(.)/g, function(match, letter) { - return letter.toUpperCase(); - }); - } - - var directiveNName = ngAttrName.replace(/(Start|End)$/, ''); - if (directiveIsMultiElement(directiveNName)) { - if (ngAttrName === directiveNName + 'Start') { - attrStartName = name; - attrEndName = name.substr(0, name.length - 5) + 'end'; - name = name.substr(0, name.length - 6); - } - } - - nName = directiveNormalize(name.toLowerCase()); - attrsMap[nName] = name; - if (isNgAttr || !attrs.hasOwnProperty(nName)) { - attrs[nName] = value; - if (getBooleanAttrName(node, nName)) { - attrs[nName] = true; // presence means true - } - } - addAttrInterpolateDirective(node, directives, value, nName, isNgAttr); - addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, - attrEndName); - } - - // use class as directive - className = node.className; - if (isObject(className)) { - // Maybe SVGAnimatedString - className = className.animVal; - } - if (isString(className) && className !== '') { - while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) { - nName = directiveNormalize(match[2]); - if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) { - attrs[nName] = trim(match[3]); - } - className = className.substr(match.index + match[0].length); - } - } - break; - case NODE_TYPE_TEXT: /* Text Node */ - if (msie === 11) { - // Workaround for #11781 - while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) { - node.nodeValue = node.nodeValue + node.nextSibling.nodeValue; - node.parentNode.removeChild(node.nextSibling); - } - } - addTextInterpolateDirective(directives, node.nodeValue); - break; - case NODE_TYPE_COMMENT: /* Comment */ - try { - match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); - if (match) { - nName = directiveNormalize(match[1]); - if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) { - attrs[nName] = trim(match[2]); - } - } - } catch (e) { - // turns out that under some circumstances IE9 throws errors when one attempts to read - // comment's node value. - // Just ignore it and continue. (Can't seem to reproduce in test case.) - } - break; - } - - directives.sort(byPriority); - return directives; - } - - /** - * Given a node with an directive-start it collects all of the siblings until it finds - * directive-end. - * @param node - * @param attrStart - * @param attrEnd - * @returns {*} - */ - function groupScan(node, attrStart, attrEnd) { - var nodes = []; - var depth = 0; - if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) { - do { - if (!node) { - throw $compileMinErr('uterdir', - "Unterminated attribute, found '{0}' but no matching '{1}' found.", - attrStart, attrEnd); - } - if (node.nodeType == NODE_TYPE_ELEMENT) { - if (node.hasAttribute(attrStart)) depth++; - if (node.hasAttribute(attrEnd)) depth--; - } - nodes.push(node); - node = node.nextSibling; - } while (depth > 0); - } else { - nodes.push(node); - } - - return jqLite(nodes); - } - - /** - * Wrapper for linking function which converts normal linking function into a grouped - * linking function. - * @param linkFn - * @param attrStart - * @param attrEnd - * @returns {Function} - */ - function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) { - return function(scope, element, attrs, controllers, transcludeFn) { - element = groupScan(element[0], attrStart, attrEnd); - return linkFn(scope, element, attrs, controllers, transcludeFn); - }; - } - - /** - * Once the directives have been collected, their compile functions are executed. This method - * is responsible for inlining directive templates as well as terminating the application - * of the directives if the terminal directive has been reached. - * - * @param {Array} directives Array of collected directives to execute their compile function. - * this needs to be pre-sorted by priority order. - * @param {Node} compileNode The raw DOM node to apply the compile functions to - * @param {Object} templateAttrs The shared attribute function - * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the - * scope argument is auto-generated to the new - * child of the transcluded parent scope. - * @param {JQLite} jqCollection If we are working on the root of the compile tree then this - * argument has the root jqLite array so that we can replace nodes - * on it. - * @param {Object=} originalReplaceDirective An optional directive that will be ignored when - * compiling the transclusion. - * @param {Array.} preLinkFns - * @param {Array.} postLinkFns - * @param {Object} previousCompileContext Context used for previous compilation of the current - * node - * @returns {Function} linkFn - */ - function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, - jqCollection, originalReplaceDirective, preLinkFns, postLinkFns, - previousCompileContext) { - previousCompileContext = previousCompileContext || {}; - - var terminalPriority = -Number.MAX_VALUE, - newScopeDirective = previousCompileContext.newScopeDirective, - controllerDirectives = previousCompileContext.controllerDirectives, - newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective, - templateDirective = previousCompileContext.templateDirective, - nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective, - hasTranscludeDirective = false, - hasTemplate = false, - hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective, - $compileNode = templateAttrs.$$element = jqLite(compileNode), - directive, - directiveName, - $template, - replaceDirective = originalReplaceDirective, - childTranscludeFn = transcludeFn, - linkFn, - directiveValue; - - // executes all directives on the current element - for (var i = 0, ii = directives.length; i < ii; i++) { - directive = directives[i]; - var attrStart = directive.$$start; - var attrEnd = directive.$$end; - - // collect multiblock sections - if (attrStart) { - $compileNode = groupScan(compileNode, attrStart, attrEnd); - } - $template = undefined; - - if (terminalPriority > directive.priority) { - break; // prevent further processing of directives - } - - if (directiveValue = directive.scope) { - - // skip the check for directives with async templates, we'll check the derived sync - // directive when the template arrives - if (!directive.templateUrl) { - if (isObject(directiveValue)) { - // This directive is trying to add an isolated scope. - // Check that there is no scope of any kind already - assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective, - directive, $compileNode); - newIsolateScopeDirective = directive; - } else { - // This directive is trying to add a child scope. - // Check that there is no isolated scope already - assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, - $compileNode); - } - } - - newScopeDirective = newScopeDirective || directive; - } - - directiveName = directive.name; - - if (!directive.templateUrl && directive.controller) { - directiveValue = directive.controller; - controllerDirectives = controllerDirectives || createMap(); - assertNoDuplicate("'" + directiveName + "' controller", - controllerDirectives[directiveName], directive, $compileNode); - controllerDirectives[directiveName] = directive; - } - - if (directiveValue = directive.transclude) { - hasTranscludeDirective = true; - - // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion. - // This option should only be used by directives that know how to safely handle element transclusion, - // where the transcluded nodes are added or replaced after linking. - if (!directive.$$tlb) { - assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode); - nonTlbTranscludeDirective = directive; - } - - if (directiveValue == 'element') { - hasElementTranscludeDirective = true; - terminalPriority = directive.priority; - $template = $compileNode; - $compileNode = templateAttrs.$$element = - jqLite(document.createComment(' ' + directiveName + ': ' + - templateAttrs[directiveName] + ' ')); - compileNode = $compileNode[0]; - replaceWith(jqCollection, sliceArgs($template), compileNode); - - childTranscludeFn = compile($template, transcludeFn, terminalPriority, - replaceDirective && replaceDirective.name, { - // Don't pass in: - // - controllerDirectives - otherwise we'll create duplicates controllers - // - newIsolateScopeDirective or templateDirective - combining templates with - // element transclusion doesn't make sense. - // - // We need only nonTlbTranscludeDirective so that we prevent putting transclusion - // on the same element more than once. - nonTlbTranscludeDirective: nonTlbTranscludeDirective - }); - } else { - $template = jqLite(jqLiteClone(compileNode)).contents(); - $compileNode.empty(); // clear contents - childTranscludeFn = compile($template, transcludeFn); - } - } - - if (directive.template) { - hasTemplate = true; - assertNoDuplicate('template', templateDirective, directive, $compileNode); - templateDirective = directive; - - directiveValue = (isFunction(directive.template)) - ? directive.template($compileNode, templateAttrs) - : directive.template; - - directiveValue = denormalizeTemplate(directiveValue); - - if (directive.replace) { - replaceDirective = directive; - if (jqLiteIsTextNode(directiveValue)) { - $template = []; - } else { - $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue))); - } - compileNode = $template[0]; - - if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) { - throw $compileMinErr('tplrt', - "Template for directive '{0}' must have exactly one root element. {1}", - directiveName, ''); - } - - replaceWith(jqCollection, $compileNode, compileNode); - - var newTemplateAttrs = {$attr: {}}; - - // combine directives from the original node and from the template: - // - take the array of directives for this element - // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed) - // - collect directives from the template and sort them by priority - // - combine directives as: processed + template + unprocessed - var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs); - var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1)); - - if (newIsolateScopeDirective) { - markDirectivesAsIsolate(templateDirectives); - } - directives = directives.concat(templateDirectives).concat(unprocessedDirectives); - mergeTemplateAttributes(templateAttrs, newTemplateAttrs); - - ii = directives.length; - } else { - $compileNode.html(directiveValue); - } - } - - if (directive.templateUrl) { - hasTemplate = true; - assertNoDuplicate('template', templateDirective, directive, $compileNode); - templateDirective = directive; - - if (directive.replace) { - replaceDirective = directive; - } - - nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode, - templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, { - controllerDirectives: controllerDirectives, - newScopeDirective: (newScopeDirective !== directive) && newScopeDirective, - newIsolateScopeDirective: newIsolateScopeDirective, - templateDirective: templateDirective, - nonTlbTranscludeDirective: nonTlbTranscludeDirective - }); - ii = directives.length; - } else if (directive.compile) { - try { - linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); - if (isFunction(linkFn)) { - addLinkFns(null, linkFn, attrStart, attrEnd); - } else if (linkFn) { - addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd); - } - } catch (e) { - $exceptionHandler(e, startingTag($compileNode)); - } - } - - if (directive.terminal) { - nodeLinkFn.terminal = true; - terminalPriority = Math.max(terminalPriority, directive.priority); - } - - } - - nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true; - nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective; - nodeLinkFn.templateOnThisElement = hasTemplate; - nodeLinkFn.transclude = childTranscludeFn; - - previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective; - - // might be normal or delayed nodeLinkFn depending on if templateUrl is present - return nodeLinkFn; - - //////////////////// - - function addLinkFns(pre, post, attrStart, attrEnd) { - if (pre) { - if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd); - pre.require = directive.require; - pre.directiveName = directiveName; - if (newIsolateScopeDirective === directive || directive.$$isolateScope) { - pre = cloneAndAnnotateFn(pre, {isolateScope: true}); - } - preLinkFns.push(pre); - } - if (post) { - if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd); - post.require = directive.require; - post.directiveName = directiveName; - if (newIsolateScopeDirective === directive || directive.$$isolateScope) { - post = cloneAndAnnotateFn(post, {isolateScope: true}); - } - postLinkFns.push(post); - } - } - - - function getControllers(directiveName, require, $element, elementControllers) { - var value; - - if (isString(require)) { - var match = require.match(REQUIRE_PREFIX_REGEXP); - var name = require.substring(match[0].length); - var inheritType = match[1] || match[3]; - var optional = match[2] === '?'; - - //If only parents then start at the parent element - if (inheritType === '^^') { - $element = $element.parent(); - //Otherwise attempt getting the controller from elementControllers in case - //the element is transcluded (and has no data) and to avoid .data if possible - } else { - value = elementControllers && elementControllers[name]; - value = value && value.instance; - } - - if (!value) { - var dataName = '$' + name + 'Controller'; - value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName); - } - - if (!value && !optional) { - throw $compileMinErr('ctreq', - "Controller '{0}', required by directive '{1}', can't be found!", - name, directiveName); - } - } else if (isArray(require)) { - value = []; - for (var i = 0, ii = require.length; i < ii; i++) { - value[i] = getControllers(directiveName, require[i], $element, elementControllers); - } - } - - return value || null; - } - - function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) { - var elementControllers = createMap(); - for (var controllerKey in controllerDirectives) { - var directive = controllerDirectives[controllerKey]; - var locals = { - $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope, - $element: $element, - $attrs: attrs, - $transclude: transcludeFn - }; - - var controller = directive.controller; - if (controller == '@') { - controller = attrs[directive.name]; - } - - var controllerInstance = $controller(controller, locals, true, directive.controllerAs); - - // For directives with element transclusion the element is a comment, - // but jQuery .data doesn't support attaching data to comment nodes as it's hard to - // clean up (http://bugs.jquery.com/ticket/8335). - // Instead, we save the controllers for the element in a local hash and attach to .data - // later, once we have the actual element. - elementControllers[directive.name] = controllerInstance; - if (!hasElementTranscludeDirective) { - $element.data('$' + directive.name + 'Controller', controllerInstance.instance); - } - } - return elementControllers; - } - - function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn, - thisLinkFn) { - var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element, - attrs; - - if (compileNode === linkNode) { - attrs = templateAttrs; - $element = templateAttrs.$$element; - } else { - $element = jqLite(linkNode); - attrs = new Attributes($element, templateAttrs); - } - - if (newIsolateScopeDirective) { - isolateScope = scope.$new(true); - } - - if (boundTranscludeFn) { - // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn` - // is later passed as `parentBoundTranscludeFn` to `publicLinkFn` - transcludeFn = controllersBoundTransclude; - transcludeFn.$$boundTransclude = boundTranscludeFn; - } - - if (controllerDirectives) { - elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope); - } - - if (newIsolateScopeDirective) { - // Initialize isolate scope bindings for new isolate scope directive. - compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective || - templateDirective === newIsolateScopeDirective.$$originalDirective))); - compile.$$addScopeClass($element, true); - isolateScope.$$isolateBindings = - newIsolateScopeDirective.$$isolateBindings; - initializeDirectiveBindings(scope, attrs, isolateScope, - isolateScope.$$isolateBindings, - newIsolateScopeDirective, isolateScope); - } - if (elementControllers) { - // Initialize bindToController bindings for new/isolate scopes - var scopeDirective = newIsolateScopeDirective || newScopeDirective; - var bindings; - var controllerForBindings; - if (scopeDirective && elementControllers[scopeDirective.name]) { - bindings = scopeDirective.$$bindings.bindToController; - controller = elementControllers[scopeDirective.name]; - - if (controller && controller.identifier && bindings) { - controllerForBindings = controller; - thisLinkFn.$$destroyBindings = - initializeDirectiveBindings(scope, attrs, controller.instance, - bindings, scopeDirective); - } - } - for (i in elementControllers) { - controller = elementControllers[i]; - var controllerResult = controller(); - - if (controllerResult !== controller.instance) { - // If the controller constructor has a return value, overwrite the instance - // from setupControllers and update the element data - controller.instance = controllerResult; - $element.data('$' + i + 'Controller', controllerResult); - if (controller === controllerForBindings) { - // Remove and re-install bindToController bindings - thisLinkFn.$$destroyBindings(); - thisLinkFn.$$destroyBindings = - initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective); - } - } - } - } - - // PRELINKING - for (i = 0, ii = preLinkFns.length; i < ii; i++) { - linkFn = preLinkFns[i]; - invokeLinkFn(linkFn, - linkFn.isolateScope ? isolateScope : scope, - $element, - attrs, - linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), - transcludeFn - ); - } - - // RECURSION - // We only pass the isolate scope, if the isolate directive has a template, - // otherwise the child elements do not belong to the isolate directive. - var scopeToChild = scope; - if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) { - scopeToChild = isolateScope; - } - childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn); - - // POSTLINKING - for (i = postLinkFns.length - 1; i >= 0; i--) { - linkFn = postLinkFns[i]; - invokeLinkFn(linkFn, - linkFn.isolateScope ? isolateScope : scope, - $element, - attrs, - linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), - transcludeFn - ); - } - - // This is the function that is injected as `$transclude`. - // Note: all arguments are optional! - function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) { - var transcludeControllers; - - // No scope passed in: - if (!isScope(scope)) { - futureParentElement = cloneAttachFn; - cloneAttachFn = scope; - scope = undefined; - } - - if (hasElementTranscludeDirective) { - transcludeControllers = elementControllers; - } - if (!futureParentElement) { - futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element; - } - return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild); - } - } - } - - function markDirectivesAsIsolate(directives) { - // mark all directives as needing isolate scope. - for (var j = 0, jj = directives.length; j < jj; j++) { - directives[j] = inherit(directives[j], {$$isolateScope: true}); - } - } - - /** - * looks up the directive and decorates it with exception handling and proper parameters. We - * call this the boundDirective. - * - * @param {string} name name of the directive to look up. - * @param {string} location The directive must be found in specific format. - * String containing any of theses characters: - * - * * `E`: element name - * * `A': attribute - * * `C`: class - * * `M`: comment - * @returns {boolean} true if directive was added. - */ - function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, - endAttrName) { - if (name === ignoreDirective) return null; - var match = null; - if (hasDirectives.hasOwnProperty(name)) { - for (var directive, directives = $injector.get(name + Suffix), - i = 0, ii = directives.length; i < ii; i++) { - try { - directive = directives[i]; - if ((maxPriority === undefined || maxPriority > directive.priority) && - directive.restrict.indexOf(location) != -1) { - if (startAttrName) { - directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); - } - tDirectives.push(directive); - match = directive; - } - } catch (e) { $exceptionHandler(e); } - } - } - return match; - } - - - /** - * looks up the directive and returns true if it is a multi-element directive, - * and therefore requires DOM nodes between -start and -end markers to be grouped - * together. - * - * @param {string} name name of the directive to look up. - * @returns true if directive was registered as multi-element. - */ - function directiveIsMultiElement(name) { - if (hasDirectives.hasOwnProperty(name)) { - for (var directive, directives = $injector.get(name + Suffix), - i = 0, ii = directives.length; i < ii; i++) { - directive = directives[i]; - if (directive.multiElement) { - return true; - } - } - } - return false; - } - - /** - * When the element is replaced with HTML template then the new attributes - * on the template need to be merged with the existing attributes in the DOM. - * The desired effect is to have both of the attributes present. - * - * @param {object} dst destination attributes (original DOM) - * @param {object} src source attributes (from the directive template) - */ - function mergeTemplateAttributes(dst, src) { - var srcAttr = src.$attr, - dstAttr = dst.$attr, - $element = dst.$$element; - - // reapply the old attributes to the new element - forEach(dst, function(value, key) { - if (key.charAt(0) != '$') { - if (src[key] && src[key] !== value) { - value += (key === 'style' ? ';' : ' ') + src[key]; - } - dst.$set(key, value, true, srcAttr[key]); - } - }); - - // copy the new attributes on the old attrs object - forEach(src, function(value, key) { - if (key == 'class') { - safeAddClass($element, value); - dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; - } else if (key == 'style') { - $element.attr('style', $element.attr('style') + ';' + value); - dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value; - // `dst` will never contain hasOwnProperty as DOM parser won't let it. - // You will get an "InvalidCharacterError: DOM Exception 5" error if you - // have an attribute like "has-own-property" or "data-has-own-property", etc. - } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { - dst[key] = value; - dstAttr[key] = srcAttr[key]; - } - }); - } - - - function compileTemplateUrl(directives, $compileNode, tAttrs, - $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) { - var linkQueue = [], - afterTemplateNodeLinkFn, - afterTemplateChildLinkFn, - beforeTemplateCompileNode = $compileNode[0], - origAsyncDirective = directives.shift(), - derivedSyncDirective = inherit(origAsyncDirective, { - templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective - }), - templateUrl = (isFunction(origAsyncDirective.templateUrl)) - ? origAsyncDirective.templateUrl($compileNode, tAttrs) - : origAsyncDirective.templateUrl, - templateNamespace = origAsyncDirective.templateNamespace; - - $compileNode.empty(); - - $templateRequest(templateUrl) - .then(function(content) { - var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn; - - content = denormalizeTemplate(content); - - if (origAsyncDirective.replace) { - if (jqLiteIsTextNode(content)) { - $template = []; - } else { - $template = removeComments(wrapTemplate(templateNamespace, trim(content))); - } - compileNode = $template[0]; - - if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) { - throw $compileMinErr('tplrt', - "Template for directive '{0}' must have exactly one root element. {1}", - origAsyncDirective.name, templateUrl); - } - - tempTemplateAttrs = {$attr: {}}; - replaceWith($rootElement, $compileNode, compileNode); - var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs); - - if (isObject(origAsyncDirective.scope)) { - markDirectivesAsIsolate(templateDirectives); - } - directives = templateDirectives.concat(directives); - mergeTemplateAttributes(tAttrs, tempTemplateAttrs); - } else { - compileNode = beforeTemplateCompileNode; - $compileNode.html(content); - } - - directives.unshift(derivedSyncDirective); - - afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, - childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns, - previousCompileContext); - forEach($rootElement, function(node, i) { - if (node == compileNode) { - $rootElement[i] = $compileNode[0]; - } - }); - afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); - - while (linkQueue.length) { - var scope = linkQueue.shift(), - beforeTemplateLinkNode = linkQueue.shift(), - linkRootElement = linkQueue.shift(), - boundTranscludeFn = linkQueue.shift(), - linkNode = $compileNode[0]; - - if (scope.$$destroyed) continue; - - if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { - var oldClasses = beforeTemplateLinkNode.className; - - if (!(previousCompileContext.hasElementTranscludeDirective && - origAsyncDirective.replace)) { - // it was cloned therefore we have to clone as well. - linkNode = jqLiteClone(compileNode); - } - replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); - - // Copy in CSS classes from original node - safeAddClass(jqLite(linkNode), oldClasses); - } - if (afterTemplateNodeLinkFn.transcludeOnThisElement) { - childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); - } else { - childBoundTranscludeFn = boundTranscludeFn; - } - afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, - childBoundTranscludeFn, afterTemplateNodeLinkFn); - } - linkQueue = null; - }); - - return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { - var childBoundTranscludeFn = boundTranscludeFn; - if (scope.$$destroyed) return; - if (linkQueue) { - linkQueue.push(scope, - node, - rootElement, - childBoundTranscludeFn); - } else { - if (afterTemplateNodeLinkFn.transcludeOnThisElement) { - childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); - } - afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn, - afterTemplateNodeLinkFn); - } - }; - } - - - /** - * Sorting function for bound directives. - */ - function byPriority(a, b) { - var diff = b.priority - a.priority; - if (diff !== 0) return diff; - if (a.name !== b.name) return (a.name < b.name) ? -1 : 1; - return a.index - b.index; - } - - function assertNoDuplicate(what, previousDirective, directive, element) { - - function wrapModuleNameIfDefined(moduleName) { - return moduleName ? - (' (module: ' + moduleName + ')') : - ''; - } - - if (previousDirective) { - throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}', - previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName), - directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element)); - } - } - - - function addTextInterpolateDirective(directives, text) { - var interpolateFn = $interpolate(text, true); - if (interpolateFn) { - directives.push({ - priority: 0, - compile: function textInterpolateCompileFn(templateNode) { - var templateNodeParent = templateNode.parent(), - hasCompileParent = !!templateNodeParent.length; - - // When transcluding a template that has bindings in the root - // we don't have a parent and thus need to add the class during linking fn. - if (hasCompileParent) compile.$$addBindingClass(templateNodeParent); - - return function textInterpolateLinkFn(scope, node) { - var parent = node.parent(); - if (!hasCompileParent) compile.$$addBindingClass(parent); - compile.$$addBindingInfo(parent, interpolateFn.expressions); - scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { - node[0].nodeValue = value; - }); - }; - } - }); - } - } - - - function wrapTemplate(type, template) { - type = lowercase(type || 'html'); - switch (type) { - case 'svg': - case 'math': - var wrapper = document.createElement('div'); - wrapper.innerHTML = '<' + type + '>' + template + ''; - return wrapper.childNodes[0].childNodes; - default: - return template; - } - } - - - function getTrustedContext(node, attrNormalizedName) { - if (attrNormalizedName == "srcdoc") { - return $sce.HTML; - } - var tag = nodeName_(node); - // maction[xlink:href] can source SVG. It's not limited to . - if (attrNormalizedName == "xlinkHref" || - (tag == "form" && attrNormalizedName == "action") || - (tag != "img" && (attrNormalizedName == "src" || - attrNormalizedName == "ngSrc"))) { - return $sce.RESOURCE_URL; - } - } - - - function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) { - var trustedContext = getTrustedContext(node, name); - allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing; - - var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing); - - // no interpolation found -> ignore - if (!interpolateFn) return; - - - if (name === "multiple" && nodeName_(node) === "select") { - throw $compileMinErr("selmulti", - "Binding to the 'multiple' attribute is not supported. Element: {0}", - startingTag(node)); - } - - directives.push({ - priority: 100, - compile: function() { - return { - pre: function attrInterpolatePreLinkFn(scope, element, attr) { - var $$observers = (attr.$$observers || (attr.$$observers = {})); - - if (EVENT_HANDLER_ATTR_REGEXP.test(name)) { - throw $compileMinErr('nodomevents', - "Interpolations for HTML DOM event attributes are disallowed. Please use the " + - "ng- versions (such as ng-click instead of onclick) instead."); - } - - // If the attribute has changed since last $interpolate()ed - var newValue = attr[name]; - if (newValue !== value) { - // we need to interpolate again since the attribute value has been updated - // (e.g. by another directive's compile function) - // ensure unset/empty values make interpolateFn falsy - interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing); - value = newValue; - } - - // if attribute was updated so that there is no interpolation going on we don't want to - // register any observers - if (!interpolateFn) return; - - // initialize attr object so that it's ready in case we need the value for isolate - // scope initialization, otherwise the value would not be available from isolate - // directive's linking fn during linking phase - attr[name] = interpolateFn(scope); - - ($$observers[name] || ($$observers[name] = [])).$$inter = true; - (attr.$$observers && attr.$$observers[name].$$scope || scope). - $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) { - //special case for class attribute addition + removal - //so that class changes can tap into the animation - //hooks provided by the $animate service. Be sure to - //skip animations when the first digest occurs (when - //both the new and the old values are the same) since - //the CSS classes are the non-interpolated values - if (name === 'class' && newValue != oldValue) { - attr.$updateClass(newValue, oldValue); - } else { - attr.$set(name, newValue); - } - }); - } - }; - } - }); - } - - - /** - * This is a special jqLite.replaceWith, which can replace items which - * have no parents, provided that the containing jqLite collection is provided. - * - * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes - * in the root of the tree. - * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep - * the shell, but replace its DOM node reference. - * @param {Node} newNode The new DOM node. - */ - function replaceWith($rootElement, elementsToRemove, newNode) { - var firstElementToRemove = elementsToRemove[0], - removeCount = elementsToRemove.length, - parent = firstElementToRemove.parentNode, - i, ii; - - if ($rootElement) { - for (i = 0, ii = $rootElement.length; i < ii; i++) { - if ($rootElement[i] == firstElementToRemove) { - $rootElement[i++] = newNode; - for (var j = i, j2 = j + removeCount - 1, - jj = $rootElement.length; - j < jj; j++, j2++) { - if (j2 < jj) { - $rootElement[j] = $rootElement[j2]; - } else { - delete $rootElement[j]; - } - } - $rootElement.length -= removeCount - 1; - - // If the replaced element is also the jQuery .context then replace it - // .context is a deprecated jQuery api, so we should set it only when jQuery set it - // http://api.jquery.com/context/ - if ($rootElement.context === firstElementToRemove) { - $rootElement.context = newNode; - } - break; - } - } - } - - if (parent) { - parent.replaceChild(newNode, firstElementToRemove); - } - - // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it? - var fragment = document.createDocumentFragment(); - fragment.appendChild(firstElementToRemove); - - if (jqLite.hasData(firstElementToRemove)) { - // Copy over user data (that includes Angular's $scope etc.). Don't copy private - // data here because there's no public interface in jQuery to do that and copying over - // event listeners (which is the main use of private data) wouldn't work anyway. - jqLite(newNode).data(jqLite(firstElementToRemove).data()); - - // Remove data of the replaced element. We cannot just call .remove() - // on the element it since that would deallocate scope that is needed - // for the new node. Instead, remove the data "manually". - if (!jQuery) { - delete jqLite.cache[firstElementToRemove[jqLite.expando]]; - } else { - // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after - // the replaced element. The cleanData version monkey-patched by Angular would cause - // the scope to be trashed and we do need the very same scope to work with the new - // element. However, we cannot just cache the non-patched version and use it here as - // that would break if another library patches the method after Angular does (one - // example is jQuery UI). Instead, set a flag indicating scope destroying should be - // skipped this one time. - skipDestroyOnNextJQueryCleanData = true; - jQuery.cleanData([firstElementToRemove]); - } - } - - for (var k = 1, kk = elementsToRemove.length; k < kk; k++) { - var element = elementsToRemove[k]; - jqLite(element).remove(); // must do this way to clean up expando - fragment.appendChild(element); - delete elementsToRemove[k]; - } - - elementsToRemove[0] = newNode; - elementsToRemove.length = 1; - } - - - function cloneAndAnnotateFn(fn, annotation) { - return extend(function() { return fn.apply(null, arguments); }, fn, annotation); - } - - - function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) { - try { - linkFn(scope, $element, attrs, controllers, transcludeFn); - } catch (e) { - $exceptionHandler(e, startingTag($element)); - } - } - - - // Set up $watches for isolate scope and controller bindings. This process - // only occurs for isolate scopes and new scopes with controllerAs. - function initializeDirectiveBindings(scope, attrs, destination, bindings, - directive, newScope) { - var onNewScopeDestroyed; - forEach(bindings, function(definition, scopeName) { - var attrName = definition.attrName, - optional = definition.optional, - mode = definition.mode, // @, =, or & - lastValue, - parentGet, parentSet, compare; - - switch (mode) { - - case '@': - if (!optional && !hasOwnProperty.call(attrs, attrName)) { - destination[scopeName] = attrs[attrName] = void 0; - } - attrs.$observe(attrName, function(value) { - if (isString(value)) { - destination[scopeName] = value; - } - }); - attrs.$$observers[attrName].$$scope = scope; - if (isString(attrs[attrName])) { - // If the attribute has been provided then we trigger an interpolation to ensure - // the value is there for use in the link fn - destination[scopeName] = $interpolate(attrs[attrName])(scope); - } - break; - - case '=': - if (!hasOwnProperty.call(attrs, attrName)) { - if (optional) break; - attrs[attrName] = void 0; - } - if (optional && !attrs[attrName]) break; - - parentGet = $parse(attrs[attrName]); - if (parentGet.literal) { - compare = equals; - } else { - compare = function(a, b) { return a === b || (a !== a && b !== b); }; - } - parentSet = parentGet.assign || function() { - // reset the change, or we will throw this exception on every $digest - lastValue = destination[scopeName] = parentGet(scope); - throw $compileMinErr('nonassign', - "Expression '{0}' used with directive '{1}' is non-assignable!", - attrs[attrName], directive.name); - }; - lastValue = destination[scopeName] = parentGet(scope); - var parentValueWatch = function parentValueWatch(parentValue) { - if (!compare(parentValue, destination[scopeName])) { - // we are out of sync and need to copy - if (!compare(parentValue, lastValue)) { - // parent changed and it has precedence - destination[scopeName] = parentValue; - } else { - // if the parent can be assigned then do so - parentSet(scope, parentValue = destination[scopeName]); - } - } - return lastValue = parentValue; - }; - parentValueWatch.$stateful = true; - var unwatch; - if (definition.collection) { - unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch); - } else { - unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal); - } - onNewScopeDestroyed = (onNewScopeDestroyed || []); - onNewScopeDestroyed.push(unwatch); - break; - - case '&': - // Don't assign Object.prototype method to scope - parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop; - - // Don't assign noop to destination if expression is not valid - if (parentGet === noop && optional) break; - - destination[scopeName] = function(locals) { - return parentGet(scope, locals); - }; - break; - } - }); - var destroyBindings = onNewScopeDestroyed ? function destroyBindings() { - for (var i = 0, ii = onNewScopeDestroyed.length; i < ii; ++i) { - onNewScopeDestroyed[i](); - } - } : noop; - if (newScope && destroyBindings !== noop) { - newScope.$on('$destroy', destroyBindings); - return noop; - } - return destroyBindings; - } - }]; -} - -var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i; -/** - * Converts all accepted directives format into proper directive name. - * @param name Name to normalize - */ -function directiveNormalize(name) { - return camelCase(name.replace(PREFIX_REGEXP, '')); -} - -/** - * @ngdoc type - * @name $compile.directive.Attributes - * - * @description - * A shared object between directive compile / linking functions which contains normalized DOM - * element attributes. The values reflect current binding state `{{ }}`. The normalization is - * needed since all of these are treated as equivalent in Angular: - * - * ``` - * - * ``` - */ - -/** - * @ngdoc property - * @name $compile.directive.Attributes#$attr - * - * @description - * A map of DOM element attribute names to the normalized name. This is - * needed to do reverse lookup from normalized name back to actual name. - */ - - -/** - * @ngdoc method - * @name $compile.directive.Attributes#$set - * @kind function - * - * @description - * Set DOM element attribute value. - * - * - * @param {string} name Normalized element attribute name of the property to modify. The name is - * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr} - * property to the original name. - * @param {string} value Value to set the attribute to. The value can be an interpolated string. - */ - - - -/** - * Closure compiler type information - */ - -function nodesetLinkingFn( - /* angular.Scope */ scope, - /* NodeList */ nodeList, - /* Element */ rootElement, - /* function(Function) */ boundTranscludeFn -) {} - -function directiveLinkingFn( - /* nodesetLinkingFn */ nodesetLinkingFn, - /* angular.Scope */ scope, - /* Node */ node, - /* Element */ rootElement, - /* function(Function) */ boundTranscludeFn -) {} - -function tokenDifference(str1, str2) { - var values = '', - tokens1 = str1.split(/\s+/), - tokens2 = str2.split(/\s+/); - - outer: - for (var i = 0; i < tokens1.length; i++) { - var token = tokens1[i]; - for (var j = 0; j < tokens2.length; j++) { - if (token == tokens2[j]) continue outer; - } - values += (values.length > 0 ? ' ' : '') + token; - } - return values; -} - -function removeComments(jqNodes) { - jqNodes = jqLite(jqNodes); - var i = jqNodes.length; - - if (i <= 1) { - return jqNodes; - } - - while (i--) { - var node = jqNodes[i]; - if (node.nodeType === NODE_TYPE_COMMENT) { - splice.call(jqNodes, i, 1); - } - } - return jqNodes; -} - -var $controllerMinErr = minErr('$controller'); - - -var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/; -function identifierForController(controller, ident) { - if (ident && isString(ident)) return ident; - if (isString(controller)) { - var match = CNTRL_REG.exec(controller); - if (match) return match[3]; - } -} - - -/** - * @ngdoc provider - * @name $controllerProvider - * @description - * The {@link ng.$controller $controller service} is used by Angular to create new - * controllers. - * - * This provider allows controller registration via the - * {@link ng.$controllerProvider#register register} method. - */ -function $ControllerProvider() { - var controllers = {}, - globals = false; - - /** - * @ngdoc method - * @name $controllerProvider#register - * @param {string|Object} name Controller name, or an object map of controllers where the keys are - * the names and the values are the constructors. - * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI - * annotations in the array notation). - */ - this.register = function(name, constructor) { - assertNotHasOwnProperty(name, 'controller'); - if (isObject(name)) { - extend(controllers, name); - } else { - controllers[name] = constructor; - } - }; - - /** - * @ngdoc method - * @name $controllerProvider#allowGlobals - * @description If called, allows `$controller` to find controller constructors on `window` - */ - this.allowGlobals = function() { - globals = true; - }; - - - this.$get = ['$injector', '$window', function($injector, $window) { - - /** - * @ngdoc service - * @name $controller - * @requires $injector - * - * @param {Function|string} constructor If called with a function then it's considered to be the - * controller constructor function. Otherwise it's considered to be a string which is used - * to retrieve the controller constructor using the following steps: - * - * * check if a controller with given name is registered via `$controllerProvider` - * * check if evaluating the string on the current scope returns a constructor - * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global - * `window` object (not recommended) - * - * The string can use the `controller as property` syntax, where the controller instance is published - * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this - * to work correctly. - * - * @param {Object} locals Injection locals for Controller. - * @return {Object} Instance of given controller. - * - * @description - * `$controller` service is responsible for instantiating controllers. - * - * It's just a simple call to {@link auto.$injector $injector}, but extracted into - * a service, so that one can override this service with [BC version](https://gist.github.com/1649788). - */ - return function(expression, locals, later, ident) { - // PRIVATE API: - // param `later` --- indicates that the controller's constructor is invoked at a later time. - // If true, $controller will allocate the object with the correct - // prototype chain, but will not invoke the controller until a returned - // callback is invoked. - // param `ident` --- An optional label which overrides the label parsed from the controller - // expression, if any. - var instance, match, constructor, identifier; - later = later === true; - if (ident && isString(ident)) { - identifier = ident; - } - - if (isString(expression)) { - match = expression.match(CNTRL_REG); - if (!match) { - throw $controllerMinErr('ctrlfmt', - "Badly formed controller string '{0}'. " + - "Must match `__name__ as __id__` or `__name__`.", expression); - } - constructor = match[1], - identifier = identifier || match[3]; - expression = controllers.hasOwnProperty(constructor) - ? controllers[constructor] - : getter(locals.$scope, constructor, true) || - (globals ? getter($window, constructor, true) : undefined); - - assertArgFn(expression, constructor, true); - } - - if (later) { - // Instantiate controller later: - // This machinery is used to create an instance of the object before calling the - // controller's constructor itself. - // - // This allows properties to be added to the controller before the constructor is - // invoked. Primarily, this is used for isolate scope bindings in $compile. - // - // This feature is not intended for use by applications, and is thus not documented - // publicly. - // Object creation: http://jsperf.com/create-constructor/2 - var controllerPrototype = (isArray(expression) ? - expression[expression.length - 1] : expression).prototype; - instance = Object.create(controllerPrototype || null); - - if (identifier) { - addIdentifier(locals, identifier, instance, constructor || expression.name); - } - - var instantiate; - return instantiate = extend(function() { - var result = $injector.invoke(expression, instance, locals, constructor); - if (result !== instance && (isObject(result) || isFunction(result))) { - instance = result; - if (identifier) { - // If result changed, re-assign controllerAs value to scope. - addIdentifier(locals, identifier, instance, constructor || expression.name); - } - } - return instance; - }, { - instance: instance, - identifier: identifier - }); - } - - instance = $injector.instantiate(expression, locals, constructor); - - if (identifier) { - addIdentifier(locals, identifier, instance, constructor || expression.name); - } - - return instance; - }; - - function addIdentifier(locals, identifier, instance, name) { - if (!(locals && isObject(locals.$scope))) { - throw minErr('$controller')('noscp', - "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.", - name, identifier); - } - - locals.$scope[identifier] = instance; - } - }]; -} - -/** - * @ngdoc service - * @name $document - * @requires $window - * - * @description - * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object. - * - * @example - - -
-

$document title:

-

window.document title:

-
-
- - angular.module('documentExample', []) - .controller('ExampleController', ['$scope', '$document', function($scope, $document) { - $scope.title = $document[0].title; - $scope.windowTitle = angular.element(window.document)[0].title; - }]); - -
- */ -function $DocumentProvider() { - this.$get = ['$window', function(window) { - return jqLite(window.document); - }]; -} - -/** - * @ngdoc service - * @name $exceptionHandler - * @requires ng.$log - * - * @description - * Any uncaught exception in angular expressions is delegated to this service. - * The default implementation simply delegates to `$log.error` which logs it into - * the browser console. - * - * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by - * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. - * - * ## Example: - * - * ```js - * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() { - * return function(exception, cause) { - * exception.message += ' (caused by "' + cause + '")'; - * throw exception; - * }; - * }); - * ``` - * - * This example will override the normal action of `$exceptionHandler`, to make angular - * exceptions fail hard when they happen, instead of just logging to the console. - * - *
- * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind` - * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler} - * (unless executed during a digest). - * - * If you wish, you can manually delegate exceptions, e.g. - * `try { ... } catch(e) { $exceptionHandler(e); }` - * - * @param {Error} exception Exception associated with the error. - * @param {string=} cause optional information about the context in which - * the error was thrown. - * - */ -function $ExceptionHandlerProvider() { - this.$get = ['$log', function($log) { - return function(exception, cause) { - $log.error.apply($log, arguments); - }; - }]; -} - -var $$ForceReflowProvider = function() { - this.$get = ['$document', function($document) { - return function(domNode) { - //the line below will force the browser to perform a repaint so - //that all the animated elements within the animation frame will - //be properly updated and drawn on screen. This is required to - //ensure that the preparation animation is properly flushed so that - //the active state picks up from there. DO NOT REMOVE THIS LINE. - //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH - //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND - //WILL TAKE YEARS AWAY FROM YOUR LIFE. - if (domNode) { - if (!domNode.nodeType && domNode instanceof jqLite) { - domNode = domNode[0]; - } - } else { - domNode = $document[0].body; - } - return domNode.offsetWidth + 1; - }; - }]; -}; - -var APPLICATION_JSON = 'application/json'; -var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'}; -var JSON_START = /^\[|^\{(?!\{)/; -var JSON_ENDS = { - '[': /]$/, - '{': /}$/ -}; -var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/; -var $httpMinErr = minErr('$http'); -var $httpMinErrLegacyFn = function(method) { - return function() { - throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method); - }; -}; - -function serializeValue(v) { - if (isObject(v)) { - return isDate(v) ? v.toISOString() : toJson(v); - } - return v; -} - - -function $HttpParamSerializerProvider() { - /** - * @ngdoc service - * @name $httpParamSerializer - * @description - * - * Default {@link $http `$http`} params serializer that converts objects to strings - * according to the following rules: - * - * * `{'foo': 'bar'}` results in `foo=bar` - * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object) - * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element) - * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object) - * - * Note that serializer will sort the request parameters alphabetically. - * */ - - this.$get = function() { - return function ngParamSerializer(params) { - if (!params) return ''; - var parts = []; - forEachSorted(params, function(value, key) { - if (value === null || isUndefined(value)) return; - if (isArray(value)) { - forEach(value, function(v, k) { - parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v))); - }); - } else { - parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value))); - } - }); - - return parts.join('&'); - }; - }; -} - -function $HttpParamSerializerJQLikeProvider() { - /** - * @ngdoc service - * @name $httpParamSerializerJQLike - * @description - * - * Alternative {@link $http `$http`} params serializer that follows - * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic. - * The serializer will also sort the params alphabetically. - * - * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property: - * - * ```js - * $http({ - * url: myUrl, - * method: 'GET', - * params: myParams, - * paramSerializer: '$httpParamSerializerJQLike' - * }); - * ``` - * - * It is also possible to set it as the default `paramSerializer` in the - * {@link $httpProvider#defaults `$httpProvider`}. - * - * Additionally, you can inject the serializer and use it explicitly, for example to serialize - * form data for submission: - * - * ```js - * .controller(function($http, $httpParamSerializerJQLike) { - * //... - * - * $http({ - * url: myUrl, - * method: 'POST', - * data: $httpParamSerializerJQLike(myData), - * headers: { - * 'Content-Type': 'application/x-www-form-urlencoded' - * } - * }); - * - * }); - * ``` - * - * */ - this.$get = function() { - return function jQueryLikeParamSerializer(params) { - if (!params) return ''; - var parts = []; - serialize(params, '', true); - return parts.join('&'); - - function serialize(toSerialize, prefix, topLevel) { - if (toSerialize === null || isUndefined(toSerialize)) return; - if (isArray(toSerialize)) { - forEach(toSerialize, function(value, index) { - serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']'); - }); - } else if (isObject(toSerialize) && !isDate(toSerialize)) { - forEachSorted(toSerialize, function(value, key) { - serialize(value, prefix + - (topLevel ? '' : '[') + - key + - (topLevel ? '' : ']')); - }); - } else { - parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize))); - } - } - }; - }; -} - -function defaultHttpResponseTransform(data, headers) { - if (isString(data)) { - // Strip json vulnerability protection prefix and trim whitespace - var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim(); - - if (tempData) { - var contentType = headers('Content-Type'); - if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) { - data = fromJson(tempData); - } - } - } - - return data; -} - -function isJsonLike(str) { - var jsonStart = str.match(JSON_START); - return jsonStart && JSON_ENDS[jsonStart[0]].test(str); -} - -/** - * Parse headers into key value object - * - * @param {string} headers Raw headers as a string - * @returns {Object} Parsed headers as key value object - */ -function parseHeaders(headers) { - var parsed = createMap(), i; - - function fillInParsed(key, val) { - if (key) { - parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; - } - } - - if (isString(headers)) { - forEach(headers.split('\n'), function(line) { - i = line.indexOf(':'); - fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1))); - }); - } else if (isObject(headers)) { - forEach(headers, function(headerVal, headerKey) { - fillInParsed(lowercase(headerKey), trim(headerVal)); - }); - } - - return parsed; -} - - -/** - * Returns a function that provides access to parsed headers. - * - * Headers are lazy parsed when first requested. - * @see parseHeaders - * - * @param {(string|Object)} headers Headers to provide access to. - * @returns {function(string=)} Returns a getter function which if called with: - * - * - if called with single an argument returns a single header value or null - * - if called with no arguments returns an object containing all headers. - */ -function headersGetter(headers) { - var headersObj; - - return function(name) { - if (!headersObj) headersObj = parseHeaders(headers); - - if (name) { - var value = headersObj[lowercase(name)]; - if (value === void 0) { - value = null; - } - return value; - } - - return headersObj; - }; -} - - -/** - * Chain all given functions - * - * This function is used for both request and response transforming - * - * @param {*} data Data to transform. - * @param {function(string=)} headers HTTP headers getter fn. - * @param {number} status HTTP status code of the response. - * @param {(Function|Array.)} fns Function or an array of functions. - * @returns {*} Transformed data. - */ -function transformData(data, headers, status, fns) { - if (isFunction(fns)) { - return fns(data, headers, status); - } - - forEach(fns, function(fn) { - data = fn(data, headers, status); - }); - - return data; -} - - -function isSuccess(status) { - return 200 <= status && status < 300; -} - - -/** - * @ngdoc provider - * @name $httpProvider - * @description - * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service. - * */ -function $HttpProvider() { - /** - * @ngdoc property - * @name $httpProvider#defaults - * @description - * - * Object containing default values for all {@link ng.$http $http} requests. - * - * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`} - * that will provide the cache for all requests who set their `cache` property to `true`. - * If you set the `defaults.cache = false` then only requests that specify their own custom - * cache object will be cached. See {@link $http#caching $http Caching} for more information. - * - * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token. - * Defaults value is `'XSRF-TOKEN'`. - * - * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the - * XSRF token. Defaults value is `'X-XSRF-TOKEN'`. - * - * - **`defaults.headers`** - {Object} - Default headers for all $http requests. - * Refer to {@link ng.$http#setting-http-headers $http} for documentation on - * setting default headers. - * - **`defaults.headers.common`** - * - **`defaults.headers.post`** - * - **`defaults.headers.put`** - * - **`defaults.headers.patch`** - * - * - * - **`defaults.paramSerializer`** - `{string|function(Object):string}` - A function - * used to the prepare string representation of request parameters (specified as an object). - * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}. - * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}. - * - **/ - var defaults = this.defaults = { - // transform incoming response data - transformResponse: [defaultHttpResponseTransform], - - // transform outgoing request data - transformRequest: [function(d) { - return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d; - }], - - // default headers - headers: { - common: { - 'Accept': 'application/json, text/plain, */*' - }, - post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), - put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), - patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON) - }, - - xsrfCookieName: 'XSRF-TOKEN', - xsrfHeaderName: 'X-XSRF-TOKEN', - - paramSerializer: '$httpParamSerializer' - }; - - var useApplyAsync = false; - /** - * @ngdoc method - * @name $httpProvider#useApplyAsync - * @description - * - * Configure $http service to combine processing of multiple http responses received at around - * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in - * significant performance improvement for bigger applications that make many HTTP requests - * concurrently (common during application bootstrap). - * - * Defaults to false. If no value is specified, returns the current configured value. - * - * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred - * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window - * to load and share the same digest cycle. - * - * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining. - * otherwise, returns the current configured value. - **/ - this.useApplyAsync = function(value) { - if (isDefined(value)) { - useApplyAsync = !!value; - return this; - } - return useApplyAsync; - }; - - var useLegacyPromise = true; - /** - * @ngdoc method - * @name $httpProvider#useLegacyPromiseExtensions - * @description - * - * Configure `$http` service to return promises without the shorthand methods `success` and `error`. - * This should be used to make sure that applications work without these methods. - * - * Defaults to false. If no value is specified, returns the current configured value. - * - * @param {boolean=} value If true, `$http` will return a normal promise without the `success` and `error` methods. - * - * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining. - * otherwise, returns the current configured value. - **/ - this.useLegacyPromiseExtensions = function(value) { - if (isDefined(value)) { - useLegacyPromise = !!value; - return this; - } - return useLegacyPromise; - }; - - /** - * @ngdoc property - * @name $httpProvider#interceptors - * @description - * - * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http} - * pre-processing of request or postprocessing of responses. - * - * These service factories are ordered by request, i.e. they are applied in the same order as the - * array, on request, but reverse order, on response. - * - * {@link ng.$http#interceptors Interceptors detailed info} - **/ - var interceptorFactories = this.interceptors = []; - - this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector', - function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) { - - var defaultCache = $cacheFactory('$http'); - - /** - * Make sure that default param serializer is exposed as a function - */ - defaults.paramSerializer = isString(defaults.paramSerializer) ? - $injector.get(defaults.paramSerializer) : defaults.paramSerializer; - - /** - * Interceptors stored in reverse order. Inner interceptors before outer interceptors. - * The reversal is needed so that we can build up the interception chain around the - * server request. - */ - var reversedInterceptors = []; - - forEach(interceptorFactories, function(interceptorFactory) { - reversedInterceptors.unshift(isString(interceptorFactory) - ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)); - }); - - /** - * @ngdoc service - * @kind function - * @name $http - * @requires ng.$httpBackend - * @requires $cacheFactory - * @requires $rootScope - * @requires $q - * @requires $injector - * - * @description - * The `$http` service is a core Angular service that facilitates communication with the remote - * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest) - * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP). - * - * For unit testing applications that use `$http` service, see - * {@link ngMock.$httpBackend $httpBackend mock}. - * - * For a higher level of abstraction, please check out the {@link ngResource.$resource - * $resource} service. - * - * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by - * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage - * it is important to familiarize yourself with these APIs and the guarantees they provide. - * - * - * ## General usage - * The `$http` service is a function which takes a single argument — a configuration object — - * that is used to generate an HTTP request and returns a {@link ng.$q promise}. - * - * ```js - * // Simple GET request example : - * $http.get('/someUrl'). - * then(function(response) { - * // this callback will be called asynchronously - * // when the response is available - * }, function(response) { - * // called asynchronously if an error occurs - * // or server returns response with an error status. - * }); - * ``` - * - * ```js - * // Simple POST request example (passing data) : - * $http.post('/someUrl', {msg:'hello word!'}). - * then(function(response) { - * // this callback will be called asynchronously - * // when the response is available - * }, function(response) { - * // called asynchronously if an error occurs - * // or server returns response with an error status. - * }); - * ``` - * - * The response object has these properties: - * - * - **data** – `{string|Object}` – The response body transformed with the transform - * functions. - * - **status** – `{number}` – HTTP status code of the response. - * - **headers** – `{function([headerName])}` – Header getter function. - * - **config** – `{Object}` – The configuration object that was used to generate the request. - * - **statusText** – `{string}` – HTTP status text of the response. - * - * A response status code between 200 and 299 is considered a success status and - * will result in the success callback being called. Note that if the response is a redirect, - * XMLHttpRequest will transparently follow it, meaning that the error callback will not be - * called for such responses. - * - * ## Writing Unit Tests that use $http - * When unit testing (using {@link ngMock ngMock}), it is necessary to call - * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending - * request using trained responses. - * - * ``` - * $httpBackend.expectGET(...); - * $http.get(...); - * $httpBackend.flush(); - * ``` - * - * ## Shortcut methods - * - * Shortcut methods are also available. All shortcut methods require passing in the URL, and - * request data must be passed in for POST/PUT requests. - * - * ```js - * $http.get('/someUrl').then(successCallback); - * $http.post('/someUrl', data).then(successCallback); - * ``` - * - * Complete list of shortcut methods: - * - * - {@link ng.$http#get $http.get} - * - {@link ng.$http#head $http.head} - * - {@link ng.$http#post $http.post} - * - {@link ng.$http#put $http.put} - * - {@link ng.$http#delete $http.delete} - * - {@link ng.$http#jsonp $http.jsonp} - * - {@link ng.$http#patch $http.patch} - * - * - * ## Deprecation Notice - *
- * The `$http` legacy promise methods `success` and `error` have been deprecated. - * Use the standard `then` method instead. - * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to - * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error. - *
- * - * ## Setting HTTP Headers - * - * The $http service will automatically add certain HTTP headers to all requests. These defaults - * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration - * object, which currently contains this default configuration: - * - * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): - * - `Accept: application/json, text/plain, * / *` - * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) - * - `Content-Type: application/json` - * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) - * - `Content-Type: application/json` - * - * To add or overwrite these defaults, simply add or remove a property from these configuration - * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object - * with the lowercased HTTP method name as the key, e.g. - * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`. - * - * The defaults can also be set at runtime via the `$http.defaults` object in the same - * fashion. For example: - * - * ``` - * module.run(function($http) { - * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w' - * }); - * ``` - * - * In addition, you can supply a `headers` property in the config object passed when - * calling `$http(config)`, which overrides the defaults without changing them globally. - * - * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis, - * Use the `headers` property, setting the desired header to `undefined`. For example: - * - * ```js - * var req = { - * method: 'POST', - * url: 'http://example.com', - * headers: { - * 'Content-Type': undefined - * }, - * data: { test: 'test' } - * } - * - * $http(req).then(function(){...}, function(){...}); - * ``` - * - * ## Transforming Requests and Responses - * - * Both requests and responses can be transformed using transformation functions: `transformRequest` - * and `transformResponse`. These properties can be a single function that returns - * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions, - * which allows you to `push` or `unshift` a new transformation function into the transformation chain. - * - * ### Default Transformations - * - * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and - * `defaults.transformResponse` properties. If a request does not provide its own transformations - * then these will be applied. - * - * You can augment or replace the default transformations by modifying these properties by adding to or - * replacing the array. - * - * Angular provides the following default transformations: - * - * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`): - * - * - If the `data` property of the request configuration object contains an object, serialize it - * into JSON format. - * - * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`): - * - * - If XSRF prefix is detected, strip it (see Security Considerations section below). - * - If JSON response is detected, deserialize it using a JSON parser. - * - * - * ### Overriding the Default Transformations Per Request - * - * If you wish override the request/response transformations only for a single request then provide - * `transformRequest` and/or `transformResponse` properties on the configuration object passed - * into `$http`. - * - * Note that if you provide these properties on the config object the default transformations will be - * overwritten. If you wish to augment the default transformations then you must include them in your - * local transformation array. - * - * The following code demonstrates adding a new response transformation to be run after the default response - * transformations have been run. - * - * ```js - * function appendTransform(defaults, transform) { - * - * // We can't guarantee that the default transformation is an array - * defaults = angular.isArray(defaults) ? defaults : [defaults]; - * - * // Append the new transformation to the defaults - * return defaults.concat(transform); - * } - * - * $http({ - * url: '...', - * method: 'GET', - * transformResponse: appendTransform($http.defaults.transformResponse, function(value) { - * return doTransform(value); - * }) - * }); - * ``` - * - * - * ## Caching - * - * To enable caching, set the request configuration `cache` property to `true` (to use default - * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}). - * When the cache is enabled, `$http` stores the response from the server in the specified - * cache. The next time the same request is made, the response is served from the cache without - * sending a request to the server. - * - * Note that even if the response is served from cache, delivery of the data is asynchronous in - * the same way that real requests are. - * - * If there are multiple GET requests for the same URL that should be cached using the same - * cache, but the cache is not populated yet, only one request to the server will be made and - * the remaining requests will be fulfilled using the response from the first request. - * - * You can change the default cache to a new object (built with - * {@link ng.$cacheFactory `$cacheFactory`}) by updating the - * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set - * their `cache` property to `true` will now use this cache object. - * - * If you set the default cache to `false` then only requests that specify their own custom - * cache object will be cached. - * - * ## Interceptors - * - * Before you start creating interceptors, be sure to understand the - * {@link ng.$q $q and deferred/promise APIs}. - * - * For purposes of global error handling, authentication, or any kind of synchronous or - * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be - * able to intercept requests before they are handed to the server and - * responses before they are handed over to the application code that - * initiated these requests. The interceptors leverage the {@link ng.$q - * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing. - * - * The interceptors are service factories that are registered with the `$httpProvider` by - * adding them to the `$httpProvider.interceptors` array. The factory is called and - * injected with dependencies (if specified) and returns the interceptor. - * - * There are two kinds of interceptors (and two kinds of rejection interceptors): - * - * * `request`: interceptors get called with a http `config` object. The function is free to - * modify the `config` object or create a new one. The function needs to return the `config` - * object directly, or a promise containing the `config` or a new `config` object. - * * `requestError`: interceptor gets called when a previous interceptor threw an error or - * resolved with a rejection. - * * `response`: interceptors get called with http `response` object. The function is free to - * modify the `response` object or create a new one. The function needs to return the `response` - * object directly, or as a promise containing the `response` or a new `response` object. - * * `responseError`: interceptor gets called when a previous interceptor threw an error or - * resolved with a rejection. - * - * - * ```js - * // register the interceptor as a service - * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { - * return { - * // optional method - * 'request': function(config) { - * // do something on success - * return config; - * }, - * - * // optional method - * 'requestError': function(rejection) { - * // do something on error - * if (canRecover(rejection)) { - * return responseOrNewPromise - * } - * return $q.reject(rejection); - * }, - * - * - * - * // optional method - * 'response': function(response) { - * // do something on success - * return response; - * }, - * - * // optional method - * 'responseError': function(rejection) { - * // do something on error - * if (canRecover(rejection)) { - * return responseOrNewPromise - * } - * return $q.reject(rejection); - * } - * }; - * }); - * - * $httpProvider.interceptors.push('myHttpInterceptor'); - * - * - * // alternatively, register the interceptor via an anonymous factory - * $httpProvider.interceptors.push(function($q, dependency1, dependency2) { - * return { - * 'request': function(config) { - * // same as above - * }, - * - * 'response': function(response) { - * // same as above - * } - * }; - * }); - * ``` - * - * ## Security Considerations - * - * When designing web applications, consider security threats from: - * - * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) - * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) - * - * Both server and the client must cooperate in order to eliminate these threats. Angular comes - * pre-configured with strategies that address these issues, but for this to work backend server - * cooperation is required. - * - * ### JSON Vulnerability Protection - * - * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) - * allows third party website to turn your JSON resource URL into - * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To - * counter this your server can prefix all JSON requests with following string `")]}',\n"`. - * Angular will automatically strip the prefix before processing it as JSON. - * - * For example if your server needs to return: - * ```js - * ['one','two'] - * ``` - * - * which is vulnerable to attack, your server can return: - * ```js - * )]}', - * ['one','two'] - * ``` - * - * Angular will strip the prefix, before processing the JSON. - * - * - * ### Cross Site Request Forgery (XSRF) Protection - * - * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which - * an unauthorized site can gain your user's private data. Angular provides a mechanism - * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie - * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only - * JavaScript that runs on your domain could read the cookie, your server can be assured that - * the XHR came from JavaScript running on your domain. The header will not be set for - * cross-domain requests. - * - * To take advantage of this, your server needs to set a token in a JavaScript readable session - * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the - * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure - * that only JavaScript running on your domain could have sent the request. The token must be - * unique for each user and must be verifiable by the server (to prevent the JavaScript from - * making up its own tokens). We recommend that the token is a digest of your site's - * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) - * for added security. - * - * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName - * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time, - * or the per-request config object. - * - * In order to prevent collisions in environments where multiple Angular apps share the - * same domain or subdomain, we recommend that each application uses unique cookie name. - * - * @param {object} config Object describing the request to be made and how it should be - * processed. The object has following properties: - * - * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) - * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. - * - **params** – `{Object.}` – Map of strings or objects which will be serialized - * with the `paramSerializer` and appended as GET parameters. - * - **data** – `{string|Object}` – Data to be sent as the request message data. - * - **headers** – `{Object}` – Map of strings or functions which return strings representing - * HTTP headers to send to the server. If the return value of a function is null, the - * header will not be sent. Functions accept a config object as an argument. - * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token. - * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. - * - **transformRequest** – - * `{function(data, headersGetter)|Array.}` – - * transform function or an array of such functions. The transform function takes the http - * request body and headers and returns its transformed (typically serialized) version. - * See {@link ng.$http#overriding-the-default-transformations-per-request - * Overriding the Default Transformations} - * - **transformResponse** – - * `{function(data, headersGetter, status)|Array.}` – - * transform function or an array of such functions. The transform function takes the http - * response body, headers and status and returns its transformed (typically deserialized) version. - * See {@link ng.$http#overriding-the-default-transformations-per-request - * Overriding the Default TransformationjqLiks} - * - **paramSerializer** - `{string|function(Object):string}` - A function used to - * prepare the string representation of request parameters (specified as an object). - * If specified as string, it is interpreted as function registered with the - * {@link $injector $injector}, which means you can create your own serializer - * by registering it as a {@link auto.$provide#service service}. - * The default serializer is the {@link $httpParamSerializer $httpParamSerializer}; - * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike} - * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the - * GET request, otherwise if a cache instance built with - * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for - * caching. - * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} - * that should abort the request when resolved. - * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the - * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials) - * for more information. - * - **responseType** - `{string}` - see - * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype). - * - * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object - * when the request succeeds or fails. - * - * - * @property {Array.} pendingRequests Array of config objects for currently pending - * requests. This is primarily meant to be used for debugging purposes. - * - * - * @example - - -
- - -
- - - -
http status code: {{status}}
-
http response data: {{data}}
-
-
- - angular.module('httpExample', []) - .controller('FetchController', ['$scope', '$http', '$templateCache', - function($scope, $http, $templateCache) { - $scope.method = 'GET'; - $scope.url = 'http-hello.html'; - - $scope.fetch = function() { - $scope.code = null; - $scope.response = null; - - $http({method: $scope.method, url: $scope.url, cache: $templateCache}). - then(function(response) { - $scope.status = response.status; - $scope.data = response.data; - }, function(response) { - $scope.data = response.data || "Request failed"; - $scope.status = response.status; - }); - }; - - $scope.updateModel = function(method, url) { - $scope.method = method; - $scope.url = url; - }; - }]); - - - Hello, $http! - - - var status = element(by.binding('status')); - var data = element(by.binding('data')); - var fetchBtn = element(by.id('fetchbtn')); - var sampleGetBtn = element(by.id('samplegetbtn')); - var sampleJsonpBtn = element(by.id('samplejsonpbtn')); - var invalidJsonpBtn = element(by.id('invalidjsonpbtn')); - - it('should make an xhr GET request', function() { - sampleGetBtn.click(); - fetchBtn.click(); - expect(status.getText()).toMatch('200'); - expect(data.getText()).toMatch(/Hello, \$http!/); - }); - -// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185 -// it('should make a JSONP request to angularjs.org', function() { -// sampleJsonpBtn.click(); -// fetchBtn.click(); -// expect(status.getText()).toMatch('200'); -// expect(data.getText()).toMatch(/Super Hero!/); -// }); - - it('should make JSONP request to invalid URL and invoke the error handler', - function() { - invalidJsonpBtn.click(); - fetchBtn.click(); - expect(status.getText()).toMatch('0'); - expect(data.getText()).toMatch('Request failed'); - }); - -
- */ - function $http(requestConfig) { - - if (!angular.isObject(requestConfig)) { - throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig); - } - - var config = extend({ - method: 'get', - transformRequest: defaults.transformRequest, - transformResponse: defaults.transformResponse, - paramSerializer: defaults.paramSerializer - }, requestConfig); - - config.headers = mergeHeaders(requestConfig); - config.method = uppercase(config.method); - config.paramSerializer = isString(config.paramSerializer) ? - $injector.get(config.paramSerializer) : config.paramSerializer; - - var serverRequest = function(config) { - var headers = config.headers; - var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest); - - // strip content-type if data is undefined - if (isUndefined(reqData)) { - forEach(headers, function(value, header) { - if (lowercase(header) === 'content-type') { - delete headers[header]; - } - }); - } - - if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { - config.withCredentials = defaults.withCredentials; - } - - // send request - return sendReq(config, reqData).then(transformResponse, transformResponse); - }; - - var chain = [serverRequest, undefined]; - var promise = $q.when(config); - - // apply interceptors - forEach(reversedInterceptors, function(interceptor) { - if (interceptor.request || interceptor.requestError) { - chain.unshift(interceptor.request, interceptor.requestError); - } - if (interceptor.response || interceptor.responseError) { - chain.push(interceptor.response, interceptor.responseError); - } - }); - - while (chain.length) { - var thenFn = chain.shift(); - var rejectFn = chain.shift(); - - promise = promise.then(thenFn, rejectFn); - } - - if (useLegacyPromise) { - promise.success = function(fn) { - assertArgFn(fn, 'fn'); - - promise.then(function(response) { - fn(response.data, response.status, response.headers, config); - }); - return promise; - }; - - promise.error = function(fn) { - assertArgFn(fn, 'fn'); - - promise.then(null, function(response) { - fn(response.data, response.status, response.headers, config); - }); - return promise; - }; - } else { - promise.success = $httpMinErrLegacyFn('success'); - promise.error = $httpMinErrLegacyFn('error'); - } - - return promise; - - function transformResponse(response) { - // make a copy since the response must be cacheable - var resp = extend({}, response); - if (!response.data) { - resp.data = response.data; - } else { - resp.data = transformData(response.data, response.headers, response.status, config.transformResponse); - } - return (isSuccess(response.status)) - ? resp - : $q.reject(resp); - } - - function executeHeaderFns(headers, config) { - var headerContent, processedHeaders = {}; - - forEach(headers, function(headerFn, header) { - if (isFunction(headerFn)) { - headerContent = headerFn(config); - if (headerContent != null) { - processedHeaders[header] = headerContent; - } - } else { - processedHeaders[header] = headerFn; - } - }); - - return processedHeaders; - } - - function mergeHeaders(config) { - var defHeaders = defaults.headers, - reqHeaders = extend({}, config.headers), - defHeaderName, lowercaseDefHeaderName, reqHeaderName; - - defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]); - - // using for-in instead of forEach to avoid unecessary iteration after header has been found - defaultHeadersIteration: - for (defHeaderName in defHeaders) { - lowercaseDefHeaderName = lowercase(defHeaderName); - - for (reqHeaderName in reqHeaders) { - if (lowercase(reqHeaderName) === lowercaseDefHeaderName) { - continue defaultHeadersIteration; - } - } - - reqHeaders[defHeaderName] = defHeaders[defHeaderName]; - } - - // execute if header value is a function for merged headers - return executeHeaderFns(reqHeaders, shallowCopy(config)); - } - } - - $http.pendingRequests = []; - - /** - * @ngdoc method - * @name $http#get - * - * @description - * Shortcut method to perform `GET` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name $http#delete - * - * @description - * Shortcut method to perform `DELETE` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name $http#head - * - * @description - * Shortcut method to perform `HEAD` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name $http#jsonp - * - * @description - * Shortcut method to perform `JSONP` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request. - * The name of the callback should be the string `JSON_CALLBACK`. - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - createShortMethods('get', 'delete', 'head', 'jsonp'); - - /** - * @ngdoc method - * @name $http#post - * - * @description - * Shortcut method to perform `POST` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {*} data Request content - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name $http#put - * - * @description - * Shortcut method to perform `PUT` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {*} data Request content - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name $http#patch - * - * @description - * Shortcut method to perform `PATCH` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {*} data Request content - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - createShortMethodsWithData('post', 'put', 'patch'); - - /** - * @ngdoc property - * @name $http#defaults - * - * @description - * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of - * default headers, withCredentials as well as request and response transformations. - * - * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. - */ - $http.defaults = defaults; - - - return $http; - - - function createShortMethods(names) { - forEach(arguments, function(name) { - $http[name] = function(url, config) { - return $http(extend({}, config || {}, { - method: name, - url: url - })); - }; - }); - } - - - function createShortMethodsWithData(name) { - forEach(arguments, function(name) { - $http[name] = function(url, data, config) { - return $http(extend({}, config || {}, { - method: name, - url: url, - data: data - })); - }; - }); - } - - - /** - * Makes the request. - * - * !!! ACCESSES CLOSURE VARS: - * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests - */ - function sendReq(config, reqData) { - var deferred = $q.defer(), - promise = deferred.promise, - cache, - cachedResp, - reqHeaders = config.headers, - url = buildUrl(config.url, config.paramSerializer(config.params)); - - $http.pendingRequests.push(config); - promise.then(removePendingReq, removePendingReq); - - - if ((config.cache || defaults.cache) && config.cache !== false && - (config.method === 'GET' || config.method === 'JSONP')) { - cache = isObject(config.cache) ? config.cache - : isObject(defaults.cache) ? defaults.cache - : defaultCache; - } - - if (cache) { - cachedResp = cache.get(url); - if (isDefined(cachedResp)) { - if (isPromiseLike(cachedResp)) { - // cached request has already been sent, but there is no response yet - cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult); - } else { - // serving from cache - if (isArray(cachedResp)) { - resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]); - } else { - resolvePromise(cachedResp, 200, {}, 'OK'); - } - } - } else { - // put the promise for the non-transformed response into cache as a placeholder - cache.put(url, promise); - } - } - - - // if we won't have the response in cache, set the xsrf headers and - // send the request to the backend - if (isUndefined(cachedResp)) { - var xsrfValue = urlIsSameOrigin(config.url) - ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName] - : undefined; - if (xsrfValue) { - reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue; - } - - $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, - config.withCredentials, config.responseType); - } - - return promise; - - - /** - * Callback registered to $httpBackend(): - * - caches the response if desired - * - resolves the raw $http promise - * - calls $apply - */ - function done(status, response, headersString, statusText) { - if (cache) { - if (isSuccess(status)) { - cache.put(url, [status, response, parseHeaders(headersString), statusText]); - } else { - // remove promise from the cache - cache.remove(url); - } - } - - function resolveHttpPromise() { - resolvePromise(response, status, headersString, statusText); - } - - if (useApplyAsync) { - $rootScope.$applyAsync(resolveHttpPromise); - } else { - resolveHttpPromise(); - if (!$rootScope.$$phase) $rootScope.$apply(); - } - } - - - /** - * Resolves the raw $http promise. - */ - function resolvePromise(response, status, headers, statusText) { - // normalize internal statuses to 0 - status = Math.max(status, 0); - - (isSuccess(status) ? deferred.resolve : deferred.reject)({ - data: response, - status: status, - headers: headersGetter(headers), - config: config, - statusText: statusText - }); - } - - function resolvePromiseWithResult(result) { - resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText); - } - - function removePendingReq() { - var idx = $http.pendingRequests.indexOf(config); - if (idx !== -1) $http.pendingRequests.splice(idx, 1); - } - } - - - function buildUrl(url, serializedParams) { - if (serializedParams.length > 0) { - url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams; - } - return url; - } - }]; -} - -function createXhr() { - return new window.XMLHttpRequest(); -} - -/** - * @ngdoc service - * @name $httpBackend - * @requires $window - * @requires $document - * - * @description - * HTTP backend used by the {@link ng.$http service} that delegates to - * XMLHttpRequest object or JSONP and deals with browser incompatibilities. - * - * You should never need to use this service directly, instead use the higher-level abstractions: - * {@link ng.$http $http} or {@link ngResource.$resource $resource}. - * - * During testing this implementation is swapped with {@link ngMock.$httpBackend mock - * $httpBackend} which can be trained with responses. - */ -function $HttpBackendProvider() { - this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { - return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]); - }]; -} - -function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) { - // TODO(vojta): fix the signature - return function(method, url, post, callback, headers, timeout, withCredentials, responseType) { - $browser.$$incOutstandingRequestCount(); - url = url || $browser.url(); - - if (lowercase(method) == 'jsonp') { - var callbackId = '_' + (callbacks.counter++).toString(36); - callbacks[callbackId] = function(data) { - callbacks[callbackId].data = data; - callbacks[callbackId].called = true; - }; - - var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), - callbackId, function(status, text) { - completeRequest(callback, status, callbacks[callbackId].data, "", text); - callbacks[callbackId] = noop; - }); - } else { - - var xhr = createXhr(); - - xhr.open(method, url, true); - forEach(headers, function(value, key) { - if (isDefined(value)) { - xhr.setRequestHeader(key, value); - } - }); - - xhr.onload = function requestLoaded() { - var statusText = xhr.statusText || ''; - - // responseText is the old-school way of retrieving response (supported by IE9) - // response/responseType properties were introduced in XHR Level2 spec (supported by IE10) - var response = ('response' in xhr) ? xhr.response : xhr.responseText; - - // normalize IE9 bug (http://bugs.jquery.com/ticket/1450) - var status = xhr.status === 1223 ? 204 : xhr.status; - - // fix status code when it is 0 (0 status is undocumented). - // Occurs when accessing file resources or on Android 4.1 stock browser - // while retrieving files from application cache. - if (status === 0) { - status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0; - } - - completeRequest(callback, - status, - response, - xhr.getAllResponseHeaders(), - statusText); - }; - - var requestError = function() { - // The response is always empty - // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error - completeRequest(callback, -1, null, null, ''); - }; - - xhr.onerror = requestError; - xhr.onabort = requestError; - - if (withCredentials) { - xhr.withCredentials = true; - } - - if (responseType) { - try { - xhr.responseType = responseType; - } catch (e) { - // WebKit added support for the json responseType value on 09/03/2013 - // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are - // known to throw when setting the value "json" as the response type. Other older - // browsers implementing the responseType - // - // The json response type can be ignored if not supported, because JSON payloads are - // parsed on the client-side regardless. - if (responseType !== 'json') { - throw e; - } - } - } - - xhr.send(post); - } - - if (timeout > 0) { - var timeoutId = $browserDefer(timeoutRequest, timeout); - } else if (isPromiseLike(timeout)) { - timeout.then(timeoutRequest); - } - - - function timeoutRequest() { - jsonpDone && jsonpDone(); - xhr && xhr.abort(); - } - - function completeRequest(callback, status, response, headersString, statusText) { - // cancel timeout and subsequent timeout promise resolution - if (timeoutId !== undefined) { - $browserDefer.cancel(timeoutId); - } - jsonpDone = xhr = null; - - callback(status, response, headersString, statusText); - $browser.$$completeOutstandingRequest(noop); - } - }; - - function jsonpReq(url, callbackId, done) { - // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.: - // - fetches local scripts via XHR and evals them - // - adds and immediately removes script elements from the document - var script = rawDocument.createElement('script'), callback = null; - script.type = "text/javascript"; - script.src = url; - script.async = true; - - callback = function(event) { - removeEventListenerFn(script, "load", callback); - removeEventListenerFn(script, "error", callback); - rawDocument.body.removeChild(script); - script = null; - var status = -1; - var text = "unknown"; - - if (event) { - if (event.type === "load" && !callbacks[callbackId].called) { - event = { type: "error" }; - } - text = event.type; - status = event.type === "error" ? 404 : 200; - } - - if (done) { - done(status, text); - } - }; - - addEventListenerFn(script, "load", callback); - addEventListenerFn(script, "error", callback); - rawDocument.body.appendChild(script); - return callback; - } -} - -var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate'); -$interpolateMinErr.throwNoconcat = function(text) { - throw $interpolateMinErr('noconcat', - "Error while interpolating: {0}\nStrict Contextual Escaping disallows " + - "interpolations that concatenate multiple expressions when a trusted value is " + - "required. See http://docs.angularjs.org/api/ng.$sce", text); -}; - -$interpolateMinErr.interr = function(text, err) { - return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString()); -}; - -/** - * @ngdoc provider - * @name $interpolateProvider - * - * @description - * - * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. - * - * @example - - - -
- //demo.label// -
-
- - it('should interpolate binding with custom symbols', function() { - expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.'); - }); - -
- */ -function $InterpolateProvider() { - var startSymbol = '{{'; - var endSymbol = '}}'; - - /** - * @ngdoc method - * @name $interpolateProvider#startSymbol - * @description - * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. - * - * @param {string=} value new value to set the starting symbol to. - * @returns {string|self} Returns the symbol when used as getter and self if used as setter. - */ - this.startSymbol = function(value) { - if (value) { - startSymbol = value; - return this; - } else { - return startSymbol; - } - }; - - /** - * @ngdoc method - * @name $interpolateProvider#endSymbol - * @description - * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. - * - * @param {string=} value new value to set the ending symbol to. - * @returns {string|self} Returns the symbol when used as getter and self if used as setter. - */ - this.endSymbol = function(value) { - if (value) { - endSymbol = value; - return this; - } else { - return endSymbol; - } - }; - - - this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) { - var startSymbolLength = startSymbol.length, - endSymbolLength = endSymbol.length, - escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'), - escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g'); - - function escape(ch) { - return '\\\\\\' + ch; - } - - function unescapeText(text) { - return text.replace(escapedStartRegexp, startSymbol). - replace(escapedEndRegexp, endSymbol); - } - - function stringify(value) { - if (value == null) { // null || undefined - return ''; - } - switch (typeof value) { - case 'string': - break; - case 'number': - value = '' + value; - break; - default: - value = toJson(value); - } - - return value; - } - - /** - * @ngdoc service - * @name $interpolate - * @kind function - * - * @requires $parse - * @requires $sce - * - * @description - * - * Compiles a string with markup into an interpolation function. This service is used by the - * HTML {@link ng.$compile $compile} service for data binding. See - * {@link ng.$interpolateProvider $interpolateProvider} for configuring the - * interpolation markup. - * - * - * ```js - * var $interpolate = ...; // injected - * var exp = $interpolate('Hello {{name | uppercase}}!'); - * expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!'); - * ``` - * - * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is - * `true`, the interpolation function will return `undefined` unless all embedded expressions - * evaluate to a value other than `undefined`. - * - * ```js - * var $interpolate = ...; // injected - * var context = {greeting: 'Hello', name: undefined }; - * - * // default "forgiving" mode - * var exp = $interpolate('{{greeting}} {{name}}!'); - * expect(exp(context)).toEqual('Hello !'); - * - * // "allOrNothing" mode - * exp = $interpolate('{{greeting}} {{name}}!', false, null, true); - * expect(exp(context)).toBeUndefined(); - * context.name = 'Angular'; - * expect(exp(context)).toEqual('Hello Angular!'); - * ``` - * - * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior. - * - * ####Escaped Interpolation - * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers - * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash). - * It will be rendered as a regular start/end marker, and will not be interpreted as an expression - * or binding. - * - * This enables web-servers to prevent script injection attacks and defacing attacks, to some - * degree, while also enabling code examples to work without relying on the - * {@link ng.directive:ngNonBindable ngNonBindable} directive. - * - * **For security purposes, it is strongly encouraged that web servers escape user-supplied data, - * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all - * interpolation start/end markers with their escaped counterparts.** - * - * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered - * output when the $interpolate service processes the text. So, for HTML elements interpolated - * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter - * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such, - * this is typically useful only when user-data is used in rendering a template from the server, or - * when otherwise untrusted data is used by a directive. - * - * - * - *
- *

{{apptitle}}: \{\{ username = "defaced value"; \}\} - *

- *

{{username}} attempts to inject code which will deface the - * application, but fails to accomplish their task, because the server has correctly - * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash) - * characters.

- *

Instead, the result of the attempted script injection is visible, and can be removed - * from the database by an administrator.

- *
- *
- *
- * - * @param {string} text The text with markup to interpolate. - * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have - * embedded expression in order to return an interpolation function. Strings with no - * embedded expression will return null for the interpolation function. - * @param {string=} trustedContext when provided, the returned function passes the interpolated - * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult, - * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that - * provides Strict Contextual Escaping for details. - * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined - * unless all embedded expressions evaluate to a value other than `undefined`. - * @returns {function(context)} an interpolation function which is used to compute the - * interpolated string. The function has these parameters: - * - * - `context`: evaluation context for all expressions embedded in the interpolated text - */ - function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) { - allOrNothing = !!allOrNothing; - var startIndex, - endIndex, - index = 0, - expressions = [], - parseFns = [], - textLength = text.length, - exp, - concat = [], - expressionPositions = []; - - while (index < textLength) { - if (((startIndex = text.indexOf(startSymbol, index)) != -1) && - ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) { - if (index !== startIndex) { - concat.push(unescapeText(text.substring(index, startIndex))); - } - exp = text.substring(startIndex + startSymbolLength, endIndex); - expressions.push(exp); - parseFns.push($parse(exp, parseStringifyInterceptor)); - index = endIndex + endSymbolLength; - expressionPositions.push(concat.length); - concat.push(''); - } else { - // we did not find an interpolation, so we have to add the remainder to the separators array - if (index !== textLength) { - concat.push(unescapeText(text.substring(index))); - } - break; - } - } - - // Concatenating expressions makes it hard to reason about whether some combination of - // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a - // single expression be used for iframe[src], object[src], etc., we ensure that the value - // that's used is assigned or constructed by some JS code somewhere that is more testable or - // make it obvious that you bound the value to some user controlled value. This helps reduce - // the load when auditing for XSS issues. - if (trustedContext && concat.length > 1) { - $interpolateMinErr.throwNoconcat(text); - } - - if (!mustHaveExpression || expressions.length) { - var compute = function(values) { - for (var i = 0, ii = expressions.length; i < ii; i++) { - if (allOrNothing && isUndefined(values[i])) return; - concat[expressionPositions[i]] = values[i]; - } - return concat.join(''); - }; - - var getValue = function(value) { - return trustedContext ? - $sce.getTrusted(trustedContext, value) : - $sce.valueOf(value); - }; - - return extend(function interpolationFn(context) { - var i = 0; - var ii = expressions.length; - var values = new Array(ii); - - try { - for (; i < ii; i++) { - values[i] = parseFns[i](context); - } - - return compute(values); - } catch (err) { - $exceptionHandler($interpolateMinErr.interr(text, err)); - } - - }, { - // all of these properties are undocumented for now - exp: text, //just for compatibility with regular watchers created via $watch - expressions: expressions, - $$watchDelegate: function(scope, listener) { - var lastValue; - return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) { - var currValue = compute(values); - if (isFunction(listener)) { - listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope); - } - lastValue = currValue; - }); - } - }); - } - - function parseStringifyInterceptor(value) { - try { - value = getValue(value); - return allOrNothing && !isDefined(value) ? value : stringify(value); - } catch (err) { - $exceptionHandler($interpolateMinErr.interr(text, err)); - } - } - } - - - /** - * @ngdoc method - * @name $interpolate#startSymbol - * @description - * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`. - * - * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change - * the symbol. - * - * @returns {string} start symbol. - */ - $interpolate.startSymbol = function() { - return startSymbol; - }; - - - /** - * @ngdoc method - * @name $interpolate#endSymbol - * @description - * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. - * - * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change - * the symbol. - * - * @returns {string} end symbol. - */ - $interpolate.endSymbol = function() { - return endSymbol; - }; - - return $interpolate; - }]; -} - -function $IntervalProvider() { - this.$get = ['$rootScope', '$window', '$q', '$$q', - function($rootScope, $window, $q, $$q) { - var intervals = {}; - - - /** - * @ngdoc service - * @name $interval - * - * @description - * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay` - * milliseconds. - * - * The return value of registering an interval function is a promise. This promise will be - * notified upon each tick of the interval, and will be resolved after `count` iterations, or - * run indefinitely if `count` is not defined. The value of the notification will be the - * number of iterations that have run. - * To cancel an interval, call `$interval.cancel(promise)`. - * - * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to - * move forward by `millis` milliseconds and trigger any functions scheduled to run in that - * time. - * - *
- * **Note**: Intervals created by this service must be explicitly destroyed when you are finished - * with them. In particular they are not automatically destroyed when a controller's scope or a - * directive's element are destroyed. - * You should take this into consideration and make sure to always cancel the interval at the - * appropriate moment. See the example below for more details on how and when to do this. - *
- * - * @param {function()} fn A function that should be called repeatedly. - * @param {number} delay Number of milliseconds between each function call. - * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat - * indefinitely. - * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise - * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. - * @param {...*=} Pass additional parameters to the executed function. - * @returns {promise} A promise which will be notified on each iteration. - * - * @example - * - * - * - * - *
- *
- *
- * Current time is: - *
- * Blood 1 : {{blood_1}} - * Blood 2 : {{blood_2}} - * - * - * - *
- *
- * - *
- *
- */ - function interval(fn, delay, count, invokeApply) { - var hasParams = arguments.length > 4, - args = hasParams ? sliceArgs(arguments, 4) : [], - setInterval = $window.setInterval, - clearInterval = $window.clearInterval, - iteration = 0, - skipApply = (isDefined(invokeApply) && !invokeApply), - deferred = (skipApply ? $$q : $q).defer(), - promise = deferred.promise; - - count = isDefined(count) ? count : 0; - - promise.then(null, null, (!hasParams) ? fn : function() { - fn.apply(null, args); - }); - - promise.$$intervalId = setInterval(function tick() { - deferred.notify(iteration++); - - if (count > 0 && iteration >= count) { - deferred.resolve(iteration); - clearInterval(promise.$$intervalId); - delete intervals[promise.$$intervalId]; - } - - if (!skipApply) $rootScope.$apply(); - - }, delay); - - intervals[promise.$$intervalId] = deferred; - - return promise; - } - - - /** - * @ngdoc method - * @name $interval#cancel - * - * @description - * Cancels a task associated with the `promise`. - * - * @param {Promise=} promise returned by the `$interval` function. - * @returns {boolean} Returns `true` if the task was successfully canceled. - */ - interval.cancel = function(promise) { - if (promise && promise.$$intervalId in intervals) { - intervals[promise.$$intervalId].reject('canceled'); - $window.clearInterval(promise.$$intervalId); - delete intervals[promise.$$intervalId]; - return true; - } - return false; - }; - - return interval; - }]; -} - -/** - * @ngdoc service - * @name $locale - * - * @description - * $locale service provides localization rules for various Angular components. As of right now the - * only public api is: - * - * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) - */ - -var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/, - DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21}; -var $locationMinErr = minErr('$location'); - - -/** - * Encode path using encodeUriSegment, ignoring forward slashes - * - * @param {string} path Path to encode - * @returns {string} - */ -function encodePath(path) { - var segments = path.split('/'), - i = segments.length; - - while (i--) { - segments[i] = encodeUriSegment(segments[i]); - } - - return segments.join('/'); -} - -function parseAbsoluteUrl(absoluteUrl, locationObj) { - var parsedUrl = urlResolve(absoluteUrl); - - locationObj.$$protocol = parsedUrl.protocol; - locationObj.$$host = parsedUrl.hostname; - locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null; -} - - -function parseAppUrl(relativeUrl, locationObj) { - var prefixed = (relativeUrl.charAt(0) !== '/'); - if (prefixed) { - relativeUrl = '/' + relativeUrl; - } - var match = urlResolve(relativeUrl); - locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? - match.pathname.substring(1) : match.pathname); - locationObj.$$search = parseKeyValue(match.search); - locationObj.$$hash = decodeURIComponent(match.hash); - - // make sure path starts with '/'; - if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') { - locationObj.$$path = '/' + locationObj.$$path; - } -} - - -/** - * - * @param {string} begin - * @param {string} whole - * @returns {string} returns text from whole after begin or undefined if it does not begin with - * expected string. - */ -function beginsWith(begin, whole) { - if (whole.indexOf(begin) === 0) { - return whole.substr(begin.length); - } -} - - -function stripHash(url) { - var index = url.indexOf('#'); - return index == -1 ? url : url.substr(0, index); -} - -function trimEmptyHash(url) { - return url.replace(/(#.+)|#$/, '$1'); -} - - -function stripFile(url) { - return url.substr(0, stripHash(url).lastIndexOf('/') + 1); -} - -/* return the server only (scheme://host:port) */ -function serverBase(url) { - return url.substring(0, url.indexOf('/', url.indexOf('//') + 2)); -} - - -/** - * LocationHtml5Url represents an url - * This object is exposed as $location service when HTML5 mode is enabled and supported - * - * @constructor - * @param {string} appBase application base URL - * @param {string} appBaseNoFile application base URL stripped of any filename - * @param {string} basePrefix url path prefix - */ -function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) { - this.$$html5 = true; - basePrefix = basePrefix || ''; - parseAbsoluteUrl(appBase, this); - - - /** - * Parse given html5 (regular) url string into properties - * @param {string} url HTML5 url - * @private - */ - this.$$parse = function(url) { - var pathUrl = beginsWith(appBaseNoFile, url); - if (!isString(pathUrl)) { - throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, - appBaseNoFile); - } - - parseAppUrl(pathUrl, this); - - if (!this.$$path) { - this.$$path = '/'; - } - - this.$$compose(); - }; - - /** - * Compose url and update `absUrl` property - * @private - */ - this.$$compose = function() { - var search = toKeyValue(this.$$search), - hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; - - this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; - this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' - }; - - this.$$parseLinkUrl = function(url, relHref) { - if (relHref && relHref[0] === '#') { - // special case for links to hash fragments: - // keep the old url and only replace the hash fragment - this.hash(relHref.slice(1)); - return true; - } - var appUrl, prevAppUrl; - var rewrittenUrl; - - if ((appUrl = beginsWith(appBase, url)) !== undefined) { - prevAppUrl = appUrl; - if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) { - rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl); - } else { - rewrittenUrl = appBase + prevAppUrl; - } - } else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) { - rewrittenUrl = appBaseNoFile + appUrl; - } else if (appBaseNoFile == url + '/') { - rewrittenUrl = appBaseNoFile; - } - if (rewrittenUrl) { - this.$$parse(rewrittenUrl); - } - return !!rewrittenUrl; - }; -} - - -/** - * LocationHashbangUrl represents url - * This object is exposed as $location service when developer doesn't opt into html5 mode. - * It also serves as the base class for html5 mode fallback on legacy browsers. - * - * @constructor - * @param {string} appBase application base URL - * @param {string} appBaseNoFile application base URL stripped of any filename - * @param {string} hashPrefix hashbang prefix - */ -function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) { - - parseAbsoluteUrl(appBase, this); - - - /** - * Parse given hashbang url into properties - * @param {string} url Hashbang url - * @private - */ - this.$$parse = function(url) { - var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url); - var withoutHashUrl; - - if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') { - - // The rest of the url starts with a hash so we have - // got either a hashbang path or a plain hash fragment - withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl); - if (isUndefined(withoutHashUrl)) { - // There was no hashbang prefix so we just have a hash fragment - withoutHashUrl = withoutBaseUrl; - } - - } else { - // There was no hashbang path nor hash fragment: - // If we are in HTML5 mode we use what is left as the path; - // Otherwise we ignore what is left - if (this.$$html5) { - withoutHashUrl = withoutBaseUrl; - } else { - withoutHashUrl = ''; - if (isUndefined(withoutBaseUrl)) { - appBase = url; - this.replace(); - } - } - } - - parseAppUrl(withoutHashUrl, this); - - this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); - - this.$$compose(); - - /* - * In Windows, on an anchor node on documents loaded from - * the filesystem, the browser will return a pathname - * prefixed with the drive name ('/C:/path') when a - * pathname without a drive is set: - * * a.setAttribute('href', '/foo') - * * a.pathname === '/C:/foo' //true - * - * Inside of Angular, we're always using pathnames that - * do not include drive names for routing. - */ - function removeWindowsDriveName(path, url, base) { - /* - Matches paths for file protocol on windows, - such as /C:/foo/bar, and captures only /foo/bar. - */ - var windowsFilePathExp = /^\/[A-Z]:(\/.*)/; - - var firstPathSegmentMatch; - - //Get the relative path from the input URL. - if (url.indexOf(base) === 0) { - url = url.replace(base, ''); - } - - // The input URL intentionally contains a first path segment that ends with a colon. - if (windowsFilePathExp.exec(url)) { - return path; - } - - firstPathSegmentMatch = windowsFilePathExp.exec(path); - return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path; - } - }; - - /** - * Compose hashbang url and update `absUrl` property - * @private - */ - this.$$compose = function() { - var search = toKeyValue(this.$$search), - hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; - - this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; - this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); - }; - - this.$$parseLinkUrl = function(url, relHref) { - if (stripHash(appBase) == stripHash(url)) { - this.$$parse(url); - return true; - } - return false; - }; -} - - -/** - * LocationHashbangUrl represents url - * This object is exposed as $location service when html5 history api is enabled but the browser - * does not support it. - * - * @constructor - * @param {string} appBase application base URL - * @param {string} appBaseNoFile application base URL stripped of any filename - * @param {string} hashPrefix hashbang prefix - */ -function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) { - this.$$html5 = true; - LocationHashbangUrl.apply(this, arguments); - - this.$$parseLinkUrl = function(url, relHref) { - if (relHref && relHref[0] === '#') { - // special case for links to hash fragments: - // keep the old url and only replace the hash fragment - this.hash(relHref.slice(1)); - return true; - } - - var rewrittenUrl; - var appUrl; - - if (appBase == stripHash(url)) { - rewrittenUrl = url; - } else if ((appUrl = beginsWith(appBaseNoFile, url))) { - rewrittenUrl = appBase + hashPrefix + appUrl; - } else if (appBaseNoFile === url + '/') { - rewrittenUrl = appBaseNoFile; - } - if (rewrittenUrl) { - this.$$parse(rewrittenUrl); - } - return !!rewrittenUrl; - }; - - this.$$compose = function() { - var search = toKeyValue(this.$$search), - hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; - - this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; - // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#' - this.$$absUrl = appBase + hashPrefix + this.$$url; - }; - -} - - -var locationPrototype = { - - /** - * Are we in html5 mode? - * @private - */ - $$html5: false, - - /** - * Has any change been replacing? - * @private - */ - $$replace: false, - - /** - * @ngdoc method - * @name $location#absUrl - * - * @description - * This method is getter only. - * - * Return full url representation with all segments encoded according to rules specified in - * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var absUrl = $location.absUrl(); - * // => "http://example.com/#/some/path?foo=bar&baz=xoxo" - * ``` - * - * @return {string} full url - */ - absUrl: locationGetter('$$absUrl'), - - /** - * @ngdoc method - * @name $location#url - * - * @description - * This method is getter / setter. - * - * Return url (e.g. `/path?a=b#hash`) when called without any parameter. - * - * Change path, search and hash, when called with parameter and return `$location`. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var url = $location.url(); - * // => "/some/path?foo=bar&baz=xoxo" - * ``` - * - * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) - * @return {string} url - */ - url: function(url) { - if (isUndefined(url)) { - return this.$$url; - } - - var match = PATH_MATCH.exec(url); - if (match[1] || url === '') this.path(decodeURIComponent(match[1])); - if (match[2] || match[1] || url === '') this.search(match[3] || ''); - this.hash(match[5] || ''); - - return this; - }, - - /** - * @ngdoc method - * @name $location#protocol - * - * @description - * This method is getter only. - * - * Return protocol of current url. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var protocol = $location.protocol(); - * // => "http" - * ``` - * - * @return {string} protocol of current url - */ - protocol: locationGetter('$$protocol'), - - /** - * @ngdoc method - * @name $location#host - * - * @description - * This method is getter only. - * - * Return host of current url. - * - * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var host = $location.host(); - * // => "example.com" - * - * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo - * host = $location.host(); - * // => "example.com" - * host = location.host; - * // => "example.com:8080" - * ``` - * - * @return {string} host of current url. - */ - host: locationGetter('$$host'), - - /** - * @ngdoc method - * @name $location#port - * - * @description - * This method is getter only. - * - * Return port of current url. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var port = $location.port(); - * // => 80 - * ``` - * - * @return {Number} port - */ - port: locationGetter('$$port'), - - /** - * @ngdoc method - * @name $location#path - * - * @description - * This method is getter / setter. - * - * Return path of current url when called without any parameter. - * - * Change path when called with parameter and return `$location`. - * - * Note: Path should always begin with forward slash (/), this method will add the forward slash - * if it is missing. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var path = $location.path(); - * // => "/some/path" - * ``` - * - * @param {(string|number)=} path New path - * @return {string} path - */ - path: locationGetterSetter('$$path', function(path) { - path = path !== null ? path.toString() : ''; - return path.charAt(0) == '/' ? path : '/' + path; - }), - - /** - * @ngdoc method - * @name $location#search - * - * @description - * This method is getter / setter. - * - * Return search part (as object) of current url when called without any parameter. - * - * Change search part when called with parameter and return `$location`. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var searchObject = $location.search(); - * // => {foo: 'bar', baz: 'xoxo'} - * - * // set foo to 'yipee' - * $location.search('foo', 'yipee'); - * // $location.search() => {foo: 'yipee', baz: 'xoxo'} - * ``` - * - * @param {string|Object.|Object.>} search New search params - string or - * hash object. - * - * When called with a single argument the method acts as a setter, setting the `search` component - * of `$location` to the specified value. - * - * If the argument is a hash object containing an array of values, these values will be encoded - * as duplicate search parameters in the url. - * - * @param {(string|Number|Array|boolean)=} paramValue If `search` is a string or number, then `paramValue` - * will override only a single search property. - * - * If `paramValue` is an array, it will override the property of the `search` component of - * `$location` specified via the first argument. - * - * If `paramValue` is `null`, the property specified via the first argument will be deleted. - * - * If `paramValue` is `true`, the property specified via the first argument will be added with no - * value nor trailing equal sign. - * - * @return {Object} If called with no arguments returns the parsed `search` object. If called with - * one or more arguments returns `$location` object itself. - */ - search: function(search, paramValue) { - switch (arguments.length) { - case 0: - return this.$$search; - case 1: - if (isString(search) || isNumber(search)) { - search = search.toString(); - this.$$search = parseKeyValue(search); - } else if (isObject(search)) { - search = copy(search, {}); - // remove object undefined or null properties - forEach(search, function(value, key) { - if (value == null) delete search[key]; - }); - - this.$$search = search; - } else { - throw $locationMinErr('isrcharg', - 'The first argument of the `$location#search()` call must be a string or an object.'); - } - break; - default: - if (isUndefined(paramValue) || paramValue === null) { - delete this.$$search[search]; - } else { - this.$$search[search] = paramValue; - } - } - - this.$$compose(); - return this; - }, - - /** - * @ngdoc method - * @name $location#hash - * - * @description - * This method is getter / setter. - * - * Return hash fragment when called without any parameter. - * - * Change hash fragment when called with parameter and return `$location`. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue - * var hash = $location.hash(); - * // => "hashValue" - * ``` - * - * @param {(string|number)=} hash New hash fragment - * @return {string} hash - */ - hash: locationGetterSetter('$$hash', function(hash) { - return hash !== null ? hash.toString() : ''; - }), - - /** - * @ngdoc method - * @name $location#replace - * - * @description - * If called, all changes to $location during current `$digest` will be replacing current history - * record, instead of adding new one. - */ - replace: function() { - this.$$replace = true; - return this; - } -}; - -forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) { - Location.prototype = Object.create(locationPrototype); - - /** - * @ngdoc method - * @name $location#state - * - * @description - * This method is getter / setter. - * - * Return the history state object when called without any parameter. - * - * Change the history state object when called with one parameter and return `$location`. - * The state object is later passed to `pushState` or `replaceState`. - * - * NOTE: This method is supported only in HTML5 mode and only in browsers supporting - * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support - * older browsers (like IE9 or Android < 4.0), don't use this method. - * - * @param {object=} state State object for pushState or replaceState - * @return {object} state - */ - Location.prototype.state = function(state) { - if (!arguments.length) { - return this.$$state; - } - - if (Location !== LocationHtml5Url || !this.$$html5) { - throw $locationMinErr('nostate', 'History API state support is available only ' + - 'in HTML5 mode and only in browsers supporting HTML5 History API'); - } - // The user might modify `stateObject` after invoking `$location.state(stateObject)` - // but we're changing the $$state reference to $browser.state() during the $digest - // so the modification window is narrow. - this.$$state = isUndefined(state) ? null : state; - - return this; - }; -}); - - -function locationGetter(property) { - return function() { - return this[property]; - }; -} - - -function locationGetterSetter(property, preprocess) { - return function(value) { - if (isUndefined(value)) { - return this[property]; - } - - this[property] = preprocess(value); - this.$$compose(); - - return this; - }; -} - - -/** - * @ngdoc service - * @name $location - * - * @requires $rootElement - * - * @description - * The $location service parses the URL in the browser address bar (based on the - * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL - * available to your application. Changes to the URL in the address bar are reflected into - * $location service and changes to $location are reflected into the browser address bar. - * - * **The $location service:** - * - * - Exposes the current URL in the browser address bar, so you can - * - Watch and observe the URL. - * - Change the URL. - * - Synchronizes the URL with the browser when the user - * - Changes the address bar. - * - Clicks the back or forward button (or clicks a History link). - * - Clicks on a link. - * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). - * - * For more information see {@link guide/$location Developer Guide: Using $location} - */ - -/** - * @ngdoc provider - * @name $locationProvider - * @description - * Use the `$locationProvider` to configure how the application deep linking paths are stored. - */ -function $LocationProvider() { - var hashPrefix = '', - html5Mode = { - enabled: false, - requireBase: true, - rewriteLinks: true - }; - - /** - * @ngdoc method - * @name $locationProvider#hashPrefix - * @description - * @param {string=} prefix Prefix for hash part (containing path and search) - * @returns {*} current value if used as getter or itself (chaining) if used as setter - */ - this.hashPrefix = function(prefix) { - if (isDefined(prefix)) { - hashPrefix = prefix; - return this; - } else { - return hashPrefix; - } - }; - - /** - * @ngdoc method - * @name $locationProvider#html5Mode - * @description - * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value. - * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported - * properties: - * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to - * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not - * support `pushState`. - * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies - * whether or not a tag is required to be present. If `enabled` and `requireBase` are - * true, and a base tag is not present, an error will be thrown when `$location` is injected. - * See the {@link guide/$location $location guide for more information} - * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled, - * enables/disables url rewriting for relative links. - * - * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter - */ - this.html5Mode = function(mode) { - if (isBoolean(mode)) { - html5Mode.enabled = mode; - return this; - } else if (isObject(mode)) { - - if (isBoolean(mode.enabled)) { - html5Mode.enabled = mode.enabled; - } - - if (isBoolean(mode.requireBase)) { - html5Mode.requireBase = mode.requireBase; - } - - if (isBoolean(mode.rewriteLinks)) { - html5Mode.rewriteLinks = mode.rewriteLinks; - } - - return this; - } else { - return html5Mode; - } - }; - - /** - * @ngdoc event - * @name $location#$locationChangeStart - * @eventType broadcast on root scope - * @description - * Broadcasted before a URL will change. - * - * This change can be prevented by calling - * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more - * details about event object. Upon successful change - * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired. - * - * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when - * the browser supports the HTML5 History API. - * - * @param {Object} angularEvent Synthetic event object. - * @param {string} newUrl New URL - * @param {string=} oldUrl URL that was before it was changed. - * @param {string=} newState New history state object - * @param {string=} oldState History state object that was before it was changed. - */ - - /** - * @ngdoc event - * @name $location#$locationChangeSuccess - * @eventType broadcast on root scope - * @description - * Broadcasted after a URL was changed. - * - * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when - * the browser supports the HTML5 History API. - * - * @param {Object} angularEvent Synthetic event object. - * @param {string} newUrl New URL - * @param {string=} oldUrl URL that was before it was changed. - * @param {string=} newState New history state object - * @param {string=} oldState History state object that was before it was changed. - */ - - this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window', - function($rootScope, $browser, $sniffer, $rootElement, $window) { - var $location, - LocationMode, - baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' - initialUrl = $browser.url(), - appBase; - - if (html5Mode.enabled) { - if (!baseHref && html5Mode.requireBase) { - throw $locationMinErr('nobase', - "$location in HTML5 mode requires a tag to be present!"); - } - appBase = serverBase(initialUrl) + (baseHref || '/'); - LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url; - } else { - appBase = stripHash(initialUrl); - LocationMode = LocationHashbangUrl; - } - var appBaseNoFile = stripFile(appBase); - - $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix); - $location.$$parseLinkUrl(initialUrl, initialUrl); - - $location.$$state = $browser.state(); - - var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i; - - function setBrowserUrlWithFallback(url, replace, state) { - var oldUrl = $location.url(); - var oldState = $location.$$state; - try { - $browser.url(url, replace, state); - - // Make sure $location.state() returns referentially identical (not just deeply equal) - // state object; this makes possible quick checking if the state changed in the digest - // loop. Checking deep equality would be too expensive. - $location.$$state = $browser.state(); - } catch (e) { - // Restore old values if pushState fails - $location.url(oldUrl); - $location.$$state = oldState; - - throw e; - } - } - - $rootElement.on('click', function(event) { - // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) - // currently we open nice url link and redirect then - - if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return; - - var elm = jqLite(event.target); - - // traverse the DOM up to find first A tag - while (nodeName_(elm[0]) !== 'a') { - // ignore rewriting if no A tag (reached root element, or no parent - removed from document) - if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; - } - - var absHref = elm.prop('href'); - // get the actual href attribute - see - // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx - var relHref = elm.attr('href') || elm.attr('xlink:href'); - - if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') { - // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during - // an animation. - absHref = urlResolve(absHref.animVal).href; - } - - // Ignore when url is started with javascript: or mailto: - if (IGNORE_URI_REGEXP.test(absHref)) return; - - if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) { - if ($location.$$parseLinkUrl(absHref, relHref)) { - // We do a preventDefault for all urls that are part of the angular application, - // in html5mode and also without, so that we are able to abort navigation without - // getting double entries in the location history. - event.preventDefault(); - // update location manually - if ($location.absUrl() != $browser.url()) { - $rootScope.$apply(); - // hack to work around FF6 bug 684208 when scenario runner clicks on links - $window.angular['ff-684208-preventDefault'] = true; - } - } - } - }); - - - // rewrite hashbang url <> html5 url - if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) { - $browser.url($location.absUrl(), true); - } - - var initializing = true; - - // update $location when $browser url changes - $browser.onUrlChange(function(newUrl, newState) { - - if (isUndefined(beginsWith(appBaseNoFile, newUrl))) { - // If we are navigating outside of the app then force a reload - $window.location.href = newUrl; - return; - } - - $rootScope.$evalAsync(function() { - var oldUrl = $location.absUrl(); - var oldState = $location.$$state; - var defaultPrevented; - - $location.$$parse(newUrl); - $location.$$state = newState; - - defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, - newState, oldState).defaultPrevented; - - // if the location was changed by a `$locationChangeStart` handler then stop - // processing this location change - if ($location.absUrl() !== newUrl) return; - - if (defaultPrevented) { - $location.$$parse(oldUrl); - $location.$$state = oldState; - setBrowserUrlWithFallback(oldUrl, false, oldState); - } else { - initializing = false; - afterLocationChange(oldUrl, oldState); - } - }); - if (!$rootScope.$$phase) $rootScope.$digest(); - }); - - // update browser - $rootScope.$watch(function $locationWatch() { - var oldUrl = trimEmptyHash($browser.url()); - var newUrl = trimEmptyHash($location.absUrl()); - var oldState = $browser.state(); - var currentReplace = $location.$$replace; - var urlOrStateChanged = oldUrl !== newUrl || - ($location.$$html5 && $sniffer.history && oldState !== $location.$$state); - - if (initializing || urlOrStateChanged) { - initializing = false; - - $rootScope.$evalAsync(function() { - var newUrl = $location.absUrl(); - var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, - $location.$$state, oldState).defaultPrevented; - - // if the location was changed by a `$locationChangeStart` handler then stop - // processing this location change - if ($location.absUrl() !== newUrl) return; - - if (defaultPrevented) { - $location.$$parse(oldUrl); - $location.$$state = oldState; - } else { - if (urlOrStateChanged) { - setBrowserUrlWithFallback(newUrl, currentReplace, - oldState === $location.$$state ? null : $location.$$state); - } - afterLocationChange(oldUrl, oldState); - } - }); - } - - $location.$$replace = false; - - // we don't need to return anything because $evalAsync will make the digest loop dirty when - // there is a change - }); - - return $location; - - function afterLocationChange(oldUrl, oldState) { - $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl, - $location.$$state, oldState); - } -}]; -} - -/** - * @ngdoc service - * @name $log - * @requires $window - * - * @description - * Simple service for logging. Default implementation safely writes the message - * into the browser's console (if present). - * - * The main purpose of this service is to simplify debugging and troubleshooting. - * - * The default is to log `debug` messages. You can use - * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this. - * - * @example - - - angular.module('logExample', []) - .controller('LogController', ['$scope', '$log', function($scope, $log) { - $scope.$log = $log; - $scope.message = 'Hello World!'; - }]); - - -
-

Reload this page with open console, enter text and hit the log button...

- - - - - - -
-
-
- */ - -/** - * @ngdoc provider - * @name $logProvider - * @description - * Use the `$logProvider` to configure how the application logs messages - */ -function $LogProvider() { - var debug = true, - self = this; - - /** - * @ngdoc method - * @name $logProvider#debugEnabled - * @description - * @param {boolean=} flag enable or disable debug level messages - * @returns {*} current value if used as getter or itself (chaining) if used as setter - */ - this.debugEnabled = function(flag) { - if (isDefined(flag)) { - debug = flag; - return this; - } else { - return debug; - } - }; - - this.$get = ['$window', function($window) { - return { - /** - * @ngdoc method - * @name $log#log - * - * @description - * Write a log message - */ - log: consoleLog('log'), - - /** - * @ngdoc method - * @name $log#info - * - * @description - * Write an information message - */ - info: consoleLog('info'), - - /** - * @ngdoc method - * @name $log#warn - * - * @description - * Write a warning message - */ - warn: consoleLog('warn'), - - /** - * @ngdoc method - * @name $log#error - * - * @description - * Write an error message - */ - error: consoleLog('error'), - - /** - * @ngdoc method - * @name $log#debug - * - * @description - * Write a debug message - */ - debug: (function() { - var fn = consoleLog('debug'); - - return function() { - if (debug) { - fn.apply(self, arguments); - } - }; - }()) - }; - - function formatError(arg) { - if (arg instanceof Error) { - if (arg.stack) { - arg = (arg.message && arg.stack.indexOf(arg.message) === -1) - ? 'Error: ' + arg.message + '\n' + arg.stack - : arg.stack; - } else if (arg.sourceURL) { - arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; - } - } - return arg; - } - - function consoleLog(type) { - var console = $window.console || {}, - logFn = console[type] || console.log || noop, - hasApply = false; - - // Note: reading logFn.apply throws an error in IE11 in IE8 document mode. - // The reason behind this is that console.log has type "object" in IE8... - try { - hasApply = !!logFn.apply; - } catch (e) {} - - if (hasApply) { - return function() { - var args = []; - forEach(arguments, function(arg) { - args.push(formatError(arg)); - }); - return logFn.apply(console, args); - }; - } - - // we are IE which either doesn't have window.console => this is noop and we do nothing, - // or we are IE where console.log doesn't have apply so we log at least first 2 args - return function(arg1, arg2) { - logFn(arg1, arg2 == null ? '' : arg2); - }; - } - }]; -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Any commits to this file should be reviewed with security in mind. * - * Changes to this file can potentially create security vulnerabilities. * - * An approval from 2 Core members with history of modifying * - * this file is required. * - * * - * Does the change somehow allow for arbitrary javascript to be executed? * - * Or allows for someone to change the prototype of built-in objects? * - * Or gives undesired access to variables likes document or window? * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -var $parseMinErr = minErr('$parse'); - -// Sandboxing Angular Expressions -// ------------------------------ -// Angular expressions are generally considered safe because these expressions only have direct -// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by -// obtaining a reference to native JS functions such as the Function constructor. -// -// As an example, consider the following Angular expression: -// -// {}.toString.constructor('alert("evil JS code")') -// -// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits -// against the expression language, but not to prevent exploits that were enabled by exposing -// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good -// practice and therefore we are not even trying to protect against interaction with an object -// explicitly exposed in this way. -// -// In general, it is not possible to access a Window object from an angular expression unless a -// window or some DOM object that has a reference to window is published onto a Scope. -// Similarly we prevent invocations of function known to be dangerous, as well as assignments to -// native objects. -// -// See https://docs.angularjs.org/guide/security - - -function ensureSafeMemberName(name, fullExpression) { - if (name === "__defineGetter__" || name === "__defineSetter__" - || name === "__lookupGetter__" || name === "__lookupSetter__" - || name === "__proto__") { - throw $parseMinErr('isecfld', - 'Attempting to access a disallowed field in Angular expressions! ' - + 'Expression: {0}', fullExpression); - } - return name; -} - -function ensureSafeObject(obj, fullExpression) { - // nifty check if obj is Function that is fast and works across iframes and other contexts - if (obj) { - if (obj.constructor === obj) { - throw $parseMinErr('isecfn', - 'Referencing Function in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } else if (// isWindow(obj) - obj.window === obj) { - throw $parseMinErr('isecwindow', - 'Referencing the Window in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } else if (// isElement(obj) - obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) { - throw $parseMinErr('isecdom', - 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } else if (// block Object so that we can't get hold of dangerous Object.* methods - obj === Object) { - throw $parseMinErr('isecobj', - 'Referencing Object in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } - } - return obj; -} - -var CALL = Function.prototype.call; -var APPLY = Function.prototype.apply; -var BIND = Function.prototype.bind; - -function ensureSafeFunction(obj, fullExpression) { - if (obj) { - if (obj.constructor === obj) { - throw $parseMinErr('isecfn', - 'Referencing Function in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } else if (obj === CALL || obj === APPLY || obj === BIND) { - throw $parseMinErr('isecff', - 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } - } -} - -var OPERATORS = createMap(); -forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; }); -var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; - - -///////////////////////////////////////// - - -/** - * @constructor - */ -var Lexer = function(options) { - this.options = options; -}; - -Lexer.prototype = { - constructor: Lexer, - - lex: function(text) { - this.text = text; - this.index = 0; - this.tokens = []; - - while (this.index < this.text.length) { - var ch = this.text.charAt(this.index); - if (ch === '"' || ch === "'") { - this.readString(ch); - } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) { - this.readNumber(); - } else if (this.isIdent(ch)) { - this.readIdent(); - } else if (this.is(ch, '(){}[].,;:?')) { - this.tokens.push({index: this.index, text: ch}); - this.index++; - } else if (this.isWhitespace(ch)) { - this.index++; - } else { - var ch2 = ch + this.peek(); - var ch3 = ch2 + this.peek(2); - var op1 = OPERATORS[ch]; - var op2 = OPERATORS[ch2]; - var op3 = OPERATORS[ch3]; - if (op1 || op2 || op3) { - var token = op3 ? ch3 : (op2 ? ch2 : ch); - this.tokens.push({index: this.index, text: token, operator: true}); - this.index += token.length; - } else { - this.throwError('Unexpected next character ', this.index, this.index + 1); - } - } - } - return this.tokens; - }, - - is: function(ch, chars) { - return chars.indexOf(ch) !== -1; - }, - - peek: function(i) { - var num = i || 1; - return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false; - }, - - isNumber: function(ch) { - return ('0' <= ch && ch <= '9') && typeof ch === "string"; - }, - - isWhitespace: function(ch) { - // IE treats non-breaking space as \u00A0 - return (ch === ' ' || ch === '\r' || ch === '\t' || - ch === '\n' || ch === '\v' || ch === '\u00A0'); - }, - - isIdent: function(ch) { - return ('a' <= ch && ch <= 'z' || - 'A' <= ch && ch <= 'Z' || - '_' === ch || ch === '$'); - }, - - isExpOperator: function(ch) { - return (ch === '-' || ch === '+' || this.isNumber(ch)); - }, - - throwError: function(error, start, end) { - end = end || this.index; - var colStr = (isDefined(start) - ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']' - : ' ' + end); - throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].', - error, colStr, this.text); - }, - - readNumber: function() { - var number = ''; - var start = this.index; - while (this.index < this.text.length) { - var ch = lowercase(this.text.charAt(this.index)); - if (ch == '.' || this.isNumber(ch)) { - number += ch; - } else { - var peekCh = this.peek(); - if (ch == 'e' && this.isExpOperator(peekCh)) { - number += ch; - } else if (this.isExpOperator(ch) && - peekCh && this.isNumber(peekCh) && - number.charAt(number.length - 1) == 'e') { - number += ch; - } else if (this.isExpOperator(ch) && - (!peekCh || !this.isNumber(peekCh)) && - number.charAt(number.length - 1) == 'e') { - this.throwError('Invalid exponent'); - } else { - break; - } - } - this.index++; - } - this.tokens.push({ - index: start, - text: number, - constant: true, - value: Number(number) - }); - }, - - readIdent: function() { - var start = this.index; - while (this.index < this.text.length) { - var ch = this.text.charAt(this.index); - if (!(this.isIdent(ch) || this.isNumber(ch))) { - break; - } - this.index++; - } - this.tokens.push({ - index: start, - text: this.text.slice(start, this.index), - identifier: true - }); - }, - - readString: function(quote) { - var start = this.index; - this.index++; - var string = ''; - var rawString = quote; - var escape = false; - while (this.index < this.text.length) { - var ch = this.text.charAt(this.index); - rawString += ch; - if (escape) { - if (ch === 'u') { - var hex = this.text.substring(this.index + 1, this.index + 5); - if (!hex.match(/[\da-f]{4}/i)) { - this.throwError('Invalid unicode escape [\\u' + hex + ']'); - } - this.index += 4; - string += String.fromCharCode(parseInt(hex, 16)); - } else { - var rep = ESCAPE[ch]; - string = string + (rep || ch); - } - escape = false; - } else if (ch === '\\') { - escape = true; - } else if (ch === quote) { - this.index++; - this.tokens.push({ - index: start, - text: rawString, - constant: true, - value: string - }); - return; - } else { - string += ch; - } - this.index++; - } - this.throwError('Unterminated quote', start); - } -}; - -var AST = function(lexer, options) { - this.lexer = lexer; - this.options = options; -}; - -AST.Program = 'Program'; -AST.ExpressionStatement = 'ExpressionStatement'; -AST.AssignmentExpression = 'AssignmentExpression'; -AST.ConditionalExpression = 'ConditionalExpression'; -AST.LogicalExpression = 'LogicalExpression'; -AST.BinaryExpression = 'BinaryExpression'; -AST.UnaryExpression = 'UnaryExpression'; -AST.CallExpression = 'CallExpression'; -AST.MemberExpression = 'MemberExpression'; -AST.Identifier = 'Identifier'; -AST.Literal = 'Literal'; -AST.ArrayExpression = 'ArrayExpression'; -AST.Property = 'Property'; -AST.ObjectExpression = 'ObjectExpression'; -AST.ThisExpression = 'ThisExpression'; - -// Internal use only -AST.NGValueParameter = 'NGValueParameter'; - -AST.prototype = { - ast: function(text) { - this.text = text; - this.tokens = this.lexer.lex(text); - - var value = this.program(); - - if (this.tokens.length !== 0) { - this.throwError('is an unexpected token', this.tokens[0]); - } - - return value; - }, - - program: function() { - var body = []; - while (true) { - if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']')) - body.push(this.expressionStatement()); - if (!this.expect(';')) { - return { type: AST.Program, body: body}; - } - } - }, - - expressionStatement: function() { - return { type: AST.ExpressionStatement, expression: this.filterChain() }; - }, - - filterChain: function() { - var left = this.expression(); - var token; - while ((token = this.expect('|'))) { - left = this.filter(left); - } - return left; - }, - - expression: function() { - return this.assignment(); - }, - - assignment: function() { - var result = this.ternary(); - if (this.expect('=')) { - result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='}; - } - return result; - }, - - ternary: function() { - var test = this.logicalOR(); - var alternate; - var consequent; - if (this.expect('?')) { - alternate = this.expression(); - if (this.consume(':')) { - consequent = this.expression(); - return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent}; - } - } - return test; - }, - - logicalOR: function() { - var left = this.logicalAND(); - while (this.expect('||')) { - left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() }; - } - return left; - }, - - logicalAND: function() { - var left = this.equality(); - while (this.expect('&&')) { - left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()}; - } - return left; - }, - - equality: function() { - var left = this.relational(); - var token; - while ((token = this.expect('==','!=','===','!=='))) { - left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() }; - } - return left; - }, - - relational: function() { - var left = this.additive(); - var token; - while ((token = this.expect('<', '>', '<=', '>='))) { - left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() }; - } - return left; - }, - - additive: function() { - var left = this.multiplicative(); - var token; - while ((token = this.expect('+','-'))) { - left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() }; - } - return left; - }, - - multiplicative: function() { - var left = this.unary(); - var token; - while ((token = this.expect('*','/','%'))) { - left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() }; - } - return left; - }, - - unary: function() { - var token; - if ((token = this.expect('+', '-', '!'))) { - return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() }; - } else { - return this.primary(); - } - }, - - primary: function() { - var primary; - if (this.expect('(')) { - primary = this.filterChain(); - this.consume(')'); - } else if (this.expect('[')) { - primary = this.arrayDeclaration(); - } else if (this.expect('{')) { - primary = this.object(); - } else if (this.constants.hasOwnProperty(this.peek().text)) { - primary = copy(this.constants[this.consume().text]); - } else if (this.peek().identifier) { - primary = this.identifier(); - } else if (this.peek().constant) { - primary = this.constant(); - } else { - this.throwError('not a primary expression', this.peek()); - } - - var next; - while ((next = this.expect('(', '[', '.'))) { - if (next.text === '(') { - primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() }; - this.consume(')'); - } else if (next.text === '[') { - primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true }; - this.consume(']'); - } else if (next.text === '.') { - primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false }; - } else { - this.throwError('IMPOSSIBLE'); - } - } - return primary; - }, - - filter: function(baseExpression) { - var args = [baseExpression]; - var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true}; - - while (this.expect(':')) { - args.push(this.expression()); - } - - return result; - }, - - parseArguments: function() { - var args = []; - if (this.peekToken().text !== ')') { - do { - args.push(this.expression()); - } while (this.expect(',')); - } - return args; - }, - - identifier: function() { - var token = this.consume(); - if (!token.identifier) { - this.throwError('is not a valid identifier', token); - } - return { type: AST.Identifier, name: token.text }; - }, - - constant: function() { - // TODO check that it is a constant - return { type: AST.Literal, value: this.consume().value }; - }, - - arrayDeclaration: function() { - var elements = []; - if (this.peekToken().text !== ']') { - do { - if (this.peek(']')) { - // Support trailing commas per ES5.1. - break; - } - elements.push(this.expression()); - } while (this.expect(',')); - } - this.consume(']'); - - return { type: AST.ArrayExpression, elements: elements }; - }, - - object: function() { - var properties = [], property; - if (this.peekToken().text !== '}') { - do { - if (this.peek('}')) { - // Support trailing commas per ES5.1. - break; - } - property = {type: AST.Property, kind: 'init'}; - if (this.peek().constant) { - property.key = this.constant(); - } else if (this.peek().identifier) { - property.key = this.identifier(); - } else { - this.throwError("invalid key", this.peek()); - } - this.consume(':'); - property.value = this.expression(); - properties.push(property); - } while (this.expect(',')); - } - this.consume('}'); - - return {type: AST.ObjectExpression, properties: properties }; - }, - - throwError: function(msg, token) { - throw $parseMinErr('syntax', - 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].', - token.text, msg, (token.index + 1), this.text, this.text.substring(token.index)); - }, - - consume: function(e1) { - if (this.tokens.length === 0) { - throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); - } - - var token = this.expect(e1); - if (!token) { - this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); - } - return token; - }, - - peekToken: function() { - if (this.tokens.length === 0) { - throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); - } - return this.tokens[0]; - }, - - peek: function(e1, e2, e3, e4) { - return this.peekAhead(0, e1, e2, e3, e4); - }, - - peekAhead: function(i, e1, e2, e3, e4) { - if (this.tokens.length > i) { - var token = this.tokens[i]; - var t = token.text; - if (t === e1 || t === e2 || t === e3 || t === e4 || - (!e1 && !e2 && !e3 && !e4)) { - return token; - } - } - return false; - }, - - expect: function(e1, e2, e3, e4) { - var token = this.peek(e1, e2, e3, e4); - if (token) { - this.tokens.shift(); - return token; - } - return false; - }, - - - /* `undefined` is not a constant, it is an identifier, - * but using it as an identifier is not supported - */ - constants: { - 'true': { type: AST.Literal, value: true }, - 'false': { type: AST.Literal, value: false }, - 'null': { type: AST.Literal, value: null }, - 'undefined': {type: AST.Literal, value: undefined }, - 'this': {type: AST.ThisExpression } - } -}; - -function ifDefined(v, d) { - return typeof v !== 'undefined' ? v : d; -} - -function plusFn(l, r) { - if (typeof l === 'undefined') return r; - if (typeof r === 'undefined') return l; - return l + r; -} - -function isStateless($filter, filterName) { - var fn = $filter(filterName); - return !fn.$stateful; -} - -function findConstantAndWatchExpressions(ast, $filter) { - var allConstants; - var argsToWatch; - switch (ast.type) { - case AST.Program: - allConstants = true; - forEach(ast.body, function(expr) { - findConstantAndWatchExpressions(expr.expression, $filter); - allConstants = allConstants && expr.expression.constant; - }); - ast.constant = allConstants; - break; - case AST.Literal: - ast.constant = true; - ast.toWatch = []; - break; - case AST.UnaryExpression: - findConstantAndWatchExpressions(ast.argument, $filter); - ast.constant = ast.argument.constant; - ast.toWatch = ast.argument.toWatch; - break; - case AST.BinaryExpression: - findConstantAndWatchExpressions(ast.left, $filter); - findConstantAndWatchExpressions(ast.right, $filter); - ast.constant = ast.left.constant && ast.right.constant; - ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch); - break; - case AST.LogicalExpression: - findConstantAndWatchExpressions(ast.left, $filter); - findConstantAndWatchExpressions(ast.right, $filter); - ast.constant = ast.left.constant && ast.right.constant; - ast.toWatch = ast.constant ? [] : [ast]; - break; - case AST.ConditionalExpression: - findConstantAndWatchExpressions(ast.test, $filter); - findConstantAndWatchExpressions(ast.alternate, $filter); - findConstantAndWatchExpressions(ast.consequent, $filter); - ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant; - ast.toWatch = ast.constant ? [] : [ast]; - break; - case AST.Identifier: - ast.constant = false; - ast.toWatch = [ast]; - break; - case AST.MemberExpression: - findConstantAndWatchExpressions(ast.object, $filter); - if (ast.computed) { - findConstantAndWatchExpressions(ast.property, $filter); - } - ast.constant = ast.object.constant && (!ast.computed || ast.property.constant); - ast.toWatch = [ast]; - break; - case AST.CallExpression: - allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false; - argsToWatch = []; - forEach(ast.arguments, function(expr) { - findConstantAndWatchExpressions(expr, $filter); - allConstants = allConstants && expr.constant; - if (!expr.constant) { - argsToWatch.push.apply(argsToWatch, expr.toWatch); - } - }); - ast.constant = allConstants; - ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast]; - break; - case AST.AssignmentExpression: - findConstantAndWatchExpressions(ast.left, $filter); - findConstantAndWatchExpressions(ast.right, $filter); - ast.constant = ast.left.constant && ast.right.constant; - ast.toWatch = [ast]; - break; - case AST.ArrayExpression: - allConstants = true; - argsToWatch = []; - forEach(ast.elements, function(expr) { - findConstantAndWatchExpressions(expr, $filter); - allConstants = allConstants && expr.constant; - if (!expr.constant) { - argsToWatch.push.apply(argsToWatch, expr.toWatch); - } - }); - ast.constant = allConstants; - ast.toWatch = argsToWatch; - break; - case AST.ObjectExpression: - allConstants = true; - argsToWatch = []; - forEach(ast.properties, function(property) { - findConstantAndWatchExpressions(property.value, $filter); - allConstants = allConstants && property.value.constant; - if (!property.value.constant) { - argsToWatch.push.apply(argsToWatch, property.value.toWatch); - } - }); - ast.constant = allConstants; - ast.toWatch = argsToWatch; - break; - case AST.ThisExpression: - ast.constant = false; - ast.toWatch = []; - break; - } -} - -function getInputs(body) { - if (body.length != 1) return; - var lastExpression = body[0].expression; - var candidate = lastExpression.toWatch; - if (candidate.length !== 1) return candidate; - return candidate[0] !== lastExpression ? candidate : undefined; -} - -function isAssignable(ast) { - return ast.type === AST.Identifier || ast.type === AST.MemberExpression; -} - -function assignableAST(ast) { - if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) { - return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='}; - } -} - -function isLiteral(ast) { - return ast.body.length === 0 || - ast.body.length === 1 && ( - ast.body[0].expression.type === AST.Literal || - ast.body[0].expression.type === AST.ArrayExpression || - ast.body[0].expression.type === AST.ObjectExpression); -} - -function isConstant(ast) { - return ast.constant; -} - -function ASTCompiler(astBuilder, $filter) { - this.astBuilder = astBuilder; - this.$filter = $filter; -} - -ASTCompiler.prototype = { - compile: function(expression, expensiveChecks) { - var self = this; - var ast = this.astBuilder.ast(expression); - this.state = { - nextId: 0, - filters: {}, - expensiveChecks: expensiveChecks, - fn: {vars: [], body: [], own: {}}, - assign: {vars: [], body: [], own: {}}, - inputs: [] - }; - findConstantAndWatchExpressions(ast, self.$filter); - var extra = ''; - var assignable; - this.stage = 'assign'; - if ((assignable = assignableAST(ast))) { - this.state.computing = 'assign'; - var result = this.nextId(); - this.recurse(assignable, result); - extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l'); - } - var toWatch = getInputs(ast.body); - self.stage = 'inputs'; - forEach(toWatch, function(watch, key) { - var fnKey = 'fn' + key; - self.state[fnKey] = {vars: [], body: [], own: {}}; - self.state.computing = fnKey; - var intoId = self.nextId(); - self.recurse(watch, intoId); - self.return_(intoId); - self.state.inputs.push(fnKey); - watch.watchId = key; - }); - this.state.computing = 'fn'; - this.stage = 'main'; - this.recurse(ast); - var fnString = - // The build and minification steps remove the string "use strict" from the code, but this is done using a regex. - // This is a workaround for this until we do a better job at only removing the prefix only when we should. - '"' + this.USE + ' ' + this.STRICT + '";\n' + - this.filterPrefix() + - 'var fn=' + this.generateFunction('fn', 's,l,a,i') + - extra + - this.watchFns() + - 'return fn;'; - - /* jshint -W054 */ - var fn = (new Function('$filter', - 'ensureSafeMemberName', - 'ensureSafeObject', - 'ensureSafeFunction', - 'ifDefined', - 'plus', - 'text', - fnString))( - this.$filter, - ensureSafeMemberName, - ensureSafeObject, - ensureSafeFunction, - ifDefined, - plusFn, - expression); - /* jshint +W054 */ - this.state = this.stage = undefined; - fn.literal = isLiteral(ast); - fn.constant = isConstant(ast); - return fn; - }, - - USE: 'use', - - STRICT: 'strict', - - watchFns: function() { - var result = []; - var fns = this.state.inputs; - var self = this; - forEach(fns, function(name) { - result.push('var ' + name + '=' + self.generateFunction(name, 's')); - }); - if (fns.length) { - result.push('fn.inputs=[' + fns.join(',') + '];'); - } - return result.join(''); - }, - - generateFunction: function(name, params) { - return 'function(' + params + '){' + - this.varsPrefix(name) + - this.body(name) + - '};'; - }, - - filterPrefix: function() { - var parts = []; - var self = this; - forEach(this.state.filters, function(id, filter) { - parts.push(id + '=$filter(' + self.escape(filter) + ')'); - }); - if (parts.length) return 'var ' + parts.join(',') + ';'; - return ''; - }, - - varsPrefix: function(section) { - return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : ''; - }, - - body: function(section) { - return this.state[section].body.join(''); - }, - - recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) { - var left, right, self = this, args, expression; - recursionFn = recursionFn || noop; - if (!skipWatchIdCheck && isDefined(ast.watchId)) { - intoId = intoId || this.nextId(); - this.if_('i', - this.lazyAssign(intoId, this.computedMember('i', ast.watchId)), - this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true) - ); - return; - } - switch (ast.type) { - case AST.Program: - forEach(ast.body, function(expression, pos) { - self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; }); - if (pos !== ast.body.length - 1) { - self.current().body.push(right, ';'); - } else { - self.return_(right); - } - }); - break; - case AST.Literal: - expression = this.escape(ast.value); - this.assign(intoId, expression); - recursionFn(expression); - break; - case AST.UnaryExpression: - this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; }); - expression = ast.operator + '(' + this.ifDefined(right, 0) + ')'; - this.assign(intoId, expression); - recursionFn(expression); - break; - case AST.BinaryExpression: - this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; }); - this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; }); - if (ast.operator === '+') { - expression = this.plus(left, right); - } else if (ast.operator === '-') { - expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0); - } else { - expression = '(' + left + ')' + ast.operator + '(' + right + ')'; - } - this.assign(intoId, expression); - recursionFn(expression); - break; - case AST.LogicalExpression: - intoId = intoId || this.nextId(); - self.recurse(ast.left, intoId); - self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId)); - recursionFn(intoId); - break; - case AST.ConditionalExpression: - intoId = intoId || this.nextId(); - self.recurse(ast.test, intoId); - self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId)); - recursionFn(intoId); - break; - case AST.Identifier: - intoId = intoId || this.nextId(); - if (nameId) { - nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s'); - nameId.computed = false; - nameId.name = ast.name; - } - ensureSafeMemberName(ast.name); - self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)), - function() { - self.if_(self.stage === 'inputs' || 's', function() { - if (create && create !== 1) { - self.if_( - self.not(self.nonComputedMember('s', ast.name)), - self.lazyAssign(self.nonComputedMember('s', ast.name), '{}')); - } - self.assign(intoId, self.nonComputedMember('s', ast.name)); - }); - }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name)) - ); - if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) { - self.addEnsureSafeObject(intoId); - } - recursionFn(intoId); - break; - case AST.MemberExpression: - left = nameId && (nameId.context = this.nextId()) || this.nextId(); - intoId = intoId || this.nextId(); - self.recurse(ast.object, left, undefined, function() { - self.if_(self.notNull(left), function() { - if (ast.computed) { - right = self.nextId(); - self.recurse(ast.property, right); - self.addEnsureSafeMemberName(right); - if (create && create !== 1) { - self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}')); - } - expression = self.ensureSafeObject(self.computedMember(left, right)); - self.assign(intoId, expression); - if (nameId) { - nameId.computed = true; - nameId.name = right; - } - } else { - ensureSafeMemberName(ast.property.name); - if (create && create !== 1) { - self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}')); - } - expression = self.nonComputedMember(left, ast.property.name); - if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) { - expression = self.ensureSafeObject(expression); - } - self.assign(intoId, expression); - if (nameId) { - nameId.computed = false; - nameId.name = ast.property.name; - } - } - }, function() { - self.assign(intoId, 'undefined'); - }); - recursionFn(intoId); - }, !!create); - break; - case AST.CallExpression: - intoId = intoId || this.nextId(); - if (ast.filter) { - right = self.filter(ast.callee.name); - args = []; - forEach(ast.arguments, function(expr) { - var argument = self.nextId(); - self.recurse(expr, argument); - args.push(argument); - }); - expression = right + '(' + args.join(',') + ')'; - self.assign(intoId, expression); - recursionFn(intoId); - } else { - right = self.nextId(); - left = {}; - args = []; - self.recurse(ast.callee, right, left, function() { - self.if_(self.notNull(right), function() { - self.addEnsureSafeFunction(right); - forEach(ast.arguments, function(expr) { - self.recurse(expr, self.nextId(), undefined, function(argument) { - args.push(self.ensureSafeObject(argument)); - }); - }); - if (left.name) { - if (!self.state.expensiveChecks) { - self.addEnsureSafeObject(left.context); - } - expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')'; - } else { - expression = right + '(' + args.join(',') + ')'; - } - expression = self.ensureSafeObject(expression); - self.assign(intoId, expression); - }, function() { - self.assign(intoId, 'undefined'); - }); - recursionFn(intoId); - }); - } - break; - case AST.AssignmentExpression: - right = this.nextId(); - left = {}; - if (!isAssignable(ast.left)) { - throw $parseMinErr('lval', 'Trying to assing a value to a non l-value'); - } - this.recurse(ast.left, undefined, left, function() { - self.if_(self.notNull(left.context), function() { - self.recurse(ast.right, right); - self.addEnsureSafeObject(self.member(left.context, left.name, left.computed)); - expression = self.member(left.context, left.name, left.computed) + ast.operator + right; - self.assign(intoId, expression); - recursionFn(intoId || expression); - }); - }, 1); - break; - case AST.ArrayExpression: - args = []; - forEach(ast.elements, function(expr) { - self.recurse(expr, self.nextId(), undefined, function(argument) { - args.push(argument); - }); - }); - expression = '[' + args.join(',') + ']'; - this.assign(intoId, expression); - recursionFn(expression); - break; - case AST.ObjectExpression: - args = []; - forEach(ast.properties, function(property) { - self.recurse(property.value, self.nextId(), undefined, function(expr) { - args.push(self.escape( - property.key.type === AST.Identifier ? property.key.name : - ('' + property.key.value)) + - ':' + expr); - }); - }); - expression = '{' + args.join(',') + '}'; - this.assign(intoId, expression); - recursionFn(expression); - break; - case AST.ThisExpression: - this.assign(intoId, 's'); - recursionFn('s'); - break; - case AST.NGValueParameter: - this.assign(intoId, 'v'); - recursionFn('v'); - break; - } - }, - - getHasOwnProperty: function(element, property) { - var key = element + '.' + property; - var own = this.current().own; - if (!own.hasOwnProperty(key)) { - own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')'); - } - return own[key]; - }, - - assign: function(id, value) { - if (!id) return; - this.current().body.push(id, '=', value, ';'); - return id; - }, - - filter: function(filterName) { - if (!this.state.filters.hasOwnProperty(filterName)) { - this.state.filters[filterName] = this.nextId(true); - } - return this.state.filters[filterName]; - }, - - ifDefined: function(id, defaultValue) { - return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')'; - }, - - plus: function(left, right) { - return 'plus(' + left + ',' + right + ')'; - }, - - return_: function(id) { - this.current().body.push('return ', id, ';'); - }, - - if_: function(test, alternate, consequent) { - if (test === true) { - alternate(); - } else { - var body = this.current().body; - body.push('if(', test, '){'); - alternate(); - body.push('}'); - if (consequent) { - body.push('else{'); - consequent(); - body.push('}'); - } - } - }, - - not: function(expression) { - return '!(' + expression + ')'; - }, - - notNull: function(expression) { - return expression + '!=null'; - }, - - nonComputedMember: function(left, right) { - return left + '.' + right; - }, - - computedMember: function(left, right) { - return left + '[' + right + ']'; - }, - - member: function(left, right, computed) { - if (computed) return this.computedMember(left, right); - return this.nonComputedMember(left, right); - }, - - addEnsureSafeObject: function(item) { - this.current().body.push(this.ensureSafeObject(item), ';'); - }, - - addEnsureSafeMemberName: function(item) { - this.current().body.push(this.ensureSafeMemberName(item), ';'); - }, - - addEnsureSafeFunction: function(item) { - this.current().body.push(this.ensureSafeFunction(item), ';'); - }, - - ensureSafeObject: function(item) { - return 'ensureSafeObject(' + item + ',text)'; - }, - - ensureSafeMemberName: function(item) { - return 'ensureSafeMemberName(' + item + ',text)'; - }, - - ensureSafeFunction: function(item) { - return 'ensureSafeFunction(' + item + ',text)'; - }, - - lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) { - var self = this; - return function() { - self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck); - }; - }, - - lazyAssign: function(id, value) { - var self = this; - return function() { - self.assign(id, value); - }; - }, - - stringEscapeRegex: /[^ a-zA-Z0-9]/g, - - stringEscapeFn: function(c) { - return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4); - }, - - escape: function(value) { - if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'"; - if (isNumber(value)) return value.toString(); - if (value === true) return 'true'; - if (value === false) return 'false'; - if (value === null) return 'null'; - if (typeof value === 'undefined') return 'undefined'; - - throw $parseMinErr('esc', 'IMPOSSIBLE'); - }, - - nextId: function(skip, init) { - var id = 'v' + (this.state.nextId++); - if (!skip) { - this.current().vars.push(id + (init ? '=' + init : '')); - } - return id; - }, - - current: function() { - return this.state[this.state.computing]; - } -}; - - -function ASTInterpreter(astBuilder, $filter) { - this.astBuilder = astBuilder; - this.$filter = $filter; -} - -ASTInterpreter.prototype = { - compile: function(expression, expensiveChecks) { - var self = this; - var ast = this.astBuilder.ast(expression); - this.expression = expression; - this.expensiveChecks = expensiveChecks; - findConstantAndWatchExpressions(ast, self.$filter); - var assignable; - var assign; - if ((assignable = assignableAST(ast))) { - assign = this.recurse(assignable); - } - var toWatch = getInputs(ast.body); - var inputs; - if (toWatch) { - inputs = []; - forEach(toWatch, function(watch, key) { - var input = self.recurse(watch); - watch.input = input; - inputs.push(input); - watch.watchId = key; - }); - } - var expressions = []; - forEach(ast.body, function(expression) { - expressions.push(self.recurse(expression.expression)); - }); - var fn = ast.body.length === 0 ? function() {} : - ast.body.length === 1 ? expressions[0] : - function(scope, locals) { - var lastValue; - forEach(expressions, function(exp) { - lastValue = exp(scope, locals); - }); - return lastValue; - }; - if (assign) { - fn.assign = function(scope, value, locals) { - return assign(scope, locals, value); - }; - } - if (inputs) { - fn.inputs = inputs; - } - fn.literal = isLiteral(ast); - fn.constant = isConstant(ast); - return fn; - }, - - recurse: function(ast, context, create) { - var left, right, self = this, args, expression; - if (ast.input) { - return this.inputs(ast.input, ast.watchId); - } - switch (ast.type) { - case AST.Literal: - return this.value(ast.value, context); - case AST.UnaryExpression: - right = this.recurse(ast.argument); - return this['unary' + ast.operator](right, context); - case AST.BinaryExpression: - left = this.recurse(ast.left); - right = this.recurse(ast.right); - return this['binary' + ast.operator](left, right, context); - case AST.LogicalExpression: - left = this.recurse(ast.left); - right = this.recurse(ast.right); - return this['binary' + ast.operator](left, right, context); - case AST.ConditionalExpression: - return this['ternary?:']( - this.recurse(ast.test), - this.recurse(ast.alternate), - this.recurse(ast.consequent), - context - ); - case AST.Identifier: - ensureSafeMemberName(ast.name, self.expression); - return self.identifier(ast.name, - self.expensiveChecks || isPossiblyDangerousMemberName(ast.name), - context, create, self.expression); - case AST.MemberExpression: - left = this.recurse(ast.object, false, !!create); - if (!ast.computed) { - ensureSafeMemberName(ast.property.name, self.expression); - right = ast.property.name; - } - if (ast.computed) right = this.recurse(ast.property); - return ast.computed ? - this.computedMember(left, right, context, create, self.expression) : - this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression); - case AST.CallExpression: - args = []; - forEach(ast.arguments, function(expr) { - args.push(self.recurse(expr)); - }); - if (ast.filter) right = this.$filter(ast.callee.name); - if (!ast.filter) right = this.recurse(ast.callee, true); - return ast.filter ? - function(scope, locals, assign, inputs) { - var values = []; - for (var i = 0; i < args.length; ++i) { - values.push(args[i](scope, locals, assign, inputs)); - } - var value = right.apply(undefined, values, inputs); - return context ? {context: undefined, name: undefined, value: value} : value; - } : - function(scope, locals, assign, inputs) { - var rhs = right(scope, locals, assign, inputs); - var value; - if (rhs.value != null) { - ensureSafeObject(rhs.context, self.expression); - ensureSafeFunction(rhs.value, self.expression); - var values = []; - for (var i = 0; i < args.length; ++i) { - values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression)); - } - value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression); - } - return context ? {value: value} : value; - }; - case AST.AssignmentExpression: - left = this.recurse(ast.left, true, 1); - right = this.recurse(ast.right); - return function(scope, locals, assign, inputs) { - var lhs = left(scope, locals, assign, inputs); - var rhs = right(scope, locals, assign, inputs); - ensureSafeObject(lhs.value, self.expression); - lhs.context[lhs.name] = rhs; - return context ? {value: rhs} : rhs; - }; - case AST.ArrayExpression: - args = []; - forEach(ast.elements, function(expr) { - args.push(self.recurse(expr)); - }); - return function(scope, locals, assign, inputs) { - var value = []; - for (var i = 0; i < args.length; ++i) { - value.push(args[i](scope, locals, assign, inputs)); - } - return context ? {value: value} : value; - }; - case AST.ObjectExpression: - args = []; - forEach(ast.properties, function(property) { - args.push({key: property.key.type === AST.Identifier ? - property.key.name : - ('' + property.key.value), - value: self.recurse(property.value) - }); - }); - return function(scope, locals, assign, inputs) { - var value = {}; - for (var i = 0; i < args.length; ++i) { - value[args[i].key] = args[i].value(scope, locals, assign, inputs); - } - return context ? {value: value} : value; - }; - case AST.ThisExpression: - return function(scope) { - return context ? {value: scope} : scope; - }; - case AST.NGValueParameter: - return function(scope, locals, assign, inputs) { - return context ? {value: assign} : assign; - }; - } - }, - - 'unary+': function(argument, context) { - return function(scope, locals, assign, inputs) { - var arg = argument(scope, locals, assign, inputs); - if (isDefined(arg)) { - arg = +arg; - } else { - arg = 0; - } - return context ? {value: arg} : arg; - }; - }, - 'unary-': function(argument, context) { - return function(scope, locals, assign, inputs) { - var arg = argument(scope, locals, assign, inputs); - if (isDefined(arg)) { - arg = -arg; - } else { - arg = 0; - } - return context ? {value: arg} : arg; - }; - }, - 'unary!': function(argument, context) { - return function(scope, locals, assign, inputs) { - var arg = !argument(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary+': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var lhs = left(scope, locals, assign, inputs); - var rhs = right(scope, locals, assign, inputs); - var arg = plusFn(lhs, rhs); - return context ? {value: arg} : arg; - }; - }, - 'binary-': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var lhs = left(scope, locals, assign, inputs); - var rhs = right(scope, locals, assign, inputs); - var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0); - return context ? {value: arg} : arg; - }; - }, - 'binary*': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary/': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary%': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary===': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary!==': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary==': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary!=': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary<': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary>': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary<=': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary>=': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary&&': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary||': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'ternary?:': function(test, alternate, consequent, context) { - return function(scope, locals, assign, inputs) { - var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - value: function(value, context) { - return function() { return context ? {context: undefined, name: undefined, value: value} : value; }; - }, - identifier: function(name, expensiveChecks, context, create, expression) { - return function(scope, locals, assign, inputs) { - var base = locals && (name in locals) ? locals : scope; - if (create && create !== 1 && base && !(base[name])) { - base[name] = {}; - } - var value = base ? base[name] : undefined; - if (expensiveChecks) { - ensureSafeObject(value, expression); - } - if (context) { - return {context: base, name: name, value: value}; - } else { - return value; - } - }; - }, - computedMember: function(left, right, context, create, expression) { - return function(scope, locals, assign, inputs) { - var lhs = left(scope, locals, assign, inputs); - var rhs; - var value; - if (lhs != null) { - rhs = right(scope, locals, assign, inputs); - ensureSafeMemberName(rhs, expression); - if (create && create !== 1 && lhs && !(lhs[rhs])) { - lhs[rhs] = {}; - } - value = lhs[rhs]; - ensureSafeObject(value, expression); - } - if (context) { - return {context: lhs, name: rhs, value: value}; - } else { - return value; - } - }; - }, - nonComputedMember: function(left, right, expensiveChecks, context, create, expression) { - return function(scope, locals, assign, inputs) { - var lhs = left(scope, locals, assign, inputs); - if (create && create !== 1 && lhs && !(lhs[right])) { - lhs[right] = {}; - } - var value = lhs != null ? lhs[right] : undefined; - if (expensiveChecks || isPossiblyDangerousMemberName(right)) { - ensureSafeObject(value, expression); - } - if (context) { - return {context: lhs, name: right, value: value}; - } else { - return value; - } - }; - }, - inputs: function(input, watchId) { - return function(scope, value, locals, inputs) { - if (inputs) return inputs[watchId]; - return input(scope, value, locals); - }; - } -}; - -/** - * @constructor - */ -var Parser = function(lexer, $filter, options) { - this.lexer = lexer; - this.$filter = $filter; - this.options = options; - this.ast = new AST(this.lexer); - this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) : - new ASTCompiler(this.ast, $filter); -}; - -Parser.prototype = { - constructor: Parser, - - parse: function(text) { - return this.astCompiler.compile(text, this.options.expensiveChecks); - } -}; - -var getterFnCacheDefault = createMap(); -var getterFnCacheExpensive = createMap(); - -function isPossiblyDangerousMemberName(name) { - return name == 'constructor'; -} - -var objectValueOf = Object.prototype.valueOf; - -function getValueOf(value) { - return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value); -} - -/////////////////////////////////// - -/** - * @ngdoc service - * @name $parse - * @kind function - * - * @description - * - * Converts Angular {@link guide/expression expression} into a function. - * - * ```js - * var getter = $parse('user.name'); - * var setter = getter.assign; - * var context = {user:{name:'angular'}}; - * var locals = {user:{name:'local'}}; - * - * expect(getter(context)).toEqual('angular'); - * setter(context, 'newValue'); - * expect(context.user.name).toEqual('newValue'); - * expect(getter(context, locals)).toEqual('local'); - * ``` - * - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - * - * The returned function also has the following properties: - * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript - * literal. - * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript - * constant literals. - * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be - * set to a function to change its value on the given context. - * - */ - - -/** - * @ngdoc provider - * @name $parseProvider - * - * @description - * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} - * service. - */ -function $ParseProvider() { - var cacheDefault = createMap(); - var cacheExpensive = createMap(); - - this.$get = ['$filter', function($filter) { - var noUnsafeEval = csp().noUnsafeEval; - var $parseOptions = { - csp: noUnsafeEval, - expensiveChecks: false - }, - $parseOptionsExpensive = { - csp: noUnsafeEval, - expensiveChecks: true - }; - - return function $parse(exp, interceptorFn, expensiveChecks) { - var parsedExpression, oneTime, cacheKey; - - switch (typeof exp) { - case 'string': - exp = exp.trim(); - cacheKey = exp; - - var cache = (expensiveChecks ? cacheExpensive : cacheDefault); - parsedExpression = cache[cacheKey]; - - if (!parsedExpression) { - if (exp.charAt(0) === ':' && exp.charAt(1) === ':') { - oneTime = true; - exp = exp.substring(2); - } - var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions; - var lexer = new Lexer(parseOptions); - var parser = new Parser(lexer, $filter, parseOptions); - parsedExpression = parser.parse(exp); - if (parsedExpression.constant) { - parsedExpression.$$watchDelegate = constantWatchDelegate; - } else if (oneTime) { - parsedExpression.$$watchDelegate = parsedExpression.literal ? - oneTimeLiteralWatchDelegate : oneTimeWatchDelegate; - } else if (parsedExpression.inputs) { - parsedExpression.$$watchDelegate = inputsWatchDelegate; - } - cache[cacheKey] = parsedExpression; - } - return addInterceptor(parsedExpression, interceptorFn); - - case 'function': - return addInterceptor(exp, interceptorFn); - - default: - return noop; - } - }; - - function expressionInputDirtyCheck(newValue, oldValueOfValue) { - - if (newValue == null || oldValueOfValue == null) { // null/undefined - return newValue === oldValueOfValue; - } - - if (typeof newValue === 'object') { - - // attempt to convert the value to a primitive type - // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can - // be cheaply dirty-checked - newValue = getValueOf(newValue); - - if (typeof newValue === 'object') { - // objects/arrays are not supported - deep-watching them would be too expensive - return false; - } - - // fall-through to the primitive equality check - } - - //Primitive or NaN - return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue); - } - - function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) { - var inputExpressions = parsedExpression.inputs; - var lastResult; - - if (inputExpressions.length === 1) { - var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails - inputExpressions = inputExpressions[0]; - return scope.$watch(function expressionInputWatch(scope) { - var newInputValue = inputExpressions(scope); - if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) { - lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]); - oldInputValueOf = newInputValue && getValueOf(newInputValue); - } - return lastResult; - }, listener, objectEquality, prettyPrintExpression); - } - - var oldInputValueOfValues = []; - var oldInputValues = []; - for (var i = 0, ii = inputExpressions.length; i < ii; i++) { - oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails - oldInputValues[i] = null; - } - - return scope.$watch(function expressionInputsWatch(scope) { - var changed = false; - - for (var i = 0, ii = inputExpressions.length; i < ii; i++) { - var newInputValue = inputExpressions[i](scope); - if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) { - oldInputValues[i] = newInputValue; - oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue); - } - } - - if (changed) { - lastResult = parsedExpression(scope, undefined, undefined, oldInputValues); - } - - return lastResult; - }, listener, objectEquality, prettyPrintExpression); - } - - function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) { - var unwatch, lastValue; - return unwatch = scope.$watch(function oneTimeWatch(scope) { - return parsedExpression(scope); - }, function oneTimeListener(value, old, scope) { - lastValue = value; - if (isFunction(listener)) { - listener.apply(this, arguments); - } - if (isDefined(value)) { - scope.$$postDigest(function() { - if (isDefined(lastValue)) { - unwatch(); - } - }); - } - }, objectEquality); - } - - function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) { - var unwatch, lastValue; - return unwatch = scope.$watch(function oneTimeWatch(scope) { - return parsedExpression(scope); - }, function oneTimeListener(value, old, scope) { - lastValue = value; - if (isFunction(listener)) { - listener.call(this, value, old, scope); - } - if (isAllDefined(value)) { - scope.$$postDigest(function() { - if (isAllDefined(lastValue)) unwatch(); - }); - } - }, objectEquality); - - function isAllDefined(value) { - var allDefined = true; - forEach(value, function(val) { - if (!isDefined(val)) allDefined = false; - }); - return allDefined; - } - } - - function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) { - var unwatch; - return unwatch = scope.$watch(function constantWatch(scope) { - return parsedExpression(scope); - }, function constantListener(value, old, scope) { - if (isFunction(listener)) { - listener.apply(this, arguments); - } - unwatch(); - }, objectEquality); - } - - function addInterceptor(parsedExpression, interceptorFn) { - if (!interceptorFn) return parsedExpression; - var watchDelegate = parsedExpression.$$watchDelegate; - - var regularWatch = - watchDelegate !== oneTimeLiteralWatchDelegate && - watchDelegate !== oneTimeWatchDelegate; - - var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) { - var value = parsedExpression(scope, locals, assign, inputs); - return interceptorFn(value, scope, locals); - } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) { - var value = parsedExpression(scope, locals, assign, inputs); - var result = interceptorFn(value, scope, locals); - // we only return the interceptor's result if the - // initial value is defined (for bind-once) - return isDefined(value) ? result : value; - }; - - // Propagate $$watchDelegates other then inputsWatchDelegate - if (parsedExpression.$$watchDelegate && - parsedExpression.$$watchDelegate !== inputsWatchDelegate) { - fn.$$watchDelegate = parsedExpression.$$watchDelegate; - } else if (!interceptorFn.$stateful) { - // If there is an interceptor, but no watchDelegate then treat the interceptor like - // we treat filters - it is assumed to be a pure function unless flagged with $stateful - fn.$$watchDelegate = inputsWatchDelegate; - fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression]; - } - - return fn; - } - }]; -} - -/** - * @ngdoc service - * @name $q - * @requires $rootScope - * - * @description - * A service that helps you run functions asynchronously, and use their return values (or exceptions) - * when they are done processing. - * - * This is an implementation of promises/deferred objects inspired by - * [Kris Kowal's Q](https://github.com/kriskowal/q). - * - * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred - * implementations, and the other which resembles ES6 promises to some degree. - * - * # $q constructor - * - * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver` - * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony, - * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). - * - * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are - * available yet. - * - * It can be used like so: - * - * ```js - * // for the purpose of this example let's assume that variables `$q` and `okToGreet` - * // are available in the current lexical scope (they could have been injected or passed in). - * - * function asyncGreet(name) { - * // perform some asynchronous operation, resolve or reject the promise when appropriate. - * return $q(function(resolve, reject) { - * setTimeout(function() { - * if (okToGreet(name)) { - * resolve('Hello, ' + name + '!'); - * } else { - * reject('Greeting ' + name + ' is not allowed.'); - * } - * }, 1000); - * }); - * } - * - * var promise = asyncGreet('Robin Hood'); - * promise.then(function(greeting) { - * alert('Success: ' + greeting); - * }, function(reason) { - * alert('Failed: ' + reason); - * }); - * ``` - * - * Note: progress/notify callbacks are not currently supported via the ES6-style interface. - * - * However, the more traditional CommonJS-style usage is still available, and documented below. - * - * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an - * interface for interacting with an object that represents the result of an action that is - * performed asynchronously, and may or may not be finished at any given point in time. - * - * From the perspective of dealing with error handling, deferred and promise APIs are to - * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. - * - * ```js - * // for the purpose of this example let's assume that variables `$q` and `okToGreet` - * // are available in the current lexical scope (they could have been injected or passed in). - * - * function asyncGreet(name) { - * var deferred = $q.defer(); - * - * setTimeout(function() { - * deferred.notify('About to greet ' + name + '.'); - * - * if (okToGreet(name)) { - * deferred.resolve('Hello, ' + name + '!'); - * } else { - * deferred.reject('Greeting ' + name + ' is not allowed.'); - * } - * }, 1000); - * - * return deferred.promise; - * } - * - * var promise = asyncGreet('Robin Hood'); - * promise.then(function(greeting) { - * alert('Success: ' + greeting); - * }, function(reason) { - * alert('Failed: ' + reason); - * }, function(update) { - * alert('Got notification: ' + update); - * }); - * ``` - * - * At first it might not be obvious why this extra complexity is worth the trouble. The payoff - * comes in the way of guarantees that promise and deferred APIs make, see - * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md. - * - * Additionally the promise api allows for composition that is very hard to do with the - * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. - * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the - * section on serial or parallel joining of promises. - * - * # The Deferred API - * - * A new instance of deferred is constructed by calling `$q.defer()`. - * - * The purpose of the deferred object is to expose the associated Promise instance as well as APIs - * that can be used for signaling the successful or unsuccessful completion, as well as the status - * of the task. - * - * **Methods** - * - * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection - * constructed via `$q.reject`, the promise will be rejected instead. - * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to - * resolving it with a rejection constructed via `$q.reject`. - * - `notify(value)` - provides updates on the status of the promise's execution. This may be called - * multiple times before the promise is either resolved or rejected. - * - * **Properties** - * - * - promise – `{Promise}` – promise object associated with this deferred. - * - * - * # The Promise API - * - * A new promise instance is created when a deferred instance is created and can be retrieved by - * calling `deferred.promise`. - * - * The purpose of the promise object is to allow for interested parties to get access to the result - * of the deferred task when it completes. - * - * **Methods** - * - * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or - * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously - * as soon as the result is available. The callbacks are called with a single argument: the result - * or rejection reason. Additionally, the notify callback may be called zero or more times to - * provide a progress indication, before the promise is resolved or rejected. - * - * This method *returns a new promise* which is resolved or rejected via the return value of the - * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved - * with the value which is resolved in that promise using - * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)). - * It also notifies via the return value of the `notifyCallback` method. The promise cannot be - * resolved or rejected from the notifyCallback method. - * - * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` - * - * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise, - * but to do so without modifying the final value. This is useful to release resources or do some - * clean-up that needs to be done whether the promise was rejected or resolved. See the [full - * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for - * more information. - * - * # Chaining promises - * - * Because calling the `then` method of a promise returns a new derived promise, it is easily - * possible to create a chain of promises: - * - * ```js - * promiseB = promiseA.then(function(result) { - * return result + 1; - * }); - * - * // promiseB will be resolved immediately after promiseA is resolved and its value - * // will be the result of promiseA incremented by 1 - * ``` - * - * It is possible to create chains of any length and since a promise can be resolved with another - * promise (which will defer its resolution further), it is possible to pause/defer resolution of - * the promises at any point in the chain. This makes it possible to implement powerful APIs like - * $http's response interceptors. - * - * - * # Differences between Kris Kowal's Q and $q - * - * There are two main differences: - * - * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation - * mechanism in angular, which means faster propagation of resolution or rejection into your - * models and avoiding unnecessary browser repaints, which would result in flickering UI. - * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains - * all the important functionality needed for common async tasks. - * - * # Testing - * - * ```js - * it('should simulate promise', inject(function($q, $rootScope) { - * var deferred = $q.defer(); - * var promise = deferred.promise; - * var resolvedValue; - * - * promise.then(function(value) { resolvedValue = value; }); - * expect(resolvedValue).toBeUndefined(); - * - * // Simulate resolving of promise - * deferred.resolve(123); - * // Note that the 'then' function does not get called synchronously. - * // This is because we want the promise API to always be async, whether or not - * // it got called synchronously or asynchronously. - * expect(resolvedValue).toBeUndefined(); - * - * // Propagate promise resolution to 'then' functions using $apply(). - * $rootScope.$apply(); - * expect(resolvedValue).toEqual(123); - * })); - * ``` - * - * @param {function(function, function)} resolver Function which is responsible for resolving or - * rejecting the newly created promise. The first parameter is a function which resolves the - * promise, the second parameter is a function which rejects the promise. - * - * @returns {Promise} The newly created promise. - */ -function $QProvider() { - - this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { - return qFactory(function(callback) { - $rootScope.$evalAsync(callback); - }, $exceptionHandler); - }]; -} - -function $$QProvider() { - this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) { - return qFactory(function(callback) { - $browser.defer(callback); - }, $exceptionHandler); - }]; -} - -/** - * Constructs a promise manager. - * - * @param {function(function)} nextTick Function for executing functions in the next turn. - * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for - * debugging purposes. - * @returns {object} Promise manager. - */ -function qFactory(nextTick, exceptionHandler) { - var $qMinErr = minErr('$q', TypeError); - function callOnce(self, resolveFn, rejectFn) { - var called = false; - function wrap(fn) { - return function(value) { - if (called) return; - called = true; - fn.call(self, value); - }; - } - - return [wrap(resolveFn), wrap(rejectFn)]; - } - - /** - * @ngdoc method - * @name ng.$q#defer - * @kind function - * - * @description - * Creates a `Deferred` object which represents a task which will finish in the future. - * - * @returns {Deferred} Returns a new instance of deferred. - */ - var defer = function() { - return new Deferred(); - }; - - function Promise() { - this.$$state = { status: 0 }; - } - - extend(Promise.prototype, { - then: function(onFulfilled, onRejected, progressBack) { - if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) { - return this; - } - var result = new Deferred(); - - this.$$state.pending = this.$$state.pending || []; - this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]); - if (this.$$state.status > 0) scheduleProcessQueue(this.$$state); - - return result.promise; - }, - - "catch": function(callback) { - return this.then(null, callback); - }, - - "finally": function(callback, progressBack) { - return this.then(function(value) { - return handleCallback(value, true, callback); - }, function(error) { - return handleCallback(error, false, callback); - }, progressBack); - } - }); - - //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native - function simpleBind(context, fn) { - return function(value) { - fn.call(context, value); - }; - } - - function processQueue(state) { - var fn, deferred, pending; - - pending = state.pending; - state.processScheduled = false; - state.pending = undefined; - for (var i = 0, ii = pending.length; i < ii; ++i) { - deferred = pending[i][0]; - fn = pending[i][state.status]; - try { - if (isFunction(fn)) { - deferred.resolve(fn(state.value)); - } else if (state.status === 1) { - deferred.resolve(state.value); - } else { - deferred.reject(state.value); - } - } catch (e) { - deferred.reject(e); - exceptionHandler(e); - } - } - } - - function scheduleProcessQueue(state) { - if (state.processScheduled || !state.pending) return; - state.processScheduled = true; - nextTick(function() { processQueue(state); }); - } - - function Deferred() { - this.promise = new Promise(); - //Necessary to support unbound execution :/ - this.resolve = simpleBind(this, this.resolve); - this.reject = simpleBind(this, this.reject); - this.notify = simpleBind(this, this.notify); - } - - extend(Deferred.prototype, { - resolve: function(val) { - if (this.promise.$$state.status) return; - if (val === this.promise) { - this.$$reject($qMinErr( - 'qcycle', - "Expected promise to be resolved with value other than itself '{0}'", - val)); - } else { - this.$$resolve(val); - } - - }, - - $$resolve: function(val) { - var then, fns; - - fns = callOnce(this, this.$$resolve, this.$$reject); - try { - if ((isObject(val) || isFunction(val))) then = val && val.then; - if (isFunction(then)) { - this.promise.$$state.status = -1; - then.call(val, fns[0], fns[1], this.notify); - } else { - this.promise.$$state.value = val; - this.promise.$$state.status = 1; - scheduleProcessQueue(this.promise.$$state); - } - } catch (e) { - fns[1](e); - exceptionHandler(e); - } - }, - - reject: function(reason) { - if (this.promise.$$state.status) return; - this.$$reject(reason); - }, - - $$reject: function(reason) { - this.promise.$$state.value = reason; - this.promise.$$state.status = 2; - scheduleProcessQueue(this.promise.$$state); - }, - - notify: function(progress) { - var callbacks = this.promise.$$state.pending; - - if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) { - nextTick(function() { - var callback, result; - for (var i = 0, ii = callbacks.length; i < ii; i++) { - result = callbacks[i][0]; - callback = callbacks[i][3]; - try { - result.notify(isFunction(callback) ? callback(progress) : progress); - } catch (e) { - exceptionHandler(e); - } - } - }); - } - } - }); - - /** - * @ngdoc method - * @name $q#reject - * @kind function - * - * @description - * Creates a promise that is resolved as rejected with the specified `reason`. This api should be - * used to forward rejection in a chain of promises. If you are dealing with the last promise in - * a promise chain, you don't need to worry about it. - * - * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of - * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via - * a promise error callback and you want to forward the error to the promise derived from the - * current promise, you have to "rethrow" the error by returning a rejection constructed via - * `reject`. - * - * ```js - * promiseB = promiseA.then(function(result) { - * // success: do something and resolve promiseB - * // with the old or a new result - * return result; - * }, function(reason) { - * // error: handle the error if possible and - * // resolve promiseB with newPromiseOrValue, - * // otherwise forward the rejection to promiseB - * if (canHandle(reason)) { - * // handle the error and recover - * return newPromiseOrValue; - * } - * return $q.reject(reason); - * }); - * ``` - * - * @param {*} reason Constant, message, exception or an object representing the rejection reason. - * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. - */ - var reject = function(reason) { - var result = new Deferred(); - result.reject(reason); - return result.promise; - }; - - var makePromise = function makePromise(value, resolved) { - var result = new Deferred(); - if (resolved) { - result.resolve(value); - } else { - result.reject(value); - } - return result.promise; - }; - - var handleCallback = function handleCallback(value, isResolved, callback) { - var callbackOutput = null; - try { - if (isFunction(callback)) callbackOutput = callback(); - } catch (e) { - return makePromise(e, false); - } - if (isPromiseLike(callbackOutput)) { - return callbackOutput.then(function() { - return makePromise(value, isResolved); - }, function(error) { - return makePromise(error, false); - }); - } else { - return makePromise(value, isResolved); - } - }; - - /** - * @ngdoc method - * @name $q#when - * @kind function - * - * @description - * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. - * This is useful when you are dealing with an object that might or might not be a promise, or if - * the promise comes from a source that can't be trusted. - * - * @param {*} value Value or a promise - * @param {Function=} successCallback - * @param {Function=} errorCallback - * @param {Function=} progressCallback - * @returns {Promise} Returns a promise of the passed value or promise - */ - - - var when = function(value, callback, errback, progressBack) { - var result = new Deferred(); - result.resolve(value); - return result.promise.then(callback, errback, progressBack); - }; - - /** - * @ngdoc method - * @name $q#resolve - * @kind function - * - * @description - * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6. - * - * @param {*} value Value or a promise - * @param {Function=} successCallback - * @param {Function=} errorCallback - * @param {Function=} progressCallback - * @returns {Promise} Returns a promise of the passed value or promise - */ - var resolve = when; - - /** - * @ngdoc method - * @name $q#all - * @kind function - * - * @description - * Combines multiple promises into a single promise that is resolved when all of the input - * promises are resolved. - * - * @param {Array.|Object.} promises An array or hash of promises. - * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values, - * each value corresponding to the promise at the same index/key in the `promises` array/hash. - * If any of the promises is resolved with a rejection, this resulting promise will be rejected - * with the same rejection value. - */ - - function all(promises) { - var deferred = new Deferred(), - counter = 0, - results = isArray(promises) ? [] : {}; - - forEach(promises, function(promise, key) { - counter++; - when(promise).then(function(value) { - if (results.hasOwnProperty(key)) return; - results[key] = value; - if (!(--counter)) deferred.resolve(results); - }, function(reason) { - if (results.hasOwnProperty(key)) return; - deferred.reject(reason); - }); - }); - - if (counter === 0) { - deferred.resolve(results); - } - - return deferred.promise; - } - - var $Q = function Q(resolver) { - if (!isFunction(resolver)) { - throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver); - } - - if (!(this instanceof Q)) { - // More useful when $Q is the Promise itself. - return new Q(resolver); - } - - var deferred = new Deferred(); - - function resolveFn(value) { - deferred.resolve(value); - } - - function rejectFn(reason) { - deferred.reject(reason); - } - - resolver(resolveFn, rejectFn); - - return deferred.promise; - }; - - $Q.defer = defer; - $Q.reject = reject; - $Q.when = when; - $Q.resolve = resolve; - $Q.all = all; - - return $Q; -} - -function $$RAFProvider() { //rAF - this.$get = ['$window', '$timeout', function($window, $timeout) { - var requestAnimationFrame = $window.requestAnimationFrame || - $window.webkitRequestAnimationFrame; - - var cancelAnimationFrame = $window.cancelAnimationFrame || - $window.webkitCancelAnimationFrame || - $window.webkitCancelRequestAnimationFrame; - - var rafSupported = !!requestAnimationFrame; - var raf = rafSupported - ? function(fn) { - var id = requestAnimationFrame(fn); - return function() { - cancelAnimationFrame(id); - }; - } - : function(fn) { - var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666 - return function() { - $timeout.cancel(timer); - }; - }; - - raf.supported = rafSupported; - - return raf; - }]; -} - -/** - * DESIGN NOTES - * - * The design decisions behind the scope are heavily favored for speed and memory consumption. - * - * The typical use of scope is to watch the expressions, which most of the time return the same - * value as last time so we optimize the operation. - * - * Closures construction is expensive in terms of speed as well as memory: - * - No closures, instead use prototypical inheritance for API - * - Internal state needs to be stored on scope directly, which means that private state is - * exposed as $$____ properties - * - * Loop operations are optimized by using while(count--) { ... } - * - this means that in order to keep the same order of execution as addition we have to add - * items to the array at the beginning (unshift) instead of at the end (push) - * - * Child scopes are created and removed often - * - Using an array would be slow since inserts in middle are expensive so we use linked list - * - * There are few watches then a lot of observers. This is why you don't want the observer to be - * implemented in the same way as watch. Watch requires return of initialization function which - * are expensive to construct. - */ - - -/** - * @ngdoc provider - * @name $rootScopeProvider - * @description - * - * Provider for the $rootScope service. - */ - -/** - * @ngdoc method - * @name $rootScopeProvider#digestTtl - * @description - * - * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and - * assuming that the model is unstable. - * - * The current default is 10 iterations. - * - * In complex applications it's possible that the dependencies between `$watch`s will result in - * several digest iterations. However if an application needs more than the default 10 digest - * iterations for its model to stabilize then you should investigate what is causing the model to - * continuously change during the digest. - * - * Increasing the TTL could have performance implications, so you should not change it without - * proper justification. - * - * @param {number} limit The number of digest iterations. - */ - - -/** - * @ngdoc service - * @name $rootScope - * @description - * - * Every application has a single root {@link ng.$rootScope.Scope scope}. - * All other scopes are descendant scopes of the root scope. Scopes provide separation - * between the model and the view, via a mechanism for watching the model for changes. - * They also provide an event emission/broadcast and subscription facility. See the - * {@link guide/scope developer guide on scopes}. - */ -function $RootScopeProvider() { - var TTL = 10; - var $rootScopeMinErr = minErr('$rootScope'); - var lastDirtyWatch = null; - var applyAsyncId = null; - - this.digestTtl = function(value) { - if (arguments.length) { - TTL = value; - } - return TTL; - }; - - function createChildScopeClass(parent) { - function ChildScope() { - this.$$watchers = this.$$nextSibling = - this.$$childHead = this.$$childTail = null; - this.$$listeners = {}; - this.$$listenerCount = {}; - this.$$watchersCount = 0; - this.$id = nextUid(); - this.$$ChildScope = null; - } - ChildScope.prototype = parent; - return ChildScope; - } - - this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser', - function($injector, $exceptionHandler, $parse, $browser) { - - function destroyChildScope($event) { - $event.currentScope.$$destroyed = true; - } - - /** - * @ngdoc type - * @name $rootScope.Scope - * - * @description - * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the - * {@link auto.$injector $injector}. Child scopes are created using the - * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when - * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for - * an in-depth introduction and usage examples. - * - * - * # Inheritance - * A scope can inherit from a parent scope, as in this example: - * ```js - var parent = $rootScope; - var child = parent.$new(); - - parent.salutation = "Hello"; - expect(child.salutation).toEqual('Hello'); - - child.salutation = "Welcome"; - expect(child.salutation).toEqual('Welcome'); - expect(parent.salutation).toEqual('Hello'); - * ``` - * - * When interacting with `Scope` in tests, additional helper methods are available on the - * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional - * details. - * - * - * @param {Object.=} providers Map of service factory which need to be - * provided for the current scope. Defaults to {@link ng}. - * @param {Object.=} instanceCache Provides pre-instantiated services which should - * append/override services provided by `providers`. This is handy - * when unit-testing and having the need to override a default - * service. - * @returns {Object} Newly created scope. - * - */ - function Scope() { - this.$id = nextUid(); - this.$$phase = this.$parent = this.$$watchers = - this.$$nextSibling = this.$$prevSibling = - this.$$childHead = this.$$childTail = null; - this.$root = this; - this.$$destroyed = false; - this.$$listeners = {}; - this.$$listenerCount = {}; - this.$$watchersCount = 0; - this.$$isolateBindings = null; - } - - /** - * @ngdoc property - * @name $rootScope.Scope#$id - * - * @description - * Unique scope ID (monotonically increasing) useful for debugging. - */ - - /** - * @ngdoc property - * @name $rootScope.Scope#$parent - * - * @description - * Reference to the parent scope. - */ - - /** - * @ngdoc property - * @name $rootScope.Scope#$root - * - * @description - * Reference to the root scope. - */ - - Scope.prototype = { - constructor: Scope, - /** - * @ngdoc method - * @name $rootScope.Scope#$new - * @kind function - * - * @description - * Creates a new child {@link ng.$rootScope.Scope scope}. - * - * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event. - * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. - * - * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is - * desired for the scope and its child scopes to be permanently detached from the parent and - * thus stop participating in model change detection and listener notification by invoking. - * - * @param {boolean} isolate If true, then the scope does not prototypically inherit from the - * parent scope. The scope is isolated, as it can not see parent scope properties. - * When creating widgets, it is useful for the widget to not accidentally read parent - * state. - * - * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent` - * of the newly created scope. Defaults to `this` scope if not provided. - * This is used when creating a transclude scope to correctly place it - * in the scope hierarchy while maintaining the correct prototypical - * inheritance. - * - * @returns {Object} The newly created child scope. - * - */ - $new: function(isolate, parent) { - var child; - - parent = parent || this; - - if (isolate) { - child = new Scope(); - child.$root = this.$root; - } else { - // Only create a child scope class if somebody asks for one, - // but cache it to allow the VM to optimize lookups. - if (!this.$$ChildScope) { - this.$$ChildScope = createChildScopeClass(this); - } - child = new this.$$ChildScope(); - } - child.$parent = parent; - child.$$prevSibling = parent.$$childTail; - if (parent.$$childHead) { - parent.$$childTail.$$nextSibling = child; - parent.$$childTail = child; - } else { - parent.$$childHead = parent.$$childTail = child; - } - - // When the new scope is not isolated or we inherit from `this`, and - // the parent scope is destroyed, the property `$$destroyed` is inherited - // prototypically. In all other cases, this property needs to be set - // when the parent scope is destroyed. - // The listener needs to be added after the parent is set - if (isolate || parent != this) child.$on('$destroy', destroyChildScope); - - return child; - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$watch - * @kind function - * - * @description - * Registers a `listener` callback to be executed whenever the `watchExpression` changes. - * - * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest - * $digest()} and should return the value that will be watched. (Since - * {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the - * `watchExpression` can execute multiple times per - * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.) - * - The `listener` is called only when the value from the current `watchExpression` and the - * previous call to `watchExpression` are not equal (with the exception of the initial run, - * see below). Inequality is determined according to reference inequality, - * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators) - * via the `!==` Javascript operator, unless `objectEquality == true` - * (see next point) - * - When `objectEquality == true`, inequality of the `watchExpression` is determined - * according to the {@link angular.equals} function. To save the value of the object for - * later comparison, the {@link angular.copy} function is used. This therefore means that - * watching complex objects will have adverse memory and performance implications. - * - The watch `listener` may change the model, which may trigger other `listener`s to fire. - * This is achieved by rerunning the watchers until no changes are detected. The rerun - * iteration limit is 10 to prevent an infinite loop deadlock. - * - * - * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, - * you can register a `watchExpression` function with no `listener`. (Be prepared for - * multiple calls to your `watchExpression` because it will execute multiple times in a - * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.) - * - * After a watcher is registered with the scope, the `listener` fn is called asynchronously - * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the - * watcher. In rare cases, this is undesirable because the listener is called when the result - * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you - * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the - * listener was called due to initialization. - * - * - * - * # Example - * ```js - // let's assume that scope was dependency injected as the $rootScope - var scope = $rootScope; - scope.name = 'misko'; - scope.counter = 0; - - expect(scope.counter).toEqual(0); - scope.$watch('name', function(newValue, oldValue) { - scope.counter = scope.counter + 1; - }); - expect(scope.counter).toEqual(0); - - scope.$digest(); - // the listener is always called during the first $digest loop after it was registered - expect(scope.counter).toEqual(1); - - scope.$digest(); - // but now it will not be called unless the value changes - expect(scope.counter).toEqual(1); - - scope.name = 'adam'; - scope.$digest(); - expect(scope.counter).toEqual(2); - - - - // Using a function as a watchExpression - var food; - scope.foodCounter = 0; - expect(scope.foodCounter).toEqual(0); - scope.$watch( - // This function returns the value being watched. It is called for each turn of the $digest loop - function() { return food; }, - // This is the change listener, called when the value returned from the above function changes - function(newValue, oldValue) { - if ( newValue !== oldValue ) { - // Only increment the counter if the value changed - scope.foodCounter = scope.foodCounter + 1; - } - } - ); - // No digest has been run so the counter will be zero - expect(scope.foodCounter).toEqual(0); - - // Run the digest but since food has not changed count will still be zero - scope.$digest(); - expect(scope.foodCounter).toEqual(0); - - // Update food and run digest. Now the counter will increment - food = 'cheeseburger'; - scope.$digest(); - expect(scope.foodCounter).toEqual(1); - - * ``` - * - * - * - * @param {(function()|string)} watchExpression Expression that is evaluated on each - * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers - * a call to the `listener`. - * - * - `string`: Evaluated as {@link guide/expression expression} - * - `function(scope)`: called with current `scope` as a parameter. - * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value - * of `watchExpression` changes. - * - * - `newVal` contains the current value of the `watchExpression` - * - `oldVal` contains the previous value of the `watchExpression` - * - `scope` refers to the current scope - * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of - * comparing for reference equality. - * @returns {function()} Returns a deregistration function for this listener. - */ - $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) { - var get = $parse(watchExp); - - if (get.$$watchDelegate) { - return get.$$watchDelegate(this, listener, objectEquality, get, watchExp); - } - var scope = this, - array = scope.$$watchers, - watcher = { - fn: listener, - last: initWatchVal, - get: get, - exp: prettyPrintExpression || watchExp, - eq: !!objectEquality - }; - - lastDirtyWatch = null; - - if (!isFunction(listener)) { - watcher.fn = noop; - } - - if (!array) { - array = scope.$$watchers = []; - } - // we use unshift since we use a while loop in $digest for speed. - // the while loop reads in reverse order. - array.unshift(watcher); - incrementWatchersCount(this, 1); - - return function deregisterWatch() { - if (arrayRemove(array, watcher) >= 0) { - incrementWatchersCount(scope, -1); - } - lastDirtyWatch = null; - }; - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$watchGroup - * @kind function - * - * @description - * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`. - * If any one expression in the collection changes the `listener` is executed. - * - * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every - * call to $digest() to see if any items changes. - * - The `listener` is called whenever any expression in the `watchExpressions` array changes. - * - * @param {Array.} watchExpressions Array of expressions that will be individually - * watched using {@link ng.$rootScope.Scope#$watch $watch()} - * - * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any - * expression in `watchExpressions` changes - * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching - * those of `watchExpression` - * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching - * those of `watchExpression` - * The `scope` refers to the current scope. - * @returns {function()} Returns a de-registration function for all listeners. - */ - $watchGroup: function(watchExpressions, listener) { - var oldValues = new Array(watchExpressions.length); - var newValues = new Array(watchExpressions.length); - var deregisterFns = []; - var self = this; - var changeReactionScheduled = false; - var firstRun = true; - - if (!watchExpressions.length) { - // No expressions means we call the listener ASAP - var shouldCall = true; - self.$evalAsync(function() { - if (shouldCall) listener(newValues, newValues, self); - }); - return function deregisterWatchGroup() { - shouldCall = false; - }; - } - - if (watchExpressions.length === 1) { - // Special case size of one - return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) { - newValues[0] = value; - oldValues[0] = oldValue; - listener(newValues, (value === oldValue) ? newValues : oldValues, scope); - }); - } - - forEach(watchExpressions, function(expr, i) { - var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) { - newValues[i] = value; - oldValues[i] = oldValue; - if (!changeReactionScheduled) { - changeReactionScheduled = true; - self.$evalAsync(watchGroupAction); - } - }); - deregisterFns.push(unwatchFn); - }); - - function watchGroupAction() { - changeReactionScheduled = false; - - if (firstRun) { - firstRun = false; - listener(newValues, newValues, self); - } else { - listener(newValues, oldValues, self); - } - } - - return function deregisterWatchGroup() { - while (deregisterFns.length) { - deregisterFns.shift()(); - } - }; - }, - - - /** - * @ngdoc method - * @name $rootScope.Scope#$watchCollection - * @kind function - * - * @description - * Shallow watches the properties of an object and fires whenever any of the properties change - * (for arrays, this implies watching the array items; for object maps, this implies watching - * the properties). If a change is detected, the `listener` callback is fired. - * - * - The `obj` collection is observed via standard $watch operation and is examined on every - * call to $digest() to see if any items have been added, removed, or moved. - * - The `listener` is called whenever anything within the `obj` has changed. Examples include - * adding, removing, and moving items belonging to an object or array. - * - * - * # Example - * ```js - $scope.names = ['igor', 'matias', 'misko', 'james']; - $scope.dataCount = 4; - - $scope.$watchCollection('names', function(newNames, oldNames) { - $scope.dataCount = newNames.length; - }); - - expect($scope.dataCount).toEqual(4); - $scope.$digest(); - - //still at 4 ... no changes - expect($scope.dataCount).toEqual(4); - - $scope.names.pop(); - $scope.$digest(); - - //now there's been a change - expect($scope.dataCount).toEqual(3); - * ``` - * - * - * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The - * expression value should evaluate to an object or an array which is observed on each - * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the - * collection will trigger a call to the `listener`. - * - * @param {function(newCollection, oldCollection, scope)} listener a callback function called - * when a change is detected. - * - The `newCollection` object is the newly modified data obtained from the `obj` expression - * - The `oldCollection` object is a copy of the former collection data. - * Due to performance considerations, the`oldCollection` value is computed only if the - * `listener` function declares two or more arguments. - * - The `scope` argument refers to the current scope. - * - * @returns {function()} Returns a de-registration function for this listener. When the - * de-registration function is executed, the internal watch operation is terminated. - */ - $watchCollection: function(obj, listener) { - $watchCollectionInterceptor.$stateful = true; - - var self = this; - // the current value, updated on each dirty-check run - var newValue; - // a shallow copy of the newValue from the last dirty-check run, - // updated to match newValue during dirty-check run - var oldValue; - // a shallow copy of the newValue from when the last change happened - var veryOldValue; - // only track veryOldValue if the listener is asking for it - var trackVeryOldValue = (listener.length > 1); - var changeDetected = 0; - var changeDetector = $parse(obj, $watchCollectionInterceptor); - var internalArray = []; - var internalObject = {}; - var initRun = true; - var oldLength = 0; - - function $watchCollectionInterceptor(_value) { - newValue = _value; - var newLength, key, bothNaN, newItem, oldItem; - - // If the new value is undefined, then return undefined as the watch may be a one-time watch - if (isUndefined(newValue)) return; - - if (!isObject(newValue)) { // if primitive - if (oldValue !== newValue) { - oldValue = newValue; - changeDetected++; - } - } else if (isArrayLike(newValue)) { - if (oldValue !== internalArray) { - // we are transitioning from something which was not an array into array. - oldValue = internalArray; - oldLength = oldValue.length = 0; - changeDetected++; - } - - newLength = newValue.length; - - if (oldLength !== newLength) { - // if lengths do not match we need to trigger change notification - changeDetected++; - oldValue.length = oldLength = newLength; - } - // copy the items to oldValue and look for changes. - for (var i = 0; i < newLength; i++) { - oldItem = oldValue[i]; - newItem = newValue[i]; - - bothNaN = (oldItem !== oldItem) && (newItem !== newItem); - if (!bothNaN && (oldItem !== newItem)) { - changeDetected++; - oldValue[i] = newItem; - } - } - } else { - if (oldValue !== internalObject) { - // we are transitioning from something which was not an object into object. - oldValue = internalObject = {}; - oldLength = 0; - changeDetected++; - } - // copy the items to oldValue and look for changes. - newLength = 0; - for (key in newValue) { - if (newValue.hasOwnProperty(key)) { - newLength++; - newItem = newValue[key]; - oldItem = oldValue[key]; - - if (key in oldValue) { - bothNaN = (oldItem !== oldItem) && (newItem !== newItem); - if (!bothNaN && (oldItem !== newItem)) { - changeDetected++; - oldValue[key] = newItem; - } - } else { - oldLength++; - oldValue[key] = newItem; - changeDetected++; - } - } - } - if (oldLength > newLength) { - // we used to have more keys, need to find them and destroy them. - changeDetected++; - for (key in oldValue) { - if (!newValue.hasOwnProperty(key)) { - oldLength--; - delete oldValue[key]; - } - } - } - } - return changeDetected; - } - - function $watchCollectionAction() { - if (initRun) { - initRun = false; - listener(newValue, newValue, self); - } else { - listener(newValue, veryOldValue, self); - } - - // make a copy for the next time a collection is changed - if (trackVeryOldValue) { - if (!isObject(newValue)) { - //primitive - veryOldValue = newValue; - } else if (isArrayLike(newValue)) { - veryOldValue = new Array(newValue.length); - for (var i = 0; i < newValue.length; i++) { - veryOldValue[i] = newValue[i]; - } - } else { // if object - veryOldValue = {}; - for (var key in newValue) { - if (hasOwnProperty.call(newValue, key)) { - veryOldValue[key] = newValue[key]; - } - } - } - } - } - - return this.$watch(changeDetector, $watchCollectionAction); - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$digest - * @kind function - * - * @description - * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and - * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change - * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} - * until no more listeners are firing. This means that it is possible to get into an infinite - * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of - * iterations exceeds 10. - * - * Usually, you don't call `$digest()` directly in - * {@link ng.directive:ngController controllers} or in - * {@link ng.$compileProvider#directive directives}. - * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within - * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`. - * - * If you want to be notified whenever `$digest()` is called, - * you can register a `watchExpression` function with - * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`. - * - * In unit tests, you may need to call `$digest()` to simulate the scope life cycle. - * - * # Example - * ```js - var scope = ...; - scope.name = 'misko'; - scope.counter = 0; - - expect(scope.counter).toEqual(0); - scope.$watch('name', function(newValue, oldValue) { - scope.counter = scope.counter + 1; - }); - expect(scope.counter).toEqual(0); - - scope.$digest(); - // the listener is always called during the first $digest loop after it was registered - expect(scope.counter).toEqual(1); - - scope.$digest(); - // but now it will not be called unless the value changes - expect(scope.counter).toEqual(1); - - scope.name = 'adam'; - scope.$digest(); - expect(scope.counter).toEqual(2); - * ``` - * - */ - $digest: function() { - var watch, value, last, - watchers, - length, - dirty, ttl = TTL, - next, current, target = this, - watchLog = [], - logIdx, logMsg, asyncTask; - - beginPhase('$digest'); - // Check for changes to browser url that happened in sync before the call to $digest - $browser.$$checkUrlChange(); - - if (this === $rootScope && applyAsyncId !== null) { - // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then - // cancel the scheduled $apply and flush the queue of expressions to be evaluated. - $browser.defer.cancel(applyAsyncId); - flushApplyAsync(); - } - - lastDirtyWatch = null; - - do { // "while dirty" loop - dirty = false; - current = target; - - while (asyncQueue.length) { - try { - asyncTask = asyncQueue.shift(); - asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals); - } catch (e) { - $exceptionHandler(e); - } - lastDirtyWatch = null; - } - - traverseScopesLoop: - do { // "traverse the scopes" loop - if ((watchers = current.$$watchers)) { - // process our watches - length = watchers.length; - while (length--) { - try { - watch = watchers[length]; - // Most common watches are on primitives, in which case we can short - // circuit it with === operator, only when === fails do we use .equals - if (watch) { - if ((value = watch.get(current)) !== (last = watch.last) && - !(watch.eq - ? equals(value, last) - : (typeof value === 'number' && typeof last === 'number' - && isNaN(value) && isNaN(last)))) { - dirty = true; - lastDirtyWatch = watch; - watch.last = watch.eq ? copy(value, null) : value; - watch.fn(value, ((last === initWatchVal) ? value : last), current); - if (ttl < 5) { - logIdx = 4 - ttl; - if (!watchLog[logIdx]) watchLog[logIdx] = []; - watchLog[logIdx].push({ - msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp, - newVal: value, - oldVal: last - }); - } - } else if (watch === lastDirtyWatch) { - // If the most recently dirty watcher is now clean, short circuit since the remaining watchers - // have already been tested. - dirty = false; - break traverseScopesLoop; - } - } - } catch (e) { - $exceptionHandler(e); - } - } - } - - // Insanity Warning: scope depth-first traversal - // yes, this code is a bit crazy, but it works and we have tests to prove it! - // this piece should be kept in sync with the traversal in $broadcast - if (!(next = ((current.$$watchersCount && current.$$childHead) || - (current !== target && current.$$nextSibling)))) { - while (current !== target && !(next = current.$$nextSibling)) { - current = current.$parent; - } - } - } while ((current = next)); - - // `break traverseScopesLoop;` takes us to here - - if ((dirty || asyncQueue.length) && !(ttl--)) { - clearPhase(); - throw $rootScopeMinErr('infdig', - '{0} $digest() iterations reached. Aborting!\n' + - 'Watchers fired in the last 5 iterations: {1}', - TTL, watchLog); - } - - } while (dirty || asyncQueue.length); - - clearPhase(); - - while (postDigestQueue.length) { - try { - postDigestQueue.shift()(); - } catch (e) { - $exceptionHandler(e); - } - } - }, - - - /** - * @ngdoc event - * @name $rootScope.Scope#$destroy - * @eventType broadcast on scope being destroyed - * - * @description - * Broadcasted when a scope and its children are being destroyed. - * - * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to - * clean up DOM bindings before an element is removed from the DOM. - */ - - /** - * @ngdoc method - * @name $rootScope.Scope#$destroy - * @kind function - * - * @description - * Removes the current scope (and all of its children) from the parent scope. Removal implies - * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer - * propagate to the current scope and its children. Removal also implies that the current - * scope is eligible for garbage collection. - * - * The `$destroy()` is usually used by directives such as - * {@link ng.directive:ngRepeat ngRepeat} for managing the - * unrolling of the loop. - * - * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope. - * Application code can register a `$destroy` event handler that will give it a chance to - * perform any necessary cleanup. - * - * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to - * clean up DOM bindings before an element is removed from the DOM. - */ - $destroy: function() { - // We can't destroy a scope that has been already destroyed. - if (this.$$destroyed) return; - var parent = this.$parent; - - this.$broadcast('$destroy'); - this.$$destroyed = true; - - if (this === $rootScope) { - //Remove handlers attached to window when $rootScope is removed - $browser.$$applicationDestroyed(); - } - - incrementWatchersCount(this, -this.$$watchersCount); - for (var eventName in this.$$listenerCount) { - decrementListenerCount(this, this.$$listenerCount[eventName], eventName); - } - - // sever all the references to parent scopes (after this cleanup, the current scope should - // not be retained by any of our references and should be eligible for garbage collection) - if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; - if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; - if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; - if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; - - // Disable listeners, watchers and apply/digest methods - this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop; - this.$on = this.$watch = this.$watchGroup = function() { return noop; }; - this.$$listeners = {}; - - // All of the code below is bogus code that works around V8's memory leak via optimized code - // and inline caches. - // - // see: - // - https://code.google.com/p/v8/issues/detail?id=2073#c26 - // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909 - // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 - - this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = - this.$$childTail = this.$root = this.$$watchers = null; - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$eval - * @kind function - * - * @description - * Executes the `expression` on the current scope and returns the result. Any exceptions in - * the expression are propagated (uncaught). This is useful when evaluating Angular - * expressions. - * - * # Example - * ```js - var scope = ng.$rootScope.Scope(); - scope.a = 1; - scope.b = 2; - - expect(scope.$eval('a+b')).toEqual(3); - expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); - * ``` - * - * @param {(string|function())=} expression An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with the current `scope` parameter. - * - * @param {(object)=} locals Local variables object, useful for overriding values in scope. - * @returns {*} The result of evaluating the expression. - */ - $eval: function(expr, locals) { - return $parse(expr)(this, locals); - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$evalAsync - * @kind function - * - * @description - * Executes the expression on the current scope at a later point in time. - * - * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only - * that: - * - * - it will execute after the function that scheduled the evaluation (preferably before DOM - * rendering). - * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after - * `expression` execution. - * - * Any exceptions from the execution of the expression are forwarded to the - * {@link ng.$exceptionHandler $exceptionHandler} service. - * - * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle - * will be scheduled. However, it is encouraged to always call code that changes the model - * from within an `$apply` call. That includes code evaluated via `$evalAsync`. - * - * @param {(string|function())=} expression An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with the current `scope` parameter. - * - * @param {(object)=} locals Local variables object, useful for overriding values in scope. - */ - $evalAsync: function(expr, locals) { - // if we are outside of an $digest loop and this is the first time we are scheduling async - // task also schedule async auto-flush - if (!$rootScope.$$phase && !asyncQueue.length) { - $browser.defer(function() { - if (asyncQueue.length) { - $rootScope.$digest(); - } - }); - } - - asyncQueue.push({scope: this, expression: expr, locals: locals}); - }, - - $$postDigest: function(fn) { - postDigestQueue.push(fn); - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$apply - * @kind function - * - * @description - * `$apply()` is used to execute an expression in angular from outside of the angular - * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). - * Because we are calling into the angular framework we need to perform proper scope life - * cycle of {@link ng.$exceptionHandler exception handling}, - * {@link ng.$rootScope.Scope#$digest executing watches}. - * - * ## Life cycle - * - * # Pseudo-Code of `$apply()` - * ```js - function $apply(expr) { - try { - return $eval(expr); - } catch (e) { - $exceptionHandler(e); - } finally { - $root.$digest(); - } - } - * ``` - * - * - * Scope's `$apply()` method transitions through the following stages: - * - * 1. The {@link guide/expression expression} is executed using the - * {@link ng.$rootScope.Scope#$eval $eval()} method. - * 2. Any exceptions from the execution of the expression are forwarded to the - * {@link ng.$exceptionHandler $exceptionHandler} service. - * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the - * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. - * - * - * @param {(string|function())=} exp An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with current `scope` parameter. - * - * @returns {*} The result of evaluating the expression. - */ - $apply: function(expr) { - try { - beginPhase('$apply'); - try { - return this.$eval(expr); - } finally { - clearPhase(); - } - } catch (e) { - $exceptionHandler(e); - } finally { - try { - $rootScope.$digest(); - } catch (e) { - $exceptionHandler(e); - throw e; - } - } - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$applyAsync - * @kind function - * - * @description - * Schedule the invocation of $apply to occur at a later time. The actual time difference - * varies across browsers, but is typically around ~10 milliseconds. - * - * This can be used to queue up multiple expressions which need to be evaluated in the same - * digest. - * - * @param {(string|function())=} exp An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with current `scope` parameter. - */ - $applyAsync: function(expr) { - var scope = this; - expr && applyAsyncQueue.push($applyAsyncExpression); - scheduleApplyAsync(); - - function $applyAsyncExpression() { - scope.$eval(expr); - } - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$on - * @kind function - * - * @description - * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for - * discussion of event life cycle. - * - * The event listener function format is: `function(event, args...)`. The `event` object - * passed into the listener has the following attributes: - * - * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or - * `$broadcast`-ed. - * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the - * event propagates through the scope hierarchy, this property is set to null. - * - `name` - `{string}`: name of the event. - * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel - * further event propagation (available only for events that were `$emit`-ed). - * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag - * to true. - * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. - * - * @param {string} name Event name to listen on. - * @param {function(event, ...args)} listener Function to call when the event is emitted. - * @returns {function()} Returns a deregistration function for this listener. - */ - $on: function(name, listener) { - var namedListeners = this.$$listeners[name]; - if (!namedListeners) { - this.$$listeners[name] = namedListeners = []; - } - namedListeners.push(listener); - - var current = this; - do { - if (!current.$$listenerCount[name]) { - current.$$listenerCount[name] = 0; - } - current.$$listenerCount[name]++; - } while ((current = current.$parent)); - - var self = this; - return function() { - var indexOfListener = namedListeners.indexOf(listener); - if (indexOfListener !== -1) { - namedListeners[indexOfListener] = null; - decrementListenerCount(self, 1, name); - } - }; - }, - - - /** - * @ngdoc method - * @name $rootScope.Scope#$emit - * @kind function - * - * @description - * Dispatches an event `name` upwards through the scope hierarchy notifying the - * registered {@link ng.$rootScope.Scope#$on} listeners. - * - * The event life cycle starts at the scope on which `$emit` was called. All - * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get - * notified. Afterwards, the event traverses upwards toward the root scope and calls all - * registered listeners along the way. The event will stop propagating if one of the listeners - * cancels it. - * - * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed - * onto the {@link ng.$exceptionHandler $exceptionHandler} service. - * - * @param {string} name Event name to emit. - * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. - * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}). - */ - $emit: function(name, args) { - var empty = [], - namedListeners, - scope = this, - stopPropagation = false, - event = { - name: name, - targetScope: scope, - stopPropagation: function() {stopPropagation = true;}, - preventDefault: function() { - event.defaultPrevented = true; - }, - defaultPrevented: false - }, - listenerArgs = concat([event], arguments, 1), - i, length; - - do { - namedListeners = scope.$$listeners[name] || empty; - event.currentScope = scope; - for (i = 0, length = namedListeners.length; i < length; i++) { - - // if listeners were deregistered, defragment the array - if (!namedListeners[i]) { - namedListeners.splice(i, 1); - i--; - length--; - continue; - } - try { - //allow all listeners attached to the current scope to run - namedListeners[i].apply(null, listenerArgs); - } catch (e) { - $exceptionHandler(e); - } - } - //if any listener on the current scope stops propagation, prevent bubbling - if (stopPropagation) { - event.currentScope = null; - return event; - } - //traverse upwards - scope = scope.$parent; - } while (scope); - - event.currentScope = null; - - return event; - }, - - - /** - * @ngdoc method - * @name $rootScope.Scope#$broadcast - * @kind function - * - * @description - * Dispatches an event `name` downwards to all child scopes (and their children) notifying the - * registered {@link ng.$rootScope.Scope#$on} listeners. - * - * The event life cycle starts at the scope on which `$broadcast` was called. All - * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get - * notified. Afterwards, the event propagates to all direct and indirect scopes of the current - * scope and calls all registered listeners along the way. The event cannot be canceled. - * - * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed - * onto the {@link ng.$exceptionHandler $exceptionHandler} service. - * - * @param {string} name Event name to broadcast. - * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. - * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} - */ - $broadcast: function(name, args) { - var target = this, - current = target, - next = target, - event = { - name: name, - targetScope: target, - preventDefault: function() { - event.defaultPrevented = true; - }, - defaultPrevented: false - }; - - if (!target.$$listenerCount[name]) return event; - - var listenerArgs = concat([event], arguments, 1), - listeners, i, length; - - //down while you can, then up and next sibling or up and next sibling until back at root - while ((current = next)) { - event.currentScope = current; - listeners = current.$$listeners[name] || []; - for (i = 0, length = listeners.length; i < length; i++) { - // if listeners were deregistered, defragment the array - if (!listeners[i]) { - listeners.splice(i, 1); - i--; - length--; - continue; - } - - try { - listeners[i].apply(null, listenerArgs); - } catch (e) { - $exceptionHandler(e); - } - } - - // Insanity Warning: scope depth-first traversal - // yes, this code is a bit crazy, but it works and we have tests to prove it! - // this piece should be kept in sync with the traversal in $digest - // (though it differs due to having the extra check for $$listenerCount) - if (!(next = ((current.$$listenerCount[name] && current.$$childHead) || - (current !== target && current.$$nextSibling)))) { - while (current !== target && !(next = current.$$nextSibling)) { - current = current.$parent; - } - } - } - - event.currentScope = null; - return event; - } - }; - - var $rootScope = new Scope(); - - //The internal queues. Expose them on the $rootScope for debugging/testing purposes. - var asyncQueue = $rootScope.$$asyncQueue = []; - var postDigestQueue = $rootScope.$$postDigestQueue = []; - var applyAsyncQueue = $rootScope.$$applyAsyncQueue = []; - - return $rootScope; - - - function beginPhase(phase) { - if ($rootScope.$$phase) { - throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase); - } - - $rootScope.$$phase = phase; - } - - function clearPhase() { - $rootScope.$$phase = null; - } - - function incrementWatchersCount(current, count) { - do { - current.$$watchersCount += count; - } while ((current = current.$parent)); - } - - function decrementListenerCount(current, count, name) { - do { - current.$$listenerCount[name] -= count; - - if (current.$$listenerCount[name] === 0) { - delete current.$$listenerCount[name]; - } - } while ((current = current.$parent)); - } - - /** - * function used as an initial value for watchers. - * because it's unique we can easily tell it apart from other values - */ - function initWatchVal() {} - - function flushApplyAsync() { - while (applyAsyncQueue.length) { - try { - applyAsyncQueue.shift()(); - } catch (e) { - $exceptionHandler(e); - } - } - applyAsyncId = null; - } - - function scheduleApplyAsync() { - if (applyAsyncId === null) { - applyAsyncId = $browser.defer(function() { - $rootScope.$apply(flushApplyAsync); - }); - } - } - }]; -} - -/** - * @description - * Private service to sanitize uris for links and images. Used by $compile and $sanitize. - */ -function $$SanitizeUriProvider() { - var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/, - imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/; - - /** - * @description - * Retrieves or overrides the default regular expression that is used for whitelisting of safe - * urls during a[href] sanitization. - * - * The sanitization is a security measure aimed at prevent XSS attacks via html links. - * - * Any url about to be assigned to a[href] via data-binding is first normalized and turned into - * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` - * regular expression. If a match is found, the original url is written into the dom. Otherwise, - * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. - * - * @param {RegExp=} regexp New regexp to whitelist urls with. - * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for - * chaining otherwise. - */ - this.aHrefSanitizationWhitelist = function(regexp) { - if (isDefined(regexp)) { - aHrefSanitizationWhitelist = regexp; - return this; - } - return aHrefSanitizationWhitelist; - }; - - - /** - * @description - * Retrieves or overrides the default regular expression that is used for whitelisting of safe - * urls during img[src] sanitization. - * - * The sanitization is a security measure aimed at prevent XSS attacks via html links. - * - * Any url about to be assigned to img[src] via data-binding is first normalized and turned into - * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` - * regular expression. If a match is found, the original url is written into the dom. Otherwise, - * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. - * - * @param {RegExp=} regexp New regexp to whitelist urls with. - * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for - * chaining otherwise. - */ - this.imgSrcSanitizationWhitelist = function(regexp) { - if (isDefined(regexp)) { - imgSrcSanitizationWhitelist = regexp; - return this; - } - return imgSrcSanitizationWhitelist; - }; - - this.$get = function() { - return function sanitizeUri(uri, isImage) { - var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist; - var normalizedVal; - normalizedVal = urlResolve(uri).href; - if (normalizedVal !== '' && !normalizedVal.match(regex)) { - return 'unsafe:' + normalizedVal; - } - return uri; - }; - }; -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Any commits to this file should be reviewed with security in mind. * - * Changes to this file can potentially create security vulnerabilities. * - * An approval from 2 Core members with history of modifying * - * this file is required. * - * * - * Does the change somehow allow for arbitrary javascript to be executed? * - * Or allows for someone to change the prototype of built-in objects? * - * Or gives undesired access to variables likes document or window? * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -var $sceMinErr = minErr('$sce'); - -var SCE_CONTEXTS = { - HTML: 'html', - CSS: 'css', - URL: 'url', - // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a - // url. (e.g. ng-include, script src, templateUrl) - RESOURCE_URL: 'resourceUrl', - JS: 'js' -}; - -// Helper functions follow. - -function adjustMatcher(matcher) { - if (matcher === 'self') { - return matcher; - } else if (isString(matcher)) { - // Strings match exactly except for 2 wildcards - '*' and '**'. - // '*' matches any character except those from the set ':/.?&'. - // '**' matches any character (like .* in a RegExp). - // More than 2 *'s raises an error as it's ill defined. - if (matcher.indexOf('***') > -1) { - throw $sceMinErr('iwcard', - 'Illegal sequence *** in string matcher. String: {0}', matcher); - } - matcher = escapeForRegexp(matcher). - replace('\\*\\*', '.*'). - replace('\\*', '[^:/.?&;]*'); - return new RegExp('^' + matcher + '$'); - } else if (isRegExp(matcher)) { - // The only other type of matcher allowed is a Regexp. - // Match entire URL / disallow partial matches. - // Flags are reset (i.e. no global, ignoreCase or multiline) - return new RegExp('^' + matcher.source + '$'); - } else { - throw $sceMinErr('imatcher', - 'Matchers may only be "self", string patterns or RegExp objects'); - } -} - - -function adjustMatchers(matchers) { - var adjustedMatchers = []; - if (isDefined(matchers)) { - forEach(matchers, function(matcher) { - adjustedMatchers.push(adjustMatcher(matcher)); - }); - } - return adjustedMatchers; -} - - -/** - * @ngdoc service - * @name $sceDelegate - * @kind function - * - * @description - * - * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict - * Contextual Escaping (SCE)} services to AngularJS. - * - * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of - * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is - * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to - * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things - * work because `$sce` delegates to `$sceDelegate` for these operations. - * - * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service. - * - * The default instance of `$sceDelegate` should work out of the box with little pain. While you - * can override it completely to change the behavior of `$sce`, the common case would - * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting - * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as - * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist - * $sceDelegateProvider.resourceUrlWhitelist} and {@link - * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} - */ - -/** - * @ngdoc provider - * @name $sceDelegateProvider - * @description - * - * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate - * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure - * that the URLs used for sourcing Angular templates are safe. Refer {@link - * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and - * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} - * - * For the general details about this service in Angular, read the main page for {@link ng.$sce - * Strict Contextual Escaping (SCE)}. - * - * **Example**: Consider the following case. - * - * - your app is hosted at url `http://myapp.example.com/` - * - but some of your templates are hosted on other domains you control such as - * `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc. - * - and you have an open redirect at `http://myapp.example.com/clickThru?...`. - * - * Here is what a secure configuration for this scenario might look like: - * - * ``` - * angular.module('myApp', []).config(function($sceDelegateProvider) { - * $sceDelegateProvider.resourceUrlWhitelist([ - * // Allow same origin resource loads. - * 'self', - * // Allow loading from our assets domain. Notice the difference between * and **. - * 'http://srv*.assets.example.com/**' - * ]); - * - * // The blacklist overrides the whitelist so the open redirect here is blocked. - * $sceDelegateProvider.resourceUrlBlacklist([ - * 'http://myapp.example.com/clickThru**' - * ]); - * }); - * ``` - */ - -function $SceDelegateProvider() { - this.SCE_CONTEXTS = SCE_CONTEXTS; - - // Resource URLs can also be trusted by policy. - var resourceUrlWhitelist = ['self'], - resourceUrlBlacklist = []; - - /** - * @ngdoc method - * @name $sceDelegateProvider#resourceUrlWhitelist - * @kind function - * - * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value - * provided. This must be an array or null. A snapshot of this array is used so further - * changes to the array are ignored. - * - * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items - * allowed in this array. - * - * Note: **an empty whitelist array will block all URLs**! - * - * @return {Array} the currently set whitelist array. - * - * The **default value** when no whitelist has been explicitly set is `['self']` allowing only - * same origin resource requests. - * - * @description - * Sets/Gets the whitelist of trusted resource URLs. - */ - this.resourceUrlWhitelist = function(value) { - if (arguments.length) { - resourceUrlWhitelist = adjustMatchers(value); - } - return resourceUrlWhitelist; - }; - - /** - * @ngdoc method - * @name $sceDelegateProvider#resourceUrlBlacklist - * @kind function - * - * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value - * provided. This must be an array or null. A snapshot of this array is used so further - * changes to the array are ignored. - * - * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items - * allowed in this array. - * - * The typical usage for the blacklist is to **block - * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as - * these would otherwise be trusted but actually return content from the redirected domain. - * - * Finally, **the blacklist overrides the whitelist** and has the final say. - * - * @return {Array} the currently set blacklist array. - * - * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there - * is no blacklist.) - * - * @description - * Sets/Gets the blacklist of trusted resource URLs. - */ - - this.resourceUrlBlacklist = function(value) { - if (arguments.length) { - resourceUrlBlacklist = adjustMatchers(value); - } - return resourceUrlBlacklist; - }; - - this.$get = ['$injector', function($injector) { - - var htmlSanitizer = function htmlSanitizer(html) { - throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); - }; - - if ($injector.has('$sanitize')) { - htmlSanitizer = $injector.get('$sanitize'); - } - - - function matchUrl(matcher, parsedUrl) { - if (matcher === 'self') { - return urlIsSameOrigin(parsedUrl); - } else { - // definitely a regex. See adjustMatchers() - return !!matcher.exec(parsedUrl.href); - } - } - - function isResourceUrlAllowedByPolicy(url) { - var parsedUrl = urlResolve(url.toString()); - var i, n, allowed = false; - // Ensure that at least one item from the whitelist allows this url. - for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) { - if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) { - allowed = true; - break; - } - } - if (allowed) { - // Ensure that no item from the blacklist blocked this url. - for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) { - if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) { - allowed = false; - break; - } - } - } - return allowed; - } - - function generateHolderType(Base) { - var holderType = function TrustedValueHolderType(trustedValue) { - this.$$unwrapTrustedValue = function() { - return trustedValue; - }; - }; - if (Base) { - holderType.prototype = new Base(); - } - holderType.prototype.valueOf = function sceValueOf() { - return this.$$unwrapTrustedValue(); - }; - holderType.prototype.toString = function sceToString() { - return this.$$unwrapTrustedValue().toString(); - }; - return holderType; - } - - var trustedValueHolderBase = generateHolderType(), - byType = {}; - - byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase); - byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase); - byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase); - byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase); - byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]); - - /** - * @ngdoc method - * @name $sceDelegate#trustAs - * - * @description - * Returns an object that is trusted by angular for use in specified strict - * contextual escaping contexts (such as ng-bind-html, ng-include, any src - * attribute interpolation, any dom event binding attribute interpolation - * such as for onclick, etc.) that uses the provided value. - * See {@link ng.$sce $sce} for enabling strict contextual escaping. - * - * @param {string} type The kind of context in which this value is safe for use. e.g. url, - * resourceUrl, html, js and css. - * @param {*} value The value that that should be considered trusted/safe. - * @returns {*} A value that can be used to stand in for the provided `value` in places - * where Angular expects a $sce.trustAs() return value. - */ - function trustAs(type, trustedValue) { - var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null); - if (!Constructor) { - throw $sceMinErr('icontext', - 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}', - type, trustedValue); - } - if (trustedValue === null || trustedValue === undefined || trustedValue === '') { - return trustedValue; - } - // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting - // mutable objects, we ensure here that the value passed in is actually a string. - if (typeof trustedValue !== 'string') { - throw $sceMinErr('itype', - 'Attempted to trust a non-string value in a content requiring a string: Context: {0}', - type); - } - return new Constructor(trustedValue); - } - - /** - * @ngdoc method - * @name $sceDelegate#valueOf - * - * @description - * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs - * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link - * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. - * - * If the passed parameter is not a value that had been returned by {@link - * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is. - * - * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} - * call or anything else. - * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs - * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns - * `value` unchanged. - */ - function valueOf(maybeTrusted) { - if (maybeTrusted instanceof trustedValueHolderBase) { - return maybeTrusted.$$unwrapTrustedValue(); - } else { - return maybeTrusted; - } - } - - /** - * @ngdoc method - * @name $sceDelegate#getTrusted - * - * @description - * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and - * returns the originally supplied value if the queried context type is a supertype of the - * created type. If this condition isn't satisfied, throws an exception. - * - * @param {string} type The kind of context in which this value is to be used. - * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs - * `$sceDelegate.trustAs`} call. - * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs - * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception. - */ - function getTrusted(type, maybeTrusted) { - if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') { - return maybeTrusted; - } - var constructor = (byType.hasOwnProperty(type) ? byType[type] : null); - if (constructor && maybeTrusted instanceof constructor) { - return maybeTrusted.$$unwrapTrustedValue(); - } - // If we get here, then we may only take one of two actions. - // 1. sanitize the value for the requested type, or - // 2. throw an exception. - if (type === SCE_CONTEXTS.RESOURCE_URL) { - if (isResourceUrlAllowedByPolicy(maybeTrusted)) { - return maybeTrusted; - } else { - throw $sceMinErr('insecurl', - 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', - maybeTrusted.toString()); - } - } else if (type === SCE_CONTEXTS.HTML) { - return htmlSanitizer(maybeTrusted); - } - throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); - } - - return { trustAs: trustAs, - getTrusted: getTrusted, - valueOf: valueOf }; - }]; -} - - -/** - * @ngdoc provider - * @name $sceProvider - * @description - * - * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service. - * - enable/disable Strict Contextual Escaping (SCE) in a module - * - override the default implementation with a custom delegate - * - * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}. - */ - -/* jshint maxlen: false*/ - -/** - * @ngdoc service - * @name $sce - * @kind function - * - * @description - * - * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS. - * - * # Strict Contextual Escaping - * - * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain - * contexts to result in a value that is marked as safe to use for that context. One example of - * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer - * to these contexts as privileged or SCE contexts. - * - * As of version 1.2, Angular ships with SCE enabled by default. - * - * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow - * one to execute arbitrary javascript by the use of the expression() syntax. Refer - * to learn more about them. - * You can ensure your document is in standards mode and not quirks mode by adding `` - * to the top of your HTML document. - * - * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for - * security vulnerabilities such as XSS, clickjacking, etc. a lot easier. - * - * Here's an example of a binding in a privileged context: - * - * ``` - * - *
- * ``` - * - * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE - * disabled, this application allows the user to render arbitrary HTML into the DIV. - * In a more realistic example, one may be rendering user comments, blog articles, etc. via - * bindings. (HTML is just one example of a context where rendering user controlled input creates - * security vulnerabilities.) - * - * For the case of HTML, you might use a library, either on the client side, or on the server side, - * to sanitize unsafe HTML before binding to the value and rendering it in the document. - * - * How would you ensure that every place that used these types of bindings was bound to a value that - * was sanitized by your library (or returned as safe for rendering by your server?) How can you - * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some - * properties/fields and forgot to update the binding to the sanitized value? - * - * To be secure by default, you want to ensure that any such bindings are disallowed unless you can - * determine that something explicitly says it's safe to use a value for binding in that - * context. You can then audit your code (a simple grep would do) to ensure that this is only done - * for those values that you can easily tell are safe - because they were received from your server, - * sanitized by your library, etc. You can organize your codebase to help with this - perhaps - * allowing only the files in a specific directory to do this. Ensuring that the internal API - * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task. - * - * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs} - * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to - * obtain values that will be accepted by SCE / privileged contexts. - * - * - * ## How does it work? - * - * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted - * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link - * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the - * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. - * - * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link - * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly - * simplified): - * - * ``` - * var ngBindHtmlDirective = ['$sce', function($sce) { - * return function(scope, element, attr) { - * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) { - * element.html(value || ''); - * }); - * }; - * }]; - * ``` - * - * ## Impact on loading templates - * - * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as - * `templateUrl`'s specified by {@link guide/directive directives}. - * - * By default, Angular only loads templates from the same domain and protocol as the application - * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl - * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or - * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist - * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value. - * - * *Please note*: - * The browser's - * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) - * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) - * policy apply in addition to this and may further restrict whether the template is successfully - * loaded. This means that without the right CORS policy, loading templates from a different domain - * won't work on all browsers. Also, loading templates from `file://` URL does not work on some - * browsers. - * - * ## This feels like too much overhead - * - * It's important to remember that SCE only applies to interpolation expressions. - * - * If your expressions are constant literals, they're automatically trusted and you don't need to - * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g. - * `
`) just works. - * - * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them - * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here. - * - * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load - * templates in `ng-include` from your application's domain without having to even know about SCE. - * It blocks loading templates from other domains or loading templates over http from an https - * served document. You can change these by setting your own custom {@link - * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link - * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs. - * - * This significantly reduces the overhead. It is far easier to pay the small overhead and have an - * application that's secure and can be audited to verify that with much more ease than bolting - * security onto an application later. - * - * - * ## What trusted context types are supported? - * - * | Context | Notes | - * |---------------------|----------------| - * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. | - * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. | - * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`
Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. | - * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. | - * - * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist}
- * - * Each element in these arrays must be one of the following: - * - * - **'self'** - * - The special **string**, `'self'`, can be used to match against all URLs of the **same - * domain** as the application document using the **same protocol**. - * - **String** (except the special value `'self'`) - * - The string is matched against the full *normalized / absolute URL* of the resource - * being tested (substring matches are not good enough.) - * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters - * match themselves. - * - `*`: matches zero or more occurrences of any character other than one of the following 6 - * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use - * in a whitelist. - * - `**`: matches zero or more occurrences of *any* character. As such, it's not - * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g. - * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might - * not have been the intention.) Its usage at the very end of the path is ok. (e.g. - * http://foo.example.com/templates/**). - * - **RegExp** (*see caveat below*) - * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax - * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to - * accidentally introduce a bug when one updates a complex expression (imho, all regexes should - * have good test coverage). For instance, the use of `.` in the regex is correct only in a - * small number of cases. A `.` character in the regex used when matching the scheme or a - * subdomain could be matched against a `:` or literal `.` that was likely not intended. It - * is highly recommended to use the string patterns and only fall back to regular expressions - * as a last resort. - * - The regular expression must be an instance of RegExp (i.e. not a string.) It is - * matched against the **entire** *normalized / absolute URL* of the resource being tested - * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags - * present on the RegExp (such as multiline, global, ignoreCase) are ignored. - * - If you are generating your JavaScript from some other templating engine (not - * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)), - * remember to escape your regular expression (and be aware that you might need more than - * one level of escaping depending on your templating engine and the way you interpolated - * the value.) Do make use of your platform's escaping mechanism as it might be good - * enough before coding your own. E.g. Ruby has - * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape) - * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape). - * Javascript lacks a similar built in function for escaping. Take a look at Google - * Closure library's [goog.string.regExpEscape(s)]( - * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962). - * - * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example. - * - * ## Show me an example using SCE. - * - * - * - *
- *

- * User comments
- * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when - * $sanitize is available. If $sanitize isn't available, this results in an error instead of an - * exploit. - *
- *
- * {{userComment.name}}: - * - *
- *
- *
- *
- *
- * - * - * angular.module('mySceApp', ['ngSanitize']) - * .controller('AppController', ['$http', '$templateCache', '$sce', - * function($http, $templateCache, $sce) { - * var self = this; - * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) { - * self.userComments = userComments; - * }); - * self.explicitlyTrustedHtml = $sce.trustAsHtml( - * 'Hover over this text.'); - * }]); - * - * - * - * [ - * { "name": "Alice", - * "htmlComment": - * "Is anyone reading this?" - * }, - * { "name": "Bob", - * "htmlComment": "Yes! Am I the only other one?" - * } - * ] - * - * - * - * describe('SCE doc demo', function() { - * it('should sanitize untrusted values', function() { - * expect(element.all(by.css('.htmlComment')).first().getInnerHtml()) - * .toBe('Is anyone reading this?'); - * }); - * - * it('should NOT sanitize explicitly trusted values', function() { - * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe( - * 'Hover over this text.'); - * }); - * }); - * - *
- * - * - * - * ## Can I disable SCE completely? - * - * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits - * for little coding overhead. It will be much harder to take an SCE disabled application and - * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE - * for cases where you have a lot of existing code that was written before SCE was introduced and - * you're migrating them a module at a time. - * - * That said, here's how you can completely disable SCE: - * - * ``` - * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) { - * // Completely disable SCE. For demonstration purposes only! - * // Do not use in new projects. - * $sceProvider.enabled(false); - * }); - * ``` - * - */ -/* jshint maxlen: 100 */ - -function $SceProvider() { - var enabled = true; - - /** - * @ngdoc method - * @name $sceProvider#enabled - * @kind function - * - * @param {boolean=} value If provided, then enables/disables SCE. - * @return {boolean} true if SCE is enabled, false otherwise. - * - * @description - * Enables/disables SCE and returns the current value. - */ - this.enabled = function(value) { - if (arguments.length) { - enabled = !!value; - } - return enabled; - }; - - - /* Design notes on the default implementation for SCE. - * - * The API contract for the SCE delegate - * ------------------------------------- - * The SCE delegate object must provide the following 3 methods: - * - * - trustAs(contextEnum, value) - * This method is used to tell the SCE service that the provided value is OK to use in the - * contexts specified by contextEnum. It must return an object that will be accepted by - * getTrusted() for a compatible contextEnum and return this value. - * - * - valueOf(value) - * For values that were not produced by trustAs(), return them as is. For values that were - * produced by trustAs(), return the corresponding input value to trustAs. Basically, if - * trustAs is wrapping the given values into some type, this operation unwraps it when given - * such a value. - * - * - getTrusted(contextEnum, value) - * This function should return the a value that is safe to use in the context specified by - * contextEnum or throw and exception otherwise. - * - * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be - * opaque or wrapped in some holder object. That happens to be an implementation detail. For - * instance, an implementation could maintain a registry of all trusted objects by context. In - * such a case, trustAs() would return the same object that was passed in. getTrusted() would - * return the same object passed in if it was found in the registry under a compatible context or - * throw an exception otherwise. An implementation might only wrap values some of the time based - * on some criteria. getTrusted() might return a value and not throw an exception for special - * constants or objects even if not wrapped. All such implementations fulfill this contract. - * - * - * A note on the inheritance model for SCE contexts - * ------------------------------------------------ - * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This - * is purely an implementation details. - * - * The contract is simply this: - * - * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value) - * will also succeed. - * - * Inheritance happens to capture this in a natural way. In some future, we - * may not use inheritance anymore. That is OK because no code outside of - * sce.js and sceSpecs.js would need to be aware of this detail. - */ - - this.$get = ['$parse', '$sceDelegate', function( - $parse, $sceDelegate) { - // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow - // the "expression(javascript expression)" syntax which is insecure. - if (enabled && msie < 8) { - throw $sceMinErr('iequirks', - 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' + - 'mode. You can fix this by adding the text to the top of your HTML ' + - 'document. See http://docs.angularjs.org/api/ng.$sce for more information.'); - } - - var sce = shallowCopy(SCE_CONTEXTS); - - /** - * @ngdoc method - * @name $sce#isEnabled - * @kind function - * - * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you - * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. - * - * @description - * Returns a boolean indicating if SCE is enabled. - */ - sce.isEnabled = function() { - return enabled; - }; - sce.trustAs = $sceDelegate.trustAs; - sce.getTrusted = $sceDelegate.getTrusted; - sce.valueOf = $sceDelegate.valueOf; - - if (!enabled) { - sce.trustAs = sce.getTrusted = function(type, value) { return value; }; - sce.valueOf = identity; - } - - /** - * @ngdoc method - * @name $sce#parseAs - * - * @description - * Converts Angular {@link guide/expression expression} into a function. This is like {@link - * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it - * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*, - * *result*)} - * - * @param {string} type The kind of SCE context in which this result will be used. - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - */ - sce.parseAs = function sceParseAs(type, expr) { - var parsed = $parse(expr); - if (parsed.literal && parsed.constant) { - return parsed; - } else { - return $parse(expr, function(value) { - return sce.getTrusted(type, value); - }); - } - }; - - /** - * @ngdoc method - * @name $sce#trustAs - * - * @description - * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, - * returns an object that is trusted by angular for use in specified strict contextual - * escaping contexts (such as ng-bind-html, ng-include, any src attribute - * interpolation, any dom event binding attribute interpolation such as for onclick, etc.) - * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual - * escaping. - * - * @param {string} type The kind of context in which this value is safe for use. e.g. url, - * resourceUrl, html, js and css. - * @param {*} value The value that that should be considered trusted/safe. - * @returns {*} A value that can be used to stand in for the provided `value` in places - * where Angular expects a $sce.trustAs() return value. - */ - - /** - * @ngdoc method - * @name $sce#trustAsHtml - * - * @description - * Shorthand method. `$sce.trustAsHtml(value)` → - * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`} - * - * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml - * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives - * only accept expressions that are either literal constants or are the - * return value of {@link ng.$sce#trustAs $sce.trustAs}.) - */ - - /** - * @ngdoc method - * @name $sce#trustAsUrl - * - * @description - * Shorthand method. `$sce.trustAsUrl(value)` → - * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`} - * - * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl - * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives - * only accept expressions that are either literal constants or are the - * return value of {@link ng.$sce#trustAs $sce.trustAs}.) - */ - - /** - * @ngdoc method - * @name $sce#trustAsResourceUrl - * - * @description - * Shorthand method. `$sce.trustAsResourceUrl(value)` → - * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`} - * - * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl - * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives - * only accept expressions that are either literal constants or are the return - * value of {@link ng.$sce#trustAs $sce.trustAs}.) - */ - - /** - * @ngdoc method - * @name $sce#trustAsJs - * - * @description - * Shorthand method. `$sce.trustAsJs(value)` → - * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`} - * - * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs - * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives - * only accept expressions that are either literal constants or are the - * return value of {@link ng.$sce#trustAs $sce.trustAs}.) - */ - - /** - * @ngdoc method - * @name $sce#getTrusted - * - * @description - * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such, - * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the - * originally supplied value if the queried context type is a supertype of the created type. - * If this condition isn't satisfied, throws an exception. - * - * @param {string} type The kind of context in which this value is to be used. - * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`} - * call. - * @returns {*} The value the was originally provided to - * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context. - * Otherwise, throws an exception. - */ - - /** - * @ngdoc method - * @name $sce#getTrustedHtml - * - * @description - * Shorthand method. `$sce.getTrustedHtml(value)` → - * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`} - * - * @param {*} value The value to pass to `$sce.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)` - */ - - /** - * @ngdoc method - * @name $sce#getTrustedCss - * - * @description - * Shorthand method. `$sce.getTrustedCss(value)` → - * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`} - * - * @param {*} value The value to pass to `$sce.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)` - */ - - /** - * @ngdoc method - * @name $sce#getTrustedUrl - * - * @description - * Shorthand method. `$sce.getTrustedUrl(value)` → - * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`} - * - * @param {*} value The value to pass to `$sce.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)` - */ - - /** - * @ngdoc method - * @name $sce#getTrustedResourceUrl - * - * @description - * Shorthand method. `$sce.getTrustedResourceUrl(value)` → - * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`} - * - * @param {*} value The value to pass to `$sceDelegate.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)` - */ - - /** - * @ngdoc method - * @name $sce#getTrustedJs - * - * @description - * Shorthand method. `$sce.getTrustedJs(value)` → - * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`} - * - * @param {*} value The value to pass to `$sce.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)` - */ - - /** - * @ngdoc method - * @name $sce#parseAsHtml - * - * @description - * Shorthand method. `$sce.parseAsHtml(expression string)` → - * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`} - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - */ - - /** - * @ngdoc method - * @name $sce#parseAsCss - * - * @description - * Shorthand method. `$sce.parseAsCss(value)` → - * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`} - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - */ - - /** - * @ngdoc method - * @name $sce#parseAsUrl - * - * @description - * Shorthand method. `$sce.parseAsUrl(value)` → - * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`} - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - */ - - /** - * @ngdoc method - * @name $sce#parseAsResourceUrl - * - * @description - * Shorthand method. `$sce.parseAsResourceUrl(value)` → - * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`} - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - */ - - /** - * @ngdoc method - * @name $sce#parseAsJs - * - * @description - * Shorthand method. `$sce.parseAsJs(value)` → - * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`} - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - */ - - // Shorthand delegations. - var parse = sce.parseAs, - getTrusted = sce.getTrusted, - trustAs = sce.trustAs; - - forEach(SCE_CONTEXTS, function(enumValue, name) { - var lName = lowercase(name); - sce[camelCase("parse_as_" + lName)] = function(expr) { - return parse(enumValue, expr); - }; - sce[camelCase("get_trusted_" + lName)] = function(value) { - return getTrusted(enumValue, value); - }; - sce[camelCase("trust_as_" + lName)] = function(value) { - return trustAs(enumValue, value); - }; - }); - - return sce; - }]; -} - -/** - * !!! This is an undocumented "private" service !!! - * - * @name $sniffer - * @requires $window - * @requires $document - * - * @property {boolean} history Does the browser support html5 history api ? - * @property {boolean} transitions Does the browser support CSS transition events ? - * @property {boolean} animations Does the browser support CSS animation events ? - * - * @description - * This is very simple implementation of testing browser's features. - */ -function $SnifferProvider() { - this.$get = ['$window', '$document', function($window, $document) { - var eventSupport = {}, - android = - toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), - boxee = /Boxee/i.test(($window.navigator || {}).userAgent), - document = $document[0] || {}, - vendorPrefix, - vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/, - bodyStyle = document.body && document.body.style, - transitions = false, - animations = false, - match; - - if (bodyStyle) { - for (var prop in bodyStyle) { - if (match = vendorRegex.exec(prop)) { - vendorPrefix = match[0]; - vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1); - break; - } - } - - if (!vendorPrefix) { - vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit'; - } - - transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle)); - animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle)); - - if (android && (!transitions || !animations)) { - transitions = isString(bodyStyle.webkitTransition); - animations = isString(bodyStyle.webkitAnimation); - } - } - - - return { - // Android has history.pushState, but it does not update location correctly - // so let's not use the history API at all. - // http://code.google.com/p/android/issues/detail?id=17471 - // https://github.com/angular/angular.js/issues/904 - - // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has - // so let's not use the history API also - // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined - // jshint -W018 - history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee), - // jshint +W018 - hasEvent: function(event) { - // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have - // it. In particular the event is not fired when backspace or delete key are pressed or - // when cut operation is performed. - // IE10+ implements 'input' event but it erroneously fires under various situations, - // e.g. when placeholder changes, or a form is focused. - if (event === 'input' && msie <= 11) return false; - - if (isUndefined(eventSupport[event])) { - var divElm = document.createElement('div'); - eventSupport[event] = 'on' + event in divElm; - } - - return eventSupport[event]; - }, - csp: csp(), - vendorPrefix: vendorPrefix, - transitions: transitions, - animations: animations, - android: android - }; - }]; -} - -var $compileMinErr = minErr('$compile'); - -/** - * @ngdoc service - * @name $templateRequest - * - * @description - * The `$templateRequest` service runs security checks then downloads the provided template using - * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request - * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the - * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the - * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted - * when `tpl` is of type string and `$templateCache` has the matching entry. - * - * @param {string|TrustedResourceUrl} tpl The HTTP request template URL - * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty - * - * @return {Promise} a promise for the HTTP response data of the given URL. - * - * @property {number} totalPendingRequests total amount of pending template requests being downloaded. - */ -function $TemplateRequestProvider() { - this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) { - function handleRequestFn(tpl, ignoreRequestError) { - handleRequestFn.totalPendingRequests++; - - // We consider the template cache holds only trusted templates, so - // there's no need to go through whitelisting again for keys that already - // are included in there. This also makes Angular accept any script - // directive, no matter its name. However, we still need to unwrap trusted - // types. - if (!isString(tpl) || !$templateCache.get(tpl)) { - tpl = $sce.getTrustedResourceUrl(tpl); - } - - var transformResponse = $http.defaults && $http.defaults.transformResponse; - - if (isArray(transformResponse)) { - transformResponse = transformResponse.filter(function(transformer) { - return transformer !== defaultHttpResponseTransform; - }); - } else if (transformResponse === defaultHttpResponseTransform) { - transformResponse = null; - } - - var httpOptions = { - cache: $templateCache, - transformResponse: transformResponse - }; - - return $http.get(tpl, httpOptions) - ['finally'](function() { - handleRequestFn.totalPendingRequests--; - }) - .then(function(response) { - $templateCache.put(tpl, response.data); - return response.data; - }, handleError); - - function handleError(resp) { - if (!ignoreRequestError) { - throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})', - tpl, resp.status, resp.statusText); - } - return $q.reject(resp); - } - } - - handleRequestFn.totalPendingRequests = 0; - - return handleRequestFn; - }]; -} - -function $$TestabilityProvider() { - this.$get = ['$rootScope', '$browser', '$location', - function($rootScope, $browser, $location) { - - /** - * @name $testability - * - * @description - * The private $$testability service provides a collection of methods for use when debugging - * or by automated test and debugging tools. - */ - var testability = {}; - - /** - * @name $$testability#findBindings - * - * @description - * Returns an array of elements that are bound (via ng-bind or {{}}) - * to expressions matching the input. - * - * @param {Element} element The element root to search from. - * @param {string} expression The binding expression to match. - * @param {boolean} opt_exactMatch If true, only returns exact matches - * for the expression. Filters and whitespace are ignored. - */ - testability.findBindings = function(element, expression, opt_exactMatch) { - var bindings = element.getElementsByClassName('ng-binding'); - var matches = []; - forEach(bindings, function(binding) { - var dataBinding = angular.element(binding).data('$binding'); - if (dataBinding) { - forEach(dataBinding, function(bindingName) { - if (opt_exactMatch) { - var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)'); - if (matcher.test(bindingName)) { - matches.push(binding); - } - } else { - if (bindingName.indexOf(expression) != -1) { - matches.push(binding); - } - } - }); - } - }); - return matches; - }; - - /** - * @name $$testability#findModels - * - * @description - * Returns an array of elements that are two-way found via ng-model to - * expressions matching the input. - * - * @param {Element} element The element root to search from. - * @param {string} expression The model expression to match. - * @param {boolean} opt_exactMatch If true, only returns exact matches - * for the expression. - */ - testability.findModels = function(element, expression, opt_exactMatch) { - var prefixes = ['ng-', 'data-ng-', 'ng\\:']; - for (var p = 0; p < prefixes.length; ++p) { - var attributeEquals = opt_exactMatch ? '=' : '*='; - var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]'; - var elements = element.querySelectorAll(selector); - if (elements.length) { - return elements; - } - } - }; - - /** - * @name $$testability#getLocation - * - * @description - * Shortcut for getting the location in a browser agnostic way. Returns - * the path, search, and hash. (e.g. /path?a=b#hash) - */ - testability.getLocation = function() { - return $location.url(); - }; - - /** - * @name $$testability#setLocation - * - * @description - * Shortcut for navigating to a location without doing a full page reload. - * - * @param {string} url The location url (path, search and hash, - * e.g. /path?a=b#hash) to go to. - */ - testability.setLocation = function(url) { - if (url !== $location.url()) { - $location.url(url); - $rootScope.$digest(); - } - }; - - /** - * @name $$testability#whenStable - * - * @description - * Calls the callback when $timeout and $http requests are completed. - * - * @param {function} callback - */ - testability.whenStable = function(callback) { - $browser.notifyWhenNoOutstandingRequests(callback); - }; - - return testability; - }]; -} - -function $TimeoutProvider() { - this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler', - function($rootScope, $browser, $q, $$q, $exceptionHandler) { - - var deferreds = {}; - - - /** - * @ngdoc service - * @name $timeout - * - * @description - * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch - * block and delegates any exceptions to - * {@link ng.$exceptionHandler $exceptionHandler} service. - * - * The return value of calling `$timeout` is a promise, which will be resolved when - * the delay has passed and the timeout function, if provided, is executed. - * - * To cancel a timeout request, call `$timeout.cancel(promise)`. - * - * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to - * synchronously flush the queue of deferred functions. - * - * If you only want a promise that will be resolved after some specified delay - * then you can call `$timeout` without the `fn` function. - * - * @param {function()=} fn A function, whose execution should be delayed. - * @param {number=} [delay=0] Delay in milliseconds. - * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise - * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. - * @param {...*=} Pass additional parameters to the executed function. - * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this - * promise will be resolved with is the return value of the `fn` function. - * - */ - function timeout(fn, delay, invokeApply) { - if (!isFunction(fn)) { - invokeApply = delay; - delay = fn; - fn = noop; - } - - var args = sliceArgs(arguments, 3), - skipApply = (isDefined(invokeApply) && !invokeApply), - deferred = (skipApply ? $$q : $q).defer(), - promise = deferred.promise, - timeoutId; - - timeoutId = $browser.defer(function() { - try { - deferred.resolve(fn.apply(null, args)); - } catch (e) { - deferred.reject(e); - $exceptionHandler(e); - } - finally { - delete deferreds[promise.$$timeoutId]; - } - - if (!skipApply) $rootScope.$apply(); - }, delay); - - promise.$$timeoutId = timeoutId; - deferreds[timeoutId] = deferred; - - return promise; - } - - - /** - * @ngdoc method - * @name $timeout#cancel - * - * @description - * Cancels a task associated with the `promise`. As a result of this, the promise will be - * resolved with a rejection. - * - * @param {Promise=} promise Promise returned by the `$timeout` function. - * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully - * canceled. - */ - timeout.cancel = function(promise) { - if (promise && promise.$$timeoutId in deferreds) { - deferreds[promise.$$timeoutId].reject('canceled'); - delete deferreds[promise.$$timeoutId]; - return $browser.defer.cancel(promise.$$timeoutId); - } - return false; - }; - - return timeout; - }]; -} - -// NOTE: The usage of window and document instead of $window and $document here is -// deliberate. This service depends on the specific behavior of anchor nodes created by the -// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and -// cause us to break tests. In addition, when the browser resolves a URL for XHR, it -// doesn't know about mocked locations and resolves URLs to the real document - which is -// exactly the behavior needed here. There is little value is mocking these out for this -// service. -var urlParsingNode = document.createElement("a"); -var originUrl = urlResolve(window.location.href); - - -/** - * - * Implementation Notes for non-IE browsers - * ---------------------------------------- - * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM, - * results both in the normalizing and parsing of the URL. Normalizing means that a relative - * URL will be resolved into an absolute URL in the context of the application document. - * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related - * properties are all populated to reflect the normalized URL. This approach has wide - * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See - * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html - * - * Implementation Notes for IE - * --------------------------- - * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other - * browsers. However, the parsed components will not be set if the URL assigned did not specify - * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We - * work around that by performing the parsing in a 2nd step by taking a previously normalized - * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the - * properties such as protocol, hostname, port, etc. - * - * References: - * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement - * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html - * http://url.spec.whatwg.org/#urlutils - * https://github.com/angular/angular.js/pull/2902 - * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ - * - * @kind function - * @param {string} url The URL to be parsed. - * @description Normalizes and parses a URL. - * @returns {object} Returns the normalized URL as a dictionary. - * - * | member name | Description | - * |---------------|----------------| - * | href | A normalized version of the provided URL if it was not an absolute URL | - * | protocol | The protocol including the trailing colon | - * | host | The host and port (if the port is non-default) of the normalizedUrl | - * | search | The search params, minus the question mark | - * | hash | The hash string, minus the hash symbol - * | hostname | The hostname - * | port | The port, without ":" - * | pathname | The pathname, beginning with "/" - * - */ -function urlResolve(url) { - var href = url; - - if (msie) { - // Normalize before parse. Refer Implementation Notes on why this is - // done in two steps on IE. - urlParsingNode.setAttribute("href", href); - href = urlParsingNode.href; - } - - urlParsingNode.setAttribute('href', href); - - // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils - return { - href: urlParsingNode.href, - protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', - host: urlParsingNode.host, - search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', - hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', - hostname: urlParsingNode.hostname, - port: urlParsingNode.port, - pathname: (urlParsingNode.pathname.charAt(0) === '/') - ? urlParsingNode.pathname - : '/' + urlParsingNode.pathname - }; -} - -/** - * Parse a request URL and determine whether this is a same-origin request as the application document. - * - * @param {string|object} requestUrl The url of the request as a string that will be resolved - * or a parsed URL object. - * @returns {boolean} Whether the request is for the same origin as the application document. - */ -function urlIsSameOrigin(requestUrl) { - var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl; - return (parsed.protocol === originUrl.protocol && - parsed.host === originUrl.host); -} - -/** - * @ngdoc service - * @name $window - * - * @description - * A reference to the browser's `window` object. While `window` - * is globally available in JavaScript, it causes testability problems, because - * it is a global variable. In angular we always refer to it through the - * `$window` service, so it may be overridden, removed or mocked for testing. - * - * Expressions, like the one defined for the `ngClick` directive in the example - * below, are evaluated with respect to the current scope. Therefore, there is - * no risk of inadvertently coding in a dependency on a global value in such an - * expression. - * - * @example - - - -
- - -
-
- - it('should display the greeting in the input box', function() { - element(by.model('greeting')).sendKeys('Hello, E2E Tests'); - // If we click the button it will block the test runner - // element(':button').click(); - }); - -
- */ -function $WindowProvider() { - this.$get = valueFn(window); -} - -/** - * @name $$cookieReader - * @requires $document - * - * @description - * This is a private service for reading cookies used by $http and ngCookies - * - * @return {Object} a key/value map of the current cookies - */ -function $$CookieReader($document) { - var rawDocument = $document[0] || {}; - var lastCookies = {}; - var lastCookieString = ''; - - function safeDecodeURIComponent(str) { - try { - return decodeURIComponent(str); - } catch (e) { - return str; - } - } - - return function() { - var cookieArray, cookie, i, index, name; - var currentCookieString = rawDocument.cookie || ''; - - if (currentCookieString !== lastCookieString) { - lastCookieString = currentCookieString; - cookieArray = lastCookieString.split('; '); - lastCookies = {}; - - for (i = 0; i < cookieArray.length; i++) { - cookie = cookieArray[i]; - index = cookie.indexOf('='); - if (index > 0) { //ignore nameless cookies - name = safeDecodeURIComponent(cookie.substring(0, index)); - // the first value that is seen for a cookie is the most - // specific one. values for the same cookie name that - // follow are for less specific paths. - if (lastCookies[name] === undefined) { - lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1)); - } - } - } - } - return lastCookies; - }; -} - -$$CookieReader.$inject = ['$document']; - -function $$CookieReaderProvider() { - this.$get = $$CookieReader; -} - -/* global currencyFilter: true, - dateFilter: true, - filterFilter: true, - jsonFilter: true, - limitToFilter: true, - lowercaseFilter: true, - numberFilter: true, - orderByFilter: true, - uppercaseFilter: true, - */ - -/** - * @ngdoc provider - * @name $filterProvider - * @description - * - * Filters are just functions which transform input to an output. However filters need to be - * Dependency Injected. To achieve this a filter definition consists of a factory function which is - * annotated with dependencies and is responsible for creating a filter function. - * - *
- * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. - * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace - * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores - * (`myapp_subsection_filterx`). - *
- * - * ```js - * // Filter registration - * function MyModule($provide, $filterProvider) { - * // create a service to demonstrate injection (not always needed) - * $provide.value('greet', function(name){ - * return 'Hello ' + name + '!'; - * }); - * - * // register a filter factory which uses the - * // greet service to demonstrate DI. - * $filterProvider.register('greet', function(greet){ - * // return the filter function which uses the greet service - * // to generate salutation - * return function(text) { - * // filters need to be forgiving so check input validity - * return text && greet(text) || text; - * }; - * }); - * } - * ``` - * - * The filter function is registered with the `$injector` under the filter name suffix with - * `Filter`. - * - * ```js - * it('should be the same instance', inject( - * function($filterProvider) { - * $filterProvider.register('reverse', function(){ - * return ...; - * }); - * }, - * function($filter, reverseFilter) { - * expect($filter('reverse')).toBe(reverseFilter); - * }); - * ``` - * - * - * For more information about how angular filters work, and how to create your own filters, see - * {@link guide/filter Filters} in the Angular Developer Guide. - */ - -/** - * @ngdoc service - * @name $filter - * @kind function - * @description - * Filters are used for formatting data displayed to the user. - * - * The general syntax in templates is as follows: - * - * {{ expression [| filter_name[:parameter_value] ... ] }} - * - * @param {String} name Name of the filter function to retrieve - * @return {Function} the filter function - * @example - - -
-

{{ originalText }}

-

{{ filteredText }}

-
-
- - - angular.module('filterExample', []) - .controller('MainCtrl', function($scope, $filter) { - $scope.originalText = 'hello'; - $scope.filteredText = $filter('uppercase')($scope.originalText); - }); - -
- */ -$FilterProvider.$inject = ['$provide']; -function $FilterProvider($provide) { - var suffix = 'Filter'; - - /** - * @ngdoc method - * @name $filterProvider#register - * @param {string|Object} name Name of the filter function, or an object map of filters where - * the keys are the filter names and the values are the filter factories. - * - *
- * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. - * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace - * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores - * (`myapp_subsection_filterx`). - *
- * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered. - * @returns {Object} Registered filter instance, or if a map of filters was provided then a map - * of the registered filter instances. - */ - function register(name, factory) { - if (isObject(name)) { - var filters = {}; - forEach(name, function(filter, key) { - filters[key] = register(key, filter); - }); - return filters; - } else { - return $provide.factory(name + suffix, factory); - } - } - this.register = register; - - this.$get = ['$injector', function($injector) { - return function(name) { - return $injector.get(name + suffix); - }; - }]; - - //////////////////////////////////////// - - /* global - currencyFilter: false, - dateFilter: false, - filterFilter: false, - jsonFilter: false, - limitToFilter: false, - lowercaseFilter: false, - numberFilter: false, - orderByFilter: false, - uppercaseFilter: false, - */ - - register('currency', currencyFilter); - register('date', dateFilter); - register('filter', filterFilter); - register('json', jsonFilter); - register('limitTo', limitToFilter); - register('lowercase', lowercaseFilter); - register('number', numberFilter); - register('orderBy', orderByFilter); - register('uppercase', uppercaseFilter); -} - -/** - * @ngdoc filter - * @name filter - * @kind function - * - * @description - * Selects a subset of items from `array` and returns it as a new array. - * - * @param {Array} array The source array. - * @param {string|Object|function()} expression The predicate to be used for selecting items from - * `array`. - * - * Can be one of: - * - * - `string`: The string is used for matching against the contents of the `array`. All strings or - * objects with string properties in `array` that match this string will be returned. This also - * applies to nested object properties. - * The predicate can be negated by prefixing the string with `!`. - * - * - `Object`: A pattern object can be used to filter specific properties on objects contained - * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items - * which have property `name` containing "M" and property `phone` containing "1". A special - * property name `$` can be used (as in `{$:"text"}`) to accept a match against any - * property of the object or its nested object properties. That's equivalent to the simple - * substring match with a `string` as described above. The predicate can be negated by prefixing - * the string with `!`. - * For example `{name: "!M"}` predicate will return an array of items which have property `name` - * not containing "M". - * - * Note that a named property will match properties on the same level only, while the special - * `$` property will match properties on the same level or deeper. E.g. an array item like - * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but - * **will** be matched by `{$: 'John'}`. - * - * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters. - * The function is called for each element of the array, with the element, its index, and - * the entire array itself as arguments. - * - * The final result is an array of those elements that the predicate returned true for. - * - * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in - * determining if the expected value (from the filter expression) and actual value (from - * the object in the array) should be considered a match. - * - * Can be one of: - * - * - `function(actual, expected)`: - * The function will be given the object value and the predicate value to compare and - * should return true if both values should be considered equal. - * - * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`. - * This is essentially strict comparison of expected and actual. - * - * - `false|undefined`: A short hand for a function which will look for a substring match in case - * insensitive way. - * - * Primitive values are converted to strings. Objects are not compared against primitives, - * unless they have a custom `toString` method (e.g. `Date` objects). - * - * @example - - -
- - - - - - - - -
NamePhone
{{friend.name}}{{friend.phone}}
-
-
-
-
-
- - - - - - -
NamePhone
{{friendObj.name}}{{friendObj.phone}}
-
- - var expectFriendNames = function(expectedNames, key) { - element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) { - arr.forEach(function(wd, i) { - expect(wd.getText()).toMatch(expectedNames[i]); - }); - }); - }; - - it('should search across all fields when filtering with a string', function() { - var searchText = element(by.model('searchText')); - searchText.clear(); - searchText.sendKeys('m'); - expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend'); - - searchText.clear(); - searchText.sendKeys('76'); - expectFriendNames(['John', 'Julie'], 'friend'); - }); - - it('should search in specific fields when filtering with a predicate object', function() { - var searchAny = element(by.model('search.$')); - searchAny.clear(); - searchAny.sendKeys('i'); - expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj'); - }); - it('should use a equal comparison when comparator is true', function() { - var searchName = element(by.model('search.name')); - var strict = element(by.model('strict')); - searchName.clear(); - searchName.sendKeys('Julie'); - strict.click(); - expectFriendNames(['Julie'], 'friendObj'); - }); - -
- */ -function filterFilter() { - return function(array, expression, comparator) { - if (!isArrayLike(array)) { - if (array == null) { - return array; - } else { - throw minErr('filter')('notarray', 'Expected array but received: {0}', array); - } - } - - var expressionType = getTypeForFilter(expression); - var predicateFn; - var matchAgainstAnyProp; - - switch (expressionType) { - case 'function': - predicateFn = expression; - break; - case 'boolean': - case 'null': - case 'number': - case 'string': - matchAgainstAnyProp = true; - //jshint -W086 - case 'object': - //jshint +W086 - predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp); - break; - default: - return array; - } - - return Array.prototype.filter.call(array, predicateFn); - }; -} - -// Helper functions for `filterFilter` -function createPredicateFn(expression, comparator, matchAgainstAnyProp) { - var shouldMatchPrimitives = isObject(expression) && ('$' in expression); - var predicateFn; - - if (comparator === true) { - comparator = equals; - } else if (!isFunction(comparator)) { - comparator = function(actual, expected) { - if (isUndefined(actual)) { - // No substring matching against `undefined` - return false; - } - if ((actual === null) || (expected === null)) { - // No substring matching against `null`; only match against `null` - return actual === expected; - } - if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) { - // Should not compare primitives against objects, unless they have custom `toString` method - return false; - } - - actual = lowercase('' + actual); - expected = lowercase('' + expected); - return actual.indexOf(expected) !== -1; - }; - } - - predicateFn = function(item) { - if (shouldMatchPrimitives && !isObject(item)) { - return deepCompare(item, expression.$, comparator, false); - } - return deepCompare(item, expression, comparator, matchAgainstAnyProp); - }; - - return predicateFn; -} - -function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) { - var actualType = getTypeForFilter(actual); - var expectedType = getTypeForFilter(expected); - - if ((expectedType === 'string') && (expected.charAt(0) === '!')) { - return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp); - } else if (isArray(actual)) { - // In case `actual` is an array, consider it a match - // if ANY of it's items matches `expected` - return actual.some(function(item) { - return deepCompare(item, expected, comparator, matchAgainstAnyProp); - }); - } - - switch (actualType) { - case 'object': - var key; - if (matchAgainstAnyProp) { - for (key in actual) { - if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) { - return true; - } - } - return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false); - } else if (expectedType === 'object') { - for (key in expected) { - var expectedVal = expected[key]; - if (isFunction(expectedVal) || isUndefined(expectedVal)) { - continue; - } - - var matchAnyProperty = key === '$'; - var actualVal = matchAnyProperty ? actual : actual[key]; - if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) { - return false; - } - } - return true; - } else { - return comparator(actual, expected); - } - break; - case 'function': - return false; - default: - return comparator(actual, expected); - } -} - -// Used for easily differentiating between `null` and actual `object` -function getTypeForFilter(val) { - return (val === null) ? 'null' : typeof val; -} - -/** - * @ngdoc filter - * @name currency - * @kind function - * - * @description - * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default - * symbol for current locale is used. - * - * @param {number} amount Input to filter. - * @param {string=} symbol Currency symbol or identifier to be displayed. - * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale - * @returns {string} Formatted number. - * - * - * @example - - - -
-
- default currency symbol ($): {{amount | currency}}
- custom currency identifier (USD$): {{amount | currency:"USD$"}} - no fractions (0): {{amount | currency:"USD$":0}} -
-
- - it('should init with 1234.56', function() { - expect(element(by.id('currency-default')).getText()).toBe('$1,234.56'); - expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56'); - expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235'); - }); - it('should update', function() { - if (browser.params.browser == 'safari') { - // Safari does not understand the minus key. See - // https://github.com/angular/protractor/issues/481 - return; - } - element(by.model('amount')).clear(); - element(by.model('amount')).sendKeys('-1234'); - expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00'); - expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00'); - expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234'); - }); - -
- */ -currencyFilter.$inject = ['$locale']; -function currencyFilter($locale) { - var formats = $locale.NUMBER_FORMATS; - return function(amount, currencySymbol, fractionSize) { - if (isUndefined(currencySymbol)) { - currencySymbol = formats.CURRENCY_SYM; - } - - if (isUndefined(fractionSize)) { - fractionSize = formats.PATTERNS[1].maxFrac; - } - - // if null or undefined pass it through - return (amount == null) - ? amount - : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize). - replace(/\u00A4/g, currencySymbol); - }; -} - -/** - * @ngdoc filter - * @name number - * @kind function - * - * @description - * Formats a number as text. - * - * If the input is null or undefined, it will just be returned. - * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned. - * If the input is not a number an empty string is returned. - * - * - * @param {number|string} number Number to format. - * @param {(number|string)=} fractionSize Number of decimal places to round the number to. - * If this is not provided then the fraction size is computed from the current locale's number - * formatting pattern. In the case of the default locale, it will be 3. - * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit. - * - * @example - - - -
-
- Default formatting: {{val | number}}
- No fractions: {{val | number:0}}
- Negative number: {{-val | number:4}} -
-
- - it('should format numbers', function() { - expect(element(by.id('number-default')).getText()).toBe('1,234.568'); - expect(element(by.binding('val | number:0')).getText()).toBe('1,235'); - expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679'); - }); - - it('should update', function() { - element(by.model('val')).clear(); - element(by.model('val')).sendKeys('3374.333'); - expect(element(by.id('number-default')).getText()).toBe('3,374.333'); - expect(element(by.binding('val | number:0')).getText()).toBe('3,374'); - expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330'); - }); - -
- */ - - -numberFilter.$inject = ['$locale']; -function numberFilter($locale) { - var formats = $locale.NUMBER_FORMATS; - return function(number, fractionSize) { - - // if null or undefined pass it through - return (number == null) - ? number - : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, - fractionSize); - }; -} - -var DECIMAL_SEP = '.'; -function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { - if (isObject(number)) return ''; - - var isNegative = number < 0; - number = Math.abs(number); - - var isInfinity = number === Infinity; - if (!isInfinity && !isFinite(number)) return ''; - - var numStr = number + '', - formatedText = '', - hasExponent = false, - parts = []; - - if (isInfinity) formatedText = '\u221e'; - - if (!isInfinity && numStr.indexOf('e') !== -1) { - var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); - if (match && match[2] == '-' && match[3] > fractionSize + 1) { - number = 0; - } else { - formatedText = numStr; - hasExponent = true; - } - } - - if (!isInfinity && !hasExponent) { - var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length; - - // determine fractionSize if it is not specified - if (isUndefined(fractionSize)) { - fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac); - } - - // safely round numbers in JS without hitting imprecisions of floating-point arithmetics - // inspired by: - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round - number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize); - - var fraction = ('' + number).split(DECIMAL_SEP); - var whole = fraction[0]; - fraction = fraction[1] || ''; - - var i, pos = 0, - lgroup = pattern.lgSize, - group = pattern.gSize; - - if (whole.length >= (lgroup + group)) { - pos = whole.length - lgroup; - for (i = 0; i < pos; i++) { - if ((pos - i) % group === 0 && i !== 0) { - formatedText += groupSep; - } - formatedText += whole.charAt(i); - } - } - - for (i = pos; i < whole.length; i++) { - if ((whole.length - i) % lgroup === 0 && i !== 0) { - formatedText += groupSep; - } - formatedText += whole.charAt(i); - } - - // format fraction part. - while (fraction.length < fractionSize) { - fraction += '0'; - } - - if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize); - } else { - if (fractionSize > 0 && number < 1) { - formatedText = number.toFixed(fractionSize); - number = parseFloat(formatedText); - } - } - - if (number === 0) { - isNegative = false; - } - - parts.push(isNegative ? pattern.negPre : pattern.posPre, - formatedText, - isNegative ? pattern.negSuf : pattern.posSuf); - return parts.join(''); -} - -function padNumber(num, digits, trim) { - var neg = ''; - if (num < 0) { - neg = '-'; - num = -num; - } - num = '' + num; - while (num.length < digits) num = '0' + num; - if (trim) { - num = num.substr(num.length - digits); - } - return neg + num; -} - - -function dateGetter(name, size, offset, trim) { - offset = offset || 0; - return function(date) { - var value = date['get' + name](); - if (offset > 0 || value > -offset) { - value += offset; - } - if (value === 0 && offset == -12) value = 12; - return padNumber(value, size, trim); - }; -} - -function dateStrGetter(name, shortForm) { - return function(date, formats) { - var value = date['get' + name](); - var get = uppercase(shortForm ? ('SHORT' + name) : name); - - return formats[get][value]; - }; -} - -function timeZoneGetter(date, formats, offset) { - var zone = -1 * offset; - var paddedZone = (zone >= 0) ? "+" : ""; - - paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + - padNumber(Math.abs(zone % 60), 2); - - return paddedZone; -} - -function getFirstThursdayOfYear(year) { - // 0 = index of January - var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay(); - // 4 = index of Thursday (+1 to account for 1st = 5) - // 11 = index of *next* Thursday (+1 account for 1st = 12) - return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst); -} - -function getThursdayThisWeek(datetime) { - return new Date(datetime.getFullYear(), datetime.getMonth(), - // 4 = index of Thursday - datetime.getDate() + (4 - datetime.getDay())); -} - -function weekGetter(size) { - return function(date) { - var firstThurs = getFirstThursdayOfYear(date.getFullYear()), - thisThurs = getThursdayThisWeek(date); - - var diff = +thisThurs - +firstThurs, - result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week - - return padNumber(result, size); - }; -} - -function ampmGetter(date, formats) { - return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; -} - -function eraGetter(date, formats) { - return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1]; -} - -function longEraGetter(date, formats) { - return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1]; -} - -var DATE_FORMATS = { - yyyy: dateGetter('FullYear', 4), - yy: dateGetter('FullYear', 2, 0, true), - y: dateGetter('FullYear', 1), - MMMM: dateStrGetter('Month'), - MMM: dateStrGetter('Month', true), - MM: dateGetter('Month', 2, 1), - M: dateGetter('Month', 1, 1), - dd: dateGetter('Date', 2), - d: dateGetter('Date', 1), - HH: dateGetter('Hours', 2), - H: dateGetter('Hours', 1), - hh: dateGetter('Hours', 2, -12), - h: dateGetter('Hours', 1, -12), - mm: dateGetter('Minutes', 2), - m: dateGetter('Minutes', 1), - ss: dateGetter('Seconds', 2), - s: dateGetter('Seconds', 1), - // while ISO 8601 requires fractions to be prefixed with `.` or `,` - // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions - sss: dateGetter('Milliseconds', 3), - EEEE: dateStrGetter('Day'), - EEE: dateStrGetter('Day', true), - a: ampmGetter, - Z: timeZoneGetter, - ww: weekGetter(2), - w: weekGetter(1), - G: eraGetter, - GG: eraGetter, - GGG: eraGetter, - GGGG: longEraGetter -}; - -var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/, - NUMBER_STRING = /^\-?\d+$/; - -/** - * @ngdoc filter - * @name date - * @kind function - * - * @description - * Formats `date` to a string based on the requested `format`. - * - * `format` string can be composed of the following elements: - * - * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) - * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) - * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) - * * `'MMMM'`: Month in year (January-December) - * * `'MMM'`: Month in year (Jan-Dec) - * * `'MM'`: Month in year, padded (01-12) - * * `'M'`: Month in year (1-12) - * * `'dd'`: Day in month, padded (01-31) - * * `'d'`: Day in month (1-31) - * * `'EEEE'`: Day in Week,(Sunday-Saturday) - * * `'EEE'`: Day in Week, (Sun-Sat) - * * `'HH'`: Hour in day, padded (00-23) - * * `'H'`: Hour in day (0-23) - * * `'hh'`: Hour in AM/PM, padded (01-12) - * * `'h'`: Hour in AM/PM, (1-12) - * * `'mm'`: Minute in hour, padded (00-59) - * * `'m'`: Minute in hour (0-59) - * * `'ss'`: Second in minute, padded (00-59) - * * `'s'`: Second in minute (0-59) - * * `'sss'`: Millisecond in second, padded (000-999) - * * `'a'`: AM/PM marker - * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) - * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year - * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year - * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD') - * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini') - * - * `format` string can also be one of the following predefined - * {@link guide/i18n localizable formats}: - * - * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale - * (e.g. Sep 3, 2010 12:05:08 PM) - * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM) - * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale - * (e.g. Friday, September 3, 2010) - * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010) - * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) - * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) - * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM) - * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM) - * - * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g. - * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence - * (e.g. `"h 'o''clock'"`). - * - * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or - * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its - * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is - * specified in the string input, the time is considered to be in the local timezone. - * @param {string=} format Formatting rules (see Description). If not specified, - * `mediumDate` is used. - * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the - * continental US time zone abbreviations, but for general use, use a time zone offset, for - * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian) - * If not specified, the timezone of the browser will be used. - * @returns {string} Formatted string or the input if input is not recognized as date/millis. - * - * @example - - - {{1288323623006 | date:'medium'}}: - {{1288323623006 | date:'medium'}}
- {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}: - {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}
- {{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}: - {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}
- {{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}: - {{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}
-
- - it('should format date', function() { - expect(element(by.binding("1288323623006 | date:'medium'")).getText()). - toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); - expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()). - toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/); - expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()). - toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); - expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()). - toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/); - }); - -
- */ -dateFilter.$inject = ['$locale']; -function dateFilter($locale) { - - - var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; - // 1 2 3 4 5 6 7 8 9 10 11 - function jsonStringToDate(string) { - var match; - if (match = string.match(R_ISO8601_STR)) { - var date = new Date(0), - tzHour = 0, - tzMin = 0, - dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear, - timeSetter = match[8] ? date.setUTCHours : date.setHours; - - if (match[9]) { - tzHour = toInt(match[9] + match[10]); - tzMin = toInt(match[9] + match[11]); - } - dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3])); - var h = toInt(match[4] || 0) - tzHour; - var m = toInt(match[5] || 0) - tzMin; - var s = toInt(match[6] || 0); - var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000); - timeSetter.call(date, h, m, s, ms); - return date; - } - return string; - } - - - return function(date, format, timezone) { - var text = '', - parts = [], - fn, match; - - format = format || 'mediumDate'; - format = $locale.DATETIME_FORMATS[format] || format; - if (isString(date)) { - date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date); - } - - if (isNumber(date)) { - date = new Date(date); - } - - if (!isDate(date) || !isFinite(date.getTime())) { - return date; - } - - while (format) { - match = DATE_FORMATS_SPLIT.exec(format); - if (match) { - parts = concat(parts, match, 1); - format = parts.pop(); - } else { - parts.push(format); - format = null; - } - } - - var dateTimezoneOffset = date.getTimezoneOffset(); - if (timezone) { - dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset()); - date = convertTimezoneToLocal(date, timezone, true); - } - forEach(parts, function(value) { - fn = DATE_FORMATS[value]; - text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset) - : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); - }); - - return text; - }; -} - - -/** - * @ngdoc filter - * @name json - * @kind function - * - * @description - * Allows you to convert a JavaScript object into JSON string. - * - * This filter is mostly useful for debugging. When using the double curly {{value}} notation - * the binding is automatically converted to JSON. - * - * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. - * @param {number=} spacing The number of spaces to use per indentation, defaults to 2. - * @returns {string} JSON string. - * - * - * @example - - -
{{ {'name':'value'} | json }}
-
{{ {'name':'value'} | json:4 }}
-
- - it('should jsonify filtered objects', function() { - expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/); - expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/); - }); - -
- * - */ -function jsonFilter() { - return function(object, spacing) { - if (isUndefined(spacing)) { - spacing = 2; - } - return toJson(object, spacing); - }; -} - - -/** - * @ngdoc filter - * @name lowercase - * @kind function - * @description - * Converts string to lowercase. - * @see angular.lowercase - */ -var lowercaseFilter = valueFn(lowercase); - - -/** - * @ngdoc filter - * @name uppercase - * @kind function - * @description - * Converts string to uppercase. - * @see angular.uppercase - */ -var uppercaseFilter = valueFn(uppercase); - -/** - * @ngdoc filter - * @name limitTo - * @kind function - * - * @description - * Creates a new array or string containing only a specified number of elements. The elements - * are taken from either the beginning or the end of the source array, string or number, as specified by - * the value and sign (positive or negative) of `limit`. If a number is used as input, it is - * converted to a string. - * - * @param {Array|string|number} input Source array, string or number to be limited. - * @param {string|number} limit The length of the returned array or string. If the `limit` number - * is positive, `limit` number of items from the beginning of the source array/string are copied. - * If the number is negative, `limit` number of items from the end of the source array/string - * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined, - * the input will be returned unchanged. - * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin` - * indicates an offset from the end of `input`. Defaults to `0`. - * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array - * had less than `limit` elements. - * - * @example - - - -
- -

Output numbers: {{ numbers | limitTo:numLimit }}

- -

Output letters: {{ letters | limitTo:letterLimit }}

- -

Output long number: {{ longNumber | limitTo:longNumberLimit }}

-
-
- - var numLimitInput = element(by.model('numLimit')); - var letterLimitInput = element(by.model('letterLimit')); - var longNumberLimitInput = element(by.model('longNumberLimit')); - var limitedNumbers = element(by.binding('numbers | limitTo:numLimit')); - var limitedLetters = element(by.binding('letters | limitTo:letterLimit')); - var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit')); - - it('should limit the number array to first three items', function() { - expect(numLimitInput.getAttribute('value')).toBe('3'); - expect(letterLimitInput.getAttribute('value')).toBe('3'); - expect(longNumberLimitInput.getAttribute('value')).toBe('3'); - expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]'); - expect(limitedLetters.getText()).toEqual('Output letters: abc'); - expect(limitedLongNumber.getText()).toEqual('Output long number: 234'); - }); - - // There is a bug in safari and protractor that doesn't like the minus key - // it('should update the output when -3 is entered', function() { - // numLimitInput.clear(); - // numLimitInput.sendKeys('-3'); - // letterLimitInput.clear(); - // letterLimitInput.sendKeys('-3'); - // longNumberLimitInput.clear(); - // longNumberLimitInput.sendKeys('-3'); - // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]'); - // expect(limitedLetters.getText()).toEqual('Output letters: ghi'); - // expect(limitedLongNumber.getText()).toEqual('Output long number: 342'); - // }); - - it('should not exceed the maximum size of input array', function() { - numLimitInput.clear(); - numLimitInput.sendKeys('100'); - letterLimitInput.clear(); - letterLimitInput.sendKeys('100'); - longNumberLimitInput.clear(); - longNumberLimitInput.sendKeys('100'); - expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]'); - expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi'); - expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342'); - }); - -
-*/ -function limitToFilter() { - return function(input, limit, begin) { - if (Math.abs(Number(limit)) === Infinity) { - limit = Number(limit); - } else { - limit = toInt(limit); - } - if (isNaN(limit)) return input; - - if (isNumber(input)) input = input.toString(); - if (!isArray(input) && !isString(input)) return input; - - begin = (!begin || isNaN(begin)) ? 0 : toInt(begin); - begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin; - - if (limit >= 0) { - return input.slice(begin, begin + limit); - } else { - if (begin === 0) { - return input.slice(limit, input.length); - } else { - return input.slice(Math.max(0, begin + limit), begin); - } - } - }; -} - -/** - * @ngdoc filter - * @name orderBy - * @kind function - * - * @description - * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically - * for strings and numerically for numbers. Note: if you notice numbers are not being sorted - * as expected, make sure they are actually being saved as numbers and not strings. - * - * @param {Array} array The array to sort. - * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be - * used by the comparator to determine the order of elements. - * - * Can be one of: - * - * - `function`: Getter function. The result of this function will be sorted using the - * `<`, `===`, `>` operator. - * - `string`: An Angular expression. The result of this expression is used to compare elements - * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by - * 3 first characters of a property called `name`). The result of a constant expression - * is interpreted as a property name to be used in comparisons (for example `"special name"` - * to sort object by the value of their `special name` property). An expression can be - * optionally prefixed with `+` or `-` to control ascending or descending sort order - * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array - * element itself is used to compare where sorting. - * - `Array`: An array of function or string predicates. The first predicate in the array - * is used for sorting, but when two items are equivalent, the next predicate is used. - * - * If the predicate is missing or empty then it defaults to `'+'`. - * - * @param {boolean=} reverse Reverse the order of the array. - * @returns {Array} Sorted copy of the source array. - * - * - * @example - * The example below demonstrates a simple ngRepeat, where the data is sorted - * by age in descending order (predicate is set to `'-age'`). - * `reverse` is not set, which means it defaults to `false`. - - - -
- - - - - - - - - - - -
NamePhone NumberAge
{{friend.name}}{{friend.phone}}{{friend.age}}
-
-
-
- * - * The predicate and reverse parameters can be controlled dynamically through scope properties, - * as shown in the next example. - * @example - - - - -
-
Sorting predicate = {{predicate}}; reverse = {{reverse}}
-
- [ unsorted ] - - - - - - - - - - - -
- Name - - - Phone Number - - - Age - -
{{friend.name}}{{friend.phone}}{{friend.age}}
-
-
-
- * - * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the - * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the - * desired parameters. - * - * Example: - * - * @example - - -
- - - - - - - - - - - -
Name - (^)Phone NumberAge
{{friend.name}}{{friend.phone}}{{friend.age}}
-
-
- - - angular.module('orderByExample', []) - .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) { - var orderBy = $filter('orderBy'); - $scope.friends = [ - { name: 'John', phone: '555-1212', age: 10 }, - { name: 'Mary', phone: '555-9876', age: 19 }, - { name: 'Mike', phone: '555-4321', age: 21 }, - { name: 'Adam', phone: '555-5678', age: 35 }, - { name: 'Julie', phone: '555-8765', age: 29 } - ]; - $scope.order = function(predicate, reverse) { - $scope.friends = orderBy($scope.friends, predicate, reverse); - }; - $scope.order('-age',false); - }]); - -
- */ -orderByFilter.$inject = ['$parse']; -function orderByFilter($parse) { - return function(array, sortPredicate, reverseOrder) { - - if (!(isArrayLike(array))) return array; - - if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; } - if (sortPredicate.length === 0) { sortPredicate = ['+']; } - - var predicates = processPredicates(sortPredicate, reverseOrder); - // Add a predicate at the end that evaluates to the element index. This makes the - // sort stable as it works as a tie-breaker when all the input predicates cannot - // distinguish between two elements. - predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1}); - - // The next three lines are a version of a Swartzian Transform idiom from Perl - // (sometimes called the Decorate-Sort-Undecorate idiom) - // See https://en.wikipedia.org/wiki/Schwartzian_transform - var compareValues = Array.prototype.map.call(array, getComparisonObject); - compareValues.sort(doComparison); - array = compareValues.map(function(item) { return item.value; }); - - return array; - - function getComparisonObject(value, index) { - return { - value: value, - predicateValues: predicates.map(function(predicate) { - return getPredicateValue(predicate.get(value), index); - }) - }; - } - - function doComparison(v1, v2) { - var result = 0; - for (var index=0, length = predicates.length; index < length; ++index) { - result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending; - if (result) break; - } - return result; - } - }; - - function processPredicates(sortPredicate, reverseOrder) { - reverseOrder = reverseOrder ? -1 : 1; - return sortPredicate.map(function(predicate) { - var descending = 1, get = identity; - - if (isFunction(predicate)) { - get = predicate; - } else if (isString(predicate)) { - if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { - descending = predicate.charAt(0) == '-' ? -1 : 1; - predicate = predicate.substring(1); - } - if (predicate !== '') { - get = $parse(predicate); - if (get.constant) { - var key = get(); - get = function(value) { return value[key]; }; - } - } - } - return { get: get, descending: descending * reverseOrder }; - }); - } - - function isPrimitive(value) { - switch (typeof value) { - case 'number': /* falls through */ - case 'boolean': /* falls through */ - case 'string': - return true; - default: - return false; - } - } - - function objectValue(value, index) { - // If `valueOf` is a valid function use that - if (typeof value.valueOf === 'function') { - value = value.valueOf(); - if (isPrimitive(value)) return value; - } - // If `toString` is a valid function and not the one from `Object.prototype` use that - if (hasCustomToString(value)) { - value = value.toString(); - if (isPrimitive(value)) return value; - } - // We have a basic object so we use the position of the object in the collection - return index; - } - - function getPredicateValue(value, index) { - var type = typeof value; - if (value === null) { - type = 'string'; - value = 'null'; - } else if (type === 'string') { - value = value.toLowerCase(); - } else if (type === 'object') { - value = objectValue(value, index); - } - return { value: value, type: type }; - } - - function compare(v1, v2) { - var result = 0; - if (v1.type === v2.type) { - if (v1.value !== v2.value) { - result = v1.value < v2.value ? -1 : 1; - } - } else { - result = v1.type < v2.type ? -1 : 1; - } - return result; - } -} - -function ngDirective(directive) { - if (isFunction(directive)) { - directive = { - link: directive - }; - } - directive.restrict = directive.restrict || 'AC'; - return valueFn(directive); -} - -/** - * @ngdoc directive - * @name a - * @restrict E - * - * @description - * Modifies the default behavior of the html A tag so that the default action is prevented when - * the href attribute is empty. - * - * This change permits the easy creation of action links with the `ngClick` directive - * without changing the location or causing page reloads, e.g.: - * `Add Item` - */ -var htmlAnchorDirective = valueFn({ - restrict: 'E', - compile: function(element, attr) { - if (!attr.href && !attr.xlinkHref) { - return function(scope, element) { - // If the linked element is not an anchor tag anymore, do nothing - if (element[0].nodeName.toLowerCase() !== 'a') return; - - // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. - var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? - 'xlink:href' : 'href'; - element.on('click', function(event) { - // if we have no href url, then don't navigate anywhere. - if (!element.attr(href)) { - event.preventDefault(); - } - }); - }; - } - } -}); - -/** - * @ngdoc directive - * @name ngHref - * @restrict A - * @priority 99 - * - * @description - * Using Angular markup like `{{hash}}` in an href attribute will - * make the link go to the wrong URL if the user clicks it before - * Angular has a chance to replace the `{{hash}}` markup with its - * value. Until Angular replaces the markup the link will be broken - * and will most likely return a 404 error. The `ngHref` directive - * solves this problem. - * - * The wrong way to write it: - * ```html - * link1 - * ``` - * - * The correct way to write it: - * ```html - * link1 - * ``` - * - * @element A - * @param {template} ngHref any string which can contain `{{}}` markup. - * - * @example - * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes - * in links and their different behaviors: - - -
- link 1 (link, don't reload)
- link 2 (link, don't reload)
- link 3 (link, reload!)
- anchor (link, don't reload)
- anchor (no link)
- link (link, change location) -
- - it('should execute ng-click but not reload when href without value', function() { - element(by.id('link-1')).click(); - expect(element(by.model('value')).getAttribute('value')).toEqual('1'); - expect(element(by.id('link-1')).getAttribute('href')).toBe(''); - }); - - it('should execute ng-click but not reload when href empty string', function() { - element(by.id('link-2')).click(); - expect(element(by.model('value')).getAttribute('value')).toEqual('2'); - expect(element(by.id('link-2')).getAttribute('href')).toBe(''); - }); - - it('should execute ng-click and change url when ng-href specified', function() { - expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/); - - element(by.id('link-3')).click(); - - // At this point, we navigate away from an Angular page, so we need - // to use browser.driver to get the base webdriver. - - browser.wait(function() { - return browser.driver.getCurrentUrl().then(function(url) { - return url.match(/\/123$/); - }); - }, 5000, 'page should navigate to /123'); - }); - - it('should execute ng-click but not reload when href empty string and name specified', function() { - element(by.id('link-4')).click(); - expect(element(by.model('value')).getAttribute('value')).toEqual('4'); - expect(element(by.id('link-4')).getAttribute('href')).toBe(''); - }); - - it('should execute ng-click but not reload when no href but name specified', function() { - element(by.id('link-5')).click(); - expect(element(by.model('value')).getAttribute('value')).toEqual('5'); - expect(element(by.id('link-5')).getAttribute('href')).toBe(null); - }); - - it('should only change url when only ng-href', function() { - element(by.model('value')).clear(); - element(by.model('value')).sendKeys('6'); - expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/); - - element(by.id('link-6')).click(); - - // At this point, we navigate away from an Angular page, so we need - // to use browser.driver to get the base webdriver. - browser.wait(function() { - return browser.driver.getCurrentUrl().then(function(url) { - return url.match(/\/6$/); - }); - }, 5000, 'page should navigate to /6'); - }); - -
- */ - -/** - * @ngdoc directive - * @name ngSrc - * @restrict A - * @priority 99 - * - * @description - * Using Angular markup like `{{hash}}` in a `src` attribute doesn't - * work right: The browser will fetch from the URL with the literal - * text `{{hash}}` until Angular replaces the expression inside - * `{{hash}}`. The `ngSrc` directive solves this problem. - * - * The buggy way to write it: - * ```html - * Description - * ``` - * - * The correct way to write it: - * ```html - * Description - * ``` - * - * @element IMG - * @param {template} ngSrc any string which can contain `{{}}` markup. - */ - -/** - * @ngdoc directive - * @name ngSrcset - * @restrict A - * @priority 99 - * - * @description - * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't - * work right: The browser will fetch from the URL with the literal - * text `{{hash}}` until Angular replaces the expression inside - * `{{hash}}`. The `ngSrcset` directive solves this problem. - * - * The buggy way to write it: - * ```html - * Description - * ``` - * - * The correct way to write it: - * ```html - * Description - * ``` - * - * @element IMG - * @param {template} ngSrcset any string which can contain `{{}}` markup. - */ - -/** - * @ngdoc directive - * @name ngDisabled - * @restrict A - * @priority 100 - * - * @description - * - * This directive sets the `disabled` attribute on the element if the - * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy. - * - * A special directive is necessary because we cannot use interpolation inside the `disabled` - * attribute. The following example would make the button enabled on Chrome/Firefox - * but not on older IEs: - * - * ```html - * - *
- * - *
- * ``` - * - * This is because the HTML specification does not require browsers to preserve the values of - * boolean attributes such as `disabled` (Their presence means true and their absence means false.) - * If we put an Angular interpolation expression into such an attribute then the - * binding information would be lost when the browser removes the attribute. - * - * @example - - -
- -
- - it('should toggle button', function() { - expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy(); - element(by.model('checked')).click(); - expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy(); - }); - -
- * - * @element INPUT - * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy, - * then the `disabled` attribute will be set on the element - */ - - -/** - * @ngdoc directive - * @name ngChecked - * @restrict A - * @priority 100 - * - * @description - * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy. - * - * Note that this directive should not be used together with {@link ngModel `ngModel`}, - * as this can lead to unexpected behavior. - * - * ### Why do we need `ngChecked`? - * - * The HTML specification does not require browsers to preserve the values of boolean attributes - * such as checked. (Their presence means true and their absence means false.) - * If we put an Angular interpolation expression into such an attribute then the - * binding information would be lost when the browser removes the attribute. - * The `ngChecked` directive solves this problem for the `checked` attribute. - * This complementary directive is not removed by the browser and so provides - * a permanent reliable place to store the binding information. - * @example - - -
- -
- - it('should check both checkBoxes', function() { - expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy(); - element(by.model('master')).click(); - expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy(); - }); - -
- * - * @element INPUT - * @param {expression} ngChecked If the {@link guide/expression expression} is truthy, - * then the `checked` attribute will be set on the element - */ - - -/** - * @ngdoc directive - * @name ngReadonly - * @restrict A - * @priority 100 - * - * @description - * The HTML specification does not require browsers to preserve the values of boolean attributes - * such as readonly. (Their presence means true and their absence means false.) - * If we put an Angular interpolation expression into such an attribute then the - * binding information would be lost when the browser removes the attribute. - * The `ngReadonly` directive solves this problem for the `readonly` attribute. - * This complementary directive is not removed by the browser and so provides - * a permanent reliable place to store the binding information. - * @example - - -
- -
- - it('should toggle readonly attr', function() { - expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy(); - element(by.model('checked')).click(); - expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy(); - }); - -
- * - * @element INPUT - * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy, - * then special attribute "readonly" will be set on the element - */ - - -/** - * @ngdoc directive - * @name ngSelected - * @restrict A - * @priority 100 - * - * @description - * The HTML specification does not require browsers to preserve the values of boolean attributes - * such as selected. (Their presence means true and their absence means false.) - * If we put an Angular interpolation expression into such an attribute then the - * binding information would be lost when the browser removes the attribute. - * The `ngSelected` directive solves this problem for the `selected` attribute. - * This complementary directive is not removed by the browser and so provides - * a permanent reliable place to store the binding information. - * - * @example - - -
- -
- - it('should select Greetings!', function() { - expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy(); - element(by.model('selected')).click(); - expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy(); - }); - -
- * - * @element OPTION - * @param {expression} ngSelected If the {@link guide/expression expression} is truthy, - * then special attribute "selected" will be set on the element - */ - -/** - * @ngdoc directive - * @name ngOpen - * @restrict A - * @priority 100 - * - * @description - * The HTML specification does not require browsers to preserve the values of boolean attributes - * such as open. (Their presence means true and their absence means false.) - * If we put an Angular interpolation expression into such an attribute then the - * binding information would be lost when the browser removes the attribute. - * The `ngOpen` directive solves this problem for the `open` attribute. - * This complementary directive is not removed by the browser and so provides - * a permanent reliable place to store the binding information. - * @example - - -
-
- Show/Hide me -
-
- - it('should toggle open', function() { - expect(element(by.id('details')).getAttribute('open')).toBeFalsy(); - element(by.model('open')).click(); - expect(element(by.id('details')).getAttribute('open')).toBeTruthy(); - }); - -
- * - * @element DETAILS - * @param {expression} ngOpen If the {@link guide/expression expression} is truthy, - * then special attribute "open" will be set on the element - */ - -var ngAttributeAliasDirectives = {}; - -// boolean attrs are evaluated -forEach(BOOLEAN_ATTR, function(propName, attrName) { - // binding to multiple is not supported - if (propName == "multiple") return; - - function defaultLinkFn(scope, element, attr) { - scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { - attr.$set(attrName, !!value); - }); - } - - var normalized = directiveNormalize('ng-' + attrName); - var linkFn = defaultLinkFn; - - if (propName === 'checked') { - linkFn = function(scope, element, attr) { - // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input - if (attr.ngModel !== attr[normalized]) { - defaultLinkFn(scope, element, attr); - } - }; - } - - ngAttributeAliasDirectives[normalized] = function() { - return { - restrict: 'A', - priority: 100, - link: linkFn - }; - }; -}); - -// aliased input attrs are evaluated -forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) { - ngAttributeAliasDirectives[ngAttr] = function() { - return { - priority: 100, - link: function(scope, element, attr) { - //special case ngPattern when a literal regular expression value - //is used as the expression (this way we don't have to watch anything). - if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") { - var match = attr.ngPattern.match(REGEX_STRING_REGEXP); - if (match) { - attr.$set("ngPattern", new RegExp(match[1], match[2])); - return; - } - } - - scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) { - attr.$set(ngAttr, value); - }); - } - }; - }; -}); - -// ng-src, ng-srcset, ng-href are interpolated -forEach(['src', 'srcset', 'href'], function(attrName) { - var normalized = directiveNormalize('ng-' + attrName); - ngAttributeAliasDirectives[normalized] = function() { - return { - priority: 99, // it needs to run after the attributes are interpolated - link: function(scope, element, attr) { - var propName = attrName, - name = attrName; - - if (attrName === 'href' && - toString.call(element.prop('href')) === '[object SVGAnimatedString]') { - name = 'xlinkHref'; - attr.$attr[name] = 'xlink:href'; - propName = null; - } - - attr.$observe(normalized, function(value) { - if (!value) { - if (attrName === 'href') { - attr.$set(name, null); - } - return; - } - - attr.$set(name, value); - - // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist - // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need - // to set the property as well to achieve the desired effect. - // we use attr[attrName] value since $set can sanitize the url. - if (msie && propName) element.prop(propName, attr[name]); - }); - } - }; - }; -}); - -/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true - */ -var nullFormCtrl = { - $addControl: noop, - $$renameControl: nullFormRenameControl, - $removeControl: noop, - $setValidity: noop, - $setDirty: noop, - $setPristine: noop, - $setSubmitted: noop -}, -SUBMITTED_CLASS = 'ng-submitted'; - -function nullFormRenameControl(control, name) { - control.$name = name; -} - -/** - * @ngdoc type - * @name form.FormController - * - * @property {boolean} $pristine True if user has not interacted with the form yet. - * @property {boolean} $dirty True if user has already interacted with the form. - * @property {boolean} $valid True if all of the containing forms and controls are valid. - * @property {boolean} $invalid True if at least one containing control or form is invalid. - * @property {boolean} $submitted True if user has submitted the form even if its invalid. - * - * @property {Object} $error Is an object hash, containing references to controls or - * forms with failing validators, where: - * - * - keys are validation tokens (error names), - * - values are arrays of controls or forms that have a failing validator for given error name. - * - * Built-in validation tokens: - * - * - `email` - * - `max` - * - `maxlength` - * - `min` - * - `minlength` - * - `number` - * - `pattern` - * - `required` - * - `url` - * - `date` - * - `datetimelocal` - * - `time` - * - `week` - * - `month` - * - * @description - * `FormController` keeps track of all its controls and nested forms as well as the state of them, - * such as being valid/invalid or dirty/pristine. - * - * Each {@link ng.directive:form form} directive creates an instance - * of `FormController`. - * - */ -//asks for $scope to fool the BC controller module -FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate']; -function FormController(element, attrs, $scope, $animate, $interpolate) { - var form = this, - controls = []; - - var parentForm = form.$$parentForm = element.parent().controller('form') || nullFormCtrl; - - // init state - form.$error = {}; - form.$$success = {}; - form.$pending = undefined; - form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope); - form.$dirty = false; - form.$pristine = true; - form.$valid = true; - form.$invalid = false; - form.$submitted = false; - - parentForm.$addControl(form); - - /** - * @ngdoc method - * @name form.FormController#$rollbackViewValue - * - * @description - * Rollback all form controls pending updates to the `$modelValue`. - * - * Updates may be pending by a debounced event or because the input is waiting for a some future - * event defined in `ng-model-options`. This method is typically needed by the reset button of - * a form that uses `ng-model-options` to pend updates. - */ - form.$rollbackViewValue = function() { - forEach(controls, function(control) { - control.$rollbackViewValue(); - }); - }; - - /** - * @ngdoc method - * @name form.FormController#$commitViewValue - * - * @description - * Commit all form controls pending updates to the `$modelValue`. - * - * Updates may be pending by a debounced event or because the input is waiting for a some future - * event defined in `ng-model-options`. This method is rarely needed as `NgModelController` - * usually handles calling this in response to input events. - */ - form.$commitViewValue = function() { - forEach(controls, function(control) { - control.$commitViewValue(); - }); - }; - - /** - * @ngdoc method - * @name form.FormController#$addControl - * - * @description - * Register a control with the form. - * - * Input elements using ngModelController do this automatically when they are linked. - */ - form.$addControl = function(control) { - // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored - // and not added to the scope. Now we throw an error. - assertNotHasOwnProperty(control.$name, 'input'); - controls.push(control); - - if (control.$name) { - form[control.$name] = control; - } - }; - - // Private API: rename a form control - form.$$renameControl = function(control, newName) { - var oldName = control.$name; - - if (form[oldName] === control) { - delete form[oldName]; - } - form[newName] = control; - control.$name = newName; - }; - - /** - * @ngdoc method - * @name form.FormController#$removeControl - * - * @description - * Deregister a control from the form. - * - * Input elements using ngModelController do this automatically when they are destroyed. - */ - form.$removeControl = function(control) { - if (control.$name && form[control.$name] === control) { - delete form[control.$name]; - } - forEach(form.$pending, function(value, name) { - form.$setValidity(name, null, control); - }); - forEach(form.$error, function(value, name) { - form.$setValidity(name, null, control); - }); - forEach(form.$$success, function(value, name) { - form.$setValidity(name, null, control); - }); - - arrayRemove(controls, control); - }; - - - /** - * @ngdoc method - * @name form.FormController#$setValidity - * - * @description - * Sets the validity of a form control. - * - * This method will also propagate to parent forms. - */ - addSetValidityMethod({ - ctrl: this, - $element: element, - set: function(object, property, controller) { - var list = object[property]; - if (!list) { - object[property] = [controller]; - } else { - var index = list.indexOf(controller); - if (index === -1) { - list.push(controller); - } - } - }, - unset: function(object, property, controller) { - var list = object[property]; - if (!list) { - return; - } - arrayRemove(list, controller); - if (list.length === 0) { - delete object[property]; - } - }, - parentForm: parentForm, - $animate: $animate - }); - - /** - * @ngdoc method - * @name form.FormController#$setDirty - * - * @description - * Sets the form to a dirty state. - * - * This method can be called to add the 'ng-dirty' class and set the form to a dirty - * state (ng-dirty class). This method will also propagate to parent forms. - */ - form.$setDirty = function() { - $animate.removeClass(element, PRISTINE_CLASS); - $animate.addClass(element, DIRTY_CLASS); - form.$dirty = true; - form.$pristine = false; - parentForm.$setDirty(); - }; - - /** - * @ngdoc method - * @name form.FormController#$setPristine - * - * @description - * Sets the form to its pristine state. - * - * This method can be called to remove the 'ng-dirty' class and set the form to its pristine - * state (ng-pristine class). This method will also propagate to all the controls contained - * in this form. - * - * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after - * saving or resetting it. - */ - form.$setPristine = function() { - $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS); - form.$dirty = false; - form.$pristine = true; - form.$submitted = false; - forEach(controls, function(control) { - control.$setPristine(); - }); - }; - - /** - * @ngdoc method - * @name form.FormController#$setUntouched - * - * @description - * Sets the form to its untouched state. - * - * This method can be called to remove the 'ng-touched' class and set the form controls to their - * untouched state (ng-untouched class). - * - * Setting a form controls back to their untouched state is often useful when setting the form - * back to its pristine state. - */ - form.$setUntouched = function() { - forEach(controls, function(control) { - control.$setUntouched(); - }); - }; - - /** - * @ngdoc method - * @name form.FormController#$setSubmitted - * - * @description - * Sets the form to its submitted state. - */ - form.$setSubmitted = function() { - $animate.addClass(element, SUBMITTED_CLASS); - form.$submitted = true; - parentForm.$setSubmitted(); - }; -} - -/** - * @ngdoc directive - * @name ngForm - * @restrict EAC - * - * @description - * Nestable alias of {@link ng.directive:form `form`} directive. HTML - * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a - * sub-group of controls needs to be determined. - * - * Note: the purpose of `ngForm` is to group controls, - * but not to be a replacement for the `
` tag with all of its capabilities - * (e.g. posting to the server, ...). - * - * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into - * related scope, under this name. - * - */ - - /** - * @ngdoc directive - * @name form - * @restrict E - * - * @description - * Directive that instantiates - * {@link form.FormController FormController}. - * - * If the `name` attribute is specified, the form controller is published onto the current scope under - * this name. - * - * # Alias: {@link ng.directive:ngForm `ngForm`} - * - * In Angular, forms can be nested. This means that the outer form is valid when all of the child - * forms are valid as well. However, browsers do not allow nesting of `` elements, so - * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to - * `` but can be nested. This allows you to have nested forms, which is very useful when - * using Angular validation directives in forms that are dynamically generated using the - * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name` - * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an - * `ngForm` directive and nest these in an outer `form` element. - * - * - * # CSS classes - * - `ng-valid` is set if the form is valid. - * - `ng-invalid` is set if the form is invalid. - * - `ng-pristine` is set if the form is pristine. - * - `ng-dirty` is set if the form is dirty. - * - `ng-submitted` is set if the form was submitted. - * - * Keep in mind that ngAnimate can detect each of these classes when added and removed. - * - * - * # Submitting a form and preventing the default action - * - * Since the role of forms in client-side Angular applications is different than in classical - * roundtrip apps, it is desirable for the browser not to translate the form submission into a full - * page reload that sends the data to the server. Instead some javascript logic should be triggered - * to handle the form submission in an application-specific way. - * - * For this reason, Angular prevents the default action (form submission to the server) unless the - * `` element has an `action` attribute specified. - * - * You can use one of the following two ways to specify what javascript method should be called when - * a form is submitted: - * - * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element - * - {@link ng.directive:ngClick ngClick} directive on the first - * button or input field of type submit (input[type=submit]) - * - * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit} - * or {@link ng.directive:ngClick ngClick} directives. - * This is because of the following form submission rules in the HTML specification: - * - * - If a form has only one input field then hitting enter in this field triggers form submit - * (`ngSubmit`) - * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter - * doesn't trigger submit - * - if a form has one or more input fields and one or more buttons or input[type=submit] then - * hitting enter in any of the input fields will trigger the click handler on the *first* button or - * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) - * - * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is - * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit` - * to have access to the updated model. - * - * ## Animation Hooks - * - * Animations in ngForm are triggered when any of the associated CSS classes are added and removed. - * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any - * other validations that are performed within the form. Animations in ngForm are similar to how - * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well - * as JS animations. - * - * The following example shows a simple way to utilize CSS transitions to style a form element - * that has been rendered as invalid after it has been validated: - * - *
- * //be sure to include ngAnimate as a module to hook into more
- * //advanced animations
- * .my-form {
- *   transition:0.5s linear all;
- *   background: white;
- * }
- * .my-form.ng-invalid {
- *   background: red;
- *   color:white;
- * }
- * 
- * - * @example - - - - - - userType: - Required!
- userType = {{userType}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
- -
- - it('should initialize to model', function() { - var userType = element(by.binding('userType')); - var valid = element(by.binding('myForm.input.$valid')); - - expect(userType.getText()).toContain('guest'); - expect(valid.getText()).toContain('true'); - }); - - it('should be invalid if empty', function() { - var userType = element(by.binding('userType')); - var valid = element(by.binding('myForm.input.$valid')); - var userInput = element(by.model('userType')); - - userInput.clear(); - userInput.sendKeys(''); - - expect(userType.getText()).toEqual('userType ='); - expect(valid.getText()).toContain('false'); - }); - -
- * - * @param {string=} name Name of the form. If specified, the form controller will be published into - * related scope, under this name. - */ -var formDirectiveFactory = function(isNgForm) { - return ['$timeout', '$parse', function($timeout, $parse) { - var formDirective = { - name: 'form', - restrict: isNgForm ? 'EAC' : 'E', - controller: FormController, - compile: function ngFormCompile(formElement, attr) { - // Setup initial state of the control - formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS); - - var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false); - - return { - pre: function ngFormPreLink(scope, formElement, attr, controller) { - // if `action` attr is not present on the form, prevent the default action (submission) - if (!('action' in attr)) { - // we can't use jq events because if a form is destroyed during submission the default - // action is not prevented. see #1238 - // - // IE 9 is not affected because it doesn't fire a submit event and try to do a full - // page reload if the form was destroyed by submission of the form via a click handler - // on a button in the form. Looks like an IE9 specific bug. - var handleFormSubmission = function(event) { - scope.$apply(function() { - controller.$commitViewValue(); - controller.$setSubmitted(); - }); - - event.preventDefault(); - }; - - addEventListenerFn(formElement[0], 'submit', handleFormSubmission); - - // unregister the preventDefault listener so that we don't not leak memory but in a - // way that will achieve the prevention of the default action. - formElement.on('$destroy', function() { - $timeout(function() { - removeEventListenerFn(formElement[0], 'submit', handleFormSubmission); - }, 0, false); - }); - } - - var parentFormCtrl = controller.$$parentForm; - var setter = nameAttr ? getSetter(controller.$name) : noop; - - if (nameAttr) { - setter(scope, controller); - attr.$observe(nameAttr, function(newValue) { - if (controller.$name === newValue) return; - setter(scope, undefined); - parentFormCtrl.$$renameControl(controller, newValue); - setter = getSetter(controller.$name); - setter(scope, controller); - }); - } - formElement.on('$destroy', function() { - parentFormCtrl.$removeControl(controller); - setter(scope, undefined); - extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards - }); - } - }; - } - }; - - return formDirective; - - function getSetter(expression) { - if (expression === '') { - //create an assignable expression, so forms with an empty name can be renamed later - return $parse('this[""]').assign; - } - return $parse(expression).assign || noop; - } - }]; -}; - -var formDirective = formDirectiveFactory(); -var ngFormDirective = formDirectiveFactory(true); - -/* global VALID_CLASS: false, - INVALID_CLASS: false, - PRISTINE_CLASS: false, - DIRTY_CLASS: false, - UNTOUCHED_CLASS: false, - TOUCHED_CLASS: false, - ngModelMinErr: false, -*/ - -// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 -var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/; -var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; -var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; -var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/; -var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/; -var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; -var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/; -var MONTH_REGEXP = /^(\d{4})-(\d\d)$/; -var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; - -var inputType = { - - /** - * @ngdoc input - * @name input[text] - * - * @description - * Standard HTML text input with angular data binding, inherited by most of the `input` elements. - * - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Adds `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of - * any length. - * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string - * that contains the regular expression body that will be converted to a regular expression - * as in the ngPattern directive. - * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match - * a RegExp found by evaluating the Angular expression given in the attribute value. - * If the expression evaluates to a RegExp object, then this is used directly. - * If the expression evaluates to a string, then it will be converted to a RegExp - * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to - * `new RegExp('^abc$')`.
- * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to - * start at the index of the last search's match, thus not taking the whole input value into - * account. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. - * This parameter is ignored for input[type=password] controls, which will never trim the - * input. - * - * @example - - - -
- -
- - Required! - - Single word only! -
- text = {{example.text}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
-
-
- - var text = element(by.binding('example.text')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.text')); - - it('should initialize to model', function() { - expect(text.getText()).toContain('guest'); - expect(valid.getText()).toContain('true'); - }); - - it('should be invalid if empty', function() { - input.clear(); - input.sendKeys(''); - - expect(text.getText()).toEqual('text ='); - expect(valid.getText()).toContain('false'); - }); - - it('should be invalid if multi word', function() { - input.clear(); - input.sendKeys('hello world'); - - expect(valid.getText()).toContain('false'); - }); - -
- */ - 'text': textInputType, - - /** - * @ngdoc input - * @name input[date] - * - * @description - * Input with date validation and transformation. In browsers that do not yet support - * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601 - * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many - * modern browsers do not yet support this input type, it is important to provide cues to users on the - * expected input format via a placeholder or label. - * - * The model must always be a Date object, otherwise Angular will throw an error. - * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. - * - * The timezone to be used to read/write the `Date` instance in the model can be defined using - * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a - * valid ISO date string (yyyy-MM-dd). - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be - * a valid ISO date string (yyyy-MM-dd). - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
- - -
- - Required! - - Not a valid date! -
- value = {{example.value | date: "yyyy-MM-dd"}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
-
-
- - var value = element(by.binding('example.value | date: "yyyy-MM-dd"')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.value')); - - // currently protractor/webdriver does not support - // sending keys to all known HTML5 input controls - // for various browsers (see https://github.com/angular/protractor/issues/562). - function setInput(val) { - // set the value of the element and force validation. - var scr = "var ipt = document.getElementById('exampleInput'); " + - "ipt.value = '" + val + "';" + - "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; - browser.executeScript(scr); - } - - it('should initialize to model', function() { - expect(value.getText()).toContain('2013-10-22'); - expect(valid.getText()).toContain('myForm.input.$valid = true'); - }); - - it('should be invalid if empty', function() { - setInput(''); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - - it('should be invalid if over max', function() { - setInput('2015-01-01'); - expect(value.getText()).toContain(''); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - -
- */ - 'date': createDateInputType('date', DATE_REGEXP, - createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']), - 'yyyy-MM-dd'), - - /** - * @ngdoc input - * @name input[datetime-local] - * - * @description - * Input with datetime validation and transformation. In browsers that do not yet support - * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 - * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`. - * - * The model must always be a Date object, otherwise Angular will throw an error. - * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. - * - * The timezone to be used to read/write the `Date` instance in the model can be defined using - * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a - * valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be - * a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
- - -
- - Required! - - Not a valid date! -
- value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
-
-
- - var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.value')); - - // currently protractor/webdriver does not support - // sending keys to all known HTML5 input controls - // for various browsers (https://github.com/angular/protractor/issues/562). - function setInput(val) { - // set the value of the element and force validation. - var scr = "var ipt = document.getElementById('exampleInput'); " + - "ipt.value = '" + val + "';" + - "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; - browser.executeScript(scr); - } - - it('should initialize to model', function() { - expect(value.getText()).toContain('2010-12-28T14:57:00'); - expect(valid.getText()).toContain('myForm.input.$valid = true'); - }); - - it('should be invalid if empty', function() { - setInput(''); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - - it('should be invalid if over max', function() { - setInput('2015-01-01T23:59:00'); - expect(value.getText()).toContain(''); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - -
- */ - 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP, - createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']), - 'yyyy-MM-ddTHH:mm:ss.sss'), - - /** - * @ngdoc input - * @name input[time] - * - * @description - * Input with time validation and transformation. In browsers that do not yet support - * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 - * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a - * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`. - * - * The model must always be a Date object, otherwise Angular will throw an error. - * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. - * - * The timezone to be used to read/write the `Date` instance in the model can be defined using - * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a - * valid ISO time format (HH:mm:ss). - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be a - * valid ISO time format (HH:mm:ss). - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
- - -
- - Required! - - Not a valid date! -
- value = {{example.value | date: "HH:mm:ss"}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
-
-
- - var value = element(by.binding('example.value | date: "HH:mm:ss"')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.value')); - - // currently protractor/webdriver does not support - // sending keys to all known HTML5 input controls - // for various browsers (https://github.com/angular/protractor/issues/562). - function setInput(val) { - // set the value of the element and force validation. - var scr = "var ipt = document.getElementById('exampleInput'); " + - "ipt.value = '" + val + "';" + - "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; - browser.executeScript(scr); - } - - it('should initialize to model', function() { - expect(value.getText()).toContain('14:57:00'); - expect(valid.getText()).toContain('myForm.input.$valid = true'); - }); - - it('should be invalid if empty', function() { - setInput(''); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - - it('should be invalid if over max', function() { - setInput('23:59:00'); - expect(value.getText()).toContain(''); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - -
- */ - 'time': createDateInputType('time', TIME_REGEXP, - createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']), - 'HH:mm:ss.sss'), - - /** - * @ngdoc input - * @name input[week] - * - * @description - * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support - * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 - * week format (yyyy-W##), for example: `2013-W02`. - * - * The model must always be a Date object, otherwise Angular will throw an error. - * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. - * - * The timezone to be used to read/write the `Date` instance in the model can be defined using - * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a - * valid ISO week format (yyyy-W##). - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be - * a valid ISO week format (yyyy-W##). - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
- -
- - Required! - - Not a valid date! -
- value = {{example.value | date: "yyyy-Www"}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
-
-
- - var value = element(by.binding('example.value | date: "yyyy-Www"')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.value')); - - // currently protractor/webdriver does not support - // sending keys to all known HTML5 input controls - // for various browsers (https://github.com/angular/protractor/issues/562). - function setInput(val) { - // set the value of the element and force validation. - var scr = "var ipt = document.getElementById('exampleInput'); " + - "ipt.value = '" + val + "';" + - "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; - browser.executeScript(scr); - } - - it('should initialize to model', function() { - expect(value.getText()).toContain('2013-W01'); - expect(valid.getText()).toContain('myForm.input.$valid = true'); - }); - - it('should be invalid if empty', function() { - setInput(''); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - - it('should be invalid if over max', function() { - setInput('2015-W01'); - expect(value.getText()).toContain(''); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - -
- */ - 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'), - - /** - * @ngdoc input - * @name input[month] - * - * @description - * Input with month validation and transformation. In browsers that do not yet support - * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 - * month format (yyyy-MM), for example: `2009-01`. - * - * The model must always be a Date object, otherwise Angular will throw an error. - * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. - * If the model is not set to the first of the month, the next view to model update will set it - * to the first of the month. - * - * The timezone to be used to read/write the `Date` instance in the model can be defined using - * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be - * a valid ISO month format (yyyy-MM). - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must - * be a valid ISO month format (yyyy-MM). - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
- - -
- - Required! - - Not a valid month! -
- value = {{example.value | date: "yyyy-MM"}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
-
-
- - var value = element(by.binding('example.value | date: "yyyy-MM"')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.value')); - - // currently protractor/webdriver does not support - // sending keys to all known HTML5 input controls - // for various browsers (https://github.com/angular/protractor/issues/562). - function setInput(val) { - // set the value of the element and force validation. - var scr = "var ipt = document.getElementById('exampleInput'); " + - "ipt.value = '" + val + "';" + - "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; - browser.executeScript(scr); - } - - it('should initialize to model', function() { - expect(value.getText()).toContain('2013-10'); - expect(valid.getText()).toContain('myForm.input.$valid = true'); - }); - - it('should be invalid if empty', function() { - setInput(''); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - - it('should be invalid if over max', function() { - setInput('2015-01'); - expect(value.getText()).toContain(''); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - -
- */ - 'month': createDateInputType('month', MONTH_REGEXP, - createDateParser(MONTH_REGEXP, ['yyyy', 'MM']), - 'yyyy-MM'), - - /** - * @ngdoc input - * @name input[number] - * - * @description - * Text input with number validation and transformation. Sets the `number` validation - * error if not a valid number. - * - *
- * The model must always be of type `number` otherwise Angular will throw an error. - * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt} - * error docs for more information and an example of how to convert your model if necessary. - *
- * - * ## Issues with HTML5 constraint validation - * - * In browsers that follow the - * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29), - * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}. - * If a non-number is entered in the input, the browser will report the value as an empty string, - * which means the view / model values in `ngModel` and subsequently the scope value - * will also be an empty string. - * - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of - * any length. - * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string - * that contains the regular expression body that will be converted to a regular expression - * as in the ngPattern directive. - * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match - * a RegExp found by evaluating the Angular expression given in the attribute value. - * If the expression evaluates to a RegExp object, then this is used directly. - * If the expression evaluates to a string, then it will be converted to a RegExp - * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to - * `new RegExp('^abc$')`.
- * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to - * start at the index of the last search's match, thus not taking the whole input value into - * account. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
- -
- - Required! - - Not valid number! -
- value = {{example.value}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
-
-
- - var value = element(by.binding('example.value')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.value')); - - it('should initialize to model', function() { - expect(value.getText()).toContain('12'); - expect(valid.getText()).toContain('true'); - }); - - it('should be invalid if empty', function() { - input.clear(); - input.sendKeys(''); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('false'); - }); - - it('should be invalid if over max', function() { - input.clear(); - input.sendKeys('123'); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('false'); - }); - -
- */ - 'number': numberInputType, - - - /** - * @ngdoc input - * @name input[url] - * - * @description - * Text input with URL validation. Sets the `url` validation error key if the content is not a - * valid URL. - * - *
- * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex - * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify - * the built-in validators (see the {@link guide/forms Forms guide}) - *
- * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of - * any length. - * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string - * that contains the regular expression body that will be converted to a regular expression - * as in the ngPattern directive. - * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match - * a RegExp found by evaluating the Angular expression given in the attribute value. - * If the expression evaluates to a RegExp object, then this is used directly. - * If the expression evaluates to a string, then it will be converted to a RegExp - * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to - * `new RegExp('^abc$')`.
- * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to - * start at the index of the last search's match, thus not taking the whole input value into - * account. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
-
- - var text = element(by.binding('url.text')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('url.text')); - - it('should initialize to model', function() { - expect(text.getText()).toContain('http://google.com'); - expect(valid.getText()).toContain('true'); - }); - - it('should be invalid if empty', function() { - input.clear(); - input.sendKeys(''); - - expect(text.getText()).toEqual('text ='); - expect(valid.getText()).toContain('false'); - }); - - it('should be invalid if not url', function() { - input.clear(); - input.sendKeys('box'); - - expect(valid.getText()).toContain('false'); - }); - -
- */ - 'url': urlInputType, - - - /** - * @ngdoc input - * @name input[email] - * - * @description - * Text input with email validation. Sets the `email` validation error key if not a valid email - * address. - * - *
- * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex - * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can - * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide}) - *
- * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of - * any length. - * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string - * that contains the regular expression body that will be converted to a regular expression - * as in the ngPattern directive. - * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match - * a RegExp found by evaluating the Angular expression given in the attribute value. - * If the expression evaluates to a RegExp object, then this is used directly. - * If the expression evaluates to a string, then it will be converted to a RegExp - * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to - * `new RegExp('^abc$')`.
- * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to - * start at the index of the last search's match, thus not taking the whole input value into - * account. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
- -
- - Required! - - Not valid email! -
- text = {{email.text}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
- myForm.$error.email = {{!!myForm.$error.email}}
-
-
- - var text = element(by.binding('email.text')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('email.text')); - - it('should initialize to model', function() { - expect(text.getText()).toContain('me@example.com'); - expect(valid.getText()).toContain('true'); - }); - - it('should be invalid if empty', function() { - input.clear(); - input.sendKeys(''); - expect(text.getText()).toEqual('text ='); - expect(valid.getText()).toContain('false'); - }); - - it('should be invalid if not email', function() { - input.clear(); - input.sendKeys('xxx'); - - expect(valid.getText()).toContain('false'); - }); - -
- */ - 'email': emailInputType, - - - /** - * @ngdoc input - * @name input[radio] - * - * @description - * HTML radio button. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string} value The value to which the `ngModel` expression should be set when selected. - * Note that `value` only supports `string` values, i.e. the scope model needs to be a string, - * too. Use `ngValue` if you need complex models (`number`, `object`, ...). - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio - * is selected. Should be used instead of the `value` attribute if you need - * a non-string `ngModel` (`boolean`, `array`, ...). - * - * @example - - - -
-
-
-
- color = {{color.name | json}}
-
- Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`. -
- - it('should change state', function() { - var color = element(by.binding('color.name')); - - expect(color.getText()).toContain('blue'); - - element.all(by.model('color.name')).get(0).click(); - - expect(color.getText()).toContain('red'); - }); - -
- */ - 'radio': radioInputType, - - - /** - * @ngdoc input - * @name input[checkbox] - * - * @description - * HTML checkbox. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {expression=} ngTrueValue The value to which the expression should be set when selected. - * @param {expression=} ngFalseValue The value to which the expression should be set when not selected. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - - - -
-
-
- value1 = {{checkboxModel.value1}}
- value2 = {{checkboxModel.value2}}
-
-
- - it('should change state', function() { - var value1 = element(by.binding('checkboxModel.value1')); - var value2 = element(by.binding('checkboxModel.value2')); - - expect(value1.getText()).toContain('true'); - expect(value2.getText()).toContain('YES'); - - element(by.model('checkboxModel.value1')).click(); - element(by.model('checkboxModel.value2')).click(); - - expect(value1.getText()).toContain('false'); - expect(value2.getText()).toContain('NO'); - }); - -
- */ - 'checkbox': checkboxInputType, - - 'hidden': noop, - 'button': noop, - 'submit': noop, - 'reset': noop, - 'file': noop -}; - -function stringBasedInputType(ctrl) { - ctrl.$formatters.push(function(value) { - return ctrl.$isEmpty(value) ? value : value.toString(); - }); -} - -function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { - baseInputType(scope, element, attr, ctrl, $sniffer, $browser); - stringBasedInputType(ctrl); -} - -function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) { - var type = lowercase(element[0].type); - - // In composition mode, users are still inputing intermediate text buffer, - // hold the listener until composition is done. - // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent - if (!$sniffer.android) { - var composing = false; - - element.on('compositionstart', function(data) { - composing = true; - }); - - element.on('compositionend', function() { - composing = false; - listener(); - }); - } - - var listener = function(ev) { - if (timeout) { - $browser.defer.cancel(timeout); - timeout = null; - } - if (composing) return; - var value = element.val(), - event = ev && ev.type; - - // By default we will trim the value - // If the attribute ng-trim exists we will avoid trimming - // If input type is 'password', the value is never trimmed - if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) { - value = trim(value); - } - - // If a control is suffering from bad input (due to native validators), browsers discard its - // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the - // control's value is the same empty value twice in a row. - if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) { - ctrl.$setViewValue(value, event); - } - }; - - // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the - // input event on backspace, delete or cut - if ($sniffer.hasEvent('input')) { - element.on('input', listener); - } else { - var timeout; - - var deferListener = function(ev, input, origValue) { - if (!timeout) { - timeout = $browser.defer(function() { - timeout = null; - if (!input || input.value !== origValue) { - listener(ev); - } - }); - } - }; - - element.on('keydown', function(event) { - var key = event.keyCode; - - // ignore - // command modifiers arrows - if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; - - deferListener(event, this, this.value); - }); - - // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it - if ($sniffer.hasEvent('paste')) { - element.on('paste cut', deferListener); - } - } - - // if user paste into input using mouse on older browser - // or form autocomplete on newer browser, we need "change" event to catch it - element.on('change', listener); - - ctrl.$render = function() { - // Workaround for Firefox validation #12102. - var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue; - if (element.val() !== value) { - element.val(value); - } - }; -} - -function weekParser(isoWeek, existingDate) { - if (isDate(isoWeek)) { - return isoWeek; - } - - if (isString(isoWeek)) { - WEEK_REGEXP.lastIndex = 0; - var parts = WEEK_REGEXP.exec(isoWeek); - if (parts) { - var year = +parts[1], - week = +parts[2], - hours = 0, - minutes = 0, - seconds = 0, - milliseconds = 0, - firstThurs = getFirstThursdayOfYear(year), - addDays = (week - 1) * 7; - - if (existingDate) { - hours = existingDate.getHours(); - minutes = existingDate.getMinutes(); - seconds = existingDate.getSeconds(); - milliseconds = existingDate.getMilliseconds(); - } - - return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds); - } - } - - return NaN; -} - -function createDateParser(regexp, mapping) { - return function(iso, date) { - var parts, map; - - if (isDate(iso)) { - return iso; - } - - if (isString(iso)) { - // When a date is JSON'ified to wraps itself inside of an extra - // set of double quotes. This makes the date parsing code unable - // to match the date string and parse it as a date. - if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') { - iso = iso.substring(1, iso.length - 1); - } - if (ISO_DATE_REGEXP.test(iso)) { - return new Date(iso); - } - regexp.lastIndex = 0; - parts = regexp.exec(iso); - - if (parts) { - parts.shift(); - if (date) { - map = { - yyyy: date.getFullYear(), - MM: date.getMonth() + 1, - dd: date.getDate(), - HH: date.getHours(), - mm: date.getMinutes(), - ss: date.getSeconds(), - sss: date.getMilliseconds() / 1000 - }; - } else { - map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 }; - } - - forEach(parts, function(part, index) { - if (index < mapping.length) { - map[mapping[index]] = +part; - } - }); - return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0); - } - } - - return NaN; - }; -} - -function createDateInputType(type, regexp, parseDate, format) { - return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) { - badInputChecker(scope, element, attr, ctrl); - baseInputType(scope, element, attr, ctrl, $sniffer, $browser); - var timezone = ctrl && ctrl.$options && ctrl.$options.timezone; - var previousDate; - - ctrl.$$parserName = type; - ctrl.$parsers.push(function(value) { - if (ctrl.$isEmpty(value)) return null; - if (regexp.test(value)) { - // Note: We cannot read ctrl.$modelValue, as there might be a different - // parser/formatter in the processing chain so that the model - // contains some different data format! - var parsedDate = parseDate(value, previousDate); - if (timezone) { - parsedDate = convertTimezoneToLocal(parsedDate, timezone); - } - return parsedDate; - } - return undefined; - }); - - ctrl.$formatters.push(function(value) { - if (value && !isDate(value)) { - throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); - } - if (isValidDate(value)) { - previousDate = value; - if (previousDate && timezone) { - previousDate = convertTimezoneToLocal(previousDate, timezone, true); - } - return $filter('date')(value, format, timezone); - } else { - previousDate = null; - return ''; - } - }); - - if (isDefined(attr.min) || attr.ngMin) { - var minVal; - ctrl.$validators.min = function(value) { - return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal; - }; - attr.$observe('min', function(val) { - minVal = parseObservedDateValue(val); - ctrl.$validate(); - }); - } - - if (isDefined(attr.max) || attr.ngMax) { - var maxVal; - ctrl.$validators.max = function(value) { - return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; - }; - attr.$observe('max', function(val) { - maxVal = parseObservedDateValue(val); - ctrl.$validate(); - }); - } - - function isValidDate(value) { - // Invalid Date: getTime() returns NaN - return value && !(value.getTime && value.getTime() !== value.getTime()); - } - - function parseObservedDateValue(val) { - return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined; - } - }; -} - -function badInputChecker(scope, element, attr, ctrl) { - var node = element[0]; - var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity); - if (nativeValidation) { - ctrl.$parsers.push(function(value) { - var validity = element.prop(VALIDITY_STATE_PROPERTY) || {}; - // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430): - // - also sets validity.badInput (should only be validity.typeMismatch). - // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email) - // - can ignore this case as we can still read out the erroneous email... - return validity.badInput && !validity.typeMismatch ? undefined : value; - }); - } -} - -function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { - badInputChecker(scope, element, attr, ctrl); - baseInputType(scope, element, attr, ctrl, $sniffer, $browser); - - ctrl.$$parserName = 'number'; - ctrl.$parsers.push(function(value) { - if (ctrl.$isEmpty(value)) return null; - if (NUMBER_REGEXP.test(value)) return parseFloat(value); - return undefined; - }); - - ctrl.$formatters.push(function(value) { - if (!ctrl.$isEmpty(value)) { - if (!isNumber(value)) { - throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value); - } - value = value.toString(); - } - return value; - }); - - if (isDefined(attr.min) || attr.ngMin) { - var minVal; - ctrl.$validators.min = function(value) { - return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal; - }; - - attr.$observe('min', function(val) { - if (isDefined(val) && !isNumber(val)) { - val = parseFloat(val, 10); - } - minVal = isNumber(val) && !isNaN(val) ? val : undefined; - // TODO(matsko): implement validateLater to reduce number of validations - ctrl.$validate(); - }); - } - - if (isDefined(attr.max) || attr.ngMax) { - var maxVal; - ctrl.$validators.max = function(value) { - return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal; - }; - - attr.$observe('max', function(val) { - if (isDefined(val) && !isNumber(val)) { - val = parseFloat(val, 10); - } - maxVal = isNumber(val) && !isNaN(val) ? val : undefined; - // TODO(matsko): implement validateLater to reduce number of validations - ctrl.$validate(); - }); - } -} - -function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { - // Note: no badInputChecker here by purpose as `url` is only a validation - // in browsers, i.e. we can always read out input.value even if it is not valid! - baseInputType(scope, element, attr, ctrl, $sniffer, $browser); - stringBasedInputType(ctrl); - - ctrl.$$parserName = 'url'; - ctrl.$validators.url = function(modelValue, viewValue) { - var value = modelValue || viewValue; - return ctrl.$isEmpty(value) || URL_REGEXP.test(value); - }; -} - -function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { - // Note: no badInputChecker here by purpose as `url` is only a validation - // in browsers, i.e. we can always read out input.value even if it is not valid! - baseInputType(scope, element, attr, ctrl, $sniffer, $browser); - stringBasedInputType(ctrl); - - ctrl.$$parserName = 'email'; - ctrl.$validators.email = function(modelValue, viewValue) { - var value = modelValue || viewValue; - return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value); - }; -} - -function radioInputType(scope, element, attr, ctrl) { - // make the name unique, if not defined - if (isUndefined(attr.name)) { - element.attr('name', nextUid()); - } - - var listener = function(ev) { - if (element[0].checked) { - ctrl.$setViewValue(attr.value, ev && ev.type); - } - }; - - element.on('click', listener); - - ctrl.$render = function() { - var value = attr.value; - element[0].checked = (value == ctrl.$viewValue); - }; - - attr.$observe('value', ctrl.$render); -} - -function parseConstantExpr($parse, context, name, expression, fallback) { - var parseFn; - if (isDefined(expression)) { - parseFn = $parse(expression); - if (!parseFn.constant) { - throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' + - '`{1}`.', name, expression); - } - return parseFn(context); - } - return fallback; -} - -function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) { - var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true); - var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false); - - var listener = function(ev) { - ctrl.$setViewValue(element[0].checked, ev && ev.type); - }; - - element.on('click', listener); - - ctrl.$render = function() { - element[0].checked = ctrl.$viewValue; - }; - - // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false` - // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert - // it to a boolean. - ctrl.$isEmpty = function(value) { - return value === false; - }; - - ctrl.$formatters.push(function(value) { - return equals(value, trueValue); - }); - - ctrl.$parsers.push(function(value) { - return value ? trueValue : falseValue; - }); -} - - -/** - * @ngdoc directive - * @name textarea - * @restrict E - * - * @description - * HTML textarea element control with angular data-binding. The data-binding and validation - * properties of this element are exactly the same as those of the - * {@link ng.directive:input input element}. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any - * length. - * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match - * a RegExp found by evaluating the Angular expression given in the attribute value. - * If the expression evaluates to a RegExp object, then this is used directly. - * If the expression evaluates to a string, then it will be converted to a RegExp - * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to - * `new RegExp('^abc$')`.
- * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to - * start at the index of the last search's match, thus not taking the whole input value into - * account. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. - */ - - -/** - * @ngdoc directive - * @name input - * @restrict E - * - * @description - * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding, - * input state control, and validation. - * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers. - * - *
- * **Note:** Not every feature offered is available for all input types. - * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`. - *
- * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {boolean=} ngRequired Sets `required` attribute if set to true - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any - * length. - * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match - * a RegExp found by evaluating the Angular expression given in the attribute value. - * If the expression evaluates to a RegExp object, then this is used directly. - * If the expression evaluates to a string, then it will be converted to a RegExp - * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to - * `new RegExp('^abc$')`.
- * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to - * start at the index of the last search's match, thus not taking the whole input value into - * account. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. - * This parameter is ignored for input[type=password] controls, which will never trim the - * input. - * - * @example - - - -
-
- -
- - Required! -
- -
- - Too short! - - Too long! -
-
-
- user = {{user}}
- myForm.userName.$valid = {{myForm.userName.$valid}}
- myForm.userName.$error = {{myForm.userName.$error}}
- myForm.lastName.$valid = {{myForm.lastName.$valid}}
- myForm.lastName.$error = {{myForm.lastName.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.required = {{!!myForm.$error.required}}
- myForm.$error.minlength = {{!!myForm.$error.minlength}}
- myForm.$error.maxlength = {{!!myForm.$error.maxlength}}
-
-
- - var user = element(by.exactBinding('user')); - var userNameValid = element(by.binding('myForm.userName.$valid')); - var lastNameValid = element(by.binding('myForm.lastName.$valid')); - var lastNameError = element(by.binding('myForm.lastName.$error')); - var formValid = element(by.binding('myForm.$valid')); - var userNameInput = element(by.model('user.name')); - var userLastInput = element(by.model('user.last')); - - it('should initialize to model', function() { - expect(user.getText()).toContain('{"name":"guest","last":"visitor"}'); - expect(userNameValid.getText()).toContain('true'); - expect(formValid.getText()).toContain('true'); - }); - - it('should be invalid if empty when required', function() { - userNameInput.clear(); - userNameInput.sendKeys(''); - - expect(user.getText()).toContain('{"last":"visitor"}'); - expect(userNameValid.getText()).toContain('false'); - expect(formValid.getText()).toContain('false'); - }); - - it('should be valid if empty when min length is set', function() { - userLastInput.clear(); - userLastInput.sendKeys(''); - - expect(user.getText()).toContain('{"name":"guest","last":""}'); - expect(lastNameValid.getText()).toContain('true'); - expect(formValid.getText()).toContain('true'); - }); - - it('should be invalid if less than required min length', function() { - userLastInput.clear(); - userLastInput.sendKeys('xx'); - - expect(user.getText()).toContain('{"name":"guest"}'); - expect(lastNameValid.getText()).toContain('false'); - expect(lastNameError.getText()).toContain('minlength'); - expect(formValid.getText()).toContain('false'); - }); - - it('should be invalid if longer than max length', function() { - userLastInput.clear(); - userLastInput.sendKeys('some ridiculously long name'); - - expect(user.getText()).toContain('{"name":"guest"}'); - expect(lastNameValid.getText()).toContain('false'); - expect(lastNameError.getText()).toContain('maxlength'); - expect(formValid.getText()).toContain('false'); - }); - -
- */ -var inputDirective = ['$browser', '$sniffer', '$filter', '$parse', - function($browser, $sniffer, $filter, $parse) { - return { - restrict: 'E', - require: ['?ngModel'], - link: { - pre: function(scope, element, attr, ctrls) { - if (ctrls[0]) { - (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer, - $browser, $filter, $parse); - } - } - } - }; -}]; - - - -var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; -/** - * @ngdoc directive - * @name ngValue - * - * @description - * Binds the given expression to the value of `