/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./app/assets/javascripts/index.js");
/******/ })
/************************************************************************/
/******/ ({
/***/ "../snjs/dist/snjs.js":
/*!****************************!*\
!*** ../snjs/dist/snjs.js ***!
\****************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
(function webpackUniversalModuleDefinition(root,factory){if(true)module.exports=factory();else {}})(window,function(){return(/******/function(modules){// webpackBootstrap
/******/ // The module cache
/******/var installedModules={};/******/ /******/ // The require function
/******/function __webpack_require__(moduleId){/******/ /******/ // Check if module is in cache
/******/if(installedModules[moduleId]){/******/return installedModules[moduleId].exports;/******/}/******/ // Create a new module (and put it into the cache)
/******/var module=installedModules[moduleId]={/******/i:moduleId,/******/l:false,/******/exports:{}/******/};/******/ /******/ // Execute the module function
/******/modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);/******/ /******/ // Flag the module as loaded
/******/module.l=true;/******/ /******/ // Return the exports of the module
/******/return module.exports;/******/}/******/ /******/ /******/ // expose the modules object (__webpack_modules__)
/******/__webpack_require__.m=modules;/******/ /******/ // expose the module cache
/******/__webpack_require__.c=installedModules;/******/ /******/ // define getter function for harmony exports
/******/__webpack_require__.d=function(exports,name,getter){/******/if(!__webpack_require__.o(exports,name)){/******/Object.defineProperty(exports,name,{enumerable:true,get:getter});/******/}/******/};/******/ /******/ // define __esModule on exports
/******/__webpack_require__.r=function(exports){/******/if(typeof Symbol!=='undefined'&&Symbol.toStringTag){/******/Object.defineProperty(exports,Symbol.toStringTag,{value:'Module'});/******/}/******/Object.defineProperty(exports,'__esModule',{value:true});/******/};/******/ /******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/__webpack_require__.t=function(value,mode){/******/if(mode&1)value=__webpack_require__(value);/******/if(mode&8)return value;/******/if(mode&4&&typeof value==='object'&&value&&value.__esModule)return value;/******/var ns=Object.create(null);/******/__webpack_require__.r(ns);/******/Object.defineProperty(ns,'default',{enumerable:true,value:value});/******/if(mode&2&&typeof value!='string')for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key];}.bind(null,key));/******/return ns;/******/};/******/ /******/ // getDefaultExport function for compatibility with non-harmony modules
/******/__webpack_require__.n=function(module){/******/var getter=module&&module.__esModule?/******/function getDefault(){return module['default'];}:/******/function getModuleExports(){return module;};/******/__webpack_require__.d(getter,'a',getter);/******/return getter;/******/};/******/ /******/ // Object.prototype.hasOwnProperty.call
/******/__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property);};/******/ /******/ // __webpack_public_path__
/******/__webpack_require__.p="/dist/";/******/ /******/ /******/ // Load entry module and return exports
/******/return __webpack_require__(__webpack_require__.s="./lib/main.js");/******/}(/************************************************************************/ /******/{/***/"./lib/application.js":/*!****************************!*\
!*** ./lib/application.js ***!
\****************************/ /*! exports provided: SNApplication */ /***/function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);/* harmony export (binding) */__webpack_require__.d(__webpack_exports__,"SNApplication",function(){return SNApplication;});/* harmony import */var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(/*! @babel/runtime/regenerator */"./node_modules/@babel/runtime/regenerator/index.js");/* harmony import */var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default=/*#__PURE__*/__webpack_require__.n(_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0__);/* harmony import */var _Lib_utils__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(/*! @Lib/utils */"./lib/utils.js");/* harmony import */var _Models_content_types__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(/*! @Models/content_types */"./lib/models/content_types.js");/* harmony import */var _Payloads_generator__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(/*! @Payloads/generator */"./lib/protocol/payloads/generator.js");/* harmony import */var _Lib__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(/*! @Lib */"./lib/index.js");/* harmony import */var _Services__WEBPACK_IMPORTED_MODULE_5__=__webpack_require__(/*! @Services */"./lib/services/index.js");function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg);var value=info.value;}catch(error){reject(error);return;}if(info.done){resolve(value);}else{Promise.resolve(value).then(_next,_throw);}}function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise(function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value);}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err);}_next(undefined);});};}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}function _defineProperties(target,props){for(var i=0;i0&&arguments[0]!==undefined?arguments[0]:{},environment=_ref.environment,platform=_ref.platform,namespace=_ref.namespace,host=_ref.host,deviceInterface=_ref.deviceInterface,swapClasses=_ref.swapClasses,skipClasses=_ref.skipClasses,crypto=_ref.crypto;_classCallCheck(this,SNApplication);if(!deviceInterface){throw'Device Interface must be supplied.';}if(!environment){throw'Environment must be supplied when creating an application.';}if(!platform){throw'Platform must be supplied when creating an application.';}this.environment=environment;this.platform=platform;this.namespace=namespace||'';this.host=host;this.deviceInterface=deviceInterface;this.crypto=crypto;this.swapClasses=swapClasses;this.skipClasses=skipClasses;this.eventHandlers=[];this.services=[];this.streamObservers=[];this.serviceObservers=[];this.managedSubscribers=[];this.constructServices();}/**
* The first thing consumers should call when starting their app.
* This function will load all services in their correct order.
* @access public
* @param {object} params
* @param {object} params.callbacks
* @param {async|function} params.callbacks.receiveChallenge
* Return an array of ChallengeResponse for each Challenge.
* @param {Array.} params.callbacks.receiveChallenge.challenges
* An array of challenges that need a ChallengeResponse.
*/_createClass(SNApplication,[{key:"prepareForLaunch",value:function(){var _prepareForLaunch=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee(_ref2){var callbacks;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee$(_context){while(1){switch(_context.prev=_context.next){case 0:callbacks=_ref2.callbacks;if(callbacks.receiveChallenge){_context.next=3;break;}throw'Application.launch callbacks are not properly configured.';case 3:this.setLaunchCallbacks(callbacks);_context.next=6;return this.deviceInterface.openDatabase();case 6:_context.next=8;return this.migrationService.initialize();case 8:_context.next=10;return this.handleStage(_Lib__WEBPACK_IMPORTED_MODULE_4__["ApplicationStages"].PreparingForLaunch_0);case 10:_context.next=12;return this.storageService.initializeFromDisk();case 12:_context.next=14;return this.keyManager.initialize();case 14:_context.next=16;return this.handleStage(_Lib__WEBPACK_IMPORTED_MODULE_4__["ApplicationStages"].ReadyForLaunch_05);case 16:this.started=true;_context.next=19;return this.notifyEvent(_Lib__WEBPACK_IMPORTED_MODULE_4__["ApplicationEvents"].Started);case 19:case"end":return _context.stop();}}},_callee,this);}));function prepareForLaunch(_x){return _prepareForLaunch.apply(this,arguments);}return prepareForLaunch;}()/** @access public */},{key:"setLaunchCallbacks",value:function setLaunchCallbacks(callbacks){this.launchCallbacks=callbacks;this.challengeService.setChallengeHandler(callbacks);}/**
* Runs migrations, handles device authentication, unlocks application, and
* issues a callback if a device activation requires user input
* (i.e local passcode or fingerprint).
* @access public
* @param {bool} params.awaitDatabaseLoad Option to await database load before marking the app
* as ready. Used as far as we know for .restart and unit tests.
*/},{key:"launch",value:function(){var _launch=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee3(){var _this=this;var _ref3,awaitDatabaseLoad,launchChallenge,response,databasePayloads,loadPromise,_args3=arguments;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee3$(_context3){while(1){switch(_context3.prev=_context3.next){case 0:_ref3=_args3.length>0&&_args3[0]!==undefined?_args3[0]:{},awaitDatabaseLoad=_ref3.awaitDatabaseLoad;_context3.next=3;return this.challengeService.getLaunchChallenge();case 3:launchChallenge=_context3.sent;if(!launchChallenge){_context3.next=10;break;}_context3.next=7;return this.challengeService.promptForChallengeResponse(launchChallenge);case 7:response=_context3.sent;_context3.next=10;return this.handleLaunchChallengeResponse(response);case 10:_context3.next=12;return this.storageService.isStorageWrapped();case 12:if(!_context3.sent){_context3.next=15;break;}_context3.next=15;return this.storageService.decryptStorage();case 15:_context3.next=17;return this.handleStage(_Lib__WEBPACK_IMPORTED_MODULE_4__["ApplicationStages"].StorageDecrypted_09);case 17:_context3.next=19;return this.sessionManager.initializeFromDisk();case 19:this.historyManager.initializeFromDisk();this.unlocked=true;_context3.next=23;return this.notifyEvent(_Lib__WEBPACK_IMPORTED_MODULE_4__["ApplicationEvents"].Launched);case 23:_context3.next=25;return this.handleStage(_Lib__WEBPACK_IMPORTED_MODULE_4__["ApplicationStages"].Launched_10);case 25:_context3.next=27;return this.syncService.getDatabasePayloads();case 27:databasePayloads=_context3.sent;_context3.next=30;return this.handleStage(_Lib__WEBPACK_IMPORTED_MODULE_4__["ApplicationStages"].LoadingDatabase_11);case 30:/**
* We don't want to await this, as we want to begin allowing the app to function
* before local data has been loaded fully. We await only initial
* `getDatabasePayloads` to lock in on database state.
*/loadPromise=this.syncService.loadDatabasePayloads(databasePayloads).then(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee2(){return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee2$(_context2){while(1){switch(_context2.prev=_context2.next){case 0:if(!_this.dealloced){_context2.next=2;break;}throw'Application has been destroyed.';case 2:_context2.next=4;return _this.handleStage(_Lib__WEBPACK_IMPORTED_MODULE_4__["ApplicationStages"].LoadedDatabase_12);case 4:_this.beginAutoSyncTimer();return _context2.abrupt("return",_this.syncService.sync({mode:_Services__WEBPACK_IMPORTED_MODULE_5__["SyncModes"].DownloadFirst}));case 6:case"end":return _context2.stop();}}},_callee2);})));if(!awaitDatabaseLoad){_context3.next=34;break;}_context3.next=34;return loadPromise;case 34:case"end":return _context3.stop();}}},_callee3,this);}));function launch(){return _launch.apply(this,arguments);}return launch;}()},{key:"handleLaunchChallengeResponse",value:function(){var _handleLaunchChallengeResponse=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee4(response){var wrappingKey;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee4$(_context4){while(1){switch(_context4.prev=_context4.next){case 0:if(!response.challenge.types.includes(_Lib__WEBPACK_IMPORTED_MODULE_4__["ChallengeType"].LocalPasscode)){_context4.next=8;break;}wrappingKey=response.artifacts.wrappingKey;if(wrappingKey){_context4.next=6;break;}_context4.next=5;return this.keyManager.computeWrappingKey({passcode:response.value});case 5:wrappingKey=_context4.sent;case 6:_context4.next=8;return this.keyManager.unwrapRootKey({wrappingKey:wrappingKey});case 8:case"end":return _context4.stop();}}},_callee4,this);}));function handleLaunchChallengeResponse(_x2){return _handleLaunchChallengeResponse.apply(this,arguments);}return handleLaunchChallengeResponse;}()/**
* @access private
*/},{key:"beginAutoSyncTimer",value:function beginAutoSyncTimer(){var _this2=this;this.autoSyncInterval=this.deviceInterface.interval(function(){_this2.syncService.log('Syncing from autosync');_this2.sync();},DEFAULT_AUTO_SYNC_INTERVAL);}/**
* The migrations service is initialized with this function, so that it can retrieve
* raw challenge values as necessary.
* @access private
*/},{key:"getMigrationChallengeResponder",value:function getMigrationChallengeResponder(){var _this3=this;return(/*#__PURE__*/function(){var _ref5=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee5(challenge,validate,orchestratorFill){return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee5$(_context5){while(1){switch(_context5.prev=_context5.next){case 0:return _context5.abrupt("return",_this3.challengeService.promptForChallengeResponse(challenge,validate,orchestratorFill));case 1:case"end":return _context5.stop();}}},_callee5);}));return function(_x3,_x4,_x5){return _ref5.apply(this,arguments);};}());}/** @access private */},{key:"handleStage",value:function(){var _handleStage=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee6(stage){var _iteratorNormalCompletion,_didIteratorError,_iteratorError,_iterator,_step,service;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee6$(_context6){while(1){switch(_context6.prev=_context6.next){case 0:_iteratorNormalCompletion=true;_didIteratorError=false;_iteratorError=undefined;_context6.prev=3;_iterator=this.services[Symbol.iterator]();case 5:if(_iteratorNormalCompletion=(_step=_iterator.next()).done){_context6.next=12;break;}service=_step.value;_context6.next=9;return service.handleApplicationStage(stage);case 9:_iteratorNormalCompletion=true;_context6.next=5;break;case 12:_context6.next=18;break;case 14:_context6.prev=14;_context6.t0=_context6["catch"](3);_didIteratorError=true;_iteratorError=_context6.t0;case 18:_context6.prev=18;_context6.prev=19;if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return();}case 21:_context6.prev=21;if(!_didIteratorError){_context6.next=24;break;}throw _iteratorError;case 24:return _context6.finish(21);case 25:return _context6.finish(18);case 26:case"end":return _context6.stop();}}},_callee6,this,[[3,14,18,26],[19,,21,25]]);}));function handleStage(_x6){return _handleStage.apply(this,arguments);}return handleStage;}()/**
* @access public
* @param {function} callback
* @param {ApplicationEvent} [singleEvent] Whether to only listen for a particular event.
*/},{key:"addEventObserver",value:function addEventObserver(callback,singleEvent){var _this4=this;var observer={callback:callback,singleEvent:singleEvent};this.eventHandlers.push(observer);return function(){Object(_Lib_utils__WEBPACK_IMPORTED_MODULE_1__["removeFromArray"])(_this4.eventHandlers,observer);};}/**
* @access public
* @param {ApplicationEvent} singleEvent Event to listen for.
* @param {function} callback
*/},{key:"addSingleEventObserver",value:function addSingleEventObserver(event,callback){return this.addEventObserver(function(firedEvent){if(firedEvent===event){callback();}},event);}/** @access private */},{key:"notifyEvent",value:function(){var _notifyEvent=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee7(event,data){var _iteratorNormalCompletion2,_didIteratorError2,_iteratorError2,_iterator2,_step2,observer;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee7$(_context7){while(1){switch(_context7.prev=_context7.next){case 0:_iteratorNormalCompletion2=true;_didIteratorError2=false;_iteratorError2=undefined;_context7.prev=3;_iterator2=this.eventHandlers.slice()[Symbol.iterator]();case 5:if(_iteratorNormalCompletion2=(_step2=_iterator2.next()).done){_context7.next=18;break;}observer=_step2.value;if(!(observer.singleEvent&&observer.singleEvent===event)){_context7.next=12;break;}_context7.next=10;return observer.callback(event,data||{});case 10:_context7.next=15;break;case 12:if(observer.singleEvent){_context7.next=15;break;}_context7.next=15;return observer.callback(event,data||{});case 15:_iteratorNormalCompletion2=true;_context7.next=5;break;case 18:_context7.next=24;break;case 20:_context7.prev=20;_context7.t0=_context7["catch"](3);_didIteratorError2=true;_iteratorError2=_context7.t0;case 24:_context7.prev=24;_context7.prev=25;if(!_iteratorNormalCompletion2&&_iterator2.return!=null){_iterator2.return();}case 27:_context7.prev=27;if(!_didIteratorError2){_context7.next=30;break;}throw _iteratorError2;case 30:return _context7.finish(27);case 31:return _context7.finish(24);case 32:case"end":return _context7.stop();}}},_callee7,this,[[3,20,24,32],[25,,27,31]]);}));function notifyEvent(_x7,_x8){return _notifyEvent.apply(this,arguments);}return notifyEvent;}()/**
* Whether the local database has completed loading local items.
* @access public
*/},{key:"isDatabaseLoaded",value:function isDatabaseLoaded(){return this.syncService.isDatabaseLoaded();}/** @access public */},{key:"savePayload",value:function(){var _savePayload=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee8(_ref6){var payload,dirtied;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee8$(_context8){while(1){switch(_context8.prev=_context8.next){case 0:payload=_ref6.payload;dirtied=Object(_Payloads_generator__WEBPACK_IMPORTED_MODULE_3__["CopyPayload"])({payload:payload,override:{dirty:true}});_context8.next=4;return this.modelManager.mapPayloadToLocalItem({payload:dirtied});case 4:_context8.next=6;return this.syncService.sync();case 6:case"end":return _context8.stop();}}},_callee8,this);}));function savePayload(_x9){return _savePayload.apply(this,arguments);}return savePayload;}()/**
* Finds an item by UUID.
* @access public
* @param uuid The uuid of the item to find.
*/},{key:"findItem",value:function findItem(_ref7){var uuid=_ref7.uuid;return this.modelManager.findItem(uuid);}/**
* Finds an item by predicate.
* @access public
*/},{key:"findItems",value:function findItems(_ref8){var predicate=_ref8.predicate;return this.modelManager.itemsMatchingPredicate(predicate);}/** @access public */},{key:"mergeItem",value:function(){var _mergeItem=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee9(_ref9){var item,source;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee9$(_context9){while(1){switch(_context9.prev=_context9.next){case 0:item=_ref9.item,source=_ref9.source;return _context9.abrupt("return",this.modelManager.mapItem({item:item,source:source}));case 2:case"end":return _context9.stop();}}},_callee9,this);}));function mergeItem(_x10){return _mergeItem.apply(this,arguments);}return mergeItem;}()/**
* @access public
* @param add Whether to add the item to application state.
* @param needsSync Whether to mark the item as needing sync. `add` must also be true.
*/},{key:"createItem",value:function(){var _createItem=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee10(_ref10){var contentType,content,add,needsSync,item;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee10$(_context10){while(1){switch(_context10.prev=_context10.next){case 0:contentType=_ref10.contentType,content=_ref10.content,add=_ref10.add,needsSync=_ref10.needsSync;_context10.next=3;return this.modelManager.createItem({contentType:contentType,content:content,add:add,needsSync:needsSync});case 3:item=_context10.sent;return _context10.abrupt("return",item);case 5:case"end":return _context10.stop();}}},_callee10,this);}));function createItem(_x11){return _createItem.apply(this,arguments);}return createItem;}()/** @access public */},{key:"saveItem",value:function(){var _saveItem=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee11(_ref11){var item;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee11$(_context11){while(1){switch(_context11.prev=_context11.next){case 0:item=_ref11.item;_context11.next=3;return this.modelManager.setItemDirty(item,true);case 3:_context11.next=5;return this.syncService.sync();case 5:case"end":return _context11.stop();}}},_callee11,this);}));function saveItem(_x12){return _saveItem.apply(this,arguments);}return saveItem;}()/**
* @access public
* @param {Array.} params.items
*/},{key:"saveItems",value:function(){var _saveItems=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee12(_ref12){var items;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee12$(_context12){while(1){switch(_context12.prev=_context12.next){case 0:items=_ref12.items;_context12.next=3;return this.modelManager.setItemsDirty(items);case 3:_context12.next=5;return this.syncService.sync();case 5:case"end":return _context12.stop();}}},_callee12,this);}));function saveItems(_x13){return _saveItems.apply(this,arguments);}return saveItems;}()/**
* @access public
* @param {SNItem} params.item
* @param {bool} params.updateUserModifiedDate Whether to change the modified date the user
* sees of the item.
*/},{key:"setItemNeedsSync",value:function(){var _setItemNeedsSync=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee13(_ref13){var item,updateUserModifiedDate;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee13$(_context13){while(1){switch(_context13.prev=_context13.next){case 0:item=_ref13.item,updateUserModifiedDate=_ref13.updateUserModifiedDate;return _context13.abrupt("return",this.modelManager.setItemDirty(item,true,updateUserModifiedDate));case 2:case"end":return _context13.stop();}}},_callee13,this);}));function setItemNeedsSync(_x14){return _setItemNeedsSync.apply(this,arguments);}return setItemNeedsSync;}()/**
* @access public
* @param updateUserModifiedDate
* Whether to change the modified date the user sees of the item.
*/},{key:"setItemsNeedsSync",value:function(){var _setItemsNeedsSync=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee14(_ref14){var items;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee14$(_context14){while(1){switch(_context14.prev=_context14.next){case 0:items=_ref14.items;return _context14.abrupt("return",this.modelManager.setItemsDirty(items));case 2:case"end":return _context14.stop();}}},_callee14,this);}));function setItemsNeedsSync(_x15){return _setItemsNeedsSync.apply(this,arguments);}return setItemsNeedsSync;}()/** @access public */},{key:"deleteItem",value:function(){var _deleteItem=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee15(_ref15){var item;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee15$(_context15){while(1){switch(_context15.prev=_context15.next){case 0:item=_ref15.item;this.modelManager.setItemToBeDeleted(item);this.sync();case 3:case"end":return _context15.stop();}}},_callee15,this);}));function deleteItem(_x16){return _deleteItem.apply(this,arguments);}return deleteItem;}()/** @access public */},{key:"deleteItemLocally",value:function(){var _deleteItemLocally=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee16(_ref16){var item;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee16$(_context16){while(1){switch(_context16.prev=_context16.next){case 0:item=_ref16.item;this.modelManager.removeItemLocally(item);case 2:case"end":return _context16.stop();}}},_callee16,this);}));function deleteItemLocally(_x17){return _deleteItemLocally.apply(this,arguments);}return deleteItemLocally;}()/** @access public */},{key:"emptyTrash",value:function(){var _emptyTrash=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee17(){return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee17$(_context17){while(1){switch(_context17.prev=_context17.next){case 0:_context17.next=2;return this.modelManager.emptyTrash();case 2:return _context17.abrupt("return",this.sync());case 3:case"end":return _context17.stop();}}},_callee17,this);}));function emptyTrash(){return _emptyTrash.apply(this,arguments);}return emptyTrash;}()/** @access public */},{key:"getTrashedItems",value:function getTrashedItems(){return this.modelManager.trashedItems();}/**
* @access public
* @param {string|ContentType} contentType A string, array of strings, or '*'
*/},{key:"getItems",value:function getItems(_ref17){var contentType=_ref17.contentType;return this.modelManager.getItems(contentType);}/** @access public */},{key:"getDisplayableItems",value:function getDisplayableItems(_ref18){var contentType=_ref18.contentType;return this.modelManager.validItemsForContentType(contentType);}/** @access public */},{key:"getNotesMatchingSmartTag",value:function getNotesMatchingSmartTag(_ref19){var smartTag=_ref19.smartTag;return this.modelManager.notesMatchingSmartTag(smartTag);}/** @access public */},{key:"findTag",value:function findTag(_ref20){var title=_ref20.title;return this.modelManager.findTagByTitle(title);}/** @access public */},{key:"findOrCreateTag",value:function(){var _findOrCreateTag=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee18(_ref21){var title;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee18$(_context18){while(1){switch(_context18.prev=_context18.next){case 0:title=_ref21.title;return _context18.abrupt("return",this.modelManager.findOrCreateTagByTitle(title));case 2:case"end":return _context18.stop();}}},_callee18,this);}));function findOrCreateTag(_x18){return _findOrCreateTag.apply(this,arguments);}return findOrCreateTag;}()/** @access public */},{key:"getSmartTags",value:function getSmartTags(){return this.modelManager.getSmartTags();}/** @access public */},{key:"getNoteCount",value:function getNoteCount(){return this.modelManager.noteCount();}/**
* Begin streaming items to display in the UI.
* @access public
* @param contentType Can be string, '*', or array of types.
*/},{key:"streamItems",value:function streamItems(_ref22){var _this5=this;var contentType=_ref22.contentType,stream=_ref22.stream;var observer=this.modelManager.addMappingObserver(contentType,function(allItems,validItems,deletedItems,source,sourceKey){var includedContentTypes=allItems.map(function(item){return item.content_type;});stream({items:allItems,contentTypes:includedContentTypes,source:source,sourceKey:sourceKey});});this.streamObservers.push(observer);return function(){Object(_Lib_utils__WEBPACK_IMPORTED_MODULE_1__["removeFromArray"])(_this5.streamObservers,observer);};}/** @access public */},{key:"setHost",value:function(){var _setHost=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee19(host){return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee19$(_context19){while(1){switch(_context19.prev=_context19.next){case 0:return _context19.abrupt("return",this.apiService.setHost(host));case 1:case"end":return _context19.stop();}}},_callee19,this);}));function setHost(_x19){return _setHost.apply(this,arguments);}return setHost;}()/** @access public */},{key:"getHost",value:function(){var _getHost=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee20(){return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee20$(_context20){while(1){switch(_context20.prev=_context20.next){case 0:return _context20.abrupt("return",this.apiService.getHost());case 1:case"end":return _context20.stop();}}},_callee20,this);}));function getHost(){return _getHost.apply(this,arguments);}return getHost;}()/** @access public */},{key:"getUser",value:function getUser(){if(!this.unlocked){throw'Attempting to access user before application unlocked';}return this.sessionManager.getUser();}/** @access public */},{key:"getUserVersion",value:function(){var _getUserVersion=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee21(){return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee21$(_context21){while(1){switch(_context21.prev=_context21.next){case 0:return _context21.abrupt("return",this.protocolService.getUserVersion());case 1:case"end":return _context21.stop();}}},_callee21,this);}));function getUserVersion(){return _getUserVersion.apply(this,arguments);}return getUserVersion;}()/**
* Returns true if there is an upgrade available for the account or passcode
* @access public
*/},{key:"protocolUpgradeAvailable",value:function(){var _protocolUpgradeAvailable=_asyncToGenerator(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee22(){return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee22$(_context22){while(1){switch(_context22.prev=_context22.next){case 0:return _context22.abrupt("return",this.protocolService.upgradeAvailable());case 1:case"end":return _context22.stop();}}},_callee22,this);}));function protocolUpgradeAvailable(){return _protocolUpgradeAvailable.apply(this,arguments);}return protocolUpgradeAvailable;}()/**
* @access public
* @returns {Array.
.red-add, .red-remove {
transition: all 4s cubic-bezier(0.250, 0.460, 0.450, 0.940);
}
.red,
.red-add.red-add-active {
color: #FF0000;
font-size: 40px;
}
.red-remove.red-remove-active {
font-size: 10px;
color: black;
}
*/
cancel: function(runner) {
if (runner.cancel) {
runner.cancel();
}
},
/**
*
* @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.
* The object can have the following properties:
*
* - **addClass** - `{string}` - space-separated CSS classes to add to element
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} the animation runner
*/
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.
* The object can have the following properties:
*
* - **addClass** - `{string}` - space-separated CSS classes to add to element
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} the animation runner
*/
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.
* The object can have the following properties:
*
* - **addClass** - `{string}` - space-separated CSS classes to add to element
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} the animation runner
*/
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.
* The object can have the following properties:
*
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} animationRunner the animation runner
*/
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.
* The object can have the following properties:
*
* - **addClass** - `{string}` - space-separated CSS classes to add to element
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} the animation runner
*/
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.
* The object can have the following properties:
*
* - **addClass** - `{string}` - space-separated CSS classes to add to element
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} the animation runner
*/
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 the CSS style provided in `from` does not have a corresponding
* style in `to`, the style in `from` is applied immediately, and no animation is run.
* If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
* method (or as part of the `options` parameter):
*
* ```js
* ngModule.animation('.my-inline-animation', function() {
* return {
* animate : function(element, from, to, done, options) {
* //animation
* done();
* }
* }
* });
* ```
*
* @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 applied to the element.)
* @param {object=} options an optional collection of options/styles that will be applied to the element.
* The object can have the following properties:
*
* - **addClass** - `{string}` - space-separated CSS classes to add to element
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} the animation runner
*/
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);
}
};
}];
}];
var $$AnimateAsyncRunFactoryProvider = /** @this */ function() {
this.$get = ['$$rAF', function($$rAF) {
var waitQueue = [];
function waitForTick(fn) {
waitQueue.push(fn);
if (waitQueue.length > 1) return;
$$rAF(function() {
for (var i = 0; i < waitQueue.length; i++) {
waitQueue[i]();
}
waitQueue = [];
});
}
return function() {
var passed = false;
waitForTick(function() {
passed = true;
});
return function(callback) {
if (passed) {
callback();
} else {
waitForTick(callback);
}
};
};
}];
};
var $$AnimateRunnerFactoryProvider = /** @this */ function() {
this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$$isDocumentHidden', '$timeout',
function($q, $sniffer, $$animateAsyncRun, $$isDocumentHidden, $timeout) {
var INITIAL_STATE = 0;
var DONE_PENDING_STATE = 1;
var DONE_COMPLETE_STATE = 2;
AnimateRunner.chain = function(chain, callback) {
var index = 0;
next();
function next() {
if (index === chain.length) {
callback(true);
return;
}
chain[index](function(response) {
if (response === false) {
callback(false);
return;
}
index++;
next();
});
}
};
AnimateRunner.all = function(runners, callback) {
var count = 0;
var status = true;
forEach(runners, function(runner) {
runner.done(onProgress);
});
function onProgress(response) {
status = status && response;
if (++count === runners.length) {
callback(status);
}
}
};
function AnimateRunner(host) {
this.setHost(host);
var rafTick = $$animateAsyncRun();
var timeoutTick = function(fn) {
$timeout(fn, 0, false);
};
this._doneCallbacks = [];
this._tick = function(fn) {
if ($$isDocumentHidden()) {
timeoutTick(fn);
} else {
rafTick(fn);
}
};
this._state = 0;
}
AnimateRunner.prototype = {
setHost: function(host) {
this.host = host || {};
},
done: function(fn) {
if (this._state === DONE_COMPLETE_STATE) {
fn();
} else {
this._doneCallbacks.push(fn);
}
},
progress: noop,
getPromise: function() {
if (!this.promise) {
var self = this;
this.promise = $q(function(resolve, reject) {
self.done(function(status) {
if (status === false) {
reject();
} else {
resolve();
}
});
});
}
return this.promise;
},
then: function(resolveHandler, rejectHandler) {
return this.getPromise().then(resolveHandler, rejectHandler);
},
'catch': function(handler) {
return this.getPromise()['catch'](handler);
},
'finally': function(handler) {
return this.getPromise()['finally'](handler);
},
pause: function() {
if (this.host.pause) {
this.host.pause();
}
},
resume: function() {
if (this.host.resume) {
this.host.resume();
}
},
end: function() {
if (this.host.end) {
this.host.end();
}
this._resolve(true);
},
cancel: function() {
if (this.host.cancel) {
this.host.cancel();
}
this._resolve(false);
},
complete: function(response) {
var self = this;
if (self._state === INITIAL_STATE) {
self._state = DONE_PENDING_STATE;
self._tick(function() {
self._resolve(response);
});
}
},
_resolve: function(response) {
if (this._state !== DONE_COMPLETE_STATE) {
forEach(this._doneCallbacks, function(fn) {
fn(response);
});
this._doneCallbacks.length = 0;
this._state = DONE_COMPLETE_STATE;
}
}
};
return AnimateRunner;
}];
};
/* exported $CoreAnimateCssProvider */
/**
* @ngdoc service
* @name $animateCss
* @kind object
* @this
*
* @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', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
return function(element, initialOptions) {
// all of the animation functions should create
// a copy of the options data, however, if a
// parent service has already created a copy then
// we should stick to using that
var options = initialOptions || {};
if (!options.$$prepared) {
options = copy(options);
}
// there is no point in applying the styles since
// there is no animation that goes on at all in
// this version of $animateCss.
if (options.cleanupStyles) {
options.from = options.to = null;
}
if (options.from) {
element.css(options.from);
options.from = null;
}
var closed, runner = new $$AnimateRunner();
return {
start: run,
end: run
};
function run() {
$$rAF(function() {
applyAnimationContents();
if (!closed) {
runner.complete();
}
closed = true;
});
return runner;
}
function applyAnimationContents() {
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 getHash: true, stripHash: false */
function getHash(url) {
var index = url.indexOf('#');
return index === -1 ? '' : url.substr(index);
}
function trimEmptyHash(url) {
return url.replace(/#$/, '');
}
/**
* ! 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, $$taskTrackerFactory) {
var self = this,
location = window.location,
history = window.history,
setTimeout = window.setTimeout,
clearTimeout = window.clearTimeout,
pendingDeferIds = {},
taskTracker = $$taskTrackerFactory($log);
self.isMock = false;
//////////////////////////////////////////////////////////////
// Task-tracking API
//////////////////////////////////////////////////////////////
// TODO(vojta): remove this temporary api
self.$$completeOutstandingRequest = taskTracker.completeTask;
self.$$incOutstandingRequestCount = taskTracker.incTaskCount;
// TODO(vojta): prefix this method with $$ ?
self.notifyWhenNoOutstandingRequests = taskTracker.notifyWhenNoPendingTasks;
//////////////////////////////////////////////////////////////
// URL API
//////////////////////////////////////////////////////////////
var cachedState, lastHistoryState,
lastBrowserUrl = location.href,
baseElement = document.find('base'),
pendingLocation = null,
getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
try {
return history.state;
} catch (e) {
// MSIE can reportedly throw when there is no state (UNCONFIRMED).
}
};
cacheState();
/**
* @name $browser#url
*
* @description
* GETTER:
* Without any argument, this method just returns current value of `location.href` (with a
* trailing `#` stripped of if the hash is empty).
*
* 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 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;
// Normalize the inputted URL
url = urlResolve(url).href;
// 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();
} else {
if (!sameBase) {
pendingLocation = url;
}
if (replace) {
location.replace(url);
} else if (!sameBase) {
location.href = url;
} else {
location.hash = getHash(url);
}
if (location.href !== url) {
pendingLocation = url;
}
}
if (pendingLocation) {
pendingLocation = url;
}
return self;
// getter
} else {
// - pendingLocation is needed as browsers don't allow to read out
// the new location.href if a reload happened or if there is a bug like in iOS 9 (see
// https://openradar.appspot.com/22186109).
return trimEmptyHash(pendingLocation || location.href);
}
};
/**
* @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() {
pendingLocation = null;
fireStateOrUrlChange();
}
// 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;
lastHistoryState = cachedState;
}
function fireStateOrUrlChange() {
var prevLastHistoryState = lastHistoryState;
cacheState();
if (lastBrowserUrl === self.url() && prevLastHistoryState === 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 AngularJS:
* - 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 AngularJS 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 don't
// fire popstate when user changes 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 AngularJS.
* 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 = fireStateOrUrlChange;
//////////////////////////////////////////////////////////////
// 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] Number of milliseconds to defer the function execution.
* @param {string=} [taskType=DEFAULT_TASK_TYPE] The type of task that is deferred.
* @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, taskType) {
var timeoutId;
delay = delay || 0;
taskType = taskType || taskTracker.DEFAULT_TASK_TYPE;
taskTracker.incTaskCount(taskType);
timeoutId = setTimeout(function() {
delete pendingDeferIds[timeoutId];
taskTracker.completeTask(fn, taskType);
}, delay);
pendingDeferIds[timeoutId] = taskType;
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.hasOwnProperty(deferId)) {
var taskType = pendingDeferIds[deferId];
delete pendingDeferIds[deferId];
clearTimeout(deferId);
taskTracker.completeTask(noop, taskType);
return true;
}
return false;
};
}
/** @this */
function $BrowserProvider() {
this.$get = ['$window', '$log', '$sniffer', '$document', '$$taskTrackerFactory',
function($window, $log, $sniffer, $document, $$taskTrackerFactory) {
return new Browser($window, $document, $log, $sniffer, $$taskTrackerFactory);
}];
}
/**
* @ngdoc service
* @name $cacheFactory
* @this
*
* @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 (angular.isUndefined($scope.cache.get(key))) {
$scope.keys.push(key);
}
$scope.cache.put(key, angular.isUndefined(value) ? 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 = createMap(),
capacity = (options && options.capacity) || Number.MAX_VALUE,
lruHash = createMap(),
freshEnd = null,
staleEnd = null;
/**
* @ngdoc type
* @name $cacheFactory.Cache
*
* @description
* A cache object used to store and retrieve data, primarily used by
* {@link $templateRequest $templateRequest} 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];
}
if (!(key in data)) return;
delete data[key];
size--;
},
/**
* @ngdoc method
* @name $cacheFactory.Cache#removeAll
* @kind function
*
* @description
* Clears the cache object of any entries.
*/
removeAll: function() {
data = createMap();
size = 0;
lruHash = createMap();
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
* @this
*
* @description
* `$templateCache` is a {@link $cacheFactory.Cache Cache object} created by the
* {@link ng.$cacheFactory $cacheFactory}.
*
* 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, by using {@link $templateRequest},
* 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} (e.g.
* element with {@link ngApp} 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 component:
* ```js
* myApp.component('myComponent', {
* templateUrl: 'templateId.html'
* });
* ```
*
* or get it via the `$templateCache` service:
* ```js
* $templateCache.get('templateId.html')
* ```
*
*/
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 like 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 {@link $compile#directive-definition-object 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.
*
* **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) { ... }
* });
* ```
*
* ### Life-cycle hooks
* Directive controllers can provide the following methods that are called by AngularJS at points in the life-cycle of the
* directive:
* * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
* had their bindings initialized (and before the pre & post linking functions for the directives on
* this element). This is a good place to put initialization code for your controller.
* * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
* `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
* object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
* component such as cloning the bound value to prevent accidental mutation of the outer value. Note that this will
* also be called when your bindings are initialized.
* * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on
* changes. Any actions that you wish to take in response to the changes that you detect must be
* invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook
* could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not
* be detected by AngularJS's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments;
* if detecting changes, you must store the previous value(s) for comparison to the current values.
* * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
* external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
* the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
* components will have their `$onDestroy()` hook called before child components.
* * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
* function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
* 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.
*
* #### Comparison with life-cycle hooks in the new Angular
* The new Angular also uses life-cycle hooks for its components. While the AngularJS life-cycle hooks are similar there are
* some differences that you should be aware of, especially when it comes to moving your code from AngularJS to Angular:
*
* * AngularJS hooks are prefixed with `$`, such as `$onInit`. Angular hooks are prefixed with `ng`, such as `ngOnInit`.
* * AngularJS hooks can be defined on the controller prototype or added to the controller inside its constructor.
* In Angular you can only define hooks on the prototype of the Component class.
* * Due to the differences in change-detection, you may get many more calls to `$doCheck` in AngularJS than you would to
* `ngDoCheck` in Angular.
* * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be
* propagated throughout the application.
* Angular does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an
* error or do nothing depending upon the state of `enableProdMode()`.
*
* #### Life-cycle hook examples
*
* This example shows how you can check for mutations to a Date object even though the identity of the object
* has not changed.
*
*
*
* angular.module('do-check-module', [])
* .component('app', {
* template:
* 'Month: ' +
* 'Date: {{ $ctrl.date }}' +
* '',
* controller: function() {
* this.date = new Date();
* this.month = this.date.getMonth();
* this.updateDate = function() {
* this.date.setMonth(this.month);
* };
* }
* })
* .component('test', {
* bindings: { date: '<' },
* template:
* '
{{ $ctrl.log | json }}
',
* controller: function() {
* var previousValue;
* this.log = [];
* this.$doCheck = function() {
* var currentValue = this.date && this.date.valueOf();
* if (previousValue !== currentValue) {
* this.log.push('doCheck: date mutated: ' + this.date);
* previousValue = currentValue;
* }
* };
* }
* });
*
*
*
*
*
*
* This example show how you might use `$doCheck` to trigger changes in your component's inputs even if the
* actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large
* arrays or objects can have a negative impact on your application performance.)
*
*
*
*
',
* controller: function() {
* this.log = [];
*
* this.$doCheck = function() {
* if (this.items_ref !== this.items) {
* this.log.push('doCheck: items changed');
* this.items_ref = this.items;
* }
* if (!angular.equals(this.items_clone, this.items)) {
* this.log.push('doCheck: items mutated');
* this.items_clone = angular.copy(this.items);
* }
* };
* }
* });
*
*
*
*
* ### 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 (default is `false`), 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 behavioral (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`
* The scope property can be `false`, `true`, or an object:
*
* * **`false` (default):** No scope will be created for the directive. The directive will use its
* parent's scope.
*
* * **`true`:** A new child scope that prototypically inherits from its parent will be created for
* the directive's element. If multiple directives on the same element request a new scope,
* only one new scope is created.
*
* * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's template.
* The 'isolate' scope differs from normal scope in that it does not prototypically
* inherit from its parent scope. This is useful when creating reusable components, which should not
* accidentally read or modify data in the parent scope. Note that an isolate scope
* directive without a `template` or `templateUrl` will not apply the isolate scope
* to its children elements.
*
* The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
* directive's element. These local properties are useful for aliasing values for templates. The keys in
* the object hash map to the name of the property on the isolate scope; the values define how the property
* is bound to the parent scope, via matching attributes on the directive's element:
*
* * `@` 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 the isolate scope definition `scope: { localName:'@myAttr' }`,
* the directive's scope property `localName` will reflect the interpolated value of `hello
* {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
* scope. The `name` is read from the parent scope (not the directive's scope).
*
* * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
* passed via the attribute `attr`. The expression is evaluated 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 the isolate scope definition `scope: {
* localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
* value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
* `localModel` and vice versa. If the binding expression is non-assignable, or if the attribute
* isn't optional and doesn't exist, an exception
* ({@link error/$compile/nonassign `$compile:nonassign`}) will be thrown upon discovering changes
* to the local value, since it will be impossible to sync them back to the parent scope.
*
* By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
* method is used for tracking changes, and the equality check is based on object identity.
* However, if an object literal or an array literal is passed as the binding expression, the
* equality check is done by value (using the {@link angular.equals} function). It's also possible
* to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
* `$watchCollection`}: use `=*` or `=*attr`
*
* * `<` or `` and directive definition of
* `scope: { localModel:'` and the isolate scope definition `scope: {
* localFn:'&myAttr' }`, the 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})`.
*
* All 4 kinds of bindings (`@`, `=`, `<`, and `&`) can be made optional by adding `?` to the expression.
* The marker must come after the mode and before the attribute name.
* See the {@link error/$compile/iscp Invalid Isolate Scope Definition error} for definition examples.
* This is useful to refine the interface directives provide.
* One subtle difference between optional and non-optional happens **when the binding attribute is not
* set**:
* - the binding is optional: the property will not be defined
* - the binding is not optional: the property is defined
*
* ```js
*app.directive('testDir', function() {
return {
scope: {
notoptional: '=',
optional: '=?',
},
bindToController: true,
controller: function() {
this.$onInit = function() {
console.log(this.hasOwnProperty('notoptional')) // true
console.log(this.hasOwnProperty('optional')) // false
}
}
}
})
*```
*
*
* ##### Combining directives with different scope defintions
*
* In general it's possible to apply more than one directive to one element, but there might be limitations
* depending on the type of scope required by the directives. The following points will help explain these limitations.
* For simplicity only two directives are taken into account, but it is also applicable for several directives:
*
* * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
* * **child scope** + **no scope** => Both directives will share one single child scope
* * **child scope** + **child scope** => Both directives will share one single child scope
* * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use
* its parent's scope
* * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
* be applied to the same element.
* * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives
* cannot be applied to the same element.
*
*
* #### `bindToController`
* This property is used to bind scope properties directly to the controller. It can be either
* `true` or an object hash with the same format as the `scope` property.
*
* When an isolate scope is used for a directive (see above), `bindToController: true` will
* allow a component to have its properties bound to the controller, rather than to scope.
*
* After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
* properties. You can access these bindings once they have been initialized by providing a controller method called
* `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
* initialized.
*
* It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
* This will set up the scope bindings to the controller directly. Note that `scope` can still be used
* to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
* scope (useful for component directives).
*
* If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
*
*
* #### `controller`
* Controller constructor function. The controller is instantiated before the
* pre-linking phase and can be accessed by 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, slotName)`:
* * `scope`: (optional) override the scope.
* * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
* * `futureParentElement` (optional):
* * 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 `cloneLinkingFn` 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 `