diff --git a/.eslintignore b/.eslintignore index 30d911da9..d6a0b59bb 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,4 @@ node_modules app/bower_components app/src/padlock.js -app/src/components test diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 54b9fe6c1..000000000 --- a/.jshintrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "curly": true, - "indent": 4, - "nonbsp": true, - "quotmark": "double", - "undef": true, - "unused": true, - "strict": true, - "maxlen": 120, - "newcap": false, - "browser": true, - "expr": true -} diff --git a/README.md b/README.md index cbfdad01d..970052ac5 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A minimal open source password manager. **If you just want to use the app, we recommend downloading one of the [official releases](https://github.com/maklesoft/padlock/releases).** -However, if you want to get your hands dirty and contribute or build the app from source read on! +However, if you want to get your hands dirty and contribute or build your own version from source, read on! ## Getting Started diff --git a/app/.eslintrc.json b/app/.eslintrc.json index ad15baf32..37bab586f 100644 --- a/app/.eslintrc.json +++ b/app/.eslintrc.json @@ -31,7 +31,7 @@ "no-console": ["error", { "allow": ["warn", "error"] }], "no-loop-func": "error", "no-multi-spaces": "error", - "no-multiple-empty-lines": ["error", {"max": 1, "maxBOF": 0, "maxEOF": 0}], + "no-multiple-empty-lines": ["error", {"max": 1, "maxBOF": 1, "maxEOF": 0}], "no-spaced-func": "error", "no-trailing-spaces": "error", "no-undef": "error", diff --git a/app/src/components/app/app.html b/app/src/components/app/app.html deleted file mode 100644 index 1b1987596..000000000 --- a/app/src/components/app/app.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/components/app/app.js b/app/src/components/app/app.js deleted file mode 100644 index 6cc9790dc..000000000 --- a/app/src/components/app/app.js +++ /dev/null @@ -1,949 +0,0 @@ -/* jshint browser: true */ -/* global Polymer, padlock */ - -/** - * Top-level component for rendering application interface. Requires a `padlock.Collection` and - * `padlock.Settings` object to be passed into the constructor as dependencies. - */ -padlock.App = (function(Polymer, platform) { - "use strict"; - - return Polymer({ - is: "padlock-app", - properties: { - //* `padlock.Settings` object handling application settings - settings: Object, - //* `padlock.Collection object holding main user data - collection: Object, - //* String used to filter record list - _filterString: String, - //* Currently selected record (will be opened in record view) - _selected: { - type: Object, - observer: "_selectedChanged" - }, - //* View that is currently active - _currentView: Object, - //* Array of records which acts as a data binding proxy for the data from the `collection` object - _records: { - type: Array, - value: function() { return []; } - }, - //* Array of categories, which is managed during runtime by aggregating the `category` properties - //* of all records - _categories: { - type: Array, - value: function() { return []; } - } - }, - listeners: { - "open-form": "_openFormHandler", - "alert": "_alertHandler", - "notify": "_notify", - "error": "_errorHandler", - "buy-subscription": "_buySubscription" - }, - observers: [ - "_saveSettings(settings.*)", - "_notifyHeaderTitle(_selected.name)", - "_autoLockChanged(settings.auto_lock, settings.auto_lock_delay, _currentView)" - ], - // This is called by the constructor with the same arguments passed into the constructor - factoryImpl: function() { - this.init.apply(this, arguments); - }, - //* Initialize application with a `padlock.Collection` and `padlock.Settings` object - init: function(collection, settings, announcements) { - this.collection = collection; - // Wire up the collection object with the `_records` binding proxy by subscribing to the `update` - // event - this.collection.addEventListener("update", function(e) { - // Starting from the 3rd arguments, all passed in arguments are added records - e.detail.slice(2).forEach(function(record) { - // Add category to `_categories` list in case it is not there yet - if (record.category && this._categories.indexOf(record.category) == -1) { - this.push("_categories", record.category); - } - }.bind(this)); - // We need to use the `splice` api to make sure that the `repeat` template picks up the change - this.splice.apply(this, ["_records"].concat(e.detail)); - }.bind(this)); - - this.settings = settings; - - this.announcements = announcements; - - this.remoteSource = new padlock.CloudSource(this.settings); - - // Check if collection data already exists in persistent storage. If no, that means that means that - // we are starting with a 'clean slate' and have to set a master password first - this.collection.exists({success: this._initView.bind(this), fail: function() { - this._alert("Padlock found existing data stored on the device but failed to read it. " + - "This may be due to someone medling with your device storage or some sort of failure " + - "on the OS level. You may proceed by creating a new store but note that this will " + - "overwrite the existing data."); - this._initView(false); - }.bind(this)}); - - document.addEventListener("touchstart", this._autoLockChanged.bind(this), false); - document.addEventListener("keydown", this._autoLockChanged.bind(this), false); - document.addEventListener("mousemove", - this.debounce.bind(this, "resetautolock", this._autoLockChanged, 50), false); - - // If we want to capture all keydown events, we have to add the listener - // directly to the document - document.addEventListener("keydown", this._keydown.bind(this), false); - - // Listen for android back button - document.addEventListener("backbutton", this._back.bind(this), false); - - // Lock app when it goes into the background - document.addEventListener("pause", this._pause.bind(this), false); - - // Init view when app resumes - document.addEventListener("resume", this._resume.bind(this, true), false); - - window.addEventListener("update-downloaded", function(e) { - this._updateDownloaded.apply(this, e.detail); - }.bind(this)); - }, - _cancelAutoLock: function() { - this._pausedAt = null; - if (this._lockTimeout) { - clearTimeout(this._lockTimeout); - } - if (this._lockNotificationTimeout) { - clearTimeout(this._lockNotificationTimeout); - } - }, - _autoLockChanged: function() { - this._cancelAutoLock(); - - if ( - this.settings.auto_lock && - this._currentView !== this.$.lockView && - this._currentView !== this.$.startView - ) { - this._lockTimeout = setTimeout(this._lock.bind(this, true), - this.settings.auto_lock_delay * 60 * 1000); - this._lockNotificationTimeout = setTimeout(function() { - this.$.notification.show("Auto-lock in 10 seconds", "warning", 3000); - }.bind(this), (this.settings.auto_lock_delay) * 50 * 1000); - } - }, - _initView: function(collExists) { - // If there already is data in the local storage ask for password - // Otherwise start with choosing a new one - var view = collExists ? this.$.lockView : this.$.startView; - - // open the first view - // this._openView(this.$.listView, { animation: "" }); - this._openView(view, { animation: "" }); - - // Fetch and display announcements from Padlock Server - this.announcements.fetch(function(aa) { - this._currentAnnouncements = aa; - this._displayNextAnnouncement(); - }.bind(this)); - }, - // Enter handler for lock view; triggers unlock attempt - _pwdEnter: function(event, detail) { - this._unlock(detail.password); - }, - _changePwd: function() { - this._openStartView(true); - }, - _popinOpen: function(view) { - this._openView( - view, - {animation: "popin", delay: 1500}, - {animation: "expand", delay: 500, easing: "cubic-bezier(1, -0.05, 0.9, 0.05)"} - ); - - // Show header with a slight delay - this.async(function() { - this.$.header.showing = true; - }, 1500); - }, - // Submit handler for start view. A new master password was selected so we have to update it - _newPwd: function(event, detail) { - // Update master password - this.collection.setPassword(detail.password); - this.settings.save(); - // Navigate to list view - this._popinOpen(this.$.listView); - }, - _restored: function() { - this._popinOpen(this.$.listView); - }, - //* Tries to unlock the current collection with the provided password - _unlock: function(password) { - if (this._decrypting) { - // We're already busy decrypting the data, so no unlocking right now! - return; - } - - // set flag to indicate that decryption is in process - this._decrypting = true; - // show progress indicator - this.$.decrypting.show(); - // Attempt to fetch data from storage and decrypt it - this.collection.fetch({password: password, rememberPassword: true, - success: function() { - // Fetch settings from persistent storage - this.settings.fetch({success: function() { - // Write version to settings - this._notifySettings(); - this._unlockSuccess(); - }.bind(this), fail: function() { - // Something went wrong with fetching the settings, so we'll have to start - // with a clean slate - this.settings.save(); - this._unlockSuccess(); - }.bind(this)}); - }.bind(this), fail: this._unlockFail.bind(this)}); - }, - _unlockSuccess: function() { - // Hide progress indicator - this.$.decrypting.hide(); - // We're done decrypting so, reset the `_decrypting` flag - this._decrypting = false; - // Show either the last view shown before locking the screen or default to the list view - this._popinOpen(this.$.listView); - - // After a short delay, trigger synchronization if auto-sync is enabled - this.async(function() { - // If there is more than ten records in the collection and no backup reminder has been - // shown, show it. - if ( - !this.settings.sync_connected && - this._records.filter(function(rec) { return !rec.removed; }).length > 10 && - !this.settings.showed_backup_reminder - ) { - this._showBackupReminder(); - } - - if (this.settings.sync_connected && this.settings.sync_auto) { - this._synchronize(); - } - }, 2000); - }, - _unlockFail: function() { - // Fetching/decrypting the data failed. This can have multiple reasons but by far the most - // likely is that the password was incorrect, so that is what we tell the user. - this.$.notification.show("Wrong Password!", "error", 2000); - // Hide progress indicator - this.$.decrypting.hide(); - // We're done decrypting so, reset the `_decrypting` flag - this._decrypting = false; - }, - // Handler for cordova `pause` event. Records the current time for auto locking when resuming - _pause: function() { - this._pausedAt = new Date(); - }, - // Handler for cordova `resume` event. If auto lock is enabled and the specified time has passed - // since the app was paused, locks the app - _resume: function() { - this._autoLockChanged(); - if ( - this.settings.auto_lock && - this._pausedAt && - new Date().getTime() - this._pausedAt.getTime() > this.settings.auto_lock_delay * 60 * 1000 - ) { - this._lock(true); - } - }, - //* Locks the collection and opens the lock view - _lock: function(auto) { - // Close all currently opened dialogs - this._closeAllDialogs(); - - // Hide header - this.$.header.showing = false; - if (this._currentView !== this.$.lockView && this._currentView !== this.$.startView) { - // Navigate to lock view - this._openView( - this.$.lockView, - {animation: "contract", easing: "cubic-bezier(0.8, 0, 0.2, 1.2)", delay: 300}, - {animation: "popout"} - ); - - if (auto === true) { - var delay = this.settings.auto_lock_delay; - this.async(function() { - this._alert("Padlock was automatically locked after " + - delay + " " + (delay > 1 ? "minutes" : "minute") + " of inactivity. " + - "You can change this behavior from the settings page."); - }, 1000); - } - } - // Wait for a bit for the animation to finish, then clear the collection data from memory - setTimeout(this.collection.clear.bind(this.collection), 500); - }, - //* Change handler for the _selected property; Opens the record view when record is selected - _selectedChanged: function() { - if (this._selected) { - // Open record view (which is bound to the currently selected record) - this._openView(this.$.recordView); - // Blur filter input to hide the keyboard in case it is showing right now - this.$.header.blurFilterInput(); - } - }, - // Opens the provided _view_ - _openView: function(view, inOpts, outOpts) { - var views = Polymer.dom(this.root).querySelectorAll(".view"), - // Choose left or right animation based on the order the views - // are included in the app - back = views.indexOf(view) < views.indexOf(this._currentView); - - // Unless otherwise specified, use a right-to-left animation when navigating 'forward' - // and a left-to-right animation when animating 'back' - inOpts = inOpts || {}; - if (!("animation" in inOpts)) { - inOpts.animation = back ? "slideInFromLeft" : "slideInFromRight"; - } - outOpts = outOpts || {}; - if (!("animation" in outOpts)) { - outOpts.animation = back ? "slideOutToRight" : "slideOutToLeft"; - } - - // Hide current view (if any) - if (this._currentView) { - // Start the in animation after a small delay - this.async(view.show.bind(view, inOpts), inOpts.delay || 100); - this.async(this._currentView.hide.bind(this._currentView, outOpts), outOpts.delay || 0); - } else { - view.show(inOpts); - } - this._currentView = view; - }, - //* Saves changes to the currently selected record (if any) - _saveRecord: function() { - var record = this._selected; - if (record) { - // Save the changes - this.collection.save({record: record}); - // If auto-sync is enabled, sync after each save - if (this.settings.sync_connected && this.settings.sync_auto) { - this._synchronize(); - } - } - }, - //* Opens the dialog for adding a new record - _addRecord: function() { - this._openForm([ - {element: "input", placeholder: "Enter Record Name", value: "", name: "name", autofocus: true}, - {element: "button", label: "Add", submit: true} - ], "Add Record", this._confirmAddRecord.bind(this)); - }, - // Confirmation method for adding a new record. Creates the record object and adds it to the collection - _confirmAddRecord: function(values) { - var record = { - // Default record name to 'Unnamed' - name: values.name || "Unnamed", - // Add default fields specified in settings - fields: this.settings.default_fields.map(function(field) { - return {name: field, value: ""}; - }), - category: "" - }; - - this.collection.add(record); - - // select the newly added record (which will open the record view) - this.$.selector.select(record); - // save the newly create record - this._saveRecord(); - }, - // Deletes the currently selected record (if any) - _deleteRecord: function() { - this.collection.remove(this._selected); - this.collection.save(); - // Notify the element that the `removed` property of the selected record has changed, which - // will cause the list to be rerendered without the item - this.notifyPath("_selected.removed", true); - // Close record view since that is where the record was deleted - this._recordViewBack(); - // Auto sync - if (this.settings.sync_connected && this.settings.sync_auto) { - this._synchronize(); - } - }, - // Navigate back from record view by opening list view - _recordViewBack: function() { - this._openView(this.$.listView, null, { - endCallback: function() { - // after the animation has finished, deselect the currently selected record - this.$.selector.deselect(); - }.bind(this) - }); - }, - // - _openMainMenu: function() { - this.$.mainMenu.open = true; - }, - _openSettings: function() { - this._openView(this.$.settingsView); - }, - _settingsBack: function() { - this._openView(this.$.listView); - }, - _openImportView: function() { - this._openView(this.$.importView); - }, - _openCloudView: function() { - this._openView(this.$.cloudView); - }, - //* Add the records imported with the import view to the collection - _imported: function(event, detail) { - this._openView(this.$.listView); - this.$.notification.show(detail.count + " records imported!", "success", 2000); - // Auto sync - if (this.settings.sync_connected && this.settings.sync_auto) { - this._synchronize(); - } - }, - _openExportView: function() { - this._openView(this.$.exportView); - }, - //* Show an alert dialog with the provided message - _alert: function(msg) { - this._openForm([{element: "button", label: "OK", submit: true}], msg); - }, - //* Keyboard shortcuts - _keydown: function(event) { - var shortcut; - var dialogsOpen = !!Polymer.dom(this.root).querySelectorAll("padlock-dialog.open").length; - - // CTRL/CMD + F -> Filter - if ((event.ctrlKey || event.metaKey) && event.keyCode === 70 && - this._currentView.showFilter && !dialogsOpen) { - shortcut = this.$.header.focusFilterInput.bind(this.$.header); - } - // DOWN -> Mark next - else if (event.keyCode == 40 && !dialogsOpen) { - shortcut = this._currentView.markNext && this._currentView.markNext.bind(this._currentView); - } - // UP -> Mark previous - else if (event.keyCode == 38 && !dialogsOpen) { - shortcut = this._currentView.markPrev && this._currentView.markPrev.bind(this._currentView); - } - // ENTER -> Select marked - else if (event.keyCode == 13 && !dialogsOpen) { - shortcut = this._selectMarkedRecord.bind(this); - } - // ESCAPE -> Back - else if (event.keyCode == 27 && !padlock.currentInput) { - shortcut = this._back.bind(this); - } - // CTRL/CMD + C -> Copy - else if ((event.ctrlKey || event.metaKey) && event.keyCode === 67 && !padlock.currentInput) { - shortcut = this._currentView.copyToClipboard && - this._currentView.copyToClipboard.bind(this._currentView); - } - // CTRL/CMD + N -> New Record - else if ((event.ctrlKey || event.metaKey) && event.keyCode === 78 && !dialogsOpen) { - shortcut = this._currentView.add && this._currentView.add.bind(this._currentView); - } - // CTRL/CMD + S -> Synchronize - else if ((event.ctrlKey || event.metaKey) && event.keyCode === 83 && !dialogsOpen) { - shortcut = this._synchronize(); - } - // CTRL/CMD + L -> Lock - else if ((event.ctrlKey || event.metaKey) && event.keyCode === 76 && !dialogsOpen) { - shortcut = this._lock(); - } - // console.log(event.keyCode); - - // If one of the shortcuts matches, execute it and prevent the default behaviour - if (shortcut) { - shortcut(); - event.preventDefault(); - } - }, - _openCategories: function() { - this._openView(this.$.categoriesView, {animation: "slideInFromBottom"}, {animation: "slideOutToBottom"}); - }, - _categoriesDone: function() { - this._openView(this.$.recordView, { - animation: "slideInFromBottom", - endCallback: this._saveRecord.bind(this) - }, { - animation: "slideOutToBottom" - }); - }, - // Handler for when the category of a record has changed. Basically, when the category name of a - // record is changed, we assume that all other records with the same original category name should - // be updated as well, so that's what we're doing. - _categoryChanged: function(e) { - this._records.forEach(function(rec, ind) { - if (rec.category == e.detail.previous) { - this.set("_records." + ind + ".category", e.detail.current); - } - }.bind(this)); - }, - /** - * Synchronizes the data with a remote source - * @param {String} remotePassword Password for the remote source in case it is different from the local one - */ - _synchronize: function(remotePassword) { - // Ignore the remotePassword argument if it is not a string - remotePassword = typeof remotePassword === "string" ? remotePassword : undefined; - - // Check if the user has connected the client to the cloud already. - // If not, prompt him to do so - if (this.settings.sync_connected) { - // Show progress indicator - this.$.synchronizing.show(); - - // Start synchronization - this.collection.sync(this.remoteSource, { - // The password for the remote source might be different from the local one so if that's - // the case, provide that password explicitly - remotePassword: remotePassword, - success: function() { - this.set("settings.sync_readonly", false); - this.$.synchronizing.hide(); - - // If we explicitly used a different password for the remote source than for the local source, - // ask the user if he wants to update the remote password - if (remotePassword !== undefined && this.collection.defaultPassword !== remotePassword) { - this._promptUpdateRemotePassword(); - } else { - this.$.notification.show("Synchronization successful!", "success", 2000); - } - // Some settings might have been updated during the request - this._notifySettings(); - }.bind(this), - fail: function(e) { - var err = typeof e === "string" ? e : e.error; - switch (err) { - case padlock.ERR_SOURCE_INVALID_JSON: - case padlock.ERR_CRYPTO_INVALID_KEY_PARAMS: - case padlock.ERR_CRYPTO_INVALID_CONTAINER: - this._alert("The data received from Padlock Cloud seems to be corrupt and " + - "cannot be decrypted. This might be due to a network error but could " + - "also be the result of someone trying to compromise your connection to " + - "Padlock Cloud. If the problem persists, please notify " + - "Padlock support!"); - break; - case padlock.ERR_CRYPTO_DECRYPTION_FAILED: - // Decryption failed, presumably on the remote data. This means that the local master - // password does not match the one that was used for encrypting the remote data so - // we need to prompt the user for the correct password. - this._requireRemotePassword(); - break; - case padlock.ERR_CLOUD_SUBSCRIPTION_REQUIRED: - this._alertSubscriptionRequired(); - break; - default: - this._handleError(e); - } - - // Hide progress indicator - this.$.synchronizing.hide(); - // Some settings might have been updated during the request - this._notifySettings(); - }.bind(this) - }); - } else { - var msg = this.settings.sync_key ? - "It seems you have not completed the connection process for Padlock Cloud yet! Go to " + - "Settings > Padlock Cloud for more information!" : - "You need to be connected to Padlock Cloud to synchronize your data. " + - "Go to Settings > Padlock Cloud for more information!"; - // User has not connected to Padlock Cloud yet so we're prompting them to do so - this._openForm([ - {element: "button", label: "Open Padlock Cloud Settings", - tap: this._openCloudView.bind(this), submit: true}, - {element: "button", label: "Cancel", cancel: true} - ], msg); - } - }, - // Prompts for the password to use for the remote source - _requireRemotePassword: function() { - this._openForm([ - {element: "input", name: "password", type: "password", - placeholder: "Enter Remote Password", autofocus: true}, - {element: "button", label: "OK", submit: true}, - {element: "button", label: "Cancel", cancel: true} - ], - "It seems your local master password does not match the one used to encrypt " + - "the data on Padlock Cloud. Please enter your remote password!", function(vals) { - this._synchronize(vals.password); - }.bind(this)); - }, - // Asks if the user wants to update the remote password with the local one - _promptUpdateRemotePassword: function() { - this._openForm( - [ - {element: "button", label: "Yes", submit: true}, - {element: "button", label: "No", cancel: true} - ], - "Synchronization successful! Do you want to change the remote password to your local one?", - this._updateRemotePassword.bind(this) - ); - }, - // Updates the remote password with the local one - _updateRemotePassword: function() { - // Show progress indicator - this.$.synchronizing.show(); - // Update remote password by overwriting the remote data with data encrypted with the local password - this.collection.save({ - source: this.remoteSource, - password: this.collection.defaultPassword, - success: function() { - this.$.synchronizing.hide(); - this.$.notification.show("Remote password updated!", "success", 2000); - }.bind(this), - fail: function() { - this.$.synchronizing.hide(); - this._alert("Failed to update remote password. Try again later!"); - }.bind(this) - }); - }, - //* Back method. Chooses the right back method based on the current view - _back: function() { - var dialogsClosed = this._closeAllDialogs(); - var cw = this._currentView; - - if (!dialogsClosed) { - if ( - (cw == this.$.listView || cw == this.$.lockView || cw == this.$.startView) && - navigator.Backbutton - ) { - this._pause(); - navigator.Backbutton.goBack(); - } else { - cw.back(); - } - } - }, - _listViewBack: function() { - if (this.$.header.filterActive) { - this.$.header.cancelFilter(); - } else { - this._openMainMenu(); - } - }, - /** - * Closes all currently open dialogs - * @returns {Boolean} A boolean indicating whether any dialogs where closed - */ - _closeAllDialogs: function() { - var dialogs = Polymer.dom(this.root).querySelectorAll("padlock-dialog"); - var dialogsClosed = false; - - dialogs.forEach(function(dialog) { - if (dialog.open) { - dialog.open = false; - // In case the dialog contains a dynamic form, call the associated cancel callback - var form = Polymer.dom(dialog).querySelector("padlock-dynamic-form"); - if (form && typeof form.cancelCallback == "function") { - form.cancelCallback(); - } - dialogsClosed = true; - } - }); - - return dialogsClosed; - }, - // Saves the settings to persistent storage - _saveSettings: function() { - // Only save if the the data has actually been loaded from persistent storage yet. This check is - // to prevent settings data being overwritten with default values on app startup - if (this.settings.loaded) { - this.debounce("save_settings", function() { - this.settings.save(); - }, 500); - } - }, - // Notify observers of changes to every existing settings property. This is used as a catch-all - // notification mechanism to update bindings in case the where not notified through other channels - _notifySettings: function() { - for (var prop in this.settings.properties) { - this.notifyPath("settings." + prop, this.settings[prop]); - } - }, - _openStartView: function(changingPwd) { - this.$.startView.mode = (changingPwd === true) ? "change-password" : "get-started"; - this.$.header.showing = false; - this._openView( - this.$.startView, - {animation: "contract", easing: "cubic-bezier(0.8, 0, 0.2, 1.2)", delay: 300}, - {animation: "popout"} - ); - }, - // Handler for when a record is selected from the list view. Updates the `_selected` property through - // the selector element - _recordSelected: function(e) { - this.$.selector.select(e.detail.record); - }, - // Notifies bindings of changes to the header title. It is necessary to do this after a record name was - // changed from the record view - _notifyHeaderTitle: function() { - this.notifyPath("_currentView.headerTitle", this._currentView && this._currentView.headerTitle); - }, - // Notifies bindings of changes to the header title. It is necessary to do this if the header icons change - // based on an action in the current view without the view itself being changed - _notifyHeaderIcons: function() { - this.notifyPath("_currentView.leftHeaderIcon", this._currentView && this._currentView.leftHeaderIcon); - this.notifyPath("_currentView.rightHeaderIcon", this._currentView && this._currentView.rightHeaderIcon); - }, - _openGenerator: function(e) { - // Set field object to generate a value for, if any - this.$.generatorView.field = e && e.detail.field; - // If a value is to be generated for an existing field (meaning the generate view was invoked from - // the record view), show a slightly different animation - if (this.$.generatorView.field) { - this._openView(this.$.generatorView, {animation: "slideInFromBottom"}, {animation: "slideOutToBottom"}); - } else { - this._openView(this.$.generatorView); - } - }, - // Back / cancel handler for generator view. Behaves differently based on where the generator view - // was openened from - _generatorBack: function(e) { - if (e.detail.field) { - // The value was to be generated for an existing field, meaning the generator was invoked from - // the record view but then canceled. Go back to the record view and call the `generateConfirm` - // method, but without the value argument, indicating that the generate action was canceled - this._openView(this.$.recordView, {animation: "slideInFromBottom"}, {animation: "slideOutToBottom"}); - this.async(function() { - this.$.recordView.generateConfirm(e.detail.field, e.detail.value); - }, 100); - } else { - // The generator view had been opened from the list view, so simply go back there - this._openView(this.$.listView); - } - }, - // Opens a dialog with a dynamic form based on the provided arguments - _openForm: function(components, title, submitCallback, cancelCallback, allowDismiss) { - // We have two dialogs; if the first one is currently showing, we use the second one. - // That way we get a nice and smooth transition between dialogs in case we close one and - // instantly open another - var dialog = this.$.formDialog1.isShowing ? this.$.formDialog2 : this.$.formDialog1; - dialog.allowDismiss = allowDismiss !== false; - // Get form and title element associated with the selected form - var form = Polymer.dom(dialog).querySelector("padlock-dynamic-form"); - var titleEl = Polymer.dom(dialog).querySelector(".title"); - // Close both forms - this.$.formDialog1.open = this.$.formDialog2.open = false; - // Update title - titleEl.textContent = title || ""; - // Update components property, which causes the dynamic form elements to be rendered - form.components = components; - // Update callbacks - form.submitCallback = function() { - if (typeof submitCallback === "function") { - submitCallback.apply(null, arguments); - } - dialog.open = false; - }; - form.cancelCallback = function() { - if (typeof cancelCallback === "function") { - cancelCallback.apply(null, arguments); - } - dialog.open = false; - }; - // Open the dialog asynchrously and focus first first input with the `auto-focus` attribute (if any) - this.async(function() { - dialog.open = true; - var input = form.querySelector("input[auto-focus]"); - // Instantly focusing an input look kind of jumpy if a virtual keyboard is shown so only - // do the focus if not on a smartphone/tablet - if (input && !platform.isIOS()) { - input.focus(); - } - }, 50); - }, - // Closes the dialog from which the event was sent - _closeCurrentDialog: function(e) { - e.currentTarget.open = false; - }, - _formDialogOpened: function() { - // This is a little tweak to improve alignment of the keyboard on iOS - platform.keyboardDisableScroll(false); - }, - _formDialogClosed: function(e) { - // Fetch form element of current target - var form = Polymer.dom(e.target).querySelector("padlock-dynamic-form"); - // Blur all input elements - form.blurInputElements(); - // If no dialogs remain open, disable keyboard scroll again - if (!Polymer.dom(this.root).querySelectorAll("padlock-dialog.open").length) { - platform.keyboardDisableScroll(true); - } - }, - // Handler for when a form dialog gets dismissed (by clicking outside of it). Does some cleanup work - // and calls the form cancel callback - _formDialogDismissed: function(e) { - var form = Polymer.dom(e.target).querySelector("padlock-dynamic-form"); - // Call cancel callback if there is one - if (typeof form.cancelCallback == "function") { - form.cancelCallback(); - } - }, - // Simple function for identifying an empty string. Mainly used in declaratative data bindings - _isEmpty: function(str) { - return !str; - }, - // Handler for children elements requesting to open a form - _openFormHandler: function(e) { - this._openForm(e.detail.components, e.detail.title, e.detail.submit, e.detail.cancel, - e.detail.allowDismiss); - }, - // Handler for children elements requesting to open an alert dialog - _alertHandler: function(e) { - this._alert(e.detail.message); - }, - // Display a dialog listing all the available keyboard shortcuts - _displayShortcuts: function() { - this._openForm( - [ - {element: "button", label: "[Esc] \u279e Back / Open Menu", submit: true}, - {element: "button", label: "[ctrl/cmd] + F \u279e Find Record", submit: true}, - {element: "button", label: "[ctrl/cmd] + N \u279e Create New", submit: true}, - {element: "button", label: "[ctrl/cmd] + L \u279e Lock App", submit: true}, - {element: "button", label: "[ctrl/cmd] + S \u279e Synchronize", submit: true}, - {element: "button", label: "[\u2193] \u279e Mark Next Item", submit: true}, - {element: "button", label: "[\u2191] \u279e Mark Previous Item", submit: true}, - {element: "button", label: "[Enter] \u279e Select Marked Item", submit: true} - ] - ); - }, - // Simple function for determining if the current environment is touch-enabled. Mostly used in - // declaratative data bindings - _isTouch: function() { - return platform.isTouch(); - }, - // Shows a toaster notification - _notify: function(e) { - this.$.notification.show(e.detail.message, e.detail.type, e.detail.duration); - }, - // Shows a dialog reminding the user about backing up data and provides some options for doing so - _showBackupReminder: function() { - this._openForm( - [ - {element: "button", label: "Connect To Padlock Cloud", close: true, - tap: this._connectToCloud.bind(this)}, - {element: "button", label: "Export Data", close: true, tap: this._openExportView.bind(this)}, - {element: "button", label: "Dismiss", close: true} - ], - "Have you backed up your data yet? Remember that by default your data is only stored " + - "locally and may be lost if you uninstall Padlock, lose your device or accidentally " + - "reset your data. Padlock Cloud is a great way to back up your data online and synchronize " + - "with other devices. Alternatively, you can export your data and store it somewhere safe." - ); - this.set("settings.showed_backup_reminder", new Date().getTime()); - }, - _errorHandler: function(e) { - this._handleError(e.detail); - }, - _handleError: function(e) { - switch (typeof e === "string" ? e : e.error) { - case padlock.ERR_CLOUD_INVALID_AUTH_TOKEN: - case padlock.ERR_CLOUD_EXPIRED_AUTH_TOKEN: - this.set("settings.sync_connected", false); - this.set("settings.sync_key", ""); - this.set("settings.sync_email", ""); - this._openForm( - [{element: "button", label: "Open Padlock Cloud Settings", - tap: this._openCloudView.bind(this), submit: true}], - "It seems you have been disconnected from Padlock Cloud. Please reconnect " + - "via Settings > Padlock Cloud!" - ); - break; - case padlock.ERR_CRYPTO_DECRYPTION_FAILED: - this._alert("The password you entered was wrong! Please try again!"); - break; - case padlock.ERR_CLOUD_FAILED_CONNECTION: - this._alert("Failed to connect to Padlock Cloud. Please check your " + - "internet connection and try again!"); - break; - case padlock.ERR_CLOUD_VERSION_DEPRECATED: - this._openForm( - [{element: "button", label: "Update Now", tap: this._openAppStore.bind(this), submit: true}], - "A newer version of Padlock is available now! You can download it using the button below. " + - "Please note that you won't be able to use Padlock Cloud until you install the latest version!" - ); - break; - case padlock.ERR_CLOUD_LIMIT_EXCEEDED: - this._alert("Padlock Cloud is over capacity right now. Please try again in a few minutes!"); - break; - default: - this._alert("There was an error while trying to connect to Padlock Cloud. " + - "Please try again later!"); - } - }, - _openAppStore: function() { - window.open(platform.getAppStoreLink(), "_system"); - }, - _alertSubscriptionRequired: function() { - this._openForm( - [ - {element: "button", label: "Go To Padlock Cloud Settings", submit: true, - tap: this._openCloudView.bind(this)}, - ], - "You currently don't have an active subscription which means you can access " + - "your existing data on Padlock Cloud but you won't be able to upload any new data " + - "or synchronize changes between devices. Get a subscription now to regain full access " + - "to Padlock Cloud!" - ); - }, - _selectMarkedRecord: function() { - this._currentView.selectMarked && this._currentView.selectMarked(); - }, - _subscriptionVerified: function() { - this.set("settings.sync_readonly", false); - var actions = this.settings.sync_connected ? [ - {element: "button", label: "Synchronize Now", tap: this._synchronize.bind(this), submit: true}, - {element: "button", label: "Dismiss", cancel: true} - ] : [ - {element: "button", label: "Connect Now", tap: this._connectToCloud.bind(this), submit: true}, - {element: "button", label: "Dismiss", cancel: true} - ]; - this._openForm( - actions, - "Your purchase was successful! You can now start using Padlock Cloud on this device!" - ); - }, - _connectToCloud: function() { - if (this._currentView != this.$.cloudView) { - this._openCloudView(); - } - this.async(this.$.cloudView.connect.bind(this.$.cloudView), 100); - }, - _displayNextAnnouncement: function() { - var a = this._currentAnnouncements[0]; - var dismissed = function() { - this.announcements.markRead(a); - this._currentAnnouncements.splice(0, 1); - this._displayNextAnnouncement(); - }.bind(this); - - if (a) { - var els = a.link ? [{element: "button", label: "Learn More", tap: function() { - window.open(a.link, "_system"); - }}] : []; - els.push({element: "button", label: "Dismiss", submit: true}); - - this._openForm(els, a.text, dismissed, dismissed, true); - } - }, - _updateDownloaded: function() { - this._alert("New update available! Restart the app to install!"); - }, - _resetData: function() { - this.settings.reset(); - this._notifySettings(); - this.collection.clear(); - this.collection.destroy(); - this._openStartView(); - this.async(function() { - this._alert("Data deleted and app reset succesfully!"); - }, 1000); - } - }); - -})(Polymer, padlock.platform); diff --git a/app/src/components/app/app.styl b/app/src/components/app/app.styl deleted file mode 100644 index 6c78d9f91..000000000 --- a/app/src/components/app/app.styl +++ /dev/null @@ -1,35 +0,0 @@ -@import "../../styles/config.styl"; -@import "../../styles/mixins.styl"; - -:host { - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; - margin: auto; - background: $color-quaternary; - overflow: hidden; - unselectable(); -} - -:host(.ios-standalone) { - padlock-header::shadow > div { - padding-top: 20px; - } - - .view { - padding-top: 70px; - } -} - -padlock-progress#decrypting { - z-index: 10; - background: $color-secondary; -} - -padlock-header { - position: absolute; - z-index: 1; - width: 100%; -} diff --git a/app/src/components/categories-view/categories-view.html b/app/src/components/categories-view/categories-view.html deleted file mode 100644 index 453640089..000000000 --- a/app/src/components/categories-view/categories-view.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/src/components/categories-view/categories-view.js b/app/src/components/categories-view/categories-view.js deleted file mode 100644 index dc4fc7849..000000000 --- a/app/src/components/categories-view/categories-view.js +++ /dev/null @@ -1,108 +0,0 @@ -/* jshint browser: true */ -/* global Polymer, padlock */ - -(function(Polymer, ViewBehavior) { - "use strict"; - - Polymer({ - is: "padlock-categories-view", - behaviors: [ViewBehavior], - properties: { - categories: Array, - record: Object - }, - observers: [ - "_updateHeaderTitle(record.name)" - ], - ready: function() { - this.leftHeaderIcon = "cancel"; - this.rightHeaderIcon = "plus"; - this._updateHeaderTitle(); - }, - leftHeaderButton: function() { - this.fire("back"); - }, - rightHeaderButton: function() { - this._newCategory(); - }, - add: function() { - this._newCategory(); - }, - _categoryTapped: function(e) { - this.set("record.category", e.model.item); - this._delayedBack(); - }, - //* Updates the headerTitle property with the name of the current record - _updateHeaderTitle: function() { - this.headerTitle = this.record && this.record.name || "Categories"; - }, - _newCategory: function() { - this.fire("open-form", { - title: "New Category", - components: [ - {element: "input", placeholder: "Category Name", name: "name", autofocus: true}, - {element: "button", label: "Create", submit: true} - ], - submit: function(data) { - this.set("record.category", data.name); - if (this.categories.indexOf(data.name) == -1) { - this.push("categories", data.name); - } - this._delayedBack(200); - }.bind(this) - }); - }, - _editCategory: function(e) { - var category = e.model.item; - this.fire("open-form", { - title: "Edit '" + category + "'", - components: [ - {element: "input", placeholder: "Category Name", name: "name", value: category, autofocus: true}, - {element: "button", label: "Save", submit: true}, - {element: "button", label: "Remove", cancel: true, tap: this._removeCategory.bind(this, category)} - ], - submit: function(data) { - var ind = this.categories.indexOf(category); - this.set("categories." + ind, data.name); - this.fire("categorychanged", {previous: category, current: data.name}); - if (this.record.category == data.name) { - this._delayedBack(200); - } - }.bind(this) - }); - event.stopPropagation(); - }, - _removeCategory: function(category) { - this.fire("open-form", { - title: "Are you sure you want to remove this category? The category will be removed " + - "from all other records as well.", - components: [ - {element: "button", label: "Remove", submit: true}, - {element: "button", label: "Cancel", cancel: true} - ], - submit: function() { - var index = this.categories.indexOf(category); - this.fire("categorychanged", {previous: category, current: ""}); - this.splice("categories", index, 1); - if (!this.record.category) { - this._delayedBack(200); - } - }.bind(this) - }); - }, - _selectNone: function() { - this.set("record.category", ""); - this._delayedBack(); - }, - _isSelected: function(cat, currentCat) { - return cat == currentCat; - }, - _delayedBack: function(delay) { - this.async(this.fire.bind(this, "back"), delay || 50); - }, - _hasCategories: function(count) { - return !!count; - } - }); - -})(Polymer, padlock.ViewBehavior); diff --git a/app/src/components/categories-view/categories-view.styl b/app/src/components/categories-view/categories-view.styl deleted file mode 100644 index e05a2311e..000000000 --- a/app/src/components/categories-view/categories-view.styl +++ /dev/null @@ -1,43 +0,0 @@ -@import "../../styles/config"; -@import "../view/view"; -@import "nib"; - -:host { - overflow: auto; - -webkit-overflow-scrolling: touch; -} - -.category { - line-height: $row-height; - cursor: pointer; - margin: 5px 0; - padding: 0 10px; - text-align: center; - position: relative; - background: $color-quinary; - - shape-shifter { - position: absolute; - right: 0; - top: 0; - padding: 10px; - } - - &[selected] { - color: $text-color-light; - background: $color-secondary; - } -} - -.new-button { - width: 100%; -} - -.header { - background: $color-quinary; - color: $text-color-faint; - text-align: center; - font-size: $font-size-small; - padding: 10px; - overflow: ellipsis; -} diff --git a/app/src/components/cloud-view/cloud-view.html b/app/src/components/cloud-view/cloud-view.html deleted file mode 100644 index 918eec2e2..000000000 --- a/app/src/components/cloud-view/cloud-view.html +++ /dev/null @@ -1,297 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/components/cloud-view/cloud-view.styl b/app/src/components/cloud-view/cloud-view.styl deleted file mode 100644 index 1e50ae462..000000000 --- a/app/src/components/cloud-view/cloud-view.styl +++ /dev/null @@ -1,58 +0,0 @@ -@import "../../styles/config.styl" -@import "nib" - -:host { - overflow-x: hidden; - overflow-y: auto; - -webkit-overflow-scrolling: touch; -} - -.status { - background: $color-quinary; - text-align: center; - font-size: $font-size-small; - padding: 10px; - - .not-connected { - color: $text-color-faint; - } -} - -button, padlock-toggle-button { - display: block; - width: 100%; - box-sizing: border-box; - margin-bottom: 5px; -} - -.note { - background: $color-quinary; - font-size: $font-size-tiny; - padding: 15px; - margin-bottom: 5px; - text-align: center; - - &.warning { - color: $color-error; - } -} - -.conn-id { - margin-top: 10px; - font-weight: bold; -} - -#customUrlInput { - width: 100%; - padding-left: 15px; - padding-right: 15px; - margin-bottom: 5px; - - &:invalid { - color: $color-error; - } -} - -#customUrlInput:valid + .warning { - display: none; -} diff --git a/app/src/components/dialog/dialog.html b/app/src/components/dialog/dialog.html deleted file mode 100644 index 84fea1b26..000000000 --- a/app/src/components/dialog/dialog.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/components/dialog/dialog.js b/app/src/components/dialog/dialog.js deleted file mode 100644 index d7b0b310a..000000000 --- a/app/src/components/dialog/dialog.js +++ /dev/null @@ -1,76 +0,0 @@ -/* global Polymer */ - -(function(Polymer) { - "use strict"; - - Polymer({ - is: "padlock-dialog", - properties: { - open: { - type: Boolean, - value: false, - observer: "_openChanged" - }, - isShowing: { - type: Boolean, - value: false, - notify: true - }, - closeOnTap: Boolean, - allowDismiss: { - type: Boolean, - value: true - } - }, - listeners: { - tap: "_dismiss" - }, - //* Changed handler for the _open_ property. Shows/hides the dialog - _openChanged: function(curr, prev) { - // Set _display: block_ if we're showing. If we're hiding - // we need to wait until the transitions have finished before we - // set _display: none_. - if (this.open) { - this.cancelAsync(this._hideTimeout); - this.style.display = "block"; - this.isShowing = true; - } else { - this._hideTimeout = this.async(function() { - this.style.display = "none"; - this.isShowing = false; - }, 250); - } - - // Trigger relayout to make sure all elements have been rendered - // when applying the transition - // jshint expr: true - // eslint-disable-next-line no-unused-expressions - this.offsetLeft; - // jshint expr: false - - this.toggleClass("open", this.open); - - if (prev !== undefined) { - this.fire(this.open ? "open" : "close"); - } - }, - _innerTap: function(event) { - // Intercept the tap event to prevent closing the popup if one - // of the inner elements was tapped. - if (!this.closeOnTap) { - event.stopPropagation(); - } - }, - //* Closes the popup (duh) - _close: function() { - this.open = false; - }, - _dismiss: function() { - if (this.allowDismiss) { - this._close(); - this.fire("dismiss"); - } - } - }); - -})(Polymer); diff --git a/app/src/components/dialog/dialog.styl b/app/src/components/dialog/dialog.styl deleted file mode 100644 index ea2fd9358..000000000 --- a/app/src/components/dialog/dialog.styl +++ /dev/null @@ -1,84 +0,0 @@ -@import "../../styles/config"; -@import "../../styles/mixins"; -@import "nib"; - -:host { - position: fixed; - z-index: 10; - left: 0; - right: 0; - top: 0; - bottom: 0; - background-color: transparent; - display: none; - - .scrim { - content: ""; - display: block; - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; - background: $scrim-color; - opacity: 0; - transition: opacity 0.4s; - } -} - -:host(.open) .scrim { - opacity: $scrim-opacity; -} - -.scroll-container { - position: absolute; - bottom: 0; - width: 100%; - max-height: 100%; - overflow-x: hidden; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - transition: transform 0.5s $toaster-easing; -} - -.inner { - background: $color-primary; - box-sizing: border-box; - margin-top: 60px; - padding-top: 5px; - overflow: hidden; -} - -.inner ::content > * { - text-align: center; - cursor: pointer; - width: 100%; - box-sizing: border-box; - margin-bottom: 5px; -} - -.inner ::content > .title { - background: transparent; - color: $text-color-light; - cursor: default; - border: none; - padding: 10px; - line-height: 25px; - word-wrap: break-word; - font-weight: $font-weight-bold; - margin-bottom: 5px; - text-shadow(); - - &:empty { - display: none; - } -} - -.inner ::content > button { - border-top: solid 1px; - border-bottom: solid 1px; -} - -:host(:not(.open)) .scroll-container { - transform: translate3d(0, 100%, 0); -} diff --git a/app/src/components/dynamic-form/dynamic-form.html b/app/src/components/dynamic-form/dynamic-form.html deleted file mode 100644 index c62838bce..000000000 --- a/app/src/components/dynamic-form/dynamic-form.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/components/dynamic-form/dynamic-form.styl b/app/src/components/dynamic-form/dynamic-form.styl deleted file mode 100644 index 60d52dc4a..000000000 --- a/app/src/components/dynamic-form/dynamic-form.styl +++ /dev/null @@ -1,19 +0,0 @@ -@import "../../styles/config"; -@import "../../styles/mixins"; - -input, button { - text-align: center; - display: block; - width: 100%; - margin-bottom: 5px; -} - -input.ghost { - color: $text-color-light; - cursor: text; -} - -button.ghost { - border-top: solid 1px; - border-bottom: solid 1px; -} diff --git a/app/src/components/export-view/export-view.html b/app/src/components/export-view/export-view.html deleted file mode 100644 index eacacaad4..000000000 --- a/app/src/components/export-view/export-view.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/app/src/components/export-view/export-view.styl b/app/src/components/export-view/export-view.styl deleted file mode 100644 index 4e0c6a364..000000000 --- a/app/src/components/export-view/export-view.styl +++ /dev/null @@ -1,31 +0,0 @@ -@import "../../styles/config.styl"; - -:host { - display: flex; - flex-direction: column; -} - -textarea { - flex: 1; - margin: 10px; - font-size: 12px; - font-family: monospace; - border: solid 10px transparent; - color: $text-color-faint; -} - -padlock-select { - margin: 10px 10px 0 10px; -} - -padlock-option { - &::after { - content: ""; - display: block; - width: 100%; - position: absolute; - top: 100%; - height: 5px; - background: $color-quaternary; - } -} diff --git a/app/src/components/generator-view/generator-view.html b/app/src/components/generator-view/generator-view.html deleted file mode 100644 index 682b4a7c8..000000000 --- a/app/src/components/generator-view/generator-view.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/app/src/components/generator-view/generator-view.styl b/app/src/components/generator-view/generator-view.styl deleted file mode 100644 index 69b54f39d..000000000 --- a/app/src/components/generator-view/generator-view.styl +++ /dev/null @@ -1,5 +0,0 @@ -input, button, padlock-password-input, padlock-toggle-button { - display: block; - width: 100%; - margin: 5px 0; -} diff --git a/app/src/components/header/header.html b/app/src/components/header/header.html deleted file mode 100644 index 7b547466e..000000000 --- a/app/src/components/header/header.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/src/components/header/header.js b/app/src/components/header/header.js deleted file mode 100644 index b4b06efde..000000000 --- a/app/src/components/header/header.js +++ /dev/null @@ -1,104 +0,0 @@ -/* global Polymer */ - -(function(Polymer) { - "use strict"; - - Polymer({ - is: "padlock-header", - properties: { - view: Object, - filterString: { - type: String, - notify: true - }, - filterActive: { - type: Boolean, - value: false, - reflectToAttribute: true - }, - showing: { - type: Boolean, - value: false, - reflectToAttribute: true - }, - _filterHasFocus: { - type: Boolean, - observer: "_filterHasFocusChanged" - }, - _title: String - }, - observers: [ - "_updateIcons(view, filterActive)", - "_updateTitle(view.headerTitle)", - "_toggleFilter(view.showFilter)" - ], - //* Updates the icon shapes for the left and right header button - _updateIcons: function(view, filterActive) { - if (view && view.showFilter && filterActive) { - // In case the filter input is showing, view preferences are overwritten - this.$.leftIcon.shape = ""; - this.$.rightIcon.shape = "cancel"; - } else { - // The current view provides the icon shapes it wants - this.$.leftIcon.shape = view && view.leftHeaderIcon; - this.$.rightIcon.shape = view && view.rightHeaderIcon; - } - }, - //* The left button was clicked. Delegates to the corresponding view method - _leftClicked: function() { - if (this.view && this.view.leftHeaderButton) { - this.view.leftHeaderButton(); - } - }, - /** - * The right button was clicked. Resets the filter string if the filter input is showing, - * delegates to view method otherwise - */ - _rightClicked: function() { - if (this.view && this.view.showFilter && this.filterActive) { - this.cancelFilter(); - } else if (this.view && this.view.rightHeaderButton) { - this.view.rightHeaderButton(); - } - }, - _titleTapped: function() { - if (this.view && this.view.titleTapped) { - this.view.titleTapped(); - } - }, - //* Clears the filter input and resets the filter string - cancelFilter: function() { - this.filterString = ""; - this.$.filterInput.blur(); - this.filterActive = false; - }, - blurFilterInput: function() { - this.$.filterInput.blur(); - }, - focusFilterInput: function() { - this.$.filterInput.focus(); - }, - _filterHasFocusChanged: function() { - if (this._filterHasFocus) { - this.filterActive = true; - } else if (!this.$.filterInput.value) { - setTimeout(function() { - this.filterActive = false; - }.bind(this), 50); - } - }, - _filterPlaceholder: function(hasFocus) { - return hasFocus ? "type to search..." : "tap to search..."; - }, - _updateTitle: function(title) { - this._title = title || this._title; - }, - _toggleFilter: function(showFilter) { - this.toggleClass("show-filter", showFilter); - }, - _filterEnter: function() { - this.fire("filter-enter"); - } - }); - -})(Polymer); diff --git a/app/src/components/header/header.styl b/app/src/components/header/header.styl deleted file mode 100644 index 3585ad5ce..000000000 --- a/app/src/components/header/header.styl +++ /dev/null @@ -1,129 +0,0 @@ -@import "../../styles/config.styl"; -@import "../../styles/mixins.styl"; -@import "nib"; - -@keyframes titleIn { - from { transform: translate3d(150px, 0, 0); opacity: 0; } - 50% { transform: translate3d(-5px, 0, 0); opacity: 1; } - to { transform: translate3d(0, 0, 0); opacity: 1; } -} - -@keyframes filterIn { - from { transform: translate3d(-150px, 0, 0); opacity: 0; } - 50% { transform: translate3d(5px, 0, 0); opacity: 1; } - to { transform: translate3d(0, 0, 0); opacity: 1; } -} - -@keyframes titleOut { - from { transform: translate3d(0, 0, 0); opacity: 1; } - 50% { transform: translate3d(150px, 0, 0); opacity: 0; } - to { transform: translate3d(150px, 0, 0); opacity: 0; } -} - -@keyframes filterOut { - from { transform: translate3d(0, 0, 0); opacity: 1; } - 50% { transform: translate3d(-150px, 0, 0); opacity: 0; } - to { transform: translate3d(-150px, 0, 0); opacity: 0; } -} - -:host { - line-height: 0; - pointer-events: none; - transition: transform 0.5s $toaster-easing; - font-weight: $font-weight-bold; - text-shadow(); - - &::before { - content: ""; - display: block; - width: 100%; - height: 100%; - background: inherit; - position: absolute; - top: -100%; - left: 0; - background: $color-primary; - } -} - -:host > div { - overflow: hidden; - height: $row-height; - color: $text-color-light; - background: $color-primary; - position: relative; - width: 100%; - pointer-events: auto; - border-bottom: solid 2px $shadow-color; - display: flex; - - shape-shifter { - display: block; - $padding = 10px; - padding: $padding; - width: $row-height - $padding * 2; - height: $row-height - $padding * 2; - cursor: pointer; - position: relative; - z-index: 1; - --shape-shifter-secondary-color: $color-primary; - tap-highlight(); - } -} - -:host(.show-filter) { - .filter-input { - animation: filterIn 0.5s ease 0s both; - } - - .title { - pointer-events: none; - animation: titleOut 0.5s ease 0s both; - } -} - -:host(.show-filter[filter-active]) #leftIcon { - width: 0; - padding: 0; -} - -:host(:not([showing])) { - transform: translate3d(0, -100%, 0); -} - -.middle { - position: relative; - flex: 1; -} - -.middle > * { - display: block; - position: absolute; - top: 0; - left: 0; - width: 100%; -} - -.filter-input { - background: transparent; - color: $text-color-light; - font-weight: $font-weight-bold; - text-shadow(); - - animation: filterOut 0.5s ease 0s both; - - &::-webkit-input-placeholder { - color: $text-color-light; - } - - &:not(:focus)::-webkit-input-placeholder { - text-align: center; - } -} - -.title { - animation: titleIn 0.5s ease 0s both; - overflow: ellipsis; - text-align: center; - line-height: $row-height; -} diff --git a/app/src/components/import-view/import-view.html b/app/src/components/import-view/import-view.html deleted file mode 100644 index 9ed46c9b9..000000000 --- a/app/src/components/import-view/import-view.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/app/src/components/import-view/import-view.js b/app/src/components/import-view/import-view.js deleted file mode 100644 index 545aaaffa..000000000 --- a/app/src/components/import-view/import-view.js +++ /dev/null @@ -1,155 +0,0 @@ -/* global Polymer, padlock */ - -(function(Polymer, ViewBehavior, util, imp, platform) { - "use strict"; - - var inputPlaceholder = - "Paste your data here! It should be in CSV format, like this:\n" + - "\n" + - "Name,Category,Url,Username,Password\n" + - "Gmail,Work,google.com,Martin,j83jaDK\n" + - "Twitter,,twitter.com,mclovin,dj83$j\n" + - "\n" + - "Encrypted Padlock backups are also supported."; - - Polymer({ - is: "padlock-import-view", - behaviors: [ViewBehavior], - properties: { - collection: Object - }, - ready: function() { - this.leftHeaderIcon = "left"; - this.rightHeaderIcon = "copy"; - this.headerTitle = "Import"; - }, - leftHeaderButton: function() { - this.fire("back"); - }, - rightHeaderButton: function() { - platform.getClipboard(function(text) { - this.$.rawInput.value = text; - }.bind(this)); - }, - show: function() { - this.$.rawInput.value = inputPlaceholder; - ViewBehavior.show.apply(this, arguments); - }, - _dataInput: function() { - var rawStr = this.$.rawInput.value; - - if (imp.isSecuStoreBackup(rawStr)) { - this._type = "secustore"; - } else if (imp.isPadlockBackup(rawStr)) { - this._type = "padlock"; - } else if (imp.isLastPassExport(rawStr)) { - this._type = "lastpass"; - } else { - this._type = "csv"; - } - }, - //* Shows password dialog - _requirePassword: function(callback) { - this.fire("open-form", { - title: "Encrypted backup detected. Please enter the password for this backup.", - components: [ - {element: "input", type: "password", placeholder: "Enter Password", - name: "password", autofocus: true}, - {element: "button", label: "Decrypt", submit: true}, - {element: "button", label: "Cancel", cancel: true} - ], - submit: callback - }); - }, - _startImport: function() { - var rawStr = this.$.rawInput.value; - if (!rawStr || rawStr == inputPlaceholder) { - this.fire("notify", {message: "Please enter some data!", type: "error", duration: 1500}); - return; - } - - if (this._type == "secustore") { - this._requirePassword(this._importSecuStoreBackup.bind(this)); - } else if (this._type == "padlock") { - this._requirePassword(this._importPadlockBackup.bind(this)); - } else if (this._type == "lastpass") { - this._importLastPassExport(); - } else { - this._csvData = imp.parseCsv(rawStr); - this._getNameCol(); - } - }, - _importPadlockBackup: function(data) { - this.$$("padlock-progress").show(); - imp.importPadlockBackup(this.collection, this.$.rawInput.value, data.password, function(records) { - this.collection.save(); - this.fire("imported", {count: records.length}); - }.bind(this), this._promptDecryptionFailed.bind(this)); - this.$$("padlock-progress").hide(); - }, - //* Starts the import using the raw input and the provided password - _importSecuStoreBackup: function(data) { - this.$$("padlock-progress").show(); - - imp.importSecuStoreBackup(this.collection, this.$.rawInput.value, data.password, function(records) { - this.collection.save(); - this.fire("imported", {count: records.length}); - }.bind(this), this._promptDecryptionFailed.bind(this)); - this.$$("padlock-progress").hide(); - }, - _importLastPassExport: function() { - var records = imp.importLastPassExport(this.collection, this.$.rawInput.value); - this.collection.save(); - this.fire("imported", {count: records.length}); - }, - _promptDecryptionFailed: function() { - this.fire("open-form", { - title: "Decrypting the data failed. Either the password you entered was incorrect or the " + - "data provided is incomplete or corrupted.", - components: [ - {element: "button", label: "Retry", submit: true, tap: this._startImport.bind(this)}, - {element: "button", label: "Cancel", cancel: true} - ] - }); - }, - //* Opens a dialog for selecting a column for record names - _getNameCol: function() { - this._colNames = this._csvData[0].slice(); - this.fire("open-form", { - title: "Which column would you like to use for record names?", - components: this._colNames.map(function(col) { - return {element: "button", label: col, submit: true, tap: this._selectNameCol.bind(this, col)}; - }.bind(this)) - }); - }, - _selectNameCol: function(colName) { - this._nameColIndex = this._colNames.indexOf(colName); - this._getCatCol(); - }, - //* Opens the dialog for selecting a column for the category - _getCatCol: function() { - // One column is already taken by the record name - var opts = util.remove(this._colNames, this._nameColIndex); - var components = [ - {element: "button", label: "(none)", submit: true, tap: this._selectCatCol.bind(this, null)} - ].concat(opts.map(function(col) { - return {element: "button", label: col, submit: true, tap: this._selectCatCol.bind(this, col)}; - }.bind(this))); - - this.fire("open-form", { - title: "Which column would you like to use for categories?", - components: components - }); - }, - _selectCatCol: function(colName) { - this._catColIndex = this._colNames.indexOf(colName); - this.async(this._importCsv, 100); - }, - _importCsv: function() { - var records = imp.importTable(this.collection, this._csvData, this._nameColIndex, this._catColIndex); - this.collection.save(); - this.fire("imported", {count: records.length}); - } - }); - -})(Polymer, padlock.ViewBehavior, padlock.util, padlock.import, padlock.platform); diff --git a/app/src/components/import-view/import-view.styl b/app/src/components/import-view/import-view.styl deleted file mode 100644 index 82aab43f1..000000000 --- a/app/src/components/import-view/import-view.styl +++ /dev/null @@ -1,39 +0,0 @@ -@import "../../styles/config"; -@import "nib"; - -.flex-wrapper { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; -} - -padlock-select { - margin: 10px 10px 0 10px; -} - -padlock-option { - &::after { - content: ""; - display: block; - width: 100%; - position: absolute; - top: 100%; - height: 5px; - background: $color-quaternary; - } -} - -textarea { - margin: 10px; - flex: 1; - font-size: 12px; - font-family: monospace; - border: solid 10px transparent; - color: $text-color-faint; -} - -.import-button { - display: block; - margin: 0 10px 10px 10px; -} diff --git a/app/src/components/input/input.html b/app/src/components/input/input.html deleted file mode 100644 index 6de31634e..000000000 --- a/app/src/components/input/input.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - diff --git a/app/src/components/list-view/list-view.html b/app/src/components/list-view/list-view.html deleted file mode 100644 index ad645acbc..000000000 --- a/app/src/components/list-view/list-view.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/components/list-view/list-view.js b/app/src/components/list-view/list-view.js deleted file mode 100644 index 6fb2643ca..000000000 --- a/app/src/components/list-view/list-view.js +++ /dev/null @@ -1,125 +0,0 @@ -/* jshint browser: true */ -/* global Polymer, padlock */ - -(function(Polymer, ViewBehavior, MarkableBehavior) { - "use strict"; - - Polymer({ - is: "padlock-list-view", - behaviors: [ViewBehavior, MarkableBehavior], - properties: { - filterString: { - type: String, - value: "" - }, - selected: Object, - records: Array - }, - observers: [ - "_refresh(filterString)" - ], - _firstInSection: {}, - ready: function() { - this.adjustScrollHeight = true; - this.leftHeaderIcon = "menu"; - this.rightHeaderIcon = "plus"; - this.showFilter = true; - this._itemSelector = ".record-item"; - }, - leftHeaderButton: function() { - this.fire("menu"); - }, - rightHeaderButton: function() { - this.add(); - }, - show: function() { - this._marked = -1; - ViewBehavior.show.apply(this, arguments); - }, - add: function() { - this.fire("add"); - }, - _filterBySearchString: function(rec) { - var fs = this.filterString && this.filterString.toLowerCase(), - words = fs && fs.split(" ") || []; - - // For the record to be a match, each word in the filter string has to appear - // in either the category or the record name. - for (var i=0, match=true; i 0) { - this._firstInSection[section] = rec; - } - } - - return include; - }, - _sort: function(a, b) { - var secA = this._section(a.name); - var secB = this._section(b.name); - - if (secA > secB) { - return 1; - } else if (secA < secB) { - return -1; - } else { - if (a.name > b.name) { - return 1; - } else if (a.name < b.name) { - return -1; - } else { - return a.uuid < b.uuid ? -1 : 1; - } - } - }, - _recordTapped: function(e) { - this._marked = e.model.index; - this.fire("select", {record: e.model.item}); - }, - _import: function() { - this.fire("import"); - }, - _synchronize: function() { - this.fire("synchronize"); - }, - selectMarked: function() { - var item = this.$.list.itemForElement(this._items()[this._marked]); - if (item) { - this.fire("select", {record: item}); - } - }, - _section: function(name) { - return (name || "").toUpperCase()[0]; - }, - _showSectionHeader: function(record) { - var section = this._section(record.name); - return this._firstInSection[section] == record; - }, - _isEmpty: function() { - return !this.records.filter(function(rec) { - return !rec.removed; - }).length; - }, - _filtered: function() { - return this.records.filter(this._filter.bind(this)).sort(this._sort.bind(this)); - }, - _refresh: function() { - this._cachedItems = null; - this.$.list.render(); - } - }); - -})(Polymer, padlock.ViewBehavior, padlock.MarkableBehavior); diff --git a/app/src/components/list-view/list-view.styl b/app/src/components/list-view/list-view.styl deleted file mode 100644 index 4b613b37b..000000000 --- a/app/src/components/list-view/list-view.styl +++ /dev/null @@ -1,117 +0,0 @@ -@import "../../styles/config"; -@import "../../styles/mixins"; -@import "nib"; - -:host { - overflow-x: hidden; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - font-size: 0; - padding-bottom: 5px; - box-sizing: border-box; -} - -.record-item { - display: block; - cursor: pointer; - padding: 5px 0 0 0; - vertical-align: top; - box-sizing: border-box; - - .record-item-inner { - height: $row-height; - background: $color-quinary; - width: 100%; - display: flex; - align-items: center; - } - - .record-item-name { - padding: 0 15px; - flex: 1; - overflow: ellipsis; - } - - .record-item-category { - font-size: $font-size-tiny; - background: #ddd; - color: $text-color-light; - border: solid transparent; - border-width: 0 5px; - line-height: 27px; - max-width: 80px; - border-radius: 4px; - margin-right: 15px; - font-weight: $font-weight-bold; - overflow: ellipsis; - background: $color-secondary; - } - - &.marked .record-item-inner { - background: $color-primary; - color: $text-color-light; - } -} - -.record-item, .section-header, .empty { - font-size: $font-size-default; -} - -.section-header { - padding: 5px 15px; - margin-bottom: -5px; - color: $color-primary; - top: 0; - background: $color-quaternary; - position-sticky(); - text-shadow(); -} - -.list { - min-height: 100%; -} - -.empty { - position: absolute; - height: 210px; - left: 0; - right: 0; - top: 0; - bottom: 0; - margin: auto; - text-align: center; - - > button { - width: 100%; - margin-top: 15px; - } -} - -@media (min-width: 700px) { - :host { - padding-left: 5px; - } - - .record-item { - display: inline-block; - padding-right: 5px; - } -} - -@media (min-width: 700px) and (max-width: 1000px) { - .record-item { - width: 50%; - } -} - -@media (min-width: 1000px) and (max-width: 1300px) { - .record-item { - width: 33.33%; - } -} - -@media (min-width: 1300px) { - .record-item { - width: 25%; - } -} diff --git a/app/src/components/lock-view/lock-view.html b/app/src/components/lock-view/lock-view.html deleted file mode 100644 index ab79edf1c..000000000 --- a/app/src/components/lock-view/lock-view.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/app/src/components/lock-view/lock-view.js b/app/src/components/lock-view/lock-view.js deleted file mode 100644 index 3ebc2e4f9..000000000 --- a/app/src/components/lock-view/lock-view.js +++ /dev/null @@ -1,66 +0,0 @@ -/* global Polymer, padlock */ - -(function(Polymer, ViewBehavior, platform) { - "use strict"; - - Polymer({ - is: "padlock-lock-view", - behaviors: [ViewBehavior], - hide: function() { - this.$$("padlock-lock").unlocked = true; - var args = arguments; - this.async(function() { - ViewBehavior.hide.apply(this, args); - }, 500); - }, - show: function() { - this._clear(); - this.$$("padlock-lock").unlocked = false; - ViewBehavior.show.apply(this, arguments); - if (!platform.isTouch()) { - this.async(function() { - this.$.pwdInput.focus(); - }, 500); - } - }, - enter: function() { - this.$.pwdInput.blur(); - this.fire("pwdenter", {password: this.$.pwdInput.value}); - }, - getAnimationElement: function() { - return this.$$("padlock-lock"); - }, - _clear: function() { - this.$.pwdInput.value = ""; - }, - _openOptions: function() { - this.fire("open-form", { - components: [ - {element: "button", label: "Reset App", submit: true} - ], - submit: this._confirmResetApp.bind(this, false) - }); - }, - _confirmResetApp: function(failed) { - var title = failed ? "Failed to Confirm. Make sure to type 'RESET' in the text field below." : - "Are you sure you want to delete all your data and reset the app? " + - "This action can not be undone! Type 'RESET' to Confirm."; - - this.fire("open-form", { - title: title, - components: [ - {element: "input", name: "confirm", placeholder: "Type 'RESET' to Confirm"}, - {element: "button", label: "Reset App", submit: true} - ], - submit: function(data) { - if (data.confirm.toLowerCase() == "reset") { - this.fire("reset-app"); - } else { - this._confirmResetApp(true); - } - }.bind(this) - }); - } - }); - -})(Polymer, padlock.ViewBehavior, padlock.platform); diff --git a/app/src/components/lock-view/lock-view.styl b/app/src/components/lock-view/lock-view.styl deleted file mode 100644 index 828b82d9f..000000000 --- a/app/src/components/lock-view/lock-view.styl +++ /dev/null @@ -1,77 +0,0 @@ -@import "../../styles/config"; -@import "../../styles/mixins"; -@import "nib"; - -@keyframes reveal { - from { transform: translate3d(0, 100%, 0); opacity: 0; } - 50% { transform: translate3d(0, -10%, 0); opacity: 1; } - to { transform: translate3d(0, 0, 0); opacity: 1; } -} - -:host { - display: flex; - flex-direction: column; - background: $color-primary; - color: #fff; - top: 0; - text-shadow(); -} - -.wrapper { - flex: 1; - display: flex; - flex-direction: column; - text-align: center; - justify-content: center; - margin-top: 100px; -} - -.header { - font-size: 50px; - font-weight: 300; - margin-bottom: 30px; - - animation: reveal 0.5s ease 0.2s both; -} - -.password-input { - text-align: center; - width: 100%; - animation: reveal 0.5s ease 0.4s both; -} - -.enter-button { - width: 100%; - margin-top: 20px; - font-size: 120%; - animation: reveal 0.5s ease 0.6s both; -} - -.header, .enter-button { - text-shadow: rgba(0,0,0,0.2) 1px 1px; -} - -button.reset-button { - width: 50px; - height: 50px; - position: absolute; - bottom: 0; - right: 0; - animation reveal 0.5s ease 0.6s both; - font-size: 115%; -} - -padlock-lock { - align-self: center; - animation: reveal 0.5s ease 0.8s both; -} - -@media (max-height: 500px) { - padlock-lock { - height: 0; - } - - .wrapper { - margin-top: 0; - } -} diff --git a/app/src/components/lock/lock.html b/app/src/components/lock/lock.html deleted file mode 100644 index 3be57197c..000000000 --- a/app/src/components/lock/lock.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/components/lock/lock.styl b/app/src/components/lock/lock.styl deleted file mode 100644 index 0882497be..000000000 --- a/app/src/components/lock/lock.styl +++ /dev/null @@ -1,33 +0,0 @@ -@import "nib"; -@import "../../styles/config.styl"; - -:host { - display: block; - position: relative; - width: 240px; - height: 240px; - transform-origin: center bottom; - color: inherit; - fill: currentColor; - stroke: none; - - svg { - width: 100%; - height: 100%; - overflow: visible; - position: absolute; - top: 50px; - } - - .bolt { - transition: transform 0.3s cubic-bezier(0, 1.1, 0.49, 1.1); - } -} - -:host([expanded]) { - animation: expand 0.5s cubic-bezier(1, -0.05, 0.9, 0.05) both !important; -} - -:host([unlocked]) .bolt { - transform: translate3d(0, -100px, 0); -} diff --git a/app/src/components/markable-behavior/markable-behavior.html b/app/src/components/markable-behavior/markable-behavior.html deleted file mode 100644 index 0536e4e85..000000000 --- a/app/src/components/markable-behavior/markable-behavior.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - diff --git a/app/src/components/notification/notification.html b/app/src/components/notification/notification.html deleted file mode 100644 index a08f6a8fd..000000000 --- a/app/src/components/notification/notification.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/components/notification/notification.styl b/app/src/components/notification/notification.styl deleted file mode 100644 index 90ce2e378..000000000 --- a/app/src/components/notification/notification.styl +++ /dev/null @@ -1,43 +0,0 @@ -@import "../../styles/config"; -@import "nib"; - -:host { - height: $row-height; - line-height: $row-height; - text-align: center; - color: $text-color-light; - transition: transform 0.5s $toaster-easing; - position: fixed; - left: 0; - right: 0; - bottom: 0; - z-index: 10; - visibility: hidden; - - &::after { - content: ""; - display: block; - width: 100%; - height: 100%; - background: inherit; - position: absolute; - top: 100%; - left: 0; - } -} - -:host(:not(.showing)) { - transform: translate3d(0, 100%, 0); -} - -div { - overflow: ellipsis; -} - -:host(.success), :host(.info) { - background: $color-primary; -} - -:host(.error), :host(.warning) { - background: $color-secondary; -} diff --git a/app/src/components/password-input/password-input.html b/app/src/components/password-input/password-input.html deleted file mode 100644 index dac692f55..000000000 --- a/app/src/components/password-input/password-input.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - diff --git a/app/src/components/password-input/password-input.styl b/app/src/components/password-input/password-input.styl deleted file mode 100644 index 79c449393..000000000 --- a/app/src/components/password-input/password-input.styl +++ /dev/null @@ -1,51 +0,0 @@ -@import "../../styles/config.styl"; -@import "nib"; - -:host { - display: block; - position: relative; - color: inherit; - - &::after { - content: ""; - display: block; - width: 100%; - height: 4px; - position: absolute; - bottom: 0; - left: 0; - transition: transform 0.5s, background-color 0.5s; - transform-origin: left center; - transform: scale(0, 1); - } - - &[score="0"]::after { - transform: scale(0.2, 1); - background-color: $color-red; - } - - &[score="1"]::after { - transform: scale(0.4, 1); - background-color: $color-red; - } - - &[score="2"]::after { - transform: scale(0.6, 1); - background-color: $color-yellow; - } - - &[score="3"]::after { - transform: scale(0.8, 1); - background-color: $color-yellow; - } - - &[score="4"]::after { - transform: scale(1, 1); - background-color: $color-green; - } -} - -input { - width: 100%; - text-align: center; -} diff --git a/app/src/components/progress/progress.html b/app/src/components/progress/progress.html deleted file mode 100644 index 6284cc1a5..000000000 --- a/app/src/components/progress/progress.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/components/progress/progress.styl b/app/src/components/progress/progress.styl deleted file mode 100644 index d26c2706b..000000000 --- a/app/src/components/progress/progress.styl +++ /dev/null @@ -1,41 +0,0 @@ -@import "nib"; -@import "../../styles/config.styl"; - -@keyframes leftToRight { - from { transform: translate(-100%, 0); } - 80% { transform: translate(100%, 0); } - to { transform: translate(100%, 0); } -} - -@keyframes rightToLeft { - from { transform: translate(50%, 0); } - 80% { transform: translate(-50%, 0); } - to { transform: translate(-50%, 0); } -} - -:host { - display: block; - position: absolute; - bottom: 0; - width: 100%; - text-align: center; - height: 30px; - line-height: 30px; - background: $color-primary; - overflow: hidden; - font-size: 16px; -} - -:host(.animated) { - animation: leftToRight 2.5s cubic-bezier(0.2,1,0.8,0); - - span { - animation: rightToLeft 2.5s cubic-bezier(0.2,1,0.8,0); - } -} - -span { - display: inline-block; - width: 100%; - color: #fff; -} diff --git a/app/src/components/record-view/record-view.html b/app/src/components/record-view/record-view.html deleted file mode 100644 index 72dc7fdac..000000000 --- a/app/src/components/record-view/record-view.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/app/src/components/record-view/record-view.js b/app/src/components/record-view/record-view.js deleted file mode 100644 index 559f9f704..000000000 --- a/app/src/components/record-view/record-view.js +++ /dev/null @@ -1,282 +0,0 @@ -/* global Polymer, padlock */ - -(function(Polymer, ViewBehavior, MarkableBehavior, util, rand, platform) { - "use strict"; - - Polymer({ - is: "padlock-record-view", - behaviors: [ViewBehavior, MarkableBehavior], - properties: { - // Record object containing the data to display - record: Object, - // Reference to global settings object; used to obfuscate fields based on corresponding setting - settings: Object, - // Currently 'selected' field. Used for keeping a reference to a field when doing manipulations on it - _selectedField: { - type: Object, - observer: "_selectedFieldChanged" - } - }, - observers: [ - "_updateTitleText(record.name)", - "_updateObfuscate(settings.obfuscate_fields)" - ], - ready: function() { - this.adjustScrollHeight = true; - this.leftHeaderIcon = "left"; - this.rightHeaderIcon = "trash"; - this._itemSelector = ".field"; - // Add 'touch' class to hide some desktop specific UI features like hover-behavior - this.toggleClass("touch", platform.isTouch()); - }, - show: function() { - // When returning to this view from a different view, make sure to restore the 'marked' state - // based on the currently selected field - this._marked = this.record ? this.record.fields.indexOf(this._selectedField) : -1; - ViewBehavior.show.apply(this, arguments); - }, - add: function() { - this._addField(); - }, - leftHeaderButton: function() { - this.fire("back"); - }, - rightHeaderButton: function() { - this._deleteRecord(); - }, - titleTapped: function() { - this._editName(); - }, - // Opens a prompt for deleting the current record - _deleteRecord: function() { - this.fire("open-form", { - title: "Are you sure you want to delete this record? This action can not be undone!", - components: [ - {element: "button", label: "Delete", submit: true}, - {element: "button", label: "Cancel", cancel: true} - ], - submit: this.fire.bind(this, "delete") - }); - }, - // Opens a dialog for editing the record name - _editName: function() { - this.fire("open-form", { - title: "Edit Name", - components: [ - {element: "input", placeholder: "Enter Name", name: "name", - value: this.record.name, autofocus: true, selectAllOnFocus: true}, - {element: "button", label: "Save", submit: true}, - {element: "button", label: "Cancel", cancel: true} - ], - submit: function(data) { - this.set("record.name", data.name); - this.fire("save"); - this.fire("notify", {message: "Changes Saved!", type: "success", duration: 1000}); - }.bind(this) - }); - }, - // Opens a dialog for adding a field - _addField: function() { - this.$.selector.deselect(); - - this.fire("open-form", { - title: "Add Field", - components: [ - {element: "input", placeholder: "Enter Field Name", name: "name", autofocus: true}, - {element: "button", label: "Save", submit: true} - ], - submit: function(data) { - // new fields should have a non-empty name that doesn't exist in this record yet - if (!data.name) { - this.fire("notify", {message: "Please enter a field name!", type: "error", duration: 2000}); - } else if (this.record.fields.some(function(f) { return f.name == data.name; })) { - this.fire("notify", {message: "A field with this name already exists!", - type: "error", duration: 2000}); - } else { - // Create field and add it to the record - var field = { - name: data.name, - value: "" - }; - this.push("record.fields", field); - this.fire("save"); - - // Start editing the field value - this.async(function() { - this.$.selector.select(field); - this._editField(); - }); - } - }.bind(this) - }); - }, - _fieldTapped: function(e) { - // Depending on the timing, a blur event on any currently selected input field might happen - // after this, which will undo the select unless we do something to prevent it. - this._noDeselectOnBlur = true; - this.$.selector.select(e.model.item); - this.async(function() { - this._noDeselectOnBlur = false; - }, 300); - this._openFieldMenu(); - }, - // Opens a context menu for the currently selected field - _openFieldMenu: function() { - this.fire("open-form", { - components: [ - {element: "button", label: "Copy", submit: true, tap: this.copyToClipboard.bind(this)}, - {element: "button", label: "Edit", submit: true, tap: this._editField.bind(this)}, - {element: "button", label: "Generate", submit: true, tap: this._generateValue.bind(this)}, - {element: "button", label: "Remove", submit: true, tap: this._removeField.bind(this)} - ], - cancel: function() { - this.$.selector.deselect(); - }.bind(this) - }); - }, - // Saves the record and shows a notification whenever a change event fires on an input field - _changeHandler: function() { - this.fire("save"); - this.fire("notify", {message: "Changes Saved!", type: "success", duration: 1000}); - }, - // Finds the value input for a given index - _valueInputForIndex: function(index) { - return Polymer.dom(this.root).querySelectorAll(".field .value:not(.obfuscated)")[index]; - }, - // Finds the value input for a given field - _valueInputForField: function(field) { - var index = this.record.fields.indexOf(field); - return this._valueInputForIndex(index); - }, - // Focus handler for input fields - _focusHandler: function(e) { - this.$.selector.select(e.model.item); - - // Make sure the currently focus input is visible in the viewport. On iOS we have a separate - // solution for this which is implemented in padlock.ViewBehavior - if (!platform.isIOS()) { - this.async(this._revealMarked, 300); - } - }, - // Blur handler for input fields. Deselects the currently selected field - _blurHandler: function() { - if (!this._noDeselectOnBlur) { - this.$.selector.deselect(); - } - }, - // Starts editing a field by moving focus to it - _editField: function() { - var input = this._valueInputForField(this._selectedField); - this.async(function() { - input && input.focus(); - }, 300); - }, - // Opens a prompt for removing the currently selected field - _removeField: function() { - this.fire("open-form", { - title: "Are you sure you want to remove this field? This action can not be undone!", - components: [ - {element: "button", label: "Remove", submit: true}, - {element: "button", label: "Cancel", cancel: true} - ], - submit: function() { - // Remove the field from the record and save - var ind = this.record.fields.indexOf(this._selectedField); - this.splice("record.fields", ind, 1); - this.$.selector.deselect(); - this.fire("save"); - this.fire("notify", {message: "Changes Saved!", type: "success", duration: 1000}); - }.bind(this), - cancel: function() { - this.$.selector.deselect(); - }.bind(this) - }); - }, - // Opens the categories menu for this record - _openCategories: function() { - this.fire("categories"); - }, - //* Copies the value of the currently selected or marked field to the clipboard - copyToClipboard: function() { - // If a field has been selected copy that one, otherwise copy the marked one - var field = this._selectedField ? this._selectedField : this.record.fields[this._marked]; - - if (field) { - platform.setClipboard(field.value); - this.$.selector.deselect(); - this.fire("notify", {message: "Copied to clipboard!", type: "success", duration: 1500}); - } - }, - // Fills the current value input with a randomized value - _generateValue: function() { - var field = this._selectedField; - this.async(function() { - this.fire("generate-value", {field: field}); - }, 300); - }, - // Callback function for generating values for a field. Updates the given field and moves focus to it - generateConfirm: function(field, value) { - if (value) { - this.set("_selectedField.value", value); - this.fire("save"); - var valueInput = this._valueInputForField(field); - this.async(function() { - valueInput && valueInput.focus(); - valueInput && valueInput.updateSize(); - this.fire("notify", {message: "Changes Saved!", type: "success", duration: 1000}); - }, 300); - } else { - // If no value is provided the generation process is considered canceled - this.$.selector.deselect(); - } - }, - _selectedFieldChanged: function() { - // If a field is selected, lets also consider it 'marked' - this._marked = this._selectedField ? this.record.fields.indexOf(this._selectedField) : -1; - }, - // Updates the header title whenever the record name changes - _updateTitleText: function(name) { - this.headerTitle = name; - }, - // Filter function for category class - _categoryClass: function(category) { - return category ? "category selected" : "category"; - }, - // Filter function for category label - _categoryLabel: function(category) { - return category || "Add a Category"; - }, - // Replaces all non-newline characters in a given string with dots - _obfuscate: function(value) { - return value.replace(/[^\n]/g, "\u2022"); - }, - // Marks a field when hovered (if not on a touch device) - _fieldMouseover: function(e) { - if (!platform.isTouch() && !this._selectedField) { - this._marked = e.model.index; - } - }, - // Unmarks a field when hover ends (if not on a touch device) - _fieldMouseout: function() { - if (!platform.isTouch() && !this._selectedField) { - this._marked = -1; - } - }, - // Trigger obfuscation based on settings - _updateObfuscate: function(obfuscate) { - this.toggleClass("obfuscate", obfuscate); - }, - // Selects the currently marked field and opens the field menu - selectMarked: function() { - var field = this.record.fields[this._marked]; - if (field) { - this.$.selector.select(field); - this._openFieldMenu(); - } - }, - _isEqual: function(a, b) { - return a === b; - } - }); - -})(Polymer, padlock.ViewBehavior, padlock.MarkableBehavior, padlock.util, padlock.rand, padlock.platform); diff --git a/app/src/components/record-view/record-view.styl b/app/src/components/record-view/record-view.styl deleted file mode 100644 index 1bfd60b66..000000000 --- a/app/src/components/record-view/record-view.styl +++ /dev/null @@ -1,150 +0,0 @@ -@import "../../styles/config"; -@import "../../styles/mixins"; -@import "nib"; - -:host { - display: block; - overflow-x: hidden; - overflow-y: auto; - -webkit-overflow-scrolling: touch; -} - -.field { - background: $color-quinary; - width: 100%; - margin-top: 5px; - font-size: 16px; - box-sizing: border-box; - position: relative; - - &:not([selected]) { - tap-highlight($color-primary); - } - - .label-wrapper { - position: relative; - z-index: 1; - color: $color-primary; - display: flex; - } - - .label { - flex: 1; - overflow: ellipsis; - padding: 0 15px; - line-height: 41px; - } - - .context-menu { - width: 25px; - height: 25px; - padding: 8px; - tap-highlight(); - } - - &:not([selected]) .context-menu { - display: none; - } - - .separator { - height: 1px; - background: $color-quaternary; - padding: 0 10px; - background-clip: content-box; - } - - .shortcut-hint { - position: absolute; - font-size: $font-size-tiny; - top: 0; - right: 0; - padding: 0 15px; - line-height: 41px; - color: $text-color-faint; - background: alpha($color-quinary, 0.9); - pointer-events: none; - } - - &:not(.marked) .shortcut-hint, &[selected] .shortcut-hint { - display: none; - } - - .value { - word-wrap: break-word; - width: 100%; - padding: 10px 15px; - box-sizing: border-box; - - &:not(:focus) { - pointer-events: none; - } - - &:empty:after { - content: "(tap to edit)"; - color: $text-color-faint; - } - } - - &::before { - content: ""; - display: block; - position: absolute; - top: 0; - left: 0; - z-index: 2; - bottom: 0; - width: 5px; - background: $color-primary; - transform: translate(-100%, 0); - transition: transform 0.3s; - } - - &.marked::before { - transform: translate(0, 0); - } - - .value-wrapper { - position: relative; - } - - .obfuscated { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: $color-quinary; - letter-spacing: 0.07em; - pointer-events: none; - display: none; - } -} - -:host(.obfuscate) .field:not(.marked) .obfuscated:not(:empty) { - display: block; -} - -.add-button { - width: 100%; - margin: 5px 0; - font-size: $font-size-small; -} - -.category { - background: #fff; - color: $text-color-faint; - text-align: center; - font-size: $font-size-small; - padding: 10px; - cursor: pointer; - overflow: ellipsis; - - &.selected { - color: $text-color-light; - background: $color-secondary; - } -} - -:host(.touch) .shortcut-hint { - display: none; -} diff --git a/app/src/components/select/option.html b/app/src/components/select/option.html deleted file mode 100644 index ab192f6fb..000000000 --- a/app/src/components/select/option.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/components/select/option.styl b/app/src/components/select/option.styl deleted file mode 100644 index de8c3c1b1..000000000 --- a/app/src/components/select/option.styl +++ /dev/null @@ -1,6 +0,0 @@ -@import "../../styles/config.styl"; - -button { - width: 100%; - color: $text-color; -} diff --git a/app/src/components/select/select.html b/app/src/components/select/select.html deleted file mode 100644 index 780338a1b..000000000 --- a/app/src/components/select/select.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/src/components/select/select.js b/app/src/components/select/select.js deleted file mode 100644 index 80f509c5b..000000000 --- a/app/src/components/select/select.js +++ /dev/null @@ -1,116 +0,0 @@ -/* global Polymer */ - -(function(Polymer) { - "use strict"; - - Polymer({ - is: "padlock-select", - properties: { - //* Determines wheter the select options are shown or not - open: { - type: Boolean, - value: false, - observer: "_openChanged" - }, - //* Text shown if no option is selected - label: { - type: String, - value: "tap to select" - }, - //* The selected element - selected: { - type: Number, - notify: true, - observer: "_selectedChanged" - }, - //* Value of the selected option - value: { - type: String, - notify: true, - observer: "_valueChanged" - }, - //* If _true_, options will expand upwards instead of downwards - openUpwards: Boolean - }, - get options() { - return Polymer.dom(this).querySelectorAll("padlock-option"); - }, - get selectedOption() { - return this.options[this.selected]; - }, - attached: function() { - this.async(this._selectDefault.bind(this)); - }, - _selectDefault: function() { - var opts = this.options; - // Initially select the first item with the _selected_ attribute - for (var i=0; i < opts.length; i++) { - if (opts[i].default) { - this.selected = i; - return; - } - } - this.selected = -1; - }, - _openChanged: function() { - var options = this.options, - // We're assuming all rows have the same height - rowHeight = options[0] && options[0].offsetHeight || 50, - gutterWidth = 5; - - // Show all options except the selected one by making them opaque - // and lining them up via a css transform - for (var i=0, j=0, o; i * { - position: absolute; - left: 0; - right: 0; - top: 0; - width: 100%; - transition: transform 0.3s, opacity 0.3s; -} diff --git a/app/src/components/settings-view/settings-view.html b/app/src/components/settings-view/settings-view.html deleted file mode 100644 index 786ecbfd4..000000000 --- a/app/src/components/settings-view/settings-view.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/app/src/components/settings-view/settings-view.js b/app/src/components/settings-view/settings-view.js deleted file mode 100644 index d5dc4ee57..000000000 --- a/app/src/components/settings-view/settings-view.js +++ /dev/null @@ -1,89 +0,0 @@ -/* jshint browser: true */ -/* global Polymer, padlock */ - -(function(Polymer, platform, ViewBehavior) { - "use strict"; - - Polymer({ - is: "padlock-settings-view", - behaviors: [ViewBehavior], - properties: { - collection: Object, - settings: Object - }, - ready: function() { - this.leftHeaderIcon = "left"; - this.rightHeaderIcon = ""; - this.headerTitle = "Settings"; - }, - leftHeaderButton: function() { - this.fire("back"); - }, - //* Opens the change password dialog and resets the corresponding input elements - _changePassword: function() { - this.fire("open-form", { - components: [ - {element: "input", type: "password", placeholder: "Enter Current Password", - name: "password", autofocus: true}, - {element: "button", label: "Enter", submit: true}, - {element: "button", label: "Cancel", cancel: true} - ], - title: "Change Master Password", - submit: function(data) { - // TODO: Add a better check for the current password - if (data.password != this.collection.defaultPassword) { - this.fire("notify", {message: "Wrong password!", type: "error", duration: 2000}); - } else { - this.fire("change-password"); - } - }.bind(this) - }); - }, - _import: function() { - this.fire("import"); - }, - _export: function() { - this.fire("export"); - }, - _openWebsite: function() { - window.open("https://padlock.io", "_system"); - }, - _sendMail: function() { - var url = "mailto:support@padlock.io"; - window.open(url, "_system"); - }, - _openGithub: function() { - window.open("https://github.com/maklesoft", "_system"); - }, - _openHomepage: function() { - // window.open("http://maklesoft.com/", "_system"); - this._openGithub(); - }, - _resetData: function() { - this.fire("open-form", { - components: [ - {element: "input", type: "password", placeholder: "Enter Master Password", - name: "password", autofocus: true}, - {element: "button", label: "Reset", submit: true}, - {element: "button", label: "Cancel", cancel: true} - ], - title: "Are you sure you want to reset Padlock and delete all your data from this device? " + - "This action can not be undone! Please enter your master password to confirm.", - submit: function(data) { - if (data.password == this.collection.defaultPassword) { - this.fire("reset"); - } else { - this.fire("notify", {message: "Wrong password!", type: "error", duration: 2000}); - } - }.bind(this) - }); - }, - _openCloudView: function() { - this.fire("open-cloud-view"); - }, - _version: function() { - return padlock.version; - } - }); - -})(Polymer, padlock.platform, padlock.ViewBehavior); diff --git a/app/src/components/settings-view/settings-view.styl b/app/src/components/settings-view/settings-view.styl deleted file mode 100644 index ffc463cdd..000000000 --- a/app/src/components/settings-view/settings-view.styl +++ /dev/null @@ -1,37 +0,0 @@ -@import "../../styles/config"; -@import "nib"; - -:host { - overflow-x: hidden; - overflow-y: auto; - -webkit-overflow-scrolling: touch; -} - -button, .button, padlock-toggle-button, padlock-slider { - margin-bottom: 5px; -} - -button, .button { - display: block; - width: 100%; - box-sizing: border-box; - - shape-shifter { - position: absolute; - right: 5px; - top: 0; - bottom: 0; - margin: auto; - width: 25px; - height: 25px; - } -} - -.info { - padding: 15px; - width: 200px; - margin: 0 auto; - font-size: 15px; - text-align: center; - cursor: pointer; -} diff --git a/app/src/components/slider/slider.html b/app/src/components/slider/slider.html deleted file mode 100644 index edbafa5a0..000000000 --- a/app/src/components/slider/slider.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - diff --git a/app/src/components/slider/slider.styl b/app/src/components/slider/slider.styl deleted file mode 100644 index 9c70985d5..000000000 --- a/app/src/components/slider/slider.styl +++ /dev/null @@ -1,24 +0,0 @@ -@import "../../styles/config"; -@import "../../styles/mixins"; - -:host { - tap-highlight(); - background: #fff; - display: flex; - align-items: center; - margin-bottom: 5px; - color: $color-primary; - - input[type="range"] { - margin: 0; - flex: 1; - } - - .label { - margin: 0 5px 0 20px; - } - - .value-display { - margin: 0 20px 0 5px; - } -} diff --git a/app/src/components/start-view/start-view.html b/app/src/components/start-view/start-view.html deleted file mode 100644 index d2fd6833f..000000000 --- a/app/src/components/start-view/start-view.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/app/src/components/start-view/start-view.js b/app/src/components/start-view/start-view.js deleted file mode 100644 index f3fc1e543..000000000 --- a/app/src/components/start-view/start-view.js +++ /dev/null @@ -1,227 +0,0 @@ -/* global Polymer, padlock */ - -(function(Polymer, ViewBehavior, platform, CloudSource) { - "use strict"; - - Polymer({ - is: "padlock-start-view", - behaviors: [ViewBehavior], - properties: { - mode: { - type: String, - value: "create", - reflectToAttribute: true - }, - collection: Object - }, - hide: function() { - this.$$("padlock-lock").unlocked = true; - this.toggleClass("reveal", false, this.$$(".switch-button")); - var args = arguments; - this.async(function() { - ViewBehavior.hide.apply(this, args); - }, 500); - }, - show: function() { - this._clear(); - this.$$("padlock-lock").unlocked = false; - this.async(function() { - this.toggleClass("reveal", true, this.$$(".switch-button")); - }, 1000); - ViewBehavior.show.apply(this, arguments); - if (!platform.isTouch()) { - this.async(function() { - this.$.pwdInput.focus(); - }, 500); - } - }, - _enter: function() { - this.$.pwdInput.blur(); - - var newPwd = this.$.pwdInput.value, - score = this.$.pwdInput.score; - - if (!newPwd) { - this.fire("notify", {message: "Please enter a master password!", type: "error", duration: 2000}); - } else if (score < 2) { - this._promptWeakPassword(); - } else { - this._confirmPassword(); - } - }, - getAnimationElement: function() { - return this.$$("padlock-lock"); - }, - _promptWeakPassword: function() { - this.fire("open-form", { - components: [ - {element: "button", label: "Retry", cancel: true}, - {element: "button", label: "Use Anyway", submit: true} - ], - title: "WARNING: The password you entered is weak which makes it easier for attackers to break " + - "the encryption used to protect your data. Try to use a longer password or include a " + - "variation of uppercase, lowercase and special characters as well as numbers.", - submit: this._confirmPassword.bind(this) - }); - }, - _confirmPassword: function() { - var newPwd = this.$.pwdInput.value; - - this.fire("open-form", { - title: "Remember your master password! Without it, nobody will be able to access your data, " + - "not even we! This is to ensure that your data is as safe as possible but it also means " + - "that if you lose your master password, we won't be able to assist you with recovering your " + - "data.", - components: [ - {element: "input", placeholder: "Repeat Password", type: "password", name: "password"}, - {element: "button", label: "Confirm", submit: true} - ], - submit: function(data) { - if (newPwd == data.password) { - this.fire("newpwd", {password: newPwd}); - } else { - this.fire("notify", {message: "Passwords do not match!", type: "error", duration: 2000}); - } - }.bind(this) - }); - }, - _clear: function() { - this.$.pwdInput.value = ""; - this.$.emailInput.value = ""; - this.$.cloudPwdInput.value = ""; - }, - _buttonLabel: function(mode) { - return this._isChangeMode(mode) ? "Change Password" : "Get Started"; - }, - _switchButtonLabel: function(mode) { - return mode == "restore-cloud" ? "Get Started Offline" : "Restore From Cloud"; - }, - _isChangeMode: function(mode) { - return mode == "change-password"; - }, - _switchMode: function() { - this.mode = this.mode == "restore-cloud" ? "get-started" : "restore-cloud"; - }, - _cloudEnter: function() { - var input = this.$.emailInput; - var email = input.value; - - if (!email || !input.checkValidity()) { - this.fire("alert", { - message: email ? input.validationMessage : "Please enter an email address!" - }); - return; - } - - var cloudSource = new CloudSource(this.settings); - - this.$$("padlock-progress").show(); - this.$.cloudEnterButton.disabled = true; - cloudSource.requestAuthToken(email, false, function(authToken) { - this.$$("padlock-progress").hide(); - this.$.cloudEnterButton.disabled = false; - this.set("settings.sync_email", email); - this.set("settings.sync_key", authToken.token); - this.set("settings.sync_id", authToken.id); - this._promptConnecting(); - this._attemptRestore(); - }.bind(this), function(e) { - this.$.cloudEnterButton.disabled = false; - this.$$("padlock-progress").hide(); - switch (typeof e === "string" ? e : e.error) { - case padlock.ERR_CLOUD_NOT_FOUND: - case padlock.ERR_CLOUD_SUBSCRIPTION_REQUIRED: - this.fire("open-form", { - components: [ - {element: "button", label: "Try Different Email", submit: true, tap: function() { - this.$.emailInput.value = ""; - this.$.emailInput.focus(); - }.bind(this)}, - {element: "button", label: "Get Started Offline", submit: true, tap: function() { - this.mode = "get-started"; - }.bind(this)} - ], - title: "There is no existing Padlock Cloud account with this email address! " + - "Create an offline account first, then connect to Padlock Cloud later!" - }); - break; - default: - this.fire("error", e); - } - }.bind(this)); - }, - _attemptRestore: function() { - var cloudSource = new CloudSource(this.settings); - - this._cancelRestore = false; - - this.collection.fetch({ - source: cloudSource, - password: this.$.cloudPwdInput.value, - success: this._restoreSuccess.bind(this), - fail: function(e) { - if (this._cancelRestore) { - this._cancelRestore = false; - this.set("settings.sync_email", ""); - this.set("settings.sync_key", ""); - this.set("settings.sync_id", ""); - return; - } - if (e.error == padlock.ERR_CLOUD_INVALID_AUTH_TOKEN) { - this._attemptRestoreTimeout = setTimeout(this._attemptRestore.bind(this), 3000); - } else { - this.fire("error", e); - } - }.bind(this) - }); - }, - _stopAttemptRestore: function() { - this._cancelRestore = true; - this.set("settings.sync_email", ""); - this.set("settings.sync_key", ""); - this.set("settings.sync_id", ""); - clearTimeout(this._attemptRestoreTimeout); - }, - _restoreSuccess: function() { - if (this._cancelRestore) { - this._cancelRestore = false; - return; - } - this.set("settings.sync_connected", true); - this.notifyPath("settings.sync_sub_status", this.settings.sync_sub_status); - this.notifyPath("settings.sync_trial_end", this.settings.sync_trial_end); - this.collection.save({password: this.$.cloudPwdInput.value, rememberPassword: true}); - - this.fire("open-form", { - title: "Your data has been successfully restored from Padlock Cloud!", - components: [ - {element: "button", label: "View My Records", submit: true} - ], - submit: this.fire.bind(this, "restore"), - cancel: this.fire.bind(this, "restore"), - allowDismiss: false - }); - }, - _promptConnecting: function() { - this.fire("open-form", { - title: "Almost done! An email was sent to " + this.settings.sync_email + - " with further instructions. Hit 'Cancel' to abort the process. (Connection ID: " + - this.settings.sync_id + ")", - components: [ - {element: "button", label: "Cancel", tap: this._cancelConnect.bind(this), close: true} - ], - allowDismiss: false - }); - }, - _cancelConnect: function() { - this.fire("open-form", { - title: "Are you sure you want to cancel the connection process?", - components: [ - {element: "button", label: "Yes", tap: this._stopAttemptRestore.bind(this), close: true}, - {element: "button", label: "No", tap: this._promptConnecting.bind(this), close: true} - ] - }); - } - }); - -})(Polymer, padlock.ViewBehavior, padlock.platform, padlock.CloudSource); diff --git a/app/src/components/start-view/start-view.styl b/app/src/components/start-view/start-view.styl deleted file mode 100644 index 1688c0fc7..000000000 --- a/app/src/components/start-view/start-view.styl +++ /dev/null @@ -1,154 +0,0 @@ -@import "../../styles/config"; -@import "../../styles/mixins"; -@import "nib"; - -@keyframes reveal { - from { transform: translate3d(0, 100%, 0); opacity: 0; } - 50% { transform: translate3d(0, -10%, 0); opacity: 1; } - to { transform: translate3d(0, 0, 0); opacity: 1; } -} - -:host { - display: flex; - flex-direction: column; - background-color: $color-primary; - overflow: hidden; - top: 0; - color: $text-color-light; - transition: background-color 1s, color 1s; - text-shadow(); -} - -:host[mode="restore-cloud"] { - background-color: $color-quaternary; - color: $color-primary; - - .enter-button, input { - color: inherit; - } - - .switch-button { - color: $text-color-light; - } -} - -.form-wrapper { - flex: 1; - position: relative; - margin-top: 70px; -} - -.get-started, .restore-cloud { - position: absolute; - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; - transition: transform 0.7s, opacity 0.7s; - - input, padlock-password-input { - text-align: center; - width: 100%; - } -} - -.get-started { - color: $text-color-light; - - input, padlock-password-input { - background: rgba(255, 255, 255, 0.1); - } -} - -.restore-cloud { - color: $color-primary; - - .header { - font-size: 35px; - font-weight: 400; - } -} - -:host[mode="restore-cloud"] .get-started, :host:not([mode="restore-cloud"]) .restore-cloud { - transform: translate(0, 50px); - opacity: 0; - pointer-events: none; - transition-duration: 0.2s; -} - -.tap-highlight { - margin-bottom: 10px; -} - -.header { - font-size: 50px; - font-weight: 300; - margin-bottom: 30px; - animation: reveal 0.5s ease 0.2s both; -} - -.pwdinput { - animation: reveal 0.5s ease 0.4s both; -} - -.enter-button { - width: 100%; - margin-top: 10px; - animation reveal 0.5s ease 0.6s both; - font-size: 115%; -} - -.header, .enter-button { - text-shadow: darken($color-primary, 30%) 1px 1px; -} - -.switch-button { - width: 100%; - position: absolute; - left: 0; - bottom: 0; - z-index: 1; - background: transparent; - transition: opacity 0.5s ease; - - &:not(.reveal) { - opacity: 0; - } -} - -padlock-lock { - animation: reveal 0.5s ease 0.8s both; -} - -.lock-wrapper { - align-self: center; -} - -padlock-progress { - background: $color-secondary; - z-index: 1; -} - -@media (max-height: 500px) { - padlock-lock { - height: 0; - } - - :host .switch-button { - color: $text-color-light; - } - - :host[mode="restore-cloud"] .switch-button { - color: $color-primary; - } - - .switch-button { - color: $color-primary; - } - - .form-wrapper { - margin-top: 0; - } -} diff --git a/app/src/components/toggle/toggle-button.styl b/app/src/components/toggle/toggle-button.styl deleted file mode 100644 index a7aad2771..000000000 --- a/app/src/components/toggle/toggle-button.styl +++ /dev/null @@ -1,18 +0,0 @@ -:host { - text-align: left; - display: block; -} - -button { - position: relative; - width: 100%; - text-align: inherit; -} - -padlock-toggle { - position: absolute; - right: 10px; - top: 0; - bottom: 0; - margin: auto 0; -} diff --git a/app/src/components/toggle/toggle.html b/app/src/components/toggle/toggle.html deleted file mode 100644 index 3ce2fa9c5..000000000 --- a/app/src/components/toggle/toggle.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/app/src/components/toggle/toggle.styl b/app/src/components/toggle/toggle.styl deleted file mode 100644 index 7008a3811..000000000 --- a/app/src/components/toggle/toggle.styl +++ /dev/null @@ -1,40 +0,0 @@ -@import "../../styles/config"; -@import "nib"; - -$width = 50px; -$height = 30px; -$gutter-width = 2px; -$bg-color-off = $color-quaternary; -$bg-color-on = $color-primary; -$anim-duration = 0.5s; - -:host { - display: inline-block; - width: $width; - height: $height; - background: $bg-color-off; - border-radius: $height; - - transition: background $anim-duration ease; -} - -.knob { - $size = $height - 2 * $gutter-width; - content: ""; - display: block; - height: $size; - width: $size; - margin: $gutter-width; - background: #fff; - border-radius: $size; - - transition: transform $anim-duration cubic-bezier(1, -0.5, 0, 1.5) -0.2s; -} - -:host(.on) { - background: $bg-color-on; - - .knob { - transform: translate($width - $height, 0); - } -} diff --git a/app/src/components/view/view.html b/app/src/components/view/view.html deleted file mode 100644 index febad7082..000000000 --- a/app/src/components/view/view.html +++ /dev/null @@ -1,212 +0,0 @@ - - - - diff --git a/app/src/components/view/view.styl b/app/src/components/view/view.styl deleted file mode 100644 index fcdc805c8..000000000 --- a/app/src/components/view/view.styl +++ /dev/null @@ -1,76 +0,0 @@ -@import "../../styles/config"; -@import "nib"; - -@keyframes slideInFromRight { - from {transform: translate3d(100%, 0, 0);} - 50% {transform: translate3d(-2%, 0, 0);} - to {transform: translate3d(0, 0, 0);} -} - -@keyframes slideInFromLeft { - from {transform: translate3d(-100%, 0, 0);} - 50% {transform: translate3d(2%, 0, 0);} - to {transform: translate3d(0, 0, 0);} -} - -@keyframes slideOutToRight { - from {transform: translate3d(0, 0, 0);} - 50% {transform: translate3d(100%, 0, 0);} - to {transform: translate3d(100%, 0, 0);} -} - -@keyframes slideOutToLeft { - from {transform: translate3d(0, 0, 0);} - 50% {transform: translate3d(-100%, 0, 0);} - to {transform: translate3d(-100%, 0, 0);} -} - -@keyframes slideInFromBottom { - from {transform: translate3d(0, 100%, 0);} - 50% {transform: translate3d(0, -2%, 0);} - to {transform: translate3d(0, 0, 0);} -} - -@keyframes slideOutToBottom { - from {transform: translate3d(0, 0, 0);} - 80% {transform: translate3d(0, 100%, 0);} - to {transform: translate3d(0, 100%, 0);} -} - -@keyframes fadeOut { - from {opacity: 1;} - 50% {opacity: 0;} - to {opacity: 0;} -} - -@keyframes popin { - from {opacity: 0;} - 20% {opacity: 1; transform: scale(1.05);} - to {opacity: 1;} -} - -@keyframes popout { - from {opacity: 1;} - 80% {opacity: 1; transform: scale(1.05);} - to {opacity: 0; transform: scale(0.1);} -} - -@keyframes expand { - to { transform: scale(10); fill: $color-quaternary; } -} - -@keyframes contract { - from { transform: scale(10); fill: $color-quaternary; } -} - -:host { - position: absolute; - left: 0; - right: 0; - top: $row-height; - bottom: 0; -} - -:host(:not(.showing)) { - display: none; -} diff --git a/package.json b/package.json index 7f653915a..44032e23b 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "compile": "gulp compile --silent", "debug": "gulp --silent --watch", "lint": "eslint app/**/*.{js,html}", - "test": "npm run compile && mocha --ui tdd", + "test": "npm run lint && npm run compile && mocha --ui tdd", "start": "gulp build --electron && electron dist/electron", "postinstall": "npm run bower-install && npm run compile" }