diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..d280a2850 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "vendor/extensions/extensions-manager"] + path = vendor/extensions/extensions-manager + url = https://github.com/sn-extensions/extensions-manager.git +[submodule "app/extensions/extensions-manager"] + path = app/extensions/extensions-manager + url = https://github.com/sn-extensions/extensions-manager.git +[submodule "public/extensions/extensions-manager"] + path = public/extensions/extensions-manager + url = https://github.com/sn-extensions/extensions-manager.git diff --git a/Capfile b/Capfile index 08de6d646..532cfeef5 100644 --- a/Capfile +++ b/Capfile @@ -4,6 +4,12 @@ require "capistrano/setup" # Include default deployment tasks require "capistrano/deploy" +require "capistrano/scm/git" +install_plugin Capistrano::SCM::Git + +require "capistrano/scm/git-with-submodules" +install_plugin Capistrano::SCM::Git::WithSubmodules + # Include tasks from other gems included in your Gemfile # # For documentation on these, see for example: @@ -23,7 +29,6 @@ require 'capistrano/rails/assets' # require 'capistrano/rails/migrations' require 'capistrano/passenger' # require 'capistrano/sidekiq' -require 'capistrano/git-submodule-strategy' # require "whenever/capistrano" # Update crontab on deploy diff --git a/Gemfile b/Gemfile index 753576444..906d29d4c 100644 --- a/Gemfile +++ b/Gemfile @@ -44,5 +44,5 @@ group :development, :test do gem 'capistrano-rails' gem 'capistrano-rvm' gem 'capistrano-sidekiq' - gem 'capistrano-git-submodule-strategy', '~> 0.1.22' + gem 'capistrano-git-with-submodules', '~> 2.0' end diff --git a/Gemfile.lock b/Gemfile.lock index 7c79bb6c2..d70ec701e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -38,74 +38,75 @@ GEM i18n (~> 0.7) minitest (~> 5.1) tzinfo (~> 1.1) - airbrussh (1.1.1) + airbrussh (1.3.0) sshkit (>= 1.6.1, != 1.7.0) arel (7.1.4) - binding_of_caller (0.7.2) + binding_of_caller (0.8.0) debug_inspector (>= 0.0.1) bower-rails (0.10.0) - builder (3.2.2) - byebug (9.0.6) - capistrano (3.6.1) + builder (3.2.3) + byebug (10.0.0) + capistrano (3.10.1) airbrussh (>= 1.0.0) - capistrano-harrow i18n rake (>= 10.0.0) sshkit (>= 1.9.0) - capistrano-bundler (1.2.0) + capistrano-bundler (1.3.0) capistrano (~> 3.1) sshkit (~> 1.2) - capistrano-git-submodule-strategy (0.1.22) - capistrano (~> 3.1) - capistrano-harrow (0.5.3) + capistrano-git-with-submodules (2.0.3) + capistrano (~> 3.7) capistrano-passenger (0.2.0) capistrano (~> 3.0) - capistrano-rails (1.2.0) + capistrano-rails (1.3.1) capistrano (~> 3.1) capistrano-bundler (~> 1.1) capistrano-rvm (0.1.2) capistrano (~> 3.0) sshkit (~> 1.2) - capistrano-sidekiq (0.10.0) - capistrano + capistrano-sidekiq (1.0.0) + capistrano (>= 3.9.0) sidekiq (>= 3.4) - concurrent-ruby (1.0.2) + concurrent-ruby (1.0.5) connection_pool (2.2.1) - debug_inspector (0.0.2) - dotenv (2.1.1) - dotenv-rails (2.1.1) - dotenv (= 2.1.1) - railties (>= 4.0, < 5.1) + crass (1.0.3) + debug_inspector (0.0.3) + dotenv (2.1.2) + dotenv-rails (2.1.2) + dotenv (= 2.1.2) + railties (>= 3.2, < 5.1) erubis (2.7.0) execjs (2.7.0) - globalid (0.3.7) - activesupport (>= 4.1.0) - haml (4.0.7) + ffi (1.9.18) + globalid (0.4.1) + activesupport (>= 4.2.0) + haml (5.0.4) + temple (>= 0.8.0) tilt - i18n (0.7.0) - json (1.8.3) - loofah (2.0.3) + i18n (0.9.3) + concurrent-ruby (~> 1.0) + json (1.8.6) + loofah (2.1.1) + crass (~> 1.0.2) nokogiri (>= 1.5.9) - mail (2.6.4) - mime-types (>= 1.16, < 4) - method_source (0.8.2) - mime-types (3.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2016.0521) - mini_portile2 (2.1.0) - minitest (5.9.1) + mail (2.7.0) + mini_mime (>= 0.1.1) + method_source (0.9.0) + mini_mime (1.0.0) + mini_portile2 (2.3.0) + minitest (5.11.3) net-scp (1.2.1) net-ssh (>= 2.6.5) - net-ssh (3.2.0) + net-ssh (4.2.0) nio4r (1.2.1) - nokogiri (1.6.8.1) - mini_portile2 (~> 2.1.0) + nokogiri (1.8.2) + mini_portile2 (~> 2.3.0) non-stupid-digest-assets (1.0.9) sprockets (>= 2.0) - puma (3.6.2) - rack (2.0.1) - rack-cors (1.0.0) - rack-protection (1.5.3) + puma (3.11.2) + rack (2.0.3) + rack-cors (1.0.2) + rack-protection (2.0.0) rack rack-test (0.6.3) rack (>= 1.0) @@ -121,9 +122,9 @@ GEM bundler (>= 1.3.0, < 2.0) railties (= 5.0.0.1) sprockets-rails (>= 2.0.0) - rails-dom-testing (2.0.1) - activesupport (>= 4.2.0, < 6.0) - nokogiri (~> 1.6.0) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) rails-html-sanitizer (1.0.3) loofah (~> 2.0) railties (5.0.0.1) @@ -132,50 +133,59 @@ GEM method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (11.3.0) + rake (12.3.0) + rb-fsevent (0.10.2) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) rdoc (4.3.0) - redis (3.3.2) - responders (2.3.0) - railties (>= 4.2.0, < 5.1) - sass (3.4.22) + redis (4.0.1) + responders (2.4.0) + actionpack (>= 4.2.0, < 5.3) + railties (>= 4.2.0, < 5.3) + sass (3.5.5) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) sdoc (0.4.2) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) - secure_headers (3.6.7) - useragent - sidekiq (4.2.7) + secure_headers (5.0.4) + useragent (>= 0.15.0) + sidekiq (5.0.5) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) - redis (~> 3.2, >= 3.2.1) - spring (2.0.0) + redis (>= 3.3.4, < 5) + spring (2.0.2) activesupport (>= 4.2) - sprockets (3.7.0) + sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.0) + sprockets-rails (3.2.1) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - sshkit (1.11.4) + sshkit (1.15.1) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) - thor (0.19.4) - thread_safe (0.3.5) - tilt (2.0.5) - tzinfo (1.2.2) + temple (0.8.0) + thor (0.20.0) + thread_safe (0.3.6) + tilt (2.0.8) + tzinfo (1.2.4) thread_safe (~> 0.1) - uglifier (3.0.3) + uglifier (4.1.5) execjs (>= 0.3.0, < 3) - useragent (0.16.8) + useragent (0.16.9) web-console (2.3.0) activemodel (>= 4.0) binding_of_caller (>= 0.7.2) railties (>= 4.0) sprockets-rails (>= 2.0, < 4.0) - websocket-driver (0.6.4) + websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.2) + websocket-extensions (0.1.3) PLATFORMS ruby @@ -185,7 +195,7 @@ DEPENDENCIES byebug capistrano capistrano-bundler - capistrano-git-submodule-strategy (~> 0.1.22) + capistrano-git-with-submodules (~> 2.0) capistrano-passenger (>= 0.2.0) capistrano-rails capistrano-rvm @@ -205,4 +215,4 @@ DEPENDENCIES web-console (~> 2.0) BUNDLED WITH - 1.15.1 + 1.16.1 diff --git a/Gruntfile.js b/Gruntfile.js index 7aecbdebc..6878b6524 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -34,7 +34,7 @@ module.exports = function(grunt) { style: 'expanded' }, files: { - 'vendor/assets/stylesheets/app.css': 'app/assets/stylesheets/frontend.css.scss' + 'vendor/assets/stylesheets/app.css': 'app/assets/stylesheets/main.css.scss' } } }, @@ -59,7 +59,7 @@ module.exports = function(grunt) { src: ['**/*.html'], dest: 'vendor/assets/javascripts/templates.js', options: { - module: 'app.frontend' + module: 'app' } } }, @@ -70,12 +70,13 @@ module.exports = function(grunt) { }, app: { src: [ - 'app/assets/javascripts/app/services/encryption/*.js', + 'app/assets/javascripts/app/services/encryption/*.js', // this should come first 'app/assets/javascripts/app/*.js', - 'app/assets/javascripts/app/frontend/*.js', - 'app/assets/javascripts/app/frontend/controllers/*.js', - 'app/assets/javascripts/app/frontend/models/**/*.js', - 'app/assets/javascripts/app/services/**/*.js' + 'app/assets/javascripts/app/controllers/**/*.js', + 'app/assets/javascripts/app/models/**/*.js', + 'app/assets/javascripts/app/services/**/*.js', + 'app/assets/javascripts/app/filters/**/*.js', + 'app/assets/javascripts/app/directives/**/*.js', ], dest: 'vendor/assets/javascripts/app.js', }, @@ -95,8 +96,12 @@ module.exports = function(grunt) { }, css: { + options: { + separator: '', + }, src: [ - 'vendor/assets/stylesheets/app.css' + 'vendor/assets/stylesheets/app.css', + 'node_modules/sn-stylekit/dist/stylekit.css' ], dest: 'vendor/assets/stylesheets/app.css' } diff --git a/app/assets/images/.keep b/app/assets/images/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/assets/images/archive.png b/app/assets/images/archive.png deleted file mode 100644 index d6e8a2049..000000000 Binary files a/app/assets/images/archive.png and /dev/null differ diff --git a/app/assets/images/encryption.png b/app/assets/images/encryption.png deleted file mode 100644 index 7f80b67dd..000000000 Binary files a/app/assets/images/encryption.png and /dev/null differ diff --git a/app/assets/images/filter.png b/app/assets/images/filter.png deleted file mode 100644 index b707c70b5..000000000 Binary files a/app/assets/images/filter.png and /dev/null differ diff --git a/app/assets/images/logo.png b/app/assets/images/logo.png deleted file mode 100644 index 9b0c179d2..000000000 Binary files a/app/assets/images/logo.png and /dev/null differ diff --git a/app/assets/javascripts/app/app.frontend.js b/app/assets/javascripts/app/app.js similarity index 69% rename from app/assets/javascripts/app/app.frontend.js rename to app/assets/javascripts/app/app.js index 80a607ee3..af4ac2d2e 100644 --- a/app/assets/javascripts/app/app.frontend.js +++ b/app/assets/javascripts/app/app.js @@ -13,7 +13,7 @@ if(!IEOrEdge && (window.crypto && window.crypto.subtle)) { Neeto.crypto = new SNCryptoJS(); } -angular.module('app.frontend', []) +angular.module('app', []) function getParameterByName(name, url) { name = name.replace(/[\[\]]/g, "\\$&"); @@ -36,3 +36,17 @@ function parametersFromURL(url) { function isDesktopApplication() { return window && window.process && window.process.type && window.process.versions["electron"]; } + +function isMacApplication() { + return window && window.process && window.process.type && window.process.platform == "darwin"; +} + +/* Use with numbers and strings, not objects */ +Array.prototype.containsPrimitiveSubset = function(array) { + return !array.some(val => this.indexOf(val) === -1); +} + +/* Use with numbers and strings, not objects */ +Array.prototype.containsObjectSubset = function(array) { + return !array.some(val => !_.find(this, val)); +} diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/controllers/editor.js similarity index 58% rename from app/assets/javascripts/app/frontend/controllers/editor.js rename to app/assets/javascripts/app/controllers/editor.js index b976d90fb..42f403470 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/controllers/editor.js @@ -1,4 +1,4 @@ -angular.module('app.frontend') +angular.module('app') .directive("editorSection", function($timeout, $sce){ return { restrict: 'E', @@ -8,7 +8,7 @@ angular.module('app.frontend') note: "=", updateTags: "&" }, - templateUrl: 'frontend/editor.html', + templateUrl: 'editor.html', replace: true, controller: 'EditorCtrl', controllerAs: 'ctrl', @@ -23,10 +23,10 @@ angular.module('app.frontend') } } }) - .controller('EditorCtrl', function ($sce, $timeout, authManager, $rootScope, extensionManager, syncManager, modelManager, themeManager, componentManager, storageManager) { + .controller('EditorCtrl', function ($sce, $timeout, authManager, $rootScope, actionsManager, syncManager, modelManager, themeManager, componentManager, storageManager) { + this.spellcheck = true; this.componentManager = componentManager; - this.componentStack = []; $rootScope.$on("sync:taking-too-long", function(){ this.syncTakingTooLong = true; @@ -50,31 +50,30 @@ angular.module('app.frontend') this.showMenu = false; this.loadTagsString(); + let onReady = () => { + this.noteReady = true; + $timeout(() => { + this.loadPreferences(); + }) + } + let associatedEditor = this.editorForNote(note); - if(associatedEditor) { + if(associatedEditor && associatedEditor != this.selectedEditor) { // setting note to not ready will remove the editor from view in a flash, // so we only want to do this if switching between external editors this.noteReady = false; - } else { - this.noteReady = true; - } - - if(this.editorComponent && this.editorComponent != associatedEditor) { - // Deactivate old editor - componentManager.deactivateComponent(this.editorComponent); - this.editorComponent = null; - } - - // Activate new editor if it's different from the one currently activated - if(associatedEditor && associatedEditor != this.editorComponent) { - // switch after timeout, so that note data isnt posted to current editor + // switch after timeout, so that note data isnt posted to current editor $timeout(() => { - this.enableComponent(associatedEditor); - this.editorComponent = associatedEditor; - this.noteReady = true; + this.selectedEditor = associatedEditor; + onReady(); }) + } else if(associatedEditor) { + // Same editor as currently active + onReady(); } else { - this.noteReady = true; + // No editor + this.selectedEditor = null; + onReady(); } if(note.safeText().length == 0 && note.dummy) { @@ -93,7 +92,7 @@ angular.module('app.frontend') this.editorForNote = function(note) { let editors = componentManager.componentsForArea("editor-editor"); for(var editor of editors) { - if(editor.isActiveForItem(note)) { + if(editor.isExplicitlyEnabledForItem(note)) { return editor; } } @@ -104,33 +103,56 @@ angular.module('app.frontend') } } - this.selectedEditor = function(editorComponent) { + this.onEditorMenuClick = function() { + // App bar menu item click + this.showEditorMenu = !this.showEditorMenu; + this.showMenu = false; + this.showExtensions = false; + } + + this.closeAllMenus = function() { this.showEditorMenu = false; + this.showMenu = false; + this.showExtensions = false; + } - if(this.editorComponent && this.editorComponent !== editorComponent) { - // This disassociates the editor from the note, but the component itself still needs to be deactivated - this.disableComponentForCurrentItem(this.editorComponent); - // Now deactivate the component - componentManager.deactivateComponent(this.editorComponent); + this.editorMenuOnSelect = function(component) { + if(!component || component.area == "editor-editor") { + // if plain editor or other editor + this.showEditorMenu = false; + var editor = component; + if(this.selectedEditor && editor !== this.selectedEditor) { + this.disassociateComponentWithCurrentNote(this.selectedEditor); + } + if(editor) { + if(this.note.getAppDataItem("prefersPlainEditor") == true) { + this.note.setAppDataItem("prefersPlainEditor", false); + this.note.setDirty(true); + } + this.associateComponentWithCurrentNote(editor); + } else { + // Note prefers plain editor + if(!this.note.getAppDataItem("prefersPlainEditor")) { + this.note.setAppDataItem("prefersPlainEditor", true); + this.note.setDirty(true); + } + $timeout(() => { + this.reloadFont(); + }) + } + + this.selectedEditor = editor; + } else if(component.area == "editor-stack") { + // If component stack item + this.toggleStackComponentForCurrentItem(component); } - if(editorComponent) { - this.note.setAppDataItem("prefersPlainEditor", false); - this.note.setDirty(true); - this.enableComponent(editorComponent); - this.associateComponentWithCurrentItem(editorComponent); - } else { - // Note prefers plain editor - this.note.setAppDataItem("prefersPlainEditor", true); - this.note.setDirty(true); - syncManager.sync(); - } - - this.editorComponent = editorComponent; + // Lots of dirtying can happen above, so we'll sync + syncManager.sync("editorMenuOnSelect"); }.bind(this) this.hasAvailableExtensions = function() { - return extensionManager.extensionsInContextOfItem(this.note).length > 0; + return actionsManager.extensionsInContextOfItem(this.note).length > 0; } this.focusEditor = function(delay) { @@ -183,16 +205,25 @@ angular.module('app.frontend') } var saveTimeout; - this.changesMade = function() { - this.note.hasChanges = true; + this.changesMade = function(bypassDebouncer = false) { this.note.dummy = false; + /* In the case of keystrokes, saving should go through a debouncer to avoid frequent calls. + In the case of deleting or archiving a note, it should happen immediately before the note is switched out + */ + let delay = bypassDebouncer ? 0 : 275; + + // In the case of archiving a note, the note is saved immediately, then switched to another note. + // Usually note.hasChanges is set back to false after the saving delay, but in this case, because there is no delay, + // we set it to false immediately so that it is not saved twice: once now, and the other on setNote in oldNote.hasChanges. + this.note.hasChanges = bypassDebouncer ? false : true; + if(saveTimeout) $timeout.cancel(saveTimeout); if(statusTimeout) $timeout.cancel(statusTimeout); saveTimeout = $timeout(function(){ this.showSavingStatus(); this.saveNote(); - }.bind(this), 275) + }.bind(this), delay) } this.showSavingStatus = function() { @@ -229,18 +260,13 @@ angular.module('app.frontend') this.onNameBlur = function() { this.editingName = false; - this.updateTagsFromTagsString() } - this.toggleFullScreen = function() { - this.fullscreen = !this.fullscreen; - if(this.fullscreen) { - this.focusEditor(0); + this.selectedMenuItem = function($event, hide) { + if(hide) { + this.showMenu = false; } - } - - this.selectedMenuItem = function($event) { - this.showMenu = false; + $event.stopPropagation(); } this.deleteNote = function() { @@ -260,7 +286,7 @@ angular.module('app.frontend') this.toggleArchiveNote = function() { this.note.setAppDataItem("archived", !this.note.archived); this.note.setDirty(true); - this.changesMade(); + this.changesMade(true); $rootScope.$broadcast("noteArchived"); } @@ -280,11 +306,7 @@ angular.module('app.frontend') */ this.loadTagsString = function() { - var string = ""; - for(var tag of this.note.tags) { - string += "#" + tag.title + " "; - } - this.tagsString = string; + this.tagsString = this.note.tagsString(); } this.addTag = function(tag) { @@ -309,20 +331,100 @@ angular.module('app.frontend') } this.updateTagsFromTagsString = function() { - var tags = this.tagsString.split("#"); - tags = _.filter(tags, function(tag){ - return tag.length > 0; - }) - tags = _.map(tags, function(tag){ - return tag.trim(); + if(this.tagsString == this.note.tagsString()) { + return; + } + + var strings = this.tagsString.split("#").filter((string) => { + return string.length > 0; + }).map((string) => { + return string.trim(); }) this.note.dummy = false; - this.updateTags()(this.note, tags); + this.updateTags()(this.note, strings); } + /* Resizability */ + this.resizeControl = {}; + + this.onPanelResizeFinish = function(width, left, isMaxWidth) { + if(isMaxWidth) { + authManager.setUserPrefValue("editorWidth", null); + } else { + if(width !== undefined && width !== null) { + authManager.setUserPrefValue("editorWidth", width); + } + } + + if(left !== undefined && left !== null) { + authManager.setUserPrefValue("editorLeft", left); + } + authManager.syncUserPreferences(); + } + + $rootScope.$on("user-preferences-changed", () => { + this.loadPreferences(); + }); + + this.loadPreferences = function() { + this.monospaceFont = authManager.getUserPrefValue("monospaceFont", "monospace"); + this.spellcheck = authManager.getUserPrefValue("spellcheck", true); + + if(!document.getElementById("editor-content")) { + // Elements have not yet loaded due to ng-if around wrapper + return; + } + + this.reloadFont(); + + let width = authManager.getUserPrefValue("editorWidth", null); + if(width !== null) { + this.resizeControl.setWidth(width); + } + + let left = authManager.getUserPrefValue("editorLeft", null); + if(left !== null) { + this.resizeControl.setLeft(left); + } + } + + this.reloadFont = function() { + var editable = document.getElementById("note-text-editor"); + + if(!editable) { + return; + } + + if(this.monospaceFont) { + if(isMacApplication()) { + editable.style.fontFamily = "Menlo, Consolas, 'DejaVu Sans Mono', monospace"; + } else { + editable.style.fontFamily = "monospace"; + } + } else { + editable.style.fontFamily = "inherit"; + } + } + + this.toggleKey = function(key) { + this[key] = !this[key]; + authManager.setUserPrefValue(key, this[key], true); + this.reloadFont(); + + if(key == "spellcheck") { + // Allows textarea to reload + this.noteReady = false; + $timeout(() => { + this.noteReady = true; + $timeout(() => { + this.reloadFont(); + }) + }, 0) + } + } @@ -330,43 +432,27 @@ angular.module('app.frontend') Components */ - componentManager.registerHandler({identifier: "editor", areas: ["note-tags", "editor-stack", "editor-editor"], activationHandler: function(component){ - + componentManager.registerHandler({identifier: "editor", areas: ["note-tags", "editor-stack", "editor-editor"], activationHandler: (component) => { if(component.area === "note-tags") { // Autocomplete Tags this.tagsComponent = component.active ? component : null; - } else if(component.area == "editor-stack") { - // Stack - if(component.active) { - if(!_.find(this.componentStack, component)) { - this.componentStack.push(component); - } - } else { - _.pull(this.componentStack, component); - } - } else { + } else if(component.area == "editor-editor") { // Editor - if(component.active && this.note && component.isActiveForItem(this.note)) { - this.editorComponent = component; + if(component.active && this.note && (component.isExplicitlyEnabledForItem(this.note) || component.isDefaultEditor())) { + this.selectedEditor = component; } else { - this.editorComponent = null; + this.selectedEditor = null; } + } else if(component.area == "editor-stack") { + this.reloadComponentContext(); } - - if(component.active) { - $timeout(function(){ - var iframe = componentManager.iframeForComponent(component); - if(iframe) { - iframe.onload = function() { - componentManager.registerComponentWindow(component, iframe.contentWindow); - }.bind(this); - } - }.bind(this)); - } - - }.bind(this), contextRequestHandler: function(component){ + }, contextRequestHandler: (component) => { return this.note; - }.bind(this), actionHandler: function(component, action, data){ + }, focusHandler: (component, focused) => { + if(component.isEditor() && focused) { + this.closeAllMenus(); + } + }, actionHandler: (component, action, data) => { if(action === "set-size") { var setSize = function(element, size) { var widthString = typeof size.width === 'string' ? size.width : `${data.width}px`; @@ -374,21 +460,10 @@ angular.module('app.frontend') element.setAttribute("style", `width:${widthString}; height:${heightString}; `); } - if(data.type === "content") { - var iframe = componentManager.iframeForComponent(component); - var width = data.width; - var height = data.height; - iframe.width = width; - iframe.height = height; - - setSize(iframe, data); - } else { + if(data.type == "container") { if(component.area == "note-tags") { var container = document.getElementById("note-tags-component-container"); setSize(container, data); - } else { - var container = document.getElementById("component-" + component.uuid); - setSize(container, data); } } } @@ -423,11 +498,15 @@ angular.module('app.frontend') } } } - }.bind(this)}); + }}); this.reloadComponentContext = function() { + // componentStack is used by the template to ng-repeat + this.componentStack = componentManager.componentsForArea("editor-stack"); for(var component of this.componentStack) { - componentManager.setEventFlowForComponent(component, component.isActiveForItem(this.note)); + if(component.active) { + component.hidden = !this.note || component.isExplicitlyDisabledForItem(this.note); + } } componentManager.contextItemDidChangeInArea("note-tags"); @@ -435,51 +514,41 @@ angular.module('app.frontend') componentManager.contextItemDidChangeInArea("editor-editor"); } - this.enableComponent = function(component) { - componentManager.activateComponent(component); - componentManager.setEventFlowForComponent(component, 1); - } - - this.associateComponentWithCurrentItem = function(component) { - componentManager.associateComponentWithItem(component, this.note); - } - - let alertKey = "displayed-component-disable-alert"; - this.disableComponentForCurrentItem = function(component, showAlert) { - componentManager.disassociateComponentWithItem(component, this.note); - componentManager.setEventFlowForComponent(component, 0); - if(showAlert && !storageManager.getItem(alertKey)) { - alert("This component will be disabled for this note. You can re-enable this component in the 'Menu' of the editor pane."); - storageManager.setItem(alertKey, true); - } - } - - this.hasDisabledStackComponents = function() { - for(var component of this.componentStack) { - if(component.ignoreEvents) { - return true; + this.toggleStackComponentForCurrentItem = function(component) { + if(component.hidden) { + // Unhide, associate with current item + component.hidden = false; + if(!component.active) { + componentManager.activateComponent(component); } - } - - return false; - } - - this.restoreDisabledStackComponents = function() { - var relevantComponents = this.componentStack.filter(function(component){ - return component.ignoreEvents; - }) - - componentManager.enableComponentsForItem(relevantComponents, this.note); - - for(var component of relevantComponents) { - componentManager.setEventFlowForComponent(component, true); + this.associateComponentWithCurrentNote(component); componentManager.contextItemDidChangeInArea("editor-stack"); + } else { + // not hidden, hide + component.hidden = true; + this.disassociateComponentWithCurrentNote(component); } } + this.disassociateComponentWithCurrentNote = function(component) { + component.associatedItemIds = component.associatedItemIds.filter((id) => {return id !== this.note.uuid}); + if(!component.disassociatedItemIds.includes(this.note.uuid)) { + component.disassociatedItemIds.push(this.note.uuid); + } + component.setDirty(true); + } + this.associateComponentWithCurrentNote = function(component) { + component.disassociatedItemIds = component.disassociatedItemIds.filter((id) => {return id !== this.note.uuid}); + + if(!component.associatedItemIds.includes(this.note.uuid)) { + component.associatedItemIds.push(this.note.uuid); + } + + component.setDirty(true); + } diff --git a/app/assets/javascripts/app/frontend/controllers/footer.js b/app/assets/javascripts/app/controllers/footer.js similarity index 50% rename from app/assets/javascripts/app/frontend/controllers/footer.js rename to app/assets/javascripts/app/controllers/footer.js index e59321187..4da3b6ba1 100644 --- a/app/assets/javascripts/app/frontend/controllers/footer.js +++ b/app/assets/javascripts/app/controllers/footer.js @@ -1,9 +1,9 @@ -angular.module('app.frontend') +angular.module('app') .directive("footer", function(authManager){ return { restrict: 'E', scope: {}, - templateUrl: 'frontend/footer.html', + templateUrl: 'footer.html', replace: true, controller: 'FooterCtrl', controllerAs: 'ctrl', @@ -22,9 +22,12 @@ angular.module('app.frontend') } } }) - .controller('FooterCtrl', function ($rootScope, authManager, modelManager, $timeout, dbManager, syncManager, storageManager, passcodeManager) { + .controller('FooterCtrl', function ($rootScope, authManager, modelManager, $timeout, dbManager, + syncManager, storageManager, passcodeManager, componentManager, singletonManager) { - this.user = authManager.user; + this.getUser = function() { + return authManager.user; + } this.updateOfflineStatus = function() { this.offline = authManager.offline(); @@ -45,23 +48,11 @@ angular.module('app.frontend') }.bind(this) this.accountMenuPressed = function() { - this.serverData = {}; this.showAccountMenu = !this.showAccountMenu; - this.showFaq = false; - this.showNewPasswordForm = false; - this.showExtensionsMenu = false; - this.showIOMenu = false; + this.closeAllRooms(); } - this.toggleExtensions = function() { - this.showAccountMenu = false; - this.showIOMenu = false; - this.showExtensionsMenu = !this.showExtensionsMenu; - } - - this.toggleIO = function() { - this.showIOMenu = !this.showIOMenu; - this.showExtensionsMenu = false; + this.closeAccountMenu = () => { this.showAccountMenu = false; } @@ -75,7 +66,7 @@ angular.module('app.frontend') this.refreshData = function() { this.isRefreshing = true; - syncManager.sync(function(response){ + syncManager.sync((response) => { $timeout(function(){ this.isRefreshing = false; }.bind(this), 200) @@ -84,7 +75,7 @@ angular.module('app.frontend') } else { this.syncUpdated(); } - }.bind(this)); + }, null, "refreshData"); } this.syncUpdated = function() { @@ -104,6 +95,60 @@ angular.module('app.frontend') this.clickedNewUpdateAnnouncement = function() { this.newUpdateAvailable = false; - alert("A new update is ready to install. Updates address performance and security issues, as well as bug fixes and feature enhancements. Simply quit Standard Notes and re-open it for the update to be applied.") + alert("A new update is ready to install. Simply quit Standard Notes and reopen it after a brief delay to apply the update.") + } + + + /* Rooms */ + + this.componentManager = componentManager; + this.rooms = []; + + modelManager.addItemSyncObserver("room-bar", "SN|Component", (allItems, validItems, deletedItems, source) => { + var incomingRooms = allItems.filter((candidate) => {return candidate.area == "rooms"}); + this.rooms = _.uniq(this.rooms.concat(incomingRooms)).filter((candidate) => {return !candidate.deleted}); + }); + + componentManager.registerHandler({identifier: "roomBar", areas: ["rooms", "modal"], activationHandler: (component) => { + if(component.active) { + // Show room, if it was not activated manually (in the event of event from componentManager) + if(component.area == "rooms" && !component.showRoom) { + component.showRoom = true; + } + $timeout(() => { + var lastSize = component.getLastSize(); + if(lastSize) { + componentManager.handleSetSizeEvent(component, lastSize); + } + }); + } + }, actionHandler: (component, action, data) => { + if(action == "set-size") { + component.setLastSize(data); + } + }, focusHandler: (component, focused) => { + if(component.isEditor() && focused) { + this.closeAllRooms(); + this.closeAccountMenu(); + } + }}); + + $rootScope.$on("editorFocused", () => { + this.closeAllRooms(); + this.closeAccountMenu(); + }) + + this.onRoomDismiss = function(room) { + room.showRoom = false; + } + + this.closeAllRooms = function() { + for(var room of this.rooms) { + room.showRoom = false; + } + } + + this.selectRoom = function(room) { + room.showRoom = !room.showRoom; } }); diff --git a/app/assets/javascripts/app/frontend/controllers/home.js b/app/assets/javascripts/app/controllers/home.js similarity index 91% rename from app/assets/javascripts/app/frontend/controllers/home.js rename to app/assets/javascripts/app/controllers/home.js index 830479df3..d52c3c59d 100644 --- a/app/assets/javascripts/app/frontend/controllers/home.js +++ b/app/assets/javascripts/app/controllers/home.js @@ -1,4 +1,4 @@ -angular.module('app.frontend') +angular.module('app') .controller('HomeCtrl', function ($scope, $location, $rootScope, $timeout, modelManager, dbManager, syncManager, authManager, themeManager, passcodeManager, storageManager, migrationManager) { @@ -8,6 +8,11 @@ angular.module('app.frontend') $rootScope.$broadcast('new-update-available', version); } + /* Used to avoid circular dependencies where syncManager cannot be imported but rootScope can */ + $rootScope.sync = function(source) { + syncManager.sync("$rootScope.sync - " + source); + } + $rootScope.lockApplication = function() { // Reloading wipes current objects from memory window.location.reload(); @@ -44,7 +49,7 @@ angular.module('app.frontend') dbManager.openDatabase(null, function() { // new database, delete syncToken so that items can be refetched entirely from server syncManager.clearSyncToken(); - syncManager.sync(); + syncManager.sync("openDatabase"); }) } @@ -52,15 +57,14 @@ angular.module('app.frontend') authManager.loadInitialData(); syncManager.loadLocalItems(function(items) { $scope.allTag.didLoad = true; - themeManager.activateInitialTheme(); $scope.$apply(); $rootScope.$broadcast("initial-data-loaded"); - syncManager.sync(null); + syncManager.sync("initiateSync"); // refresh every 30s setInterval(function () { - syncManager.sync(null); + syncManager.sync("timer"); }, 30000); }); } @@ -111,7 +115,7 @@ angular.module('app.frontend') } note.setDirty(true); - syncManager.sync(); + syncManager.sync("updateTagsForNote"); } /* @@ -141,7 +145,7 @@ angular.module('app.frontend') return; } tag.setDirty(true); - syncManager.sync(callback); + syncManager.sync(callback, null, "tagsSave"); $rootScope.$broadcast("tag-changed"); modelManager.resortTag(tag); } @@ -157,7 +161,7 @@ angular.module('app.frontend') syncManager.sync(function(){ // force scope tags to update on sub directives $scope.safeApply(); - }); + }, null, "removeTag"); } } @@ -195,7 +199,7 @@ angular.module('app.frontend') callback(true); } } - }) + }, null, "saveNote") } $scope.safeApply = function(fn) { @@ -236,7 +240,7 @@ angular.module('app.frontend') } else { $scope.notifyDelete(); } - }); + }, null, "deleteNote"); } @@ -259,12 +263,13 @@ angular.module('app.frontend') return; } else { // sign out + authManager.signOut(); syncManager.destroyLocalData(function(){ window.location.reload(); }) } } else { - authManager.login(server, email, pw, false, function(response){ + authManager.login(server, email, pw, false, {}, function(response){ window.location.reload(); }) } diff --git a/app/assets/javascripts/app/frontend/controllers/lockScreen.js b/app/assets/javascripts/app/controllers/lockScreen.js similarity index 78% rename from app/assets/javascripts/app/frontend/controllers/lockScreen.js rename to app/assets/javascripts/app/controllers/lockScreen.js index 409344fb6..b04c77c21 100644 --- a/app/assets/javascripts/app/frontend/controllers/lockScreen.js +++ b/app/assets/javascripts/app/controllers/lockScreen.js @@ -2,7 +2,7 @@ class LockScreen { constructor() { this.restrict = "E"; - this.templateUrl = "frontend/lock-screen.html"; + this.templateUrl = "lock-screen.html"; this.scope = { onSuccess: "&", }; @@ -27,4 +27,4 @@ class LockScreen { } -angular.module('app.frontend').directive('lockScreen', () => new LockScreen); +angular.module('app').directive('lockScreen', () => new LockScreen); diff --git a/app/assets/javascripts/app/frontend/controllers/notes.js b/app/assets/javascripts/app/controllers/notes.js similarity index 56% rename from app/assets/javascripts/app/frontend/controllers/notes.js rename to app/assets/javascripts/app/controllers/notes.js index 2f35f5d98..b04444c04 100644 --- a/app/assets/javascripts/app/frontend/controllers/notes.js +++ b/app/assets/javascripts/app/controllers/notes.js @@ -1,4 +1,4 @@ -angular.module('app.frontend') +angular.module('app') .directive("notesSection", function(){ return { scope: { @@ -7,7 +7,7 @@ angular.module('app.frontend') tag: "=" }, - templateUrl: 'frontend/notes.html', + templateUrl: 'notes.html', replace: true, controller: 'NotesCtrl', controllerAs: 'ctrl', @@ -33,49 +33,109 @@ angular.module('app.frontend') }) .controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager, storageManager) { - this.sortBy = storageManager.getItem("sortBy") || "created_at"; - this.showArchived = storageManager.getBooleanValue("showArchived") || false; - this.sortDescending = this.sortBy != "title"; + this.panelController = {}; + + $rootScope.$on("user-preferences-changed", () => { + this.loadPreferences(); + }); + + this.loadPreferences = function() { + let prevSortValue = this.sortBy; + this.sortBy = authManager.getUserPrefValue("sortBy", "created_at"); + if(prevSortValue && prevSortValue != this.sortBy) { + $timeout(() => { + this.selectFirstNote(); + }) + } + this.sortDescending = this.sortBy != "title"; + + this.showArchived = authManager.getUserPrefValue("showArchived", false); + this.hidePinned = authManager.getUserPrefValue("hidePinned", false); + this.hideNotePreview = authManager.getUserPrefValue("hideNotePreview", false); + this.hideDate = authManager.getUserPrefValue("hideDate", false); + this.hideTags = authManager.getUserPrefValue("hideTags", false); + + let width = authManager.getUserPrefValue("notesPanelWidth"); + if(width) { + this.panelController.setWidth(width); + } + } + + this.loadPreferences(); + + this.onPanelResize = function(newWidth) { + authManager.setUserPrefValue("notesPanelWidth", newWidth); + authManager.syncUserPreferences(); + } + + angular.element(document).ready(() => { + this.loadPreferences(); + }); $rootScope.$on("editorFocused", function(){ this.showMenu = false; }.bind(this)) $rootScope.$on("noteDeleted", function() { - this.selectFirstNote(false); + $timeout(this.onNoteRemoval.bind(this)); }.bind(this)) $rootScope.$on("noteArchived", function() { - this.selectFirstNote(false); - }.bind(this)) + $timeout(this.onNoteRemoval.bind(this)); + }.bind(this)); - this.DefaultNotesToDisplayValue = 20; + + // When a note is removed from the list + this.onNoteRemoval = function() { + let visibleNotes = this.visibleNotes(); + if(this.selectedIndex < visibleNotes.length) { + this.selectNote(visibleNotes[this.selectedIndex]); + } else { + this.selectNote(visibleNotes[visibleNotes.length - 1]); + } + } + + let MinNoteHeight = 51.0; // This is the height of a note cell with nothing but the title, which *is* a display option + this.DefaultNotesToDisplayValue = (document.documentElement.clientHeight / MinNoteHeight) || 20; this.notesToDisplay = this.DefaultNotesToDisplayValue; this.paginate = function() { this.notesToDisplay += this.DefaultNotesToDisplayValue } + this.panelTitle = function() { + if(this.noteFilter.text.length) { + return `${this.tag.notes.filter((i) => {return i.visible;}).length} search results`; + } else if(this.tag) { + return `${this.tag.title} notes`; + } + } + this.optionsSubtitle = function() { - var base = "Sorting by"; + var base = ""; if(this.sortBy == "created_at") { - base += " date added"; + base += " Date Added"; } else if(this.sortBy == "updated_at") { - base += " date modifed"; + base += " Date Modifed"; } else if(this.sortBy == "title") { - base += " title"; + base += " Title"; } if(this.showArchived && (!this.tag || !this.tag.archiveTag)) { - base += " | Including archived" + base += " | + Archived" + } + + if(this.hidePinned) { + base += " | – Pinned" } return base; } - this.toggleShowArchived = function() { - this.showArchived = !this.showArchived; - storageManager.setBooleanValue("showArchived", this.showArchived); + this.toggleKey = function(key) { + this[key] = !this[key]; + authManager.setUserPrefValue(key, this[key]); + authManager.syncUserPreferences(); } this.tagDidChange = function(tag, oldTag) { @@ -108,10 +168,14 @@ angular.module('app.frontend') this.selectFirstNote(createNew); } - this.selectFirstNote = function(createNew) { - var visibleNotes = this.sortedNotes.filter(function(note){ + this.visibleNotes = function() { + return this.sortedNotes.filter(function(note){ return note.visible; }); + } + + this.selectFirstNote = function(createNew) { + var visibleNotes = this.visibleNotes(); if(visibleNotes.length > 0) { this.selectNote(visibleNotes[0]); @@ -121,9 +185,11 @@ angular.module('app.frontend') } this.selectNote = function(note) { + if(!note) { return; } this.selectedNote = note; note.conflict_of = null; // clear conflict this.selectionMade()(note); + this.selectedIndex = this.visibleNotes().indexOf(note); } this.createNewNote = function() { @@ -137,12 +203,7 @@ angular.module('app.frontend') this.noteFilter = {text : ''}; this.filterNotes = function(note) { - if(this.tag.archiveTag) { - note.visible = note.archived; - return note.visible; - } - - if(note.archived && !this.showArchived) { + if((note.archived && !this.showArchived && !this.tag.archiveTag) || (note.pinned && this.hidePinned)) { note.visible = false; return note.visible; } @@ -156,6 +217,11 @@ angular.module('app.frontend') var matchesBody = words.every(function(word) { return note.safeText().toLowerCase().indexOf(word) >= 0; }); note.visible = matchesTitle || matchesBody; } + + if(this.tag.archiveTag) { + note.visible = note.visible && note.archived; + } + return note.visible; }.bind(this) @@ -188,7 +254,21 @@ angular.module('app.frontend') this.setSortBy = function(type) { this.sortBy = type; - storageManager.setItem("sortBy", type); + authManager.setUserPrefValue("sortBy", this.sortBy); + authManager.syncUserPreferences(); + } + + this.shouldShowTags = function(note) { + if(this.hideTags) { + return false; + } + + if(this.tag.all) { + return true; + } + + // Inside a tag, only show tags string if note contains tags other than this.tag + return note.tags && note.tags.length > 1; } }); diff --git a/app/assets/javascripts/app/frontend/controllers/tags.js b/app/assets/javascripts/app/controllers/tags.js similarity index 84% rename from app/assets/javascripts/app/frontend/controllers/tags.js rename to app/assets/javascripts/app/controllers/tags.js index abad7e59f..575011f58 100644 --- a/app/assets/javascripts/app/frontend/controllers/tags.js +++ b/app/assets/javascripts/app/controllers/tags.js @@ -1,4 +1,4 @@ -angular.module('app.frontend') +angular.module('app') .directive("tagsSection", function(){ return { restrict: 'E', @@ -13,7 +13,7 @@ angular.module('app.frontend') updateNoteTag: "&", removeTag: "&" }, - templateUrl: 'frontend/tags.html', + templateUrl: 'tags.html', replace: true, controller: 'TagsCtrl', controllerAs: 'ctrl', @@ -34,26 +34,37 @@ angular.module('app.frontend') } } }) - .controller('TagsCtrl', function ($rootScope, modelManager, $timeout, componentManager) { + .controller('TagsCtrl', function ($rootScope, modelManager, $timeout, componentManager, authManager) { var initialLoad = true; + this.panelController = {}; + + $rootScope.$on("user-preferences-changed", () => { + this.loadPreferences(); + }); + + this.loadPreferences = function() { + let width = authManager.getUserPrefValue("tagsPanelWidth"); + if(width) { + this.panelController.setWidth(width); + } + } + + this.loadPreferences(); + + this.onPanelResize = function(newWidth) { + authManager.setUserPrefValue("tagsPanelWidth", newWidth); + authManager.syncUserPreferences(); + } + + this.componentManager = componentManager; + componentManager.registerHandler({identifier: "tags", areas: ["tags-list"], activationHandler: function(component){ this.component = component; - - if(component.active) { - $timeout(function(){ - var iframe = document.getElementById("tags-list-iframe"); - iframe.onload = function() { - componentManager.registerComponentWindow(this.component, iframe.contentWindow); - }.bind(this); - }.bind(this)); - } - }.bind(this), contextRequestHandler: function(component){ return null; }.bind(this), actionHandler: function(component, action, data){ - if(action === "select-item") { var tag = modelManager.findItem(data.item.uuid); if(tag) { @@ -64,7 +75,6 @@ angular.module('app.frontend') else if(action === "clear-selection") { this.selectTag(this.allTag); } - }.bind(this)}); this.setAllTag = function(allTag) { diff --git a/app/assets/javascripts/app/services/directives/functional/autofocus.js b/app/assets/javascripts/app/directives/functional/autofocus.js similarity index 75% rename from app/assets/javascripts/app/services/directives/functional/autofocus.js rename to app/assets/javascripts/app/directives/functional/autofocus.js index 49c733ecc..8bcf6c1d1 100644 --- a/app/assets/javascripts/app/services/directives/functional/autofocus.js +++ b/app/assets/javascripts/app/directives/functional/autofocus.js @@ -1,6 +1,6 @@ angular - .module('app.frontend') - .directive('mbAutofocus', ['$timeout', function($timeout) { + .module('app') + .directive('snAutofocus', ['$timeout', function($timeout) { return { restrict: 'A', scope: { diff --git a/app/assets/javascripts/app/services/directives/functional/click-outside.js b/app/assets/javascripts/app/directives/functional/click-outside.js similarity index 95% rename from app/assets/javascripts/app/services/directives/functional/click-outside.js rename to app/assets/javascripts/app/directives/functional/click-outside.js index 10e40e1da..dd8296470 100644 --- a/app/assets/javascripts/app/services/directives/functional/click-outside.js +++ b/app/assets/javascripts/app/directives/functional/click-outside.js @@ -1,5 +1,5 @@ angular - .module('app.frontend') + .module('app') .directive('clickOutside', ['$document', function($document) { return { restrict: 'A', diff --git a/app/assets/javascripts/app/services/directives/functional/delay-hide.js b/app/assets/javascripts/app/directives/functional/delay-hide.js similarity index 97% rename from app/assets/javascripts/app/services/directives/functional/delay-hide.js rename to app/assets/javascripts/app/directives/functional/delay-hide.js index 6e381ed3f..fd52d70fb 100644 --- a/app/assets/javascripts/app/services/directives/functional/delay-hide.js +++ b/app/assets/javascripts/app/directives/functional/delay-hide.js @@ -1,5 +1,5 @@ angular - .module('app.frontend') + .module('app') .directive('delayHide', function($timeout) { return { restrict: 'A', diff --git a/app/assets/javascripts/app/services/directives/functional/file-change.js b/app/assets/javascripts/app/directives/functional/file-change.js similarity index 92% rename from app/assets/javascripts/app/services/directives/functional/file-change.js rename to app/assets/javascripts/app/directives/functional/file-change.js index afec10954..c133c0852 100644 --- a/app/assets/javascripts/app/services/directives/functional/file-change.js +++ b/app/assets/javascripts/app/directives/functional/file-change.js @@ -1,5 +1,5 @@ angular - .module('app.frontend') + .module('app') .directive('fileChange', function() { return { restrict: 'A', diff --git a/app/assets/javascripts/app/services/directives/functional/infiniteScroll.js b/app/assets/javascripts/app/directives/functional/infiniteScroll.js similarity index 89% rename from app/assets/javascripts/app/services/directives/functional/infiniteScroll.js rename to app/assets/javascripts/app/directives/functional/infiniteScroll.js index 372f0bf7b..3e1e633ef 100644 --- a/app/assets/javascripts/app/services/directives/functional/infiniteScroll.js +++ b/app/assets/javascripts/app/directives/functional/infiniteScroll.js @@ -1,4 +1,4 @@ -angular.module('app.frontend').directive('infiniteScroll', [ +angular.module('app').directive('infiniteScroll', [ '$rootScope', '$window', '$timeout', function($rootScope, $window, $timeout) { return { link: function(scope, elem, attrs) { diff --git a/app/assets/javascripts/app/services/directives/functional/lowercase.js b/app/assets/javascripts/app/directives/functional/lowercase.js similarity index 95% rename from app/assets/javascripts/app/services/directives/functional/lowercase.js rename to app/assets/javascripts/app/directives/functional/lowercase.js index 1899a251b..40ce7421c 100644 --- a/app/assets/javascripts/app/services/directives/functional/lowercase.js +++ b/app/assets/javascripts/app/directives/functional/lowercase.js @@ -1,5 +1,5 @@ angular - .module('app.frontend') + .module('app') .directive('lowercase', function() { return { require: 'ngModel', diff --git a/app/assets/javascripts/app/services/directives/functional/selectOnClick.js b/app/assets/javascripts/app/directives/functional/selectOnClick.js similarity index 94% rename from app/assets/javascripts/app/services/directives/functional/selectOnClick.js rename to app/assets/javascripts/app/directives/functional/selectOnClick.js index a8ad86cb1..598e45086 100644 --- a/app/assets/javascripts/app/services/directives/functional/selectOnClick.js +++ b/app/assets/javascripts/app/directives/functional/selectOnClick.js @@ -1,5 +1,5 @@ angular - .module('app.frontend') + .module('app') .directive('selectOnClick', ['$window', function ($window) { return { restrict: 'A', diff --git a/app/assets/javascripts/app/services/directives/views/accountMenu.js b/app/assets/javascripts/app/directives/views/accountMenu.js similarity index 88% rename from app/assets/javascripts/app/services/directives/views/accountMenu.js rename to app/assets/javascripts/app/directives/views/accountMenu.js index 4c9d977ee..c561c0214 100644 --- a/app/assets/javascripts/app/services/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/directives/views/accountMenu.js @@ -2,9 +2,10 @@ class AccountMenu { constructor() { this.restrict = "E"; - this.templateUrl = "frontend/directives/account-menu.html"; + this.templateUrl = "directives/account-menu.html"; this.scope = { - "onSuccessfulAuth" : "&" + "onSuccessfulAuth" : "&", + "closeFunction" : "&" }; } @@ -15,28 +16,17 @@ class AccountMenu { $scope.user = authManager.user; $scope.server = syncManager.serverURL; + $scope.close = function() { + $timeout(() => { + $scope.closeFunction()(); + }) + } + $scope.encryptedBackupsAvailable = function() { return authManager.user || passcodeManager.hasPasscode(); } $scope.syncStatus = syncManager.syncStatus; - - $scope.encryptionKey = function() { - return authManager.keys().mk; - } - - $scope.authKey = function() { - return authManager.keys().ak; - } - - $scope.serverPassword = function() { - return syncManager.serverPassword; - } - - $scope.dashboardURL = function() { - return `${$scope.server}/dashboard/#server=${$scope.server}&id=${encodeURIComponent($scope.user.email)}&pw=${$scope.serverPassword()}`; - } - $scope.newPasswordData = {}; $scope.showPasswordChangeForm = function() { @@ -45,7 +35,13 @@ class AccountMenu { $scope.submitPasswordChange = function() { - if($scope.newPasswordData.newPassword != $scope.newPasswordData.newPasswordConfirmation) { + let newPass = $scope.newPasswordData.newPassword; + + if(!newPass || newPass.length == 0) { + return; + } + + if(newPass != $scope.newPasswordData.newPasswordConfirmation) { alert("Your new password does not match its confirmation."); $scope.newPasswordData.status = null; return; @@ -63,7 +59,7 @@ class AccountMenu { // perform a sync beforehand to pull in any last minutes changes before we change the encryption key (and thus cant decrypt new changes) syncManager.sync(function(response){ - authManager.changePassword(email, $scope.newPasswordData.newPassword, function(response){ + authManager.changePassword(email, newPass, function(response){ if(response.error) { alert("There was an error changing your password. Please try again."); $scope.newPasswordData.status = null; @@ -86,10 +82,19 @@ class AccountMenu { }, 1000) }); }) - }) + }, null, "submitPasswordChange") + } + + $scope.submitMfaForm = function() { + var params = {}; + params[$scope.formData.mfa.payload.mfa_key] = $scope.formData.userMfaCode; + $scope.login(params); } $scope.submitAuthForm = function() { + if(!$scope.formData.email || !$scope.formData.user_password) { + return; + } if($scope.formData.showLogin) { $scope.login(); } else { @@ -97,19 +102,37 @@ class AccountMenu { } } - $scope.login = function() { + $scope.login = function(extraParams) { $scope.formData.status = "Generating Login Keys..."; $timeout(function(){ - authManager.login($scope.formData.url, $scope.formData.email, $scope.formData.user_password, $scope.formData.ephemeral, function(response){ - if(!response || response.error) { - $scope.formData.status = null; - var error = response ? response.error : {message: "An unknown error occured."} - if(!response || (response && !response.didDisplayAlert)) { - alert(error.message); + authManager.login($scope.formData.url, $scope.formData.email, $scope.formData.user_password, $scope.formData.ephemeral, extraParams, + (response) => { + if(!response || response.error) { + $scope.formData.status = null; + var error = response ? response.error : {message: "An unknown error occured."} + + // MFA Error + if(error.tag == "mfa-required" || error.tag == "mfa-invalid") { + $timeout(() => { + $scope.formData.showLogin = false; + $scope.formData.mfa = error; + }) + } + + // General Error + else { + $timeout(() => { + $scope.formData.showLogin = true; + $scope.formData.mfa = null; + }) + alert(error.message); + } + } + + // Success + else { + $scope.onAuthSuccess(); } - } else { - $scope.onAuthSuccess(); - } }); }) } @@ -150,7 +173,7 @@ class AccountMenu { $timeout(function(){ $scope.onSuccessfulAuth()(); syncManager.refreshErroredItems(); - syncManager.sync(); + syncManager.sync("onAuthSuccess"); }) } @@ -159,7 +182,6 @@ class AccountMenu { $rootScope.$broadcast("major-data-change"); $scope.clearDatabaseAndRewriteAllItems(true, block); } - else { modelManager.resetLocalMemory(); storageManager.clearAllModels(function(){ @@ -268,7 +290,7 @@ class AccountMenu { syncManager.sync((response) => { callback(response, errorCount); - }, {additionalFields: ["created_at", "updated_at"]}); + }, {additionalFields: ["created_at", "updated_at"]}, "importJSONData"); }.bind(this) if(data.auth_params) { @@ -431,7 +453,7 @@ class AccountMenu { alert("Your items have been successfully re-encrypted and synced. You must sign out of all other signed in applications (mobile, desktop, web) and sign in again, or else you may corrupt your data.") $scope.newPasswordData = {}; }, 1000) - }); + }, null, "reencryptPressed"); } @@ -499,9 +521,9 @@ class AccountMenu { $scope.encryptionStatusString = function() { if(!authManager.offline()) { - return "End-to-end encryption is enabled. Your data is encrypted before being synced to your private account."; + return "End-to-end encryption is enabled. Your data is encrypted before syncing to your private account."; } else if(passcodeManager.hasPasscode()) { - return "Encryption is enabled. Your data is encrypted using your passcode before being stored on disk."; + return "Encryption is enabled. Your data is encrypted using your passcode before saving to your device storage."; } else { return "Encryption is not enabled. Sign in, register, or add a passcode lock to enable encryption."; } @@ -511,11 +533,6 @@ class AccountMenu { Passcode Lock */ - $scope.passcodeOptionAvailable = function() { - // If you're signed in with an ephemeral session, passcode lock is unavailable - return authManager.offline() || !authManager.isEphemeralSession(); - } - $scope.hasPasscode = function() { return passcodeManager.hasPasscode(); } @@ -531,18 +548,13 @@ class AccountMenu { return; } - passcodeManager.setPasscode(passcode, () => { + let fn = $scope.formData.changingPasscode ? passcodeManager.changePasscode : passcodeManager.setPasscode; + + fn(passcode, () => { $timeout(function(){ $scope.formData.showPasscodeForm = false; var offline = authManager.offline(); - // Allow UI to update before showing alert - setTimeout(function () { - var message = "You've succesfully set an app passcode."; - if(offline) { message += " Your items will now be encrypted using this passcode."; } - alert(message); - }, 10); - if(offline) { // Allows desktop to make backup file $rootScope.$broadcast("major-data-change"); @@ -552,6 +564,12 @@ class AccountMenu { }) } + $scope.changePasscodePressed = function() { + $scope.formData.changingPasscode = true; + $scope.addPasscodeClicked(); + $scope.formData.changingPasscode = false; + } + $scope.removePasscodePressed = function() { var signedIn = !authManager.offline(); var message = "Are you sure you want to remove your local passcode?"; @@ -577,4 +595,4 @@ class AccountMenu { } } -angular.module('app.frontend').directive('accountMenu', () => new AccountMenu); +angular.module('app').directive('accountMenu', () => new AccountMenu); diff --git a/app/assets/javascripts/app/directives/views/actionsMenu.js b/app/assets/javascripts/app/directives/views/actionsMenu.js new file mode 100644 index 000000000..1c7ace647 --- /dev/null +++ b/app/assets/javascripts/app/directives/views/actionsMenu.js @@ -0,0 +1,86 @@ +class ActionsMenu { + + constructor() { + this.restrict = "E"; + this.templateUrl = "directives/actions-menu.html"; + this.scope = { + item: "=" + }; + } + + controller($scope, modelManager, actionsManager) { + 'ngInject'; + + $scope.renderData = {}; + + $scope.extensions = actionsManager.extensions.sort((a, b) => {return a.name.toLowerCase() > b.name.toLowerCase()}); + + for(let ext of $scope.extensions) { + ext.loading = true; + actionsManager.loadExtensionInContextOfItem(ext, $scope.item, function(scopedExtension) { + ext.loading = false; + }) + } + + $scope.executeAction = function(action, extension, parentAction) { + if(action.verb == "nested") { + if(!action.subrows) { + action.subrows = $scope.subRowsForAction(action, extension); + } else { + action.subrows = null; + } + return; + } + action.running = true; + actionsManager.executeAction(action, extension, $scope.item, function(response){ + action.running = false; + $scope.handleActionResponse(action, response); + + // reload extension actions + actionsManager.loadExtensionInContextOfItem(extension, $scope.item, function(ext){ + // keep nested state + if(parentAction) { + var matchingAction = _.find(ext.actions, {label: parentAction.label}); + matchingAction.subrows = $scope.subRowsForAction(parentAction, extension); + } + }); + }) + } + + $scope.handleActionResponse = function(action, response) { + switch (action.verb) { + case "render": { + var item = response.item; + if(item.content_type == "Note") { + $scope.renderData.title = item.title; + $scope.renderData.text = item.text; + $scope.renderData.showRenderModal = true; + } + } + } + } + + + $scope.subRowsForAction = function(parentAction, extension) { + if(!parentAction.subactions) { + return null; + } + return parentAction.subactions.map((subaction) => { + return { + onClick: ($event) => { + this.executeAction(subaction, extension, parentAction); + $event.stopPropagation(); + }, + title: subaction.label, + subtitle: subaction.desc, + spinnerClass: subaction.running ? 'info' : null + } + }) + } + + + } + +} + +angular.module('app').directive('actionsMenu', () => new ActionsMenu); diff --git a/app/assets/javascripts/app/directives/views/componentModal.js b/app/assets/javascripts/app/directives/views/componentModal.js new file mode 100644 index 000000000..7cda3c897 --- /dev/null +++ b/app/assets/javascripts/app/directives/views/componentModal.js @@ -0,0 +1,31 @@ +class ComponentModal { + + constructor() { + this.restrict = "E"; + this.templateUrl = "directives/component-modal.html"; + this.scope = { + show: "=", + component: "=", + callback: "=", + onDismiss: "&" + }; + } + + link($scope, el, attrs) { + $scope.el = el; + } + + controller($scope, $timeout, componentManager) { + 'ngInject'; + + $scope.dismiss = function(callback) { + $scope.el.remove(); + $scope.$destroy(); + $scope.onDismiss && $scope.onDismiss() && $scope.onDismiss()($scope.component); + callback && callback(); + } + } + +} + +angular.module('app').directive('componentModal', () => new ComponentModal); diff --git a/app/assets/javascripts/app/directives/views/componentView.js b/app/assets/javascripts/app/directives/views/componentView.js new file mode 100644 index 000000000..dd67eadee --- /dev/null +++ b/app/assets/javascripts/app/directives/views/componentView.js @@ -0,0 +1,130 @@ +class ComponentView { + + constructor(componentManager, desktopManager, $timeout) { + this.restrict = "E"; + this.templateUrl = "directives/component-view.html"; + this.scope = { + component: "=", + manualDealloc: "=" + }; + + this.componentManager = componentManager; + this.desktopManager = desktopManager; + this.timeout = $timeout; + } + + link($scope, el, attrs, ctrl) { + $scope.el = el; + + $scope.identifier = "component-view-" + Math.random(); + + // console.log("Registering handler", $scope.identifier, $scope.component.name); + + this.componentManager.registerHandler({identifier: $scope.identifier, areas: [$scope.component.area], activationHandler: (component) => { + if(component.active) { + this.timeout(() => { + var iframe = this.componentManager.iframeForComponent(component); + if(iframe) { + iframe.onload = function() { + this.componentManager.registerComponentWindow(component, iframe.contentWindow); + }.bind(this); + } + }); + } + }, + actionHandler: (component, action, data) => { + if(action == "set-size") { + this.componentManager.handleSetSizeEvent(component, data); + } + }}); + + $scope.updateObserver = this.desktopManager.registerUpdateObserver((component) => { + if(component == $scope.component && component.active) { + $scope.reloadComponent(); + } + }) + + $scope.$watch('component', function(component, prevComponent){ + ctrl.componentValueChanging(component, prevComponent); + }); + } + + controller($scope, $timeout, componentManager, desktopManager) { + 'ngInject'; + + this.componentValueChanging = (component, prevComponent) => { + if(prevComponent && component !== prevComponent) { + // Deactive old component + componentManager.deactivateComponent(prevComponent); + } + + if(component) { + componentManager.activateComponent(component); + console.log("Loading", $scope.component.name, $scope.getUrl(), component.valid_until); + + $scope.reloadStatus(); + } + } + + $scope.reloadComponent = function() { + console.log("Reloading component", $scope.component); + componentManager.deactivateComponent($scope.component); + $timeout(() => { + componentManager.activateComponent($scope.component); + }) + } + + $scope.reloadStatus = function() { + let component = $scope.component; + $scope.reloading = true; + let previouslyValid = $scope.componentValid; + + var expired, offlineRestricted, urlError; + + offlineRestricted = component.offlineOnly && !isDesktopApplication(); + + urlError = + (!isDesktopApplication() && (!component.url && !component.hosted_url)) + || + (isDesktopApplication() && (!component.local_url && !component.url && !component.hosted_url)) + + expired = component.valid_until && component.valid_until <= new Date(); + + $scope.componentValid = !offlineRestricted && !urlError && !expired; + + if(offlineRestricted) $scope.error = 'offline-restricted'; + else if(urlError) $scope.error = 'url-missing'; + else if(expired) $scope.error = 'expired'; + else $scope.error = null; + + if($scope.componentValid !== previouslyValid) { + if($scope.componentValid) { + componentManager.activateComponent(component); + } + } + + $timeout(() => { + $scope.reloading = false; + }, 500) + } + + $scope.getUrl = function() { + var url = componentManager.urlForComponent($scope.component); + $scope.component.runningLocally = (url !== $scope.component.url) && url !== ($scope.component.hosted_url); + return url; + } + + $scope.$on("$destroy", function() { + // console.log("Deregistering handler", $scope.identifier, $scope.component.name); + componentManager.deregisterHandler($scope.identifier); + if($scope.component && !$scope.manualDealloc) { + componentManager.deactivateComponent($scope.component); + } + + desktopManager.deregisterUpdateObserver($scope.updateObserver); + }); + } + +} + +angular.module('app').directive('componentView', (componentManager, desktopManager, $timeout) => new ComponentView(componentManager, desktopManager, $timeout)); diff --git a/app/assets/javascripts/app/directives/views/editorMenu.js b/app/assets/javascripts/app/directives/views/editorMenu.js new file mode 100644 index 000000000..734a829cc --- /dev/null +++ b/app/assets/javascripts/app/directives/views/editorMenu.js @@ -0,0 +1,91 @@ +class EditorMenu { + + constructor() { + this.restrict = "E"; + this.templateUrl = "directives/editor-menu.html"; + this.scope = { + callback: "&", + selectedEditor: "=", + currentItem: "=" + }; + } + + controller($scope, componentManager, syncManager, $timeout) { + 'ngInject'; + + $scope.formData = {}; + + $scope.editors = componentManager.componentsForArea("editor-editor").sort((a, b) => {return a.name.toLowerCase() > b.name.toLowerCase()}); + $scope.stack = componentManager.componentsForArea("editor-stack").sort((a, b) => {return a.name.toLowerCase() > b.name.toLowerCase()}); + + $scope.isDesktop = isDesktopApplication(); + + $scope.defaultEditor = $scope.editors.filter((e) => {return e.isDefaultEditor()})[0]; + + $scope.selectComponent = function($event, component) { + $event.stopPropagation(); + if(component) { + component.conflict_of = null; // clear conflict if applicable + } + $timeout(() => { + $scope.callback()(component); + }) + } + + $scope.toggleDefaultForEditor = function(editor) { + if($scope.defaultEditor == editor) { + $scope.removeEditorDefault(editor); + } else { + $scope.makeEditorDefault(editor); + } + } + + $scope.offlineAvailableForComponent = function(component) { + return component.local_url && isDesktopApplication(); + } + + $scope.makeEditorDefault = function(component) { + var currentDefault = componentManager.componentsForArea("editor-editor").filter((e) => {return e.isDefaultEditor()})[0]; + if(currentDefault) { + currentDefault.setAppDataItem("defaultEditor", false); + currentDefault.setDirty(true); + } + + component.setAppDataItem("defaultEditor", true); + component.setDirty(true); + syncManager.sync("makeEditorDefault"); + + $scope.defaultEditor = component; + } + + $scope.removeEditorDefault = function(component) { + component.setAppDataItem("defaultEditor", false); + component.setDirty(true); + syncManager.sync("removeEditorDefault"); + + $scope.defaultEditor = null; + } + + $scope.shouldDisplayRunningLocallyLabel = function(component) { + if(!component.runningLocally) { + return false; + } + + if(component == $scope.selectedEditor) { + return true; + } else if(component.area == "editor-stack") { + return $scope.stackComponentEnabled(component); + } else { + return false; + } + } + + $scope.stackComponentEnabled = function(component) { + return component.active && !component.isExplicitlyDisabledForItem($scope.currentItem); + } + + } + +} + +angular.module('app').directive('editorMenu', () => new EditorMenu); diff --git a/app/assets/javascripts/app/directives/views/menuRow.js b/app/assets/javascripts/app/directives/views/menuRow.js new file mode 100644 index 000000000..fe7a988f0 --- /dev/null +++ b/app/assets/javascripts/app/directives/views/menuRow.js @@ -0,0 +1,32 @@ +class MenuRow { + + constructor() { + this.restrict = "E"; + this.transclude = true; + this.templateUrl = "directives/menu-row.html"; + this.scope = { + circle: "=", + title: "=", + subtite: "=", + hasButton: "=", + buttonText: "=", + buttonClass: "=", + buttonAction: "&", + spinnerClass: "=", + subRows: "=", + faded: "=" + }; + } + + controller($scope, componentManager) { + 'ngInject'; + + $scope.clickButton = function($event) { + $event.stopPropagation(); + $scope.buttonAction(); + } + + } +} + +angular.module('app').directive('menuRow', () => new MenuRow); diff --git a/app/assets/javascripts/app/directives/views/panelResizer.js b/app/assets/javascripts/app/directives/views/panelResizer.js new file mode 100644 index 000000000..646bc1ab4 --- /dev/null +++ b/app/assets/javascripts/app/directives/views/panelResizer.js @@ -0,0 +1,246 @@ +class PanelResizer { + + constructor() { + this.restrict = "E"; + this.templateUrl = "directives/panel-resizer.html"; + this.scope = { + index: "=", + panelId: "=", + onResize: "&", + onResizeFinish: "&", + control: "=", + alwaysVisible: "=", + minWidth: "=", + property: "=", + hoverable: "=", + collapsable: "=" + }; + } + + link(scope, elem, attrs, ctrl) { + scope.elem = elem; + + scope.control.setWidth = function(value) { + scope.setWidth(value, true); + } + + scope.control.setLeft = function(value) { + scope.setLeft(value); + } + } + + controller($scope, $element, modelManager, actionsManager, $timeout) { + 'ngInject'; + + let panel = document.getElementById($scope.panelId); + if(!panel) { + console.log("Panel not found for", $scope.panelId); + } + + let resizerColumn = $element[0]; + let resizerWidth = resizerColumn.offsetWidth; + let minWidth = $scope.minWidth || resizerWidth; + var pressed = false; + var startWidth = panel.scrollWidth, startX = 0, lastDownX = 0, collapsed, lastWidth = startWidth, startLeft, lastLeft; + var appFrame; + + function getParentRect() { + return panel.parentNode.getBoundingClientRect(); + } + + if($scope.property == "right") { + let handleReize = debounce((event) => { + reloadDefaultValues(); + handleWidthEvent(); + $timeout(() => { $scope.finishSettingWidth(); }) + }, 250); + + window.addEventListener('resize', handleReize); + + $scope.$on("$destroy", function() { + window.removeEventListener('resize', handleReize); + }); + } + + function reloadDefaultValues() { + startWidth = panel.scrollWidth; + appFrame = document.getElementById("app").getBoundingClientRect(); + } + reloadDefaultValues(); + + if($scope.alwaysVisible) { + resizerColumn.classList.add("always-visible"); + } + + if($scope.hoverable) { + resizerColumn.classList.add("hoverable"); + } + + $scope.setWidth = function(width, finish) { + if(width < minWidth) { + width = minWidth; + } + + let parentRect = getParentRect(); + + if(width > parentRect.width) { + width = parentRect.width; + } + + let maxWidth = appFrame.width - panel.getBoundingClientRect().x; + if(width > maxWidth) { + width = maxWidth; + } + + if(width == parentRect.width) { + panel.style.width = "100%"; + panel.style.flexBasis = "100%"; + } else { + panel.style.flexBasis = width + "px"; + panel.style.width = width + "px"; + } + + lastWidth = width; + + if(finish) { + $scope.finishSettingWidth(); + } + } + + $scope.setLeft = function(left) { + panel.style.left = left + "px"; + lastLeft = left; + } + + $scope.finishSettingWidth = function() { + if(!$scope.collapsable) { + return; + } + + if(lastWidth <= minWidth) { + collapsed = true; + } else { + collapsed = false; + } + if(collapsed) { + resizerColumn.classList.add("collapsed"); + } else { + resizerColumn.classList.remove("collapsed"); + } + } + + resizerColumn.addEventListener("mousedown", function(event){ + pressed = true; + lastDownX = event.clientX; + startWidth = panel.scrollWidth; + startLeft = panel.offsetLeft; + panel.classList.add("no-selection"); + + if($scope.hoverable) { + resizerColumn.classList.add("dragging"); + } + }) + + document.addEventListener("mousemove", function(event){ + if(!pressed) { + return; + } + + event.preventDefault(); + + if($scope.property && $scope.property == 'left') { + handleLeftEvent(event); + } else { + handleWidthEvent(event); + } + }) + + function handleWidthEvent(event) { + var rect = panel.getBoundingClientRect(); + var panelMaxX = rect.left + (startWidth || panel.style.maxWidth); + + var x; + if(event) { + x = event.clientX; + } else { + // coming from resize event + x = 0; + lastDownX = 0; + } + + let deltaX = x - lastDownX; + var newWidth = startWidth + deltaX; + + $scope.setWidth(newWidth, false); + + if($scope.onResize()) { + $scope.onResize()(lastWidth, panel); + } + } + + function handleLeftEvent(event) { + var panelRect = panel.getBoundingClientRect(); + var x = event.clientX || panelRect.x; + let deltaX = x - lastDownX; + var newLeft = startLeft + deltaX; + if(newLeft < 0) { + newLeft = 0; + deltaX = -startLeft; + } + + let parentRect = getParentRect(); + + var newWidth = startWidth - deltaX; + if(newWidth < minWidth) { + newWidth = minWidth; + } + + if(newWidth > parentRect.width) { + newWidth = parentRect.width; + } + + + if(newLeft + newWidth > parentRect.width) { + newLeft = parentRect.width - newWidth; + } + + $scope.setLeft(newLeft, false); + $scope.setWidth(newWidth, false); + } + + document.addEventListener("mouseup", function(event){ + if(pressed) { + pressed = false; + resizerColumn.classList.remove("dragging"); + panel.classList.remove("no-selection"); + + let isMaxWidth = lastWidth == getParentRect().width; + + if($scope.onResizeFinish) { + $scope.onResizeFinish()(lastWidth, lastLeft, isMaxWidth); + } + + $scope.finishSettingWidth(); + } + }) + } + +} + +angular.module('app').directive('panelResizer', () => new PanelResizer); + +/* via https://davidwalsh.name/javascript-debounce-function */ +function debounce(func, wait, immediate) { + var timeout; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; +}; diff --git a/app/assets/javascripts/app/directives/views/permissionsModal.js b/app/assets/javascripts/app/directives/views/permissionsModal.js new file mode 100644 index 000000000..a04ad0d8a --- /dev/null +++ b/app/assets/javascripts/app/directives/views/permissionsModal.js @@ -0,0 +1,100 @@ +class PermissionsModal { + + constructor() { + this.restrict = "E"; + this.templateUrl = "directives/permissions-modal.html"; + this.scope = { + show: "=", + component: "=", + permissions: "=", + callback: "=" + }; + } + + link($scope, el, attrs) { + + $scope.dismiss = function() { + el.remove(); + } + + $scope.accept = function() { + $scope.callback(true); + $scope.dismiss(); + } + + $scope.deny = function() { + $scope.callback(false); + $scope.dismiss(); + } + } + + controller($scope, modelManager) { + 'ngInject'; + + $scope.permissionsString = function() { + var finalString = ""; + let permissionsCount = $scope.permissions.length; + + let addSeparator = (index, length) => { + if(index > 0) { + if(index == length - 1) { + if(length == 2) { + return " and "; + } else { + return ", and " + } + } else { + return ", "; + } + } + + return ""; + } + + $scope.permissions.forEach((permission, index) => { + + if(permission.name === "stream-items") { + var types = permission.content_types.map(function(type){ + var desc = modelManager.humanReadableDisplayForContentType(type); + if(desc) { + return desc + "s"; + } else { + return "items of type " + type; + } + }) + var typesString = ""; + + for(var i = 0;i < types.length;i++) { + var type = types[i]; + typesString += addSeparator(i, types.length + permissionsCount - index - 1); + typesString += type; + } + + finalString += addSeparator(index, permissionsCount); + + finalString += typesString; + + if(types.length >= 2 && index < permissionsCount - 1) { + // If you have a list of types, and still an additional root-level permission coming up, add a comma + finalString += ", "; + } + } else if(permission.name === "stream-context-item") { + var mapping = { + "editor-stack" : "working note", + "note-tags" : "working note", + "editor-editor": "working note" + } + + finalString += addSeparator(index, permissionsCount, true); + + finalString += mapping[$scope.component.area]; + } + }) + + return finalString + "."; + } + } + +} + +angular.module('app').directive('permissionsModal', () => new PermissionsModal); diff --git a/app/assets/javascripts/app/services/filters/appDate.js b/app/assets/javascripts/app/filters/appDate.js similarity index 91% rename from app/assets/javascripts/app/services/filters/appDate.js rename to app/assets/javascripts/app/filters/appDate.js index 381c3ed54..9a84a796e 100644 --- a/app/assets/javascripts/app/services/filters/appDate.js +++ b/app/assets/javascripts/app/filters/appDate.js @@ -1,4 +1,4 @@ -angular.module('app.frontend') +angular.module('app') .filter('appDate', function ($filter) { return function (input) { return input ? $filter('date')(new Date(input), 'MM/dd/yyyy', 'UTC') : ''; diff --git a/app/assets/javascripts/app/services/filters/sortBy.js b/app/assets/javascripts/app/filters/sortBy.js similarity index 97% rename from app/assets/javascripts/app/services/filters/sortBy.js rename to app/assets/javascripts/app/filters/sortBy.js index c3902604b..2f19d3769 100644 --- a/app/assets/javascripts/app/services/filters/sortBy.js +++ b/app/assets/javascripts/app/filters/sortBy.js @@ -1,4 +1,4 @@ -angular.module('app.frontend') +angular.module('app') .filter('sortBy', function ($filter) { return function(items, sortBy) { let sortValueFn = (a, b, pinCheck = false) => { diff --git a/app/assets/javascripts/app/services/filters/startFrom.js b/app/assets/javascripts/app/filters/startFrom.js similarity index 56% rename from app/assets/javascripts/app/services/filters/startFrom.js rename to app/assets/javascripts/app/filters/startFrom.js index 3cf824c4c..7b64038f6 100644 --- a/app/assets/javascripts/app/services/filters/startFrom.js +++ b/app/assets/javascripts/app/filters/startFrom.js @@ -1,4 +1,4 @@ -angular.module('app.frontend').filter('startFrom', function() { +angular.module('app').filter('startFrom', function() { return function(input, start) { return input.slice(start); }; diff --git a/app/assets/javascripts/app/services/filters/trusted.js b/app/assets/javascripts/app/filters/trusted.js similarity index 52% rename from app/assets/javascripts/app/services/filters/trusted.js rename to app/assets/javascripts/app/filters/trusted.js index 7bb57b301..f2a41a3c0 100644 --- a/app/assets/javascripts/app/services/filters/trusted.js +++ b/app/assets/javascripts/app/filters/trusted.js @@ -1,4 +1,4 @@ -angular.module('app.frontend').filter('trusted', ['$sce', function ($sce) { +angular.module('app').filter('trusted', ['$sce', function ($sce) { return function(url) { return $sce.trustAsResourceUrl(url); }; diff --git a/app/assets/javascripts/app/frontend/models/app/extension.js b/app/assets/javascripts/app/frontend/models/app/extension.js deleted file mode 100644 index 445c4dbb1..000000000 --- a/app/assets/javascripts/app/frontend/models/app/extension.js +++ /dev/null @@ -1,125 +0,0 @@ -class Action { - constructor(json) { - _.merge(this, json); - this.running = false; // in case running=true was synced with server since model is uploaded nondiscriminatory - this.error = false; - if(this.lastExecuted) { - // is string - this.lastExecuted = new Date(this.lastExecuted); - } - } - - permissionsString() { - if(!this.permissions) { - return ""; - } - - var permission = this.permissions.charAt(0).toUpperCase() + this.permissions.slice(1); // capitalize first letter - permission += ": "; - for(var contentType of this.content_types) { - if(contentType == "*") { - permission += "All items"; - } else { - permission += contentType; - } - - permission += " "; - } - - return permission; - } - - encryptionModeString() { - if(this.verb != "post") { - return null; - } - var encryptionMode = "This action accepts data "; - if(this.accepts_encrypted && this.accepts_decrypted) { - encryptionMode += "encrypted or decrypted."; - } else { - if(this.accepts_encrypted) { - encryptionMode += "encrypted."; - } else { - encryptionMode += "decrypted."; - } - } - return encryptionMode; - } - -} - -class Extension extends Item { - constructor(json) { - super(json); - - if(this.encrypted === null || this.encrypted === undefined) { - // Default to encrypted on creation. - this.encrypted = true; - } - - if(json.actions) { - this.actions = json.actions.map(function(action){ - return new Action(action); - }) - } - - if(!this.actions) { - this.actions = []; - } - } - - actionsInGlobalContext() { - return this.actions.filter(function(action){ - return action.context == "global"; - }) - } - - actionsWithContextForItem(item) { - return this.actions.filter(function(action){ - return action.context == item.content_type || action.context == "Item"; - }) - } - - mapContentToLocalProperties(content) { - super.mapContentToLocalProperties(content) - this.name = content.name; - this.description = content.description; - this.url = content.url; - - if(content.encrypted !== null && content.encrypted !== undefined) { - this.encrypted = content.encrypted; - } else { - this.encrypted = true; - } - - this.supported_types = content.supported_types; - if(content.actions) { - this.actions = content.actions.map(function(action){ - return new Action(action); - }) - } - } - - referenceParams() { - return null; - } - - get content_type() { - return "Extension"; - } - - structureParams() { - var params = { - name: this.name, - url: this.url, - description: this.description, - actions: this.actions, - supported_types: this.supported_types, - encrypted: this.encrypted - }; - - _.merge(params, super.structureParams()); - return params; - } - -} diff --git a/app/assets/javascripts/app/frontend/models/api/item.js b/app/assets/javascripts/app/models/api/item.js similarity index 79% rename from app/assets/javascripts/app/frontend/models/api/item.js rename to app/assets/javascripts/app/models/api/item.js index 1c3dcff1d..7671b061e 100644 --- a/app/assets/javascripts/app/frontend/models/api/item.js +++ b/app/assets/javascripts/app/models/api/item.js @@ -50,9 +50,17 @@ class Item { if(json.content) { this.mapContentToLocalProperties(this.contentObject); + } else if(json.deleted == true) { + this.handleDeletedContent(); } } + + /* Allows the item to handle the case where the item is deleted and the content is null */ + handleDeletedContent() { + // Subclasses can override + } + setDirty(dirty) { this.dirty = dirty; @@ -84,10 +92,10 @@ class Item { } mapContentToLocalProperties(contentObj) { - this.appData = contentObj.appData; - if(!this.appData) { - this.appData = {}; + if(contentObj.appData) { + this.appData = contentObj.appData; } + if(!this.appData) { this.appData = {}; } } createContentJSONFromProperties() { @@ -185,7 +193,27 @@ class Item { return this.getAppDataItem("archived"); } + /* + During sync conflicts, when determing whether to create a duplicate for an item, we can omit keys that have no + meaningful weight and can be ignored. For example, if one component has active = true and another component has active = false, + it would be silly to duplicate them, so instead we ignore this. + */ + keysToIgnoreWhenCheckingContentEquality() { + return []; + } + isItemContentEqualWith(otherItem) { + let omit = (obj, keys) => { + for(var key of keys) { + delete obj[key]; + } + return obj; + } + var left = omit(this.structureParams(), this.keysToIgnoreWhenCheckingContentEquality()); + var right = omit(otherItem.structureParams(), otherItem.keysToIgnoreWhenCheckingContentEquality()); + + return JSON.stringify(left) === JSON.stringify(right) + } /* Dates diff --git a/app/assets/javascripts/app/frontend/models/app/theme.js b/app/assets/javascripts/app/models/api/mfa.js similarity index 50% rename from app/assets/javascripts/app/frontend/models/app/theme.js rename to app/assets/javascripts/app/models/api/mfa.js index ec7486012..d3792654d 100644 --- a/app/assets/javascripts/app/frontend/models/app/theme.js +++ b/app/assets/javascripts/app/models/api/mfa.js @@ -1,4 +1,4 @@ -class Theme extends Item { +class Mfa extends Item { constructor(json_obj) { super(json_obj); @@ -6,18 +6,11 @@ class Theme extends Item { mapContentToLocalProperties(content) { super.mapContentToLocalProperties(content) - this.url = content.url; - this.name = content.name; + this.serverContent = content; } structureParams() { - var params = { - url: this.url, - name: this.name, - }; - - _.merge(params, super.structureParams()); - return params; + return _.merge(this.serverContent, super.structureParams()); } toJSON() { @@ -25,6 +18,11 @@ class Theme extends Item { } get content_type() { - return "SN|Theme"; + return "SF|MFA"; } + + doNotEncrypt() { + return true; + } + } diff --git a/app/assets/javascripts/app/frontend/models/api/syncAdapter.js b/app/assets/javascripts/app/models/api/serverExtension.js similarity index 96% rename from app/assets/javascripts/app/frontend/models/api/syncAdapter.js rename to app/assets/javascripts/app/models/api/serverExtension.js index 12f87d20f..8149c5a95 100644 --- a/app/assets/javascripts/app/frontend/models/api/syncAdapter.js +++ b/app/assets/javascripts/app/models/api/serverExtension.js @@ -1,4 +1,4 @@ -class SyncAdapter extends Item { +class ServerExtension extends Item { constructor(json_obj) { super(json_obj); diff --git a/app/assets/javascripts/app/frontend/models/app/component.js b/app/assets/javascripts/app/models/app/component.js similarity index 61% rename from app/assets/javascripts/app/frontend/models/app/component.js rename to app/assets/javascripts/app/models/app/component.js index ac0c30d0e..92fae1ba6 100644 --- a/app/assets/javascripts/app/frontend/models/app/component.js +++ b/app/assets/javascripts/app/models/app/component.js @@ -18,13 +18,30 @@ class Component extends Item { mapContentToLocalProperties(content) { super.mapContentToLocalProperties(content) - this.url = content.url; + /* Legacy */ + this.url = content.url || content.hosted_url; + /* New */ + this.local_url = content.local_url; + this.hosted_url = content.hosted_url || content.url; + this.offlineOnly = content.offlineOnly; + + if(content.valid_until) { + this.valid_until = new Date(content.valid_until); + } + this.name = content.name; + this.autoupdateDisabled = content.autoupdateDisabled; + + this.package_info = content.package_info; // the location in the view this component is located in. Valid values are currently tags-list, note-tags, and editor-stack` this.area = content.area; this.permissions = content.permissions; + if(!this.permissions) { + this.permissions = []; + } + this.active = content.active; // custom data that a component can store in itself @@ -37,13 +54,25 @@ class Component extends Item { this.associatedItemIds = content.associatedItemIds || []; } + handleDeletedContent() { + super.handleDeletedContent(); + + this.active = false; + } + structureParams() { var params = { url: this.url, + hosted_url: this.hosted_url, + local_url: this.local_url, + valid_until: this.valid_until, + offlineOnly: this.offlineOnly, name: this.name, area: this.area, + package_info: this.package_info, permissions: this.permissions, active: this.active, + autoupdateDisabled: this.autoupdateDisabled, componentData: this.componentData, disassociatedItemIds: this.disassociatedItemIds, associatedItemIds: this.associatedItemIds, @@ -65,10 +94,26 @@ class Component extends Item { return this.area == "editor-editor"; } + isTheme() { + return this.content_type == "SN|Theme" || this.area == "themes"; + } + isDefaultEditor() { return this.getAppDataItem("defaultEditor") == true; } + setLastSize(size) { + this.setAppDataItem("lastSize", size); + } + + getLastSize() { + return this.getAppDataItem("lastSize"); + } + + keysToIgnoreWhenCheckingContentEquality() { + return ["active"].concat(super.keysToIgnoreWhenCheckingContentEquality()); + } + /* An associative component depends on being explicitly activated for a given item, compared to a dissaciative component, @@ -86,11 +131,11 @@ class Component extends Item { this.associatedItemIds.push(item.uuid); } - isActiveForItem(item) { - if(this.isAssociative()) { - return this.associatedItemIds.indexOf(item.uuid) !== -1; - } else { - return this.disassociatedItemIds.indexOf(item.uuid) === -1; - } + isExplicitlyEnabledForItem(item) { + return this.associatedItemIds.indexOf(item.uuid) !== -1; + } + + isExplicitlyDisabledForItem(item) { + return this.disassociatedItemIds.indexOf(item.uuid) !== -1; } } diff --git a/app/assets/javascripts/app/frontend/models/app/editor.js b/app/assets/javascripts/app/models/app/editor.js similarity index 100% rename from app/assets/javascripts/app/frontend/models/app/editor.js rename to app/assets/javascripts/app/models/app/editor.js diff --git a/app/assets/javascripts/app/models/app/extension.js b/app/assets/javascripts/app/models/app/extension.js new file mode 100644 index 000000000..f990432a5 --- /dev/null +++ b/app/assets/javascripts/app/models/app/extension.js @@ -0,0 +1,61 @@ +class Action { + constructor(json) { + _.merge(this, json); + this.running = false; // in case running=true was synced with server since model is uploaded nondiscriminatory + this.error = false; + if(this.lastExecuted) { + // is string + this.lastExecuted = new Date(this.lastExecuted); + } + } +} + +class Extension extends Component { + constructor(json) { + super(json); + + if(json.actions) { + this.actions = json.actions.map(function(action){ + return new Action(action); + }) + } + + if(!this.actions) { + this.actions = []; + } + } + + actionsWithContextForItem(item) { + return this.actions.filter(function(action){ + return action.context == item.content_type || action.context == "Item"; + }) + } + + mapContentToLocalProperties(content) { + super.mapContentToLocalProperties(content) + this.description = content.description; + + this.supported_types = content.supported_types; + if(content.actions) { + this.actions = content.actions.map(function(action){ + return new Action(action); + }) + } + } + + get content_type() { + return "Extension"; + } + + structureParams() { + var params = { + description: this.description, + actions: this.actions.map((a) => {return _.omit(a, ["subrows", "subactions"])}), + supported_types: this.supported_types + }; + + _.merge(params, super.structureParams()); + return params; + } + +} diff --git a/app/assets/javascripts/app/frontend/models/app/note.js b/app/assets/javascripts/app/models/app/note.js similarity index 100% rename from app/assets/javascripts/app/frontend/models/app/note.js rename to app/assets/javascripts/app/models/app/note.js diff --git a/app/assets/javascripts/app/frontend/models/app/tag.js b/app/assets/javascripts/app/models/app/tag.js similarity index 89% rename from app/assets/javascripts/app/frontend/models/app/tag.js rename to app/assets/javascripts/app/models/app/tag.js index f94ee4151..b83585234 100644 --- a/app/assets/javascripts/app/frontend/models/app/tag.js +++ b/app/assets/javascripts/app/models/app/tag.js @@ -87,13 +87,9 @@ class Tag extends Item { return this.notes; } - static arrayToDisplayString(tags, includeComma) { - return tags.map(function(tag, i){ - var text = "#" + tag.title; - if(i != tags.length - 1) { - text += includeComma ? ", " : " "; - } - return text; + static arrayToDisplayString(tags) { + return tags.sort((a, b) => {return a.title > b.title}).map(function(tag, i){ + return "#" + tag.title; }).join(" "); } } diff --git a/app/assets/javascripts/app/models/app/theme.js b/app/assets/javascripts/app/models/app/theme.js new file mode 100644 index 000000000..7d03cb2c3 --- /dev/null +++ b/app/assets/javascripts/app/models/app/theme.js @@ -0,0 +1,12 @@ +class Theme extends Component { + + constructor(json_obj) { + super(json_obj); + + this.area = "themes"; + } + + get content_type() { + return "SN|Theme"; + } +} diff --git a/app/assets/javascripts/app/frontend/models/local/encryptedStorage.js b/app/assets/javascripts/app/models/local/encryptedStorage.js similarity index 100% rename from app/assets/javascripts/app/frontend/models/local/encryptedStorage.js rename to app/assets/javascripts/app/models/local/encryptedStorage.js diff --git a/app/assets/javascripts/app/frontend/models/local/itemParams.js b/app/assets/javascripts/app/models/local/itemParams.js similarity index 91% rename from app/assets/javascripts/app/frontend/models/local/itemParams.js rename to app/assets/javascripts/app/models/local/itemParams.js index d5a488ae7..a1cb83e18 100644 --- a/app/assets/javascripts/app/frontend/models/local/itemParams.js +++ b/app/assets/javascripts/app/models/local/itemParams.js @@ -6,10 +6,14 @@ class ItemParams { this.version = version || "002"; } - paramsForExportFile() { + paramsForExportFile(includeDeleted) { this.additionalFields = ["updated_at"]; this.forExportFile = true; - return _.omit(this.__params(), ["deleted"]); + if(includeDeleted) { + return this.__params(); + } else { + return _.omit(this.__params(), ["deleted"]); + } } paramsForExtension() { diff --git a/app/assets/javascripts/app/frontend/routes.js b/app/assets/javascripts/app/routes.js similarity index 90% rename from app/assets/javascripts/app/frontend/routes.js rename to app/assets/javascripts/app/routes.js index 5409347db..fe6dac476 100644 --- a/app/assets/javascripts/app/frontend/routes.js +++ b/app/assets/javascripts/app/routes.js @@ -1,4 +1,4 @@ -angular.module('app.frontend') +angular.module('app') .config(function ($locationProvider) { if(!isDesktopApplication()) { @@ -11,5 +11,4 @@ angular.module('app.frontend') } else { $locationProvider.html5Mode(false); } - }); diff --git a/app/assets/javascripts/app/services/actionsManager.js b/app/assets/javascripts/app/services/actionsManager.js new file mode 100644 index 000000000..85a30253b --- /dev/null +++ b/app/assets/javascripts/app/services/actionsManager.js @@ -0,0 +1,159 @@ +class ActionsManager { + + constructor(httpManager, modelManager, authManager, syncManager) { + this.httpManager = httpManager; + this.modelManager = modelManager; + this.authManager = authManager; + this.syncManager = syncManager; + } + + get extensions() { + return this.modelManager.extensions; + } + + extensionsInContextOfItem(item) { + return this.extensions.filter(function(ext){ + return _.includes(ext.supported_types, item.content_type) || ext.actionsWithContextForItem(item).length > 0; + }) + } + + /* + Loads an extension in the context of a certain item. The server then has the chance to respond with actions that are + relevant just to this item. The response extension is not saved, just displayed as a one-time thing. + */ + loadExtensionInContextOfItem(extension, item, callback) { + this.httpManager.getAbsolute(extension.url, {content_type: item.content_type, item_uuid: item.uuid}, function(response){ + this.updateExtensionFromRemoteResponse(extension, response); + callback && callback(extension); + }.bind(this), function(response){ + console.log("Error loading extension", response); + if(callback) { + callback(null); + } + }.bind(this)) + } + + updateExtensionFromRemoteResponse(extension, response) { + if(response.description) { extension.description = response.description; } + if(response.supported_types) { extension.supported_types = response.supported_types; } + + if(response.actions) { + extension.actions = response.actions.map(function(action){ + return new Action(action); + }) + } else { + extension.actions = []; + } + } + + executeAction(action, extension, item, callback) { + + var customCallback = function(response) { + action.running = false; + callback(response); + } + + action.running = true; + + let decrypted = action.access_type == "decrypted"; + + switch (action.verb) { + case "get": { + + this.httpManager.getAbsolute(action.url, {}, function(response){ + action.error = false; + var items = response.items || [response.item]; + EncryptionHelper.decryptMultipleItems(items, this.authManager.keys()); + items = this.modelManager.mapResponseItemsToLocalModels(items, ModelManager.MappingSourceRemoteActionRetrieved); + for(var item of items) { + item.setDirty(true); + } + this.syncManager.sync(null); + customCallback({items: items}); + }.bind(this), function(response){ + action.error = true; + customCallback(null); + }) + + break; + } + + case "render": { + + this.httpManager.getAbsolute(action.url, {}, function(response){ + action.error = false; + EncryptionHelper.decryptItem(response.item, this.authManager.keys()); + var item = this.modelManager.createItem(response.item, true /* Dont notify observers */); + customCallback({item: item}); + + }.bind(this), function(response){ + action.error = true; + customCallback(null); + }) + + break; + } + + case "show": { + var win = window.open(action.url, '_blank'); + win.focus(); + customCallback(); + break; + } + + case "post": { + var params = {}; + + if(action.all) { + var items = this.modelManager.allItemsMatchingTypes(action.content_types); + params.items = items.map(function(item){ + var params = this.outgoingParamsForItem(item, extension, decrypted); + return params; + }.bind(this)) + + } else { + params.items = [this.outgoingParamsForItem(item, extension, decrypted)]; + } + + this.performPost(action, extension, params, function(response){ + customCallback(response); + }); + + break; + } + + default: { + + } + } + + action.lastExecuted = new Date(); + } + + outgoingParamsForItem(item, extension, decrypted = false) { + var keys = this.authManager.keys(); + if(decrypted) { + keys = null; + } + var itemParams = new ItemParams(item, keys, this.authManager.protocolVersion()); + return itemParams.paramsForExtension(); + } + + performPost(action, extension, params, callback) { + this.httpManager.postAbsolute(action.url, params, function(response){ + action.error = false; + if(callback) { + callback(response); + } + }.bind(this), function(response){ + action.error = true; + console.log("Action error response:", response); + if(callback) { + callback({error: "Request error"}); + } + }) + } + +} + +angular.module('app').service('actionsManager', ActionsManager); diff --git a/app/assets/javascripts/app/services/authManager.js b/app/assets/javascripts/app/services/authManager.js index 46011d36f..e90b92120 100644 --- a/app/assets/javascripts/app/services/authManager.js +++ b/app/assets/javascripts/app/services/authManager.js @@ -1,4 +1,4 @@ -angular.module('app.frontend') +angular.module('app') .provider('authManager', function () { function domainName() { @@ -7,11 +7,11 @@ angular.module('app.frontend') return domain; } - this.$get = function($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager) { - return new AuthManager($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager); + this.$get = function($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager) { + return new AuthManager($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager); } - function AuthManager($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager) { + function AuthManager($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager) { this.loadInitialData = function() { var userData = storageManager.getItem("user"); @@ -43,11 +43,10 @@ angular.module('app.frontend') this.ephemeral = ephemeral; if(ephemeral) { storageManager.setModelStorageMode(StorageManager.Ephemeral); - storageManager.setItemsMode(storageManager.hasPasscode() ? StorageManager.FixedEncrypted : StorageManager.Ephemeral); + storageManager.setItemsMode(StorageManager.Ephemeral); } else { storageManager.setModelStorageMode(StorageManager.Fixed); storageManager.setItemsMode(storageManager.hasPasscode() ? StorageManager.FixedEncrypted : StorageManager.Fixed); - storageManager.setItem("ephemeral", JSON.stringify(false), StorageManager.Fixed); } } @@ -95,9 +94,9 @@ angular.module('app.frontend') return supportedVersions.includes(version); } - this.getAuthParamsForEmail = function(url, email, callback) { + this.getAuthParamsForEmail = function(url, email, extraParams, callback) { var requestUrl = url + "/auth/params"; - httpManager.getAbsolute(requestUrl, {email: email}, function(response){ + httpManager.getAbsolute(requestUrl, _.merge({email: email}, extraParams), function(response){ callback(response); }, function(response){ console.error("Error getting auth params", response); @@ -120,8 +119,8 @@ angular.module('app.frontend') } } - this.login = function(url, email, password, ephemeral, callback) { - this.getAuthParamsForEmail(url, email, function(authParams){ + this.login = function(url, email, password, ephemeral, extraParams, callback) { + this.getAuthParamsForEmail(url, email, extraParams, function(authParams){ if(authParams.error) { callback(authParams); @@ -134,31 +133,30 @@ angular.module('app.frontend') } if(!this.isProtocolVersionSupported(authParams.version)) { - alert("The protocol version associated with your account is outdated and no longer supported by this application. Please visit standardnotes.org/help/security-update for more information."); - callback({didDisplayAlert: true}); + let message = "The protocol version associated with your account is outdated and no longer supported by this application. Please visit standardnotes.org/help/security-update for more information."; + callback({error: {message: message}}); return; } if(!this.supportsPasswordDerivationCost(authParams.pw_cost)) { - var string = "Your account was created on a platform with higher security capabilities than this browser supports. " + + let message = "Your account was created on a platform with higher security capabilities than this browser supports. " + "If we attempted to generate your login keys here, it would take hours. " + - "Please use a browser with more up to date security capabilities, like Google Chrome or Firefox, to login." - alert(string) - callback({didDisplayAlert: true}); + "Please use a browser with more up to date security capabilities, like Google Chrome or Firefox, to log in." + callback({error: {message: message}}); return; } var minimum = this.costMinimumForVersion(authParams.version); if(authParams.pw_cost < minimum) { - alert("Unable to login due to insecure password parameters. Please visit standardnotes.org/help/password-upgrade for more information."); - callback({didDisplayAlert: true}); + let message = "Unable to login due to insecure password parameters. Please visit standardnotes.org/help/password-upgrade for more information."; + callback({error: {message: message}}); return; } Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: password}, authParams), function(keys){ var requestUrl = url + "/auth/sign_in"; - var params = {password: keys.pw, email: email}; + var params = _.merge({password: keys.pw, email: email}, extraParams); httpManager.postAbsolute(requestUrl, params, function(response){ this.setEphemeral(ephemeral); this.handleAuthResponse(response, email, url, authParams, keys); @@ -291,5 +289,45 @@ angular.module('app.frontend') this._authParams = null; } - } + + /* User Preferences */ + + let prefsContentType = "SN|UserPreferences"; + + singletonManager.registerSingleton({content_type: prefsContentType}, (resolvedSingleton) => { + this.userPreferences = resolvedSingleton; + this.userPreferencesDidChange(); + }, (valueCallback) => { + // Safe to create. Create and return object. + var prefs = new Item({content_type: prefsContentType}); + modelManager.addItem(prefs); + prefs.setDirty(true); + $rootScope.sync("authManager singletonCreate"); + valueCallback(prefs); + }); + + this.userPreferencesDidChange = function() { + $rootScope.$broadcast("user-preferences-changed"); + } + + this.syncUserPreferences = function() { + this.userPreferences.setDirty(true); + $rootScope.sync("syncUserPreferences"); + } + + this.getUserPrefValue = function(key, defaultValue) { + if(!this.userPreferences) { return defaultValue; } + var value = this.userPreferences.getAppDataItem(key); + return (value !== undefined && value != null) ? value : defaultValue; + } + + this.setUserPrefValue = function(key, value, sync) { + if(!this.userPreferences) { console.log("Prefs are null, not setting value", key); return; } + this.userPreferences.setAppDataItem(key, value); + if(sync) { + this.syncUserPreferences(); + } + } + + } }); diff --git a/app/assets/javascripts/app/services/componentManager.js b/app/assets/javascripts/app/services/componentManager.js index c22cdbd85..52cde7942 100644 --- a/app/assets/javascripts/app/services/componentManager.js +++ b/app/assets/javascripts/app/services/componentManager.js @@ -3,27 +3,45 @@ let ClientDataDomain = "org.standardnotes.sn.components"; class ComponentManager { - constructor($rootScope, modelManager, syncManager, themeManager, $timeout, $compile) { + constructor($rootScope, modelManager, syncManager, desktopManager, nativeExtManager, $timeout, $compile) { this.$compile = $compile; this.$rootScope = $rootScope; this.modelManager = modelManager; this.syncManager = syncManager; - this.themeManager = themeManager; + this.desktopManager = desktopManager; + this.nativeExtManager = nativeExtManager; this.timeout = $timeout; this.streamObservers = []; this.contextStreamObservers = []; this.activeComponents = []; + const detectFocusChange = (event) => { + for(var component of this.activeComponents) { + if(document.activeElement == this.iframeForComponent(component)) { + this.timeout(() => { + this.focusChangedForComponent(component); + }) + break; + } + } + } + + window.addEventListener ? window.addEventListener('focus', detectFocusChange, true) : window.attachEvent('onfocusout', detectFocusChange); + window.addEventListener ? window.addEventListener('blur', detectFocusChange, true) : window.attachEvent('onblur', detectFocusChange); + + desktopManager.registerUpdateObserver((component) => { + // Reload theme if active + if(component.active && component.isTheme()) { + this.postActiveThemeToAllComponents(); + } + }) + // this.loggingEnabled = true; this.permissionDialogs = []; this.handlers = []; - $rootScope.$on("theme-changed", function(){ - this.postThemeToComponents(); - }.bind(this)) - window.addEventListener("message", function(event){ if(this.loggingEnabled) { console.log("Web app: received message", event); @@ -31,7 +49,7 @@ class ComponentManager { this.handleMessage(this.componentForSessionKey(event.data.sessionKey), event.data); }.bind(this), false); - this.modelManager.addItemSyncObserver("component-manager", "*", function(allItems, validItems, deletedItems, source) { + this.modelManager.addItemSyncObserver("component-manager", "*", (allItems, validItems, deletedItems, source) => { /* If the source of these new or updated items is from a Component itself saving items, we don't need to notify components again of the same item. Regarding notifying other components than the issuing component, other mapping sources @@ -41,7 +59,18 @@ class ComponentManager { return; } - var syncedComponents = allItems.filter(function(item){return item.content_type === "SN|Component" }); + var syncedComponents = allItems.filter(function(item) { + return item.content_type === "SN|Component" || item.content_type == "SN|Theme" + }); + + /* We only want to sync if the item source is Retrieved, not MappingSourceRemoteSaved to avoid + recursion caused by the component being modified and saved after it is updated. + */ + if(syncedComponents.length > 0 && source != ModelManager.MappingSourceRemoteSaved) { + // Ensure any component in our data is installed by the system + this.desktopManager.syncComponentsInstallation(syncedComponents); + } + for(var component of syncedComponents) { var activeComponent = _.find(this.activeComponents, {uuid: component.uuid}); if(component.active && !component.deleted && !activeComponent) { @@ -56,6 +85,10 @@ class ComponentManager { return observer.contentTypes.indexOf(item.content_type) !== -1; }) + if(relevantItems.length == 0) { + continue; + } + var requiredPermissions = [ { name: "stream-items", @@ -63,9 +96,9 @@ class ComponentManager { } ]; - this.runWithPermissions(observer.component, requiredPermissions, observer.originalMessage.permissions, function(){ + this.runWithPermissions(observer.component, requiredPermissions, () => { this.sendItemsInReply(observer.component, relevantItems, observer.originalMessage); - }.bind(this)) + }) } var requiredContextPermissions = [ @@ -75,36 +108,45 @@ class ComponentManager { ]; for(let observer of this.contextStreamObservers) { - this.runWithPermissions(observer.component, requiredContextPermissions, observer.originalMessage.permissions, function(){ - for(let handler of this.handlers) { - if(!handler.areas.includes(observer.component.area)) { - continue; - } + for(let handler of this.handlers) { + if(!handler.areas.includes(observer.component.area) && !handler.areas.includes("*")) { + continue; + } + if(handler.contextRequestHandler) { var itemInContext = handler.contextRequestHandler(observer.component); if(itemInContext) { var matchingItem = _.find(allItems, {uuid: itemInContext.uuid}); if(matchingItem) { - this.sendContextItemInReply(observer.component, matchingItem, observer.originalMessage, source); + this.runWithPermissions(observer.component, requiredContextPermissions, () => { + this.sendContextItemInReply(observer.component, matchingItem, observer.originalMessage, source); + }) } } } - }.bind(this)) + } } - }.bind(this)) + }); } - postThemeToComponents() { + postActiveThemeToAllComponents() { for(var component of this.components) { - if(!component.active || !component.window) { + // Skip over components that are themes themselves, + // or components that are not active, or components that don't have a window + if(component.isTheme() || !component.active || !component.window) { continue; } - this.postThemeToComponent(component); + this.postActiveThemeToComponent(component); } } - postThemeToComponent(component) { + getActiveTheme() { + return this.componentsForArea("themes").find((theme) => {return theme.active}); + } + + postActiveThemeToComponent(component) { + var activeTheme = this.getActiveTheme(); var data = { - themes: [this.themeManager.currentTheme ? this.themeManager.currentTheme.url : null] + themes: [activeTheme ? this.urlForComponent(activeTheme) : null] } this.sendMessageToComponent(component, {action: "themes", data: data}) @@ -112,7 +154,7 @@ class ComponentManager { contextItemDidChangeInArea(area) { for(let handler of this.handlers) { - if(handler.areas.includes(area) === false) { + if(handler.areas.includes(area) === false && !handler.areas.includes("*")) { continue; } var observers = this.contextStreamObservers.filter(function(observer){ @@ -120,8 +162,10 @@ class ComponentManager { }) for(let observer of observers) { - var itemInContext = handler.contextRequestHandler(observer.component); - this.sendContextItemInReply(observer.component, itemInContext, observer.originalMessage); + if(handler.contextRequestHandler) { + var itemInContext = handler.contextRequestHandler(observer.component); + this.sendContextItemInReply(observer.component, itemInContext, observer.originalMessage); + } } } } @@ -129,7 +173,8 @@ class ComponentManager { jsonForItem(item, component, source) { var params = {uuid: item.uuid, content_type: item.content_type, created_at: item.created_at, updated_at: item.updated_at, deleted: item.deleted}; params.content = item.createContentJSONFromProperties(); - params.clientData = item.getDomainDataItem(component.url, ClientDataDomain) || {}; + /* Legacy is using component.url key, so if it's present, use it, otherwise use uuid */ + params.clientData = item.getDomainDataItem(component.url || component.uuid, ClientDataDomain) || {}; /* This means the this function is being triggered through a remote Saving response, which should not update actual local content values. The reason is, Save responses may be delayed, and a user may have changed some values @@ -139,7 +184,7 @@ class ComponentManager { if(source && source == ModelManager.MappingSourceRemoteSaved) { params.isMetadataUpdate = true; } - this.removePrivatePropertiesFromResponseItems([params]); + this.removePrivatePropertiesFromResponseItems([params], component); return params; } @@ -160,8 +205,33 @@ class ComponentManager { this.replyToMessage(component, originalMessage, response); } + replyToMessage(component, originalMessage, replyData) { + var reply = { + action: "reply", + original: originalMessage, + data: replyData + } + + this.sendMessageToComponent(component, reply); + } + + sendMessageToComponent(component, message) { + let permissibleActionsWhileHidden = ["component-registered", "themes"]; + if(component.hidden && !permissibleActionsWhileHidden.includes(message.action)) { + if(this.loggingEnabled) { + console.log("Component disabled for current item, not sending any messages.", component.name); + } + return; + } + + if(this.loggingEnabled) { + console.log("Web|sendMessageToComponent", component, message); + } + component.window.postMessage(message, "*"); + } + get components() { - return this.modelManager.itemsForContentType("SN|Component"); + return this.modelManager.allItemsMatchingTypes(["SN|Component", "SN|Theme"]); } componentsForArea(area) { @@ -170,9 +240,17 @@ class ComponentManager { }) } + urlForComponent(component) { + if(component.offlineOnly || (isDesktopApplication() && component.local_url)) { + return component.local_url && component.local_url.replace("sn://", this.desktopManager.getApplicationDataPath() + "/"); + } else { + return component.hosted_url || component.url; + } + } + componentForUrl(url) { return this.components.filter(function(component){ - return component.url === url; + return component.url === url || component.hosted_url === url; })[0]; } @@ -191,91 +269,46 @@ class ComponentManager { /** Possible Messages: - set-size - stream-items - stream-context-item - save-items - select-item - associate-item - deassociate-item - clear-selection - create-item - delete-items - set-component-data - save-context-client-data - get-context-client-data + set-size + stream-items + stream-context-item + save-items + select-item + associate-item + deassociate-item + clear-selection + create-item + delete-items + set-component-data + install-local-component + toggle-activate-component + request-permissions */ if(message.action === "stream-items") { this.handleStreamItemsMessage(component, message); - } - - else if(message.action === "stream-context-item") { + } else if(message.action === "stream-context-item") { this.handleStreamContextItemMessage(component, message); + } else if(message.action === "set-component-data") { + this.handleSetComponentDataMessage(component, message); + } else if(message.action === "delete-items") { + this.handleDeleteItemsMessage(component, message); + } else if(message.action === "create-item") { + this.handleCreateItemMessage(component, message); + } else if(message.action === "save-items") { + this.handleSaveItemsMessage(component, message); + } else if(message.action === "toggle-activate-component") { + let componentToToggle = this.modelManager.findItem(message.data.uuid); + this.handleToggleComponentMessage(component, componentToToggle, message); + } else if(message.action === "request-permissions") { + this.handleRequestPermissionsMessage(component, message); + } else if(message.action === "install-local-component") { + this.handleInstallLocalComponentMessage(component, message); } - else if(message.action === "set-component-data") { - component.componentData = message.data.componentData; - component.setDirty(true); - this.syncManager.sync(); - } - - else if(message.action === "delete-items") { - var items = message.data.items; - var noun = items.length == 1 ? "item" : "items"; - if(confirm(`Are you sure you want to delete ${items.length} ${noun}?`)) { - for(var item of items) { - var model = this.modelManager.findItem(item.uuid); - this.modelManager.setItemToBeDeleted(model); - } - - this.syncManager.sync(); - } - } - - else if(message.action === "create-item") { - var responseItem = message.data.item; - this.removePrivatePropertiesFromResponseItems([responseItem]); - var item = this.modelManager.createItem(responseItem); - if(responseItem.clientData) { - item.setDomainDataItem(component.url, responseItem.clientData, ClientDataDomain); - } - this.modelManager.addItem(item); - this.modelManager.resolveReferencesForItem(item); - item.setDirty(true); - this.syncManager.sync(); - this.replyToMessage(component, message, {item: this.jsonForItem(item, component)}) - } - - else if(message.action === "save-items") { - var responseItems = message.data.items; - - this.removePrivatePropertiesFromResponseItems(responseItems); - - /* - We map the items here because modelManager is what updates the UI. If you were to instead get the items directly, - this would update them server side via sync, but would never make its way back to the UI. - */ - var localItems = this.modelManager.mapResponseItemsToLocalModels(responseItems, ModelManager.MappingSourceComponentRetrieved); - - for(var item of localItems) { - var responseItem = _.find(responseItems, {uuid: item.uuid}); - _.merge(item.content, responseItem.content); - if(responseItem.clientData) { - item.setDomainDataItem(component.url, responseItem.clientData, ClientDataDomain); - } - item.setDirty(true); - } - this.syncManager.sync((response) => { - // Allow handlers to be notified when a save begins and ends, to update the UI - var saveMessage = Object.assign({}, message); - saveMessage.action = response && response.error ? "save-error" : "save-success"; - this.handleMessage(component, saveMessage); - }); - } - + // Notify observers for(let handler of this.handlers) { - if(handler.areas.includes(component.area)) { + if(handler.areas.includes(component.area) || handler.areas.includes("*")) { this.timeout(function(){ handler.actionHandler(component, message.action, message.data); }) @@ -283,9 +316,18 @@ class ComponentManager { } } - removePrivatePropertiesFromResponseItems(responseItems) { + removePrivatePropertiesFromResponseItems(responseItems, component, options = {}) { + if(component) { + // System extensions can bypass this step + if(this.nativeExtManager.isSystemExtension(component)) { + return; + } + } // Don't allow component to overwrite these properties. - let privateProperties = ["appData"]; + var privateProperties = ["appData", "autoupdateDisabled", "permissions", "active"]; + if(options) { + if(options.includeUrls) { privateProperties = privateProperties.concat(["url", "hosted_url", "local_url"])} + } for(var responseItem of responseItems) { // Do not pass in actual items here, otherwise that would be destructive. @@ -293,7 +335,7 @@ class ComponentManager { console.assert(typeof responseItem.setDirty !== 'function'); for(var prop of privateProperties) { - delete responseItem[prop]; + delete responseItem.content[prop]; } } } @@ -306,25 +348,24 @@ class ComponentManager { } ]; - this.runWithPermissions(component, requiredPermissions, message.permissions, function(){ - if(!_.find(this.streamObservers, {identifier: component.url})) { + this.runWithPermissions(component, requiredPermissions, () => { + if(!_.find(this.streamObservers, {identifier: component.uuid})) { // for pushing laster as changes come in this.streamObservers.push({ - identifier: component.url, + identifier: component.uuid, component: component, originalMessage: message, contentTypes: message.data.content_types }) } - // push immediately now var items = []; for(var contentType of message.data.content_types) { items = items.concat(this.modelManager.itemsForContentType(contentType)); } this.sendItemsInReply(component, items, message); - }.bind(this)); + }); } handleStreamContextItemMessage(component, message) { @@ -335,55 +376,223 @@ class ComponentManager { } ]; - this.runWithPermissions(component, requiredPermissions, message.permissions, function(){ - if(!_.find(this.contextStreamObservers, {identifier: component.url})) { + this.runWithPermissions(component, requiredPermissions, function(){ + if(!_.find(this.contextStreamObservers, {identifier: component.uuid})) { // for pushing laster as changes come in this.contextStreamObservers.push({ - identifier: component.url, + identifier: component.uuid, component: component, originalMessage: message }) } // push immediately now - for(let handler of this.handlers) { - if(handler.areas.includes(component.area) === false) { - continue; + for(let handler of this.handlersForArea(component.area)) { + if(handler.contextRequestHandler) { + var itemInContext = handler.contextRequestHandler(component); + this.sendContextItemInReply(component, itemInContext, message); } - var itemInContext = handler.contextRequestHandler(component); - this.sendContextItemInReply(component, itemInContext, message); } }.bind(this)) } - runWithPermissions(component, requiredPermissions, requestedPermissions, runFunction) { - - var acquiredPermissions = component.permissions; - - var requestedMatchesRequired = true; - - for(var required of requiredPermissions) { - var matching = _.find(requestedPermissions, required); - if(!matching) { - requestedMatchesRequired = false; - break; + isItemWithinComponentContextJurisdiction(item, component) { + for(let handler of this.handlersForArea(component.area)) { + if(handler.contextRequestHandler) { + var itemInContext = handler.contextRequestHandler(component); + if(itemInContext && itemInContext.uuid == item.uuid) { + return true; + } } } + return false; + } - if(!requestedMatchesRequired) { - // Error with Component permissions request - console.error("You are requesting permissions", requestedPermissions, "when you need to be requesting", requiredPermissions, ". Component:", component); + handlersForArea(area) { + return this.handlers.filter((candidate) => {return candidate.areas.includes(area)}); + } + + handleSaveItemsMessage(component, message) { + var responseItems = message.data.items; + var requiredPermissions; + + // Check if you're just trying to save the context item, which requires only stream-context-item permissions + if(responseItems.length == 1 && this.isItemWithinComponentContextJurisdiction(responseItems[0], component)) { + requiredPermissions = [ + { + name: "stream-context-item" + } + ]; + } else { + var requiredContentTypes = _.uniq(responseItems.map((i) => {return i.content_type})).sort(); + requiredPermissions = [ + { + name: "stream-items", + content_types: requiredContentTypes + } + ]; + } + + this.runWithPermissions(component, requiredPermissions, () => { + + this.removePrivatePropertiesFromResponseItems(responseItems, component, {includeUrls: true}); + + /* + We map the items here because modelManager is what updates the UI. If you were to instead get the items directly, + this would update them server side via sync, but would never make its way back to the UI. + */ + var localItems = this.modelManager.mapResponseItemsToLocalModels(responseItems, ModelManager.MappingSourceComponentRetrieved); + + for(var item of localItems) { + var responseItem = _.find(responseItems, {uuid: item.uuid}); + _.merge(item.content, responseItem.content); + if(responseItem.clientData) { + item.setDomainDataItem(component.url || component.uuid, responseItem.clientData, ClientDataDomain); + } + item.setDirty(true); + } + + this.syncManager.sync((response) => { + // Allow handlers to be notified when a save begins and ends, to update the UI + var saveMessage = Object.assign({}, message); + saveMessage.action = response && response.error ? "save-error" : "save-success"; + this.replyToMessage(component, message, {error: response.error}) + this.handleMessage(component, saveMessage); + }, null, "handleSaveItemsMessage"); + }); + } + + handleCreateItemMessage(component, message) { + var requiredPermissions = [ + { + name: "stream-items", + content_types: [message.data.item.content_type] + } + ]; + + this.runWithPermissions(component, requiredPermissions, () => { + var responseItem = message.data.item; + this.removePrivatePropertiesFromResponseItems([responseItem], component); + var item = this.modelManager.createItem(responseItem); + if(responseItem.clientData) { + item.setDomainDataItem(component.url || component.uuid, responseItem.clientData, ClientDataDomain); + } + this.modelManager.addItem(item); + this.modelManager.resolveReferencesForItem(item); + item.setDirty(true); + this.syncManager.sync("handleCreateItemMessage"); + this.replyToMessage(component, message, {item: this.jsonForItem(item, component)}) + }); + } + + handleDeleteItemsMessage(component, message) { + var requiredContentTypes = _.uniq(message.data.items.map((i) => {return i.content_type})).sort(); + var requiredPermissions = [ + { + name: "stream-items", + content_types: requiredContentTypes + } + ]; + + this.runWithPermissions(component, requiredPermissions, () => { + var itemsData = message.data.items; + var noun = itemsData.length == 1 ? "item" : "items"; + if(confirm(`Are you sure you want to delete ${itemsData.length} ${noun}?`)) { + // Filter for any components and deactivate before deleting + for(var itemData of itemsData) { + var model = this.modelManager.findItem(itemData.uuid); + if(["SN|Component", "SN|Theme"].includes(model.content_type)) { + this.deactivateComponent(model, true); + } + this.modelManager.setItemToBeDeleted(model); + } + + this.syncManager.sync("handleDeleteItemsMessage"); + } + }); + } + + handleRequestPermissionsMessage(component, message) { + this.runWithPermissions(component, message.data.permissions, () => { + this.replyToMessage(component, message, {approved: true}); + }); + } + + handleSetComponentDataMessage(component, message) { + // A component setting its own data does not require special permissions + this.runWithPermissions(component, [], () => { + component.componentData = message.data.componentData; + component.setDirty(true); + this.syncManager.sync("handleSetComponentDataMessage"); + }); + } + + handleToggleComponentMessage(sourceComponent, targetComponent, message) { + if(targetComponent.area == "modal") { + this.openModalComponent(targetComponent); + } else { + if(targetComponent.active) { + this.deactivateComponent(targetComponent); + } else { + if(targetComponent.content_type == "SN|Theme") { + // Deactive currently active theme + var activeTheme = this.getActiveTheme(); + if(activeTheme) { + this.deactivateComponent(activeTheme); + } + } + this.activateComponent(targetComponent); + } + } + } + + handleInstallLocalComponentMessage(sourceComponent, message) { + // Only extensions manager has this permission + if(!this.nativeExtManager.isSystemExtension(sourceComponent)) { return; } + let targetComponent = this.modelManager.findItem(message.data.uuid); + this.desktopManager.installComponent(targetComponent); + } + + runWithPermissions(component, requiredPermissions, runFunction) { + if(!component.permissions) { component.permissions = []; } - var acquiredMatchesRequested = angular.toJson(component.permissions.sort()) === angular.toJson(requestedPermissions.sort()); + var acquiredPermissions = component.permissions; + var acquiredMatchesRequired = true; - if(!acquiredMatchesRequested) { - this.promptForPermissions(component, requestedPermissions, function(approved){ + for(var required of requiredPermissions) { + var matching = acquiredPermissions.find((candidate) => { + var matchesContentTypes = true; + if(candidate.content_types && required.content_types) { + matchesContentTypes = JSON.stringify(candidate.content_types.sort()) == JSON.stringify(required.content_types.sort()); + } + return candidate.name == required.name && matchesContentTypes; + }); + + if(!matching) { + /* Required permissions can be 1 content type, and requestedPermisisons may send an array of content types. + In the case of an array, we can just check to make sure that requiredPermissions content type is found in the array + */ + matching = acquiredPermissions.find((candidate) => { + return Array.isArray(candidate.content_types) + && Array.isArray(required.content_types) + && candidate.content_types.containsPrimitiveSubset(required.content_types); + }); + + if(!matching) { + acquiredMatchesRequired = false; + break; + } + } + } + + if(!acquiredMatchesRequired) { + this.promptForPermissions(component, requiredPermissions, function(approved){ if(approved) { runFunction(); } @@ -393,107 +602,86 @@ class ComponentManager { } } - promptForPermissions(component, requestedPermissions, callback) { - // since these calls are asyncronous, multiple dialogs may be requested at the same time. We only want to present one and trigger all callbacks based on one modal result - var existingDialog = _.find(this.permissionDialogs, {component: component}); - - component.trusted = component.url.startsWith("https://standardnotes.org") || component.url.startsWith("https://extensions.standardnotes.org"); + promptForPermissions(component, permissions, callback) { var scope = this.$rootScope.$new(true); scope.component = component; - scope.permissions = requestedPermissions; + scope.permissions = permissions; scope.actionBlock = callback; scope.callback = function(approved) { if(approved) { - component.permissions = requestedPermissions; - component.setDirty(true); - this.syncManager.sync(); - } - - for(var existing of this.permissionDialogs) { - if(existing.component === component && existing.actionBlock) { - existing.actionBlock(approved); + for(var permission of permissions) { + if(!component.permissions.includes(permission)) { + component.permissions.push(permission); + } } + component.setDirty(true); + this.syncManager.sync("promptForPermissions"); } - this.permissionDialogs = this.permissionDialogs.filter(function(dialog){ - return dialog.component !== component; + this.permissionDialogs = this.permissionDialogs.filter((pendingDialog) => { + // Remove self + if(pendingDialog == scope) { + pendingDialog.actionBlock && pendingDialog.actionBlock(approved); + return false; + } + + if(pendingDialog.component == component) { + // remove pending dialogs that are encapsulated by already approved permissions, and run its function + if(pendingDialog.permissions == permissions || permissions.containsObjectSubset(pendingDialog.permissions)) { + // If approved, run the action block. Otherwise, if canceled, cancel any pending ones as well, since the user was + // explicit in their intentions + if(approved) { + pendingDialog.actionBlock && pendingDialog.actionBlock(approved); + } + return false; + } + } + return true; }) + if(this.permissionDialogs.length > 0) { + this.presentDialog(this.permissionDialogs[0]); + } + }.bind(this); + // since these calls are asyncronous, multiple dialogs may be requested at the same time. We only want to present one and trigger all callbacks based on one modal result + var existingDialog = _.find(this.permissionDialogs, {component: component}); + this.permissionDialogs.push(scope); if(!existingDialog) { - var el = this.$compile( "" )(scope); - angular.element(document.body).append(el); + this.presentDialog(scope); } else { console.log("Existing dialog, not presenting."); } } - replyToMessage(component, originalMessage, replyData) { - var reply = { - action: "reply", - original: originalMessage, - data: replyData - } - - this.sendMessageToComponent(component, reply); + presentDialog(dialog) { + var permissions = dialog.permissions; + var component = dialog.component; + var callback = dialog.callback; + var el = this.$compile( "" )(dialog); + angular.element(document.body).append(el); } - sendMessageToComponent(component, message) { - if(component.ignoreEvents && message.action !== "component-registered") { - if(this.loggingEnabled) { - console.log("Component disabled for current item, not sending any messages.", component.name); - } - return; - } - if(this.loggingEnabled) { - console.log("Web|sendMessageToComponent", component, message); - } - component.window.postMessage(message, "*"); - } - - installComponent(url) { - var name = getParameterByName("name", url); - var area = getParameterByName("area", url); - var component = this.modelManager.createItem({ - content_type: "SN|Component", - url: url, - name: name, - area: area - }) - - this.modelManager.addItem(component); - component.setDirty(true); - this.syncManager.sync(); - } - - activateComponent(component) { - var didChange = component.active != true; - - component.active = true; - for(var handler of this.handlers) { - if(handler.areas.includes(component.area)) { - handler.activationHandler(component); - } - } - - if(didChange) { - component.setDirty(true); - this.syncManager.sync(); - } - - if(!this.activeComponents.includes(component)) { - this.activeComponents.push(component); - } + openModalComponent(component) { + var scope = this.$rootScope.$new(true); + scope.component = component; + var el = this.$compile( "" )(scope); + angular.element(document.body).append(el); } registerHandler(handler) { this.handlers.push(handler); } + deregisterHandler(identifier) { + var handler = _.find(this.handlers, {identifier: identifier}); + this.handlers.splice(this.handlers.indexOf(handler), 1); + } + // Called by other views when the iframe is ready registerComponentWindow(component, componentWindow) { if(component.window === componentWindow) { @@ -507,24 +695,56 @@ class ComponentManager { } component.window = componentWindow; component.sessionKey = Neeto.crypto.generateUUID(); - this.sendMessageToComponent(component, {action: "component-registered", sessionKey: component.sessionKey, componentData: component.componentData}); - this.postThemeToComponent(component); + this.sendMessageToComponent(component, { + action: "component-registered", + sessionKey: component.sessionKey, + componentData: component.componentData, + data: { + uuid: component.uuid, + environment: isDesktopApplication() ? "desktop" : "web" + } + }); + this.postActiveThemeToComponent(component); } - deactivateComponent(component) { + activateComponent(component, dontSync = false) { + var didChange = component.active != true; + + component.active = true; + for(var handler of this.handlers) { + if(handler.areas.includes(component.area) || handler.areas.includes("*")) { + handler.activationHandler(component); + } + } + + if(didChange && !dontSync) { + component.setDirty(true); + this.syncManager.sync("activateComponent"); + } + + if(!this.activeComponents.includes(component)) { + this.activeComponents.push(component); + } + + if(component.area == "themes") { + this.postActiveThemeToAllComponents(); + } + } + + deactivateComponent(component, dontSync = false) { var didChange = component.active != false; component.active = false; component.sessionKey = null; for(var handler of this.handlers) { - if(handler.areas.includes(component.area)) { + if(handler.areas.includes(component.area) || handler.areas.includes("*")) { handler.activationHandler(component); } } - if(didChange) { + if(didChange && !dontSync) { component.setDirty(true); - this.syncManager.sync(); + this.syncManager.sync("deactivateComponent"); } _.pull(this.activeComponents, component); @@ -536,57 +756,23 @@ class ComponentManager { this.contextStreamObservers = this.contextStreamObservers.filter(function(o){ return o.component !== component; }) + + if(component.area == "themes") { + this.postActiveThemeToAllComponents(); + } } deleteComponent(component) { this.modelManager.setItemToBeDeleted(component); - this.syncManager.sync(); + this.syncManager.sync("deleteComponent"); } isComponentActive(component) { return component.active; } - disassociateComponentWithItem(component, item) { - _.pull(component.associatedItemIds, item.uuid); - - if(component.disassociatedItemIds.indexOf(item.uuid) !== -1) { - return; - } - - component.disassociatedItemIds.push(item.uuid); - - component.setDirty(true); - this.syncManager.sync(); - } - - associateComponentWithItem(component, item) { - _.pull(component.disassociatedItemIds, item.uuid); - - if(component.associatedItemIds.includes(item.uuid)) { - return; - } - - component.associatedItemIds.push(item.uuid); - - component.setDirty(true); - this.syncManager.sync(); - } - - enableComponentsForItem(components, item) { - for(var component of components) { - _.pull(component.disassociatedItemIds, item.uuid); - component.setDirty(true); - } - this.syncManager.sync(); - } - - setEventFlowForComponent(component, on) { - component.ignoreEvents = !on; - } - iframeForComponent(component) { - for(var frame of document.getElementsByTagName("iframe")) { + for(var frame of Array.from(document.getElementsByTagName("iframe"))) { var componentId = frame.dataset.componentId; if(componentId === component.uuid) { return frame; @@ -594,7 +780,40 @@ class ComponentManager { } } + focusChangedForComponent(component) { + let focused = document.activeElement == this.iframeForComponent(component); + for(var handler of this.handlers) { + // Notify all handlers, and not just ones that match this component type + handler.focusHandler && handler.focusHandler(component, focused); + } + } + + handleSetSizeEvent(component, data) { + var setSize = function(element, size) { + var widthString = typeof size.width === 'string' ? size.width : `${data.width}px`; + var heightString = typeof size.height === 'string' ? size.height : `${data.height}px`; + element.setAttribute("style", `width:${widthString}; height:${heightString}; `); + } + + if(data.type === "content") { + var iframe = this.iframeForComponent(component); + var width = data.width; + var height = data.height; + iframe.width = width; + iframe.height = height; + + setSize(iframe, data); + } else { + var container = document.getElementById("component-" + component.uuid); + if(container) { + // in the case of Modals, sometimes they may be "active" because they were so in another session, + // but no longer actually visible. So check to make sure the container exists + setSize(container, data); + } + } + } + } -angular.module('app.frontend').service('componentManager', ComponentManager); +angular.module('app').service('componentManager', ComponentManager); diff --git a/app/assets/javascripts/app/services/dbManager.js b/app/assets/javascripts/app/services/dbManager.js index 4c5d0341e..521fe2a1e 100644 --- a/app/assets/javascripts/app/services/dbManager.js +++ b/app/assets/javascripts/app/services/dbManager.js @@ -158,4 +158,4 @@ class DBManager { } } -angular.module('app.frontend').service('dbManager', DBManager); +angular.module('app').service('dbManager', DBManager); diff --git a/app/assets/javascripts/app/services/desktopManager.js b/app/assets/javascripts/app/services/desktopManager.js index 1cb28506c..3044e6da2 100644 --- a/app/assets/javascripts/app/services/desktopManager.js +++ b/app/assets/javascripts/app/services/desktopManager.js @@ -2,11 +2,16 @@ class DesktopManager { - constructor($rootScope, modelManager, authManager, passcodeManager) { + constructor($rootScope, $timeout, modelManager, syncManager, authManager, passcodeManager) { this.passcodeManager = passcodeManager; this.modelManager = modelManager; this.authManager = authManager; + this.syncManager = syncManager; this.$rootScope = $rootScope; + this.timeout = $timeout; + this.updateObservers = []; + + this.isDesktop = isDesktopApplication(); $rootScope.$on("initial-data-loaded", () => { this.dataLoaded = true; @@ -22,6 +27,84 @@ class DesktopManager { }) } + getApplicationDataPath() { + console.assert(this.applicationDataPath, "applicationDataPath is null"); + return this.applicationDataPath; + } + + /* Sending a component in its raw state is really slow for the desktop app */ + convertComponentForTransmission(component) { + return new ItemParams(component).paramsForExportFile(true); + } + + // All `components` should be installed + syncComponentsInstallation(components) { + if(!this.isDesktop) return; + + var data = components.map((component) => { + return this.convertComponentForTransmission(component); + }) + this.installationSyncHandler(data); + } + + installComponent(component) { + this.installComponentHandler(this.convertComponentForTransmission(component)); + } + + registerUpdateObserver(callback) { + var observer = {id: Math.random, callback: callback}; + this.updateObservers.push(observer); + return observer; + } + + deregisterUpdateObserver(observer) { + _.pull(this.updateObservers, observer); + } + + desktop_onComponentInstallationComplete(componentData, error) { + console.log("Web|Component Installation/Update Complete", componentData, error); + + // Desktop is only allowed to change these keys: + let permissableKeys = ["package_info", "local_url"]; + var component = this.modelManager.findItem(componentData.uuid); + + if(!component) { + console.error("desktop_onComponentInstallationComplete component is null for uuid", componentData.uuid); + return; + } + + if(error) { + component.setAppDataItem("installError", error); + } else { + for(var key of permissableKeys) { + component[key] = componentData.content[key]; + } + this.modelManager.notifySyncObserversOfModels([component], ModelManager.MappingSourceDesktopInstalled); + component.setAppDataItem("installError", null); + } + component.setDirty(true); + this.syncManager.sync("onComponentInstallationComplete"); + + this.timeout(() => { + for(var observer of this.updateObservers) { + observer.callback(component); + } + }) + } + + /* Used to resolve "sn://" */ + desktop_setApplicationDataPath(path) { + this.applicationDataPath = path; + } + + desktop_setComponentInstallationSyncHandler(handler) { + this.installationSyncHandler = handler; + } + + desktop_setInstallComponentHandler(handler) { + this.installComponentHandler = handler; + } + desktop_setInitialDataLoadHandler(handler) { this.dataLoadHandler = handler; if(this.dataLoaded) { @@ -56,4 +139,4 @@ class DesktopManager { } -angular.module('app.frontend').service('desktopManager', DesktopManager); +angular.module('app').service('desktopManager', DesktopManager); diff --git a/app/assets/javascripts/app/services/directives/views/contextualExtensionsMenu.js b/app/assets/javascripts/app/services/directives/views/contextualExtensionsMenu.js deleted file mode 100644 index 3825aed6e..000000000 --- a/app/assets/javascripts/app/services/directives/views/contextualExtensionsMenu.js +++ /dev/null @@ -1,86 +0,0 @@ -class ContextualExtensionsMenu { - - constructor() { - this.restrict = "E"; - this.templateUrl = "frontend/directives/contextual-menu.html"; - this.scope = { - item: "=" - }; - } - - controller($scope, modelManager, extensionManager) { - 'ngInject'; - - $scope.renderData = {}; - - $scope.extensions = _.map(extensionManager.extensionsInContextOfItem($scope.item), function(ext){ - // why are we cloning deep? commenting out because we want original reference so that extension.hide is saved between menu opens - // return _.cloneDeep(ext); - return ext; - }); - - for(let ext of $scope.extensions) { - ext.loading = true; - extensionManager.loadExtensionInContextOfItem(ext, $scope.item, function(scopedExtension) { - ext.loading = false; - }) - } - - $scope.executeAction = function(action, extension, parentAction) { - if(!$scope.isActionEnabled(action, extension)) { - alert("This action requires " + action.access_type + " access to this note. You can change this setting in the Extensions menu on the bottom of the app."); - return; - } - if(action.verb == "nested") { - action.showNestedActions = !action.showNestedActions; - return; - } - action.running = true; - extensionManager.executeAction(action, extension, $scope.item, function(response){ - action.running = false; - $scope.handleActionResponse(action, response); - - // reload extension actions - extensionManager.loadExtensionInContextOfItem(extension, $scope.item, function(ext){ - // keep nested state - if(parentAction) { - var matchingAction = _.find(ext.actions, {label: parentAction.label}); - matchingAction.showNestedActions = true; - } - }); - }) - } - - $scope.handleActionResponse = function(action, response) { - switch (action.verb) { - case "render": { - var item = response.item; - if(item.content_type == "Note") { - $scope.renderData.title = item.title; - $scope.renderData.text = item.text; - $scope.renderData.showRenderModal = true; - } - } - } - } - - $scope.isActionEnabled = function(action, extension) { - if(action.access_type) { - var extEncryptedAccess = extension.encrypted; - if(action.access_type == "decrypted" && extEncryptedAccess) { - return false; - } else if(action.access_type == "encrypted" && !extEncryptedAccess) { - return false; - } - } - return true; - } - - $scope.accessTypeForExtension = function(extension) { - return extension.encrypted ? "encrypted" : "decrypted"; - } - } - -} - -angular.module('app.frontend').directive('contextualExtensionsMenu', () => new ContextualExtensionsMenu); diff --git a/app/assets/javascripts/app/services/directives/views/editorMenu.js b/app/assets/javascripts/app/services/directives/views/editorMenu.js deleted file mode 100644 index 008c5fadc..000000000 --- a/app/assets/javascripts/app/services/directives/views/editorMenu.js +++ /dev/null @@ -1,30 +0,0 @@ -class EditorMenu { - - constructor() { - this.restrict = "E"; - this.templateUrl = "frontend/directives/editor-menu.html"; - this.scope = { - callback: "&", - selectedEditor: "=" - }; - } - - controller($scope, componentManager) { - 'ngInject'; - - $scope.formData = {}; - - $scope.editors = componentManager.componentsForArea("editor-editor"); - - $scope.selectEditor = function($event, editor) { - if(editor) { - editor.conflict_of = null; // clear conflict if applicable - } - $scope.callback()(editor); - } - - } - -} - -angular.module('app.frontend').directive('editorMenu', () => new EditorMenu); diff --git a/app/assets/javascripts/app/services/directives/views/globalExtensionsMenu.js b/app/assets/javascripts/app/services/directives/views/globalExtensionsMenu.js deleted file mode 100644 index f046d48d5..000000000 --- a/app/assets/javascripts/app/services/directives/views/globalExtensionsMenu.js +++ /dev/null @@ -1,225 +0,0 @@ -class GlobalExtensionsMenu { - - constructor() { - this.restrict = "E"; - this.templateUrl = "frontend/directives/global-extensions-menu.html"; - this.scope = { - }; - } - - controller($scope, extensionManager, syncManager, modelManager, themeManager, componentManager) { - 'ngInject'; - - $scope.formData = {}; - - $scope.extensionManager = extensionManager; - $scope.themeManager = themeManager; - $scope.componentManager = componentManager; - - $scope.serverExtensions = modelManager.itemsForContentType("SF|Extension"); - - $scope.selectedAction = function(action, extension) { - extensionManager.executeAction(action, extension, null, function(response){ - if(response && response.error) { - action.error = true; - alert("There was an error performing this action. Please try again."); - } else { - action.error = false; - syncManager.sync(null); - } - }) - } - - $scope.changeExtensionEncryptionFormat = function(encrypted, extension) { - extension.encrypted = encrypted; - extension.setDirty(true); - syncManager.sync(); - } - - $scope.deleteActionExtension = function(extension) { - if(confirm("Are you sure you want to delete this extension?")) { - extensionManager.deleteExtension(extension); - } - } - - $scope.reloadExtensionsPressed = function() { - if(confirm("For your security, reloading extensions will disable any currently enabled repeat actions.")) { - extensionManager.refreshExtensionsFromServer(); - } - } - - $scope.deleteTheme = function(theme) { - if(confirm("Are you sure you want to delete this theme?")) { - themeManager.deactivateTheme(theme); - modelManager.setItemToBeDeleted(theme); - syncManager.sync(); - } - } - - $scope.renameExtension = function(extension) { - extension.tempName = extension.name; - extension.rename = true; - } - - $scope.submitExtensionRename = function(extension) { - extension.name = extension.tempName; - extension.tempName = null; - extension.setDirty(true); - extension.rename = false; - syncManager.sync(); - } - - $scope.clickedExtension = function(extension) { - if(extension.rename) { - return; - } - - if($scope.currentlyExpandedExtension && $scope.currentlyExpandedExtension !== extension) { - $scope.currentlyExpandedExtension.showDetails = false; - $scope.currentlyExpandedExtension.rename = false; - } - - extension.showDetails = !extension.showDetails; - - if(extension.showDetails) { - $scope.currentlyExpandedExtension = extension; - } - } - - // Server extensions - - $scope.deleteServerExt = function(ext) { - if(confirm("Are you sure you want to delete and disable this extension?")) { - _.remove($scope.serverExtensions, {uuid: ext.uuid}); - modelManager.setItemToBeDeleted(ext); - syncManager.sync(); - } - } - - $scope.nameForServerExtension = function(ext) { - var url = ext.url; - if(!url) { - return "Invalid Extension"; - } - if(url.includes("gdrive")) { - return "Google Drive Sync"; - } else if(url.includes("file_attacher")) { - return "File Attacher"; - } else if(url.includes("onedrive")) { - return "OneDrive Sync"; - } else if(url.includes("backup.email_archive")) { - return "Daily Email Backups"; - } else if(url.includes("dropbox")) { - return "Dropbox Sync"; - } else if(url.includes("revisions")) { - return "Revision History"; - } else { - return null; - } - } - - - // Components - - $scope.revokePermissions = function(component) { - component.permissions = []; - component.setDirty(true); - syncManager.sync(); - } - - $scope.deleteComponent = function(component) { - if(confirm("Are you sure you want to delete this component?")) { - componentManager.deleteComponent(component); - } - } - - $scope.makeEditorDefault = function(component) { - var currentDefault = componentManager.componentsForArea("editor-editor").filter((e) => {return e.isDefaultEditor()})[0]; - if(currentDefault) { - currentDefault.setAppDataItem("defaultEditor", false); - currentDefault.setDirty(true); - } - component.setAppDataItem("defaultEditor", true); - component.setDirty(true); - syncManager.sync(); - } - - $scope.removeEditorDefault = function(component) { - component.setAppDataItem("defaultEditor", false); - component.setDirty(true); - syncManager.sync(); - } - - // Installation - - $scope.submitInstallLink = function() { - - var fullLink = $scope.formData.installLink; - if(!fullLink) { - return; - } - - var completion = function() { - $scope.formData.installLink = ""; - $scope.formData.successfullyInstalled = true; - } - - var links = fullLink.split(","); - for(var link of links) { - var type = getParameterByName("type", link); - - if(type == "sf") { - $scope.handleSyncAdapterLink(link, completion); - } else if(type == "editor") { - $scope.handleEditorLink(link, completion); - } else if(link.indexOf(".css") != -1 || type == "theme") { - $scope.handleThemeLink(link, completion); - } else if(type == "component") { - $scope.handleComponentLink(link, completion); - } - - else { - $scope.handleActionLink(link, completion); - } - } - } - - $scope.handleSyncAdapterLink = function(link, completion) { - var params = parametersFromURL(link); - params["url"] = link; - var ext = new SyncAdapter({content: params}); - ext.setDirty(true); - - modelManager.addItem(ext); - syncManager.sync(); - $scope.serverExtensions.push(ext); - completion(); - } - - $scope.handleThemeLink = function(link, completion) { - themeManager.submitTheme(link); - completion(); - } - - $scope.handleComponentLink = function(link, completion) { - componentManager.installComponent(link); - completion(); - } - - $scope.handleActionLink = function(link, completion) { - if(link) { - extensionManager.addExtension(link, function(response){ - if(!response) { - alert("Unable to register this extension. Make sure the link is valid and try again."); - } else { - completion(); - } - }) - } - } - - } - -} - -angular.module('app.frontend').directive('globalExtensionsMenu', () => new GlobalExtensionsMenu); diff --git a/app/assets/javascripts/app/services/directives/views/permissionsModal.js b/app/assets/javascripts/app/services/directives/views/permissionsModal.js deleted file mode 100644 index e2a1f0a07..000000000 --- a/app/assets/javascripts/app/services/directives/views/permissionsModal.js +++ /dev/null @@ -1,70 +0,0 @@ -class PermissionsModal { - - constructor() { - this.restrict = "E"; - this.templateUrl = "frontend/directives/permissions-modal.html"; - this.scope = { - show: "=", - component: "=", - permissions: "=", - callback: "=" - }; - } - - link($scope, el, attrs) { - - $scope.dismiss = function() { - el.remove(); - } - - $scope.accept = function() { - $scope.callback(true); - $scope.dismiss(); - } - - $scope.deny = function() { - $scope.callback(false); - $scope.dismiss(); - } - - $scope.formattedPermissions = $scope.permissions.map(function(permission){ - if(permission.name === "stream-items") { - var title = "Access to "; - var types = permission.content_types.map(function(type){ - return (type + "s").toLowerCase(); - }) - var typesString = ""; - var separator = ", "; - - for(var i = 0;i < types.length;i++) { - var type = types[i]; - if(i == 0) { - // first element - typesString = typesString + type; - } else if(i == types.length - 1) { - // last element - if(types.length > 2) { - typesString += separator + "and " + typesString; - } else if(types.length == 2) { - typesString = typesString + " and " + type; - } - } else { - typesString += separator + type; - } - } - - return title + typesString; - } else if(permission.name === "stream-context-item") { - var mapping = { - "editor-stack" : "working note", - "note-tags" : "working note", - "editor-editor": "working note" - } - return "Access to " + mapping[$scope.component.area]; - } - }) - } - -} - -angular.module('app.frontend').directive('permissionsModal', () => new PermissionsModal); diff --git a/app/assets/javascripts/app/services/extensionManager.js b/app/assets/javascripts/app/services/extensionManager.js deleted file mode 100644 index ff7b66cd1..000000000 --- a/app/assets/javascripts/app/services/extensionManager.js +++ /dev/null @@ -1,337 +0,0 @@ -class ExtensionManager { - - constructor(httpManager, modelManager, authManager, syncManager, storageManager) { - this.httpManager = httpManager; - this.modelManager = modelManager; - this.authManager = authManager; - this.enabledRepeatActionUrls = JSON.parse(storageManager.getItem("enabledRepeatActionUrls")) || []; - this.syncManager = syncManager; - this.storageManager = storageManager; - - modelManager.addItemSyncObserver("extensionManager", "Extension", function(allItems, validItems, deletedItems){ - for (var ext of validItems) { - for (var action of ext.actions) { - if(_.includes(this.enabledRepeatActionUrls, action.url)) { - this.enableRepeatAction(action, ext); - } - } - } - }.bind(this)) - } - - get extensions() { - return this.modelManager.extensions; - } - - extensionsInContextOfItem(item) { - return this.extensions.filter(function(ext){ - return _.includes(ext.supported_types, item.content_type) || ext.actionsWithContextForItem(item).length > 0; - }) - } - - actionWithURL(url) { - for (var extension of this.extensions) { - return _.find(extension.actions, {url: url}) - } - } - - addExtension(url, callback) { - this.retrieveExtensionFromServer(url, callback); - } - - deleteExtension(extension) { - for(var action of extension.actions) { - if(action.repeat_mode) { - if(this.isRepeatActionEnabled(action)) { - this.disableRepeatAction(action); - } - } - } - - this.modelManager.setItemToBeDeleted(extension); - this.syncManager.sync(null); - } - - /* - Loads an extension in the context of a certain item. The server then has the chance to respond with actions that are - relevant just to this item. The response extension is not saved, just displayed as a one-time thing. - */ - loadExtensionInContextOfItem(extension, item, callback) { - - this.httpManager.getAbsolute(extension.url, {content_type: item.content_type, item_uuid: item.uuid}, function(response){ - this.updateExtensionFromRemoteResponse(extension, response); - callback && callback(extension); - }.bind(this), function(response){ - console.log("Error loading extension", response); - if(callback) { - callback(null); - } - }.bind(this)) - } - - /* - Registers new extension and saves it to user's account - */ - retrieveExtensionFromServer(url, callback) { - this.httpManager.getAbsolute(url, {}, function(response){ - if(typeof response !== 'object') { - callback(null); - return; - } - var ext = this.handleExtensionLoadExternalResponseItem(url, response); - if(callback) { - callback(ext); - } - }.bind(this), function(response){ - console.error("Error registering extension", response); - callback(null); - }) - } - - handleExtensionLoadExternalResponseItem(url, externalResponseItem) { - // Don't allow remote response to set these flags - delete externalResponseItem.encrypted; - delete externalResponseItem.uuid; - - var extension = _.find(this.extensions, {url: url}); - if(extension) { - this.updateExtensionFromRemoteResponse(extension, externalResponseItem); - } else { - extension = new Extension(externalResponseItem); - extension.url = url; - extension.setDirty(true); - this.modelManager.addItem(extension); - this.syncManager.sync(null); - } - - return extension; - } - - updateExtensionFromRemoteResponse(extension, response) { - if(response.description) { - extension.description = response.description; - } - if(response.supported_types) { - extension.supported_types = response.supported_types; - } - - if(response.actions) { - extension.actions = response.actions.map(function(action){ - return new Action(action); - }) - } else { - extension.actions = []; - } - } - - refreshExtensionsFromServer() { - for (var url of this.enabledRepeatActionUrls) { - var action = this.actionWithURL(url); - if(action) { - this.disableRepeatAction(action); - } - } - - for(var ext of this.extensions) { - this.retrieveExtensionFromServer(ext.url, function(extension){ - extension.setDirty(true); - }); - } - } - - executeAction(action, extension, item, callback) { - - if(extension.encrypted && this.authManager.offline()) { - alert("To send data encrypted, you must have an encryption key, and must therefore be signed in."); - callback(null); - return; - } - - var customCallback = function(response) { - action.running = false; - callback(response); - } - - action.running = true; - - switch (action.verb) { - case "get": { - - this.httpManager.getAbsolute(action.url, {}, function(response){ - action.error = false; - var items = response.items || [response.item]; - EncryptionHelper.decryptMultipleItems(items, this.authManager.keys()); - items = this.modelManager.mapResponseItemsToLocalModels(items, ModelManager.MappingSourceRemoteActionRetrieved); - for(var item of items) { - item.setDirty(true); - } - this.syncManager.sync(null); - customCallback({items: items}); - }.bind(this), function(response){ - action.error = true; - customCallback(null); - }) - - break; - } - - case "render": { - - this.httpManager.getAbsolute(action.url, {}, function(response){ - action.error = false; - EncryptionHelper.decryptItem(response.item, this.authManager.keys()); - var item = this.modelManager.createItem(response.item); - customCallback({item: item}); - - }.bind(this), function(response){ - action.error = true; - customCallback(null); - }) - - break; - } - - case "show": { - var win = window.open(action.url, '_blank'); - win.focus(); - customCallback(); - break; - } - - case "post": { - var params = {}; - - if(action.all) { - var items = this.modelManager.allItemsMatchingTypes(action.content_types); - params.items = items.map(function(item){ - var params = this.outgoingParamsForItem(item, extension); - return params; - }.bind(this)) - - } else { - params.items = [this.outgoingParamsForItem(item, extension)]; - } - - this.performPost(action, extension, params, function(response){ - customCallback(response); - }); - - break; - } - - default: { - - } - } - - action.lastExecuted = new Date(); - } - - isRepeatActionEnabled(action) { - return _.includes(this.enabledRepeatActionUrls, action.url); - } - - disableRepeatAction(action, extension) { - _.pull(this.enabledRepeatActionUrls, action.url); - this.storageManager.setItem("enabledRepeatActionUrls", JSON.stringify(this.enabledRepeatActionUrls)); - this.modelManager.removeItemChangeObserver(action.url); - - console.assert(this.isRepeatActionEnabled(action) == false); - } - - enableRepeatAction(action, extension) { - if(!_.find(this.enabledRepeatActionUrls, action.url)) { - this.enabledRepeatActionUrls.push(action.url); - this.storageManager.setItem("enabledRepeatActionUrls", JSON.stringify(this.enabledRepeatActionUrls)); - } - - if(action.repeat_mode) { - - if(action.repeat_mode == "watch") { - this.modelManager.addItemChangeObserver(action.url, action.content_types, function(changedItems){ - this.triggerWatchAction(action, extension, changedItems); - }.bind(this)) - } - - if(action.repeat_mode == "loop") { - // todo - } - - } - } - - queueAction(action, extension, delay, changedItems) { - this.actionQueue = this.actionQueue || []; - if(_.find(this.actionQueue, {url: action.url})) { - return; - } - - this.actionQueue.push(action); - - setTimeout(function () { - this.triggerWatchAction(action, extension, changedItems); - _.pull(this.actionQueue, action); - }.bind(this), delay * 1000); - } - - triggerWatchAction(action, extension, changedItems) { - if(action.repeat_timeout > 0) { - var lastExecuted = action.lastExecuted; - var diffInSeconds = (new Date() - lastExecuted)/1000; - if(diffInSeconds < action.repeat_timeout) { - var delay = action.repeat_timeout - diffInSeconds; - this.queueAction(action, extension, delay, changedItems); - return; - } - } - - action.lastExecuted = new Date(); - - if(action.verb == "post") { - var params = {}; - params.items = changedItems.map(function(item){ - var params = this.outgoingParamsForItem(item, extension); - return params; - }.bind(this)) - - action.running = true; - this.performPost(action, extension, params, function(){ - action.running = false; - }); - } else { - // todo - } - } - - outgoingParamsForItem(item, extension) { - var keys = this.authManager.keys(); - if(!extension.encrypted) { - keys = null; - } - var itemParams = new ItemParams(item, keys, this.authManager.protocolVersion()); - return itemParams.paramsForExtension(); - } - - performPost(action, extension, params, callback) { - - if(extension.encrypted) { - params.auth_params = this.authManager.getAuthParams(); - } - - this.httpManager.postAbsolute(action.url, params, function(response){ - action.error = false; - if(callback) { - callback(response); - } - }.bind(this), function(response){ - action.error = true; - console.log("Action error response:", response); - if(callback) { - callback({error: "Request error"}); - } - }) - } - -} - -angular.module('app.frontend').service('extensionManager', ExtensionManager); diff --git a/app/assets/javascripts/app/services/httpManager.js b/app/assets/javascripts/app/services/httpManager.js index a5d157d57..71692f478 100644 --- a/app/assets/javascripts/app/services/httpManager.js +++ b/app/assets/javascripts/app/services/httpManager.js @@ -77,4 +77,4 @@ class HttpManager { } -angular.module('app.frontend').service('httpManager', HttpManager); +angular.module('app').service('httpManager', HttpManager); diff --git a/app/assets/javascripts/app/services/migrationManager.js b/app/assets/javascripts/app/services/migrationManager.js index bb139860c..9fec09d65 100644 --- a/app/assets/javascripts/app/services/migrationManager.js +++ b/app/assets/javascripts/app/services/migrationManager.js @@ -50,7 +50,7 @@ class MigrationManager { this.modelManager.setItemToBeDeleted(editor); } - this.syncManager.sync(); + this.syncManager.sync("addEditorToComponentMigrator"); } }) } @@ -58,4 +58,4 @@ class MigrationManager { } -angular.module('app.frontend').service('migrationManager', MigrationManager); +angular.module('app').service('migrationManager', MigrationManager); diff --git a/app/assets/javascripts/app/services/modelManager.js b/app/assets/javascripts/app/services/modelManager.js index 43cdbe11c..112d1da02 100644 --- a/app/assets/javascripts/app/services/modelManager.js +++ b/app/assets/javascripts/app/services/modelManager.js @@ -3,8 +3,10 @@ class ModelManager { constructor(storageManager) { ModelManager.MappingSourceRemoteRetrieved = "MappingSourceRemoteRetrieved"; ModelManager.MappingSourceRemoteSaved = "MappingSourceRemoteSaved"; + ModelManager.MappingSourceLocalSaved = "MappingSourceLocalSaved"; ModelManager.MappingSourceLocalRetrieved = "MappingSourceLocalRetrieved"; ModelManager.MappingSourceComponentRetrieved = "MappingSourceComponentRetrieved"; + ModelManager.MappingSourceDesktopInstalled = "MappingSourceDesktopInstalled"; // When a component is installed by the desktop and some of its values change ModelManager.MappingSourceRemoteActionRetrieved = "MappingSourceRemoteActionRetrieved"; /* aciton-based Extensions like note history */ ModelManager.MappingSourceFileImport = "MappingSourceFileImport"; @@ -18,7 +20,7 @@ class ModelManager { this._extensions = []; this.acceptableContentTypes = [ "Note", "Tag", "Extension", "SN|Editor", "SN|Theme", - "SN|Component", "SF|Extension", "SN|UserPreferences" + "SN|Component", "SF|Extension", "SN|UserPreferences", "SF|MFA" ]; } @@ -52,6 +54,8 @@ class ModelManager { this.informModelsOfUUIDChangeForItem(newItem, item.uuid, newItem.uuid); + console.log(item.uuid, "-->", newItem.uuid); + var block = () => { this.addItem(newItem); newItem.setDirty(true); @@ -60,9 +64,10 @@ class ModelManager { } if(removeOriginal) { - this.removeItemLocally(item, function(){ - block(); - }); + // Set to deleted, then run through mapping function so that observers can be notified + item.deleted = true; + this.mapResponseItemsToLocalModels([item], ModelManager.MappingSourceLocalSaved); + block(); } else { block(); } @@ -79,13 +84,13 @@ class ModelManager { } allItemsMatchingTypes(contentTypes) { - return this.items.filter(function(item){ + return this.allItems.filter(function(item){ return (_.includes(contentTypes, item.content_type) || _.includes(contentTypes, "*")) && !item.dummy; }) } itemsForContentType(contentType) { - return this.items.filter(function(item){ + return this.allItems.filter(function(item){ return item.content_type == contentType; }); } @@ -103,6 +108,10 @@ class ModelManager { return tag; } + didSyncModelsOffline(items) { + this.notifySyncObserversOfModels(items, ModelManager.MappingSourceLocalSaved); + } + mapResponseItemsToLocalModels(items, source) { return this.mapResponseItemsToLocalModelsOmittingFields(items, null, source); } @@ -139,7 +148,8 @@ class ModelManager { continue; } - var unknownContentType = !_.includes(this.acceptableContentTypes, json_obj["content_type"]); + let contentType = json_obj["content_type"] || (item && item.content_type); + var unknownContentType = !_.includes(this.acceptableContentTypes, contentType); if(json_obj.deleted == true || unknownContentType) { if(item && !unknownContentType) { modelsToNotifyObserversOf.push(item); @@ -149,7 +159,7 @@ class ModelManager { } if(!item) { - item = this.createItem(json_obj); + item = this.createItem(json_obj, true); } this.addItem(item); @@ -172,6 +182,7 @@ class ModelManager { return models; } + /* Note that this function is public, and can also be called manually (desktopManager uses it) */ notifySyncObserversOfModels(models, source) { for(var observer of this.itemSyncObservers) { var allRelevantItems = models.filter(function(item){return item.content_type == observer.type || observer.type == "*"}); @@ -202,7 +213,7 @@ class ModelManager { } } - createItem(json_obj) { + createItem(json_obj, dontNotifyObservers) { var item; if(json_obj.content_type == "Note") { item = new Note(json_obj); @@ -217,13 +228,24 @@ class ModelManager { } else if(json_obj.content_type == "SN|Component") { item = new Component(json_obj); } else if(json_obj.content_type == "SF|Extension") { - item = new SyncAdapter(json_obj); + item = new ServerExtension(json_obj); + } else if(json_obj.content_type == "SF|MFA") { + item = new Mfa(json_obj); } else { item = new Item(json_obj); } + // Some observers would be interested to know when an an item is locally created + // If we don't send this out, these observers would have to wait until MappingSourceRemoteSaved + // to hear about it, but sometimes, RemoveSaved is explicitly ignored by the observer to avoid + // recursive callbacks. See componentManager's syncObserver callback. + // dontNotifyObservers is currently only set true by modelManagers mapResponseItemsToLocalModels + if(!dontNotifyObservers) { + this.notifySyncObserversOfModels([item], ModelManager.MappingSourceLocalSaved); + } + item.addObserver(this, function(changedItem){ this.notifyItemChangeObserversOfModels([changedItem]); }.bind(this)); @@ -232,7 +254,7 @@ class ModelManager { } createDuplicateItem(itemResponse, sourceItem) { - var dup = this.createItem(itemResponse); + var dup = this.createItem(itemResponse, true); this.resolveReferencesForItem(dup); return dup; } @@ -405,6 +427,25 @@ class ModelManager { return JSON.stringify(data, null, 2 /* pretty print */); } + + + /* + Misc + */ + + humanReadableDisplayForContentType(contentType) { + return { + "Note" : "note", + "Tag" : "tag", + "Extension" : "action-based extension", + "SN|Component" : "component", + "SN|Editor" : "editor", + "SN|Theme" : "theme", + "SF|Extension" : "server extension", + "SF|MFA" : "two-factor authentication setting" + }[contentType]; + } + } -angular.module('app.frontend').service('modelManager', ModelManager); +angular.module('app').service('modelManager', ModelManager); diff --git a/app/assets/javascripts/app/services/nativeExtManager.js b/app/assets/javascripts/app/services/nativeExtManager.js new file mode 100644 index 000000000..503e3f3b3 --- /dev/null +++ b/app/assets/javascripts/app/services/nativeExtManager.js @@ -0,0 +1,92 @@ +/* A class for handling installation of system extensions */ + +class NativeExtManager { + + constructor(modelManager, syncManager, singletonManager) { + this.modelManager = modelManager; + this.syncManager = syncManager; + this.singletonManager = singletonManager; + + this.extensionsIdentifier = "org.standardnotes.extensions-manager"; + this.systemExtensions = []; + + this.resolveExtensionsManager(); + } + + isSystemExtension(extension) { + return this.systemExtensions.includes(extension.uuid); + } + + resolveExtensionsManager() { + + this.singletonManager.registerSingleton({content_type: "SN|Component", package_info: {identifier: this.extensionsIdentifier}}, (resolvedSingleton) => { + // Resolved Singleton + this.systemExtensions.push(resolvedSingleton.uuid); + + var needsSync = false; + if(isDesktopApplication()) { + if(!resolvedSingleton.local_url) { + resolvedSingleton.local_url = window._extensions_manager_location; + needsSync = true; + } + } else { + if(!resolvedSingleton.hosted_url) { + resolvedSingleton.hosted_url = window._extensions_manager_location; + needsSync = true; + } + } + + if(needsSync) { + resolvedSingleton.setDirty(true); + this.syncManager.sync("resolveExtensionsManager"); + } + }, (valueCallback) => { + // Safe to create. Create and return object. + let url = window._extensions_manager_location; + console.log("Installing Extensions Manager from URL", url); + if(!url) { + console.error("window._extensions_manager_location must be set."); + return; + } + + let packageInfo = { + name: "Extensions", + identifier: this.extensionsIdentifier + } + + var item = { + content_type: "SN|Component", + content: { + name: packageInfo.name, + area: "rooms", + package_info: packageInfo, + permissions: [ + { + name: "stream-items", + content_types: ["SN|Component", "SN|Theme", "SF|Extension", "Extension", "SF|MFA", "SN|Editor"] + } + ] + } + } + + if(isDesktopApplication()) { + item.content.local_url = window._extensions_manager_location; + } else { + item.content.hosted_url = window._extensions_manager_location; + } + + var component = this.modelManager.createItem(item); + this.modelManager.addItem(component); + + component.setDirty(true); + this.syncManager.sync("resolveExtensionsManager createNew"); + + this.systemExtensions.push(component.uuid); + + valueCallback(component); + }); + } + +} + +angular.module('app').service('nativeExtManager', NativeExtManager); diff --git a/app/assets/javascripts/app/services/passcodeManager.js b/app/assets/javascripts/app/services/passcodeManager.js index e4862fbe6..a3a65ca86 100644 --- a/app/assets/javascripts/app/services/passcodeManager.js +++ b/app/assets/javascripts/app/services/passcodeManager.js @@ -1,4 +1,4 @@ -angular.module('app.frontend') +angular.module('app') .provider('passcodeManager', function () { this.$get = function($rootScope, $timeout, modelManager, dbManager, authManager, storageManager) { @@ -41,7 +41,7 @@ angular.module('app.frontend') }.bind(this)); } - this.setPasscode = function(passcode, callback) { + this.setPasscode = (passcode, callback) => { var cost = Neeto.crypto.defaultPasswordGenerationCost(); var salt = Neeto.crypto.generateRandomKey(512); var defaultParams = {pw_cost: cost, pw_salt: salt, version: "002"}; @@ -60,6 +60,10 @@ angular.module('app.frontend') }.bind(this)); } + this.changePasscode = (newPasscode, callback) => { + this.setPasscode(newPasscode, callback); + } + this.clearPasscode = function() { storageManager.setItemsMode(authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.Fixed); // Transfer from Ephemeral storageManager.removeItem("offlineParams", StorageManager.Fixed); @@ -70,7 +74,8 @@ angular.module('app.frontend') this.encryptLocalStorage = function(keys) { storageManager.setKeys(keys); // Switch to Ephemeral storage, wiping Fixed storage - storageManager.setItemsMode(authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.FixedEncrypted); + // Last argument is `force`, which we set to true because in the case of changing passcode + storageManager.setItemsMode(authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.FixedEncrypted, true); } this.decryptLocalStorage = function(keys) { diff --git a/app/assets/javascripts/app/services/singletonManager.js b/app/assets/javascripts/app/services/singletonManager.js new file mode 100644 index 000000000..2e8774729 --- /dev/null +++ b/app/assets/javascripts/app/services/singletonManager.js @@ -0,0 +1,174 @@ +/* + The SingletonManager allows controllers to register an item as a singleton, which means only one instance of that model + should exist, both on the server and on the client. When the SingletonManager detects multiple items matching the singleton predicate, + the oldest ones will be deleted, leaving the newest ones. + + We will treat the model most recently arrived from the server as the most recent one. The reason for this is, if you're offline, + a singleton can be created, as in the case of UserPreferneces. Then when you sign in, you'll retrieve your actual user preferences. + In that case, even though the offline singleton has a more recent updated_at, the server retreived value is the one we care more about. +*/ + +class SingletonManager { + + constructor($rootScope, modelManager) { + this.$rootScope = $rootScope; + this.modelManager = modelManager; + this.singletonHandlers = []; + + $rootScope.$on("initial-data-loaded", (event, data) => { + this.resolveSingletons(modelManager.allItems, null, true); + }) + + $rootScope.$on("sync:completed", (event, data) => { + // The reason we also need to consider savedItems in consolidating singletons is in case of sync conflicts, + // a new item can be created, but is never processed through "retrievedItems" since it is only created locally then saved. + + // HOWEVER, by considering savedItems, we are now ruining everything, especially during sign in. A singleton can be created + // offline, and upon sign in, will sync all items to the server, and by combining retrievedItems & savedItems, and only choosing + // the latest, you are now resolving to the most recent one, which is in the savedItems list and not retrieved items, defeating + // the whole purpose of this thing. + + // Updated solution: resolveSingletons will now evaluate both of these arrays separately. + this.resolveSingletons(data.retrievedItems, data.savedItems); + }) + } + + registerSingleton(predicate, resolveCallback, createBlock) { + /* + predicate: a key/value pair that specifies properties that should match in order for an item to be considered a predicate + resolveCallback: called when one or more items are deleted and a new item becomes the reigning singleton + createBlock: called when a sync is complete and no items are found. The createBlock should create the item and return it. + */ + this.singletonHandlers.push({ + predicate: predicate, + resolutionCallback: resolveCallback, + createBlock: createBlock + }); + } + + resolveSingletons(retrievedItems, savedItems, initialLoad) { + retrievedItems = retrievedItems || []; + savedItems = savedItems || []; + + for(let singletonHandler of this.singletonHandlers) { + var predicate = singletonHandler.predicate; + let retrievedSingletonItems = this.filterItemsWithPredicate(retrievedItems, predicate); + + // We only want to consider saved items count to see if it's more than 0, and do nothing else with it. + // This way we know there was some action and things need to be resolved. The saved items will come up + // in filterItemsWithPredicate(this.modelManager.allItems) and be deleted anyway + let savedSingletonItemsCount = this.filterItemsWithPredicate(savedItems, predicate).length; + + if(retrievedSingletonItems.length > 0 || savedSingletonItemsCount > 0) { + /* + Check local inventory and make sure only 1 similar item exists. If more than 1, delete oldest + Note that this local inventory will also contain whatever is in retrievedItems. + However, as stated in the header comment, retrievedItems take precendence over existing items, + even if they have a lower updated_at value + */ + var allExtantItemsMatchingPredicate = this.filterItemsWithPredicate(this.modelManager.allItems, predicate); + + /* + If there are more than 1 matches, delete everything not in `retrievedSingletonItems`, + then delete all but the latest in `retrievedSingletonItems` + */ + if(allExtantItemsMatchingPredicate.length >= 2) { + + // Items that will be deleted + var toDelete = []; + // The item that will be chosen to be kept + var winningItem, sorted; + + if(retrievedSingletonItems.length > 0) { + for(let extantItem of allExtantItemsMatchingPredicate) { + if(!retrievedSingletonItems.includes(extantItem)) { + // Delete it + toDelete.push(extantItem); + } + } + + // Sort incoming singleton items by most recently updated first, then delete all the rest + sorted = retrievedSingletonItems.sort((a, b) => { + return a.updated_at < b.updated_at; + }) + + } else { + // We're in here because of savedItems + // This can be the case if retrievedSingletonItems/retrievedItems length is 0, but savedSingletonItemsCount is non zero. + // In this case, we want to sort by date and delete all but the most recent one + sorted = allExtantItemsMatchingPredicate.sort((a, b) => { + return a.updated_at < b.updated_at; + }); + } + + winningItem = sorted[0]; + + // Delete everything but the first one + toDelete = toDelete.concat(sorted.slice(1, sorted.length)); + + for(var d of toDelete) { + this.modelManager.setItemToBeDeleted(d); + } + + this.$rootScope.sync("resolveSingletons"); + + // Send remaining item to callback + singletonHandler.singleton = winningItem; + singletonHandler.resolutionCallback(winningItem); + + } else if(allExtantItemsMatchingPredicate.length == 1) { + if(!singletonHandler.singleton) { + // Not yet notified interested parties of object + var singleton = allExtantItemsMatchingPredicate[0]; + singletonHandler.singleton = singleton; + singletonHandler.resolutionCallback(singleton); + + } + } + } else { + // Retrieved items does not include any items of interest. If we don't have a singleton registered to this handler, + // we need to create one. Only do this on actual sync completetions and not on initial data load. Because we want + // to get the latest from the server before making the decision to create a new item + if(!singletonHandler.singleton && !initialLoad && !singletonHandler.pendingCreateBlockCallback) { + singletonHandler.pendingCreateBlockCallback = true; + singletonHandler.createBlock((created) => { + singletonHandler.singleton = created; + singletonHandler.pendingCreateBlockCallback = false; + singletonHandler.resolutionCallback(created); + }); + } + } + } + } + + filterItemsWithPredicate(items, predicate) { + return items.filter((candidate) => { + return this.itemSatisfiesPredicate(candidate, predicate); + }) + } + + itemSatisfiesPredicate(candidate, predicate) { + for(var key in predicate) { + var predicateValue = predicate[key]; + var candidateValue = candidate[key]; + if(typeof predicateValue == 'object') { + // Check nested properties + if(!candidateValue) { + // predicateValue is 'object' but candidateValue is null + return false; + } + + if(!this.itemSatisfiesPredicate(candidateValue, predicateValue)) { + return false; + } + } + else if(candidateValue != predicateValue) { + return false; + } + } + return true; + } + +} + +angular.module('app').service('singletonManager', SingletonManager); diff --git a/app/assets/javascripts/app/services/storageManager.js b/app/assets/javascripts/app/services/storageManager.js index cc8a3fa9b..e1aa26c4f 100644 --- a/app/assets/javascripts/app/services/storageManager.js +++ b/app/assets/javascripts/app/services/storageManager.js @@ -62,9 +62,9 @@ class StorageManager { return this._memoryStorage; } - setItemsMode(mode) { + setItemsMode(mode, force) { var newStorage = this.getVault(mode); - if(newStorage !== this.storage) { + if(newStorage !== this.storage || mode !== this.itemsStorageMode || force) { // transfer storages var length = this.storage.length; for(var i = 0; i < length; i++) { @@ -161,7 +161,6 @@ class StorageManager { for(var key of Object.keys(encryptedStorage.storage)) { this.setItem(key, encryptedStorage.storage[key]); } - } hasPasscode() { @@ -228,4 +227,4 @@ StorageManager.FixedEncrypted = "FixedEncrypted"; // encrypted memoryStorage + l StorageManager.Ephemeral = "Ephemeral"; // memoryStorage StorageManager.Fixed = "Fixed"; // localStorage -angular.module('app.frontend').service('storageManager', StorageManager); +angular.module('app').service('storageManager', StorageManager); diff --git a/app/assets/javascripts/app/services/syncManager.js b/app/assets/javascripts/app/services/syncManager.js index d11fe51c3..599aca41b 100644 --- a/app/assets/javascripts/app/services/syncManager.js +++ b/app/assets/javascripts/app/services/syncManager.js @@ -21,10 +21,6 @@ class SyncManager { return this.storageManager.getItem("mk"); } - get serverPassword() { - return this.storageManager.getItem("pw"); - } - writeItemsToLocalStorage(items, offlineOnly, callback) { if(items.length == 0) { callback && callback(); @@ -62,6 +58,11 @@ class SyncManager { } } + this.$rootScope.$broadcast("sync:completed", {}); + + // Required in order for modelManager to notify sync observers + this.modelManager.didSyncModelsOffline(items); + if(callback) { callback({success: true}); } @@ -92,7 +93,7 @@ class SyncManager { let alternateNextItem = () => { if(index >= originalItems.length) { - // We don't use originalItems as altnerating UUID will have deleted them. + // We don't use originalItems as alternating UUID will have deleted them. block(); return; } @@ -188,7 +189,17 @@ class SyncManager { this.$interval.cancel(this.syncStatus.checker); } - sync(callback, options = {}) { + sync(callback, options = {}, source) { + + if(!options) options = {}; + + if(typeof callback == 'string') { + // is source string, used to avoid filling parameters on call + source = callback; + callback = null; + } + + // console.log("Syncing from", source); var allDirtyItems = this.modelManager.getDirtyItems(); @@ -241,6 +252,11 @@ class SyncManager { this.allRetreivedItems = []; } + // We also want to do this for savedItems + if(!this.allSavedItems) { + this.allSavedItems = []; + } + var version = this.authManager.protocolVersion(); var keys = this.authManager.keys(); @@ -265,7 +281,17 @@ class SyncManager { this.$rootScope.$broadcast("sync:updated_token", this.syncToken); + // Filter retrieved_items to remove any items that may be in saved_items for this complete sync operation + // When signing in, and a user requires many round trips to complete entire retrieval of data, an item may be saved + // on the first trip, then on subsequent trips using cursor_token, this same item may be returned, since it's date is + // greater than cursor_token. We keep track of all saved items in whole sync operation with this.allSavedItems + // We need this because singletonManager looks at retrievedItems as higher precendence than savedItems, but if it comes in both + // then that's problematic. + let allSavedUUIDs = this.allSavedItems.map((item) => {return item.uuid}); + response.retrieved_items = response.retrieved_items.filter((candidate) => {return !allSavedUUIDs.includes(candidate.uuid)}); + // Map retrieved items to local data + // Note that deleted items will not be returned var retrieved = this.handleItemsResponse(response.retrieved_items, null, ModelManager.MappingSourceRemoteRetrieved); @@ -281,6 +307,9 @@ class SyncManager { var saved = this.handleItemsResponse(response.saved_items, omitFields, ModelManager.MappingSourceRemoteSaved); + // Append items to master list of saved items for this ongoing sync operation + this.allSavedItems = this.allSavedItems.concat(saved); + // Create copies of items or alternate their uuids if neccessary var unsaved = response.unsaved; this.handleUnsavedItemsResponse(unsaved) @@ -298,12 +327,12 @@ class SyncManager { if(this.cursorToken || this.syncStatus.needsMoreSync) { setTimeout(function () { - this.sync(callback, options); + this.sync(callback, options, "onSyncSuccess cursorToken || needsMoreSync"); }.bind(this), 10); // wait 10ms to allow UI to update } else if(this.repeatOnCompletion) { this.repeatOnCompletion = false; setTimeout(function () { - this.sync(callback, options); + this.sync(callback, options, "onSyncSuccess repeatOnCompletion"); }.bind(this), 10); // wait 10ms to allow UI to update } else { this.writeItemsToLocalStorage(this.allRetreivedItems, false, null); @@ -319,10 +348,11 @@ class SyncManager { this.$rootScope.$broadcast("major-data-change"); } - this.allRetreivedItems = []; - this.callQueuedCallbacksAndCurrent(callback, response); - this.$rootScope.$broadcast("sync:completed"); + this.$rootScope.$broadcast("sync:completed", {retrievedItems: this.allRetreivedItems, savedItems: this.allSavedItems}); + + this.allRetreivedItems = []; + this.allSavedItems = []; } }.bind(this); @@ -391,14 +421,13 @@ class SyncManager { console.log("Handle unsaved", unsaved); var i = 0; - var handleNext = function() { + var handleNext = () => { if(i >= unsaved.length) { // Handled all items this.sync(null, {additionalFields: ["created_at", "updated_at"]}); return; } - var handled = false; var mapping = unsaved[i]; var itemResponse = mapping.item; EncryptionHelper.decryptMultipleItems([itemResponse], this.authManager.keys()); @@ -414,8 +443,10 @@ class SyncManager { if(error.tag === "uuid_conflict") { // UUID conflicts can occur if a user attempts to // import an old data archive with uuids from the old account into a new account - handled = true; - this.modelManager.alternateUUIDForItem(item, handleNext, true); + this.modelManager.alternateUUIDForItem(item, () => { + i++; + handleNext(); + }, true); } else if(error.tag === "sync_conflict") { @@ -425,20 +456,16 @@ class SyncManager { itemResponse.uuid = null; var dup = this.modelManager.createDuplicateItem(itemResponse, item); - if(!itemResponse.deleted && JSON.stringify(item.structureParams()) !== JSON.stringify(dup.structureParams())) { + if(!itemResponse.deleted && !item.isItemContentEqualWith(dup)) { this.modelManager.addItem(dup); dup.conflict_of = item.uuid; dup.setDirty(true); } - } - ++i; - - if(!handled) { + i++; handleNext(); } - - }.bind(this); + } handleNext(); } @@ -459,4 +486,4 @@ class SyncManager { } } -angular.module('app.frontend').service('syncManager', SyncManager); +angular.module('app').service('syncManager', SyncManager); diff --git a/app/assets/javascripts/app/services/themeManager.js b/app/assets/javascripts/app/services/themeManager.js index 0e799ba08..deda2e089 100644 --- a/app/assets/javascripts/app/services/themeManager.js +++ b/app/assets/javascripts/app/services/themeManager.js @@ -1,99 +1,45 @@ class ThemeManager { - constructor(modelManager, syncManager, $rootScope, storageManager) { - this.syncManager = syncManager; - this.modelManager = modelManager; - this.$rootScope = $rootScope; - this.storageManager = storageManager; - } + constructor(componentManager, desktopManager) { + this.componentManager = componentManager; - get themes() { - return this.modelManager.itemsForContentType("SN|Theme"); - } + desktopManager.registerUpdateObserver((component) => { + // Reload theme if active + if(component.active && component.isTheme()) { + this.deactivateTheme(component); + setTimeout(() => { + this.activateTheme(component); + }, 10); + } + }) - /* - activeTheme: computed property that returns saved theme - currentTheme: stored variable that allows other classes to watch changes - */ - - get activeTheme() { - var activeThemeId = this.storageManager.getItem("activeTheme"); - if(!activeThemeId) { - return null; - } - - var theme = _.find(this.themes, {uuid: activeThemeId}); - return theme; - } - - activateInitialTheme() { - var theme = this.activeTheme; - if(theme) { - this.activateTheme(theme); - } - } - - submitTheme(url) { - var name = this.displayNameForThemeFile(this.fileNameFromPath(url)); - var theme = this.modelManager.createItem({content_type: "SN|Theme", url: url, name: name}); - this.modelManager.addItem(theme); - theme.setDirty(true); - this.syncManager.sync(); + componentManager.registerHandler({identifier: "themeManager", areas: ["themes"], activationHandler: (component) => { + if(component.active) { + this.activateTheme(component); + } else { + this.deactivateTheme(component); + } + }}); } activateTheme(theme) { - var activeTheme = this.activeTheme; - if(activeTheme) { - this.deactivateTheme(activeTheme); - } - + var url = this.componentManager.urlForComponent(theme); var link = document.createElement("link"); - link.href = theme.url; + link.href = url; link.type = "text/css"; link.rel = "stylesheet"; link.media = "screen,print"; link.id = theme.uuid; document.getElementsByTagName("head")[0].appendChild(link); - this.storageManager.setItem("activeTheme", theme.uuid); - - this.currentTheme = theme; - this.$rootScope.$broadcast("theme-changed"); } deactivateTheme(theme) { - this.storageManager.removeItem("activeTheme"); var element = document.getElementById(theme.uuid); if(element) { element.disabled = true; element.parentNode.removeChild(element); } - - this.currentTheme = null; - this.$rootScope.$broadcast("theme-changed"); } - - isThemeActive(theme) { - return this.storageManager.getItem("activeTheme") === theme.uuid; - } - - fileNameFromPath(filePath) { - return filePath.replace(/^.*[\\\/]/, ''); - } - - capitalizeString(string) { - return string.replace(/(?:^|\s)\S/g, function(a) { return a.toUpperCase(); }); - } - - displayNameForThemeFile(fileName) { - let fromParam = getParameterByName("name", fileName); - if(fromParam) { - return fromParam; - } - let name = fileName.split(".")[0]; - let cleaned = name.split("-").join(" "); - return this.capitalizeString(cleaned); - } - } -angular.module('app.frontend').service('themeManager', ThemeManager); +angular.module('app').service('themeManager', ThemeManager); diff --git a/app/assets/javascripts/frontend.js b/app/assets/javascripts/frontend.js deleted file mode 100644 index 7e4f399dd..000000000 --- a/app/assets/javascripts/frontend.js +++ /dev/null @@ -1,5 +0,0 @@ -//= require app/app.frontend.js -//= require_tree ./app/services - -//= require app/app.frontend.js -//= require_tree ./app/frontend diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js new file mode 100644 index 000000000..d7ba63e74 --- /dev/null +++ b/app/assets/javascripts/main.js @@ -0,0 +1 @@ +//= require_tree ./app diff --git a/app/assets/stylesheets/_ionicons.scss b/app/assets/stylesheets/_ionicons.scss deleted file mode 100644 index 47fd1b15c..000000000 --- a/app/assets/stylesheets/_ionicons.scss +++ /dev/null @@ -1,2966 +0,0 @@ -@charset "UTF-8"; -/*! - Ionicons, v2.0.0 - Created by Ben Sperry for the Ionic Framework, http://ionicons.com/ - https://twitter.com/benjsperry https://twitter.com/ionicframework - MIT License: https://github.com/driftyco/ionicons - - Android-style icons originally built by Google’s - Material Design Icons: https://github.com/google/material-design-icons - used under CC BY http://creativecommons.org/licenses/by/4.0/ - Modified icons to fit ionicon’s grid from original. -*/ -@font-face { - font-family: "Ionicons"; - src: url("../assets/ionicons.eot?v=2.0.0"); - src: url("../assets/ionicons.eot?v=2.0.0#iefix") format("embedded-opentype"), url("../assets/ionicons.ttf?v=2.0.0") format("truetype"), url("../assets/ionicons.woff?v=2.0.0") format("woff"), url("../assets/ionicons.svg?v=2.0.0#Ionicons") format("svg"); - font-weight: normal; - font-style: normal; -} -.ion, .ionicons, .ion-alert:before, .ion-alert-circled:before, .ion-android-add:before, .ion-android-add-circle:before, .ion-android-alarm-clock:before, .ion-android-alert:before, .ion-android-apps:before, .ion-android-archive:before, .ion-android-arrow-back:before, .ion-android-arrow-down:before, .ion-android-arrow-dropdown:before, .ion-android-arrow-dropdown-circle:before, .ion-android-arrow-dropleft:before, .ion-android-arrow-dropleft-circle:before, .ion-android-arrow-dropright:before, .ion-android-arrow-dropright-circle:before, .ion-android-arrow-dropup:before, .ion-android-arrow-dropup-circle:before, .ion-android-arrow-forward:before, .ion-android-arrow-up:before, .ion-android-attach:before, .ion-android-bar:before, .ion-android-bicycle:before, .ion-android-boat:before, .ion-android-bookmark:before, .ion-android-bulb:before, .ion-android-bus:before, .ion-android-calendar:before, .ion-android-call:before, .ion-android-camera:before, .ion-android-cancel:before, .ion-android-car:before, .ion-android-cart:before, .ion-android-chat:before, .ion-android-checkbox:before, .ion-android-checkbox-blank:before, .ion-android-checkbox-outline:before, .ion-android-checkbox-outline-blank:before, .ion-android-checkmark-circle:before, .ion-android-clipboard:before, .ion-android-close:before, .ion-android-cloud:before, .ion-android-cloud-circle:before, .ion-android-cloud-done:before, .ion-android-cloud-outline:before, .ion-android-color-palette:before, .ion-android-compass:before, .ion-android-contact:before, .ion-android-contacts:before, .ion-android-contract:before, .ion-android-create:before, .ion-android-delete:before, .ion-android-desktop:before, .ion-android-document:before, .ion-android-done:before, .ion-android-done-all:before, .ion-android-download:before, .ion-android-drafts:before, .ion-android-exit:before, .ion-android-expand:before, .ion-android-favorite:before, .ion-android-favorite-outline:before, .ion-android-film:before, .ion-android-folder:before, .ion-android-folder-open:before, .ion-android-funnel:before, .ion-android-globe:before, .ion-android-hand:before, .ion-android-hangout:before, .ion-android-happy:before, .ion-android-home:before, .ion-android-image:before, .ion-android-laptop:before, .ion-android-list:before, .ion-android-locate:before, .ion-android-lock:before, .ion-android-mail:before, .ion-android-map:before, .ion-android-menu:before, .ion-android-microphone:before, .ion-android-microphone-off:before, .ion-android-more-horizontal:before, .ion-android-more-vertical:before, .ion-android-navigate:before, .ion-android-notifications:before, .ion-android-notifications-none:before, .ion-android-notifications-off:before, .ion-android-open:before, .ion-android-options:before, .ion-android-people:before, .ion-android-person:before, .ion-android-person-add:before, .ion-android-phone-landscape:before, .ion-android-phone-portrait:before, .ion-android-pin:before, .ion-android-plane:before, .ion-android-playstore:before, .ion-android-print:before, .ion-android-radio-button-off:before, .ion-android-radio-button-on:before, .ion-android-refresh:before, .ion-android-remove:before, .ion-android-remove-circle:before, .ion-android-restaurant:before, .ion-android-sad:before, .ion-android-search:before, .ion-android-send:before, .ion-android-settings:before, .ion-android-share:before, .ion-android-share-alt:before, .ion-android-star:before, .ion-android-star-half:before, .ion-android-star-outline:before, .ion-android-stopwatch:before, .ion-android-subway:before, .ion-android-sunny:before, .ion-android-sync:before, .ion-android-textsms:before, .ion-android-time:before, .ion-android-train:before, .ion-android-unlock:before, .ion-android-upload:before, .ion-android-volume-down:before, .ion-android-volume-mute:before, .ion-android-volume-off:before, .ion-android-volume-up:before, .ion-android-walk:before, .ion-android-warning:before, .ion-android-watch:before, .ion-android-wifi:before, .ion-aperture:before, .ion-archive:before, .ion-arrow-down-a:before, .ion-arrow-down-b:before, .ion-arrow-down-c:before, .ion-arrow-expand:before, .ion-arrow-graph-down-left:before, .ion-arrow-graph-down-right:before, .ion-arrow-graph-up-left:before, .ion-arrow-graph-up-right:before, .ion-arrow-left-a:before, .ion-arrow-left-b:before, .ion-arrow-left-c:before, .ion-arrow-move:before, .ion-arrow-resize:before, .ion-arrow-return-left:before, .ion-arrow-return-right:before, .ion-arrow-right-a:before, .ion-arrow-right-b:before, .ion-arrow-right-c:before, .ion-arrow-shrink:before, .ion-arrow-swap:before, .ion-arrow-up-a:before, .ion-arrow-up-b:before, .ion-arrow-up-c:before, .ion-asterisk:before, .ion-at:before, .ion-backspace:before, .ion-backspace-outline:before, .ion-bag:before, .ion-battery-charging:before, .ion-battery-empty:before, .ion-battery-full:before, .ion-battery-half:before, .ion-battery-low:before, .ion-beaker:before, .ion-beer:before, .ion-bluetooth:before, .ion-bonfire:before, .ion-bookmark:before, .ion-bowtie:before, .ion-briefcase:before, .ion-bug:before, .ion-calculator:before, .ion-calendar:before, .ion-camera:before, .ion-card:before, .ion-cash:before, .ion-chatbox:before, .ion-chatbox-working:before, .ion-chatboxes:before, .ion-chatbubble:before, .ion-chatbubble-working:before, .ion-chatbubbles:before, .ion-checkmark:before, .ion-checkmark-circled:before, .ion-checkmark-round:before, .ion-chevron-down:before, .ion-chevron-left:before, .ion-chevron-right:before, .ion-chevron-up:before, .ion-clipboard:before, .ion-clock:before, .ion-close:before, .ion-close-circled:before, .ion-close-round:before, .ion-closed-captioning:before, .ion-cloud:before, .ion-code:before, .ion-code-download:before, .ion-code-working:before, .ion-coffee:before, .ion-compass:before, .ion-compose:before, .ion-connection-bars:before, .ion-contrast:before, .ion-crop:before, .ion-cube:before, .ion-disc:before, .ion-document:before, .ion-document-text:before, .ion-drag:before, .ion-earth:before, .ion-easel:before, .ion-edit:before, .ion-egg:before, .ion-eject:before, .ion-email:before, .ion-email-unread:before, .ion-erlenmeyer-flask:before, .ion-erlenmeyer-flask-bubbles:before, .ion-eye:before, .ion-eye-disabled:before, .ion-female:before, .ion-filing:before, .ion-film-marker:before, .ion-fireball:before, .ion-flag:before, .ion-flame:before, .ion-flash:before, .ion-flash-off:before, .ion-folder:before, .ion-fork:before, .ion-fork-repo:before, .ion-forward:before, .ion-funnel:before, .ion-gear-a:before, .ion-gear-b:before, .ion-grid:before, .ion-hammer:before, .ion-happy:before, .ion-happy-outline:before, .ion-headphone:before, .ion-heart:before, .ion-heart-broken:before, .ion-help:before, .ion-help-buoy:before, .ion-help-circled:before, .ion-home:before, .ion-icecream:before, .ion-image:before, .ion-images:before, .ion-information:before, .ion-information-circled:before, .ion-ionic:before, .ion-ios-alarm:before, .ion-ios-alarm-outline:before, .ion-ios-albums:before, .ion-ios-albums-outline:before, .ion-ios-americanfootball:before, .ion-ios-americanfootball-outline:before, .ion-ios-analytics:before, .ion-ios-analytics-outline:before, .ion-ios-arrow-back:before, .ion-ios-arrow-down:before, .ion-ios-arrow-forward:before, .ion-ios-arrow-left:before, .ion-ios-arrow-right:before, .ion-ios-arrow-thin-down:before, .ion-ios-arrow-thin-left:before, .ion-ios-arrow-thin-right:before, .ion-ios-arrow-thin-up:before, .ion-ios-arrow-up:before, .ion-ios-at:before, .ion-ios-at-outline:before, .ion-ios-barcode:before, .ion-ios-barcode-outline:before, .ion-ios-baseball:before, .ion-ios-baseball-outline:before, .ion-ios-basketball:before, .ion-ios-basketball-outline:before, .ion-ios-bell:before, .ion-ios-bell-outline:before, .ion-ios-body:before, .ion-ios-body-outline:before, .ion-ios-bolt:before, .ion-ios-bolt-outline:before, .ion-ios-book:before, .ion-ios-book-outline:before, .ion-ios-bookmarks:before, .ion-ios-bookmarks-outline:before, .ion-ios-box:before, .ion-ios-box-outline:before, .ion-ios-briefcase:before, .ion-ios-briefcase-outline:before, .ion-ios-browsers:before, .ion-ios-browsers-outline:before, .ion-ios-calculator:before, .ion-ios-calculator-outline:before, .ion-ios-calendar:before, .ion-ios-calendar-outline:before, .ion-ios-camera:before, .ion-ios-camera-outline:before, .ion-ios-cart:before, .ion-ios-cart-outline:before, .ion-ios-chatboxes:before, .ion-ios-chatboxes-outline:before, .ion-ios-chatbubble:before, .ion-ios-chatbubble-outline:before, .ion-ios-checkmark:before, .ion-ios-checkmark-empty:before, .ion-ios-checkmark-outline:before, .ion-ios-circle-filled:before, .ion-ios-circle-outline:before, .ion-ios-clock:before, .ion-ios-clock-outline:before, .ion-ios-close:before, .ion-ios-close-empty:before, .ion-ios-close-outline:before, .ion-ios-cloud:before, .ion-ios-cloud-download:before, .ion-ios-cloud-download-outline:before, .ion-ios-cloud-outline:before, .ion-ios-cloud-upload:before, .ion-ios-cloud-upload-outline:before, .ion-ios-cloudy:before, .ion-ios-cloudy-night:before, .ion-ios-cloudy-night-outline:before, .ion-ios-cloudy-outline:before, .ion-ios-cog:before, .ion-ios-cog-outline:before, .ion-ios-color-filter:before, .ion-ios-color-filter-outline:before, .ion-ios-color-wand:before, .ion-ios-color-wand-outline:before, .ion-ios-compose:before, .ion-ios-compose-outline:before, .ion-ios-contact:before, .ion-ios-contact-outline:before, .ion-ios-copy:before, .ion-ios-copy-outline:before, .ion-ios-crop:before, .ion-ios-crop-strong:before, .ion-ios-download:before, .ion-ios-download-outline:before, .ion-ios-drag:before, .ion-ios-email:before, .ion-ios-email-outline:before, .ion-ios-eye:before, .ion-ios-eye-outline:before, .ion-ios-fastforward:before, .ion-ios-fastforward-outline:before, .ion-ios-filing:before, .ion-ios-filing-outline:before, .ion-ios-film:before, .ion-ios-film-outline:before, .ion-ios-flag:before, .ion-ios-flag-outline:before, .ion-ios-flame:before, .ion-ios-flame-outline:before, .ion-ios-flask:before, .ion-ios-flask-outline:before, .ion-ios-flower:before, .ion-ios-flower-outline:before, .ion-ios-folder:before, .ion-ios-folder-outline:before, .ion-ios-football:before, .ion-ios-football-outline:before, .ion-ios-game-controller-a:before, .ion-ios-game-controller-a-outline:before, .ion-ios-game-controller-b:before, .ion-ios-game-controller-b-outline:before, .ion-ios-gear:before, .ion-ios-gear-outline:before, .ion-ios-glasses:before, .ion-ios-glasses-outline:before, .ion-ios-grid-view:before, .ion-ios-grid-view-outline:before, .ion-ios-heart:before, .ion-ios-heart-outline:before, .ion-ios-help:before, .ion-ios-help-empty:before, .ion-ios-help-outline:before, .ion-ios-home:before, .ion-ios-home-outline:before, .ion-ios-infinite:before, .ion-ios-infinite-outline:before, .ion-ios-information:before, .ion-ios-information-empty:before, .ion-ios-information-outline:before, .ion-ios-ionic-outline:before, .ion-ios-keypad:before, .ion-ios-keypad-outline:before, .ion-ios-lightbulb:before, .ion-ios-lightbulb-outline:before, .ion-ios-list:before, .ion-ios-list-outline:before, .ion-ios-location:before, .ion-ios-location-outline:before, .ion-ios-locked:before, .ion-ios-locked-outline:before, .ion-ios-loop:before, .ion-ios-loop-strong:before, .ion-ios-medical:before, .ion-ios-medical-outline:before, .ion-ios-medkit:before, .ion-ios-medkit-outline:before, .ion-ios-mic:before, .ion-ios-mic-off:before, .ion-ios-mic-outline:before, .ion-ios-minus:before, .ion-ios-minus-empty:before, .ion-ios-minus-outline:before, .ion-ios-monitor:before, .ion-ios-monitor-outline:before, .ion-ios-moon:before, .ion-ios-moon-outline:before, .ion-ios-more:before, .ion-ios-more-outline:before, .ion-ios-musical-note:before, .ion-ios-musical-notes:before, .ion-ios-navigate:before, .ion-ios-navigate-outline:before, .ion-ios-nutrition:before, .ion-ios-nutrition-outline:before, .ion-ios-paper:before, .ion-ios-paper-outline:before, .ion-ios-paperplane:before, .ion-ios-paperplane-outline:before, .ion-ios-partlysunny:before, .ion-ios-partlysunny-outline:before, .ion-ios-pause:before, .ion-ios-pause-outline:before, .ion-ios-paw:before, .ion-ios-paw-outline:before, .ion-ios-people:before, .ion-ios-people-outline:before, .ion-ios-person:before, .ion-ios-person-outline:before, .ion-ios-personadd:before, .ion-ios-personadd-outline:before, .ion-ios-photos:before, .ion-ios-photos-outline:before, .ion-ios-pie:before, .ion-ios-pie-outline:before, .ion-ios-pint:before, .ion-ios-pint-outline:before, .ion-ios-play:before, .ion-ios-play-outline:before, .ion-ios-plus:before, .ion-ios-plus-empty:before, .ion-ios-plus-outline:before, .ion-ios-pricetag:before, .ion-ios-pricetag-outline:before, .ion-ios-pricetags:before, .ion-ios-pricetags-outline:before, .ion-ios-printer:before, .ion-ios-printer-outline:before, .ion-ios-pulse:before, .ion-ios-pulse-strong:before, .ion-ios-rainy:before, .ion-ios-rainy-outline:before, .ion-ios-recording:before, .ion-ios-recording-outline:before, .ion-ios-redo:before, .ion-ios-redo-outline:before, .ion-ios-refresh:before, .ion-ios-refresh-empty:before, .ion-ios-refresh-outline:before, .ion-ios-reload:before, .ion-ios-reverse-camera:before, .ion-ios-reverse-camera-outline:before, .ion-ios-rewind:before, .ion-ios-rewind-outline:before, .ion-ios-rose:before, .ion-ios-rose-outline:before, .ion-ios-search:before, .ion-ios-search-strong:before, .ion-ios-settings:before, .ion-ios-settings-strong:before, .ion-ios-shuffle:before, .ion-ios-shuffle-strong:before, .ion-ios-skipbackward:before, .ion-ios-skipbackward-outline:before, .ion-ios-skipforward:before, .ion-ios-skipforward-outline:before, .ion-ios-snowy:before, .ion-ios-speedometer:before, .ion-ios-speedometer-outline:before, .ion-ios-star:before, .ion-ios-star-half:before, .ion-ios-star-outline:before, .ion-ios-stopwatch:before, .ion-ios-stopwatch-outline:before, .ion-ios-sunny:before, .ion-ios-sunny-outline:before, .ion-ios-telephone:before, .ion-ios-telephone-outline:before, .ion-ios-tennisball:before, .ion-ios-tennisball-outline:before, .ion-ios-thunderstorm:before, .ion-ios-thunderstorm-outline:before, .ion-ios-time:before, .ion-ios-time-outline:before, .ion-ios-timer:before, .ion-ios-timer-outline:before, .ion-ios-toggle:before, .ion-ios-toggle-outline:before, .ion-ios-trash:before, .ion-ios-trash-outline:before, .ion-ios-undo:before, .ion-ios-undo-outline:before, .ion-ios-unlocked:before, .ion-ios-unlocked-outline:before, .ion-ios-upload:before, .ion-ios-upload-outline:before, .ion-ios-videocam:before, .ion-ios-videocam-outline:before, .ion-ios-volume-high:before, .ion-ios-volume-low:before, .ion-ios-wineglass:before, .ion-ios-wineglass-outline:before, .ion-ios-world:before, .ion-ios-world-outline:before, .ion-ipad:before, .ion-iphone:before, .ion-ipod:before, .ion-jet:before, .ion-key:before, .ion-knife:before, .ion-laptop:before, .ion-leaf:before, .ion-levels:before, .ion-lightbulb:before, .ion-link:before, .ion-load-a:before, .ion-load-b:before, .ion-load-c:before, .ion-load-d:before, .ion-location:before, .ion-lock-combination:before, .ion-locked:before, .ion-log-in:before, .ion-log-out:before, .ion-loop:before, .ion-magnet:before, .ion-male:before, .ion-man:before, .ion-map:before, .ion-medkit:before, .ion-merge:before, .ion-mic-a:before, .ion-mic-b:before, .ion-mic-c:before, .ion-minus:before, .ion-minus-circled:before, .ion-minus-round:before, .ion-model-s:before, .ion-monitor:before, .ion-more:before, .ion-mouse:before, .ion-music-note:before, .ion-navicon:before, .ion-navicon-round:before, .ion-navigate:before, .ion-network:before, .ion-no-smoking:before, .ion-nuclear:before, .ion-outlet:before, .ion-paintbrush:before, .ion-paintbucket:before, .ion-paper-airplane:before, .ion-paperclip:before, .ion-pause:before, .ion-person:before, .ion-person-add:before, .ion-person-stalker:before, .ion-pie-graph:before, .ion-pin:before, .ion-pinpoint:before, .ion-pizza:before, .ion-plane:before, .ion-planet:before, .ion-play:before, .ion-playstation:before, .ion-plus:before, .ion-plus-circled:before, .ion-plus-round:before, .ion-podium:before, .ion-pound:before, .ion-power:before, .ion-pricetag:before, .ion-pricetags:before, .ion-printer:before, .ion-pull-request:before, .ion-qr-scanner:before, .ion-quote:before, .ion-radio-waves:before, .ion-record:before, .ion-refresh:before, .ion-reply:before, .ion-reply-all:before, .ion-ribbon-a:before, .ion-ribbon-b:before, .ion-sad:before, .ion-sad-outline:before, .ion-scissors:before, .ion-search:before, .ion-settings:before, .ion-share:before, .ion-shuffle:before, .ion-skip-backward:before, .ion-skip-forward:before, .ion-social-android:before, .ion-social-android-outline:before, .ion-social-angular:before, .ion-social-angular-outline:before, .ion-social-apple:before, .ion-social-apple-outline:before, .ion-social-bitcoin:before, .ion-social-bitcoin-outline:before, .ion-social-buffer:before, .ion-social-buffer-outline:before, .ion-social-chrome:before, .ion-social-chrome-outline:before, .ion-social-codepen:before, .ion-social-codepen-outline:before, .ion-social-css3:before, .ion-social-css3-outline:before, .ion-social-designernews:before, .ion-social-designernews-outline:before, .ion-social-dribbble:before, .ion-social-dribbble-outline:before, .ion-social-dropbox:before, .ion-social-dropbox-outline:before, .ion-social-euro:before, .ion-social-euro-outline:before, .ion-social-facebook:before, .ion-social-facebook-outline:before, .ion-social-foursquare:before, .ion-social-foursquare-outline:before, .ion-social-freebsd-devil:before, .ion-social-github:before, .ion-social-github-outline:before, .ion-social-google:before, .ion-social-google-outline:before, .ion-social-googleplus:before, .ion-social-googleplus-outline:before, .ion-social-hackernews:before, .ion-social-hackernews-outline:before, .ion-social-html5:before, .ion-social-html5-outline:before, .ion-social-instagram:before, .ion-social-instagram-outline:before, .ion-social-javascript:before, .ion-social-javascript-outline:before, .ion-social-linkedin:before, .ion-social-linkedin-outline:before, .ion-social-markdown:before, .ion-social-nodejs:before, .ion-social-octocat:before, .ion-social-pinterest:before, .ion-social-pinterest-outline:before, .ion-social-python:before, .ion-social-reddit:before, .ion-social-reddit-outline:before, .ion-social-rss:before, .ion-social-rss-outline:before, .ion-social-sass:before, .ion-social-skype:before, .ion-social-skype-outline:before, .ion-social-snapchat:before, .ion-social-snapchat-outline:before, .ion-social-tumblr:before, .ion-social-tumblr-outline:before, .ion-social-tux:before, .ion-social-twitch:before, .ion-social-twitch-outline:before, .ion-social-twitter:before, .ion-social-twitter-outline:before, .ion-social-usd:before, .ion-social-usd-outline:before, .ion-social-vimeo:before, .ion-social-vimeo-outline:before, .ion-social-whatsapp:before, .ion-social-whatsapp-outline:before, .ion-social-windows:before, .ion-social-windows-outline:before, .ion-social-wordpress:before, .ion-social-wordpress-outline:before, .ion-social-yahoo:before, .ion-social-yahoo-outline:before, .ion-social-yen:before, .ion-social-yen-outline:before, .ion-social-youtube:before, .ion-social-youtube-outline:before, .ion-soup-can:before, .ion-soup-can-outline:before, .ion-speakerphone:before, .ion-speedometer:before, .ion-spoon:before, .ion-star:before, .ion-stats-bars:before, .ion-steam:before, .ion-stop:before, .ion-thermometer:before, .ion-thumbsdown:before, .ion-thumbsup:before, .ion-toggle:before, .ion-toggle-filled:before, .ion-transgender:before, .ion-trash-a:before, .ion-trash-b:before, .ion-trophy:before, .ion-tshirt:before, .ion-tshirt-outline:before, .ion-umbrella:before, .ion-university:before, .ion-unlocked:before, .ion-upload:before, .ion-usb:before, .ion-videocamera:before, .ion-volume-high:before, .ion-volume-low:before, .ion-volume-medium:before, .ion-volume-mute:before, .ion-wand:before, .ion-waterdrop:before, .ion-wifi:before, .ion-wineglass:before, .ion-woman:before, .ion-wrench:before, .ion-xbox:before { - display: inline-block; - font-family: "Ionicons"; - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - text-rendering: auto; - line-height: 1; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.ion-alert:before { - content: "\f101"; -} - -.ion-alert-circled:before { - content: "\f100"; -} - -.ion-android-add:before { - content: "\f2c7"; -} - -.ion-android-add-circle:before { - content: "\f359"; -} - -.ion-android-alarm-clock:before { - content: "\f35a"; -} - -.ion-android-alert:before { - content: "\f35b"; -} - -.ion-android-apps:before { - content: "\f35c"; -} - -.ion-android-archive:before { - content: "\f2c9"; -} - -.ion-android-arrow-back:before { - content: "\f2ca"; -} - -.ion-android-arrow-down:before { - content: "\f35d"; -} - -.ion-android-arrow-dropdown:before { - content: "\f35f"; -} - -.ion-android-arrow-dropdown-circle:before { - content: "\f35e"; -} - -.ion-android-arrow-dropleft:before { - content: "\f361"; -} - -.ion-android-arrow-dropleft-circle:before { - content: "\f360"; -} - -.ion-android-arrow-dropright:before { - content: "\f363"; -} - -.ion-android-arrow-dropright-circle:before { - content: "\f362"; -} - -.ion-android-arrow-dropup:before { - content: "\f365"; -} - -.ion-android-arrow-dropup-circle:before { - content: "\f364"; -} - -.ion-android-arrow-forward:before { - content: "\f30f"; -} - -.ion-android-arrow-up:before { - content: "\f366"; -} - -.ion-android-attach:before { - content: "\f367"; -} - -.ion-android-bar:before { - content: "\f368"; -} - -.ion-android-bicycle:before { - content: "\f369"; -} - -.ion-android-boat:before { - content: "\f36a"; -} - -.ion-android-bookmark:before { - content: "\f36b"; -} - -.ion-android-bulb:before { - content: "\f36c"; -} - -.ion-android-bus:before { - content: "\f36d"; -} - -.ion-android-calendar:before { - content: "\f2d1"; -} - -.ion-android-call:before { - content: "\f2d2"; -} - -.ion-android-camera:before { - content: "\f2d3"; -} - -.ion-android-cancel:before { - content: "\f36e"; -} - -.ion-android-car:before { - content: "\f36f"; -} - -.ion-android-cart:before { - content: "\f370"; -} - -.ion-android-chat:before { - content: "\f2d4"; -} - -.ion-android-checkbox:before { - content: "\f374"; -} - -.ion-android-checkbox-blank:before { - content: "\f371"; -} - -.ion-android-checkbox-outline:before { - content: "\f373"; -} - -.ion-android-checkbox-outline-blank:before { - content: "\f372"; -} - -.ion-android-checkmark-circle:before { - content: "\f375"; -} - -.ion-android-clipboard:before { - content: "\f376"; -} - -.ion-android-close:before { - content: "\f2d7"; -} - -.ion-android-cloud:before { - content: "\f37a"; -} - -.ion-android-cloud-circle:before { - content: "\f377"; -} - -.ion-android-cloud-done:before { - content: "\f378"; -} - -.ion-android-cloud-outline:before { - content: "\f379"; -} - -.ion-android-color-palette:before { - content: "\f37b"; -} - -.ion-android-compass:before { - content: "\f37c"; -} - -.ion-android-contact:before { - content: "\f2d8"; -} - -.ion-android-contacts:before { - content: "\f2d9"; -} - -.ion-android-contract:before { - content: "\f37d"; -} - -.ion-android-create:before { - content: "\f37e"; -} - -.ion-android-delete:before { - content: "\f37f"; -} - -.ion-android-desktop:before { - content: "\f380"; -} - -.ion-android-document:before { - content: "\f381"; -} - -.ion-android-done:before { - content: "\f383"; -} - -.ion-android-done-all:before { - content: "\f382"; -} - -.ion-android-download:before { - content: "\f2dd"; -} - -.ion-android-drafts:before { - content: "\f384"; -} - -.ion-android-exit:before { - content: "\f385"; -} - -.ion-android-expand:before { - content: "\f386"; -} - -.ion-android-favorite:before { - content: "\f388"; -} - -.ion-android-favorite-outline:before { - content: "\f387"; -} - -.ion-android-film:before { - content: "\f389"; -} - -.ion-android-folder:before { - content: "\f2e0"; -} - -.ion-android-folder-open:before { - content: "\f38a"; -} - -.ion-android-funnel:before { - content: "\f38b"; -} - -.ion-android-globe:before { - content: "\f38c"; -} - -.ion-android-hand:before { - content: "\f2e3"; -} - -.ion-android-hangout:before { - content: "\f38d"; -} - -.ion-android-happy:before { - content: "\f38e"; -} - -.ion-android-home:before { - content: "\f38f"; -} - -.ion-android-image:before { - content: "\f2e4"; -} - -.ion-android-laptop:before { - content: "\f390"; -} - -.ion-android-list:before { - content: "\f391"; -} - -.ion-android-locate:before { - content: "\f2e9"; -} - -.ion-android-lock:before { - content: "\f392"; -} - -.ion-android-mail:before { - content: "\f2eb"; -} - -.ion-android-map:before { - content: "\f393"; -} - -.ion-android-menu:before { - content: "\f394"; -} - -.ion-android-microphone:before { - content: "\f2ec"; -} - -.ion-android-microphone-off:before { - content: "\f395"; -} - -.ion-android-more-horizontal:before { - content: "\f396"; -} - -.ion-android-more-vertical:before { - content: "\f397"; -} - -.ion-android-navigate:before { - content: "\f398"; -} - -.ion-android-notifications:before { - content: "\f39b"; -} - -.ion-android-notifications-none:before { - content: "\f399"; -} - -.ion-android-notifications-off:before { - content: "\f39a"; -} - -.ion-android-open:before { - content: "\f39c"; -} - -.ion-android-options:before { - content: "\f39d"; -} - -.ion-android-people:before { - content: "\f39e"; -} - -.ion-android-person:before { - content: "\f3a0"; -} - -.ion-android-person-add:before { - content: "\f39f"; -} - -.ion-android-phone-landscape:before { - content: "\f3a1"; -} - -.ion-android-phone-portrait:before { - content: "\f3a2"; -} - -.ion-android-pin:before { - content: "\f3a3"; -} - -.ion-android-plane:before { - content: "\f3a4"; -} - -.ion-android-playstore:before { - content: "\f2f0"; -} - -.ion-android-print:before { - content: "\f3a5"; -} - -.ion-android-radio-button-off:before { - content: "\f3a6"; -} - -.ion-android-radio-button-on:before { - content: "\f3a7"; -} - -.ion-android-refresh:before { - content: "\f3a8"; -} - -.ion-android-remove:before { - content: "\f2f4"; -} - -.ion-android-remove-circle:before { - content: "\f3a9"; -} - -.ion-android-restaurant:before { - content: "\f3aa"; -} - -.ion-android-sad:before { - content: "\f3ab"; -} - -.ion-android-search:before { - content: "\f2f5"; -} - -.ion-android-send:before { - content: "\f2f6"; -} - -.ion-android-settings:before { - content: "\f2f7"; -} - -.ion-android-share:before { - content: "\f2f8"; -} - -.ion-android-share-alt:before { - content: "\f3ac"; -} - -.ion-android-star:before { - content: "\f2fc"; -} - -.ion-android-star-half:before { - content: "\f3ad"; -} - -.ion-android-star-outline:before { - content: "\f3ae"; -} - -.ion-android-stopwatch:before { - content: "\f2fd"; -} - -.ion-android-subway:before { - content: "\f3af"; -} - -.ion-android-sunny:before { - content: "\f3b0"; -} - -.ion-android-sync:before { - content: "\f3b1"; -} - -.ion-android-textsms:before { - content: "\f3b2"; -} - -.ion-android-time:before { - content: "\f3b3"; -} - -.ion-android-train:before { - content: "\f3b4"; -} - -.ion-android-unlock:before { - content: "\f3b5"; -} - -.ion-android-upload:before { - content: "\f3b6"; -} - -.ion-android-volume-down:before { - content: "\f3b7"; -} - -.ion-android-volume-mute:before { - content: "\f3b8"; -} - -.ion-android-volume-off:before { - content: "\f3b9"; -} - -.ion-android-volume-up:before { - content: "\f3ba"; -} - -.ion-android-walk:before { - content: "\f3bb"; -} - -.ion-android-warning:before { - content: "\f3bc"; -} - -.ion-android-watch:before { - content: "\f3bd"; -} - -.ion-android-wifi:before { - content: "\f305"; -} - -.ion-aperture:before { - content: "\f313"; -} - -.ion-archive:before { - content: "\f102"; -} - -.ion-arrow-down-a:before { - content: "\f103"; -} - -.ion-arrow-down-b:before { - content: "\f104"; -} - -.ion-arrow-down-c:before { - content: "\f105"; -} - -.ion-arrow-expand:before { - content: "\f25e"; -} - -.ion-arrow-graph-down-left:before { - content: "\f25f"; -} - -.ion-arrow-graph-down-right:before { - content: "\f260"; -} - -.ion-arrow-graph-up-left:before { - content: "\f261"; -} - -.ion-arrow-graph-up-right:before { - content: "\f262"; -} - -.ion-arrow-left-a:before { - content: "\f106"; -} - -.ion-arrow-left-b:before { - content: "\f107"; -} - -.ion-arrow-left-c:before { - content: "\f108"; -} - -.ion-arrow-move:before { - content: "\f263"; -} - -.ion-arrow-resize:before { - content: "\f264"; -} - -.ion-arrow-return-left:before { - content: "\f265"; -} - -.ion-arrow-return-right:before { - content: "\f266"; -} - -.ion-arrow-right-a:before { - content: "\f109"; -} - -.ion-arrow-right-b:before { - content: "\f10a"; -} - -.ion-arrow-right-c:before { - content: "\f10b"; -} - -.ion-arrow-shrink:before { - content: "\f267"; -} - -.ion-arrow-swap:before { - content: "\f268"; -} - -.ion-arrow-up-a:before { - content: "\f10c"; -} - -.ion-arrow-up-b:before { - content: "\f10d"; -} - -.ion-arrow-up-c:before { - content: "\f10e"; -} - -.ion-asterisk:before { - content: "\f314"; -} - -.ion-at:before { - content: "\f10f"; -} - -.ion-backspace:before { - content: "\f3bf"; -} - -.ion-backspace-outline:before { - content: "\f3be"; -} - -.ion-bag:before { - content: "\f110"; -} - -.ion-battery-charging:before { - content: "\f111"; -} - -.ion-battery-empty:before { - content: "\f112"; -} - -.ion-battery-full:before { - content: "\f113"; -} - -.ion-battery-half:before { - content: "\f114"; -} - -.ion-battery-low:before { - content: "\f115"; -} - -.ion-beaker:before { - content: "\f269"; -} - -.ion-beer:before { - content: "\f26a"; -} - -.ion-bluetooth:before { - content: "\f116"; -} - -.ion-bonfire:before { - content: "\f315"; -} - -.ion-bookmark:before { - content: "\f26b"; -} - -.ion-bowtie:before { - content: "\f3c0"; -} - -.ion-briefcase:before { - content: "\f26c"; -} - -.ion-bug:before { - content: "\f2be"; -} - -.ion-calculator:before { - content: "\f26d"; -} - -.ion-calendar:before { - content: "\f117"; -} - -.ion-camera:before { - content: "\f118"; -} - -.ion-card:before { - content: "\f119"; -} - -.ion-cash:before { - content: "\f316"; -} - -.ion-chatbox:before { - content: "\f11b"; -} - -.ion-chatbox-working:before { - content: "\f11a"; -} - -.ion-chatboxes:before { - content: "\f11c"; -} - -.ion-chatbubble:before { - content: "\f11e"; -} - -.ion-chatbubble-working:before { - content: "\f11d"; -} - -.ion-chatbubbles:before { - content: "\f11f"; -} - -.ion-checkmark:before { - content: "\f122"; -} - -.ion-checkmark-circled:before { - content: "\f120"; -} - -.ion-checkmark-round:before { - content: "\f121"; -} - -.ion-chevron-down:before { - content: "\f123"; -} - -.ion-chevron-left:before { - content: "\f124"; -} - -.ion-chevron-right:before { - content: "\f125"; -} - -.ion-chevron-up:before { - content: "\f126"; -} - -.ion-clipboard:before { - content: "\f127"; -} - -.ion-clock:before { - content: "\f26e"; -} - -.ion-close:before { - content: "\f12a"; -} - -.ion-close-circled:before { - content: "\f128"; -} - -.ion-close-round:before { - content: "\f129"; -} - -.ion-closed-captioning:before { - content: "\f317"; -} - -.ion-cloud:before { - content: "\f12b"; -} - -.ion-code:before { - content: "\f271"; -} - -.ion-code-download:before { - content: "\f26f"; -} - -.ion-code-working:before { - content: "\f270"; -} - -.ion-coffee:before { - content: "\f272"; -} - -.ion-compass:before { - content: "\f273"; -} - -.ion-compose:before { - content: "\f12c"; -} - -.ion-connection-bars:before { - content: "\f274"; -} - -.ion-contrast:before { - content: "\f275"; -} - -.ion-crop:before { - content: "\f3c1"; -} - -.ion-cube:before { - content: "\f318"; -} - -.ion-disc:before { - content: "\f12d"; -} - -.ion-document:before { - content: "\f12f"; -} - -.ion-document-text:before { - content: "\f12e"; -} - -.ion-drag:before { - content: "\f130"; -} - -.ion-earth:before { - content: "\f276"; -} - -.ion-easel:before { - content: "\f3c2"; -} - -.ion-edit:before { - content: "\f2bf"; -} - -.ion-egg:before { - content: "\f277"; -} - -.ion-eject:before { - content: "\f131"; -} - -.ion-email:before { - content: "\f132"; -} - -.ion-email-unread:before { - content: "\f3c3"; -} - -.ion-erlenmeyer-flask:before { - content: "\f3c5"; -} - -.ion-erlenmeyer-flask-bubbles:before { - content: "\f3c4"; -} - -.ion-eye:before { - content: "\f133"; -} - -.ion-eye-disabled:before { - content: "\f306"; -} - -.ion-female:before { - content: "\f278"; -} - -.ion-filing:before { - content: "\f134"; -} - -.ion-film-marker:before { - content: "\f135"; -} - -.ion-fireball:before { - content: "\f319"; -} - -.ion-flag:before { - content: "\f279"; -} - -.ion-flame:before { - content: "\f31a"; -} - -.ion-flash:before { - content: "\f137"; -} - -.ion-flash-off:before { - content: "\f136"; -} - -.ion-folder:before { - content: "\f139"; -} - -.ion-fork:before { - content: "\f27a"; -} - -.ion-fork-repo:before { - content: "\f2c0"; -} - -.ion-forward:before { - content: "\f13a"; -} - -.ion-funnel:before { - content: "\f31b"; -} - -.ion-gear-a:before { - content: "\f13d"; -} - -.ion-gear-b:before { - content: "\f13e"; -} - -.ion-grid:before { - content: "\f13f"; -} - -.ion-hammer:before { - content: "\f27b"; -} - -.ion-happy:before { - content: "\f31c"; -} - -.ion-happy-outline:before { - content: "\f3c6"; -} - -.ion-headphone:before { - content: "\f140"; -} - -.ion-heart:before { - content: "\f141"; -} - -.ion-heart-broken:before { - content: "\f31d"; -} - -.ion-help:before { - content: "\f143"; -} - -.ion-help-buoy:before { - content: "\f27c"; -} - -.ion-help-circled:before { - content: "\f142"; -} - -.ion-home:before { - content: "\f144"; -} - -.ion-icecream:before { - content: "\f27d"; -} - -.ion-image:before { - content: "\f147"; -} - -.ion-images:before { - content: "\f148"; -} - -.ion-information:before { - content: "\f14a"; -} - -.ion-information-circled:before { - content: "\f149"; -} - -.ion-ionic:before { - content: "\f14b"; -} - -.ion-ios-alarm:before { - content: "\f3c8"; -} - -.ion-ios-alarm-outline:before { - content: "\f3c7"; -} - -.ion-ios-albums:before { - content: "\f3ca"; -} - -.ion-ios-albums-outline:before { - content: "\f3c9"; -} - -.ion-ios-americanfootball:before { - content: "\f3cc"; -} - -.ion-ios-americanfootball-outline:before { - content: "\f3cb"; -} - -.ion-ios-analytics:before { - content: "\f3ce"; -} - -.ion-ios-analytics-outline:before { - content: "\f3cd"; -} - -.ion-ios-arrow-back:before { - content: "\f3cf"; -} - -.ion-ios-arrow-down:before { - content: "\f3d0"; -} - -.ion-ios-arrow-forward:before { - content: "\f3d1"; -} - -.ion-ios-arrow-left:before { - content: "\f3d2"; -} - -.ion-ios-arrow-right:before { - content: "\f3d3"; -} - -.ion-ios-arrow-thin-down:before { - content: "\f3d4"; -} - -.ion-ios-arrow-thin-left:before { - content: "\f3d5"; -} - -.ion-ios-arrow-thin-right:before { - content: "\f3d6"; -} - -.ion-ios-arrow-thin-up:before { - content: "\f3d7"; -} - -.ion-ios-arrow-up:before { - content: "\f3d8"; -} - -.ion-ios-at:before { - content: "\f3da"; -} - -.ion-ios-at-outline:before { - content: "\f3d9"; -} - -.ion-ios-barcode:before { - content: "\f3dc"; -} - -.ion-ios-barcode-outline:before { - content: "\f3db"; -} - -.ion-ios-baseball:before { - content: "\f3de"; -} - -.ion-ios-baseball-outline:before { - content: "\f3dd"; -} - -.ion-ios-basketball:before { - content: "\f3e0"; -} - -.ion-ios-basketball-outline:before { - content: "\f3df"; -} - -.ion-ios-bell:before { - content: "\f3e2"; -} - -.ion-ios-bell-outline:before { - content: "\f3e1"; -} - -.ion-ios-body:before { - content: "\f3e4"; -} - -.ion-ios-body-outline:before { - content: "\f3e3"; -} - -.ion-ios-bolt:before { - content: "\f3e6"; -} - -.ion-ios-bolt-outline:before { - content: "\f3e5"; -} - -.ion-ios-book:before { - content: "\f3e8"; -} - -.ion-ios-book-outline:before { - content: "\f3e7"; -} - -.ion-ios-bookmarks:before { - content: "\f3ea"; -} - -.ion-ios-bookmarks-outline:before { - content: "\f3e9"; -} - -.ion-ios-box:before { - content: "\f3ec"; -} - -.ion-ios-box-outline:before { - content: "\f3eb"; -} - -.ion-ios-briefcase:before { - content: "\f3ee"; -} - -.ion-ios-briefcase-outline:before { - content: "\f3ed"; -} - -.ion-ios-browsers:before { - content: "\f3f0"; -} - -.ion-ios-browsers-outline:before { - content: "\f3ef"; -} - -.ion-ios-calculator:before { - content: "\f3f2"; -} - -.ion-ios-calculator-outline:before { - content: "\f3f1"; -} - -.ion-ios-calendar:before { - content: "\f3f4"; -} - -.ion-ios-calendar-outline:before { - content: "\f3f3"; -} - -.ion-ios-camera:before { - content: "\f3f6"; -} - -.ion-ios-camera-outline:before { - content: "\f3f5"; -} - -.ion-ios-cart:before { - content: "\f3f8"; -} - -.ion-ios-cart-outline:before { - content: "\f3f7"; -} - -.ion-ios-chatboxes:before { - content: "\f3fa"; -} - -.ion-ios-chatboxes-outline:before { - content: "\f3f9"; -} - -.ion-ios-chatbubble:before { - content: "\f3fc"; -} - -.ion-ios-chatbubble-outline:before { - content: "\f3fb"; -} - -.ion-ios-checkmark:before { - content: "\f3ff"; -} - -.ion-ios-checkmark-empty:before { - content: "\f3fd"; -} - -.ion-ios-checkmark-outline:before { - content: "\f3fe"; -} - -.ion-ios-circle-filled:before { - content: "\f400"; -} - -.ion-ios-circle-outline:before { - content: "\f401"; -} - -.ion-ios-clock:before { - content: "\f403"; -} - -.ion-ios-clock-outline:before { - content: "\f402"; -} - -.ion-ios-close:before { - content: "\f406"; -} - -.ion-ios-close-empty:before { - content: "\f404"; -} - -.ion-ios-close-outline:before { - content: "\f405"; -} - -.ion-ios-cloud:before { - content: "\f40c"; -} - -.ion-ios-cloud-download:before { - content: "\f408"; -} - -.ion-ios-cloud-download-outline:before { - content: "\f407"; -} - -.ion-ios-cloud-outline:before { - content: "\f409"; -} - -.ion-ios-cloud-upload:before { - content: "\f40b"; -} - -.ion-ios-cloud-upload-outline:before { - content: "\f40a"; -} - -.ion-ios-cloudy:before { - content: "\f410"; -} - -.ion-ios-cloudy-night:before { - content: "\f40e"; -} - -.ion-ios-cloudy-night-outline:before { - content: "\f40d"; -} - -.ion-ios-cloudy-outline:before { - content: "\f40f"; -} - -.ion-ios-cog:before { - content: "\f412"; -} - -.ion-ios-cog-outline:before { - content: "\f411"; -} - -.ion-ios-color-filter:before { - content: "\f414"; -} - -.ion-ios-color-filter-outline:before { - content: "\f413"; -} - -.ion-ios-color-wand:before { - content: "\f416"; -} - -.ion-ios-color-wand-outline:before { - content: "\f415"; -} - -.ion-ios-compose:before { - content: "\f418"; -} - -.ion-ios-compose-outline:before { - content: "\f417"; -} - -.ion-ios-contact:before { - content: "\f41a"; -} - -.ion-ios-contact-outline:before { - content: "\f419"; -} - -.ion-ios-copy:before { - content: "\f41c"; -} - -.ion-ios-copy-outline:before { - content: "\f41b"; -} - -.ion-ios-crop:before { - content: "\f41e"; -} - -.ion-ios-crop-strong:before { - content: "\f41d"; -} - -.ion-ios-download:before { - content: "\f420"; -} - -.ion-ios-download-outline:before { - content: "\f41f"; -} - -.ion-ios-drag:before { - content: "\f421"; -} - -.ion-ios-email:before { - content: "\f423"; -} - -.ion-ios-email-outline:before { - content: "\f422"; -} - -.ion-ios-eye:before { - content: "\f425"; -} - -.ion-ios-eye-outline:before { - content: "\f424"; -} - -.ion-ios-fastforward:before { - content: "\f427"; -} - -.ion-ios-fastforward-outline:before { - content: "\f426"; -} - -.ion-ios-filing:before { - content: "\f429"; -} - -.ion-ios-filing-outline:before { - content: "\f428"; -} - -.ion-ios-film:before { - content: "\f42b"; -} - -.ion-ios-film-outline:before { - content: "\f42a"; -} - -.ion-ios-flag:before { - content: "\f42d"; -} - -.ion-ios-flag-outline:before { - content: "\f42c"; -} - -.ion-ios-flame:before { - content: "\f42f"; -} - -.ion-ios-flame-outline:before { - content: "\f42e"; -} - -.ion-ios-flask:before { - content: "\f431"; -} - -.ion-ios-flask-outline:before { - content: "\f430"; -} - -.ion-ios-flower:before { - content: "\f433"; -} - -.ion-ios-flower-outline:before { - content: "\f432"; -} - -.ion-ios-folder:before { - content: "\f435"; -} - -.ion-ios-folder-outline:before { - content: "\f434"; -} - -.ion-ios-football:before { - content: "\f437"; -} - -.ion-ios-football-outline:before { - content: "\f436"; -} - -.ion-ios-game-controller-a:before { - content: "\f439"; -} - -.ion-ios-game-controller-a-outline:before { - content: "\f438"; -} - -.ion-ios-game-controller-b:before { - content: "\f43b"; -} - -.ion-ios-game-controller-b-outline:before { - content: "\f43a"; -} - -.ion-ios-gear:before { - content: "\f43d"; -} - -.ion-ios-gear-outline:before { - content: "\f43c"; -} - -.ion-ios-glasses:before { - content: "\f43f"; -} - -.ion-ios-glasses-outline:before { - content: "\f43e"; -} - -.ion-ios-grid-view:before { - content: "\f441"; -} - -.ion-ios-grid-view-outline:before { - content: "\f440"; -} - -.ion-ios-heart:before { - content: "\f443"; -} - -.ion-ios-heart-outline:before { - content: "\f442"; -} - -.ion-ios-help:before { - content: "\f446"; -} - -.ion-ios-help-empty:before { - content: "\f444"; -} - -.ion-ios-help-outline:before { - content: "\f445"; -} - -.ion-ios-home:before { - content: "\f448"; -} - -.ion-ios-home-outline:before { - content: "\f447"; -} - -.ion-ios-infinite:before { - content: "\f44a"; -} - -.ion-ios-infinite-outline:before { - content: "\f449"; -} - -.ion-ios-information:before { - content: "\f44d"; -} - -.ion-ios-information-empty:before { - content: "\f44b"; -} - -.ion-ios-information-outline:before { - content: "\f44c"; -} - -.ion-ios-ionic-outline:before { - content: "\f44e"; -} - -.ion-ios-keypad:before { - content: "\f450"; -} - -.ion-ios-keypad-outline:before { - content: "\f44f"; -} - -.ion-ios-lightbulb:before { - content: "\f452"; -} - -.ion-ios-lightbulb-outline:before { - content: "\f451"; -} - -.ion-ios-list:before { - content: "\f454"; -} - -.ion-ios-list-outline:before { - content: "\f453"; -} - -.ion-ios-location:before { - content: "\f456"; -} - -.ion-ios-location-outline:before { - content: "\f455"; -} - -.ion-ios-locked:before { - content: "\f458"; -} - -.ion-ios-locked-outline:before { - content: "\f457"; -} - -.ion-ios-loop:before { - content: "\f45a"; -} - -.ion-ios-loop-strong:before { - content: "\f459"; -} - -.ion-ios-medical:before { - content: "\f45c"; -} - -.ion-ios-medical-outline:before { - content: "\f45b"; -} - -.ion-ios-medkit:before { - content: "\f45e"; -} - -.ion-ios-medkit-outline:before { - content: "\f45d"; -} - -.ion-ios-mic:before { - content: "\f461"; -} - -.ion-ios-mic-off:before { - content: "\f45f"; -} - -.ion-ios-mic-outline:before { - content: "\f460"; -} - -.ion-ios-minus:before { - content: "\f464"; -} - -.ion-ios-minus-empty:before { - content: "\f462"; -} - -.ion-ios-minus-outline:before { - content: "\f463"; -} - -.ion-ios-monitor:before { - content: "\f466"; -} - -.ion-ios-monitor-outline:before { - content: "\f465"; -} - -.ion-ios-moon:before { - content: "\f468"; -} - -.ion-ios-moon-outline:before { - content: "\f467"; -} - -.ion-ios-more:before { - content: "\f46a"; -} - -.ion-ios-more-outline:before { - content: "\f469"; -} - -.ion-ios-musical-note:before { - content: "\f46b"; -} - -.ion-ios-musical-notes:before { - content: "\f46c"; -} - -.ion-ios-navigate:before { - content: "\f46e"; -} - -.ion-ios-navigate-outline:before { - content: "\f46d"; -} - -.ion-ios-nutrition:before { - content: "\f470"; -} - -.ion-ios-nutrition-outline:before { - content: "\f46f"; -} - -.ion-ios-paper:before { - content: "\f472"; -} - -.ion-ios-paper-outline:before { - content: "\f471"; -} - -.ion-ios-paperplane:before { - content: "\f474"; -} - -.ion-ios-paperplane-outline:before { - content: "\f473"; -} - -.ion-ios-partlysunny:before { - content: "\f476"; -} - -.ion-ios-partlysunny-outline:before { - content: "\f475"; -} - -.ion-ios-pause:before { - content: "\f478"; -} - -.ion-ios-pause-outline:before { - content: "\f477"; -} - -.ion-ios-paw:before { - content: "\f47a"; -} - -.ion-ios-paw-outline:before { - content: "\f479"; -} - -.ion-ios-people:before { - content: "\f47c"; -} - -.ion-ios-people-outline:before { - content: "\f47b"; -} - -.ion-ios-person:before { - content: "\f47e"; -} - -.ion-ios-person-outline:before { - content: "\f47d"; -} - -.ion-ios-personadd:before { - content: "\f480"; -} - -.ion-ios-personadd-outline:before { - content: "\f47f"; -} - -.ion-ios-photos:before { - content: "\f482"; -} - -.ion-ios-photos-outline:before { - content: "\f481"; -} - -.ion-ios-pie:before { - content: "\f484"; -} - -.ion-ios-pie-outline:before { - content: "\f483"; -} - -.ion-ios-pint:before { - content: "\f486"; -} - -.ion-ios-pint-outline:before { - content: "\f485"; -} - -.ion-ios-play:before { - content: "\f488"; -} - -.ion-ios-play-outline:before { - content: "\f487"; -} - -.ion-ios-plus:before { - content: "\f48b"; -} - -.ion-ios-plus-empty:before { - content: "\f489"; -} - -.ion-ios-plus-outline:before { - content: "\f48a"; -} - -.ion-ios-pricetag:before { - content: "\f48d"; -} - -.ion-ios-pricetag-outline:before { - content: "\f48c"; -} - -.ion-ios-pricetags:before { - content: "\f48f"; -} - -.ion-ios-pricetags-outline:before { - content: "\f48e"; -} - -.ion-ios-printer:before { - content: "\f491"; -} - -.ion-ios-printer-outline:before { - content: "\f490"; -} - -.ion-ios-pulse:before { - content: "\f493"; -} - -.ion-ios-pulse-strong:before { - content: "\f492"; -} - -.ion-ios-rainy:before { - content: "\f495"; -} - -.ion-ios-rainy-outline:before { - content: "\f494"; -} - -.ion-ios-recording:before { - content: "\f497"; -} - -.ion-ios-recording-outline:before { - content: "\f496"; -} - -.ion-ios-redo:before { - content: "\f499"; -} - -.ion-ios-redo-outline:before { - content: "\f498"; -} - -.ion-ios-refresh:before { - content: "\f49c"; -} - -.ion-ios-refresh-empty:before { - content: "\f49a"; -} - -.ion-ios-refresh-outline:before { - content: "\f49b"; -} - -.ion-ios-reload:before { - content: "\f49d"; -} - -.ion-ios-reverse-camera:before { - content: "\f49f"; -} - -.ion-ios-reverse-camera-outline:before { - content: "\f49e"; -} - -.ion-ios-rewind:before { - content: "\f4a1"; -} - -.ion-ios-rewind-outline:before { - content: "\f4a0"; -} - -.ion-ios-rose:before { - content: "\f4a3"; -} - -.ion-ios-rose-outline:before { - content: "\f4a2"; -} - -.ion-ios-search:before { - content: "\f4a5"; -} - -.ion-ios-search-strong:before { - content: "\f4a4"; -} - -.ion-ios-settings:before { - content: "\f4a7"; -} - -.ion-ios-settings-strong:before { - content: "\f4a6"; -} - -.ion-ios-shuffle:before { - content: "\f4a9"; -} - -.ion-ios-shuffle-strong:before { - content: "\f4a8"; -} - -.ion-ios-skipbackward:before { - content: "\f4ab"; -} - -.ion-ios-skipbackward-outline:before { - content: "\f4aa"; -} - -.ion-ios-skipforward:before { - content: "\f4ad"; -} - -.ion-ios-skipforward-outline:before { - content: "\f4ac"; -} - -.ion-ios-snowy:before { - content: "\f4ae"; -} - -.ion-ios-speedometer:before { - content: "\f4b0"; -} - -.ion-ios-speedometer-outline:before { - content: "\f4af"; -} - -.ion-ios-star:before { - content: "\f4b3"; -} - -.ion-ios-star-half:before { - content: "\f4b1"; -} - -.ion-ios-star-outline:before { - content: "\f4b2"; -} - -.ion-ios-stopwatch:before { - content: "\f4b5"; -} - -.ion-ios-stopwatch-outline:before { - content: "\f4b4"; -} - -.ion-ios-sunny:before { - content: "\f4b7"; -} - -.ion-ios-sunny-outline:before { - content: "\f4b6"; -} - -.ion-ios-telephone:before { - content: "\f4b9"; -} - -.ion-ios-telephone-outline:before { - content: "\f4b8"; -} - -.ion-ios-tennisball:before { - content: "\f4bb"; -} - -.ion-ios-tennisball-outline:before { - content: "\f4ba"; -} - -.ion-ios-thunderstorm:before { - content: "\f4bd"; -} - -.ion-ios-thunderstorm-outline:before { - content: "\f4bc"; -} - -.ion-ios-time:before { - content: "\f4bf"; -} - -.ion-ios-time-outline:before { - content: "\f4be"; -} - -.ion-ios-timer:before { - content: "\f4c1"; -} - -.ion-ios-timer-outline:before { - content: "\f4c0"; -} - -.ion-ios-toggle:before { - content: "\f4c3"; -} - -.ion-ios-toggle-outline:before { - content: "\f4c2"; -} - -.ion-ios-trash:before { - content: "\f4c5"; -} - -.ion-ios-trash-outline:before { - content: "\f4c4"; -} - -.ion-ios-undo:before { - content: "\f4c7"; -} - -.ion-ios-undo-outline:before { - content: "\f4c6"; -} - -.ion-ios-unlocked:before { - content: "\f4c9"; -} - -.ion-ios-unlocked-outline:before { - content: "\f4c8"; -} - -.ion-ios-upload:before { - content: "\f4cb"; -} - -.ion-ios-upload-outline:before { - content: "\f4ca"; -} - -.ion-ios-videocam:before { - content: "\f4cd"; -} - -.ion-ios-videocam-outline:before { - content: "\f4cc"; -} - -.ion-ios-volume-high:before { - content: "\f4ce"; -} - -.ion-ios-volume-low:before { - content: "\f4cf"; -} - -.ion-ios-wineglass:before { - content: "\f4d1"; -} - -.ion-ios-wineglass-outline:before { - content: "\f4d0"; -} - -.ion-ios-world:before { - content: "\f4d3"; -} - -.ion-ios-world-outline:before { - content: "\f4d2"; -} - -.ion-ipad:before { - content: "\f1f9"; -} - -.ion-iphone:before { - content: "\f1fa"; -} - -.ion-ipod:before { - content: "\f1fb"; -} - -.ion-jet:before { - content: "\f295"; -} - -.ion-key:before { - content: "\f296"; -} - -.ion-knife:before { - content: "\f297"; -} - -.ion-laptop:before { - content: "\f1fc"; -} - -.ion-leaf:before { - content: "\f1fd"; -} - -.ion-levels:before { - content: "\f298"; -} - -.ion-lightbulb:before { - content: "\f299"; -} - -.ion-link:before { - content: "\f1fe"; -} - -.ion-load-a:before { - content: "\f29a"; -} - -.ion-load-b:before { - content: "\f29b"; -} - -.ion-load-c:before { - content: "\f29c"; -} - -.ion-load-d:before { - content: "\f29d"; -} - -.ion-location:before { - content: "\f1ff"; -} - -.ion-lock-combination:before { - content: "\f4d4"; -} - -.ion-locked:before { - content: "\f200"; -} - -.ion-log-in:before { - content: "\f29e"; -} - -.ion-log-out:before { - content: "\f29f"; -} - -.ion-loop:before { - content: "\f201"; -} - -.ion-magnet:before { - content: "\f2a0"; -} - -.ion-male:before { - content: "\f2a1"; -} - -.ion-man:before { - content: "\f202"; -} - -.ion-map:before { - content: "\f203"; -} - -.ion-medkit:before { - content: "\f2a2"; -} - -.ion-merge:before { - content: "\f33f"; -} - -.ion-mic-a:before { - content: "\f204"; -} - -.ion-mic-b:before { - content: "\f205"; -} - -.ion-mic-c:before { - content: "\f206"; -} - -.ion-minus:before { - content: "\f209"; -} - -.ion-minus-circled:before { - content: "\f207"; -} - -.ion-minus-round:before { - content: "\f208"; -} - -.ion-model-s:before { - content: "\f2c1"; -} - -.ion-monitor:before { - content: "\f20a"; -} - -.ion-more:before { - content: "\f20b"; -} - -.ion-mouse:before { - content: "\f340"; -} - -.ion-music-note:before { - content: "\f20c"; -} - -.ion-navicon:before { - content: "\f20e"; -} - -.ion-navicon-round:before { - content: "\f20d"; -} - -.ion-navigate:before { - content: "\f2a3"; -} - -.ion-network:before { - content: "\f341"; -} - -.ion-no-smoking:before { - content: "\f2c2"; -} - -.ion-nuclear:before { - content: "\f2a4"; -} - -.ion-outlet:before { - content: "\f342"; -} - -.ion-paintbrush:before { - content: "\f4d5"; -} - -.ion-paintbucket:before { - content: "\f4d6"; -} - -.ion-paper-airplane:before { - content: "\f2c3"; -} - -.ion-paperclip:before { - content: "\f20f"; -} - -.ion-pause:before { - content: "\f210"; -} - -.ion-person:before { - content: "\f213"; -} - -.ion-person-add:before { - content: "\f211"; -} - -.ion-person-stalker:before { - content: "\f212"; -} - -.ion-pie-graph:before { - content: "\f2a5"; -} - -.ion-pin:before { - content: "\f2a6"; -} - -.ion-pinpoint:before { - content: "\f2a7"; -} - -.ion-pizza:before { - content: "\f2a8"; -} - -.ion-plane:before { - content: "\f214"; -} - -.ion-planet:before { - content: "\f343"; -} - -.ion-play:before { - content: "\f215"; -} - -.ion-playstation:before { - content: "\f30a"; -} - -.ion-plus:before { - content: "\f218"; -} - -.ion-plus-circled:before { - content: "\f216"; -} - -.ion-plus-round:before { - content: "\f217"; -} - -.ion-podium:before { - content: "\f344"; -} - -.ion-pound:before { - content: "\f219"; -} - -.ion-power:before { - content: "\f2a9"; -} - -.ion-pricetag:before { - content: "\f2aa"; -} - -.ion-pricetags:before { - content: "\f2ab"; -} - -.ion-printer:before { - content: "\f21a"; -} - -.ion-pull-request:before { - content: "\f345"; -} - -.ion-qr-scanner:before { - content: "\f346"; -} - -.ion-quote:before { - content: "\f347"; -} - -.ion-radio-waves:before { - content: "\f2ac"; -} - -.ion-record:before { - content: "\f21b"; -} - -.ion-refresh:before { - content: "\f21c"; -} - -.ion-reply:before { - content: "\f21e"; -} - -.ion-reply-all:before { - content: "\f21d"; -} - -.ion-ribbon-a:before { - content: "\f348"; -} - -.ion-ribbon-b:before { - content: "\f349"; -} - -.ion-sad:before { - content: "\f34a"; -} - -.ion-sad-outline:before { - content: "\f4d7"; -} - -.ion-scissors:before { - content: "\f34b"; -} - -.ion-search:before { - content: "\f21f"; -} - -.ion-settings:before { - content: "\f2ad"; -} - -.ion-share:before { - content: "\f220"; -} - -.ion-shuffle:before { - content: "\f221"; -} - -.ion-skip-backward:before { - content: "\f222"; -} - -.ion-skip-forward:before { - content: "\f223"; -} - -.ion-social-android:before { - content: "\f225"; -} - -.ion-social-android-outline:before { - content: "\f224"; -} - -.ion-social-angular:before { - content: "\f4d9"; -} - -.ion-social-angular-outline:before { - content: "\f4d8"; -} - -.ion-social-apple:before { - content: "\f227"; -} - -.ion-social-apple-outline:before { - content: "\f226"; -} - -.ion-social-bitcoin:before { - content: "\f2af"; -} - -.ion-social-bitcoin-outline:before { - content: "\f2ae"; -} - -.ion-social-buffer:before { - content: "\f229"; -} - -.ion-social-buffer-outline:before { - content: "\f228"; -} - -.ion-social-chrome:before { - content: "\f4db"; -} - -.ion-social-chrome-outline:before { - content: "\f4da"; -} - -.ion-social-codepen:before { - content: "\f4dd"; -} - -.ion-social-codepen-outline:before { - content: "\f4dc"; -} - -.ion-social-css3:before { - content: "\f4df"; -} - -.ion-social-css3-outline:before { - content: "\f4de"; -} - -.ion-social-designernews:before { - content: "\f22b"; -} - -.ion-social-designernews-outline:before { - content: "\f22a"; -} - -.ion-social-dribbble:before { - content: "\f22d"; -} - -.ion-social-dribbble-outline:before { - content: "\f22c"; -} - -.ion-social-dropbox:before { - content: "\f22f"; -} - -.ion-social-dropbox-outline:before { - content: "\f22e"; -} - -.ion-social-euro:before { - content: "\f4e1"; -} - -.ion-social-euro-outline:before { - content: "\f4e0"; -} - -.ion-social-facebook:before { - content: "\f231"; -} - -.ion-social-facebook-outline:before { - content: "\f230"; -} - -.ion-social-foursquare:before { - content: "\f34d"; -} - -.ion-social-foursquare-outline:before { - content: "\f34c"; -} - -.ion-social-freebsd-devil:before { - content: "\f2c4"; -} - -.ion-social-github:before { - content: "\f233"; -} - -.ion-social-github-outline:before { - content: "\f232"; -} - -.ion-social-google:before { - content: "\f34f"; -} - -.ion-social-google-outline:before { - content: "\f34e"; -} - -.ion-social-googleplus:before { - content: "\f235"; -} - -.ion-social-googleplus-outline:before { - content: "\f234"; -} - -.ion-social-hackernews:before { - content: "\f237"; -} - -.ion-social-hackernews-outline:before { - content: "\f236"; -} - -.ion-social-html5:before { - content: "\f4e3"; -} - -.ion-social-html5-outline:before { - content: "\f4e2"; -} - -.ion-social-instagram:before { - content: "\f351"; -} - -.ion-social-instagram-outline:before { - content: "\f350"; -} - -.ion-social-javascript:before { - content: "\f4e5"; -} - -.ion-social-javascript-outline:before { - content: "\f4e4"; -} - -.ion-social-linkedin:before { - content: "\f239"; -} - -.ion-social-linkedin-outline:before { - content: "\f238"; -} - -.ion-social-markdown:before { - content: "\f4e6"; -} - -.ion-social-nodejs:before { - content: "\f4e7"; -} - -.ion-social-octocat:before { - content: "\f4e8"; -} - -.ion-social-pinterest:before { - content: "\f2b1"; -} - -.ion-social-pinterest-outline:before { - content: "\f2b0"; -} - -.ion-social-python:before { - content: "\f4e9"; -} - -.ion-social-reddit:before { - content: "\f23b"; -} - -.ion-social-reddit-outline:before { - content: "\f23a"; -} - -.ion-social-rss:before { - content: "\f23d"; -} - -.ion-social-rss-outline:before { - content: "\f23c"; -} - -.ion-social-sass:before { - content: "\f4ea"; -} - -.ion-social-skype:before { - content: "\f23f"; -} - -.ion-social-skype-outline:before { - content: "\f23e"; -} - -.ion-social-snapchat:before { - content: "\f4ec"; -} - -.ion-social-snapchat-outline:before { - content: "\f4eb"; -} - -.ion-social-tumblr:before { - content: "\f241"; -} - -.ion-social-tumblr-outline:before { - content: "\f240"; -} - -.ion-social-tux:before { - content: "\f2c5"; -} - -.ion-social-twitch:before { - content: "\f4ee"; -} - -.ion-social-twitch-outline:before { - content: "\f4ed"; -} - -.ion-social-twitter:before { - content: "\f243"; -} - -.ion-social-twitter-outline:before { - content: "\f242"; -} - -.ion-social-usd:before { - content: "\f353"; -} - -.ion-social-usd-outline:before { - content: "\f352"; -} - -.ion-social-vimeo:before { - content: "\f245"; -} - -.ion-social-vimeo-outline:before { - content: "\f244"; -} - -.ion-social-whatsapp:before { - content: "\f4f0"; -} - -.ion-social-whatsapp-outline:before { - content: "\f4ef"; -} - -.ion-social-windows:before { - content: "\f247"; -} - -.ion-social-windows-outline:before { - content: "\f246"; -} - -.ion-social-wordpress:before { - content: "\f249"; -} - -.ion-social-wordpress-outline:before { - content: "\f248"; -} - -.ion-social-yahoo:before { - content: "\f24b"; -} - -.ion-social-yahoo-outline:before { - content: "\f24a"; -} - -.ion-social-yen:before { - content: "\f4f2"; -} - -.ion-social-yen-outline:before { - content: "\f4f1"; -} - -.ion-social-youtube:before { - content: "\f24d"; -} - -.ion-social-youtube-outline:before { - content: "\f24c"; -} - -.ion-soup-can:before { - content: "\f4f4"; -} - -.ion-soup-can-outline:before { - content: "\f4f3"; -} - -.ion-speakerphone:before { - content: "\f2b2"; -} - -.ion-speedometer:before { - content: "\f2b3"; -} - -.ion-spoon:before { - content: "\f2b4"; -} - -.ion-star:before { - content: "\f24e"; -} - -.ion-stats-bars:before { - content: "\f2b5"; -} - -.ion-steam:before { - content: "\f30b"; -} - -.ion-stop:before { - content: "\f24f"; -} - -.ion-thermometer:before { - content: "\f2b6"; -} - -.ion-thumbsdown:before { - content: "\f250"; -} - -.ion-thumbsup:before { - content: "\f251"; -} - -.ion-toggle:before { - content: "\f355"; -} - -.ion-toggle-filled:before { - content: "\f354"; -} - -.ion-transgender:before { - content: "\f4f5"; -} - -.ion-trash-a:before { - content: "\f252"; -} - -.ion-trash-b:before { - content: "\f253"; -} - -.ion-trophy:before { - content: "\f356"; -} - -.ion-tshirt:before { - content: "\f4f7"; -} - -.ion-tshirt-outline:before { - content: "\f4f6"; -} - -.ion-umbrella:before { - content: "\f2b7"; -} - -.ion-university:before { - content: "\f357"; -} - -.ion-unlocked:before { - content: "\f254"; -} - -.ion-upload:before { - content: "\f255"; -} - -.ion-usb:before { - content: "\f2b8"; -} - -.ion-videocamera:before { - content: "\f256"; -} - -.ion-volume-high:before { - content: "\f257"; -} - -.ion-volume-low:before { - content: "\f258"; -} - -.ion-volume-medium:before { - content: "\f259"; -} - -.ion-volume-mute:before { - content: "\f25a"; -} - -.ion-wand:before { - content: "\f358"; -} - -.ion-waterdrop:before { - content: "\f25b"; -} - -.ion-wifi:before { - content: "\f25c"; -} - -.ion-wineglass:before { - content: "\f2b9"; -} - -.ion-woman:before { - content: "\f25d"; -} - -.ion-wrench:before { - content: "\f2ba"; -} - -.ion-xbox:before { - content: "\f30c"; -} - -/*# sourceMappingURL=ionicons.css.map */ diff --git a/app/assets/stylesheets/app/_editor.scss b/app/assets/stylesheets/app/_editor.scss index 0860d57be..a1afdd7bb 100644 --- a/app/assets/stylesheets/app/_editor.scss +++ b/app/assets/stylesheets/app/_editor.scss @@ -1,25 +1,10 @@ $heading-height: 75px; .editor { flex: 1 50%; - min-width: 300px; display: flex; flex-direction: column; overflow-y: hidden; background-color: white; - - &.fullscreen { - width: 100%; - position: absolute; - left: 0px; - top: 0px; - z-index: 200; - padding: 0; - } - - .section-menu-bar { - flex: 1 0 28px; - max-height: 28px; - } } #editor-title-bar { @@ -37,10 +22,6 @@ $heading-height: 75px; height: auto; overflow: visible; - &.fullscreen { - position: relative; - } - > .title { font-size: 18px; font-weight: bold; @@ -87,7 +68,7 @@ $heading-height: 75px; #note-tags-component-container { height: 50px; - #note-tags-iframe { + iframe { height: 50px; width: 100%; position: absolute; @@ -103,7 +84,7 @@ $heading-height: 75px; } } -.editor-content { +.editor-content, #editor-content { flex: 1; z-index: 10; overflow-y: hidden; @@ -111,9 +92,7 @@ $heading-height: 75px; display: flex; background-color: white; - &.fullscreen { - padding-top: 0px; - } + position: relative; #editor-iframe { flex: 1; @@ -122,7 +101,6 @@ $heading-height: 75px; .editable { font-family: monospace; - flex: 1; overflow-y: scroll; width: 100%; @@ -132,54 +110,16 @@ $heading-height: 75px; padding-top: 11px; font-size: 17px; resize: none; - - &.fullscreen { - padding: 85px 10%; - max-width: 1200px; - display: inline-block; - margin-left: auto; - margin-right: auto; - } } } #editor-pane-component-stack { width: 100%; - .component { - height: 50px; + .component-stack-item { width: 100%; position: relative; - &:not(:last-child) { - border-bottom: 1px solid $bg-color; - } - - &:first-child { - border-top: 1px solid $bg-color; - } - - .exit-button { - width: 15px; - height: 100%; - position: absolute; - right: 0; - background-color: transparent; - cursor: pointer; - display: flex; - align-items: center; - color: rgba(black, 0.7); - text-align: center; - padding-left: 2px; - - .content { - - } - - &:hover { - background-color: rgba(gray, 0.3); - } - } - + border-top: 1px solid $bg-color; iframe { width: 100%; } diff --git a/app/assets/stylesheets/app/_extensions.scss b/app/assets/stylesheets/app/_extensions.scss deleted file mode 100644 index 71edefcdb..000000000 --- a/app/assets/stylesheets/app/_extensions.scss +++ /dev/null @@ -1,97 +0,0 @@ -.extension-render-modal { - position: fixed; - margin-left: auto; - margin-right: auto; - left: 0; - right: 0; - top: 0; - bottom: 0; - z-index: 10000; - width: 100vw; - height: 100vh; - background-color: rgba(gray, 0.3); - color: black; - - .content { - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - background-color: white; - width: 700px; - height: 500px; - margin: auto; - padding: 25px; - position: absolute; - top: 0; left: 0; bottom: 0; right: 0; - overflow-y: scroll; - } -} - -#global-ext-menu { - color: black; - .panel-body { - padding: 0; - } - - .container { - padding: 13px 18px; - - &.no-bottom { - padding-bottom: 0; - } - } - - p { - font-size: 14px; - - &.small { - font-size: 12px; - } - } - - .link-group { - a { - margin-right: 2px; - } - } - - .dashboard-link { - padding-top: 12px; - font-weight: normal; - } - - .section-margin { - margin-top: 20px; - } - - input { - border: 1px solid $blue-color; - border-radius: 2px; - } - - .header { - padding-bottom: 12px; - } - - ul { - border-top: 1px solid $light-bg-color; - border-bottom: 1px solid $light-bg-color; - margin: 0; - padding: 0; - - li { - cursor: pointer; - background-color: rgba($light-bg-color, 0.2); - &:hover { - background-color: rgba($light-bg-color, 0.4); - } - &:not(:last-child) { - border-bottom: 1px solid $light-bg-color; - } - } - } - -} - -ul { - margin: 0; - padding: 0; -} diff --git a/app/assets/stylesheets/app/_footer.scss b/app/assets/stylesheets/app/_footer.scss index ac85969c9..950331dea 100644 --- a/app/assets/stylesheets/app/_footer.scss +++ b/app/assets/stylesheets/app/_footer.scss @@ -1,221 +1,48 @@ -.fake-link { - font-weight: bold; - cursor: pointer; - - &:hover { - text-decoration: underline; - } -} - -h2 { - margin-bottom: 0px; - margin-top: 0px; -} - #footer-bar { position: relative; width: 100%; - padding: 5px; - background-color: #f1f1f1; - border-top: 1px solid rgba(black, 0.04); height: $footer-height; max-height: $footer-height; z-index: 100; - font-size: 10px; - color: $dark-gray; - - .medium-text { - font-size: 14px; - } - - a { - font-weight: bold; - cursor: pointer; - - &.gray { - color: $dark-gray; - } - - &.block { - display: block; - } - } - - p { - margin: 2px 0px; - font-size: 12px; - } - - label { - font-weight: bold; - margin-bottom: 4px; - } - - h1 { - font-size: 16px; - } - - h3 { - font-size: 14px; - margin-top: 4px; - margin-bottom: 3px; - } - - h4 { - margin-bottom: 4px; - font-size: 13px; - } - - section { - padding: 5px; - padding-bottom: 2px; - margin-top: 5px; - - &.inline-h { - padding-top: 5px; - padding-left: 0; - padding-right: 0; - } - - } - - input { - // margin-bottom: 10px; - border-radius: 0px; - } } -#footer-bar .footer-bar-link { - font-size: 11px; - font-weight: bold; - margin-left: 8px; - color: #515263; - +#footer-bar .item { z-index: 1000; - display: inline-block; position: relative; - cursor: pointer; - user-select: none; - > a { - color: #515263; + .panel { + max-height: 85vh; + position: absolute; + right: 0px; + left: 10px; + bottom: 40px; + min-width: 300px; + z-index: 1000; + margin-top: 15px; + background-color: white; } + } -.footer-bar-link .panel { - font-weight: normal; - font-size: 12px; +#account-panel { + width: 400px; +} - max-height: 85vh; - position: absolute; - right: 0px; - bottom: 20px; - min-width: 300px; - z-index: 1000; - margin-top: 15px; - - box-shadow: 0px 0px 15px rgba(black, 0.2); - border: none; +.panel { cursor: default; - overflow: auto; - background-color: white; -} - -button.light { - font-weight: bold; - margin-bottom: 0px; - font-size: 12px; - height: 30px; - padding-top: 3px; - text-align: center; - margin-bottom: 6px; - background-color: white; - display: block; - width: 100%; - border: 1px solid rgba(gray, 0.15); - cursor: pointer; - - &:hover { - background-color: rgba(gray, 0.10); - } -} - -.half-button { - $spacing: 2px; - width: calc(50% - #{$spacing}); - margin-left: $spacing/2.0; - margin-right: $spacing/2.0; - float: left; -} - -.gray-bg { - background-color: #f6f6f6; - border: 1px solid #f2f2f2; -} - -.white-bg { - background-color: white; - border: 1px solid rgba(gray, 0.2); } a.disabled { pointer-events: none; } +#lock-item { + margin-left: 3px; +} .icon.ion-locked { margin-left: 5px; border-left: 1px solid gray; padding-left: 8px; } - - - - -/* Global Ext Menu */ - -.ext-section { - - min-height: 50px; - - h1 { - margin: 0; - padding: 0; - padding-top: 5px; - } - - &.opened { - h1 { - padding-top: 0px; - // padding-bottom: 6px; - } - } - -} - - - - - - - - -.spinner { - height: 10px; - width: 10px; - animation: rotate 0.8s infinite linear; - border: 1px solid #515263; - border-right-color: transparent; - border-radius: 50%; - - &.tinted { - border: 1px solid $blue-color; - border-right-color: transparent; - } -} - -@keyframes rotate { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} diff --git a/app/assets/stylesheets/app/_ionicons.scss b/app/assets/stylesheets/app/_ionicons.scss new file mode 100644 index 000000000..884e1f781 --- /dev/null +++ b/app/assets/stylesheets/app/_ionicons.scss @@ -0,0 +1,24 @@ +@charset "UTF-8"; +/*! + Ionicons, v2.0.1 + Created by Ben Sperry for the Ionic Framework, http://ionicons.com/ + https://twitter.com/benjsperry https://twitter.com/ionicframework + MIT License: https://github.com/driftyco/ionicons + + Android-style icons originally built by Google’s + Material Design Icons: https://github.com/google/material-design-icons + used under CC BY http://creativecommons.org/licenses/by/4.0/ + Modified icons to fit ionicon’s grid from original. +*/ +@font-face { font-family: "Ionicons"; src: url("../assets/ionicons.eot?v=2.0.0"); src: url("../assets/ionicons.eot?v=2.0.1#iefix") format("embedded-opentype"), url("../assets/ionicons.ttf?v=2.0.1") format("truetype"), url("../assets/ionicons.woff?v=2.0.1") format("woff"), url("../assets/ionicons.svg?v=2.0.1#Ionicons") format("svg"); font-weight: normal; font-style: normal; } +.ion, .ionicons, .ion-ios-box:before, .ion-bookmark:before, .ion-locked:before { display: inline-block; font-family: "Ionicons"; speak: none; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; text-rendering: auto; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } + +.ion-ios-box:before { content: "\f3ec"; } + +.ion-locked:before { content: "\f200"; } + +.ion-bookmark:before { + content: "\f26b"; +} + +/*# sourceMappingURL=ionicons.css.map */ diff --git a/app/assets/stylesheets/app/_lock-screen.scss b/app/assets/stylesheets/app/_lock-screen.scss index 4fef13adb..5b0fe4ba9 100644 --- a/app/assets/stylesheets/app/_lock-screen.scss +++ b/app/assets/stylesheets/app/_lock-screen.scss @@ -14,6 +14,7 @@ font-size: 16px; display: flex; align-items: center; + justify-content: center; .background { position: absolute; @@ -22,30 +23,12 @@ height: 100%; } - .content { - // box-shadow: 0 3px 3px rgba(0, 0, 0, 0.175); - border: 1px solid rgba(black, 0.1); - background-color: white; - width: 300px; - // height: 500px; - margin: auto; - padding: 10px 30px; - padding-bottom: 30px; - // position: absolute; - // top: 0; left: 0; bottom: 0; right: 0; - overflow-y: scroll; + .panel { + width: 315px; + flex-grow: 0; - p { - margin-bottom: 8px; - margin-top: 0; - } - - h3 { - margin-bottom: 0; - } - - h4 { - margin-bottom: 6px; + .header { + justify-content: center; } } } diff --git a/app/assets/stylesheets/app/_main.scss b/app/assets/stylesheets/app/_main.scss index 83d9f0bad..45228e125 100644 --- a/app/assets/stylesheets/app/_main.scss +++ b/app/assets/stylesheets/app/_main.scss @@ -6,56 +6,6 @@ $selection-color: $bg-color; $selected-text-color: black; $blue-color: #086dd6; -@mixin MQ-Small() { - @media (max-width: $screen-xs-max) { - @content; - } - - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - @content; - } -} - -@mixin MQ-Medium() { - @media (min-width: $screen-md-min) and (max-width: $screen-md-max) { - @content; - } -} - - -@mixin MQ-Large() { - @media (min-width: $screen-lg-min) { - @content; - } -} - -.tinted { - color: $blue-color; -} - -.tinted-selected { - color: white; -} - -.tinted-box { - background-color: $blue-color; - color: white; - border-radius: 4px; - padding: 16px 20px; - - button { - background-color: white; - color: $blue-color; - border-radius: 3px; - font-weight: bold; - padding: 6px 20px; - width: 100%; - &:hover { - text-decoration: underline; - } - } -} - html, body { font-family: -apple-system, BlinkMacSystemFont, @@ -66,34 +16,49 @@ body { -webkit-font-smoothing: antialiased; min-height: 100%; height: 100%; - font-size: 20px; + font-size: 14px; margin: 0; background-color: $bg-color; } -.dark-button { - background-color: #2e2e2e; - border: 0; - padding: 6px 18px; - font-size: 16px; - cursor: pointer; - color: #fff; - border-radius: 2px; - border: 1px solid transparent; - -webkit-appearance: none; - -webkit-font-smoothing: antialiased; - -webkit-tap-highlight-color: transparent; +* { + box-sizing: border-box; +} - &:hover { - background-color: black; - } +.tinted { + color: $blue-color; +} + +.tinted-selected { + color: white; +} + +*:focus {outline:0;} + +button:focus { + outline:0; +} + +input, button, select, textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; } a { color: $blue-color; text-decoration: none; + + &.no-decoration { + color: inherit; + } + &:hover { - text-decoration: underline;; + text-decoration: underline; + + &.no-decoration { + text-decoration: none; + } } } @@ -108,12 +73,9 @@ p { background-color: $bg-color; } -$footer-height: 25px; - -$section-header-height: 70px; +$footer-height: 32px; .app { - // height: 100%; height: calc(100% - #{$footer-height}); width: 100%; display: flex; @@ -121,18 +83,38 @@ $section-header-height: 70px; overflow: hidden; position: relative; - .light-button { - background-color: $bg-color; - font-weight: bold; - color: $main-text-color; - font-size: 16px; - text-align: center; - height: 35px; - border-radius: 4px; - padding-top: 6px; + panel-resizer { + top: 0; + right: 0; + z-index: 1; + width: 8px; + height: 100%; + position: absolute; + cursor: col-resize; + background-color: rgb(224, 224, 224); + opacity: 0; - &:hover { - background-color: #cdcdcd; + &.left { + left: 0; + right: none; + } + + &.always-visible { + opacity: 1; + } + + &.collapsed { + opacity: 1; + } + + &.dragging { + opacity: 1; + } + + &.hoverable { + &:hover { + opacity: 1; + } } } @@ -141,6 +123,8 @@ $section-header-height: 70px; height: 100%; max-height: calc(100vh - #{$footer-height}); font-size: 17px; + position: relative; + overflow: hidden; .scrollable { overflow-y: auto; @@ -155,34 +139,42 @@ $section-header-height: 70px; } .section-title-bar { - padding: 20px; - height: $section-header-height; font-weight: bold; font-size: 14px; - > .title { - float: left; - white-space: nowrap; - text-overflow: ellipsis; - width: 80%; - overflow: hidden; + .padded { + padding: 0 14px; } - > .add-button { - float: right; - font-size: 18px; - width: 45px; - height: 24px; - cursor: pointer; - background-color: #e9e9e9; - border-radius: 4px; - font-weight: normal; - text-align: center; - position: absolute; - right: 12px; + .section-title-bar-header { + display: flex; + justify-content: space-between; + align-items: center; + overflow: hidden; - &:hover { - background-color: rgba(#e9e9e9, 0.8); + > .title { + white-space: nowrap; + text-overflow: ellipsis; + width: 80%; + overflow: hidden; + } + + > .add-button { + $button-bg: #e9e9e9; + color: lighten($main-text-color, 40%); + font-size: 18px; + width: 45px; + height: 24px; + cursor: pointer; + background-color: $button-bg; + border-radius: 4px; + font-weight: normal; + text-align: center; + + &:hover { + background-color: darken($button-bg, 5%); + color: lighten($main-text-color, 40%); + } } } } diff --git a/app/assets/stylesheets/app/_menus.scss b/app/assets/stylesheets/app/_menus.scss index e1bfbd4af..362eaf767 100644 --- a/app/assets/stylesheets/app/_menus.scss +++ b/app/assets/stylesheets/app/_menus.scss @@ -1,243 +1,20 @@ -ul.section-menu-bar { - width: 100%; - padding-top: 0px; - padding-left: 6px; - padding-right: 21px; - - user-select: none; - - background-color: #f1f1f1; - color: $selected-text-color; - height: 28px; - cursor: default; - - margin-top: 0px; - margin-bottom: 0; - list-style: none; - font-weight: bold; - font-size: 0; /* trick to remove gaps between li inline-block elements */ - - > li { - padding: 6px 8px; - text-align: left; - display: inline-block; +.app-bar { + .item { position: relative; - - font-size: 13px; - font-weight: bold; - user-select: none; - - &.full-width { - width: 100%; - } - - &.item-with-subtitle { - label { - // float: left; - margin-right: 8px; - } - - .subtitle { - margin-top: 1px; - opacity: 0.5; - font-weight: normal; - font-size: 12px; - - overflow: hidden; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; /* number of lines to show */ - $line-height: 18px; - line-height: $line-height; /* fallback */ - max-height: calc(#{$line-height} * 1); /* fallback */ - } - } - - - &.selected { - background-color: $blue-color; - border-radius: 1px; - color: white; - } } } .dropdown-menu { - position: absolute; - top: 100%; - left: 0; - float: left; - min-width: 160px; - z-index: 100; + position: absolute; + top: 100%; + left: 0; + float: left; + min-width: 160px; + z-index: 100; - list-style: none; - font-size: 14px; - text-align: left; - - padding: 0 0; - border: none; - width: 280px; - - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - background-clip: padding-box; - - background-color: white; - color: $selected-text-color; - - li:hover { - background-color: $blue-color; - color: white; - } - - > li { - width: 100%; - height: 40px; - padding-top: 3px; - overflow: hidden; - cursor: pointer; - - border-bottom: 1px solid rgba(black, 0.1); - - color: $selected-text-color; - float: left; - - label { - padding: 10px; - padding-top: 8px; - width: 100%; - height: 100%; - display: block; - cursor: pointer; - } - - - .shortcut { - float: right; - font-size: 12px; - font-weight: normal; - opacity: 0.5; - margin-top: 10px; - padding-right: 10px; - } - } -} - - - - - - - - - -.dropdown-menu.sectioned-menu { - overflow-y: scroll; + margin-top: 5px; + width: 280px; max-height: calc(85vh - 90px); - - ul { - margin-top: 0px; - margin-bottom: 0px; - padding-left:0; - position: relative; - - li { - cursor: pointer; - height: auto; - - padding: 10px; - border-bottom: 1px solid rgba(black, 0.1); - background-color: rgba(white, 0.9); - height: auto; - - .left-side { - left: 0; - width: 60%; - display: inline-block; - vertical-align: top; - user-select: text; - } - - .right-side { - right: 12px; - width: 30%; - display: inline-block; - vertical-align: top; - text-align: right; - position: absolute; - } - - &:hover { - background-color: $blue-color; - - - .tinted { - color: white; - } - - &.nested-hover { - color: black; - background-color: $light-bg-color; - } - } - - &.nested-hover { - color: black; - background-color: white; - } - - .menu-item-title { - font-weight: bold; - font-size: 14px; - margin-bottom: 3px; - } - - .menu-item-subtitle { - font-weight: normal; - opacity: 0.5; - margin-top: 1px; - font-size: 12px; - } - } - } - - .header { - background-color: #ededed; - border-bottom: 1px solid #d3d3d3; - position: relative; - padding-top: 12px; - padding-left: 10px; - padding-bottom: 10px; - cursor: pointer; - user-select: none; - - - > .title { - font-size: 14px; - font-weight: bold; - } - - > .subtitle { - font-size: 12px; - opacity: 0.5; - font-weight: normal; - margin-top: 2px; - } - - > .loading { - position: absolute; - height: 15px; - width: 15px; - right: 10px; - top: 20px; - } - } - - .footer { - background-color: #ededed; - border-top: 1px solid #d3d3d3; - position: relative; - padding: 10px; - } + background-color: white; + color: $selected-text-color; } diff --git a/app/assets/stylesheets/app/_modals.scss b/app/assets/stylesheets/app/_modals.scss new file mode 100644 index 000000000..69b9754e1 --- /dev/null +++ b/app/assets/stylesheets/app/_modals.scss @@ -0,0 +1,91 @@ +#permissions-modal { + width: 350px; + .panel { + border-radius: 0; + background-color: white; + } + .content { + padding-top: 1.1rem; + } + .footer { + padding-bottom: 1.4rem; + } +} + +.panel { + background-color: white; +} + +.header .subtitle { + font-size: 1.1rem; +} + +.modal { + position: fixed; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 10000; + width: 100vw; + height: 100vh; + background-color: rgba(gray, 0.3); + color: black; + display: flex; + align-items: center; + justify-content: center; + + .sn-component { + height: 100%; + .panel { + height: 100%; + } + } + + &.medium { + > .content { + width: 700px; + height: 500px; + } + } + + .background { + position: absolute; + z-index: -1; + width: 100%; + height: 100%; + } + + > .content { + overflow-y: auto; + width: auto; + padding: 0; + padding-bottom: 0; + min-width: 300px; + } +} + +// Optionally use if .component-view container is not flex-based +.component-view-container { + flex-grow: 1; + display: flex; + height: 100%; + width: 100%; +} + +.component-view { + flex-grow: 1; + display: flex; + overflow: auto; + + .sn-component { + min-width: 100%; + } + + iframe { + flex: 1; + width: 100%; + } +} diff --git a/app/assets/stylesheets/app/_notes.scss b/app/assets/stylesheets/app/_notes.scss index 7dd46dfd7..45e6cd0f0 100644 --- a/app/assets/stylesheets/app/_notes.scss +++ b/app/assets/stylesheets/app/_notes.scss @@ -1,45 +1,44 @@ -.notes { +#notes-column, .notes { border-left: 1px solid #dddddd; border-right: 1px solid #dddddd; - flex: 1 20%; - max-width: 350px; - min-width: 170px; - + width: 350px; + flex-grow: 0; user-select: none; - $notes-title-bar-height: 148px; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + + .content { + display: flex; + flex-direction: column; + } #notes-title-bar { - color: rgba(black, 0.40); padding-top: 16px; - padding-left: 14px; - padding-right: 14px; - height: $notes-title-bar-height; font-weight: normal; font-size: 18px; - .title { + .section-title-bar-header .title { + color: rgba(black, 0.40); width: calc(90% - 45px); } } #notes-add-button { - right: 14px; + } #notes-menu-bar { position: relative; - margin: 0 -14px; margin-top: 14px; - height: 45px; - width: auto; } .filter-section { clear: left; height: 32px; - margin-top: 20px; + margin-top: 14px; position: relative; .filter-bar { @@ -80,7 +79,7 @@ } .scrollable { - height: calc(100vh - (#{$notes-title-bar-height} + #{$footer-height})); + height: 100%; } .infinite-scroll { diff --git a/app/assets/stylesheets/app/_permissions-modal.scss b/app/assets/stylesheets/app/_permissions-modal.scss deleted file mode 100644 index c51800dd1..000000000 --- a/app/assets/stylesheets/app/_permissions-modal.scss +++ /dev/null @@ -1,86 +0,0 @@ -.permissions-modal { - position: fixed; - margin-left: auto; - margin-right: auto; - left: 0; - right: 0; - top: 0; - bottom: 0; - z-index: 10000; - width: 100vw; - height: 100vh; - background-color: rgba(gray, 0.3); - color: black; - font-size: 16px; - display: flex; - align-items: center; - - .background { - position: absolute; - z-index: -1; - width: 100%; - height: 100%; - } - - .content { - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - background-color: white; - width: 700px; - // height: 500px; - margin: auto; - padding: 10px 30px; - padding-bottom: 30px; - // position: absolute; - // top: 0; left: 0; bottom: 0; right: 0; - overflow-y: scroll; - - p { - margin-bottom: 8px; - margin-top: 0; - } - - h3 { - margin-bottom: 0; - } - - h4 { - margin-bottom: 6px; - } - } - - .learn-more { - margin-top: 20px; - line-height: 1.3; - } - - .status { - color: orange; - } - - .buttons { - margin-top: 35px; - } - - button.standard { - border-radius: 1px; - font-weight: bold; - padding: 6px 20px; - display: inline-block; - - &:hover { - text-decoration: underline; - } - - &.tinted { - background-color: $blue-color; - color: white; - } - - &.white { - color: black; - background-color: white; - border: 1px solid gray; - } - } - -} diff --git a/app/assets/stylesheets/app/_standard.scss b/app/assets/stylesheets/app/_standard.scss deleted file mode 100644 index c111f54d2..000000000 --- a/app/assets/stylesheets/app/_standard.scss +++ /dev/null @@ -1,298 +0,0 @@ -.selectable { - user-select: text !important; -} - -.clear { - clear: both; -} - -.pull-left { - float: left !important; -} - -.pull-right { - float: right !important; -} - -.mt-1 { - margin-top: 1px !important; -} - -.mt-2 { - margin-top: 2px !important; -} - -.mt-3 { - margin-top: 3px !important; -} - -.mt-5 { - margin-top: 5px !important; -} - -.mt-10 { - margin-top: 10px !important; -} - -.mt-15 { - margin-top: 15px !important; -} - -.mt-20 { - margin-top: 20px !important; -} - -.mt-25 { - margin-top: 25px !important; -} - -.mt-50 { - margin-top: 50px !important; -} - -.mt-100 { - margin-top: 100px !important; -} - -.mb-0 { - margin-bottom: 0px !important; -} - -.mb-5 { - margin-bottom: 5px !important; -} - -.mb-10 { - margin-bottom: 10px !important; -} - -.mr-5 { - margin-right: 5px; -} - -.mr-10 { - margin-right: 10px; -} - -.mr-15 { - margin-right: 15px; -} - -.mr-20 { - margin-right: 20px; -} - -.ml-2 { - margin-left: 2px; -} - -.pb-0 { - padding-bottom: 0px !important; -} - -.pt-5 { - padding-top: 5px; -} - -.faded { - opacity: 0.5; -} - -.center-align { - text-align: center !important; -} - -.center { - margin-left: auto !important; - margin-right: auto !important; -} - -.block { - display: block !important; -} - -.wrap { - word-wrap: break-word; - word-break: break-all; -} - -.one-line-overflow { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; -} - -.small-v-space { - height: 6px; - display: block; -} - -.medium-v-space { - height: 12px; - display: block; -} - -.large-v-space { - height: 24px; - display: block; -} - -.small-padding { - padding: 5px !important; -} - -.medium-padding { - padding: 10px !important; -} - -.pb-4 { - padding-bottom: 4px !important; -} - -.pb-6 { - padding-bottom: 6px !important; -} - -.pb-10 { - padding-bottom: 10px !important; -} - -.large-padding { - padding: 22px !important; -} - -.red { - color: red !important; -} - -.orange { - color: orange !important; -} - -.bold { - font-weight: bold !important; -} - -.normal { - font-weight: normal !important; -} - -.small { - font-size: 10px; -} - -.medium { - font-size: 14px !important; -} - -.inline { - display: inline-block !important; - - &.top { - vertical-align: top; - } - - &.middle { - vertical-align: middle; - } -} - -button { - cursor: pointer; -} - -input.form-control { - margin-bottom: 10px; - border-radius: 0px; - min-height: 39px; - font-size: 14px; - padding-left: 6px; -} - -button { - border: none; - - @mixin wide-button() { - font-weight: bold; - text-align: center; - padding: 10px; - font-size: 16px; - // min-width: 200px; - - &:hover { - text-decoration: underline; - } - } - - &.black { - @include wide-button(); - background-color: black; - color: white; - } - - &.white { - @include wide-button(); - background-color: white; - color: black; - border: 1px solid rgba(gray, 0.2); - } -} - - -.gray-bg { - background-color: #f6f6f6; - border: 1px solid #f2f2f2; -} - -.white-bg { - background-color: white; - border: 1px solid rgba(gray, 0.2); -} - -.col-container { - // white-space: nowrap; -} - -@mixin col() { - display: inline-block; - vertical-align: top; - white-space: normal; -} - -.col-10 { - width: 10%; - @include col(); -} - -.col-15 { - width: 15%; - @include col(); -} - -.col-20 { - width: 20%; - @include col(); -} - -.col-45 { - width: 45%; - @include col(); -} - -.col-50 { - width: 50%; - @include col(); -} - -.col-80 { - width: 80%; - @include col(); -} - -.relative { - position: relative !important; -} - -.absolute { - position: absolute !important; -} diff --git a/app/assets/stylesheets/app/_stylekit-sub.scss b/app/assets/stylesheets/app/_stylekit-sub.scss new file mode 100644 index 000000000..5fb32e5bc --- /dev/null +++ b/app/assets/stylesheets/app/_stylekit-sub.scss @@ -0,0 +1,31 @@ +.sn-component { + +} + +.panel { + color: black; + + .header { + .close-button { + &:hover { + text-decoration: none; + } + } + } + + input { + min-height: 39px; + } + + + .button-group.stretch { + .button:not(.featured) { + // Default buttons that are not featured and stretched should have larger vertical padding + padding: 9px; + } + } + + a { + color: $blue-color; + } +} diff --git a/app/assets/stylesheets/app/_tags.scss b/app/assets/stylesheets/app/_tags.scss index d0e1e31db..5285e9c91 100644 --- a/app/assets/stylesheets/app/_tags.scss +++ b/app/assets/stylesheets/app/_tags.scss @@ -1,31 +1,28 @@ .tags { - flex: 1 10%; - max-width: 180px; - min-width: 100px; width: 180px; - background-color: #f6f6f6; + flex-grow: 0; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; - user-select: none; - - $tags-title-bar-height: 55px; + &, #tags-content { + background-color: #f6f6f6; + display: flex; + flex-direction: column; + } #tags-title-bar { color: black; - height: $tags-title-bar-height; + padding-top: 14px; + padding-bottom: 16px; padding-left: 12px; padding-right: 12px; font-size: 12px; color: rgba(black, 0.8); } - #tags-content { - background-color: #f6f6f6; - } - #tag-add-button { - margin-top: -6px; background-color: #d7d7d7; - float: right; &:hover { background-color: rgba(#d7d7d7, 0.8); @@ -33,7 +30,12 @@ } .scrollable { - height: calc(100vh - (#{$tags-title-bar-height} + #{$footer-height})); + height: 100%; + } + + .infinite-scroll { + overflow-x: hidden; + height: inherit; } .tag { diff --git a/app/assets/stylesheets/app/_ui.scss b/app/assets/stylesheets/app/_ui.scss index fcbaa03eb..91b0be017 100644 --- a/app/assets/stylesheets/app/_ui.scss +++ b/app/assets/stylesheets/app/_ui.scss @@ -57,104 +57,56 @@ $screen-md-max: ($screen-lg-min - 1) !default; } } -* { - box-sizing: border-box; +.selectable { + user-select: text !important; + cursor: text; } -*:focus {outline:0;} - -.float-group { - height: 15px; - &.h10 { - height: 10px; - } - - &.h20 { - height: 20px; - } - - &.h30 { - height: 30px; - } - clear: both; +.mt-5 { + margin-top: 5px !important; } -button:focus {outline:0;} - - -.button-group { - clear: both; - height: 36px; +.mt-10 { + margin-top: 10px !important; } -.ui-button { - background-color: $blue-color; - border: 0; - color: white; - font-weight: bold; - min-height: 36px; - font-size: 14px; - - &.block { - width: 100%; - } +.faded { + opacity: 0.5; } -.panel { - position: absolute; - right: 0px; - min-width: 300px; - z-index: 1000; - margin-top: 10px; - box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.2); - border: none; - background-color: white; +.center-align { + text-align: center !important; } -.panel-right { - left: 0px; +.block { + display: block !important; } -.panel-body { - padding: 15px; +.wrap { + word-wrap: break-word; + word-break: break-all; } -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857; - color: #555555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - box-shadow: 0; +.medium-padding { + padding: 10px !important; } -input, button, select, textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; +.red { + color: red !important; } -button { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; +.bold { + font-weight: bold !important; } -.checkbox { - font-size: 14px; - font-weight: bold; - margin-left: auto; - margin-right: auto; - margin-bottom: 10px; - - input { - margin-left: 0px; - } +.normal { + font-weight: normal !important; +} + +.small { + font-size: 10px; +} + +.medium { + font-size: 14px !important; } diff --git a/app/assets/stylesheets/frontend.css.scss b/app/assets/stylesheets/main.css.scss similarity index 57% rename from app/assets/stylesheets/frontend.css.scss rename to app/assets/stylesheets/main.css.scss index 249d4f9ed..9e28dc002 100644 --- a/app/assets/stylesheets/frontend.css.scss +++ b/app/assets/stylesheets/main.css.scss @@ -1,15 +1,11 @@ -$dark-gray: #2e2e2e; - -@import "app/standard"; @import "app/main"; @import "app/ui"; @import "app/footer"; @import "app/tags"; @import "app/notes"; @import "app/editor"; -@import "app/extensions"; @import "app/menus"; -@import "app/permissions-modal"; +@import "app/modals"; @import "app/lock-screen"; - -@import "ionicons"; +@import "app/stylekit-sub"; +@import "app/ionicons"; diff --git a/app/assets/templates/directives/account-menu.html.haml b/app/assets/templates/directives/account-menu.html.haml new file mode 100644 index 000000000..9ddb39cfc --- /dev/null +++ b/app/assets/templates/directives/account-menu.html.haml @@ -0,0 +1,189 @@ +.sn-component + .panel#account-panel + .header + %h1.title Account + %a.close-button{"ng-click" => "close()"} Close + .content + + .panel-section.hero{"ng-if" => "!user && !formData.showLogin && !formData.showRegister && !formData.mfa"} + %h1.title Sign in or register to enable sync and end-to-end encryption. + .panel-row + .panel-row + .button-group.stretch + .button.info.featured{"ng-click" => "formData.showLogin = true"} + .label Sign In + .button.info.featured{"ng-click" => "formData.showRegister = true"} + .label Register + %p + Standard Notes is free on every platform, and comes standard with sync and encryption. + + .panel-section{"ng-if" => "formData.showLogin || formData.showRegister"} + %h3.title.panel-row + {{formData.showLogin ? "Sign In" : "Register"}} + + %form.panel-form{"ng-submit" => "submitAuthForm()"} + %input{:placeholder => 'Email', "sn-autofocus" => 'true', "should-focus" => "true", :name => 'email', :required => true, :type => 'email', 'ng-model' => 'formData.email'} + %input{:placeholder => 'Password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'formData.user_password'} + %input{:placeholder => 'Confirm Password', "ng-if" => "formData.showRegister", :name => 'password', :required => true, :type => 'password', 'ng-model' => 'formData.password_conf'} + + %a.panel-row{"ng-click" => "formData.showAdvanced = !formData.showAdvanced"} + Advanced Options + .notification.info{"ng-if" => "formData.showRegister"} + %h2.title No Password Reset. + .text Because your notes are encrypted using your password, Standard Notes does not have a password reset option. You cannot forget your password. + .advanced-options.panel-row{"ng-if" => "formData.showAdvanced"} + .panel-column.stretch + %label Sync Server Domain + %input.form-control.mt-5{:name => 'server', :placeholder => 'Server URL', :required => true, :type => 'text', 'ng-model' => 'formData.url'} + + .button-group.stretch.panel-row.form-submit + %button.button.info.featured{"type" => "submit"} + .label {{formData.showLogin ? "Sign In" : "Register"}} + + %label + %input{"type" => "checkbox", "ng-model" => "formData.ephemeral", "ng-true-value" => "false", "ng-false-value" => "true"} + Stay signed in + %label{"ng-if" => "notesAndTagsCount() > 0"} + %input{"type" => "checkbox", "ng-model" => "formData.mergeLocal", "ng-bind" => "true", "ng-change" => "mergeLocalChanged()"} + Merge local data ({{notesAndTagsCount()}} notes and tags) + + %em.block.center-align.mt-10{"ng-if" => "formData.status", "style" => "font-size: 14px;"} + {{formData.status}} + + .panel-section{"ng-if" => "formData.mfa"} + %form{"ng-submit" => "submitMfaForm()"} + %p {{formData.mfa.message}} + %input.form-control.mt-10{:placeholder => "Enter Code", "sn-autofocus" => "true", "should-focus" => "true", :autofocus => "true", :name => 'mfa', :required => true, 'ng-model' => 'formData.userMfaCode'} + .button-group.stretch.panel-row.form-submit + %button.button.info.featured{"type" => "submit"} + .label Sign In + + %div{"ng-if" => "!formData.showLogin && !formData.showRegister && !formData.mfa"} + .panel-section{"ng-if" => "user"} + .panel-row + %h2.title.wrap {{user.email}} + .horizontal-group{"delay-hide" => "true", "show" => "syncStatus.syncOpInProgress || syncStatus.needsMoreSync", "delay" => "1000"} + .spinner.small.info + .sublabel + {{"Syncing" + (syncStatus.total > 0 ? ":" : "")}} + %span{"ng-if" => "syncStatus.total > 0"} {{syncStatus.current}}/{{syncStatus.total}} + + .subtitle.danger.panel-row{"ng-if" => "syncStatus.error"} Error syncing: {{syncStatus.error.message}} + + .subtitle.subtle.normal {{server}} + + .panel-row + + %a.panel-row.condensed{"ng-click" => "newPasswordData.changePassword = !newPasswordData.changePassword"} Change Password + .notification.warning{"ng-if" => "newPasswordData.changePassword"} + %h1.title Change Password + .text + %p Since your encryption key is based on your password, changing your password requires all your notes and tags to be re-encrypted using your new key. + %p If you have thousands of items, this can take several minutes — you must keep the application window open during this process. + %p After changing your password, you must log out of all other applications currently signed in to your account. + %p.bold It is highly recommended you download a backup of your data before proceeding. + .panel-row{"ng-if" => "!newPasswordData.status"} + .horizontal-group{"ng-if" => "!newPasswordData.showForm"} + %a.red{"ng-click" => "showPasswordChangeForm()"} Continue + %a{"ng-click" => "newPasswordData.changePassword = false; newPasswordData.showForm = false"} Cancel + .panel-row{"ng-if" => "newPasswordData.showForm"} + %form.panel-form.stretch + %input{:type => 'password', "ng-model" => "newPasswordData.newPassword", "placeholder" => "Enter new password"} + %input{:type => 'password', "ng-model" => "newPasswordData.newPasswordConfirmation", "placeholder" => "Confirm new password"} + .button-group.stretch.panel-row.form-submit + .button.info{"type" => "submit", "ng-click" => "submitPasswordChange()"} + .label Submit + %a{"ng-click" => "newPasswordData.changePassword = false; newPasswordData.showForm = false"} Cancel + + %p.italic.mt-10{"ng-if" => "newPasswordData.status"} {{newPasswordData.status}} + + + + %a.panel-row.condensed{"ng-click" => "showAdvanced = !showAdvanced"} Advanced + %div{"ng-if" => "showAdvanced"} + %a.panel-row{"ng-click" => "reencryptPressed()"} Resync All Items + + + %a.panel-row.condensed{"ng-if" => "securityUpdateAvailable()", "ng-click" => "clickedSecurityUpdate()"} Security Update Available + .notification.default{"ng-if" => "securityUpdateData.showForm"} + %p + %a{"href" => "https://standardnotes.org/help/security-update", "target" => "_blank"} Learn more. + %form.panel-form.stretch{"ng-if" => "!securityUpdateData.processing", "ng-submit" => "submitSecurityUpdateForm()"} + %p Enter your password to update: + %input.panel-row{:type => 'password', "ng-model" => "securityUpdateData.password", "placeholder" => "Enter password"} + .button-group.stretch.panel-row.form-submit + %button.button.info{"ng-type" => "submit"} + .label Update + .panel-row{"ng-if" => "securityUpdateData.processing"} + %p.info Processing... + + + .panel-section + %h3.title.panel-row Encryption + %h5.subtitle.info.panel-row{"ng-if" => "encryptionEnabled()"} + {{encryptionStatusForNotes()}} + %p + {{encryptionStatusString()}} + + .panel-section + %h3.title.panel-row Passcode Lock + %div{"ng-if" => "!hasPasscode()"} + .panel-row{"ng-if" => "!formData.showPasscodeForm"} + .button.info{"ng-click" => "addPasscodeClicked(); $event.stopPropagation();"} + .label Add Passcode + + %p Add an app passcode to lock the app and encrypt on-device key storage. + + %form{"ng-if" => "formData.showPasscodeForm", "ng-submit" => "submitPasscodeForm()"} + %input.form-control{:type => 'password', "ng-model" => "formData.passcode", "placeholder" => "Passcode", "sn-autofocus" => "true", "should-focus" => "true"} + %input.form-control{:type => 'password', "ng-model" => "formData.confirmPasscode", "placeholder" => "Confirm Passcode"} + .button-group.stretch.panel-row.form-submit + %button.button.info{"type" => "submit"} + .label Set Passcode + %a.panel-row{"ng-click" => "formData.showPasscodeForm = false"} Cancel + + %div{"ng-if" => "hasPasscode() && !formData.showPasscodeForm"} + .panel-row + %p + Passcode lock is enabled. + %span{"ng-if" => "isDesktopApplication()"} Your passcode will be required on new sessions after app quit. + .panel-row.justify-left + .horizontal-group + %a.info{"ng-click" => "changePasscodePressed()"} Change Passcode + %a.danger{"ng-click" => "removePasscodePressed()"} Remove Passcode + + + + .panel-section{"ng-if" => "!importData.loading"} + %h3.title Data Backups + %form.panel-form{"ng-if" => "encryptedBackupsAvailable()"} + .input-group + %label + %input{"type" => "radio", "ng-model" => "archiveFormData.encrypted", "ng-value" => "true", "ng-change" => "archiveFormData.encrypted = true"} + Encrypted + %label + %input{"type" => "radio", "ng-model" => "archiveFormData.encrypted", "ng-value" => "false", "ng-change" => "archiveFormData.encrypted = false"} + Decrypted + + .button-group + .button.info{"ng-click" => "downloadDataArchive()", "ng-class" => "{'mt-5' : !user}"} + .label Download Backup + + %label.button.info + %input{"type" => "file", "style" => "display: none;", "file-change" => "->", "handler" => "importFileSelected(files)"} + .label Import From Backup + + %div{"ng-if" => "importData.requestPassword"} + %form.panel-form.stretch{"ng-submit" => "submitImportPassword()"} + %p Enter the account password associated with the import file. + %input.form-control.mt-5{:type => 'password', "placeholder" => "Enter File Account Password", "ng-model" => "importData.password", "autofocus" => "true"} + .button-group.stretch.panel-row.form-submit + %button.button.info{"type" => "submit"} + .label Decrypt & Import + .panel-row + .spinner.small.info{"ng-if" => "importData.loading"} + .footer + %a.right{"ng-if" => "formData.showLogin || formData.showRegister", "ng-click" => "formData.showLogin = false; formData.showRegister = false;"} + Cancel + %a.right{"ng-if" => "!formData.showLogin && !formData.showRegister", "ng-click" => "destroyLocalData()"} + {{ user ? "Sign out and clear local data" : "Clear all local data" }} diff --git a/app/assets/templates/directives/actions-menu.html.haml b/app/assets/templates/directives/actions-menu.html.haml new file mode 100644 index 000000000..52470f972 --- /dev/null +++ b/app/assets/templates/directives/actions-menu.html.haml @@ -0,0 +1,32 @@ +.sn-component + .menu-panel.dropdown-menu + + %a.no-decoration{"ng-if" => "extensions.length == 0", "href" => "https://standardnotes.org/extensions", "target" => "blank"} + %menu-row{"title" => "'Download Actions'"} + + %div{"ng-repeat" => "extension in extensions"} + .header{"ng-click" => "extension.hide = !extension.hide; $event.stopPropagation();"} + .column + %h4.title {{extension.name}} + .spinner.small.loading{"ng-if" => "extension.loading"} + %div{"ng-if" => "extension.hide"} … + + %menu-row{"ng-if" => "!extension.hide", "ng-repeat" => "action in extension.actionsWithContextForItem(item)", + "ng-click" => "executeAction(action, extension); $event.stopPropagation();", "title" => "action.label", "subtitle" => "action.desc", + "spinner-class" => "action.running ? 'info' : null", "sub-rows" => "action.subrows"} + .sublabel{"ng-if" => "action.access_type"} + Uses + %strong {{action.access_type}} + access to this note. + + +.modal.medium{"ng-if" => "renderData.showRenderModal", "ng-click" => "$event.stopPropagation();"} + .content + .sn-component + .panel + .header + %h1.title Preview + %a.close-button.info{"ng-click" => "renderData.showRenderModal = false; $event.stopPropagation();"} Close + .content.selectable + %h2 {{renderData.title}} + %p.normal{"style" => "white-space: pre-wrap; font-family: monospace; font-size: 16px;"} {{renderData.text}} diff --git a/app/assets/templates/directives/component-modal.html.haml b/app/assets/templates/directives/component-modal.html.haml new file mode 100644 index 000000000..1229327cc --- /dev/null +++ b/app/assets/templates/directives/component-modal.html.haml @@ -0,0 +1,10 @@ +.background{"ng-click" => "dismiss()"} + +.content + .sn-component + .panel{"ng-attr-id" => "component-{{component.uuid}}"} + .header + %h1.title + {{component.name}} + %a.close-button.info{"ng-click" => "dismiss()"} Close + %component-view.component-view{"component" => "component"} diff --git a/app/assets/templates/directives/component-view.html.haml b/app/assets/templates/directives/component-view.html.haml new file mode 100644 index 000000000..e507ea060 --- /dev/null +++ b/app/assets/templates/directives/component-view.html.haml @@ -0,0 +1,70 @@ +.sn-component{"ng-if" => "error == 'expired'"} + .panel.static + .content + .panel-section.stretch + %h2.title Unable to load Standard Notes Extended + %p Your Extended subscription expired on {{component.dateToLocalizedString(component.valid_until)}}. + %p + Please visit + %a{"href" => "https://dashboard.standardnotes.org", "target" => "_blank"} dashboard.standardnotes.org + to renew your subscription, then open the "Extensions" menu via the bottom menu of the app to refresh your account data. + Afterwards, press the button below to attempt to reload this component. + .panel-row + .button.info{"ng-if" => "!reloading", "ng-click" => "reloadStatus()"} + .label Reload + .spinner.info.small{"ng-if" => "reloading"} + + .panel-row + .panel-row + .panel-column + %p Otherwise, please follow the steps below to disable any external editors, so you can edit your note using the plain text editor instead. + + %p + %ol + %li Click the "Editor" menu item above (under the note title). + %li Select "Plain Editor". + %li Repeat this for every note you'd like to access. You can also delete the editor completely to disable it for all notes. To do so, click "Extensions" in the lower left corner of the app, then, for every editor, click "Uninstall". + + %p + Need help? Please email us at + %a{"href" => "mailto:hello@standardnotes.org", "target" => "_blank"} hello@standardnotes.org + or check out the + %a{"href" => "https://standardnotes.org/help", "target" => "_blank"} Help + page. + +.sn-component{"ng-if" => "error == 'offline-restricted'"} + .panel.static + .content + .panel-section.stretch + %h2.title You have restricted this extension to be used offline only. + %p Offline extensions are not available in the Web app. + .panel-row + .panel-column + %p You can either: + %p + %ul + %li Enable the Hosted option for this extension by opening the 'Extensions' menu and toggling 'Use hosted when local is unavailable' under this extension's options. Then press Reload below. + %li Use the Desktop application. + .panel-row + .button.info{"ng-if" => "!reloading", "ng-click" => "reloadStatus()"} + .label Reload + .spinner.info.small{"ng-if" => "reloading"} + +.sn-component{"ng-if" => "error == 'url-missing'"} + .panel.static + .content + .panel-section.stretch + %h2.title This extension is not installed correctly. + %p Please uninstall {{component.name}}, then re-install it. + + %p + This issue can occur if you access Standard Notes using an older version of the app. + Ensure you are running at least version 2.1 on all platforms. + + +%iframe{"ng-if" => "component && componentValid", + "ng-attr-id" => "component-{{component.uuid}}", + "ng-src" => "{{getUrl() | trusted}}", "frameBorder" => "0", + "sandbox" => "allow-scripts allow-top-navigation-by-user-activation allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-modals allow-forms", + "data-component-id" => "{{component.uuid}}"} + Loading diff --git a/app/assets/templates/directives/editor-menu.html.haml b/app/assets/templates/directives/editor-menu.html.haml new file mode 100644 index 000000000..6787ab986 --- /dev/null +++ b/app/assets/templates/directives/editor-menu.html.haml @@ -0,0 +1,26 @@ +.sn-component + .menu-panel.dropdown-menu + .section + .header + %h4.title Note Editor + %menu-row{"title" => "'Plain Editor'", "circle" => "selectedEditor == null && 'success'", "ng-click" => "selectComponent($event, null)"} + + %menu-row{"ng-repeat" => "editor in editors", "ng-click" => "selectComponent($event, editor)", "title" => "editor.name", + "circle" => "selectedEditor === editor && 'success'", + "has-button" => "selectedEditor == editor || defaultEditor == editor", "button-text" => "defaultEditor == editor ? 'Undefault' : 'Set Default'", + "button-action" => "toggleDefaultForEditor(editor)", "button-class" => "defaultEditor == editor ? 'warning' : 'info'"} + .column{"ng-if" => "component.conflict_of || shouldDisplayRunningLocallyLabel(editor)"} + %strong.red.medium{"ng-if" => "editor.conflict_of"} Conflicted copy + .sublabel{"ng-if" => "shouldDisplayRunningLocallyLabel(editor)"} Running Locally + + %a.no-decoration{"ng-if" => "editors.length == 0", "href" => "https://standardnotes.org/extensions", "target" => "blank"} + %menu-row{"title" => "'Download More Editors'"} + + .section{"ng-if" => "stack.length > 0"} + .header + %h4.title Editor Stack + %menu-row{"ng-repeat" => "component in stack", "ng-click" => "selectComponent($event, component)", "title" => "component.name", + "circle" => "stackComponentEnabled(component) ? 'success' : 'danger'"} + .column{"ng-if" => "component.conflict_of || shouldDisplayRunningLocallyLabel(component)"} + %strong.red.medium{"ng-if" => "component.conflict_of"} Conflicted copy + .sublabel{"ng-if" => "shouldDisplayRunningLocallyLabel(component)"} Running Locally diff --git a/app/assets/templates/directives/menu-row.html.haml b/app/assets/templates/directives/menu-row.html.haml new file mode 100644 index 000000000..7bbff5528 --- /dev/null +++ b/app/assets/templates/directives/menu-row.html.haml @@ -0,0 +1,21 @@ +.row + .column + .left + .column{"ng-if" => "circle"} + .circle.small{"ng-class" => "circle"} + .column{"ng-class" => "{'faded' : faded}"} + .label + {{title}} + .sublabel{"ng-if" => "subtitle"} + {{subtitle}} + %ng-transclude + .subrows{"ng-if" => "subRows && subRows.length > 0"} + %menu-row{"ng-repeat" => "row in subRows", "ng-click" => "row.onClick($event); $event.stopPropagation();", + "title" => "row.title", "subtitle" => "row.subtitle", "spinner-class" => "row.spinnerClass"} + + .column{"ng-if" => "hasButton"} + .button.info{"ng-click" => "clickButton($event)", "ng-class" => "buttonClass"} + {{buttonText}} + + .column{"ng-if" => "spinnerClass"} + .spinner.small{"ng-class" => "spinnerClass"} diff --git a/app/assets/templates/directives/panel-resizer.html.haml b/app/assets/templates/directives/panel-resizer.html.haml new file mode 100644 index 000000000..b2822c8c3 --- /dev/null +++ b/app/assets/templates/directives/panel-resizer.html.haml @@ -0,0 +1 @@ +.panel-resizer-column diff --git a/app/assets/templates/directives/permissions-modal.html.haml b/app/assets/templates/directives/permissions-modal.html.haml new file mode 100644 index 000000000..63a5dc38f --- /dev/null +++ b/app/assets/templates/directives/permissions-modal.html.haml @@ -0,0 +1,22 @@ +.background{"ng-click" => "deny()"} + +.content#permissions-modal + .sn-component + .panel + .header + %h1.title Activate Extension + %a.close-button.info{"ng-click" => "deny()"} Cancel + .content + .panel-section + .panel-row + %h3 + %strong {{component.name}} + would like to interact with your + {{permissionsString()}} + + .panel-row + %p + Extensions use an offline messaging system to communicate. Learn more at + %a{"href" => "https://standardnotes.org/permissions", "target" => "_blank"} https://standardnotes.org/permissions. + .footer + .button.info.big.block.bold{"ng-click" => "accept()"} Continue diff --git a/app/assets/templates/editor.html.haml b/app/assets/templates/editor.html.haml new file mode 100644 index 000000000..31c4a0a6c --- /dev/null +++ b/app/assets/templates/editor.html.haml @@ -0,0 +1,59 @@ +.section.editor#editor-column + #editor-title-bar.section-title-bar{"ng-show" => "ctrl.note && !ctrl.note.errorDecrypting"} + .title + %input.input#note-title-editor{"ng-model" => "ctrl.note.title", "ng-keyup" => "$event.keyCode == 13 && ctrl.saveTitle($event)", + "ng-change" => "ctrl.nameChanged()", "ng-focus" => "ctrl.onNameFocus()", "ng-blur" => "ctrl.onNameBlur()", + "select-on-click" => "true"} + + #save-status{"ng-class" => "{'red bold': ctrl.saveError, 'warning bold': ctrl.syncTakingTooLong}", "ng-bind-html" => "ctrl.noteStatus"} + + .editor-tags + #note-tags-component-container{"ng-if" => "ctrl.tagsComponent"} + %component-view.component-view{ "component" => "ctrl.tagsComponent"} + %input.tags-input{"ng-if" => "!(ctrl.tagsComponent && ctrl.tagsComponent.active)", "type" => "text", "ng-keyup" => "$event.keyCode == 13 && $event.target.blur();", + "ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)", + "spellcheck" => "false"} + + .sn-component{"ng-if" => "ctrl.note"} + .app-bar.no-edges + .left + .item{"ng-click" => "ctrl.showMenu = !ctrl.showMenu; ctrl.showExtensions = false; ctrl.showEditorMenu = false;", "ng-class" => "{'selected' : ctrl.showMenu}", "click-outside" => "ctrl.showMenu = false;", "is-open" => "ctrl.showMenu"} + .label Menu + .menu-panel.dropdown-menu{"ng-if" => "ctrl.showMenu"} + .section + .header + %h4.title Note Options + %menu-row{"title" => "ctrl.note.pinned ? 'Unpin' : 'Pin'", "ng-click" => "ctrl.selectedMenuItem($event, true); ctrl.togglePin()"} + %menu-row{"title" => "ctrl.note.archived ? 'Unarchive' : 'Archive'", "ng-click" => "ctrl.selectedMenuItem($event, true); ctrl.toggleArchiveNote()"} + %menu-row{"title" => "'Delete'", "ng-click" => "ctrl.selectedMenuItem($event); ctrl.deleteNote()"} + + .section{"ng-if" => "!ctrl.selectedEditor"} + .header + %h4.title Global Display + %menu-row{"title" => "'Monospace Font'", "circle" => "ctrl.monospaceFont ? 'success' : 'default'", "ng-click" => "ctrl.selectedMenuItem($event, true); ctrl.toggleKey('monospaceFont')"} + %menu-row{"title" => "'Spellcheck'", "circle" => "ctrl.spellcheck ? 'success' : 'default'", "ng-click" => "ctrl.selectedMenuItem($event, true); ctrl.toggleKey('spellcheck')"} + + .item{"ng-click" => "ctrl.onEditorMenuClick()", "ng-class" => "{'selected' : ctrl.showEditorMenu}", "click-outside" => "ctrl.showEditorMenu = false;", "is-open" => "ctrl.showEditorMenu"} + .label Editor + %editor-menu{"ng-if" => "ctrl.showEditorMenu", "callback" => "ctrl.editorMenuOnSelect", "selected-editor" => "ctrl.selectedEditor", "current-item" => "ctrl.note"} + + .item{"ng-click" => "ctrl.showExtensions = !ctrl.showExtensions; ctrl.showMenu = false; ctrl.showEditorMenu = false;", "ng-class" => "{'selected' : ctrl.showExtensions}", "click-outside" => "ctrl.showExtensions = false;", "is-open" => "ctrl.showExtensions"} + .label Actions + %actions-menu{"ng-if" => "ctrl.showExtensions", "item" => "ctrl.note"} + + .editor-content#editor-content{"ng-if" => "ctrl.noteReady && !ctrl.note.errorDecrypting"} + %panel-resizer.left{"panel-id" => "'editor-content'", "on-resize-finish" => "ctrl.onPanelResizeFinish","control" => "ctrl.resizeControl", "min-width" => 300, "property" => "'left'", "hoverable" => "true"} + %component-view.component-view{"ng-if" => "ctrl.selectedEditor", "component" => "ctrl.selectedEditor"} + %textarea.editable#note-text-editor{"ng-if" => "!ctrl.selectedEditor", "ng-model" => "ctrl.note.text", + "ng-change" => "ctrl.contentChanged()", "ng-trim" => "false", "ng-click" => "ctrl.clickedTextArea()", + "ng-focus" => "ctrl.onContentFocus()", "dir" => "auto", "ng-attr-spellcheck" => "{{ctrl.spellcheck}}"} + {{ctrl.onSystemEditorLoad()}} + %panel-resizer{"panel-id" => "'editor-content'", "on-resize-finish" => "ctrl.onPanelResizeFinish","control" => "ctrl.resizeControl", "min-width" => 300, "hoverable" => "true", "property" => "'right'"} + + %section.section{"ng-if" => "ctrl.note.errorDecrypting"} + %p.medium-padding{"style" => "padding-top: 0 !important;"} There was an error decrypting this item. Ensure you are running the latest version of this app, then sign out and sign back in to try again. + + #editor-pane-component-stack + .sn-component + %component-view.component-view.component-stack-item.border-color{"ng-repeat" => "component in ctrl.componentStack", + "ng-if" => "component.active", "ng-show" => "!component.hidden", "manual-dealloc" => "true", "component" => "component"} diff --git a/app/assets/templates/footer.html.haml b/app/assets/templates/footer.html.haml new file mode 100644 index 000000000..07c4de532 --- /dev/null +++ b/app/assets/templates/footer.html.haml @@ -0,0 +1,41 @@ +.sn-component + #footer-bar.app-bar.no-edges + .left + .item{"ng-click" => "ctrl.accountMenuPressed()", "click-outside" => "ctrl.showAccountMenu = false;", "is-open" => "ctrl.showAccountMenu"} + .column + .circle.small{"ng-class" => "ctrl.error ? 'danger' : (ctrl.getUser() ? 'info' : 'default')"} + .column + .label.title{"ng-class" => "{red: ctrl.error}"} Account + %account-menu{"ng-click" => "$event.stopPropagation()", "ng-if" => "ctrl.showAccountMenu", "on-successful-auth" => "ctrl.onAuthSuccess", "close-function" => "ctrl.closeAccountMenu"} + + .item + %a.no-decoration.label.title{"href" => "https://standardnotes.org/help", "target" => "_blank"} + Help + + .item.border + + .item{"ng-repeat" => "room in ctrl.rooms track by room.uuid"} + .column{"ng-click" => "ctrl.selectRoom(room)"} + .label {{room.name}} + %component-modal{"ng-if" => "room.showRoom", "component" => "room", "on-dismiss" => "ctrl.onRoomDismiss"} + + + .right + + .item{"ng-if" => "ctrl.newUpdateAvailable", "ng-click" => "ctrl.clickedNewUpdateAnnouncement()"} + %span.info.label New update downloaded. Installs on app restart. + + .item.no-pointer{"ng-if" => "ctrl.lastSyncDate && !ctrl.isRefreshing"} + .label.subtle + Last refreshed {{ctrl.lastSyncDate | appDateTime}} + .item{"ng-if" => "ctrl.lastSyncDate && ctrl.isRefreshing"} + .spinner.small + + .item{"ng-if" => "ctrl.offline"} + .label Offline + .item{"ng-if" => "!ctrl.offline", "ng-click" => "ctrl.refreshData()"} + .label Refresh + + .item#lock-item{"ng-if" => "ctrl.hasPasscode()"} + .label + %i.icon.ion-locked{"ng-if" => "ctrl.hasPasscode()", "ng-click" => "ctrl.lockApp()"} diff --git a/app/assets/templates/frontend/directives/contextual-menu.html.haml b/app/assets/templates/frontend/directives/contextual-menu.html.haml deleted file mode 100644 index 416bd3c24..000000000 --- a/app/assets/templates/frontend/directives/contextual-menu.html.haml +++ /dev/null @@ -1,33 +0,0 @@ -%ul.dropdown-menu.sectioned-menu - .extension{"ng-repeat" => "extension in extensions"} - .header{"ng-click" => "extension.hide = !extension.hide"} - .title {{extension.name}} - .subtitle - Will submit your note - %strong {{accessTypeForExtension(extension)}} - .spinner.loading{"ng-if" => "extension.loading"} - %div{"ng-if" => "extension.hide"} … - %ul{"ng-if" => "!extension.hide"} - %li.menu-item{"ng-repeat" => "action in extension.actionsWithContextForItem(item)", "ng-click" => "executeAction(action, extension);", - "ng-class" => "{'faded' : !isActionEnabled(action, extension)}"} - %label.menu-item-title {{action.label}} - .menu-item-subtitle {{action.desc}} - - .small.normal{"ng-if" => "!isActionEnabled(action, extension)"} - Requires {{action.access_type}} access to this note. - - %div{"ng-if" => "action.showNestedActions"} - %ul.mt-10 - %li.menu-item.white-bg.nested-hover{"ng-repeat" => "subaction in action.subactions", "ng-click" => "executeAction(subaction, extension, action); $event.stopPropagation();", "style" => "margin-top: -1px;"} - %label.menu-item-title {{subaction.label}} - .menu-item-subtitle {{subaction.desc}} - %span{"ng-if" => "subaction.running"} - .spinner{"style" => "margin-top: 3px;"} - - %span{"ng-if" => "action.running"} - .spinner{"style" => "margin-top: 3px;"} - -.extension-render-modal{"ng-if" => "renderData.showRenderModal", "ng-click" => "renderData.showRenderModal = false"} - .content - %h2 {{renderData.title}} - %p.normal{"style" => "white-space: pre-wrap; font-family: monospace; font-size: 16px;"} {{renderData.text}} diff --git a/app/assets/templates/frontend/directives/editor-menu.html.haml b/app/assets/templates/frontend/directives/editor-menu.html.haml deleted file mode 100644 index cad2f2d0b..000000000 --- a/app/assets/templates/frontend/directives/editor-menu.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -%ul.dropdown-menu.sectioned-menu - .header - .title System Editor - %ul - %li.menu-item{"ng-click" => "selectEditor($event, null)"} - %span.pull-left.mr-10{"ng-if" => "selectedEditor == null"} ✓ - %label.menu-item-title.pull-left Plain - - %div{"ng-if" => "editors.length > 0"} - .header - .title External Editors - .subtitle Can access your current note decrypted. - %ul - %li.menu-item{"ng-repeat" => "editor in editors", "ng-click" => "selectEditor($event, editor)"} - %strong.red.medium{"ng-if" => "editor.conflict_of"} Conflicted copy - %label.menu-item-title - %span.inline.tinted.mr-10{"ng-if" => "selectedEditor === editor"} ✓ - {{editor.name}} diff --git a/app/assets/templates/frontend/directives/global-extensions-menu.html.haml b/app/assets/templates/frontend/directives/global-extensions-menu.html.haml deleted file mode 100644 index fe8965304..000000000 --- a/app/assets/templates/frontend/directives/global-extensions-menu.html.haml +++ /dev/null @@ -1,138 +0,0 @@ -.panel.panel-default.account-panel.panel-right#global-ext-menu - .panel-body - .container - .float-group.h20 - %h1.tinted.pull-left Extensions - %a.block.pull-right.dashboard-link{"href" => "https://dashboard.standardnotes.org", "target" => "_blank"} Open Dashboard - %div.clear{"ng-if" => "!extensionManager.extensions.length && !themeManager.themes.length && !componentManager.components.length"} - %p Customize your experience with editors, themes, and actions. - .tinted-box.mt-10 - %h3 Available as part of the Extended subscription. - %p.mt-5 Note history - %p.mt-5 Automated backups - %p.mt-5 Editors, themes, and actions - %a{"href" => "https://standardnotes.org/extensions", "target" => "_blank"} - %button.mt-10 - %h3 Learn More - - %div{"ng-if" => "themeManager.themes.length > 0"} - .header.container.section-margin - %h2 Themes - %ul - %li{"ng-repeat" => "theme in themeManager.themes | orderBy: 'name'", "ng-click" => "clickedExtension(theme)"} - .container - %h3 - %input.bold{"ng-if" => "theme.rename", "ng-model" => "theme.tempName", "ng-keyup" => "$event.keyCode == 13 && submitExtensionRename(theme);", "mb-autofocus" => "true", "should-focus" => "true"} - %span{"ng-if" => "!theme.rename"} {{theme.name}} - %a{"ng-if" => "!themeManager.isThemeActive(theme)", "ng-click" => "themeManager.activateTheme(theme); $event.stopPropagation();"} Activate - %a{"ng-if" => "themeManager.isThemeActive(theme)", "ng-click" => "themeManager.deactivateTheme(theme); $event.stopPropagation();"} Deactivate - .mt-3{"ng-if" => "theme.showDetails"} - .link-group - %a{"ng-click" => "renameExtension(theme); $event.stopPropagation();"} Rename - %a{"ng-click" => "theme.showLink = !theme.showLink; $event.stopPropagation();"} Show Link - %a.red{"ng-click" => "deleteTheme(theme); $event.stopPropagation();"} Delete - %p.small.selectable.wrap{"ng-if" => "theme.showLink"} - {{theme.url}} - - - %div{"ng-if" => "extensionManager.extensions.length"} - .header.container.section-margin - %h2 Actions - %p{"style" => "margin-top: 3px;"} Choose "Actions" in the note editor to use installed actions. - - %ul - %li{"ng-repeat" => "extension in extensionManager.extensions | orderBy: 'name'", "ng-init" => "extension.formData = {}", "ng-click" => "clickedExtension(extension)"} - .container - %h3 - %input.bold{"ng-if" => "extension.rename", "ng-model" => "extension.tempName", "ng-keyup" => "$event.keyCode == 13 && submitExtensionRename(extension);", "mb-autofocus" => "true", "should-focus" => "true"} - %span{"ng-if" => "!extension.rename"} {{extension.name}} - %p.small{"ng-if" => "extension.description"} {{extension.description}} - %div{"ng-if" => "extension.showDetails"} - .mt-10 - %label.block Access Type - %label.normal.block{"ng-click" => " $event.stopPropagation();"} - %input{"type" => "radio", "ng-model" => "extension.encrypted", "ng-value" => "true", "ng-change" => "changeExtensionEncryptionFormat(true, extension);"} - Encrypted - %label.normal.block{"ng-click" => " $event.stopPropagation();"} - %input{"type" => "radio", "ng-model" => "extension.encrypted", "ng-value" => "false", "ng-change" => "changeExtensionEncryptionFormat(false, extension);"} - Decrypted - - .small-v-space - - %ul{"ng-repeat" => "action in extension.actionsInGlobalContext()"} - %li - %label.block {{action.label}} - %em{"style" => "font-style: italic;"} {{action.desc}} - %em{"ng-if" => "action.repeat_mode == 'watch'"} - Repeats when a change is made to your items. - %em{"ng-if" => "action.repeat_mode == 'loop'"} - Repeats at most once every {{action.repeat_timeout}} seconds - %div - %a{"ng-click" => "action.showPermissions = !action.showPermissions"} {{action.showPermissions ? "Hide permissions" : "Show permissions"}} - %div{"ng-if" => "action.showPermissions"} - {{action.permissionsString()}} - %label.block.normal {{action.encryptionModeString()}} - - %div - .mt-5{"ng-if" => "action.repeat_mode"} - %button.light.tinted{"ng-if" => "extensionManager.isRepeatActionEnabled(action)", "ng-click" => "extensionManager.disableRepeatAction(action, extension); $event.stopPropagation();"} Disable - %button.light.tinted{"ng-if" => "!extensionManager.isRepeatActionEnabled(action)", "ng-click" => "extensionManager.enableRepeatAction(action, extension); $event.stopPropagation();"} Enable - %button.light.mt-10{"ng-if" => "!action.running && !action.repeat_mode", "ng-click" => "selectedAction(action, extension); $event.stopPropagation();"} - Perform Action - .spinner.mb-5.block{"ng-if" => "action.running"} - %p.mb-5.mt-5.small{"ng-if" => "!action.error && action.lastExecuted && !action.running"} - Last run {{action.lastExecuted | appDateTime}} - %label.red{"ng-if" => "action.error"} - Error performing action. - - %a.block.mt-5{"ng-click" => "renameExtension(extension); $event.stopPropagation();"} Rename - %a.block.mt-5{"ng-click" => "extension.showURL = !extension.showURL; $event.stopPropagation();"} Show Link - %p.wrap.selectable.small{"ng-if" => "extension.showURL"} {{extension.url}} - %a.block.mt-5{"ng-click" => "deleteActionExtension(extension); $event.stopPropagation();"} Delete - - %div{"ng-if" => "componentManager.components.length > 0"} - .header.container.section-margin - %h2 Components - %ul - %li{"ng-repeat" => "component in componentManager.components | orderBy: 'name'", "ng-click" => "clickedExtension(component)"} - .container - %h3 - %input.bold{"ng-if" => "component.rename", "ng-model" => "component.tempName", "ng-keyup" => "$event.keyCode == 13 && submitExtensionRename(component);", "mb-autofocus" => "true", "should-focus" => "true"} - %span{"ng-if" => "!component.rename"} {{component.name}} - - %div{"ng-if" => "component.isEditor()"} - %a{"ng-if" => "!component.isDefaultEditor()", "ng-click" => "makeEditorDefault(component); $event.stopPropagation();"} Make Default - %a{"ng-if" => "component.isDefaultEditor()", "ng-click" => "removeEditorDefault(component); $event.stopPropagation();"} Remove Default - %div{"ng-if" => "!component.isEditor()"} - %a{"ng-if" => "!componentManager.isComponentActive(component)", "ng-click" => "componentManager.activateComponent(component); $event.stopPropagation();"} Activate - %a{"ng-if" => "componentManager.isComponentActive(component)", "ng-click" => "componentManager.deactivateComponent(component); $event.stopPropagation();"} Deactivate - .mt-3{"ng-if" => "component.showDetails"} - .link-group - %a{"ng-click" => "renameExtension(component); $event.stopPropagation();"} Rename - %a{"ng-click" => "component.showLink = !component.showLink; $event.stopPropagation();"} Show Link - %a{"ng-if" => "component.permissions.length", "ng-click" => "revokePermissions(component); $event.stopPropagation();"} Revoke Permissions - %a.red{"ng-click" => "deleteComponent(component); $event.stopPropagation();"} Delete - %p.small.selectable.wrap{"ng-if" => "component.showLink"} - {{component.url}} - - %div{"ng-if" => "serverExtensions.length > 0"} - .header.container.section-margin - %h2 Server Extensions - %ul - %li{"ng-repeat" => "ext in serverExtensions", "ng-click" => "ext.showDetails = !ext.showDetails"} - .container - %strong.red.medium{"ng-if" => "ext.conflict_of"} Conflicted copy - %h3 {{nameForServerExtension(ext)}} - %div.mt-3{"ng-if" => "ext.showDetails"} - .link-group - %a{"ng-click" => "ext.showUrl = !ext.showUrl; $event.stopPropagation();"} Show Link - %a.red{ "ng-click" => "deleteServerExt(ext); $event.stopPropagation();"} Delete - .wrap.mt-5.selectable{"ng-if" => "ext.showUrl"} {{ext.url}} - - .container.section-margin - %h2.tinted Install - %p.faded Enter an install link - %form.mt-10.mb-10 - %input.form-control{:autofocus => 'autofocus', :name => 'url', :required => true, :autocomplete => "off", - :type => 'url', 'ng-model' => 'formData.installLink', "ng-keyup" => "$event.keyCode == 13 && submitInstallLink();"} - %p.tinted{"ng-if" => "formData.successfullyInstalled"} Successfully installed extension. diff --git a/app/assets/templates/frontend/directives/permissions-modal.html.haml b/app/assets/templates/frontend/directives/permissions-modal.html.haml deleted file mode 100644 index 0ca1c7f70..000000000 --- a/app/assets/templates/frontend/directives/permissions-modal.html.haml +++ /dev/null @@ -1,25 +0,0 @@ -.background{"ng-click" => "dismiss()"} - -.content - %h3 The following extension has requested these permissions: - - %h4 Extension - %p Name: {{component.name}} - %p.wrap URL: {{component.url}} - - %h4 Permissions - .permission{"ng-repeat" => "permission in formattedPermissions"} - %p {{permission}} - - %h4 Status - %p.status{"ng-class" => "{'trusted tinted' : component.trusted}"} {{component.trusted ? 'Trusted' : 'Untrusted'}} - - .learn-more - %h4 Details - %p - Extensions use an offline messaging system to communicate. With Trusted extensions, data is never sent remotely without your consent. Learn more about extension permissions at - %a{"href" => "https://standardnotes.org/permissions", "target" => "_blank"} https://standardnotes.org/permissions. - - .buttons - %button.standard.white{"ng-click" => "deny()"} Deny - %button.standard.tinted{"ng-click" => "accept()"} Accept diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml deleted file mode 100644 index e6cb98055..000000000 --- a/app/assets/templates/frontend/editor.html.haml +++ /dev/null @@ -1,62 +0,0 @@ -.section.editor{"ng-class" => "{'fullscreen' : ctrl.fullscreen}"} - #editor-title-bar.section-title-bar{"ng-show" => "ctrl.note && !ctrl.note.errorDecrypting", "ng-class" => "{'fullscreen' : ctrl.fullscreen }"} - .title - %input.input#note-title-editor{"ng-model" => "ctrl.note.title", "ng-keyup" => "$event.keyCode == 13 && ctrl.saveTitle($event)", - "ng-change" => "ctrl.nameChanged()", "ng-focus" => "ctrl.onNameFocus()", - "select-on-click" => "true"} - - #save-status{"ng-class" => "{'red bold': ctrl.saveError, 'orange bold': ctrl.syncTakingTooLong}", "ng-bind-html" => "ctrl.noteStatus"} - - .editor-tags - #note-tags-component-container{"ng-if" => "ctrl.tagsComponent && ctrl.tagsComponent.active"} - %iframe#note-tags-iframe{"ng-src" => "{{ctrl.tagsComponent.url | trusted}}", "frameBorder" => "0", "sandbox" => "allow-scripts", "data-component-id" => "{{ctrl.tagsComponent.uuid}}"} - %input.tags-input{"ng-if" => "!(ctrl.tagsComponent && ctrl.tagsComponent.active)", "type" => "text", "ng-keyup" => "$event.keyCode == 13 && $event.target.blur();", - "ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)"} - %ul.section-menu-bar{"ng-if" => "ctrl.note"} - %li{"ng-class" => "{'selected' : ctrl.showMenu}", "click-outside" => "ctrl.showMenu = false;", "is-open" => "ctrl.showMenu"} - %label{"ng-click" => "ctrl.showMenu = !ctrl.showMenu; ctrl.showExtensions = false; ctrl.showEditorMenu = false;"} Menu - - %ul.dropdown-menu.sectioned-menu{"ng-if" => "ctrl.showMenu"} - %li - %label{"ng-click" => "ctrl.selectedMenuItem($event); ctrl.togglePin()"} - %i.icon.ion-ios-flag - {{ctrl.note.pinned ? "Unpin" : "Pin"}} - %li - %label{"ng-click" => "ctrl.selectedMenuItem($event); ctrl.toggleArchiveNote()"} - %i.icon.ion-ios-box - {{ctrl.note.archived ? "Unarchive" : "Archive"}} - %li - %label{"ng-click" => "ctrl.selectedMenuItem($event); ctrl.deleteNote()"} - %i.icon.ion-trash-b - Delete - %li - %label{"ng-click" => "ctrl.selectedMenuItem($event); ctrl.toggleFullScreen()"} - %i.icon.ion-arrow-expand - Toggle Fullscreen - - %li{"ng-if" => "ctrl.hasDisabledStackComponents()"} - %label{"ng-click" => "ctrl.selectedMenuItem($event); ctrl.restoreDisabledStackComponents()"} Restore Disabled Components - - %li{"ng-class" => "{'selected' : ctrl.showEditorMenu}", "click-outside" => "ctrl.showEditorMenu = false;", "is-open" => "ctrl.showEditorMenu"} - %label{"ng-click" => "ctrl.showEditorMenu = !ctrl.showEditorMenu; ctrl.showMenu = false; ctrl.showExtensions = false;"} Editor - %editor-menu{"ng-if" => "ctrl.showEditorMenu", "callback" => "ctrl.selectedEditor", "selected-editor" => "ctrl.editorComponent"} - - %li{"ng-class" => "{'selected' : ctrl.showExtensions}", "ng-if" => "ctrl.hasAvailableExtensions()", "click-outside" => "ctrl.showExtensions = false;", "is-open" => "ctrl.showExtensions"} - %label{"ng-click" => "ctrl.showExtensions = !ctrl.showExtensions; ctrl.showMenu = false; ctrl.showEditorMenu = false;"} Actions - %contextual-extensions-menu{"ng-if" => "ctrl.showExtensions", "item" => "ctrl.note"} - - .editor-content{"ng-if" => "ctrl.noteReady && !ctrl.note.errorDecrypting", "ng-class" => "{'fullscreen' : ctrl.fullscreen }"} - %iframe#editor-iframe{"ng-if" => "ctrl.editorComponent && ctrl.editorComponent.active", "ng-src" => "{{ctrl.editorComponent.url | trusted}}", "data-component-id" => "{{ctrl.editorComponent.uuid}}", "frameBorder" => "0", "style" => "width: 100%;"} - Loading - %textarea.editable#note-text-editor{"ng-if" => "!ctrl.editorComponent", "ng-class" => "{'fullscreen' : ctrl.fullscreen }", "ng-model" => "ctrl.note.text", - "ng-change" => "ctrl.contentChanged()", "ng-click" => "ctrl.clickedTextArea()", "ng-focus" => "ctrl.onContentFocus()", "dir" => "auto"} - {{ctrl.onSystemEditorLoad()}} - - - %section.section{"ng-if" => "ctrl.note.errorDecrypting"} - %p.medium-padding{"style" => "padding-top: 0 !important;"} There was an error decrypting this item. Ensure you are running the latest version of this app, then sign out and sign back in to try again. - - #editor-pane-component-stack - .component.component-stack-border{"ng-repeat" => "component in ctrl.componentStack", "ng-if" => "component.active", "ng-show" => "!component.ignoreEvents", "id" => "{{'component-' + component.uuid}}", "ng-mouseover" => "component.showExit = true", "ng-mouseleave" => "component.showExit = false"} - .exit-button.body-text-color{"ng-if" => "component.showExit", "ng-click" => "ctrl.disableComponentForCurrentItem(component, true)"} × - %iframe{"ng-src" => "{{component.url | trusted}}", "frameBorder" => "0", "sandbox" => "allow-scripts allow-top-navigation-by-user-activation allow-popups allow-popups-to-escape-sandbox allow-modals", "data-component-id" => "{{component.uuid}}"} diff --git a/app/assets/templates/frontend/footer.html.haml b/app/assets/templates/frontend/footer.html.haml deleted file mode 100644 index 1f7506f45..000000000 --- a/app/assets/templates/frontend/footer.html.haml +++ /dev/null @@ -1,31 +0,0 @@ -#footer-bar - .pull-left - .footer-bar-link{"click-outside" => "ctrl.showAccountMenu = false;", "is-open" => "ctrl.showAccountMenu"} - %a{"ng-click" => "ctrl.accountMenuPressed()", "ng-class" => "{red: ctrl.error}"} Account - %account-menu{"ng-if" => "ctrl.showAccountMenu", "on-successful-auth" => "ctrl.onAuthSuccess"} - - .footer-bar-link{"click-outside" => "ctrl.showExtensionsMenu = false;", "is-open" => "ctrl.showExtensionsMenu"} - %a{"ng-click" => "ctrl.toggleExtensions()"} Extensions - %global-extensions-menu{"ng-if" => "ctrl.showExtensionsMenu"} - - .footer-bar-link - %a{"href" => "https://standardnotes.org/help", "target" => "_blank"} - Help - - .pull-right - - .footer-bar-link{"ng-if" => "ctrl.newUpdateAvailable", "ng-click" => "ctrl.clickedNewUpdateAnnouncement()"} - %span.tinted.normal New update downloaded. Installs on app restart. - - .footer-bar-link{"style" => "margin-right: 5px;"} - %span{"ng-if" => "ctrl.lastSyncDate", "style" => "float: left; font-weight: normal; margin-right: 8px;"} - %span{"ng-if" => "!ctrl.isRefreshing"} - Last refreshed {{ctrl.lastSyncDate | appDateTime}} - %span{"ng-if" => "ctrl.isRefreshing"} - .spinner{"style" => "margin-top: 2px;"} - - %strong{"ng-if" => "ctrl.offline"} Offline - %a{"ng-if" => "!ctrl.offline", "ng-click" => "ctrl.refreshData()"} Refresh - - %span{"ng-if" => "ctrl.hasPasscode()"} - %i.icon.ion-locked{"ng-if" => "ctrl.hasPasscode()", "ng-click" => "ctrl.lockApp()"} diff --git a/app/assets/templates/frontend/lock-screen.html.haml b/app/assets/templates/frontend/lock-screen.html.haml deleted file mode 100644 index e79c7cbef..000000000 --- a/app/assets/templates/frontend/lock-screen.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -#lock-screen - .content - %h3.center-align Passcode Required - - %form.mt-20{"ng-submit" => "submitPasscodeForm()"} - %input.form-control.mt-10{:type => 'password', - "ng-model" => "formData.passcode", "autofocus" => "true", - "placeholder" => "Enter Passcode", "autocomplete" => "new-password"} - %button.standard.ui-button.block.tinted.mt-5{"type" => "submit"} Unlock diff --git a/app/assets/templates/frontend/notes.html.haml b/app/assets/templates/frontend/notes.html.haml deleted file mode 100644 index 810763227..000000000 --- a/app/assets/templates/frontend/notes.html.haml +++ /dev/null @@ -1,64 +0,0 @@ -.section.notes - .content - .section-title-bar#notes-title-bar - .title {{ctrl.tag.title}} notes - .add-button#notes-add-button{"ng-click" => "ctrl.createNewNote()"} + - %br - .filter-section - %input.filter-bar{"select-on-click" => "true", "ng-model" => "ctrl.noteFilter.text", "placeholder" => "Search", "ng-change" => "ctrl.filterTextChanged()", "lowercase" => "true"} - #search-clear-button{"ng-if" => "ctrl.noteFilter.text", "ng-click" => "ctrl.noteFilter.text = ''; ctrl.filterTextChanged()"} ✕ - %ul.section-menu-bar#notes-menu-bar - %li.item-with-subtitle{"ng-class" => "{'selected' : ctrl.showMenu}"} - .wrapper{"ng-click" => "ctrl.showMenu = !ctrl.showMenu"} - %label Options - .subtitle {{ctrl.optionsSubtitle()}} - - .sectioned-menu.dropdown-menu{"ng-if" => "ctrl.showMenu"} - %ul - .header - .title Sort by - %li{"ng-click" => "ctrl.selectedMenuItem($event); ctrl.selectedSortByCreated()"} - %label - %span.top.mt-5.mr-5{"ng-if" => "ctrl.sortBy == 'created_at'"} ✓ - By date added - %li{"ng-click" => "ctrl.selectedMenuItem($event); ctrl.selectedSortByUpdated()"} - %label - %span.top.mt-5.mr-5{"ng-if" => "ctrl.sortBy == 'updated_at'"} ✓ - By date modified - %li{"ng-click" => "ctrl.selectedMenuItem($event); ctrl.selectedSortByTitle()"} - %label - %span.top.mt-5.mr-5{"ng-if" => "ctrl.sortBy == 'title'"} ✓ - By title - %ul{"ng-if" => "!ctrl.tag.archiveTag"} - .header - .title Archives - %li{"ng-click" => "ctrl.selectedMenuItem($event); ctrl.toggleShowArchived()"} - %label - %span.top.mt-5.mr-5{"ng-if" => "ctrl.showArchived == true"} ✓ - Show archived notes - - .scrollable - .infinite-scroll#notes-scrollable{"infinite-scroll" => "ctrl.paginate()", "can-load" => "true", "threshold" => "200"} - .note{"ng-repeat" => "note in (ctrl.sortedNotes = (ctrl.tag.notes | filter: ctrl.filterNotes | sortBy: ctrl.sortBy| limitTo:ctrl.notesToDisplay)) track by note.uuid", - "ng-click" => "ctrl.selectNote(note)", "ng-class" => "{'selected' : ctrl.selectedNote == note}"} - %strong.red.medium{"ng-if" => "note.conflict_of"} Conflicted copy - %strong.red.medium{"ng-if" => "note.errorDecrypting"} Error decrypting - - .pinned.tinted{"ng-if" => "note.pinned", "ng-class" => "{'tinted-selected' : ctrl.selectedNote == note}"} - %i.icon.ion-ios-flag - %strong.medium Pinned - - .archived.tinted{"ng-if" => "note.archived && !ctrl.tag.archiveTag", "ng-class" => "{'tinted-selected' : ctrl.selectedNote == note}"} - %i.icon.ion-ios-box - %strong.medium Archived - - .tags-string{"ng-if" => "ctrl.tag.all"} - .faded {{note.tagsString()}} - - .name{"ng-if" => "note.title"} - {{note.title}} - .note-preview - {{note.text}} - .date.faded - %span{"ng-if" => "ctrl.sortBy == 'updated_at'"} Modified {{note.updatedAtString() || 'Now'}} - %span{"ng-if" => "ctrl.sortBy != 'updated_at'"} {{note.createdAtString() || 'Now'}} diff --git a/app/assets/templates/frontend/home.html.haml b/app/assets/templates/home.html.haml similarity index 94% rename from app/assets/templates/frontend/home.html.haml rename to app/assets/templates/home.html.haml index bf98088c2..eb2b1f7b4 100644 --- a/app/assets/templates/frontend/home.html.haml +++ b/app/assets/templates/home.html.haml @@ -1,6 +1,6 @@ .main-ui-view %lock-screen{"ng-if" => "needsUnlock", "on-success" => "onSuccessfulUnlock"} - .app{"ng-if" => "!needsUnlock"} + .app#app{"ng-if" => "!needsUnlock"} %tags-section{"save" => "tagsSave", "add-new" => "tagsAddNew", "will-select" => "tagsWillMakeSelection", "selection-made" => "tagsSelectionMade", "all-tag" => "allTag", "archive-tag" => "archiveTag", "tags" => "tags", "remove-tag" => "removeTag"} diff --git a/app/assets/templates/lock-screen.html.haml b/app/assets/templates/lock-screen.html.haml new file mode 100644 index 000000000..08c2b4472 --- /dev/null +++ b/app/assets/templates/lock-screen.html.haml @@ -0,0 +1,14 @@ +#lock-screen.sn-component + .panel + .header + %h1.title Passcode Required + .content + .panel-section + %form.panel-form.panel-row{"ng-submit" => "submitPasscodeForm()"} + .panel-column.stretch + %input.panel-row{:type => 'password', + "ng-model" => "formData.passcode", "autofocus" => "true", "sn-autofocus" => "true", "should-focus" => "true", + "placeholder" => "Enter Passcode", "autocomplete" => "new-password"} + .button-group.stretch.panel-row.form-submit + %button.button.info{"type" => "submit"} + .label Unlock diff --git a/app/assets/templates/notes.html.haml b/app/assets/templates/notes.html.haml new file mode 100644 index 000000000..5797b152a --- /dev/null +++ b/app/assets/templates/notes.html.haml @@ -0,0 +1,68 @@ +.section.notes#notes-column + .content + .section-title-bar#notes-title-bar + .padded + .section-title-bar-header + .title {{ctrl.panelTitle()}} + .add-button#notes-add-button{"ng-click" => "ctrl.createNewNote()"} + + .filter-section + %input.filter-bar#search-bar.mousetrap{"select-on-click" => "true", "ng-model" => "ctrl.noteFilter.text", "placeholder" => "Search", "ng-change" => "ctrl.filterTextChanged()", "lowercase" => "true"} + #search-clear-button{"ng-if" => "ctrl.noteFilter.text", "ng-click" => "ctrl.noteFilter.text = ''; ctrl.filterTextChanged()"} ✕ + .sn-component#notes-menu-bar + .app-bar.no-edges + .left + .item{"ng-click" => "ctrl.showMenu = !ctrl.showMenu", "ng-class" => "{'selected' : ctrl.showMenu}"} + .column + .label + Options + .column + .sublabel {{ctrl.optionsSubtitle()}} + + .sn-component{"ng-if" => "ctrl.showMenu"} + .menu-panel.dropdown-menu + .section + .header + %h4.title Sort By + + %menu-row{"title" => "'Date Added'", "circle" => "ctrl.sortBy == 'created_at' && 'success'", "ng-click" => "ctrl.selectedMenuItem($event); ctrl.selectedSortByCreated()"} + %menu-row{"title" => "'Date Modified'", "circle" => "ctrl.sortBy == 'updated_at' && 'success'", "ng-click" => "ctrl.selectedMenuItem($event); ctrl.selectedSortByUpdated()"} + %menu-row{"title" => "'Title'", "circle" => "ctrl.sortBy == 'title' && 'success'", "ng-click" => "ctrl.selectedMenuItem($event); ctrl.selectedSortByTitle()"} + + .section{"ng-if" => "!ctrl.tag.archiveTag"} + .header + %h4.title Display + + %menu-row{"title" => "'Archived Notes'", "circle" => "ctrl.showArchived ? 'success' : 'danger'", "faded" => "!ctrl.showArchived", "ng-click" => "ctrl.selectedMenuItem($event); ctrl.toggleKey('showArchived')"} + %menu-row{"title" => "'Pinned Notes'", "circle" => "ctrl.hidePinned ? 'danger' : 'success'", "faded" => "ctrl.hidePinned", "ng-click" => "ctrl.selectedMenuItem($event); ctrl.toggleKey('hidePinned')"} + %menu-row{"title" => "'Note Preview'", "circle" => "ctrl.hideNotePreview ? 'danger' : 'success'", "faded" => "ctrl.hideNotePreview", "ng-click" => "ctrl.selectedMenuItem($event); ctrl.toggleKey('hideNotePreview')"} + %menu-row{"title" => "'Date'", "circle" => "ctrl.hideDate ? 'danger' : 'success'","faded" => "ctrl.hideDate", "ng-click" => "ctrl.selectedMenuItem($event); ctrl.toggleKey('hideDate')"} + %menu-row{"title" => "'Tags'", "circle" => "ctrl.hideTags ? 'danger' : 'success'","faded" => "ctrl.hideTags", "ng-click" => "ctrl.selectedMenuItem($event); ctrl.toggleKey('hideTags')"} + + + .scrollable + .infinite-scroll#notes-scrollable{"infinite-scroll" => "ctrl.paginate()", "can-load" => "true", "threshold" => "200"} + .note{"ng-repeat" => "note in (ctrl.sortedNotes = (ctrl.tag.notes | filter: ctrl.filterNotes | sortBy: ctrl.sortBy | limitTo:ctrl.notesToDisplay)) track by note.uuid", + "ng-click" => "ctrl.selectNote(note)", "ng-class" => "{'selected' : ctrl.selectedNote == note}"} + %strong.red.medium{"ng-if" => "note.conflict_of"} Conflicted copy + %strong.red.medium{"ng-if" => "note.errorDecrypting"} Error decrypting + + .pinned.tinted{"ng-if" => "note.pinned", "ng-class" => "{'tinted-selected' : ctrl.selectedNote == note}"} + %i.icon.ion-bookmark + %strong.medium Pinned + + .archived.tinted{"ng-if" => "note.archived && !ctrl.tag.archiveTag", "ng-class" => "{'tinted-selected' : ctrl.selectedNote == note}"} + %i.icon.ion-ios-box + %strong.medium Archived + + .tags-string{"ng-if" => "ctrl.shouldShowTags(note)"} + .faded {{note.tagsString()}} + + .name{"ng-if" => "note.title"} + {{note.title}} + .note-preview{"ng-if" => "!ctrl.hideNotePreview"} + {{note.text}} + .date.faded{"ng-if" => "!ctrl.hideDate"} + %span{"ng-if" => "ctrl.sortBy == 'updated_at'"} Modified {{note.updatedAtString() || 'Now'}} + %span{"ng-if" => "ctrl.sortBy != 'updated_at'"} {{note.createdAtString() || 'Now'}} + + %panel-resizer{"panel-id" => "'notes-column'", "on-resize-finish" => "ctrl.onPanelResize", "control" => "ctrl.panelController", "hoverable" => "true", "collapsable" => "true"} diff --git a/app/assets/templates/frontend/tags.html.haml b/app/assets/templates/tags.html.haml similarity index 77% rename from app/assets/templates/frontend/tags.html.haml rename to app/assets/templates/tags.html.haml index 4b689860c..f99e9bfba 100644 --- a/app/assets/templates/frontend/tags.html.haml +++ b/app/assets/templates/tags.html.haml @@ -1,9 +1,13 @@ .section.tags#tags-column - %iframe#tags-list-iframe{"ng-if" => "ctrl.component && ctrl.component.active", "ng-src" => "{{ctrl.component.url | trusted}}", "frameBorder" => "0", "style" => "width: 100%; height: 100%;", "sandbox" => "allow-scripts"} + + .component-view-container{"ng-if" => "ctrl.component.active"} + %component-view.component-view{"component" => "ctrl.component"} + #tags-content.content{"ng-if" => "!(ctrl.component && ctrl.component.active)"} #tags-title-bar.section-title-bar - .title Tags - .add-button#tag-add-button{"ng-click" => "ctrl.clickedAddNewTag()"} + + .section-title-bar-header + .title Tags + .add-button#tag-add-button{"ng-click" => "ctrl.clickedAddNewTag()"} + .scrollable .tag{"ng-if" => "ctrl.allTag", "ng-click" => "ctrl.selectTag(ctrl.allTag)", "ng-class" => "{'selected' : ctrl.selectedTag == ctrl.allTag}"} @@ -13,7 +17,7 @@ .tag{"ng-repeat" => "tag in ctrl.tags track by tag.uuid", "ng-click" => "ctrl.selectTag(tag)", "ng-class" => "{'selected' : ctrl.selectedTag == tag}"} .info %input.title{"ng-attr-id" => "tag-{{tag.uuid}}", "ng-click" => "ctrl.selectTag(tag)", "ng-model" => "tag.title", - "ng-keyup" => "$event.keyCode == 13 && ctrl.saveTag($event, tag)", "mb-autofocus" => "true", "should-focus" => "ctrl.newTag || ctrl.editingTag == tag", + "ng-keyup" => "$event.keyCode == 13 && ctrl.saveTag($event, tag)", "sn-autofocus" => "true", "should-focus" => "ctrl.newTag || ctrl.editingTag == tag", "ng-change" => "ctrl.tagTitleDidChange(tag)", "ng-blur" => "ctrl.saveTag($event, tag)", "spellcheck" => "false"} .count {{ctrl.noteCount(tag)}} @@ -27,3 +31,5 @@ .tag.faded{"ng-if" => "ctrl.archiveTag", "ng-click" => "ctrl.selectTag(ctrl.archiveTag)", "ng-class" => "{'selected' : ctrl.selectedTag == ctrl.archiveTag}"} .info %input.title{"ng-disabled" => "true", "ng-model" => "ctrl.archiveTag.title"} + + %panel-resizer{"panel-id" => "'tags-column'", "on-resize-finish" => "ctrl.onPanelResize", "control" => "ctrl.panelController", "hoverable" => "true", "collapsable" => "true"} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 76def86a7..e3aa18d09 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -7,7 +7,7 @@ class ApplicationController < ActionController::Base layout :false - def frontend + def app end diff --git a/app/views/application/frontend.html.erb b/app/views/application/app.html.erb similarity index 92% rename from app/views/application/frontend.html.erb rename to app/views/application/app.html.erb index c1dea5432..d05065dc1 100644 --- a/app/views/application/frontend.html.erb +++ b/app/views/application/app.html.erb @@ -1,5 +1,5 @@ - + @@ -30,6 +30,7 @@ <% if Rails.env.development? %> @@ -42,7 +43,7 @@ -
+
diff --git a/config/application.rb b/config/application.rb index 16e4e7e40..ac93fc14b 100644 --- a/config/application.rb +++ b/config/application.rb @@ -53,7 +53,7 @@ module Neeto font_src: %w(* 'self'), form_action: %w('self'), frame_ancestors: ["*"], - img_src: %w('self' data:), + img_src: %w('self' * data:), manifest_src: %w('self'), media_src: %w('self'), object_src: %w('self'), diff --git a/config/cable.yml b/config/cable.yml deleted file mode 100644 index 73739d638..000000000 --- a/config/cable.yml +++ /dev/null @@ -1,10 +0,0 @@ -development: - adapter: redis - url: redis://localhost:6379 - -test: - adapter: async - -production: - adapter: redis - url: redis://localhost:6379/1 diff --git a/config/deploy.rb b/config/deploy.rb index 0397e5190..572e17333 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -1,6 +1,4 @@ CAP_CONFIG = YAML.load_file("config/cap.yml") -# config valid only for current version of Capistrano -lock '3.6.1' set :application, 'neeto' set :repo_url, CAP_CONFIG["default"]["repo_url"] @@ -11,10 +9,6 @@ set :repo_url, CAP_CONFIG["default"]["repo_url"] # Default deploy_to directory is /var/www/my_app_name # set :deploy_to, '/var/www/my_app_name' -# Default value for :scm is :git -set :scm, :git -set :git_strategy, Capistrano::Git::SubmoduleStrategy - # Default value for :format is :airbrussh. # set :format, :airbrussh diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index bd097f99c..cb2eb3fc6 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -12,7 +12,6 @@ Rails.application.config.assets.precompile << /\.(?:svg|eot|woff|ttf)\z/ # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. -# Rails.application.config.assets.precompile += %w( search.js ) Rails.application.config.assets.precompile += %w( app.css compiled.min.js compiled.js ) # zip library diff --git a/config/routes.rb b/config/routes.rb index 3bd46ba1b..1697cb1b1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,3 @@ Rails.application.routes.draw do - root 'application#frontend' + root 'application#app' end diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..8555f1a06 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6544 @@ +{ + "name": "standard-notes", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "JSONStream": { + "version": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", + "dev": true, + "requires": { + "jsonparse": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "through": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + }, + "abbrev": { + "version": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", + "dev": true + }, + "accepts": { + "version": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "dev": true, + "requires": { + "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "negotiator": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz" + } + }, + "acorn": { + "version": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + }, + "after": { + "version": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "align-text": { + "version": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "longest": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "repeat-string": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" + } + }, + "alter": { + "version": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", + "integrity": "sha1-x1iICGF1cgNKrmJICvJrHU0cs80=", + "dev": true, + "requires": { + "stable": "https://registry.npmjs.org/stable/-/stable-0.1.6.tgz" + } + }, + "amdefine": { + "version": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "angular": { + "version": "https://registry.npmjs.org/angular/-/angular-1.6.6.tgz", + "integrity": "sha1-/Vo8+0N844LYVO4BEgeXl4Uny2Q=", + "dev": true + }, + "angular-mocks": { + "version": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.6.6.tgz", + "integrity": "sha1-yTAY54OMbcXOrxprz5vhPIMOpRU=", + "dev": true + }, + "ansi-regex": { + "version": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "anymatch": { + "version": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha1-VT3Lj5HjyImEXf26NMd3IbkLnXo=", + "dev": true, + "requires": { + "micromatch": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "normalize-path": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" + } + }, + "argparse": { + "version": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "dev": true, + "requires": { + "sprintf-js": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + } + }, + "arr-diff": { + "version": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz" + } + }, + "arr-flatten": { + "version": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "dev": true + }, + "array-filter": { + "version": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", + "dev": true + }, + "array-find-index": { + "version": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-map": { + "version": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", + "dev": true + }, + "array-reduce": { + "version": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", + "dev": true + }, + "array-slice": { + "version": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "array-unique": { + "version": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "arraybuffer.slice": { + "version": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", + "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=", + "dev": true + }, + "asn1": { + "version": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "asn1.js": { + "version": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", + "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=", + "dev": true, + "requires": { + "bn.js": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimalistic-assert": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz" + } + }, + "assert": { + "version": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "https://registry.npmjs.org/util/-/util-0.10.3.tgz" + } + }, + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, + "astw": { + "version": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", + "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=", + "dev": true, + "requires": { + "acorn": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz" + } + }, + "async": { + "version": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "async-each": { + "version": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "asynckit": { + "version": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true + }, + "aws4": { + "version": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true + }, + "babel-cli": { + "version": "https://registry.npmjs.org/babel-cli/-/babel-cli-6.26.0.tgz", + "integrity": "sha1-UCq1SHTX24itALiHoGODzgPQAvE=", + "dev": true, + "requires": { + "babel-core": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", + "babel-polyfill": "6.26.0", + "babel-register": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "chokidar": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "commander": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "convert-source-map": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "fs-readdir-recursive": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz", + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "output-file-sync": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "slash": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "v8flags": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz" + }, + "dependencies": { + "babel-code-frame": { + "version": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "js-tokens": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" + } + }, + "babel-core": { + "version": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", + "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", + "dev": true, + "requires": { + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "babel-generator": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", + "babel-helpers": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-register": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "convert-source-map": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "json5": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "private": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "slash": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } + }, + "babel-generator": { + "version": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", + "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", + "dev": true, + "requires": { + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "detect-indent": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "jsesc": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "trim-right": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" + } + }, + "babel-register": { + "version": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "dev": true, + "requires": { + "babel-core": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", + "home-or-tmp": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "source-map-support": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz" + }, + "dependencies": { + "core-js": { + "version": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", + "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", + "dev": true + } + } + }, + "babel-runtime": { + "version": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "regenerator-runtime": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz" + } + }, + "babel-template": { + "version": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + } + }, + "babel-traverse": { + "version": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "globals": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + } + }, + "babel-types": { + "version": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "to-fast-properties": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" + } + }, + "babylon": { + "version": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM=", + "dev": true + }, + "regenerator-runtime": { + "version": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha1-flT+W1zNXWYk6mJVw0c74JC4AuE=", + "dev": true + } + } + }, + "babel-code-frame": { + "version": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "js-tokens": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" + } + }, + "babel-core": { + "version": "https://registry.npmjs.org/babel-core/-/babel-core-6.25.0.tgz", + "integrity": "sha1-fdQrBGPHQunVKW3rPsZ6kyLa1yk=", + "dev": true, + "requires": { + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "babel-generator": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz", + "babel-helpers": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-register": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "convert-source-map": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "json5": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "private": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "slash": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } + }, + "babel-generator": { + "version": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz", + "integrity": "sha1-M6GvcNXyiQrrRlpKd5PB32qeqfw=", + "dev": true, + "requires": { + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "detect-indent": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "jsesc": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "trim-right": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-helper-call-delegate": { + "version": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-helper-define-map": { + "version": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true, + "requires": { + "babel-helper-function-name": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + }, + "dependencies": { + "babel-runtime": { + "version": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "regenerator-runtime": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz" + } + }, + "babel-types": { + "version": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "to-fast-properties": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" + } + }, + "regenerator-runtime": { + "version": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha1-flT+W1zNXWYk6mJVw0c74JC4AuE=", + "dev": true + } + } + }, + "babel-helper-explode-assignable-expression": { + "version": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-helper-function-name": { + "version": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true, + "requires": { + "babel-helper-get-function-arity": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-helper-get-function-arity": { + "version": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-helper-hoist-variables": { + "version": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-helper-optimise-call-expression": { + "version": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-helper-regex": { + "version": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + }, + "dependencies": { + "babel-runtime": { + "version": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "regenerator-runtime": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz" + } + }, + "babel-types": { + "version": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "to-fast-properties": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" + } + }, + "regenerator-runtime": { + "version": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha1-flT+W1zNXWYk6mJVw0c74JC4AuE=", + "dev": true + } + } + }, + "babel-helper-remap-async-to-generator": { + "version": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "dev": true, + "requires": { + "babel-helper-function-name": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-helper-replace-supers": { + "version": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-helpers": { + "version": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz" + } + }, + "babel-messages": { + "version": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-syntax-async-functions": { + "version": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "babel-plugin-transform-async-to-generator": { + "version": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "babel-plugin-syntax-async-functions": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + }, + "dependencies": { + "babel-code-frame": { + "version": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "js-tokens": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" + } + }, + "babel-runtime": { + "version": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "regenerator-runtime": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz" + } + }, + "babel-template": { + "version": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + } + }, + "babel-traverse": { + "version": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "globals": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + } + }, + "babel-types": { + "version": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "to-fast-properties": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" + } + }, + "babylon": { + "version": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM=", + "dev": true + }, + "regenerator-runtime": { + "version": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha1-flT+W1zNXWYk6mJVw0c74JC4AuE=", + "dev": true + } + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true, + "requires": { + "babel-helper-define-map": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "babel-helper-function-name": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "babel-helper-optimise-call-expression": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "babel-helper-replace-supers": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true, + "requires": { + "babel-helper-function-name": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", + "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz" + }, + "dependencies": { + "babel-code-frame": { + "version": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "js-tokens": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" + } + }, + "babel-runtime": { + "version": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "regenerator-runtime": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz" + } + }, + "babel-template": { + "version": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + } + }, + "babel-traverse": { + "version": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "globals": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + } + }, + "babel-types": { + "version": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "to-fast-properties": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" + } + }, + "babylon": { + "version": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM=", + "dev": true + }, + "regenerator-runtime": { + "version": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha1-flT+W1zNXWYk6mJVw0c74JC4AuE=", + "dev": true + } + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-amd": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true, + "requires": { + "babel-helper-replace-supers": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true, + "requires": { + "babel-helper-call-delegate": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "babel-helper-get-function-arity": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-template": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true, + "requires": { + "babel-helper-regex": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true, + "requires": { + "babel-helper-regex": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "regexpu-core": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz" + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "dev": true, + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "babel-plugin-syntax-exponentiation-operator": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz" + } + }, + "babel-plugin-transform-regenerator": { + "version": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "requires": { + "regenerator-transform": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz" + } + }, + "babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "core-js": "2.5.3", + "regenerator-runtime": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz" + }, + "dependencies": { + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "2.5.3", + "regenerator-runtime": "0.11.1" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + } + } + }, + "core-js": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz", + "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4=", + "dev": true + } + } + }, + "babel-preset-env": { + "version": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.0.tgz", + "integrity": "sha1-LeHHgqeAoKXWBdGZyVdZbaQ8ROQ=", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "babel-plugin-syntax-trailing-function-commas": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "babel-plugin-transform-async-to-generator": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "babel-plugin-transform-es2015-arrow-functions": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "babel-plugin-transform-es2015-block-scoped-functions": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "babel-plugin-transform-es2015-block-scoping": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "babel-plugin-transform-es2015-classes": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "babel-plugin-transform-es2015-computed-properties": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "babel-plugin-transform-es2015-destructuring": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "babel-plugin-transform-es2015-duplicate-keys": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "babel-plugin-transform-es2015-for-of": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "babel-plugin-transform-es2015-function-name": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "babel-plugin-transform-es2015-literals": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "babel-plugin-transform-es2015-modules-amd": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "babel-plugin-transform-es2015-modules-commonjs": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", + "babel-plugin-transform-es2015-modules-systemjs": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "babel-plugin-transform-es2015-modules-umd": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "babel-plugin-transform-es2015-object-super": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "babel-plugin-transform-es2015-parameters": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "babel-plugin-transform-es2015-shorthand-properties": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "babel-plugin-transform-es2015-spread": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "babel-plugin-transform-es2015-sticky-regex": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "babel-plugin-transform-es2015-template-literals": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "babel-plugin-transform-es2015-typeof-symbol": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "babel-plugin-transform-es2015-unicode-regex": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "babel-plugin-transform-exponentiation-operator": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "babel-plugin-transform-regenerator": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "browserslist": "https://registry.npmjs.org/browserslist/-/browserslist-2.4.0.tgz", + "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "semver": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" + } + }, + "babel-preset-es2016": { + "version": "https://registry.npmjs.org/babel-preset-es2016/-/babel-preset-es2016-6.24.1.tgz", + "integrity": "sha1-+QC/k+LrwNJ235uKtZck6/2Vn4s=", + "dev": true, + "requires": { + "babel-plugin-transform-exponentiation-operator": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz" + } + }, + "babel-register": { + "version": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz", + "integrity": "sha1-fhDhOi9xBlvfrVoXh7pFvKbe118=", + "dev": true, + "requires": { + "babel-core": "https://registry.npmjs.org/babel-core/-/babel-core-6.25.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "home-or-tmp": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "source-map-support": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz" + } + }, + "babel-runtime": { + "version": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", + "dev": true, + "requires": { + "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "regenerator-runtime": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz" + } + }, + "babel-template": { + "version": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", + "integrity": "sha1-ZlJBFmt8KqTGGdceGSlpVSsQwHE=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-traverse": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + } + }, + "babel-traverse": { + "version": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", + "integrity": "sha1-IldJfi/NGbie3BPEyROB+VEklvE=", + "dev": true, + "requires": { + "babel-code-frame": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "babel-messages": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "babylon": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "globals": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + } + }, + "babel-types": { + "version": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "integrity": "sha1-cK+ySNVmDl0Y+BHZHIMDtUE0oY4=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "esutils": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "to-fast-properties": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" + } + }, + "babylon": { + "version": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", + "integrity": "sha1-Pot0AriNIsNCPhN6FXeIOxX/hpo=", + "dev": true + }, + "backo2": { + "version": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-arraybuffer": { + "version": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, + "base64-js": { + "version": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY=", + "dev": true + }, + "base64id": { + "version": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + } + }, + "better-assert": { + "version": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "dev": true, + "requires": { + "callsite": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz" + } + }, + "binary-extensions": { + "version": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", + "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", + "dev": true + }, + "blob": { + "version": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", + "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", + "dev": true + }, + "bluebird": { + "version": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", + "dev": true + }, + "bn.js": { + "version": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha1-LN4J617jQfSEdGuwMJsyU7GxRC8=", + "dev": true + }, + "body-parser": { + "version": "https://registry.npmjs.org/body-parser/-/body-parser-1.14.2.tgz", + "integrity": "sha1-EBXLH+LEQ4WCWVgdtTMy+NDPUPk=", + "dev": true, + "requires": { + "bytes": "https://registry.npmjs.org/bytes/-/bytes-2.2.0.tgz", + "content-type": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "depd": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", + "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", + "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "qs": "https://registry.npmjs.org/qs/-/qs-5.2.0.tgz", + "raw-body": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz", + "type-is": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz" + }, + "dependencies": { + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + } + }, + "iconv-lite": { + "version": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", + "dev": true + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + }, + "qs": { + "version": "https://registry.npmjs.org/qs/-/qs-5.2.0.tgz", + "integrity": "sha1-qfMRQq9GjLcrJbMBNrokVoNJFr4=", + "dev": true + } + } + }, + "boom": { + "version": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + } + }, + "bower": { + "version": "https://registry.npmjs.org/bower/-/bower-1.8.2.tgz", + "integrity": "sha1-rfU1KcjUrwLvJPuNU0HBQZ0z4vc=", + "dev": true + }, + "brace-expansion": { + "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "concat-map": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + }, + "braces": { + "version": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "preserve": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "repeat-element": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz" + } + }, + "brorand": { + "version": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-pack": { + "version": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.2.tgz", + "integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=", + "dev": true, + "requires": { + "JSONStream": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "combine-source-map": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz", + "defined": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "umd": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz" + } + }, + "browser-resolve": { + "version": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", + "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", + "dev": true, + "requires": { + "resolve": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" + } + }, + "browserify": { + "version": "https://registry.npmjs.org/browserify/-/browserify-14.4.0.tgz", + "integrity": "sha1-CJo0Y69Y0OSNjNQHCz90ZU1avKk=", + "dev": true, + "requires": { + "JSONStream": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "assert": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "browser-pack": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.2.tgz", + "browser-resolve": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", + "browserify-zlib": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "buffer": "https://registry.npmjs.org/buffer/-/buffer-5.0.7.tgz", + "cached-path-relative": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz", + "concat-stream": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "console-browserify": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "constants-browserify": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "crypto-browserify": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz", + "defined": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "deps-sort": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", + "domain-browser": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "duplexer2": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "events": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "htmlescape": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "https-browserify": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "insert-module-globals": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz", + "labeled-stream-splicer": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz", + "module-deps": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz", + "os-browserify": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.1.2.tgz", + "parents": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "path-browserify": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "process": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "punycode": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "querystring-es3": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "read-only-stream": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "resolve": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "shasum": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", + "shell-quote": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "stream-browserify": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "stream-http": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "subarg": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "syntax-error": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.3.0.tgz", + "through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "timers-browserify": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "tty-browserify": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "url": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "util": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "vm-browserify": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } + }, + "browserify-aes": { + "version": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.8.tgz", + "integrity": "sha1-yPo7G3WFu3unfFVgtgmW3extUwk=", + "dev": true, + "requires": { + "buffer-xor": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "cipher-base": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "create-hash": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "evp_bytestokey": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + }, + "browserify-cache-api": { + "version": "https://registry.npmjs.org/browserify-cache-api/-/browserify-cache-api-3.0.1.tgz", + "integrity": "sha1-liR+hT8Gj9bg1FzHPwuyzZd47wI=", + "dev": true, + "requires": { + "async": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } + }, + "browserify-cipher": { + "version": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", + "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", + "dev": true, + "requires": { + "browserify-aes": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.8.tgz", + "browserify-des": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", + "evp_bytestokey": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz" + } + }, + "browserify-des": { + "version": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", + "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", + "dev": true, + "requires": { + "cipher-base": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "des.js": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + }, + "browserify-incremental": { + "version": "https://registry.npmjs.org/browserify-incremental/-/browserify-incremental-3.1.1.tgz", + "integrity": "sha1-BxPLdYckemMqnwjPG9FpuHi2Koo=", + "dev": true, + "requires": { + "JSONStream": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz", + "browserify-cache-api": "https://registry.npmjs.org/browserify-cache-api/-/browserify-cache-api-3.0.1.tgz", + "through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + }, + "dependencies": { + "JSONStream": { + "version": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz", + "integrity": "sha1-dDSdDYlSK3HzDwoD/5vSDKbxKsA=", + "dev": true, + "requires": { + "jsonparse": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", + "through": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + }, + "jsonparse": { + "version": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", + "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=", + "dev": true + } + } + }, + "browserify-rsa": { + "version": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "randombytes": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz" + } + }, + "browserify-sign": { + "version": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "browserify-rsa": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "create-hash": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "create-hmac": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "elliptic": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "parse-asn1": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz" + } + }, + "browserify-zlib": { + "version": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "dev": true, + "requires": { + "pako": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz" + } + }, + "browserslist": { + "version": "https://registry.npmjs.org/browserslist/-/browserslist-2.4.0.tgz", + "integrity": "sha1-aT7pPQHmZGimNI2lSY4BH1ePh/g=", + "dev": true, + "requires": { + "caniuse-lite": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000739.tgz", + "electron-to-chromium": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.23.tgz" + } + }, + "buffer": { + "version": "https://registry.npmjs.org/buffer/-/buffer-5.0.7.tgz", + "integrity": "sha1-VwopC2Jc8mAykMEUkiPSfM8E25c=", + "dev": true, + "requires": { + "base64-js": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "ieee754": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz" + } + }, + "buffer-xor": { + "version": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-modules": { + "version": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "https://registry.npmjs.org/bytes/-/bytes-2.2.0.tgz", + "integrity": "sha1-/TVGSkA/b5EXwt42Cez/nK4ABYg=", + "dev": true + }, + "cached-path-relative": { + "version": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz", + "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=", + "dev": true + }, + "callsite": { + "version": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "camel-case": { + "version": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", + "upper-case": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz" + } + }, + "camelcase": { + "version": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "map-obj": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + } + }, + "caniuse-lite": { + "version": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000739.tgz", + "integrity": "sha1-nujHAW9cUi27DAhj1Vxh77RTrpU=", + "dev": true + }, + "caseless": { + "version": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", + "dev": true + }, + "center-align": { + "version": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "lazy-cache": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz" + } + }, + "chalk": { + "version": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "has-ansi": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" + } + }, + "change-case": { + "version": "https://registry.npmjs.org/change-case/-/change-case-3.0.1.tgz", + "integrity": "sha1-7l9a0EFa0a2egHLPSc1M+nZgpVQ=", + "dev": true, + "requires": { + "camel-case": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "constant-case": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz", + "dot-case": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz", + "header-case": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz", + "is-lower-case": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", + "is-upper-case": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", + "lower-case": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "lower-case-first": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", + "no-case": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", + "param-case": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "pascal-case": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz", + "path-case": "https://registry.npmjs.org/path-case/-/path-case-2.1.1.tgz", + "sentence-case": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.1.tgz", + "snake-case": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", + "swap-case": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", + "title-case": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", + "upper-case": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "upper-case-first": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz" + } + }, + "chokidar": { + "version": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true, + "requires": { + "anymatch": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "async-each": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "fsevents": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz", + "glob-parent": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "is-binary-path": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "readdirp": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz" + } + }, + "cipher-base": { + "version": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + }, + "clean-css": { + "version": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.27.tgz", + "integrity": "sha1-re91sxwWD/pdcvTeZ5ZuJmDBolU=", + "dev": true, + "requires": { + "commander": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz" + }, + "dependencies": { + "commander": { + "version": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "dev": true, + "requires": { + "graceful-readlink": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" + } + }, + "source-map": { + "version": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" + } + } + } + }, + "cliui": { + "version": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "right-align": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "wordwrap": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" + } + }, + "coffee-script": { + "version": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.10.0.tgz", + "integrity": "sha1-EpOLz5vhlI+gBvkuDEyegXBRCMA=", + "dev": true + }, + "colors": { + "version": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "combine-lists": { + "version": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", + "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", + "dev": true, + "requires": { + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + } + }, + "combine-source-map": { + "version": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz", + "integrity": "sha1-CHAxKFazB6h8xKxIbzqaYq7MwJ4=", + "dev": true, + "requires": { + "convert-source-map": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "inline-source-map": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "lodash.memoize": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + }, + "dependencies": { + "convert-source-map": { + "version": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + } + } + }, + "combined-stream": { + "version": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true, + "requires": { + "delayed-stream": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + } + }, + "commander": { + "version": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM=", + "dev": true + }, + "component-bind": { + "version": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", + "dev": true + }, + "component-emitter": { + "version": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", + "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", + "dev": true + }, + "component-inherit": { + "version": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "concat-map": { + "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "typedarray": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" + }, + "dependencies": { + "readable-stream": { + "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + }, + "string_decoder": { + "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "connect": { + "version": "https://registry.npmjs.org/connect/-/connect-3.6.5.tgz", + "integrity": "sha1-+43ee6B2OHfQ7J352sC0tA5yx9o=", + "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "finalhandler": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.6.tgz", + "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "utils-merge": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" + }, + "dependencies": { + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + } + }, + "parseurl": { + "version": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "dev": true + } + } + }, + "console-browserify": { + "version": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz" + } + }, + "constant-case": { + "version": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz", + "integrity": "sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY=", + "dev": true, + "requires": { + "snake-case": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", + "upper-case": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz" + } + }, + "constants-browserify": { + "version": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-type": { + "version": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", + "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=", + "dev": true + }, + "convert-source-map": { + "version": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=", + "dev": true + }, + "cookie": { + "version": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "core-js": { + "version": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=", + "dev": true + }, + "core-util-is": { + "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", + "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", + "dev": true, + "requires": { + "bn.js": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "elliptic": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz" + } + }, + "create-hash": { + "version": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", + "dev": true, + "requires": { + "cipher-base": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "ripemd160": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "sha.js": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz" + } + }, + "create-hmac": { + "version": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", + "dev": true, + "requires": { + "cipher-base": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "create-hash": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "ripemd160": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "sha.js": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz" + } + }, + "cross-spawn": { + "version": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-0.2.9.tgz", + "integrity": "sha1-vWf5bAfvtjA7f+lMHpefiEeOCjk=", + "dev": true, + "requires": { + "lru-cache": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" + } + }, + "cryptiles": { + "version": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "requires": { + "boom": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + } + }, + "crypto-browserify": { + "version": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz", + "integrity": "sha1-lIlF78Z1ekANbl5a9HGU0QBkJ58=", + "dev": true, + "requires": { + "browserify-cipher": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", + "browserify-sign": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "create-ecdh": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", + "create-hash": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "create-hmac": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "diffie-hellman": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "pbkdf2": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", + "public-encrypt": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", + "randombytes": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz" + } + }, + "currently-unhandled": { + "version": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz" + } + }, + "custom-event": { + "version": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "dargs": { + "version": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz", + "integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=", + "dev": true, + "requires": { + "number-is-nan": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + } + }, + "dashdash": { + "version": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "dependencies": { + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "date-now": { + "version": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "dateformat": { + "version": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "meow": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz" + } + }, + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + } + }, + "decamelize": { + "version": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "define-properties": { + "version": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true, + "requires": { + "foreach": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "object-keys": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz" + } + }, + "defined": { + "version": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "delayed-stream": { + "version": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", + "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM=", + "dev": true + }, + "deps-sort": { + "version": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", + "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", + "dev": true, + "requires": { + "JSONStream": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "shasum": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", + "subarg": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz" + } + }, + "des.js": { + "version": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimalistic-assert": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz" + } + }, + "detect-indent": { + "version": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" + } + }, + "detective": { + "version": "https://registry.npmjs.org/detective/-/detective-4.5.0.tgz", + "integrity": "sha1-blqMaybmx6JUsca210kNmOyR7dE=", + "dev": true, + "requires": { + "acorn": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "defined": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" + } + }, + "di": { + "version": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "diffie-hellman": { + "version": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", + "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", + "dev": true, + "requires": { + "bn.js": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "miller-rabin": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "randombytes": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz" + } + }, + "dom-serialize": { + "version": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "requires": { + "custom-event": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "ent": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "extend": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "void-elements": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz" + } + }, + "domain-browser": { + "version": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", + "dev": true + }, + "dot-case": { + "version": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz", + "integrity": "sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4=", + "dev": true, + "requires": { + "no-case": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz" + } + }, + "duplexer2": { + "version": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + } + }, + "ecc-jsbn": { + "version": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + } + }, + "ee-first": { + "version": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.23.tgz", + "integrity": "sha1-5maKsYy2mvuPV3yKn8I9ACeIvnQ=", + "dev": true + }, + "elliptic": { + "version": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "dev": true, + "requires": { + "bn.js": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "brorand": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "hash.js": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "hmac-drbg": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimalistic-assert": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "minimalistic-crypto-utils": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" + } + }, + "encodeurl": { + "version": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", + "dev": true + }, + "engine.io": { + "version": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", + "integrity": "sha1-jef5eJXSDTm4X4ju7nd7K9QrE9Q=", + "dev": true, + "requires": { + "accepts": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "base64id": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "cookie": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "engine.io-parser": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", + "ws": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz" + }, + "dependencies": { + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true, + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + } + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + } + } + }, + "engine.io-client": { + "version": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", + "integrity": "sha1-F5jtk0USRkU9TG9jXXogH+lA1as=", + "dev": true, + "requires": { + "component-emitter": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "component-inherit": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "engine.io-parser": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", + "has-cors": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "indexof": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "parsejson": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", + "parseqs": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "parseuri": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "ws": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", + "xmlhttprequest-ssl": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", + "yeast": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz" + }, + "dependencies": { + "component-emitter": { + "version": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true, + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + } + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + } + } + }, + "engine.io-parser": { + "version": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", + "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=", + "dev": true, + "requires": { + "after": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "arraybuffer.slice": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", + "base64-arraybuffer": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "blob": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", + "has-binary": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", + "wtf-8": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz" + } + }, + "ent": { + "version": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "error-ex": { + "version": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + } + }, + "es6-promise": { + "version": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.0.5.tgz", + "integrity": "sha1-eILzCt3lskDM+n99eMVIMwlRrkI=", + "dev": true + }, + "escape-html": { + "version": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "esutils": { + "version": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "eventemitter2": { + "version": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", + "dev": true + }, + "eventemitter3": { + "version": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", + "dev": true + }, + "events": { + "version": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "evp_bytestokey": { + "version": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=", + "dev": true, + "requires": { + "md5.js": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + }, + "exit": { + "version": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-braces": { + "version": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", + "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", + "dev": true, + "requires": { + "array-slice": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "array-unique": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "braces": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz" + }, + "dependencies": { + "braces": { + "version": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", + "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", + "dev": true, + "requires": { + "expand-range": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz" + } + }, + "expand-range": { + "version": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", + "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", + "dev": true, + "requires": { + "is-number": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", + "repeat-string": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz" + } + }, + "is-number": { + "version": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", + "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", + "dev": true + }, + "repeat-string": { + "version": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", + "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", + "dev": true + } + } + }, + "expand-brackets": { + "version": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz" + } + }, + "expand-range": { + "version": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz" + } + }, + "extend": { + "version": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extglob": { + "version": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + } + }, + "extract-zip": { + "version": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.5.0.tgz", + "integrity": "sha1-ksz22B73Cp+kwXRxFMzvbYaIpsQ=", + "dev": true, + "requires": { + "concat-stream": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.0.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "yauzl": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz" + }, + "dependencies": { + "concat-stream": { + "version": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.0.tgz", + "integrity": "sha1-U/fUPFHF5D+ByP3QMyHGMb5o1hE=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "typedarray": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" + } + }, + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", + "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=", + "dev": true + }, + "mkdirp": { + "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", + "dev": true, + "requires": { + "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + }, + "readable-stream": { + "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + }, + "string_decoder": { + "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "extsprintf": { + "version": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", + "dev": true + }, + "faye-websocket": { + "version": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz" + } + }, + "fd-slicer": { + "version": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" + } + }, + "figures": { + "version": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + } + }, + "file-sync-cmp": { + "version": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz", + "integrity": "sha1-peeo/7+kk7Q7kju9TKiaU7Y7YSs=", + "dev": true + }, + "filename-regex": { + "version": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "dev": true, + "requires": { + "is-number": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "isobject": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "randomatic": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "repeat-element": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "repeat-string": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" + } + }, + "finalhandler": { + "version": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.6.tgz", + "integrity": "sha1-AHrqM9Gk0+QgF/YkhIrVjSEvgU8=", + "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "encodeurl": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "unpipe": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + }, + "dependencies": { + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + } + }, + "parseurl": { + "version": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "dev": true + } + } + }, + "find-up": { + "version": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + } + }, + "findup-sync": { + "version": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", + "dev": true, + "requires": { + "glob": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz" + }, + "dependencies": { + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + } + } + } + }, + "for-in": { + "version": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" + } + }, + "foreach": { + "version": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "forever-agent": { + "version": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "requires": { + "asynckit": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz" + } + }, + "fs-extra": { + "version": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "jsonfile": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "klaw": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz" + } + }, + "fs-readdir-recursive": { + "version": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz", + "integrity": "sha1-jNF0XItPiinIyuw5JHaSG6GV9WA=", + "dev": true + }, + "fs.realpath": { + "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz", + "integrity": "sha1-MoK3E/s62A7eDp/PRhG1qm/AM/Q=", + "dev": true, + "optional": true, + "requires": { + "nan": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", + "node-pre-gyp": "0.6.36" + }, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", + "dev": true, + "optional": true + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "optional": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "aproba": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", + "integrity": "sha1-ldNgDwdxCqDpKYxyatXs8urLq6s=", + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.2.9" + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", + "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", + "dev": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + } + }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true, + "optional": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "optional": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", + "dev": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.1" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "dev": true, + "optional": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "optional": true, + "requires": { + "aproba": "1.1.1", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "dev": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "dev": true, + "optional": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "optional": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", + "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "optional": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", + "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", + "dev": true + }, + "mime-types": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", + "dev": true, + "requires": { + "mime-db": "1.27.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.36", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz", + "integrity": "sha1-22BBEst04NR3VU6bUFsXq936t4Y=", + "dev": true, + "optional": true, + "requires": { + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.0", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.1", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.0", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.0.tgz", + "integrity": "sha512-ocolIkZYZt8UveuiDS0yAkkIjid1o7lPG8cYm05yNYzBn8ykQtaiPMEGp8fY9tKdDgm8okpdKzkvu1y9hUYugA==", + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=", + "dev": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.1", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.0.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.0.1" + } + }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", + "dev": true + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true, + "optional": true + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "optional": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz", + "integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=", + "dev": true, + "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jodid25519": "1.0.2", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz", + "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", + "dev": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=", + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.8", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.2.9", + "rimraf": "2.6.1", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "dev": true, + "optional": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", + "dev": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=", + "dev": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", + "dev": true, + "optional": true, + "requires": { + "extsprintf": "1.0.2" + } + }, + "wide-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } + }, + "function-bind": { + "version": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E=", + "dev": true + }, + "gaze": { + "version": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", + "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", + "dev": true, + "requires": { + "globule": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz" + } + }, + "generate-function": { + "version": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true + }, + "generate-object-property": { + "version": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz" + } + }, + "get-stdin": { + "version": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "getobject": { + "version": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", + "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=", + "dev": true + }, + "getpass": { + "version": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "dependencies": { + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "dev": true, + "requires": { + "fs.realpath": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + } + }, + "glob-base": { + "version": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" + } + }, + "glob-parent": { + "version": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" + } + }, + "globals": { + "version": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=", + "dev": true + }, + "globule": { + "version": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", + "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", + "dev": true, + "requires": { + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + } + }, + "graceful-fs": { + "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "graceful-readlink": { + "version": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "grunt": { + "version": "https://registry.npmjs.org/grunt/-/grunt-1.0.1.tgz", + "integrity": "sha1-6HeHZOlEsY8yuw8QuQeEdcnftWs=", + "dev": true, + "requires": { + "coffee-script": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.10.0.tgz", + "dateformat": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "eventemitter2": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "exit": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "findup-sync": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "glob": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "grunt-cli": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "grunt-known-options": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.0.tgz", + "grunt-legacy-log": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-1.0.0.tgz", + "grunt-legacy-util": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.0.0.tgz", + "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", + "js-yaml": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "nopt": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + }, + "dependencies": { + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", + "dev": true, + "requires": { + "fs.realpath": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + } + }, + "grunt-cli": { + "version": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", + "dev": true, + "requires": { + "findup-sync": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "grunt-known-options": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.0.tgz", + "nopt": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "resolve": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" + } + } + } + }, + "grunt-angular-templates": { + "version": "https://registry.npmjs.org/grunt-angular-templates/-/grunt-angular-templates-1.1.0.tgz", + "integrity": "sha1-EJYDorlf8BAZtxjHA0EmjwnYvhk=", + "dev": true, + "requires": { + "html-minifier": "https://registry.npmjs.org/html-minifier/-/html-minifier-2.1.7.tgz" + } + }, + "grunt-babel": { + "version": "https://registry.npmjs.org/grunt-babel/-/grunt-babel-6.0.0.tgz", + "integrity": "sha1-N4GJtIfeEWjExKn8iN1gBbNd+WA=", + "dev": true, + "requires": { + "babel-core": "https://registry.npmjs.org/babel-core/-/babel-core-6.25.0.tgz" + } + }, + "grunt-browserify": { + "version": "https://registry.npmjs.org/grunt-browserify/-/grunt-browserify-5.2.0.tgz", + "integrity": "sha1-N65E+9x199gelyUmThGwXmJTfEY=", + "dev": true, + "requires": { + "async": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "browserify": "https://registry.npmjs.org/browserify/-/browserify-14.4.0.tgz", + "browserify-incremental": "https://registry.npmjs.org/browserify-incremental/-/browserify-incremental-3.1.1.tgz", + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "resolve": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "watchify": "https://registry.npmjs.org/watchify/-/watchify-3.9.0.tgz" + }, + "dependencies": { + "async": { + "version": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "integrity": "sha1-hDGQ/WtzV6C54clW7d3V7IRitU0=", + "dev": true, + "requires": { + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + } + } + } + }, + "grunt-contrib-concat": { + "version": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-1.0.1.tgz", + "integrity": "sha1-YVCYYwhOhx1+ht5IwBUlntl3Rb0=", + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } + }, + "grunt-contrib-copy": { + "version": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz", + "integrity": "sha1-cGDGWB6QS4qw0A8HbgqPbj58NXM=", + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "file-sync-cmp": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz" + } + }, + "grunt-contrib-cssmin": { + "version": "https://registry.npmjs.org/grunt-contrib-cssmin/-/grunt-contrib-cssmin-1.0.2.tgz", + "integrity": "sha1-FzTL09hMpzZHWLflj/GOUqpgu3Y=", + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "clean-css": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.27.tgz", + "maxmin": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz" + } + }, + "grunt-contrib-sass": { + "version": "https://registry.npmjs.org/grunt-contrib-sass/-/grunt-contrib-sass-1.0.0.tgz", + "integrity": "sha1-gGg4JRy8DhqU1k1RXN00z2dNcBs=", + "dev": true, + "requires": { + "async": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "cross-spawn": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-0.2.9.tgz", + "dargs": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz", + "which": "https://registry.npmjs.org/which/-/which-1.2.14.tgz" + }, + "dependencies": { + "async": { + "version": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + } + } + }, + "grunt-contrib-uglify": { + "version": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-2.3.0.tgz", + "integrity": "sha1-s9AmDr3WzvoS/y+Onh4ln33kIW8=", + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "maxmin": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz", + "object.assign": "https://registry.npmjs.org/object.assign/-/object.assign-4.0.4.tgz", + "uglify-js": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "uri-path": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz" + }, + "dependencies": { + "uglify-js": { + "version": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "uglify-to-browserify": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "yargs": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz" + } + } + } + }, + "grunt-contrib-watch": { + "version": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.0.0.tgz", + "integrity": "sha1-hKGnodar0m7VaEE0lscxM+mQAY8=", + "dev": true, + "requires": { + "async": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "gaze": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "tiny-lr": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.2.1.tgz" + }, + "dependencies": { + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + } + } + }, + "grunt-haml2html": { + "version": "https://registry.npmjs.org/grunt-haml2html/-/grunt-haml2html-0.3.1.tgz", + "integrity": "sha1-b7W9R+JQ3Q9VzS76kj7kMQFEFCM=", + "dev": true, + "requires": { + "dargs": "https://registry.npmjs.org/dargs/-/dargs-0.1.0.tgz" + }, + "dependencies": { + "dargs": { + "version": "https://registry.npmjs.org/dargs/-/dargs-0.1.0.tgz", + "integrity": "sha1-I2Stn0Qfl23NX+mWHiFxVmWl48M=", + "dev": true + } + } + }, + "grunt-known-options": { + "version": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.0.tgz", + "integrity": "sha1-pCdO6zL6dl2lp6OxcSYXzjsUQUk=", + "dev": true + }, + "grunt-legacy-log": { + "version": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-1.0.0.tgz", + "integrity": "sha1-+4bxgJhHvAfcR4Q/ns1srLYt8tU=", + "dev": true, + "requires": { + "colors": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "grunt-legacy-log-utils": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-1.0.0.tgz", + "hooker": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "underscore.string": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.2.3.tgz" + }, + "dependencies": { + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + } + } + }, + "grunt-legacy-log-utils": { + "version": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-1.0.0.tgz", + "integrity": "sha1-p7ji0Ps1taUPSvmG/BEnSevJbz0=", + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz" + }, + "dependencies": { + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz", + "integrity": "sha1-79nEpuxT87BUEkKZFcPkgk5NJaQ=", + "dev": true + } + } + }, + "grunt-legacy-util": { + "version": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.0.0.tgz", + "integrity": "sha1-OGqnjcbtUJhsKxiVcmWxtIq7m4Y=", + "dev": true, + "requires": { + "async": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "exit": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "getobject": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", + "hooker": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz", + "underscore.string": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.2.3.tgz", + "which": "https://registry.npmjs.org/which/-/which-1.2.14.tgz" + }, + "dependencies": { + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz", + "integrity": "sha1-79nEpuxT87BUEkKZFcPkgk5NJaQ=", + "dev": true + } + } + }, + "grunt-newer": { + "version": "https://registry.npmjs.org/grunt-newer/-/grunt-newer-1.3.0.tgz", + "integrity": "sha1-g8y3od2ny9irI7BZAk6+YUrS80I=", + "dev": true, + "requires": { + "async": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz" + }, + "dependencies": { + "rimraf": { + "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "dev": true, + "requires": { + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + } + } + } + }, + "grunt-ng-annotate": { + "version": "https://registry.npmjs.org/grunt-ng-annotate/-/grunt-ng-annotate-3.0.0.tgz", + "integrity": "sha1-dqKiGha6Y+Vve+G3XuXErZMCeTk=", + "dev": true, + "requires": { + "lodash.clonedeep": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "ng-annotate": "https://registry.npmjs.org/ng-annotate/-/ng-annotate-1.2.2.tgz" + } + }, + "gzip-size": { + "version": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz", + "integrity": "sha1-Zs+LEBBHInuVus5uodoMF37Vwi8=", + "dev": true, + "requires": { + "browserify-zlib": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "concat-stream": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz" + } + }, + "har-validator": { + "version": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "commander": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "is-my-json-valid": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + } + }, + "has": { + "version": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz" + } + }, + "has-ansi": { + "version": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + } + }, + "has-binary": { + "version": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", + "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=", + "dev": true, + "requires": { + "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "dependencies": { + "isarray": { + "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, + "has-cors": { + "version": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "hash-base": { + "version": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + }, + "hash.js": { + "version": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha1-NA3tvmKQGHFRweodd3o0SJNd+EY=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimalistic-assert": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz" + } + }, + "hasha": { + "version": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz", + "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", + "dev": true, + "requires": { + "is-stream": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + } + }, + "hawk": { + "version": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "requires": { + "boom": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "cryptiles": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "hoek": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "sntp": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + } + }, + "he": { + "version": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "header-case": { + "version": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz", + "integrity": "sha1-lTWXMZfBRLCWE81l0xfvGZY70C0=", + "dev": true, + "requires": { + "no-case": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", + "upper-case": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz" + } + }, + "hmac-drbg": { + "version": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "minimalistic-assert": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "minimalistic-crypto-utils": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" + } + }, + "hoek": { + "version": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "home-or-tmp": { + "version": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true, + "requires": { + "os-homedir": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } + }, + "hooker": { + "version": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", + "dev": true + }, + "hosted-git-info": { + "version": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "integrity": "sha1-bWDjSzq7yDEwYsO3mO+NkBoHrzw=", + "dev": true + }, + "html-minifier": { + "version": "https://registry.npmjs.org/html-minifier/-/html-minifier-2.1.7.tgz", + "integrity": "sha1-kFHW/LvPIU7TB+GtdPQyu5rWVcw=", + "dev": true, + "requires": { + "change-case": "https://registry.npmjs.org/change-case/-/change-case-3.0.1.tgz", + "clean-css": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.27.tgz", + "commander": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "he": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "ncname": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", + "relateurl": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "uglify-js": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.6.4.tgz" + }, + "dependencies": { + "commander": { + "version": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" + } + } + } + }, + "htmlescape": { + "version": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", + "dev": true + }, + "http-errors": { + "version": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", + "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz" + } + }, + "http-proxy": { + "version": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", + "dev": true, + "requires": { + "eventemitter3": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "requires-port": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + } + }, + "http-signature": { + "version": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "jsprim": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "sshpk": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz" + } + }, + "https-browserify": { + "version": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "iconv-lite": { + "version": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", + "integrity": "sha1-I9hlaxaq5nQqwpcy6o8DNqR4nPI=", + "dev": true + }, + "ieee754": { + "version": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", + "dev": true + }, + "indent-string": { + "version": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" + } + }, + "indexof": { + "version": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + }, + "inherits": { + "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inline-source-map": { + "version": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, + "requires": { + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } + }, + "insert-module-globals": { + "version": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz", + "integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=", + "dev": true, + "requires": { + "JSONStream": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "combine-source-map": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz", + "concat-stream": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "lexical-scope": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz", + "process": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } + }, + "invariant": { + "version": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", + "dev": true, + "requires": { + "loose-envify": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz" + } + }, + "is-arrayish": { + "version": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz" + } + }, + "is-buffer": { + "version": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", + "dev": true + }, + "is-builtin-module": { + "version": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz" + } + }, + "is-dotfile": { + "version": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz" + } + }, + "is-extendable": { + "version": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-finite": { + "version": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + } + }, + "is-glob": { + "version": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + } + }, + "is-lower-case": { + "version": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", + "integrity": "sha1-fhR75HaNxGbbO/shzGCzHmrWk5M=", + "dev": true, + "requires": { + "lower-case": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz" + } + }, + "is-my-json-valid": { + "version": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", + "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", + "dev": true, + "requires": { + "generate-function": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "generate-object-property": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "jsonpointer": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } + }, + "is-number": { + "version": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" + } + }, + "is-posix-bracket": { + "version": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-property": { + "version": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-stream": { + "version": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-typedarray": { + "version": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-upper-case": { + "version": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", + "integrity": "sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8=", + "dev": true, + "requires": { + "upper-case": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz" + } + }, + "is-utf8": { + "version": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "isarray": { + "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isbinaryfile": { + "version": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", + "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=", + "dev": true + }, + "isexe": { + "version": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + } + }, + "isstream": { + "version": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "jasmine": { + "version": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", + "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", + "dev": true, + "requires": { + "exit": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "jasmine-core": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz" + } + }, + "jasmine-core": { + "version": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "dev": true + }, + "js-tokens": { + "version": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz", + "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=", + "dev": true, + "requires": { + "argparse": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "esprima": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz" + } + }, + "jsbn": { + "version": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "jsesc": { + "version": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, + "json-schema": { + "version": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-stable-stringify": { + "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", + "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", + "dev": true, + "requires": { + "jsonify": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + } + }, + "json-stringify-safe": { + "version": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "json5": { + "version": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "jsonfile": { + "version": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + } + }, + "jsonify": { + "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonparse": { + "version": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "jsonpointer": { + "version": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true + }, + "jsprim": { + "version": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", + "dev": true, + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "extsprintf": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "json-schema": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "verror": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" + }, + "dependencies": { + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "karma": { + "version": "https://registry.npmjs.org/karma/-/karma-1.7.1.tgz", + "integrity": "sha1-hcwI6eCiLXzpzKN8ShvoJPaisa4=", + "dev": true, + "requires": { + "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "body-parser": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "chokidar": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "colors": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "combine-lists": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", + "connect": "https://registry.npmjs.org/connect/-/connect-3.6.5.tgz", + "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "di": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "dom-serialize": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "expand-braces": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "http-proxy": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "isbinaryfile": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "log4js": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", + "mime": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "optimist": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "qjobs": "https://registry.npmjs.org/qjobs/-/qjobs-1.1.5.tgz", + "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "socket.io": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.3.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "tmp": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", + "useragent": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz" + }, + "dependencies": { + "body-parser": { + "version": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "dev": true, + "requires": { + "bytes": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "content-type": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "depd": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "qs": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "raw-body": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "type-is": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz" + } + }, + "bytes": { + "version": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "content-type": { + "version": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", + "dev": true + }, + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + } + }, + "depd": { + "version": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "dev": true + }, + "http-errors": { + "version": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "dev": true, + "requires": { + "depd": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "setprototypeof": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz" + } + }, + "iconv-lite": { + "version": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs=", + "dev": true + }, + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + }, + "qs": { + "version": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg=", + "dev": true + }, + "raw-body": { + "version": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "dev": true, + "requires": { + "bytes": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "unpipe": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + } + }, + "rimraf": { + "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "dev": true, + "requires": { + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + } + } + } + }, + "karma-cli": { + "version": "https://registry.npmjs.org/karma-cli/-/karma-cli-1.0.1.tgz", + "integrity": "sha1-rmw8WKMTodALRRZMRVubhs4X+WA=", + "dev": true, + "requires": { + "resolve": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" + } + }, + "karma-jasmine": { + "version": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.0.tgz", + "integrity": "sha1-IuTAa/mhguUpTR9wXjczgRuBCs8=", + "dev": true + }, + "karma-phantomjs-launcher": { + "version": "https://registry.npmjs.org/karma-phantomjs-launcher/-/karma-phantomjs-launcher-1.0.4.tgz", + "integrity": "sha1-0jyjSAG9qYY60xjju0vUBisTrNI=", + "dev": true, + "requires": { + "lodash": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "phantomjs-prebuilt": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.14.tgz" + } + }, + "kew": { + "version": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", + "dev": true + }, + "kind-of": { + "version": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + } + }, + "klaw": { + "version": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + } + }, + "labeled-stream-splicer": { + "version": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz", + "integrity": "sha1-pS4dE4AkwAuGscDJH2d5GLiuClk=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "stream-splicer": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz" + }, + "dependencies": { + "isarray": { + "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, + "lazy-cache": { + "version": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, + "lexical-scope": { + "version": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz", + "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=", + "dev": true, + "requires": { + "astw": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz" + } + }, + "livereload-js": { + "version": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.2.2.tgz", + "integrity": "sha1-bIclfmSKtHW8JOoldFftzB+NC8I=", + "dev": true + }, + "load-json-file": { + "version": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "parse-json": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "strip-bom": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" + } + }, + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + }, + "lodash.clonedeep": { + "version": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.memoize": { + "version": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "dev": true + }, + "log4js": { + "version": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", + "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=", + "dev": true, + "requires": { + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "semver": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz" + }, + "dependencies": { + "isarray": { + "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + }, + "semver": { + "version": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "dev": true + }, + "string_decoder": { + "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "longest": { + "version": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "loose-envify": { + "version": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "dev": true, + "requires": { + "js-tokens": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" + } + }, + "loud-rejection": { + "version": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "signal-exit": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" + } + }, + "lower-case": { + "version": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lower-case-first": { + "version": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", + "integrity": "sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E=", + "dev": true, + "requires": { + "lower-case": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz" + } + }, + "lru-cache": { + "version": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "dev": true + }, + "map-obj": { + "version": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "maxmin": { + "version": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz", + "integrity": "sha1-cTZehKmd2Piz99X94vANHn9zvmE=", + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "figures": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "gzip-size": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz", + "pretty-bytes": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz" + } + }, + "md5.js": { + "version": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "dev": true, + "requires": { + "hash-base": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "dependencies": { + "hash-base": { + "version": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + } + } + }, + "media-typer": { + "version": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "meow": { + "version": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "loud-rejection": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "map-obj": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "normalize-package-data": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "read-pkg-up": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "redent": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "trim-newlines": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz" + }, + "dependencies": { + "minimist": { + "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "micromatch": { + "version": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "array-unique": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "braces": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "expand-brackets": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "extglob": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "filename-regex": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "normalize-path": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "object.omit": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "parse-glob": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "regex-cache": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz" + } + }, + "miller-rabin": { + "version": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=", + "dev": true, + "requires": { + "bn.js": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "brorand": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" + } + }, + "mime": { + "version": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY=", + "dev": true + }, + "mime-db": { + "version": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", + "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", + "dev": true + }, + "mime-types": { + "version": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", + "dev": true, + "requires": { + "mime-db": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz" + } + }, + "minimalistic-assert": { + "version": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "dev": true, + "requires": { + "brace-expansion": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz" + } + }, + "minimist": { + "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + }, + "module-deps": { + "version": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz", + "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", + "dev": true, + "requires": { + "JSONStream": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "browser-resolve": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", + "cached-path-relative": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz", + "concat-stream": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "defined": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "detective": "https://registry.npmjs.org/detective/-/detective-4.5.0.tgz", + "duplexer2": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "parents": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "resolve": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "stream-combiner2": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "subarg": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", + "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=", + "dev": true, + "optional": true + }, + "ncname": { + "version": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", + "integrity": "sha1-W1etGLHKCShk72Kwse2BlPODtxw=", + "dev": true, + "requires": { + "xml-char-classes": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz" + } + }, + "negotiator": { + "version": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "ng-annotate": { + "version": "https://registry.npmjs.org/ng-annotate/-/ng-annotate-1.2.2.tgz", + "integrity": "sha1-3D/FG6Cy+LOF2+BH9NoG9YCh/WE=", + "dev": true, + "requires": { + "acorn": "https://registry.npmjs.org/acorn/-/acorn-2.6.4.tgz", + "alter": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", + "convert-source-map": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "optimist": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "ordered-ast-traverse": "https://registry.npmjs.org/ordered-ast-traverse/-/ordered-ast-traverse-1.1.1.tgz", + "simple-fmt": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz", + "simple-is": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "stable": "https://registry.npmjs.org/stable/-/stable-0.1.6.tgz", + "stringmap": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz", + "stringset": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz", + "tryor": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz" + }, + "dependencies": { + "acorn": { + "version": "https://registry.npmjs.org/acorn/-/acorn-2.6.4.tgz", + "integrity": "sha1-6x9FtKQ/ox0DcBpexG87Umc+kO4=", + "dev": true + }, + "convert-source-map": { + "version": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + } + } + }, + "no-case": { + "version": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", + "integrity": "sha1-euuhxzpSGEJlVUt9wDuvcg34AIE=", + "dev": true, + "requires": { + "lower-case": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz" + } + }, + "nopt": { + "version": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz" + } + }, + "normalize-package-data": { + "version": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", + "dev": true, + "requires": { + "hosted-git-info": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "is-builtin-module": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "semver": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "validate-npm-package-license": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz" + } + }, + "normalize-path": { + "version": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" + } + }, + "number-is-nan": { + "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "object-assign": { + "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-component": { + "version": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", + "dev": true + }, + "object-keys": { + "version": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "dev": true + }, + "object.assign": { + "version": "https://registry.npmjs.org/object.assign/-/object.assign-4.0.4.tgz", + "integrity": "sha1-scnMBE7xuf5jYG/BQau7MuFHMMw=", + "dev": true, + "requires": { + "define-properties": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "object-keys": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz" + } + }, + "object.omit": { + "version": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "is-extendable": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" + } + }, + "on-finished": { + "version": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + } + }, + "once": { + "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + }, + "optimist": { + "version": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "wordwrap": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" + } + }, + "options": { + "version": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", + "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", + "dev": true + }, + "ordered-ast-traverse": { + "version": "https://registry.npmjs.org/ordered-ast-traverse/-/ordered-ast-traverse-1.1.1.tgz", + "integrity": "sha1-aEOhcLwO7otSDMjdwd3TqjD6BXw=", + "dev": true, + "requires": { + "ordered-esprima-props": "https://registry.npmjs.org/ordered-esprima-props/-/ordered-esprima-props-1.1.0.tgz" + } + }, + "ordered-esprima-props": { + "version": "https://registry.npmjs.org/ordered-esprima-props/-/ordered-esprima-props-1.1.0.tgz", + "integrity": "sha1-qYJwht9fAQqmDpvQK24DNc6i/8s=", + "dev": true + }, + "os-browserify": { + "version": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.1.2.tgz", + "integrity": "sha1-ScoCk+CxlZCl9d4Qx/JlphfY/lQ=", + "dev": true + }, + "os-homedir": { + "version": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-tmpdir": { + "version": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "outpipe": { + "version": "https://registry.npmjs.org/outpipe/-/outpipe-1.1.1.tgz", + "integrity": "sha1-UM+GFjZeh+Ax4ppeyTOaPaRyX6I=", + "dev": true, + "requires": { + "shell-quote": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz" + } + }, + "output-file-sync": { + "version": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz", + "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=", + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + } + }, + "pako": { + "version": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", + "dev": true + }, + "param-case": { + "version": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz" + } + }, + "parents": { + "version": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "dev": true, + "requires": { + "path-platform": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz" + } + }, + "parse-asn1": { + "version": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", + "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", + "dev": true, + "requires": { + "asn1.js": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", + "browserify-aes": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.8.tgz", + "create-hash": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "evp_bytestokey": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "pbkdf2": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz" + } + }, + "parse-glob": { + "version": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "is-dotfile": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "is-extglob": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "is-glob": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" + } + }, + "parse-json": { + "version": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz" + } + }, + "parsejson": { + "version": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", + "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", + "dev": true, + "requires": { + "better-assert": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz" + } + }, + "parseqs": { + "version": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true, + "requires": { + "better-assert": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz" + } + }, + "parseuri": { + "version": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz" + } + }, + "parseurl": { + "version": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=", + "dev": true + }, + "pascal-case": { + "version": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz", + "integrity": "sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=", + "dev": true, + "requires": { + "camel-case": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "upper-case-first": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz" + } + }, + "path-browserify": { + "version": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-case": { + "version": "https://registry.npmjs.org/path-case/-/path-case-2.1.1.tgz", + "integrity": "sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=", + "dev": true, + "requires": { + "no-case": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz" + } + }, + "path-exists": { + "version": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + } + }, + "path-is-absolute": { + "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-platform": { + "version": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", + "dev": true + }, + "path-type": { + "version": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "pify": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "pinkie-promise": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + } + }, + "pbkdf2": { + "version": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", + "integrity": "sha1-o14TxkeZsGzhUyD0WcIw5o5zut4=", + "dev": true, + "requires": { + "create-hash": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "create-hmac": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "ripemd160": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "sha.js": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz" + } + }, + "pend": { + "version": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "phantomjs-prebuilt": { + "version": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.14.tgz", + "integrity": "sha1-1T0xH8+30dCN2yQBRVjxGIxRbaA=", + "dev": true, + "requires": { + "es6-promise": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.0.5.tgz", + "extract-zip": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.5.0.tgz", + "fs-extra": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "hasha": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz", + "kew": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "progress": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "request": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", + "request-progress": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz", + "which": "https://registry.npmjs.org/which/-/which-1.2.14.tgz" + } + }, + "pify": { + "version": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" + } + }, + "preserve": { + "version": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "pretty-bytes": { + "version": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", + "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", + "dev": true, + "requires": { + "get-stdin": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "meow": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz" + } + }, + "private": { + "version": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=", + "dev": true + }, + "process": { + "version": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "progress": { + "version": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "dev": true + }, + "public-encrypt": { + "version": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", + "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", + "dev": true, + "requires": { + "bn.js": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "browserify-rsa": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "create-hash": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "parse-asn1": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", + "randombytes": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz" + } + }, + "punycode": { + "version": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qjobs": { + "version": "https://registry.npmjs.org/qjobs/-/qjobs-1.1.5.tgz", + "integrity": "sha1-ZZ3p8s+NzCehSBJ28gU3cnI4LnM=", + "dev": true + }, + "qs": { + "version": "https://registry.npmjs.org/qs/-/qs-5.1.0.tgz", + "integrity": "sha1-TZMuXH6kEcynajEtOaYGIA/VDNk=", + "dev": true + }, + "querystring": { + "version": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randomatic": { + "version": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha1-x6vpzIuHwLqodrGf3oP9RkeX44w=", + "dev": true, + "requires": { + "is-number": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz" + }, + "dependencies": { + "is-number": { + "version": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" + }, + "dependencies": { + "kind-of": { + "version": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + } + } + } + }, + "kind-of": { + "version": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + } + } + } + }, + "randombytes": { + "version": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", + "integrity": "sha1-3ACaJGuNCaF3tLegrne8Vw9LG3k=", + "dev": true, + "requires": { + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + }, + "range-parser": { + "version": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "raw-body": { + "version": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz", + "integrity": "sha1-rf6s4uT7MJgFgBTQjActzFl1h3Q=", + "dev": true, + "requires": { + "bytes": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", + "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "unpipe": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + }, + "dependencies": { + "bytes": { + "version": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", + "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=", + "dev": true + }, + "iconv-lite": { + "version": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", + "dev": true + } + } + }, + "read-only-stream": { + "version": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "dev": true, + "requires": { + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + } + }, + "read-pkg": { + "version": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "normalize-package-data": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "path-type": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz" + } + }, + "read-pkg-up": { + "version": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "read-pkg": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz" + } + }, + "readable-stream": { + "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "dev": true, + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + }, + "readdirp": { + "version": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "set-immediate-shim": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz" + } + }, + "redent": { + "version": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "strip-indent": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz" + } + }, + "regenerate": { + "version": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", + "integrity": "sha1-DDNtOYBVPXVcObWGrjsgqknIK38=", + "dev": true + }, + "regenerator-runtime": { + "version": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", + "dev": true + }, + "regenerator-transform": { + "version": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha1-HkmWg3Ix2ot/PPQRTXG1aRoGgN0=", + "dev": true, + "requires": { + "babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", + "babel-types": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", + "private": "https://registry.npmjs.org/private/-/private-0.1.7.tgz" + } + }, + "regex-cache": { + "version": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", + "dev": true, + "requires": { + "is-equal-shallow": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz" + } + }, + "regexpu-core": { + "version": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true, + "requires": { + "regenerate": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", + "regjsgen": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "regjsparser": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz" + } + }, + "regjsgen": { + "version": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" + }, + "dependencies": { + "jsesc": { + "version": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "relateurl": { + "version": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-trailing-separator": { + "version": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz" + } + }, + "request": { + "version": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", + "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", + "dev": true, + "requires": { + "aws-sign2": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "aws4": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "caseless": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "extend": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "forever-agent": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "form-data": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "har-validator": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "hawk": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "http-signature": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "is-typedarray": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "isstream": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "json-stringify-safe": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "oauth-sign": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "qs": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", + "stringstream": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "tough-cookie": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "tunnel-agent": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "uuid": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" + }, + "dependencies": { + "qs": { + "version": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", + "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", + "dev": true + } + } + }, + "request-progress": { + "version": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz", + "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", + "dev": true, + "requires": { + "throttleit": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz" + } + }, + "requires-port": { + "version": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "right-align": { + "version": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "requires": { + "align-text": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz" + } + }, + "rimraf": { + "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", + "dev": true + }, + "ripemd160": { + "version": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", + "dev": true, + "requires": { + "hash-base": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + }, + "safe-buffer": { + "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", + "dev": true + }, + "semver": { + "version": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + }, + "sentence-case": { + "version": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.1.tgz", + "integrity": "sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ=", + "dev": true, + "requires": { + "no-case": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", + "upper-case-first": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz" + } + }, + "set-immediate-shim": { + "version": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "setprototypeof": { + "version": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "dev": true + }, + "sha.js": { + "version": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz", + "integrity": "sha1-mPZIgEdLdPSji42p08Dy0QRjPn0=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + }, + "shasum": { + "version": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", + "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", + "dev": true, + "requires": { + "json-stable-stringify": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", + "sha.js": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz" + } + }, + "shell-quote": { + "version": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "dev": true, + "requires": { + "array-filter": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "array-map": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "array-reduce": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "jsonify": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + } + }, + "signal-exit": { + "version": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "simple-fmt": { + "version": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz", + "integrity": "sha1-GRv1ZqWeZTBILLJatTtKjchcOms=", + "dev": true + }, + "simple-is": { + "version": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz", + "integrity": "sha1-Krt1qt453rXMgVzhDmGRFkhQuvA=", + "dev": true + }, + "slash": { + "version": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "sn-stylekit": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/sn-stylekit/-/sn-stylekit-1.0.12.tgz", + "integrity": "sha512-CkCGj6pS2FB9tF5uVbqMlmKpQ1wJFCcXAri2B4nCq8aAv2D5frUhhPFJ1oCnFgBssB9LxnbW2+C2CQojVCL9ig==", + "dev": true + }, + "snake-case": { + "version": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", + "integrity": "sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=", + "dev": true, + "requires": { + "no-case": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz" + } + }, + "sntp": { + "version": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "requires": { + "hoek": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + } + }, + "socket.io": { + "version": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.3.tgz", + "integrity": "sha1-uK+cq6AJSeVo42nxMn6pvp6iRhs=", + "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "engine.io": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", + "has-binary": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "socket.io-adapter": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", + "socket.io-client": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", + "socket.io-parser": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz" + }, + "dependencies": { + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true, + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + } + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + }, + "object-assign": { + "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", + "dev": true + } + } + }, + "socket.io-adapter": { + "version": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", + "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=", + "dev": true, + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "socket.io-parser": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz" + }, + "dependencies": { + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true, + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + } + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + } + } + }, + "socket.io-client": { + "version": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", + "integrity": "sha1-sw6GqhDV7zVGYBwJzeR2Xjgdo3c=", + "dev": true, + "requires": { + "backo2": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "component-bind": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "component-emitter": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "engine.io-client": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", + "has-binary": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", + "indexof": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "object-component": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "parseuri": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "socket.io-parser": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", + "to-array": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz" + }, + "dependencies": { + "component-emitter": { + "version": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true, + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + } + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + } + } + }, + "socket.io-parser": { + "version": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", + "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=", + "dev": true, + "requires": { + "component-emitter": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "json3": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz" + }, + "dependencies": { + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + } + }, + "isarray": { + "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + } + } + }, + "source-map": { + "version": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true + }, + "source-map-support": { + "version": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", + "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=", + "dev": true, + "requires": { + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + } + }, + "spdx-correct": { + "version": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "dev": true, + "requires": { + "spdx-license-ids": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz" + } + }, + "spdx-expression-parse": { + "version": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", + "dev": true + }, + "spdx-license-ids": { + "version": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", + "dev": true + }, + "sprintf-js": { + "version": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dev": true, + "requires": { + "asn1": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "bcrypt-pbkdf": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "dashdash": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "ecc-jsbn": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "getpass": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "jsbn": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "tweetnacl": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + }, + "dependencies": { + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "stable": { + "version": "https://registry.npmjs.org/stable/-/stable-0.1.6.tgz", + "integrity": "sha1-kQ9dKu17Ugxud3SZwfMuE5/eyxA=", + "dev": true + }, + "statuses": { + "version": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true + }, + "stream-browserify": { + "version": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + } + }, + "stream-combiner2": { + "version": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "requires": { + "duplexer2": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + } + }, + "stream-http": { + "version": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", + "integrity": "sha1-QKBQ7I3DtTsz2ZCUFcAsC/Gr+60=", + "dev": true, + "requires": { + "builtin-status-codes": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "to-arraybuffer": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } + }, + "stream-splicer": { + "version": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz", + "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + } + }, + "string_decoder": { + "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "dev": true, + "requires": { + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + }, + "stringmap": { + "version": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz", + "integrity": "sha1-VWwTeyWPlCuHdvWy71gqoGnX0bE=", + "dev": true + }, + "stringset": { + "version": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz", + "integrity": "sha1-7yWcTjSTRDd/zRyRPdLoSMnAQrU=", + "dev": true + }, + "stringstream": { + "version": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true + }, + "strip-ansi": { + "version": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + } + }, + "strip-bom": { + "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" + } + }, + "strip-indent": { + "version": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + } + }, + "subarg": { + "version": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "dev": true, + "requires": { + "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "dependencies": { + "minimist": { + "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "supports-color": { + "version": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "swap-case": { + "version": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", + "integrity": "sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM=", + "dev": true, + "requires": { + "lower-case": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "upper-case": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz" + } + }, + "syntax-error": { + "version": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.3.0.tgz", + "integrity": "sha1-HtkmbE1AvnXcVb+bsct3Biu5bKE=", + "dev": true, + "requires": { + "acorn": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz" + } + }, + "throttleit": { + "version": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "dev": true + }, + "through": { + "version": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } + }, + "timers-browserify": { + "version": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "dev": true, + "requires": { + "process": "https://registry.npmjs.org/process/-/process-0.11.10.tgz" + } + }, + "tiny-lr": { + "version": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.2.1.tgz", + "integrity": "sha1-s/26gC5dVqM8L28QeUsy5Hescp0=", + "dev": true, + "requires": { + "body-parser": "https://registry.npmjs.org/body-parser/-/body-parser-1.14.2.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "faye-websocket": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "livereload-js": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.2.2.tgz", + "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "qs": "https://registry.npmjs.org/qs/-/qs-5.1.0.tgz" + }, + "dependencies": { + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + } + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + } + } + }, + "title-case": { + "version": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", + "integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=", + "dev": true, + "requires": { + "no-case": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", + "upper-case": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz" + } + }, + "tmp": { + "version": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", + "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", + "dev": true, + "requires": { + "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } + }, + "to-array": { + "version": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", + "dev": true + }, + "to-arraybuffer": { + "version": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "tough-cookie": { + "version": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "dev": true, + "requires": { + "punycode": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + } + }, + "trim-newlines": { + "version": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "trim-right": { + "version": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "tryor": { + "version": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz", + "integrity": "sha1-gUXkynyv9ArN48z5Rui4u3W0Fys=", + "dev": true + }, + "tty-browserify": { + "version": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true + }, + "tweetnacl": { + "version": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "type-is": { + "version": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "dev": true, + "requires": { + "media-typer": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz" + } + }, + "typedarray": { + "version": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "uglify-js": { + "version": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.6.4.tgz", + "integrity": "sha1-ZeovswWck5RpLxX+2HwrNsFrmt8=", + "dev": true, + "requires": { + "async": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "uglify-to-browserify": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "yargs": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz" + }, + "dependencies": { + "async": { + "version": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + } + } + }, + "uglify-to-browserify": { + "version": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true + }, + "ultron": { + "version": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", + "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", + "dev": true + }, + "umd": { + "version": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz", + "integrity": "sha1-iuVW4RAR9jwllnCKiDclnwGz1g4=", + "dev": true + }, + "underscore.string": { + "version": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.2.3.tgz", + "integrity": "sha1-gGmSYzZl1eX8tNsfs6hi62jp5to=", + "dev": true + }, + "unpipe": { + "version": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "upper-case": { + "version": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "upper-case-first": { + "version": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", + "integrity": "sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=", + "dev": true, + "requires": { + "upper-case": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz" + } + }, + "uri-path": { + "version": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz", + "integrity": "sha1-l0fwGDWJM8Md4PzP2C0TjmcmLjI=", + "dev": true + }, + "url": { + "version": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "querystring": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" + }, + "dependencies": { + "punycode": { + "version": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "user-home": { + "version": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", + "dev": true + }, + "useragent": { + "version": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz", + "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", + "dev": true, + "requires": { + "lru-cache": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", + "tmp": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz" + }, + "dependencies": { + "lru-cache": { + "version": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", + "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=", + "dev": true + } + } + }, + "util": { + "version": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "dependencies": { + "inherits": { + "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=", + "dev": true + }, + "v8flags": { + "version": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", + "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", + "dev": true, + "requires": { + "user-home": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz" + } + }, + "validate-npm-package-license": { + "version": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "dev": true, + "requires": { + "spdx-correct": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "spdx-expression-parse": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz" + } + }, + "verror": { + "version": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", + "dev": true, + "requires": { + "extsprintf": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" + } + }, + "vm-browserify": { + "version": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz" + } + }, + "void-elements": { + "version": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "watchify": { + "version": "https://registry.npmjs.org/watchify/-/watchify-3.9.0.tgz", + "integrity": "sha1-8HX9LoqGrN6Eztum5cKgvt1SPZ4=", + "dev": true, + "requires": { + "anymatch": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "browserify": "https://registry.npmjs.org/browserify/-/browserify-14.4.0.tgz", + "chokidar": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "defined": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "outpipe": "https://registry.npmjs.org/outpipe/-/outpipe-1.1.1.tgz", + "through2": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "xtend": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } + }, + "websocket-driver": { + "version": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "dev": true, + "requires": { + "websocket-extensions": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz" + } + }, + "websocket-extensions": { + "version": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz", + "integrity": "sha1-domUmcGEtu91Q3fC27DNbLVdKec=", + "dev": true + }, + "which": { + "version": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", + "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", + "dev": true, + "requires": { + "isexe": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + } + }, + "window-size": { + "version": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "wrappy": { + "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", + "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", + "dev": true, + "requires": { + "options": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", + "ultron": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz" + } + }, + "wtf-8": { + "version": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", + "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=", + "dev": true + }, + "xml-char-classes": { + "version": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", + "integrity": "sha1-ZGV4SKIP/F31g6Qq2KJ3tFErvE0=", + "dev": true + }, + "xmlhttprequest-ssl": { + "version": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", + "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=", + "dev": true + }, + "xtend": { + "version": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "yargs": { + "version": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "cliui": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "window-size": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" + }, + "dependencies": { + "camelcase": { + "version": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + } + } + }, + "yauzl": { + "version": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "dev": true, + "requires": { + "fd-slicer": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz" + } + }, + "yeast": { + "version": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + } + } +} diff --git a/package.json b/package.json index 6e3a91bc1..71547d01e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "neeto", + "name": "standard-notes", "version": "1.0.0", "repository": { "type": "git", @@ -32,7 +32,8 @@ "karma": "^1.4.0", "karma-cli": "^1.0.1", "karma-jasmine": "^1.1.0", - "karma-phantomjs-launcher": "^1.0.2" + "karma-phantomjs-launcher": "^1.0.2", + "sn-stylekit": "1.0.12" }, "license": "GPL-3.0" } diff --git a/public/extensions/extensions-manager b/public/extensions/extensions-manager new file mode 160000 index 000000000..3bfd3c1b4 --- /dev/null +++ b/public/extensions/extensions-manager @@ -0,0 +1 @@ +Subproject commit 3bfd3c1b4fb1225fe6b063eacc51cc05697f47a8 diff --git a/test/javascripts/controllers/HomeCtrl_spec.js b/test/javascripts/controllers/HomeCtrl_spec.js index 0ae1e1650..d93065c78 100644 --- a/test/javascripts/controllers/HomeCtrl_spec.js +++ b/test/javascripts/controllers/HomeCtrl_spec.js @@ -1,6 +1,6 @@ -describe("app.frontend", function() { +describe("app", function() { - beforeEach(module('app.frontend')); + beforeEach(module('app')); describe('Home Controller', function() { diff --git a/test/javascripts/filters/DateFilter_spec.js b/test/javascripts/filters/DateFilter_spec.js index b79c1dce9..cdfa55a55 100644 --- a/test/javascripts/filters/DateFilter_spec.js +++ b/test/javascripts/filters/DateFilter_spec.js @@ -1,5 +1,5 @@ describe("date filter", function() { - beforeEach(module('app.frontend')); + beforeEach(module('app')); var $filter; beforeEach(inject(function(_$filter_){ diff --git a/vendor/assets/fonts/ionicons.eot b/vendor/assets/fonts/ionicons.eot index 92a3f20a3..9edec3907 100644 Binary files a/vendor/assets/fonts/ionicons.eot and b/vendor/assets/fonts/ionicons.eot differ diff --git a/vendor/assets/fonts/ionicons.svg b/vendor/assets/fonts/ionicons.svg index 49fc8f367..5188c5687 100644 --- a/vendor/assets/fonts/ionicons.svg +++ b/vendor/assets/fonts/ionicons.svg @@ -1,2230 +1,36 @@ - + -Created by FontForge 20120731 at Thu Dec 4 09:51:48 2014 - By Adam Bradley -Created by Adam Bradley with FontForge 2.0 (http://fontforge.sf.net) +Created by FontForge 20170925 at Fri Jan 19 12:18:20 2018 + By mo +Copyright (c) 2018, mo - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vendor/assets/fonts/ionicons.ttf b/vendor/assets/fonts/ionicons.ttf index c4e463248..01db5bbde 100644 Binary files a/vendor/assets/fonts/ionicons.ttf and b/vendor/assets/fonts/ionicons.ttf differ diff --git a/vendor/assets/fonts/ionicons.woff b/vendor/assets/fonts/ionicons.woff index 5f3a14e0a..20532652f 100644 Binary files a/vendor/assets/fonts/ionicons.woff and b/vendor/assets/fonts/ionicons.woff differ