kms 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/kms/application.js +1 -0
- data/app/assets/javascripts/kms/application/controllers/assets_controller.coffee.erb +14 -4
- data/app/assets/javascripts/kms/application/controllers/pages_controller.coffee.erb +12 -2
- data/app/assets/javascripts/kms/application/controllers/snippets_controller.coffee.erb +13 -3
- data/app/assets/javascripts/kms/application/controllers/templates_controller.coffee.erb +13 -3
- data/app/assets/javascripts/kms/application/controllers/users_controller.coffee +5 -5
- data/app/assets/javascripts/kms/application/module.coffee +6 -2
- data/app/assets/javascripts/kms/application/routes.coffee.erb +10 -0
- data/app/assets/javascripts/templates/assets/edit.html.slim +2 -1
- data/app/assets/javascripts/templates/assets/form.html.slim +1 -1
- data/app/assets/javascripts/templates/pages/edit.html.slim +1 -0
- data/app/assets/javascripts/templates/shared/hotkey_notification.html.slim +6 -0
- data/app/assets/javascripts/templates/snippets/edit.html.slim +1 -0
- data/app/assets/javascripts/templates/templates/edit.html.slim +1 -0
- data/app/assets/javascripts/templates/users/edit.html.slim +5 -0
- data/app/assets/javascripts/templates/users/form.html.slim +3 -2
- data/app/assets/javascripts/templates/users/index.html.slim +2 -1
- data/app/assets/stylesheets/kms/custom.css.scss +10 -0
- data/app/controllers/kms/assets_controller.rb +6 -3
- data/app/controllers/kms/users_controller.rb +14 -0
- data/app/services/kms/resource_service.rb +3 -1
- data/app/views/layouts/kms/kms.html.erb +1 -1
- data/config/initializers/devise.rb +9 -0
- data/config/locales/en.yml +12 -0
- data/config/locales/ru.yml +12 -0
- data/config/routes.rb +1 -1
- data/lib/kms/engine.rb +1 -1
- data/lib/kms/version.rb +1 -1
- data/spec/controllers/kms/assets_controller_spec.rb +28 -10
- data/spec/controllers/kms/users_controller_spec.rb +23 -0
- data/spec/internal/config/routes.rb +1 -1
- data/spec/internal/log/test.log +0 -105823
- data/vendor/assets/bower.json +5 -4
- data/vendor/assets/bower_components/angular-cookies/angular-cookies.js +22 -18
- data/vendor/assets/bower_components/angular-cookies/angular-cookies.min.js +4 -4
- data/vendor/assets/bower_components/angular-cookies/angular-cookies.min.js.map +2 -2
- data/vendor/assets/bower_components/angular-cookies/bower.json +2 -2
- data/vendor/assets/bower_components/angular-cookies/package.json +1 -1
- data/vendor/assets/bower_components/angular-hotkeys/Gruntfile.js +118 -0
- data/vendor/assets/bower_components/angular-hotkeys/LICENSE +20 -0
- data/vendor/assets/bower_components/angular-hotkeys/README.md +248 -0
- data/vendor/assets/bower_components/angular-hotkeys/bower.json +19 -0
- data/vendor/assets/bower_components/angular-hotkeys/build/hotkeys.css +110 -0
- data/vendor/assets/bower_components/angular-hotkeys/build/hotkeys.js +1661 -0
- data/vendor/assets/bower_components/angular-hotkeys/build/hotkeys.min.css +1 -0
- data/vendor/assets/bower_components/angular-hotkeys/build/hotkeys.min.js +7 -0
- data/vendor/assets/bower_components/angular-hotkeys/package.json +45 -0
- data/vendor/assets/bower_components/angular-hotkeys/src/hotkeys.css +104 -0
- data/vendor/assets/bower_components/angular-hotkeys/src/hotkeys.js +633 -0
- data/vendor/assets/bower_components/angular-loading-bar/CHANGELOG.md +33 -0
- data/vendor/assets/bower_components/angular-loading-bar/CONTRIBUTING.md +17 -0
- data/vendor/assets/bower_components/angular-loading-bar/Gruntfile.js +9 -1
- data/vendor/assets/bower_components/angular-loading-bar/ISSUE_TEMPLATE.md +14 -0
- data/vendor/assets/bower_components/angular-loading-bar/PULL_REQUEST_TEMPLATE.md +13 -0
- data/vendor/assets/bower_components/angular-loading-bar/README.md +30 -3
- data/vendor/assets/bower_components/angular-loading-bar/bower.json +11 -6
- data/vendor/assets/bower_components/angular-loading-bar/build/loading-bar.css +5 -5
- data/vendor/assets/bower_components/angular-loading-bar/build/loading-bar.js +39 -12
- data/vendor/assets/bower_components/angular-loading-bar/build/loading-bar.min.css +1 -8
- data/vendor/assets/bower_components/angular-loading-bar/build/loading-bar.min.js +3 -3
- data/vendor/assets/bower_components/angular-loading-bar/index.js +2 -0
- data/vendor/assets/bower_components/angular-loading-bar/package.json +12 -15
- data/vendor/assets/bower_components/angular-loading-bar/src/loading-bar.css +3 -3
- data/vendor/assets/bower_components/angular-loading-bar/src/loading-bar.js +37 -10
- data/vendor/assets/bower_components/angular-sanitize/angular-sanitize.js +504 -386
- data/vendor/assets/bower_components/angular-sanitize/angular-sanitize.min.js +13 -12
- data/vendor/assets/bower_components/angular-sanitize/angular-sanitize.min.js.map +3 -3
- data/vendor/assets/bower_components/angular-sanitize/bower.json +2 -2
- data/vendor/assets/bower_components/angular-sanitize/package.json +1 -1
- data/vendor/assets/bower_components/angular-ui-router/CHANGELOG.md +1410 -0
- data/vendor/assets/bower_components/angular-ui-router/CONTRIBUTING.md +64 -16
- data/vendor/assets/bower_components/angular-ui-router/DOCS.md +48 -0
- data/vendor/assets/bower_components/angular-ui-router/ISSUE_TEMPLATE.md +53 -0
- data/vendor/assets/bower_components/angular-ui-router/LICENSE +1 -1
- data/vendor/assets/bower_components/angular-ui-router/README.md +24 -211
- data/vendor/assets/bower_components/angular-ui-router/artifacts.json +8 -0
- data/vendor/assets/bower_components/angular-ui-router/bower.json +1 -23
- data/vendor/assets/bower_components/angular-ui-router/karma.conf.js +105 -0
- data/vendor/assets/bower_components/angular-ui-router/release/angular-ui-router.js +9744 -3901
- data/vendor/assets/bower_components/angular-ui-router/release/angular-ui-router.js.map +192 -0
- data/vendor/assets/bower_components/angular-ui-router/release/angular-ui-router.min.js +9 -4
- data/vendor/assets/bower_components/angular-ui-router/release/angular-ui-router.min.js.map +1679 -0
- data/vendor/assets/bower_components/angular-ui-router/release/resolveService.js +83 -0
- data/vendor/assets/bower_components/angular-ui-router/release/resolveService.js.map +19 -0
- data/vendor/assets/bower_components/angular-ui-router/release/resolveService.min.js +8 -0
- data/vendor/assets/bower_components/angular-ui-router/release/resolveService.min.js.map +47 -0
- data/vendor/assets/bower_components/angular-ui-router/release/stateEvents.js +294 -0
- data/vendor/assets/bower_components/angular-ui-router/release/stateEvents.js.map +17 -0
- data/vendor/assets/bower_components/angular-ui-router/release/stateEvents.min.js +8 -0
- data/vendor/assets/bower_components/angular-ui-router/release/stateEvents.min.js.map +102 -0
- data/vendor/assets/bower_components/angular-ui-router/release/ui-router-angularjs.js +2014 -0
- data/vendor/assets/bower_components/angular-ui-router/release/ui-router-angularjs.js.map +70 -0
- data/vendor/assets/bower_components/angular-ui-router/release/ui-router-angularjs.min.js +9 -0
- data/vendor/assets/bower_components/angular-ui-router/release/ui-router-angularjs.min.js.map +541 -0
- data/vendor/assets/bower_components/angular-ui-router/rollup.config.js +116 -0
- data/vendor/assets/bower_components/angular-ui-router/tslint.json +60 -0
- data/vendor/assets/bower_components/angular-ui-router/yarn.lock +4146 -0
- data/vendor/assets/bower_components/angular-ui-tree/yarn.lock +4945 -0
- data/vendor/assets/bower_components/angular/angular.js +4019 -2449
- data/vendor/assets/bower_components/angular/angular.min.js +331 -319
- data/vendor/assets/bower_components/angular/angular.min.js.gzip +0 -0
- data/vendor/assets/bower_components/angular/angular.min.js.map +3 -3
- data/vendor/assets/bower_components/angular/bower.json +1 -1
- data/vendor/assets/bower_components/angular/package.json +1 -1
- data/vendor/assets/bower_components/angularjs-dropdown-multiselect/LICENSE +21 -0
- data/vendor/assets/bower_components/angularjs-dropdown-multiselect/README.md +14 -14
- data/vendor/assets/bower_components/angularjs-dropdown-multiselect/bower.json +25 -12
- data/vendor/assets/bower_components/angularjs-dropdown-multiselect/development_index.html +59 -52
- data/vendor/assets/bower_components/angularjs-dropdown-multiselect/dist/angularjs-dropdown-multiselect.min.js +1 -1
- data/vendor/assets/bower_components/angularjs-dropdown-multiselect/index.html +73 -0
- data/vendor/assets/bower_components/angularjs-dropdown-multiselect/package.json +19 -7
- data/vendor/assets/bower_components/angularjs-dropdown-multiselect/pages/javascripts/pages/home/ExampleCtrl.js +126 -3
- data/vendor/assets/bower_components/angularjs-dropdown-multiselect/pages/javascripts/pages/home/home.html +1262 -852
- data/vendor/assets/bower_components/angularjs-dropdown-multiselect/pages/stylesheets/stylesheet.css +10 -5
- data/vendor/assets/bower_components/angularjs-dropdown-multiselect/src/angularjs-dropdown-multiselect.js +612 -287
- metadata +66 -169
- data/spec/internal/config/database.yml +0 -7
- data/spec/internal/public/uploads/kms/asset/file/1/avatar.jpg +0 -0
- data/spec/internal/public/uploads/kms/asset/file/2/avatar.jpg +0 -0
- data/spec/internal/public/uploads/kms/asset/file/2/style.css +0 -1
- data/spec/internal/public/uploads/kms/asset/file/3/style.css +0 -1
- data/spec/internal/public/uploads/kms/asset/file/4/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500976987-41025-0002-0883/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500977082-41195-0002-6495/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500977109-41364-0002-4518/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500977152-41405-0002-2345/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500977327-41694-0002-5448/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500977376-41732-0002-7916/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500977392-41759-0002-7593/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500977410-42259-0002-7527/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500977429-42306-0002-5937/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500977437-42324-0002-5880/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500983228-53594-0002-4559/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500983284-53632-0002-6590/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500983360-53784-0002-7289/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500983469-54321-0002-0386/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500983469-54321-0004-5691/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500983511-54352-0002-5720/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500983511-54352-0004-1399/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500983610-54507-0002-4280/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500983610-54507-0004-9758/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500984466-57012-0002-4146/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500984466-57012-0004-5895/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500984509-57158-0002-9657/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500984509-57158-0004-5003/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500984616-57697-0002-7201/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500984616-57697-0004-6255/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500985257-58947-0002-3629/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500985257-58947-0004-5338/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500985407-58947-0006-5929/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500985473-59264-0002-0397/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500985473-59264-0004-6493/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500985475-59264-0007-8674/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500985538-59468-0002-9206/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500985538-59468-0004-2586/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500985538-59468-0007-6200/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500988358-65877-0002-4528/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500988358-65877-0004-5904/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500988358-65877-0007-7320/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500988407-65916-0002-3138/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500988407-65916-0004-5400/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500988407-65916-0007-1655/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500988421-65950-0002-9415/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500988421-65950-0004-7130/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500988421-65950-0007-9886/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500988435-65981-0002-3228/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500988435-65981-0004-3682/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500988435-65981-0007-1582/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500988475-66122-0002-9516/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500988475-66122-0004-5634/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500988530-66122-0007-2272/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500988554-66315-0002-6262/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500988554-66315-0004-6099/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500988554-66315-0007-1632/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500991751-73722-0002-9937/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1500991751-73722-0004-8034/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1500991751-73722-0007-7763/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1501233238-34385-0002-3210/avatar.jpg +0 -0
- data/spec/internal/public/uploads/tmp/1501233238-34385-0004-5881/style.css +0 -1
- data/spec/internal/public/uploads/tmp/1501233238-34385-0007-6280/style.css +0 -1
- data/spec/internal/tmp/cache/assets/test/sprockets/v3.0/1XyAFYlYI0pK7WAgjR4PgXV6BgU6huJSviWmHetdCRs.cache +0 -1
- data/vendor/assets/bower_components/angular-ui-router/api/angular-ui-router.d.ts +0 -126
- data/vendor/assets/bower_components/angular-ui-router/src/common.js +0 -292
- data/vendor/assets/bower_components/angular-ui-router/src/resolve.js +0 -252
- data/vendor/assets/bower_components/angular-ui-router/src/state.js +0 -1373
- data/vendor/assets/bower_components/angular-ui-router/src/stateDirectives.js +0 -268
- data/vendor/assets/bower_components/angular-ui-router/src/stateFilters.js +0 -39
- data/vendor/assets/bower_components/angular-ui-router/src/templateFactory.js +0 -110
- data/vendor/assets/bower_components/angular-ui-router/src/urlMatcherFactory.js +0 -1036
- data/vendor/assets/bower_components/angular-ui-router/src/urlRouter.js +0 -413
- data/vendor/assets/bower_components/angular-ui-router/src/view.js +0 -71
- data/vendor/assets/bower_components/angular-ui-router/src/viewDirective.js +0 -302
- data/vendor/assets/bower_components/angular-ui-router/src/viewScroll.js +0 -52
- data/vendor/assets/bower_components/angularjs-dropdown-multiselect/pages/index.html +0 -67
- data/vendor/assets/bower_components/bootstrap/Gemfile.lock +0 -43
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            .cfp-hotkeys,.cfp-hotkeys-container{width:100%;height:100%}.cfp-hotkeys-container{display:table!important;position:fixed;top:0;left:0;color:#333;font-size:1em;background-color:rgba(255,255,255,.9)}.cfp-content,.cfp-hotkeys{display:table-cell;vertical-align:middle}.cfp-hotkeys-container.fade{z-index:-1024;visibility:hidden;opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.cfp-hotkeys-container.fade.in{z-index:10002;visibility:visible;opacity:1}.cfp-hotkeys-title{font-weight:700;text-align:center;font-size:1.2em}.cfp-hotkeys table{margin:auto;color:#333}.cfp-hotkeys-keys{padding:5px;text-align:right}.cfp-hotkeys-key{display:inline-block;color:#fff;background-color:#333;border:1px solid #333;border-radius:5px;text-align:center;margin-right:5px;box-shadow:inset 0 1px 0 #666,0 1px 0 #bbb;padding:5px 9px;font-size:1em}.cfp-hotkeys-text{padding-left:10px;font-size:1em}.cfp-hotkeys-close{position:fixed;top:20px;right:20px;font-size:2em;font-weight:700;padding:5px 10px;border:1px solid #ddd;border-radius:5px;min-height:45px;min-width:45px;text-align:center}.cfp-hotkeys-close:hover{background-color:#fff;cursor:pointer}@media all and (max-width:500px){.cfp-hotkeys{font-size:.8em}}@media all and (min-width:750px){.cfp-hotkeys{font-size:1.2em}}
         | 
| @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            /*! 
         | 
| 2 | 
            +
             * angular-hotkeys v1.7.0
         | 
| 3 | 
            +
             * https://chieffancypants.github.io/angular-hotkeys
         | 
| 4 | 
            +
             * Copyright (c) 2016 Wes Cruver
         | 
| 5 | 
            +
             * License: MIT
         | 
| 6 | 
            +
             */
         | 
| 7 | 
            +
            !function(){"use strict";angular.module("cfp.hotkeys",[]).provider("hotkeys",["$injector",function(a){this.includeCheatSheet=!0,this.useNgRoute=a.has("ngViewDirective"),this.templateTitle="Keyboard Shortcuts:",this.templateHeader=null,this.templateFooter=null,this.template='<div class="cfp-hotkeys-container fade" ng-class="{in: helpVisible}" style="display: none;"><div class="cfp-hotkeys"><h4 class="cfp-hotkeys-title" ng-if="!header">{{ title }}</h4><div ng-bind-html="header" ng-if="header"></div><table><tbody><tr ng-repeat="hotkey in hotkeys | filter:{ description: \'!$$undefined$$\' }"><td class="cfp-hotkeys-keys"><span ng-repeat="key in hotkey.format() track by $index" class="cfp-hotkeys-key">{{ key }}</span></td><td class="cfp-hotkeys-text">{{ hotkey.description }}</td></tr></tbody></table><div ng-bind-html="footer" ng-if="footer"></div><div class="cfp-hotkeys-close" ng-click="toggleCheatSheet()">×</div></div></div>',this.cheatSheetHotkey="?",this.cheatSheetDescription="Show / hide this help menu",this.$get=["$rootElement","$rootScope","$compile","$window","$document",function(a,b,c,d,e){function f(){q=!1}function g(){q=!0}function h(a){var b={command:"⌘",shift:"⇧",left:"←",right:"→",up:"↑",down:"↓","return":"⏎",backspace:"⌫"};a=a.split("+");for(var c=0;c<a.length;c++)"mod"===a[c]&&(d.navigator&&d.navigator.platform.indexOf("Mac")>=0?a[c]="command":a[c]="ctrl"),a[c]=b[a[c]]||a[c];return a.join(" + ")}function i(a,b,c,d,e,f){this.combo=a instanceof Array?a:[a],this.description=b,this.callback=c,this.action=d,this.allowIn=e,this.persistent=f,this._formated=null}function j(){for(var a=r.hotkeys.length;a--;){var b=r.hotkeys[a];b&&!b.persistent&&m(b)}}function k(){r.helpVisible=!r.helpVisible,r.helpVisible?(w=n("esc"),m("esc"),l("esc",w.description,k,null,["INPUT","SELECT","TEXTAREA"])):(m("esc"),w!==!1&&l(w))}function l(a,b,c,d,e,f){var g,h=["INPUT","SELECT","TEXTAREA"],j=Object.prototype.toString.call(a);if("[object Object]"===j&&(b=a.description,c=a.callback,d=a.action,f=a.persistent,e=a.allowIn,a=a.combo),m(a),b instanceof Function?(d=c,c=b,b="$$undefined$$"):angular.isUndefined(b)&&(b="$$undefined$$"),void 0===f&&(f=!0),"function"==typeof c){g=c,e instanceof Array||(e=[]);for(var k,l=0;l<e.length;l++)e[l]=e[l].toUpperCase(),k=h.indexOf(e[l]),-1!==k&&h.splice(k,1);c=function(a){var b=!0;if(a){var c=a.target||a.srcElement,d=c.nodeName.toUpperCase();if((" "+c.className+" ").indexOf(" mousetrap ")>-1)b=!0;else for(var e=0;e<h.length;e++)if(h[e]===d){b=!1;break}}b&&p(g.apply(this,arguments))}}"string"==typeof d?Mousetrap.bind(a,p(c),d):Mousetrap.bind(a,p(c));var n=new i(a,b,c,d,e,f);return r.hotkeys.push(n),n}function m(a){var b=a instanceof i?a.combo:a;if(Mousetrap.unbind(b),angular.isArray(b)){for(var c=!0,d=b.length;d--;)c=m(b[d])&&c;return c}var e=r.hotkeys.indexOf(n(b));return e>-1?(r.hotkeys[e].combo.length>1?r.hotkeys[e].combo.splice(r.hotkeys[e].combo.indexOf(b),1):(angular.forEach(s,function(a){var b=a.indexOf(r.hotkeys[e]);-1!==b&&a.splice(b,1)}),r.hotkeys.splice(e,1)),!0):!1}function n(a){if(!a)return r.hotkeys;for(var b,c=0;c<r.hotkeys.length;c++)if(b=r.hotkeys[c],b.combo.indexOf(a)>-1)return b;return!1}function o(a){return a.$id in s||(s[a.$id]=[],a.$on("$destroy",function(){for(var b=s[a.$id].length;b--;)m(s[a.$id].pop())})),{add:function(b){var c;return c=arguments.length>1?l.apply(this,arguments):l(b),s[a.$id].push(c),this}}}function p(a){return function(c,d){if(a instanceof Array){var e=a[0],f=a[1];a=function(a){f.scope.$eval(e)}}b.$apply(function(){a(c,n(d))})}}var q=!0;Mousetrap.prototype.stopCallback=function(a,b){return q?(" "+b.className+" ").indexOf(" mousetrap ")>-1?!1:b.contentEditable&&"true"==b.contentEditable:!0},i.prototype.format=function(){if(null===this._formated){for(var a=this.combo[0],b=a.split(/[\s]/),c=0;c<b.length;c++)b[c]=h(b[c]);this._formated=b}return this._formated};var r=b.$new();r.hotkeys=[],r.helpVisible=!1,r.title=this.templateTitle,r.header=this.templateHeader,r.footer=this.templateFooter,r.toggleCheatSheet=k;var s={};if(this.useNgRoute&&b.$on("$routeChangeSuccess",function(a,b){j(),b&&b.hotkeys&&angular.forEach(b.hotkeys,function(a){var c=a[2];("string"==typeof c||c instanceof String)&&(a[2]=[c,b]),a[5]=!1,l.apply(this,a)})}),this.includeCheatSheet){var t=e[0],u=a[0],v=angular.element(this.template);l(this.cheatSheetHotkey,this.cheatSheetDescription,k),(u===t||u===t.documentElement)&&(u=t.body),angular.element(u).append(c(v)(r))}var w=!1,x={add:l,del:m,get:n,bindTo:o,template:this.template,toggleCheatSheet:k,includeCheatSheet:this.includeCheatSheet,cheatSheetHotkey:this.cheatSheetHotkey,cheatSheetDescription:this.cheatSheetDescription,useNgRoute:this.useNgRoute,purgeHotkeys:j,templateTitle:this.templateTitle,pause:f,unpause:g};return x}]}]).directive("hotkey",["hotkeys",function(a){return{restrict:"A",link:function(b,c,d){var e,f=[];angular.forEach(b.$eval(d.hotkey),function(b,c){e="string"==typeof d.hotkeyAllowIn?d.hotkeyAllowIn.split(/[\s,]+/):[],f.push(c),a.add({combo:c,description:d.hotkeyDescription,callback:b,action:d.hotkeyAction,allowIn:e})}),c.bind("$destroy",function(){angular.forEach(f,a.del)})}}}]).run(["hotkeys",function(a){}])}(),function(a,b,c){function d(a,b,c){return a.addEventListener?void a.addEventListener(b,c,!1):void a.attachEvent("on"+b,c)}function e(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);return a.shiftKey||(b=b.toLowerCase()),b}return r[a.which]?r[a.which]:s[a.which]?s[a.which]:String.fromCharCode(a.which).toLowerCase()}function f(a,b){return a.sort().join(",")===b.sort().join(",")}function g(a){var b=[];return a.shiftKey&&b.push("shift"),a.altKey&&b.push("alt"),a.ctrlKey&&b.push("ctrl"),a.metaKey&&b.push("meta"),b}function h(a){return a.preventDefault?void a.preventDefault():void(a.returnValue=!1)}function i(a){return a.stopPropagation?void a.stopPropagation():void(a.cancelBubble=!0)}function j(a){return"shift"==a||"ctrl"==a||"alt"==a||"meta"==a}function k(){if(!q){q={};for(var a in r)a>95&&112>a||r.hasOwnProperty(a)&&(q[r[a]]=a)}return q}function l(a,b,c){return c||(c=k()[a]?"keydown":"keypress"),"keypress"==c&&b.length&&(c="keydown"),c}function m(a){return"+"===a?["+"]:(a=a.replace(/\+{2}/g,"+plus"),a.split("+"))}function n(a,b){var c,d,e,f=[];for(c=m(a),e=0;e<c.length;++e)d=c[e],u[d]&&(d=u[d]),b&&"keypress"!=b&&t[d]&&(d=t[d],f.push("shift")),j(d)&&f.push(d);return b=l(d,f,b),{key:d,modifiers:f,action:b}}function o(a,c){return a===b?!1:a===c?!0:o(a.parentNode,c)}function p(a){function c(a){a=a||{};var b,c=!1;for(b in u)a[b]?c=!0:u[b]=0;c||(x=!1)}function k(a,b,c,d,e,g){var h,i,k=[],l=c.type;if(!s._callbacks[a])return[];for("keyup"==l&&j(a)&&(b=[a]),h=0;h<s._callbacks[a].length;++h)if(i=s._callbacks[a][h],(d||!i.seq||u[i.seq]==i.level)&&l==i.action&&("keypress"==l&&!c.metaKey&&!c.ctrlKey||f(b,i.modifiers))){var m=!d&&i.combo==e,n=d&&i.seq==d&&i.level==g;(m||n)&&s._callbacks[a].splice(h,1),k.push(i)}return k}function l(a,b,c,d){s.stopCallback(b,b.target||b.srcElement,c,d)||a(b,c)===!1&&(h(b),i(b))}function m(a){"number"!=typeof a.which&&(a.which=a.keyCode);var b=e(a);if(b)return"keyup"==a.type&&v===b?void(v=!1):void s.handleKey(b,g(a),a)}function o(){clearTimeout(t),t=setTimeout(c,1e3)}function q(a,b,d,f){function g(b){return function(){x=b,++u[a],o()}}function h(b){l(d,b,a),"keyup"!==f&&(v=e(b)),setTimeout(c,10)}u[a]=0;for(var i=0;i<b.length;++i){var j=i+1===b.length,k=j?h:g(f||n(b[i+1]).action);r(b[i],k,f,a,i)}}function r(a,b,c,d,e){s._directMap[a+":"+c]=b,a=a.replace(/\s+/g," ");var f,g=a.split(" ");return g.length>1?void q(a,g,b,c):(f=n(a,c),s._callbacks[f.key]=s._callbacks[f.key]||[],k(f.key,f.modifiers,{type:f.action},d,a,e),void s._callbacks[f.key][d?"unshift":"push"]({callback:b,modifiers:f.modifiers,action:f.action,seq:d,level:e,combo:a}))}var s=this;if(a=a||b,!(s instanceof p))return new p(a);s.target=a,s._callbacks={},s._directMap={};var t,u={},v=!1,w=!1,x=!1;s._handleKey=function(a,b,d){var e,f=k(a,b,d),g={},h=0,i=!1;for(e=0;e<f.length;++e)f[e].seq&&(h=Math.max(h,f[e].level));for(e=0;e<f.length;++e)if(f[e].seq){if(f[e].level!=h)continue;i=!0,g[f[e].seq]=1,l(f[e].callback,d,f[e].combo,f[e].seq)}else i||l(f[e].callback,d,f[e].combo);var m="keypress"==d.type&&w;d.type!=x||j(a)||m||c(g),w=i&&"keydown"==d.type},s._bindMultiple=function(a,b,c){for(var d=0;d<a.length;++d)r(a[d],b,c)},d(a,"keypress",m),d(a,"keydown",m),d(a,"keyup",m)}for(var q,r={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},s={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},t={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},u={option:"alt",command:"meta","return":"enter",escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},v=1;20>v;++v)r[111+v]="f"+v;for(v=0;9>=v;++v)r[v+96]=v;p.prototype.bind=function(a,b,c){var d=this;return a=a instanceof Array?a:[a],d._bindMultiple.call(d,a,b,c),d},p.prototype.unbind=function(a,b){var c=this;return c.bind.call(c,a,function(){},b)},p.prototype.trigger=function(a,b){var c=this;return c._directMap[a+":"+b]&&c._directMap[a+":"+b]({},a),c},p.prototype.reset=function(){var a=this;return a._callbacks={},a._directMap={},a},p.prototype.stopCallback=function(a,b){var c=this;return(" "+b.className+" ").indexOf(" mousetrap ")>-1?!1:o(b,c.target)?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable},p.prototype.handleKey=function(){var a=this;return a._handleKey.apply(a,arguments)},p.init=function(){var a=p(b);for(var c in a)"_"!==c.charAt(0)&&(p[c]=function(b){return function(){return a[b].apply(a,arguments)}}(c))},p.init(),a.Mousetrap=p,"undefined"!=typeof module&&module.exports&&(module.exports=p),"function"==typeof define&&define.amd&&define(function(){return p})}(window,document);
         | 
| @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            {
         | 
| 2 | 
            +
              "name": "angular-hotkeys",
         | 
| 3 | 
            +
              "author": "Wes Cruver",
         | 
| 4 | 
            +
              "version": "1.7.0",
         | 
| 5 | 
            +
              "license": "MIT",
         | 
| 6 | 
            +
              "description": "Automatic keyboard shortcuts for your Angular Apps",
         | 
| 7 | 
            +
              "homepage": "https://chieffancypants.github.io/angular-hotkeys",
         | 
| 8 | 
            +
              "main": "build/hotkeys.js",
         | 
| 9 | 
            +
              "keywords": [
         | 
| 10 | 
            +
                "angular",
         | 
| 11 | 
            +
                "angularjs",
         | 
| 12 | 
            +
                "keyboard",
         | 
| 13 | 
            +
                "shortcut",
         | 
| 14 | 
            +
                "hotkeys"
         | 
| 15 | 
            +
              ],
         | 
| 16 | 
            +
              "repository": {
         | 
| 17 | 
            +
                "type": "git",
         | 
| 18 | 
            +
                "url": "git://github.com/chieffancypants/angular-hotkeys.git"
         | 
| 19 | 
            +
              },
         | 
| 20 | 
            +
              "bugs": {
         | 
| 21 | 
            +
                "url": "https://github.com/chieffancypants/angular-hotkeys/issues"
         | 
| 22 | 
            +
              },
         | 
| 23 | 
            +
              "scripts": {
         | 
| 24 | 
            +
                "test": "node_modules/karma/bin/karma start test/karma.conf.js"
         | 
| 25 | 
            +
              },
         | 
| 26 | 
            +
              "devDependencies": {
         | 
| 27 | 
            +
                "grunt": "~0.4.1",
         | 
| 28 | 
            +
                "grunt-contrib-concat": "^0.5.1",
         | 
| 29 | 
            +
                "grunt-contrib-cssmin": "^0.12.3",
         | 
| 30 | 
            +
                "grunt-contrib-jshint": "~0.6.4",
         | 
| 31 | 
            +
                "grunt-contrib-uglify": "^0.9.1",
         | 
| 32 | 
            +
                "grunt-contrib-watch": "^0.6.1",
         | 
| 33 | 
            +
                "grunt-karma": "^0.11.0",
         | 
| 34 | 
            +
                "grunt-ng-annotate": "^0.3.0",
         | 
| 35 | 
            +
                "karma": "~0.12.0",
         | 
| 36 | 
            +
                "karma-chrome-launcher": "~0.1.0",
         | 
| 37 | 
            +
                "karma-coffee-preprocessor": "~0.1.0",
         | 
| 38 | 
            +
                "karma-coverage": "~0.1.0",
         | 
| 39 | 
            +
                "karma-firefox-launcher": "~0.1.0",
         | 
| 40 | 
            +
                "karma-html2js-preprocessor": "~0.1.0",
         | 
| 41 | 
            +
                "karma-jasmine": "~0.1.3",
         | 
| 42 | 
            +
                "karma-phantomjs-launcher": "^0.2.0",
         | 
| 43 | 
            +
                "karma-script-launcher": "~0.1.0"
         | 
| 44 | 
            +
              }
         | 
| 45 | 
            +
            }
         | 
| @@ -0,0 +1,104 @@ | |
| 1 | 
            +
            .cfp-hotkeys-container {
         | 
| 2 | 
            +
              display: table !important;
         | 
| 3 | 
            +
              position: fixed;
         | 
| 4 | 
            +
              width: 100%;
         | 
| 5 | 
            +
              height: 100%;
         | 
| 6 | 
            +
              top: 0;
         | 
| 7 | 
            +
              left: 0;
         | 
| 8 | 
            +
              color: #333;
         | 
| 9 | 
            +
              font-size: 1em;
         | 
| 10 | 
            +
              background-color: rgba(255,255,255,0.9);
         | 
| 11 | 
            +
            }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            .cfp-hotkeys-container.fade {
         | 
| 14 | 
            +
              z-index: -1024;
         | 
| 15 | 
            +
              visibility: hidden;
         | 
| 16 | 
            +
              opacity: 0;
         | 
| 17 | 
            +
              -webkit-transition: opacity 0.15s linear;
         | 
| 18 | 
            +
              -moz-transition: opacity 0.15s linear;
         | 
| 19 | 
            +
              -o-transition: opacity 0.15s linear;
         | 
| 20 | 
            +
              transition: opacity 0.15s linear;
         | 
| 21 | 
            +
            }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            .cfp-hotkeys-container.fade.in {
         | 
| 24 | 
            +
              z-index: 10002;
         | 
| 25 | 
            +
              visibility: visible;
         | 
| 26 | 
            +
              opacity: 1;
         | 
| 27 | 
            +
            }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            .cfp-hotkeys-title {
         | 
| 30 | 
            +
              font-weight: bold;
         | 
| 31 | 
            +
              text-align: center;
         | 
| 32 | 
            +
              font-size: 1.2em;
         | 
| 33 | 
            +
            }
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            .cfp-hotkeys {
         | 
| 36 | 
            +
              width: 100%;
         | 
| 37 | 
            +
              height: 100%;
         | 
| 38 | 
            +
              display: table-cell;
         | 
| 39 | 
            +
              vertical-align: middle;
         | 
| 40 | 
            +
            }
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            .cfp-hotkeys table {
         | 
| 43 | 
            +
              margin: auto;
         | 
| 44 | 
            +
              color: #333;
         | 
| 45 | 
            +
            }
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            .cfp-content {
         | 
| 48 | 
            +
              display: table-cell;
         | 
| 49 | 
            +
              vertical-align: middle;
         | 
| 50 | 
            +
            }
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            .cfp-hotkeys-keys {
         | 
| 53 | 
            +
              padding: 5px;
         | 
| 54 | 
            +
              text-align: right;
         | 
| 55 | 
            +
            }
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            .cfp-hotkeys-key {
         | 
| 58 | 
            +
              display: inline-block;
         | 
| 59 | 
            +
              color: #fff;
         | 
| 60 | 
            +
              background-color: #333;
         | 
| 61 | 
            +
              border: 1px solid #333;
         | 
| 62 | 
            +
              border-radius: 5px;
         | 
| 63 | 
            +
              text-align: center;
         | 
| 64 | 
            +
              margin-right: 5px;
         | 
| 65 | 
            +
              box-shadow: inset 0 1px 0 #666, 0 1px 0 #bbb;
         | 
| 66 | 
            +
              padding: 5px 9px;
         | 
| 67 | 
            +
              font-size: 1em;
         | 
| 68 | 
            +
            }
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            .cfp-hotkeys-text {
         | 
| 71 | 
            +
              padding-left: 10px;
         | 
| 72 | 
            +
              font-size: 1em;
         | 
| 73 | 
            +
            }
         | 
| 74 | 
            +
             | 
| 75 | 
            +
            .cfp-hotkeys-close {
         | 
| 76 | 
            +
              position: fixed;
         | 
| 77 | 
            +
              top: 20px;
         | 
| 78 | 
            +
              right: 20px;
         | 
| 79 | 
            +
              font-size: 2em;
         | 
| 80 | 
            +
              font-weight: bold;
         | 
| 81 | 
            +
              padding: 5px 10px;
         | 
| 82 | 
            +
              border: 1px solid #ddd;
         | 
| 83 | 
            +
              border-radius: 5px;
         | 
| 84 | 
            +
              min-height: 45px;
         | 
| 85 | 
            +
              min-width: 45px;
         | 
| 86 | 
            +
              text-align: center;
         | 
| 87 | 
            +
            }
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            .cfp-hotkeys-close:hover {
         | 
| 90 | 
            +
              background-color: #fff;
         | 
| 91 | 
            +
              cursor: pointer;
         | 
| 92 | 
            +
            }
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            @media all and (max-width: 500px) {
         | 
| 95 | 
            +
              .cfp-hotkeys {
         | 
| 96 | 
            +
                font-size: 0.8em;
         | 
| 97 | 
            +
              }
         | 
| 98 | 
            +
            }
         | 
| 99 | 
            +
             | 
| 100 | 
            +
            @media all and (min-width: 750px) {
         | 
| 101 | 
            +
              .cfp-hotkeys {
         | 
| 102 | 
            +
                font-size: 1.2em;
         | 
| 103 | 
            +
              }
         | 
| 104 | 
            +
            }
         | 
| @@ -0,0 +1,633 @@ | |
| 1 | 
            +
            /*
         | 
| 2 | 
            +
             * angular-hotkeys
         | 
| 3 | 
            +
             *
         | 
| 4 | 
            +
             * Automatic keyboard shortcuts for your angular apps
         | 
| 5 | 
            +
             *
         | 
| 6 | 
            +
             * (c) 2016 Wes Cruver
         | 
| 7 | 
            +
             * License: MIT
         | 
| 8 | 
            +
             */
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            (function() {
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              'use strict';
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              angular.module('cfp.hotkeys', []).provider('hotkeys', function($injector) {
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                /**
         | 
| 17 | 
            +
                 * Configurable setting to disable the cheatsheet entirely
         | 
| 18 | 
            +
                 * @type {Boolean}
         | 
| 19 | 
            +
                 */
         | 
| 20 | 
            +
                this.includeCheatSheet = true;
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                /**
         | 
| 23 | 
            +
                 * Configurable setting to disable ngRoute hooks
         | 
| 24 | 
            +
                 * @type {Boolean}
         | 
| 25 | 
            +
                 */
         | 
| 26 | 
            +
                this.useNgRoute = $injector.has('ngViewDirective');
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                /**
         | 
| 29 | 
            +
                 * Configurable setting for the cheat sheet title
         | 
| 30 | 
            +
                 * @type {String}
         | 
| 31 | 
            +
                 */
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                this.templateTitle = 'Keyboard Shortcuts:';
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                /**
         | 
| 36 | 
            +
                 * Configurable settings for the cheat sheet header and footer.  Both are HTML, and the header
         | 
| 37 | 
            +
                 * overrides the normal title if specified.
         | 
| 38 | 
            +
                 * @type {String}
         | 
| 39 | 
            +
                 */
         | 
| 40 | 
            +
                this.templateHeader = null;
         | 
| 41 | 
            +
                this.templateFooter = null;
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                /**
         | 
| 44 | 
            +
                 * Cheat sheet template in the event you want to totally customize it.
         | 
| 45 | 
            +
                 * @type {String}
         | 
| 46 | 
            +
                 */
         | 
| 47 | 
            +
                this.template = '<div class="cfp-hotkeys-container fade" ng-class="{in: helpVisible}" style="display: none;"><div class="cfp-hotkeys">' +
         | 
| 48 | 
            +
                                  '<h4 class="cfp-hotkeys-title" ng-if="!header">{{ title }}</h4>' +
         | 
| 49 | 
            +
                                  '<div ng-bind-html="header" ng-if="header"></div>' +
         | 
| 50 | 
            +
                                  '<table><tbody>' +
         | 
| 51 | 
            +
                                    '<tr ng-repeat="hotkey in hotkeys | filter:{ description: \'!$$undefined$$\' }">' +
         | 
| 52 | 
            +
                                      '<td class="cfp-hotkeys-keys">' +
         | 
| 53 | 
            +
                                        '<span ng-repeat="key in hotkey.format() track by $index" class="cfp-hotkeys-key">{{ key }}</span>' +
         | 
| 54 | 
            +
                                      '</td>' +
         | 
| 55 | 
            +
                                      '<td class="cfp-hotkeys-text">{{ hotkey.description }}</td>' +
         | 
| 56 | 
            +
                                    '</tr>' +
         | 
| 57 | 
            +
                                  '</tbody></table>' +
         | 
| 58 | 
            +
                                  '<div ng-bind-html="footer" ng-if="footer"></div>' +
         | 
| 59 | 
            +
                                  '<div class="cfp-hotkeys-close" ng-click="toggleCheatSheet()">×</div>' +
         | 
| 60 | 
            +
                                '</div></div>';
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                /**
         | 
| 63 | 
            +
                 * Configurable setting for the cheat sheet hotkey
         | 
| 64 | 
            +
                 * @type {String}
         | 
| 65 | 
            +
                 */
         | 
| 66 | 
            +
                this.cheatSheetHotkey = '?';
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                /**
         | 
| 69 | 
            +
                 * Configurable setting for the cheat sheet description
         | 
| 70 | 
            +
                 * @type {String}
         | 
| 71 | 
            +
                 */
         | 
| 72 | 
            +
                this.cheatSheetDescription = 'Show / hide this help menu';
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                this.$get = function ($rootElement, $rootScope, $compile, $window, $document) {
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  var mouseTrapEnabled = true;
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  function pause() {
         | 
| 79 | 
            +
                    mouseTrapEnabled = false;
         | 
| 80 | 
            +
                  }
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  function unpause() {
         | 
| 83 | 
            +
                    mouseTrapEnabled = true;
         | 
| 84 | 
            +
                  }
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  // monkeypatch Mousetrap's stopCallback() function
         | 
| 87 | 
            +
                  // this version doesn't return true when the element is an INPUT, SELECT, or TEXTAREA
         | 
| 88 | 
            +
                  // (instead we will perform this check per-key in the _add() method)
         | 
| 89 | 
            +
                  Mousetrap.prototype.stopCallback = function(event, element) {
         | 
| 90 | 
            +
                    if (!mouseTrapEnabled) {
         | 
| 91 | 
            +
                      return true;
         | 
| 92 | 
            +
                    }
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    // if the element has the class "mousetrap" then no need to stop
         | 
| 95 | 
            +
                    if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
         | 
| 96 | 
            +
                      return false;
         | 
| 97 | 
            +
                    }
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                    return (element.contentEditable && element.contentEditable == 'true');
         | 
| 100 | 
            +
                  };
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  /**
         | 
| 103 | 
            +
                   * Convert strings like cmd into symbols like ⌘
         | 
| 104 | 
            +
                   * @param  {String} combo Key combination, e.g. 'mod+f'
         | 
| 105 | 
            +
                   * @return {String}       The key combination with symbols
         | 
| 106 | 
            +
                   */
         | 
| 107 | 
            +
                  function symbolize (combo) {
         | 
| 108 | 
            +
                    var map = {
         | 
| 109 | 
            +
                      command   : '\u2318',     // ⌘
         | 
| 110 | 
            +
                      shift     : '\u21E7',     // ⇧
         | 
| 111 | 
            +
                      left      : '\u2190',     // ←
         | 
| 112 | 
            +
                      right     : '\u2192',     // →
         | 
| 113 | 
            +
                      up        : '\u2191',     // ↑
         | 
| 114 | 
            +
                      down      : '\u2193',     // ↓
         | 
| 115 | 
            +
                      'return'  : '\u23CE',     // ⏎
         | 
| 116 | 
            +
                      backspace : '\u232B'      // ⌫
         | 
| 117 | 
            +
                    };
         | 
| 118 | 
            +
                    combo = combo.split('+');
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                    for (var i = 0; i < combo.length; i++) {
         | 
| 121 | 
            +
                      // try to resolve command / ctrl based on OS:
         | 
| 122 | 
            +
                      if (combo[i] === 'mod') {
         | 
| 123 | 
            +
                        if ($window.navigator && $window.navigator.platform.indexOf('Mac') >=0 ) {
         | 
| 124 | 
            +
                          combo[i] = 'command';
         | 
| 125 | 
            +
                        } else {
         | 
| 126 | 
            +
                          combo[i] = 'ctrl';
         | 
| 127 | 
            +
                        }
         | 
| 128 | 
            +
                      }
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                      combo[i] = map[combo[i]] || combo[i];
         | 
| 131 | 
            +
                    }
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                    return combo.join(' + ');
         | 
| 134 | 
            +
                  }
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                  /**
         | 
| 137 | 
            +
                   * Hotkey object used internally for consistency
         | 
| 138 | 
            +
                   *
         | 
| 139 | 
            +
                   * @param {array}    combo       The keycombo. it's an array to support multiple combos
         | 
| 140 | 
            +
                   * @param {String}   description Description for the keycombo
         | 
| 141 | 
            +
                   * @param {Function} callback    function to execute when keycombo pressed
         | 
| 142 | 
            +
                   * @param {string}   action      the type of event to listen for (for mousetrap)
         | 
| 143 | 
            +
                   * @param {array}    allowIn     an array of tag names to allow this combo in ('INPUT', 'SELECT', and/or 'TEXTAREA')
         | 
| 144 | 
            +
                   * @param {Boolean}  persistent  Whether the hotkey persists navigation events
         | 
| 145 | 
            +
                   */
         | 
| 146 | 
            +
                  function Hotkey (combo, description, callback, action, allowIn, persistent) {
         | 
| 147 | 
            +
                    // TODO: Check that the values are sane because we could
         | 
| 148 | 
            +
                    // be trying to instantiate a new Hotkey with outside dev's
         | 
| 149 | 
            +
                    // supplied values
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                    this.combo = combo instanceof Array ? combo : [combo];
         | 
| 152 | 
            +
                    this.description = description;
         | 
| 153 | 
            +
                    this.callback = callback;
         | 
| 154 | 
            +
                    this.action = action;
         | 
| 155 | 
            +
                    this.allowIn = allowIn;
         | 
| 156 | 
            +
                    this.persistent = persistent;
         | 
| 157 | 
            +
                    this._formated = null;
         | 
| 158 | 
            +
                  }
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                  /**
         | 
| 161 | 
            +
                   * Helper method to format (symbolize) the key combo for display
         | 
| 162 | 
            +
                   *
         | 
| 163 | 
            +
                   * @return {[Array]} An array of the key combination sequence
         | 
| 164 | 
            +
                   *   for example: "command+g c i" becomes ["⌘ + g", "c", "i"]
         | 
| 165 | 
            +
                   *
         | 
| 166 | 
            +
                   */
         | 
| 167 | 
            +
                  Hotkey.prototype.format = function() {
         | 
| 168 | 
            +
                    if (this._formated === null) {
         | 
| 169 | 
            +
                      // Don't show all the possible key combos, just the first one.  Not sure
         | 
| 170 | 
            +
                      // of usecase here, so open a ticket if my assumptions are wrong
         | 
| 171 | 
            +
                      var combo = this.combo[0];
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                      var sequence = combo.split(/[\s]/);
         | 
| 174 | 
            +
                      for (var i = 0; i < sequence.length; i++) {
         | 
| 175 | 
            +
                        sequence[i] = symbolize(sequence[i]);
         | 
| 176 | 
            +
                      }
         | 
| 177 | 
            +
                      this._formated = sequence;
         | 
| 178 | 
            +
                    }
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                    return this._formated;
         | 
| 181 | 
            +
                  };
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                  /**
         | 
| 184 | 
            +
                   * A new scope used internally for the cheatsheet
         | 
| 185 | 
            +
                   * @type {$rootScope.Scope}
         | 
| 186 | 
            +
                   */
         | 
| 187 | 
            +
                  var scope = $rootScope.$new();
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                  /**
         | 
| 190 | 
            +
                   * Holds an array of Hotkey objects currently bound
         | 
| 191 | 
            +
                   * @type {Array}
         | 
| 192 | 
            +
                   */
         | 
| 193 | 
            +
                  scope.hotkeys = [];
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                  /**
         | 
| 196 | 
            +
                   * Contains the state of the help's visibility
         | 
| 197 | 
            +
                   * @type {Boolean}
         | 
| 198 | 
            +
                   */
         | 
| 199 | 
            +
                  scope.helpVisible = false;
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                  /**
         | 
| 202 | 
            +
                   * Holds the title string for the help menu
         | 
| 203 | 
            +
                   * @type {String}
         | 
| 204 | 
            +
                   */
         | 
| 205 | 
            +
                  scope.title = this.templateTitle;
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                  /**
         | 
| 208 | 
            +
                   * Holds the header HTML for the help menu
         | 
| 209 | 
            +
                   * @type {String}
         | 
| 210 | 
            +
                   */
         | 
| 211 | 
            +
                  scope.header = this.templateHeader;
         | 
| 212 | 
            +
             | 
| 213 | 
            +
                  /**
         | 
| 214 | 
            +
                   * Holds the footer HTML for the help menu
         | 
| 215 | 
            +
                   * @type {String}
         | 
| 216 | 
            +
                   */
         | 
| 217 | 
            +
                  scope.footer = this.templateFooter;
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                  /**
         | 
| 220 | 
            +
                   * Expose toggleCheatSheet to hotkeys scope so we can call it using
         | 
| 221 | 
            +
                   * ng-click from the template
         | 
| 222 | 
            +
                   * @type {function}
         | 
| 223 | 
            +
                   */
         | 
| 224 | 
            +
                  scope.toggleCheatSheet = toggleCheatSheet;
         | 
| 225 | 
            +
             | 
| 226 | 
            +
             | 
| 227 | 
            +
                  /**
         | 
| 228 | 
            +
                   * Holds references to the different scopes that have bound hotkeys
         | 
| 229 | 
            +
                   * attached.  This is useful to catch when the scopes are `$destroy`d and
         | 
| 230 | 
            +
                   * then automatically unbind the hotkey.
         | 
| 231 | 
            +
                   *
         | 
| 232 | 
            +
                   * @type {Object}
         | 
| 233 | 
            +
                   */
         | 
| 234 | 
            +
                  var boundScopes = {};
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                  if (this.useNgRoute) {
         | 
| 237 | 
            +
                    $rootScope.$on('$routeChangeSuccess', function (event, route) {
         | 
| 238 | 
            +
                      purgeHotkeys();
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                      if (route && route.hotkeys) {
         | 
| 241 | 
            +
                        angular.forEach(route.hotkeys, function (hotkey) {
         | 
| 242 | 
            +
                          // a string was given, which implies this is a function that is to be
         | 
| 243 | 
            +
                          // $eval()'d within that controller's scope
         | 
| 244 | 
            +
                          // TODO: hotkey here is super confusing.  sometimes a function (that gets turned into an array), sometimes a string
         | 
| 245 | 
            +
                          var callback = hotkey[2];
         | 
| 246 | 
            +
                          if (typeof(callback) === 'string' || callback instanceof String) {
         | 
| 247 | 
            +
                            hotkey[2] = [callback, route];
         | 
| 248 | 
            +
                          }
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                          // todo: perform check to make sure not already defined:
         | 
| 251 | 
            +
                          // this came from a route, so it's likely not meant to be persistent
         | 
| 252 | 
            +
                          hotkey[5] = false;
         | 
| 253 | 
            +
                          _add.apply(this, hotkey);
         | 
| 254 | 
            +
                        });
         | 
| 255 | 
            +
                      }
         | 
| 256 | 
            +
                    });
         | 
| 257 | 
            +
                  }
         | 
| 258 | 
            +
             | 
| 259 | 
            +
             | 
| 260 | 
            +
             | 
| 261 | 
            +
                  // Auto-create a help menu:
         | 
| 262 | 
            +
                  if (this.includeCheatSheet) {
         | 
| 263 | 
            +
                    var document = $document[0];
         | 
| 264 | 
            +
                    var element = $rootElement[0];
         | 
| 265 | 
            +
                    var helpMenu = angular.element(this.template);
         | 
| 266 | 
            +
                    _add(this.cheatSheetHotkey, this.cheatSheetDescription, toggleCheatSheet);
         | 
| 267 | 
            +
             | 
| 268 | 
            +
                    // If $rootElement is document or documentElement, then body must be used
         | 
| 269 | 
            +
                    if (element === document || element === document.documentElement) {
         | 
| 270 | 
            +
                      element = document.body;
         | 
| 271 | 
            +
                    }
         | 
| 272 | 
            +
             | 
| 273 | 
            +
                    angular.element(element).append($compile(helpMenu)(scope));
         | 
| 274 | 
            +
                  }
         | 
| 275 | 
            +
             | 
| 276 | 
            +
             | 
| 277 | 
            +
                  /**
         | 
| 278 | 
            +
                   * Purges all non-persistent hotkeys (such as those defined in routes)
         | 
| 279 | 
            +
                   *
         | 
| 280 | 
            +
                   * Without this, the same hotkey would get recreated everytime
         | 
| 281 | 
            +
                   * the route is accessed.
         | 
| 282 | 
            +
                   */
         | 
| 283 | 
            +
                  function purgeHotkeys() {
         | 
| 284 | 
            +
                    var i = scope.hotkeys.length;
         | 
| 285 | 
            +
                    while (i--) {
         | 
| 286 | 
            +
                      var hotkey = scope.hotkeys[i];
         | 
| 287 | 
            +
                      if (hotkey && !hotkey.persistent) {
         | 
| 288 | 
            +
                        _del(hotkey);
         | 
| 289 | 
            +
                      }
         | 
| 290 | 
            +
                    }
         | 
| 291 | 
            +
                  }
         | 
| 292 | 
            +
             | 
| 293 | 
            +
                  /**
         | 
| 294 | 
            +
                   * Toggles the help menu element's visiblity
         | 
| 295 | 
            +
                   */
         | 
| 296 | 
            +
                  var previousEsc = false;
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                  function toggleCheatSheet() {
         | 
| 299 | 
            +
                    scope.helpVisible = !scope.helpVisible;
         | 
| 300 | 
            +
             | 
| 301 | 
            +
                    // Bind to esc to remove the cheat sheet.  Ideally, this would be done
         | 
| 302 | 
            +
                    // as a directive in the template, but that would create a nasty
         | 
| 303 | 
            +
                    // circular dependency issue that I don't feel like sorting out.
         | 
| 304 | 
            +
                    if (scope.helpVisible) {
         | 
| 305 | 
            +
                      previousEsc = _get('esc');
         | 
| 306 | 
            +
                      _del('esc');
         | 
| 307 | 
            +
             | 
| 308 | 
            +
                      // Here's an odd way to do this: we're going to use the original
         | 
| 309 | 
            +
                      // description of the hotkey on the cheat sheet so that it shows up.
         | 
| 310 | 
            +
                      // without it, no entry for esc will ever show up (#22)
         | 
| 311 | 
            +
                      _add('esc', previousEsc.description, toggleCheatSheet, null, ['INPUT', 'SELECT', 'TEXTAREA']);
         | 
| 312 | 
            +
                    } else {
         | 
| 313 | 
            +
                      _del('esc');
         | 
| 314 | 
            +
             | 
| 315 | 
            +
                      // restore the previously bound ESC key
         | 
| 316 | 
            +
                      if (previousEsc !== false) {
         | 
| 317 | 
            +
                        _add(previousEsc);
         | 
| 318 | 
            +
                      }
         | 
| 319 | 
            +
                    }
         | 
| 320 | 
            +
                  }
         | 
| 321 | 
            +
             | 
| 322 | 
            +
                  /**
         | 
| 323 | 
            +
                   * Creates a new Hotkey and creates the Mousetrap binding
         | 
| 324 | 
            +
                   *
         | 
| 325 | 
            +
                   * @param {string}   combo       mousetrap key binding
         | 
| 326 | 
            +
                   * @param {string}   description description for the help menu
         | 
| 327 | 
            +
                   * @param {Function} callback    method to call when key is pressed
         | 
| 328 | 
            +
                   * @param {string}   action      the type of event to listen for (for mousetrap)
         | 
| 329 | 
            +
                   * @param {array}    allowIn     an array of tag names to allow this combo in ('INPUT', 'SELECT', and/or 'TEXTAREA')
         | 
| 330 | 
            +
                   * @param {boolean}  persistent  if true, the binding is preserved upon route changes
         | 
| 331 | 
            +
                   */
         | 
| 332 | 
            +
                  function _add (combo, description, callback, action, allowIn, persistent) {
         | 
| 333 | 
            +
             | 
| 334 | 
            +
                    // used to save original callback for "allowIn" wrapping:
         | 
| 335 | 
            +
                    var _callback;
         | 
| 336 | 
            +
             | 
| 337 | 
            +
                    // these elements are prevented by the default Mousetrap.stopCallback():
         | 
| 338 | 
            +
                    var preventIn = ['INPUT', 'SELECT', 'TEXTAREA'];
         | 
| 339 | 
            +
             | 
| 340 | 
            +
                    // Determine if object format was given:
         | 
| 341 | 
            +
                    var objType = Object.prototype.toString.call(combo);
         | 
| 342 | 
            +
             | 
| 343 | 
            +
                    if (objType === '[object Object]') {
         | 
| 344 | 
            +
                      description = combo.description;
         | 
| 345 | 
            +
                      callback    = combo.callback;
         | 
| 346 | 
            +
                      action      = combo.action;
         | 
| 347 | 
            +
                      persistent  = combo.persistent;
         | 
| 348 | 
            +
                      allowIn     = combo.allowIn;
         | 
| 349 | 
            +
                      combo       = combo.combo;
         | 
| 350 | 
            +
                    }
         | 
| 351 | 
            +
             | 
| 352 | 
            +
                    // no duplicates please
         | 
| 353 | 
            +
                    _del(combo);
         | 
| 354 | 
            +
             | 
| 355 | 
            +
                    // description is optional:
         | 
| 356 | 
            +
                    if (description instanceof Function) {
         | 
| 357 | 
            +
                      action = callback;
         | 
| 358 | 
            +
                      callback = description;
         | 
| 359 | 
            +
                      description = '$$undefined$$';
         | 
| 360 | 
            +
                    } else if (angular.isUndefined(description)) {
         | 
| 361 | 
            +
                      description = '$$undefined$$';
         | 
| 362 | 
            +
                    }
         | 
| 363 | 
            +
             | 
| 364 | 
            +
                    // any items added through the public API are for controllers
         | 
| 365 | 
            +
                    // that persist through navigation, and thus undefined should mean
         | 
| 366 | 
            +
                    // true in this case.
         | 
| 367 | 
            +
                    if (persistent === undefined) {
         | 
| 368 | 
            +
                      persistent = true;
         | 
| 369 | 
            +
                    }
         | 
| 370 | 
            +
                    // if callback is defined, then wrap it in a function
         | 
| 371 | 
            +
                    // that checks if the event originated from a form element.
         | 
| 372 | 
            +
                    // the function blocks the callback from executing unless the element is specified
         | 
| 373 | 
            +
                    // in allowIn (emulates Mousetrap.stopCallback() on a per-key level)
         | 
| 374 | 
            +
                    if (typeof callback === 'function') {
         | 
| 375 | 
            +
             | 
| 376 | 
            +
                      // save the original callback
         | 
| 377 | 
            +
                      _callback = callback;
         | 
| 378 | 
            +
             | 
| 379 | 
            +
                      // make sure allowIn is an array
         | 
| 380 | 
            +
                      if (!(allowIn instanceof Array)) {
         | 
| 381 | 
            +
                        allowIn = [];
         | 
| 382 | 
            +
                      }
         | 
| 383 | 
            +
             | 
| 384 | 
            +
                      // remove anything from preventIn that's present in allowIn
         | 
| 385 | 
            +
                      var index;
         | 
| 386 | 
            +
                      for (var i=0; i < allowIn.length; i++) {
         | 
| 387 | 
            +
                        allowIn[i] = allowIn[i].toUpperCase();
         | 
| 388 | 
            +
                        index = preventIn.indexOf(allowIn[i]);
         | 
| 389 | 
            +
                        if (index !== -1) {
         | 
| 390 | 
            +
                          preventIn.splice(index, 1);
         | 
| 391 | 
            +
                        }
         | 
| 392 | 
            +
                      }
         | 
| 393 | 
            +
             | 
| 394 | 
            +
                      // create the new wrapper callback
         | 
| 395 | 
            +
                      callback = function(event) {
         | 
| 396 | 
            +
                        var shouldExecute = true;
         | 
| 397 | 
            +
             | 
| 398 | 
            +
                        // if the callback is executed directly `hotkey.get('w').callback()`
         | 
| 399 | 
            +
                        // there will be no event, so just execute the callback.
         | 
| 400 | 
            +
                        if (event) {
         | 
| 401 | 
            +
                          var target = event.target || event.srcElement; // srcElement is IE only
         | 
| 402 | 
            +
                          var nodeName = target.nodeName.toUpperCase();
         | 
| 403 | 
            +
             | 
| 404 | 
            +
                          // check if the input has a mousetrap class, and skip checking preventIn if so
         | 
| 405 | 
            +
                          if ((' ' + target.className + ' ').indexOf(' mousetrap ') > -1) {
         | 
| 406 | 
            +
                            shouldExecute = true;
         | 
| 407 | 
            +
                          } else {
         | 
| 408 | 
            +
                            // don't execute callback if the event was fired from inside an element listed in preventIn
         | 
| 409 | 
            +
                            for (var i=0; i<preventIn.length; i++) {
         | 
| 410 | 
            +
                              if (preventIn[i] === nodeName) {
         | 
| 411 | 
            +
                                shouldExecute = false;
         | 
| 412 | 
            +
                                break;
         | 
| 413 | 
            +
                              }
         | 
| 414 | 
            +
                            }
         | 
| 415 | 
            +
                          }
         | 
| 416 | 
            +
                        }
         | 
| 417 | 
            +
             | 
| 418 | 
            +
                        if (shouldExecute) {
         | 
| 419 | 
            +
                          wrapApply(_callback.apply(this, arguments));
         | 
| 420 | 
            +
                        }
         | 
| 421 | 
            +
                      };
         | 
| 422 | 
            +
                    }
         | 
| 423 | 
            +
             | 
| 424 | 
            +
                    if (typeof(action) === 'string') {
         | 
| 425 | 
            +
                      Mousetrap.bind(combo, wrapApply(callback), action);
         | 
| 426 | 
            +
                    } else {
         | 
| 427 | 
            +
                      Mousetrap.bind(combo, wrapApply(callback));
         | 
| 428 | 
            +
                    }
         | 
| 429 | 
            +
             | 
| 430 | 
            +
                    var hotkey = new Hotkey(combo, description, callback, action, allowIn, persistent);
         | 
| 431 | 
            +
                    scope.hotkeys.push(hotkey);
         | 
| 432 | 
            +
                    return hotkey;
         | 
| 433 | 
            +
                  }
         | 
| 434 | 
            +
             | 
| 435 | 
            +
                  /**
         | 
| 436 | 
            +
                   * delete and unbind a Hotkey
         | 
| 437 | 
            +
                   *
         | 
| 438 | 
            +
                   * @param  {mixed} hotkey   Either the bound key or an instance of Hotkey
         | 
| 439 | 
            +
                   * @return {boolean}        true if successful
         | 
| 440 | 
            +
                   */
         | 
| 441 | 
            +
                  function _del (hotkey) {
         | 
| 442 | 
            +
                    var combo = (hotkey instanceof Hotkey) ? hotkey.combo : hotkey;
         | 
| 443 | 
            +
             | 
| 444 | 
            +
                    Mousetrap.unbind(combo);
         | 
| 445 | 
            +
             | 
| 446 | 
            +
                    if (angular.isArray(combo)) {
         | 
| 447 | 
            +
                      var retStatus = true;
         | 
| 448 | 
            +
                      var i = combo.length;
         | 
| 449 | 
            +
                      while (i--) {
         | 
| 450 | 
            +
                        retStatus = _del(combo[i]) && retStatus;
         | 
| 451 | 
            +
                      }
         | 
| 452 | 
            +
                      return retStatus;
         | 
| 453 | 
            +
                    } else {
         | 
| 454 | 
            +
                      var index = scope.hotkeys.indexOf(_get(combo));
         | 
| 455 | 
            +
             | 
| 456 | 
            +
                      if (index > -1) {
         | 
| 457 | 
            +
                        // if the combo has other combos bound, don't unbind the whole thing, just the one combo:
         | 
| 458 | 
            +
                        if (scope.hotkeys[index].combo.length > 1) {
         | 
| 459 | 
            +
                          scope.hotkeys[index].combo.splice(scope.hotkeys[index].combo.indexOf(combo), 1);
         | 
| 460 | 
            +
                        } else {
         | 
| 461 | 
            +
             | 
| 462 | 
            +
                          // remove hotkey from bound scopes
         | 
| 463 | 
            +
                          angular.forEach(boundScopes, function (boundScope) {
         | 
| 464 | 
            +
                            var scopeIndex = boundScope.indexOf(scope.hotkeys[index]);
         | 
| 465 | 
            +
                            if (scopeIndex !== -1) {
         | 
| 466 | 
            +
                                boundScope.splice(scopeIndex, 1);
         | 
| 467 | 
            +
                            }
         | 
| 468 | 
            +
                          });
         | 
| 469 | 
            +
             | 
| 470 | 
            +
                          scope.hotkeys.splice(index, 1);
         | 
| 471 | 
            +
                        }
         | 
| 472 | 
            +
                        return true;
         | 
| 473 | 
            +
                      }
         | 
| 474 | 
            +
                    }
         | 
| 475 | 
            +
             | 
| 476 | 
            +
                    return false;
         | 
| 477 | 
            +
             | 
| 478 | 
            +
                  }
         | 
| 479 | 
            +
             | 
| 480 | 
            +
                  /**
         | 
| 481 | 
            +
                   * Get a Hotkey object by key binding
         | 
| 482 | 
            +
                   *
         | 
| 483 | 
            +
                   * @param  {[string]} [combo]  the key the Hotkey is bound to. Returns all key bindings if no key is passed
         | 
| 484 | 
            +
                   * @return {Hotkey}          The Hotkey object
         | 
| 485 | 
            +
                   */
         | 
| 486 | 
            +
                  function _get (combo) {
         | 
| 487 | 
            +
             | 
| 488 | 
            +
                    if (!combo) {
         | 
| 489 | 
            +
                      return scope.hotkeys;
         | 
| 490 | 
            +
                    }
         | 
| 491 | 
            +
             | 
| 492 | 
            +
                    var hotkey;
         | 
| 493 | 
            +
             | 
| 494 | 
            +
                    for (var i = 0; i < scope.hotkeys.length; i++) {
         | 
| 495 | 
            +
                      hotkey = scope.hotkeys[i];
         | 
| 496 | 
            +
             | 
| 497 | 
            +
                      if (hotkey.combo.indexOf(combo) > -1) {
         | 
| 498 | 
            +
                        return hotkey;
         | 
| 499 | 
            +
                      }
         | 
| 500 | 
            +
                    }
         | 
| 501 | 
            +
             | 
| 502 | 
            +
                    return false;
         | 
| 503 | 
            +
                  }
         | 
| 504 | 
            +
             | 
| 505 | 
            +
                  /**
         | 
| 506 | 
            +
                   * Binds the hotkey to a particular scope.  Useful if the scope is
         | 
| 507 | 
            +
                   * destroyed, we can automatically destroy the hotkey binding.
         | 
| 508 | 
            +
                   *
         | 
| 509 | 
            +
                   * @param  {Object} scope The scope to bind to
         | 
| 510 | 
            +
                   */
         | 
| 511 | 
            +
                  function bindTo (scope) {
         | 
| 512 | 
            +
                    // Only initialize once to allow multiple calls for same scope.
         | 
| 513 | 
            +
                    if (!(scope.$id in boundScopes)) {
         | 
| 514 | 
            +
             | 
| 515 | 
            +
                      // Add the scope to the list of bound scopes
         | 
| 516 | 
            +
                      boundScopes[scope.$id] = [];
         | 
| 517 | 
            +
             | 
| 518 | 
            +
                      scope.$on('$destroy', function () {
         | 
| 519 | 
            +
                        var i = boundScopes[scope.$id].length;
         | 
| 520 | 
            +
                        while (i--) {
         | 
| 521 | 
            +
                          _del(boundScopes[scope.$id].pop());
         | 
| 522 | 
            +
                        }
         | 
| 523 | 
            +
                      });
         | 
| 524 | 
            +
                    }
         | 
| 525 | 
            +
                    // return an object with an add function so we can keep track of the
         | 
| 526 | 
            +
                    // hotkeys and their scope that we added via this chaining method
         | 
| 527 | 
            +
                    return {
         | 
| 528 | 
            +
                      add: function (args) {
         | 
| 529 | 
            +
                        var hotkey;
         | 
| 530 | 
            +
             | 
| 531 | 
            +
                        if (arguments.length > 1) {
         | 
| 532 | 
            +
                          hotkey = _add.apply(this, arguments);
         | 
| 533 | 
            +
                        } else {
         | 
| 534 | 
            +
                          hotkey = _add(args);
         | 
| 535 | 
            +
                        }
         | 
| 536 | 
            +
             | 
| 537 | 
            +
                        boundScopes[scope.$id].push(hotkey);
         | 
| 538 | 
            +
                        return this;
         | 
| 539 | 
            +
                      }
         | 
| 540 | 
            +
                    };
         | 
| 541 | 
            +
                  }
         | 
| 542 | 
            +
             | 
| 543 | 
            +
                  /**
         | 
| 544 | 
            +
                   * All callbacks sent to Mousetrap are wrapped using this function
         | 
| 545 | 
            +
                   * so that we can force a $scope.$apply()
         | 
| 546 | 
            +
                   *
         | 
| 547 | 
            +
                   * @param  {Function} callback [description]
         | 
| 548 | 
            +
                   * @return {[type]}            [description]
         | 
| 549 | 
            +
                   */
         | 
| 550 | 
            +
                  function wrapApply (callback) {
         | 
| 551 | 
            +
                    // return mousetrap a function to call
         | 
| 552 | 
            +
                    return function (event, combo) {
         | 
| 553 | 
            +
             | 
| 554 | 
            +
                      // if this is an array, it means we provided a route object
         | 
| 555 | 
            +
                      // because the scope wasn't available yet, so rewrap the callback
         | 
| 556 | 
            +
                      // now that the scope is available:
         | 
| 557 | 
            +
                      if (callback instanceof Array) {
         | 
| 558 | 
            +
                        var funcString = callback[0];
         | 
| 559 | 
            +
                        var route = callback[1];
         | 
| 560 | 
            +
                        callback = function (event) {
         | 
| 561 | 
            +
                          route.scope.$eval(funcString);
         | 
| 562 | 
            +
                        };
         | 
| 563 | 
            +
                      }
         | 
| 564 | 
            +
             | 
| 565 | 
            +
                      // this takes place outside angular, so we'll have to call
         | 
| 566 | 
            +
                      // $apply() to make sure angular's digest happens
         | 
| 567 | 
            +
                      $rootScope.$apply(function() {
         | 
| 568 | 
            +
                        // call the original hotkey callback with the keyboard event
         | 
| 569 | 
            +
                        callback(event, _get(combo));
         | 
| 570 | 
            +
                      });
         | 
| 571 | 
            +
                    };
         | 
| 572 | 
            +
                  }
         | 
| 573 | 
            +
             | 
| 574 | 
            +
                  var publicApi = {
         | 
| 575 | 
            +
                    add                   : _add,
         | 
| 576 | 
            +
                    del                   : _del,
         | 
| 577 | 
            +
                    get                   : _get,
         | 
| 578 | 
            +
                    bindTo                : bindTo,
         | 
| 579 | 
            +
                    template              : this.template,
         | 
| 580 | 
            +
                    toggleCheatSheet      : toggleCheatSheet,
         | 
| 581 | 
            +
                    includeCheatSheet     : this.includeCheatSheet,
         | 
| 582 | 
            +
                    cheatSheetHotkey      : this.cheatSheetHotkey,
         | 
| 583 | 
            +
                    cheatSheetDescription : this.cheatSheetDescription,
         | 
| 584 | 
            +
                    useNgRoute            : this.useNgRoute,
         | 
| 585 | 
            +
                    purgeHotkeys          : purgeHotkeys,
         | 
| 586 | 
            +
                    templateTitle         : this.templateTitle,
         | 
| 587 | 
            +
                    pause                 : pause,
         | 
| 588 | 
            +
                    unpause               : unpause
         | 
| 589 | 
            +
                  };
         | 
| 590 | 
            +
             | 
| 591 | 
            +
                  return publicApi;
         | 
| 592 | 
            +
             | 
| 593 | 
            +
                };
         | 
| 594 | 
            +
             | 
| 595 | 
            +
             | 
| 596 | 
            +
              })
         | 
| 597 | 
            +
             | 
| 598 | 
            +
              .directive('hotkey', function (hotkeys) {
         | 
| 599 | 
            +
                return {
         | 
| 600 | 
            +
                  restrict: 'A',
         | 
| 601 | 
            +
                  link: function (scope, el, attrs) {
         | 
| 602 | 
            +
                    var keys = [],
         | 
| 603 | 
            +
                        allowIn;
         | 
| 604 | 
            +
             | 
| 605 | 
            +
                    angular.forEach(scope.$eval(attrs.hotkey), function (func, hotkey) {
         | 
| 606 | 
            +
                      // split and trim the hotkeys string into array
         | 
| 607 | 
            +
                      allowIn = typeof attrs.hotkeyAllowIn === "string" ? attrs.hotkeyAllowIn.split(/[\s,]+/) : [];
         | 
| 608 | 
            +
             | 
| 609 | 
            +
                      keys.push(hotkey);
         | 
| 610 | 
            +
             | 
| 611 | 
            +
                      hotkeys.add({
         | 
| 612 | 
            +
                        combo: hotkey,
         | 
| 613 | 
            +
                        description: attrs.hotkeyDescription,
         | 
| 614 | 
            +
                        callback: func,
         | 
| 615 | 
            +
                        action: attrs.hotkeyAction,
         | 
| 616 | 
            +
                        allowIn: allowIn
         | 
| 617 | 
            +
                      });
         | 
| 618 | 
            +
                    });
         | 
| 619 | 
            +
             | 
| 620 | 
            +
                    // remove the hotkey if the directive is destroyed:
         | 
| 621 | 
            +
                    el.bind('$destroy', function() {
         | 
| 622 | 
            +
                      angular.forEach(keys, hotkeys.del);
         | 
| 623 | 
            +
                    });
         | 
| 624 | 
            +
                  }
         | 
| 625 | 
            +
                };
         | 
| 626 | 
            +
              })
         | 
| 627 | 
            +
             | 
| 628 | 
            +
              .run(function(hotkeys) {
         | 
| 629 | 
            +
                // force hotkeys to run by injecting it. Without this, hotkeys only runs
         | 
| 630 | 
            +
                // when a controller or something else asks for it via DI.
         | 
| 631 | 
            +
              });
         | 
| 632 | 
            +
             | 
| 633 | 
            +
            })();
         |