From 376a574f1b12009c06fe2e576413747176cd54a7 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Fri, 15 Apr 2016 18:01:07 +0200 Subject: [PATCH 1/8] add gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fde344f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Webstorm workspace files, etc. +.idea \ No newline at end of file From 5578eb390d4965bce7956e2c460ece066d2a4bf9 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Fri, 15 Apr 2016 18:10:17 +0200 Subject: [PATCH 2/8] add `Bundle.ModalButton` and stub `Bundle.FileButton` --- source/js/classes/setting.js | 122 ++++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 2 deletions(-) diff --git a/source/js/classes/setting.js b/source/js/classes/setting.js index a1cfea0..7eb4c69 100755 --- a/source/js/classes/setting.js +++ b/source/js/classes/setting.js @@ -170,7 +170,123 @@ }).bind(this)); } }); - + + /* File Button -- Work In Progress + * + * Bundle.FileButton = new Class({ + * // label, text + * // action -> click + * "Extends": Bundle.Button, + * "initialize": function (params) { + * this.params = params; + * this.params.searchString = "•" + this.params.tab + "•" + this.params.group + "•"; + * + * this.createDOM(); + * this.setupDOM(); + * this.addEvents(); + * + * if (this.params.id !== undefined) { + * this.element.set("id", this.params.id); + * } + * + * this.params.searchString = this.params.searchString.toLowerCase(); + * }, + * + * "createDOM": function () { + * //-- do same DOM creation as `Button` + * this.parent(); + * + * //-- add file specific DOM creation + * this.element = new Element("input", { + * "class": "setting element button", + * "type": "file" + * }); + * + * this.label = new Element("label", { + * "class": "setting label button" + * }); + * }, + * + * "setupDOM": function () { + * //-- do same DOM setup as `Button` + * this.parent(); + * }, + * + * "addEvents": function () { + * this.modalElement.addEvent("click", (function () { + * this.fireEvent("action"); + * }).bind(this)); + * } + * }); + */ + + Bundle.ModalButton = new Class({ + // label, text + // action -> click + "Extends": Bundle.Button, + + "createDOM": function () { + //-- do same DOM creation as `Button` + this.parent(); + + //-- add modal specific DOM creation + this.modalBackdrop = new Element("div", { + "class": "modal backdrop hide" + }); + + this.modalContainer = new Element("div", { + "class": "modal container" + }); + + this.modalTitle = new Element("h2", { + "class": "modal title" + }); + + this.modalDone = new Element("button", { + "class": "modal done" + }) + }, + + "setupDOM": function () { + //-- do same DOM setup as `Button` + this.parent(); + + //-- add modal specific DOM setup + var that = this; + + if (this.params.modal.title !== undefined) { + this.modalTitle.set("html", this.params.modal.title); + this.modalTitle.inject(this.modalContainer); + this.params.searchString += this.params.label + "•"; + } + + this.modalContainer.inject(this.modalBackdrop); + this.modalBackdrop.inject(this.bundle); + + this.params.modal.contents.forEach(function (item) { + (new Setting(that.modalContainer)).create(item); + }); + + this.modalDone.set('html', 'Done'); + this.modalDone.inject(this.modalContainer); + }, + + "addEvents": function () { + //-- do same addEvents as `Button` + this.parent(); + + //-- add model specific events + this.element.addEvent('click', (function () { + this.modalBackdrop.removeClass('hide'); + }).bind(this)); + + this.modalDone.addEvent('click', (function(){ + this.modalBackdrop.addClass('hide'); + this.fireEvent('modal_done'); + }).bind(this)); + } + }); + Bundle.Text = new Class({ // label, text, masked // action -> change & keyup @@ -695,7 +811,9 @@ "slider": "Slider", "popupButton": "PopupButton", "listBox": "ListBox", - "radioButtons": "RadioButtons" + "radioButtons": "RadioButtons", + "modalButton": "ModalButton", + //"fileButton": "FileButton" }; if (types.hasOwnProperty(params.type)) { From 5de6ac410baec12ca40e095565572f3c8e7b4271 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Sat, 16 Apr 2016 03:10:03 +0200 Subject: [PATCH 3/8] add sample settings and manifest for `ModalButton` and `chrome.storage` --- source/manifest.js | 38 ++++++++++++++++++++++++++++++++ source/settings.js | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/source/manifest.js b/source/manifest.js index 7c75668..5adaa82 100755 --- a/source/manifest.js +++ b/source/manifest.js @@ -3,6 +3,44 @@ this.manifest = { "name": "My Extension", "icon": "icon.png", "settings": [ + { + tab: 'Manage Keys', + name: 'storedKeys', + type: 'listBox', + multiple: true, + label: 'Add all keys required for encryption/decryption of all passwords in your password store.', + group: 'Keys', + options: [] + }, + { + tab: 'Manage Keys', + name: 'addKey', + type: 'modalButton', + group: 'Keys', + text: 'Add Key...', + modal: { + title: 'Add Key', + contents: [ + { + name : 'keyName', + type : 'text', + label: 'Key name (email address):' + }, + { + name : 'keyText', + type : 'textarea', + label: 'Paste your gpg private key here:' + } + ] + } + }, + { + tab: 'Manage Keys', + name: 'removeKey', + type: 'button', + group: 'Keys', + text: 'Remove Selected Key(s)' + }, { "tab": i18n.get("information"), "group": i18n.get("login"), diff --git a/source/settings.js b/source/settings.js index 379bd77..196e17a 100755 --- a/source/settings.js +++ b/source/settings.js @@ -1,9 +1,63 @@ window.addEvent("domready", function () { // Option 1: Use the manifest: new FancySettings.initWithManifest(function (settings) { + //-- register 'action' event callback on the `myButton` setting from ./manifest:78 settings.manifest.myButton.addEvent("action", function () { alert("You clicked me!"); }); + + //-- register 'action' event callback on the `myButton` setting from ./manifest:78 + settings.manifest.removeKey.addEvent('action', function(){ + var removeIds = []; + + [].slice.call(settings.manifest.storedKeys.element.options).forEach(function(option){ + if (option.selected) removeIds.push(option.value) + }); + + options.keys = options.keys.filter(function(key){ + return removeIds.every(function(id){ + return id !== key.id; + }) + }); + + chrome.storage.sync.set({options: options}, function(){ + location.reload(); + }) + }); + + //-- register 'modal_done' event callback on the `addKey` setting from ./manifest:17 + settings.manifest.addKey.addEvent('modal_done', function() { + var keyName = JSON.parse(localStorage.getItem('store.settings.keyName')) + , keyText = JSON.parse(localStorage.getItem('store.settings.keyText')) + ; + + //-- error if keyName or keyText is empty + if (!keyName || !keyText) { + return alert('Key name and key text must be provided!'); + } + + //-- add key to encrypted storage + var newKey = { + email: keyName, + id : keyName + }; + + if (options.keys && options.keys.length > 0) { + options.keys.push(newKey); + } else { + options.keys = [newKey] + } + + //-- persist options from setting page in `chrome.storage` (see https://developer.chrome.com/extensions/storage) + chrome.storage.sync.set({options: options}, function () { + //-- cleanup entered key from localStorage + localStorage.removeItem('store.settings.keyName'); + localStorage.removeItem('store.settings.keyText'); + + //-- reload page so storage retrieval can happen before `FancySettings` is init'ed + location.reload(); + }) + }) }); // Option 2: Do everything manually: From 178b7f124e13a722146220ad04c5377f86ddd2b8 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Sat, 16 Apr 2016 03:32:53 +0200 Subject: [PATCH 4/8] reformat: 2 space indent, single quotes, and remove unecessary quotes from objects --- source/i18n.js | 115 +- source/js/classes/fancy-settings.js | 284 ++--- source/js/classes/search.js | 240 ++-- source/js/classes/setting.js | 1606 ++++++++++++++------------- source/js/classes/tab.js | 90 +- source/js/i18n.js | 40 +- source/lib/store.js | 180 +-- source/manifest.js | 334 +++--- source/settings.js | 222 ++-- 9 files changed, 1565 insertions(+), 1546 deletions(-) diff --git a/source/i18n.js b/source/i18n.js index 13d7ebb..bff34f7 100755 --- a/source/i18n.js +++ b/source/i18n.js @@ -1,71 +1,70 @@ // SAMPLE this.i18n = { - "settings": { - "en": "Settings", - "de": "Optionen" - }, - "search": { - "en": "Search", - "de": "Suche" - }, - "nothing-found": { - "en": "No matches were found.", - "de": "Keine Übereinstimmungen gefunden." - }, - - - - "information": { - "en": "Information", - "de": "Information" - }, - "login": { - "en": "Login", - "de": "Anmeldung" - }, - "username": { - "en": "Username:", - "de": "Benutzername:" - }, - "password": { - "en": "Password:", - "de": "Passwort:" - }, - "x-characters": { - "en": "6 - 12 characters", - "de": "6 - 12 Zeichen" - }, - "x-characters-pw": { - "en": "10 - 18 characters", - "de": "10 - 18 Zeichen" - }, - "description": { - "en": "This is a description. You can write any text inside of this.
\ + settings : { + en: 'Settings', + de: 'Optionen' + }, + search : { + en: 'Search', + de: 'Suche' + }, + 'nothing-found': { + en: 'No matches were found.', + de: 'Keine Übereinstimmungen gefunden.' + }, + + + information : { + en: 'Information', + de: 'Information' + }, + login : { + en: 'Login', + de: 'Anmeldung' + }, + username : { + en: 'Username:', + de: 'Benutzername:' + }, + password : { + en: 'Password:', + de: 'Passwort:' + }, + 'x-characters' : { + en: '6 - 12 characters', + de: '6 - 12 Zeichen' + }, + 'x-characters-pw': { + en: '10 - 18 characters', + de: '10 - 18 Zeichen' + }, + description : { + en: 'This is a description. You can write any text inside of this.
\ Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut\ labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores\ et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem\ ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et\ dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.\ - Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", - - "de": "Das ist eine Beschreibung. Du kannst hier beliebigen Text einfügen.
\ + Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.', + + de: 'Das ist eine Beschreibung. Du kannst hier beliebigen Text einfügen.
\ Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut\ labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores\ et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem\ ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et\ dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.\ - Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." - }, - "logout": { - "en": "Logout", - "de": "Abmeldung" - }, - "enable": { - "en": "Enable", - "de": "Aktivieren" - }, - "disconnect": { - "en": "Disconnect:", - "de": "Trennen:" - } + Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.' + }, + logout : { + en: 'Logout', + de: 'Abmeldung' + }, + enable : { + en: 'Enable', + de: 'Aktivieren' + }, + disconnect : { + en: 'Disconnect:', + de: 'Trennen:' + } }; diff --git a/source/js/classes/fancy-settings.js b/source/js/classes/fancy-settings.js index 5c0223f..0eeed1c 100644 --- a/source/js/classes/fancy-settings.js +++ b/source/js/classes/fancy-settings.js @@ -4,149 +4,149 @@ // License: LGPL v2.1 // (function () { - var FancySettings = this.FancySettings = new Class({ - "tabs": {}, - - "initialize": function (name, icon) { - // Set title and icon - $("title").set("text", name); - $("favicon").set("href", icon); - $("icon").set("src", icon); - $("settings-label").set("text", (i18n.get("settings") || "Settings")); - $("search-label").set("text", (i18n.get("search") || "Search")); - $("search").set("placeholder", (i18n.get("search") || "Search") + "..."); - - this.tab = new Tab($("tab-container"), $("content")); - this.search = new Search($("search"), $("search-result-container")); - }, - - "create": function (params) { - var tab, - group, - row, - content, - bundle; - - // Create tab if it doesn't exist already - if (this.tabs[params.tab] === undefined) { - this.tabs[params.tab] = {"groups":{}}; - tab = this.tabs[params.tab]; - - tab.content = this.tab.create(); - tab.content.tab.set("text", params.tab); - this.search.bind(tab.content.tab); - - tab.content = tab.content.content; - (new Element("h2", { - "text": params.tab - })).inject(tab.content); - } else { - tab = this.tabs[params.tab]; - } - - // Create group if it doesn't exist already - if (tab.groups[params.group] === undefined) { - tab.groups[params.group] = {}; - group = tab.groups[params.group]; - - group.content = (new Element("table", { - "class": "setting group" - })).inject(tab.content); - - row = (new Element("tr")).inject(group.content); - - (new Element("td", { - "class": "setting group-name", - "text": params.group - })).inject(row); - - content = (new Element("td", { - "class": "setting group-content" - })).inject(row); - - group.setting = new Setting(content); - } else { - group = tab.groups[params.group]; - } - - // Create and index the setting - bundle = group.setting.create(params); - this.search.add(bundle); - - return bundle; - }, - - "align": function (settings) { - var types, - type, - maxWidth; - - types = [ - "text", - "button", - "slider", - "popupButton" - ]; - type = settings[0].params.type; - maxWidth = 0; - - if (!types.contains(type)) { - throw "invalidType"; - } - - settings.each(function (setting) { - if (setting.params.type !== type) { - throw "multipleTypes"; - } - - var width = setting.label.offsetWidth; - if (width > maxWidth) { - maxWidth = width; - } - }); - - settings.each(function (setting) { - var width = setting.label.offsetWidth; - if (width < maxWidth) { - if (type === "button" || type === "slider") { - setting.element.setStyle("margin-left", (maxWidth - width + 2) + "px"); - setting.search.element.setStyle("margin-left", (maxWidth - width + 2) + "px"); - } else { - setting.element.setStyle("margin-left", (maxWidth - width) + "px"); - setting.search.element.setStyle("margin-left", (maxWidth - width) + "px"); - } - } - }); + var FancySettings = this.FancySettings = new Class({ + 'tabs': {}, + + 'initialize': function (name, icon) { + // Set title and icon + $('title').set('text', name); + $('favicon').set('href', icon); + $('icon').set('src', icon); + $('settings-label').set('text', (i18n.get('settings') || 'Settings')); + $('search-label').set('text', (i18n.get('search') || 'Search')); + $('search').set('placeholder', (i18n.get('search') || 'Search') + '...'); + + this.tab = new Tab($('tab-container'), $('content')); + this.search = new Search($('search'), $('search-result-container')); + }, + + 'create': function (params) { + var tab, + group, + row, + content, + bundle; + + // Create tab if it doesn't exist already + if (this.tabs[params.tab] === undefined) { + this.tabs[params.tab] = {'groups': {}}; + tab = this.tabs[params.tab]; + + tab.content = this.tab.create(); + tab.content.tab.set('text', params.tab); + this.search.bind(tab.content.tab); + + tab.content = tab.content.content; + (new Element('h2', { + 'text': params.tab + })).inject(tab.content); + } else { + tab = this.tabs[params.tab]; + } + + // Create group if it doesn't exist already + if (tab.groups[params.group] === undefined) { + tab.groups[params.group] = {}; + group = tab.groups[params.group]; + + group.content = (new Element('table', { + 'class': 'setting group' + })).inject(tab.content); + + row = (new Element('tr')).inject(group.content); + + (new Element('td', { + 'class': 'setting group-name', + 'text' : params.group + })).inject(row); + + content = (new Element('td', { + 'class': 'setting group-content' + })).inject(row); + + group.setting = new Setting(content); + } else { + group = tab.groups[params.group]; + } + + // Create and index the setting + bundle = group.setting.create(params); + this.search.add(bundle); + + return bundle; + }, + + 'align': function (settings) { + var types, + type, + maxWidth; + + types = [ + 'text', + 'button', + 'slider', + 'popupButton' + ]; + type = settings[0].params.type; + maxWidth = 0; + + if (!types.contains(type)) { + throw 'invalidType'; + } + + settings.each(function (setting) { + if (setting.params.type !== type) { + throw 'multipleTypes'; } - }); - - FancySettings.__proto__.initWithManifest = function (callback) { - var settings, - output; - - settings = new FancySettings(manifest.name, manifest.icon); - settings.manifest = {}; - - manifest.settings.each(function (params) { - output = settings.create(params); - if (params.name !== undefined) { - settings.manifest[params.name] = output; - } - }); - - if (manifest.alignment !== undefined) { - document.body.addClass("measuring"); - manifest.alignment.each(function (group) { - group = group.map(function (name) { - return settings.manifest[name]; - }); - settings.align(group); - }); - document.body.removeClass("measuring"); + + var width = setting.label.offsetWidth; + if (width > maxWidth) { + maxWidth = width; } - - if (callback !== undefined) { - callback(settings); + }); + + settings.each(function (setting) { + var width = setting.label.offsetWidth; + if (width < maxWidth) { + if (type === 'button' || type === 'slider') { + setting.element.setStyle('margin-left', (maxWidth - width + 2) + 'px'); + setting.search.element.setStyle('margin-left', (maxWidth - width + 2) + 'px'); + } else { + setting.element.setStyle('margin-left', (maxWidth - width) + 'px'); + setting.search.element.setStyle('margin-left', (maxWidth - width) + 'px'); + } } - }; + }); + } + }); + + FancySettings.__proto__.initWithManifest = function (callback) { + var settings, + output; + + settings = new FancySettings(manifest.name, manifest.icon); + settings.manifest = {}; + + manifest.settings.each(function (params) { + output = settings.create(params); + if (params.name !== undefined) { + settings.manifest[params.name] = output; + } + }); + + if (manifest.alignment !== undefined) { + document.body.addClass('measuring'); + manifest.alignment.each(function (group) { + group = group.map(function (name) { + return settings.manifest[name]; + }); + settings.align(group); + }); + document.body.removeClass('measuring'); + } + + if (callback !== undefined) { + callback(settings); + } + }; }()); diff --git a/source/js/classes/search.js b/source/js/classes/search.js index 7278219..166763e 100755 --- a/source/js/classes/search.js +++ b/source/js/classes/search.js @@ -4,123 +4,127 @@ // License: LGPL v2.1 // (function () { - this.Search = new Class({ - "index": [], - "groups": {}, - - "initialize": function (search, searchResultContainer) { - var setting, - find; - - this.search = search; - this.searchResultContainer = searchResultContainer; - this.setting = new Setting(new Element("div")); - - // Create setting for message "nothing found" - setting = new Setting(this.searchResultContainer); - this.nothingFound = setting.create({ - "type": "description", - "text": (i18n.get("nothing-found") || "No matches were found.") - }); - this.nothingFound.bundle.set("id", "nothing-found"); - - // Create event handlers - find = (function (event) { - this.find(event.target.get("value")); - }).bind(this); - - this.search.addEvent("keyup", (function (event) { - if (event.key === "esc") { - this.reset(); - } else { - find(event); - } - }).bind(this)); - this.search.addEventListener("search", find, false); - }, - - "bind": function (tab) { - tab.addEvent("click", this.reset.bind(this)); - }, - - "add": function (setting) { - var searchSetting = this.setting.create(setting.params); - setting.search = searchSetting; - searchSetting.original = setting; - this.index.push(searchSetting); - - setting.addEvent("action", function (value, stopPropagation) { - if (searchSetting.set !== undefined && stopPropagation !== true) { - searchSetting.set(value, true); - } - }); - searchSetting.addEvent("action", function (value) { - if (setting.set !== undefined) { - setting.set(value, true); - } - setting.fireEvent("action", [value, true]); - }); - }, - - "find": function (searchString) { - // Exit search mode - if (searchString.trim() === "") { - document.body.removeClass("searching"); - return; - } - - // Or enter search mode - this.index.each(function (setting) { setting.bundle.dispose(); }); - Object.each(this.groups, function (group) { group.dispose(); }); - document.body.addClass("searching"); - - // Filter settings - var result = this.index.filter(function (setting) { - if (setting.params.searchString.contains(searchString.trim().toLowerCase())) { - return true; - } - }); - - // Display settings - result.each((function (setting) { - var group, - row; - - // Create group if it doesn't exist already - if (this.groups[setting.params.group] === undefined) { - this.groups[setting.params.group] = (new Element("table", { - "class": "setting group" - })).inject(this.searchResultContainer); - - group = this.groups[setting.params.group]; - row = (new Element("tr")).inject(group); - - (new Element("td", { - "class": "setting group-name", - "text": setting.params.group - })).inject(row); - - group.content = (new Element("td", { - "class": "setting group-content" - })).inject(row); - } else { - group = this.groups[setting.params.group].inject(this.searchResultContainer); - } - - setting.bundle.inject(group.content); - }).bind(this)); - - if (result.length === 0) { - this.nothingFound.bundle.addClass("show"); - } else { - this.nothingFound.bundle.removeClass("show"); - } - }, - - "reset": function () { - this.search.set("value", ""); - this.search.blur(); - this.find(""); + this.Search = new Class({ + 'index' : [], + 'groups': {}, + + 'initialize': function (search, searchResultContainer) { + var setting, + find; + + this.search = search; + this.searchResultContainer = searchResultContainer; + this.setting = new Setting(new Element('div')); + + // Create setting for message 'nothing found' + setting = new Setting(this.searchResultContainer); + this.nothingFound = setting.create({ + 'type': 'description', + 'text': (i18n.get('nothing-found') || 'No matches were found.') + }); + this.nothingFound.bundle.set('id', 'nothing-found'); + + // Create event handlers + find = (function (event) { + this.find(event.target.get('value')); + }).bind(this); + + this.search.addEvent('keyup', (function (event) { + if (event.key === 'esc') { + this.reset(); + } else { + find(event); } - }); + }).bind(this)); + this.search.addEventListener('search', find, false); + }, + + 'bind': function (tab) { + tab.addEvent('click', this.reset.bind(this)); + }, + + 'add': function (setting) { + var searchSetting = this.setting.create(setting.params); + setting.search = searchSetting; + searchSetting.original = setting; + this.index.push(searchSetting); + + setting.addEvent('action', function (value, stopPropagation) { + if (searchSetting.set !== undefined && stopPropagation !== true) { + searchSetting.set(value, true); + } + }); + searchSetting.addEvent('action', function (value) { + if (setting.set !== undefined) { + setting.set(value, true); + } + setting.fireEvent('action', [value, true]); + }); + }, + + 'find': function (searchString) { + // Exit search mode + if (searchString.trim() === '') { + document.body.removeClass('searching'); + return; + } + + // Or enter search mode + this.index.each(function (setting) { + setting.bundle.dispose(); + }); + Object.each(this.groups, function (group) { + group.dispose(); + }); + document.body.addClass('searching'); + + // Filter settings + var result = this.index.filter(function (setting) { + if (setting.params.searchString.contains(searchString.trim().toLowerCase())) { + return true; + } + }); + + // Display settings + result.each((function (setting) { + var group, + row; + + // Create group if it doesn't exist already + if (this.groups[setting.params.group] === undefined) { + this.groups[setting.params.group] = (new Element('table', { + 'class': 'setting group' + })).inject(this.searchResultContainer); + + group = this.groups[setting.params.group]; + row = (new Element('tr')).inject(group); + + (new Element('td', { + 'class': 'setting group-name', + 'text' : setting.params.group + })).inject(row); + + group.content = (new Element('td', { + 'class': 'setting group-content' + })).inject(row); + } else { + group = this.groups[setting.params.group].inject(this.searchResultContainer); + } + + setting.bundle.inject(group.content); + }).bind(this)); + + if (result.length === 0) { + this.nothingFound.bundle.addClass('show'); + } else { + this.nothingFound.bundle.removeClass('show'); + } + }, + + 'reset': function () { + this.search.set('value', ''); + this.search.blur(); + this.find(''); + } + }); }()); diff --git a/source/js/classes/setting.js b/source/js/classes/setting.js index 7eb4c69..626aacf 100755 --- a/source/js/classes/setting.js +++ b/source/js/classes/setting.js @@ -4,826 +4,832 @@ // License: LGPL v2.1 // (function () { - var settings, - Bundle; - - settings = new Store("settings"); - Bundle = new Class({ - // Attributes: - // - tab - // - group - // - name - // - type - // - // Methods: - // - initialize - // - createDOM - // - setupDOM - // - addEvents - // - get - // - set - "Implements": Events, - - "initialize": function (params) { - this.params = params; - this.params.searchString = "•" + this.params.tab + "•" + this.params.group + "•"; - - this.createDOM(); - this.setupDOM(); - this.addEvents(); - - if (this.params.id !== undefined) { - this.element.set("id", this.params.id); - } - - if (this.params.name !== undefined) { - this.set(settings.get(this.params.name), true); - } - - this.params.searchString = this.params.searchString.toLowerCase(); - }, - - "addEvents": function () { - this.element.addEvent("change", (function (event) { - if (this.params.name !== undefined) { - settings.set(this.params.name, this.get()); - } - - this.fireEvent("action", this.get()); - }).bind(this)); - }, - - "get": function () { - return this.element.get("value"); - }, - - "set": function (value, noChangeEvent) { - this.element.set("value", value); - - if (noChangeEvent !== true) { - this.element.fireEvent("change"); - } - - return this; - } - }); - - Bundle.Description = new Class({ - // text - "Extends": Bundle, - "addEvents": undefined, - "get": undefined, - "set": undefined, - - "initialize": function (params) { - this.params = params; - this.params.searchString = ""; - - this.createDOM(); - this.setupDOM(); - }, - - "createDOM": function () { - this.bundle = new Element("div", { - "class": "setting bundle description" - }); - - this.container = new Element("div", { - "class": "setting container description" - }); - - this.element = new Element("p", { - "class": "setting element description" - }); - }, - - "setupDOM": function () { - if (this.params.text !== undefined) { - this.element.set("html", this.params.text); - } - - this.element.inject(this.container); - this.container.inject(this.bundle); - } - }); - - Bundle.Button = new Class({ - // label, text - // action -> click - "Extends": Bundle, - "get": undefined, - "set": undefined, - - "initialize": function (params) { - this.params = params; - this.params.searchString = "•" + this.params.tab + "•" + this.params.group + "•"; - - this.createDOM(); - this.setupDOM(); - this.addEvents(); - - if (this.params.id !== undefined) { - this.element.set("id", this.params.id); - } - - this.params.searchString = this.params.searchString.toLowerCase(); - }, - - "createDOM": function () { - this.bundle = new Element("div", { - "class": "setting bundle button" - }); - - this.container = new Element("div", { - "class": "setting container button" - }); - - this.element = new Element("input", { - "class": "setting element button", - "type": "button" - }); - - this.label = new Element("label", { - "class": "setting label button" - }); - }, - - "setupDOM": function () { - if (this.params.label !== undefined) { - this.label.set("html", this.params.label); - this.label.inject(this.container); - this.params.searchString += this.params.label + "•"; - } - - if (this.params.text !== undefined) { - this.element.set("value", this.params.text); - this.params.searchString += this.params.text + "•"; - } - - this.element.inject(this.container); - this.container.inject(this.bundle); - }, - - "addEvents": function () { - this.element.addEvent("click", (function () { - this.fireEvent("action"); - }).bind(this)); + var settings, + Bundle; + + settings = new Store('settings'); + Bundle = new Class({ + // Attributes: + // - tab + // - group + // - name + // - type + // + // Methods: + // - initialize + // - createDOM + // - setupDOM + // - addEvents + // - get + // - set + 'Implements': Events, + + 'initialize': function (params) { + this.params = params; + this.params.searchString = '•' + this.params.tab + '•' + this.params.group + '•'; + + this.createDOM(); + this.setupDOM(); + this.addEvents(); + + if (this.params.id !== undefined) { + this.element.set('id', this.params.id); + } + + if (this.params.name !== undefined) { + this.set(settings.get(this.params.name), true); + } + + this.params.searchString = this.params.searchString.toLowerCase(); + }, + + 'addEvents': function () { + this.element.addEvent('change', (function (event) { + if (this.params.name !== undefined) { + settings.set(this.params.name, this.get()); } - }); - - /* File Button -- Work In Progress - * - * Bundle.FileButton = new Class({ - * // label, text - * // action -> click - * "Extends": Bundle.Button, - * "initialize": function (params) { - * this.params = params; - * this.params.searchString = "•" + this.params.tab + "•" + this.params.group + "•"; - * - * this.createDOM(); - * this.setupDOM(); - * this.addEvents(); - * - * if (this.params.id !== undefined) { - * this.element.set("id", this.params.id); - * } - * - * this.params.searchString = this.params.searchString.toLowerCase(); - * }, - * - * "createDOM": function () { - * //-- do same DOM creation as `Button` - * this.parent(); - * - * //-- add file specific DOM creation - * this.element = new Element("input", { - * "class": "setting element button", - * "type": "file" - * }); - * - * this.label = new Element("label", { - * "class": "setting label button" - * }); - * }, - * - * "setupDOM": function () { - * //-- do same DOM setup as `Button` - * this.parent(); - * }, - * - * "addEvents": function () { - * this.modalElement.addEvent("click", (function () { - * this.fireEvent("action"); - * }).bind(this)); - * } - * }); - */ - - Bundle.ModalButton = new Class({ - // label, text - // action -> click - "Extends": Bundle.Button, - - "createDOM": function () { - //-- do same DOM creation as `Button` - this.parent(); - - //-- add modal specific DOM creation - this.modalBackdrop = new Element("div", { - "class": "modal backdrop hide" - }); - - this.modalContainer = new Element("div", { - "class": "modal container" - }); - - this.modalTitle = new Element("h2", { - "class": "modal title" - }); - - this.modalDone = new Element("button", { - "class": "modal done" - }) - }, - - "setupDOM": function () { - //-- do same DOM setup as `Button` - this.parent(); - - //-- add modal specific DOM setup - var that = this; - - if (this.params.modal.title !== undefined) { - this.modalTitle.set("html", this.params.modal.title); - this.modalTitle.inject(this.modalContainer); - this.params.searchString += this.params.label + "•"; - } - this.modalContainer.inject(this.modalBackdrop); - this.modalBackdrop.inject(this.bundle); + this.fireEvent('action', this.get()); + }).bind(this)); + }, + + 'get': function () { + return this.element.get('value'); + }, + + 'set': function (value, noChangeEvent) { + this.element.set('value', value); + + if (noChangeEvent !== true) { + this.element.fireEvent('change'); + } + + return this; + } + }); + + Bundle.Description = new Class({ + // text + 'Extends' : Bundle, + 'addEvents': undefined, + 'get' : undefined, + 'set' : undefined, + + 'initialize': function (params) { + this.params = params; + this.params.searchString = ''; + + this.createDOM(); + this.setupDOM(); + }, + + 'createDOM': function () { + this.bundle = new Element('div', { + 'class': 'setting bundle description' + }); + + this.container = new Element('div', { + 'class': 'setting container description' + }); + + this.element = new Element('p', { + 'class': 'setting element description' + }); + }, + + 'setupDOM': function () { + if (this.params.text !== undefined) { + this.element.set('html', this.params.text); + } + + this.element.inject(this.container); + this.container.inject(this.bundle); + } + }); + + Bundle.Button = new Class({ + // label, text + // action -> click + 'Extends': Bundle, + 'get' : undefined, + 'set' : undefined, + + 'initialize': function (params) { + this.params = params; + this.params.searchString = '•' + this.params.tab + '•' + this.params.group + '•'; + + this.createDOM(); + this.setupDOM(); + this.addEvents(); + + if (this.params.id !== undefined) { + this.element.set('id', this.params.id); + } + + this.params.searchString = this.params.searchString.toLowerCase(); + }, + + 'createDOM': function () { + this.bundle = new Element('div', { + 'class': 'setting bundle button' + }); + + this.container = new Element('div', { + 'class': 'setting container button' + }); + + this.element = new Element('input', { + 'class': 'setting element button', + 'type' : 'button' + }); + + this.label = new Element('label', { + 'class': 'setting label button' + }); + }, + + 'setupDOM': function () { + if (this.params.label !== undefined) { + this.label.set('html', this.params.label); + this.label.inject(this.container); + this.params.searchString += this.params.label + '•'; + } + + if (this.params.text !== undefined) { + this.element.set('value', this.params.text); + this.params.searchString += this.params.text + '•'; + } + + this.element.inject(this.container); + this.container.inject(this.bundle); + }, + + 'addEvents': function () { + this.element.addEvent('click', (function () { + this.fireEvent('action'); + }).bind(this)); + } + }); + + /* File Button -- Work In Progress + * + * Bundle.FileButton = new Class({ + * // label, text + * // action -> click + * 'Extends': Bundle.Button, + * 'initialize': function (params) { + * this.params = params; + * this.params.searchString = '•' + this.params.tab + '•' + this.params.group + '•'; + * + * this.createDOM(); + * this.setupDOM(); + * this.addEvents(); + * + * if (this.params.id !== undefined) { + * this.element.set('id', this.params.id); + * } + * + * this.params.searchString = this.params.searchString.toLowerCase(); + * }, + * + * 'createDOM': function () { + * //-- do same DOM creation as `Button` + * this.parent(); + * + * //-- add file specific DOM creation + * this.element = new Element('input', { + * 'class': 'setting element button', + * 'type': 'file' + * }); + * + * this.label = new Element('label', { + * 'class': 'setting label button' + * }); + * }, + * + * 'setupDOM': function () { + * //-- do same DOM setup as `Button` + * this.parent(); + * }, + * + * 'addEvents': function () { + * this.modalElement.addEvent('click', (function () { + * this.fireEvent('action'); + * }).bind(this)); + * } + * }); + */ - this.params.modal.contents.forEach(function (item) { - (new Setting(that.modalContainer)).create(item); - }); + Bundle.ModalButton = new Class({ + // label, text + // action -> click + 'Extends': Bundle.Button, - this.modalDone.set('html', 'Done'); - this.modalDone.inject(this.modalContainer); - }, + 'createDOM': function () { + //-- do same DOM creation as `Button` + this.parent(); - "addEvents": function () { - //-- do same addEvents as `Button` - this.parent(); + //-- add modal specific DOM creation + this.modalBackdrop = new Element('div', { + 'class': 'modal backdrop hide' + }); - //-- add model specific events - this.element.addEvent('click', (function () { - this.modalBackdrop.removeClass('hide'); - }).bind(this)); + this.modalContainer = new Element('div', { + 'class': 'modal container' + }); - this.modalDone.addEvent('click', (function(){ - this.modalBackdrop.addClass('hide'); - this.fireEvent('modal_done'); - }).bind(this)); + this.modalTitle = new Element('h2', { + 'class': 'modal title' + }); + + this.modalDone = new Element('button', { + 'class': 'modal done' + }) + }, + + 'setupDOM': function () { + //-- do same DOM setup as `Button` + this.parent(); + + //-- add modal specific DOM setup + var that = this; + + if (this.params.modal.title !== undefined) { + this.modalTitle.set('html', this.params.modal.title); + this.modalTitle.inject(this.modalContainer); + this.params.searchString += this.params.label + '•'; + } + + this.modalContainer.inject(this.modalBackdrop); + this.modalBackdrop.inject(this.bundle); + + this.params.modal.contents.forEach(function (item) { + (new Setting(that.modalContainer)).create(item); + }); + + this.modalDone.set('html', 'Done'); + this.modalDone.inject(this.modalContainer); + }, + + 'addEvents': function () { + //-- do same addEvents as `Button` + this.parent(); + + //-- add model specific events + this.element.addEvent('click', (function () { + this.modalBackdrop.removeClass('hide'); + }).bind(this)); + + this.modalDone.addEvent('click', (function () { + this.modalBackdrop.addClass('hide'); + this.fireEvent('modal_done'); + }).bind(this)); + } + }); + + Bundle.Text = new Class({ + // label, text, masked + // action -> change & keyup + 'Extends': Bundle, + + 'createDOM': function () { + this.bundle = new Element('div', { + 'class': 'setting bundle text' + }); + + this.container = new Element('div', { + 'class': 'setting container text' + }); + + this.element = new Element('input', { + 'class': 'setting element text', + 'type' : 'text' + }); + + this.label = new Element('label', { + 'class': 'setting label text' + }); + }, + + 'setupDOM': function () { + if (this.params.label !== undefined) { + this.label.set('html', this.params.label); + this.label.inject(this.container); + this.params.searchString += this.params.label + '•'; + } + + if (this.params.text !== undefined) { + this.element.set('placeholder', this.params.text); + this.params.searchString += this.params.text + '•'; + } + + if (this.params.masked === true) { + this.element.set('type', 'password'); + this.params.searchString += 'password' + '•'; + } + + this.element.inject(this.container); + this.container.inject(this.bundle); + }, + + 'addEvents': function () { + var change = (function (event) { + if (this.params.name !== undefined) { + settings.set(this.params.name, this.get()); } - }); - - Bundle.Text = new Class({ - // label, text, masked - // action -> change & keyup - "Extends": Bundle, - - "createDOM": function () { - this.bundle = new Element("div", { - "class": "setting bundle text" - }); - - this.container = new Element("div", { - "class": "setting container text" - }); - - this.element = new Element("input", { - "class": "setting element text", - "type": "text" - }); - - this.label = new Element("label", { - "class": "setting label text" - }); - }, - - "setupDOM": function () { - if (this.params.label !== undefined) { - this.label.set("html", this.params.label); - this.label.inject(this.container); - this.params.searchString += this.params.label + "•"; - } - - if (this.params.text !== undefined) { - this.element.set("placeholder", this.params.text); - this.params.searchString += this.params.text + "•"; - } - - if (this.params.masked === true) { - this.element.set("type", "password"); - this.params.searchString += "password" + "•"; - } - - this.element.inject(this.container); - this.container.inject(this.bundle); - }, - - "addEvents": function () { - var change = (function (event) { - if (this.params.name !== undefined) { - settings.set(this.params.name, this.get()); - } - - this.fireEvent("action", this.get()); - }).bind(this); - - this.element.addEvent("change", change); - this.element.addEvent("keyup", change); + + this.fireEvent('action', this.get()); + }).bind(this); + + this.element.addEvent('change', change); + this.element.addEvent('keyup', change); + } + }); + + Bundle.Checkbox = new Class({ + // label + // action -> change + 'Extends': Bundle, + + 'createDOM': function () { + this.bundle = new Element('div', { + 'class': 'setting bundle checkbox' + }); + + this.container = new Element('div', { + 'class': 'setting container checkbox' + }); + + this.element = new Element('input', { + 'id' : String.uniqueID(), + 'class': 'setting element checkbox', + 'type' : 'checkbox', + 'value': 'true' + }); + + this.label = new Element('label', { + 'class': 'setting label checkbox', + 'for' : this.element.get('id') + }); + }, + + 'setupDOM': function () { + this.element.inject(this.container); + this.container.inject(this.bundle); + + if (this.params.label !== undefined) { + this.label.set('html', this.params.label); + this.label.inject(this.container); + this.params.searchString += this.params.label + '•'; + } + }, + + 'get': function () { + return this.element.get('checked'); + }, + + 'set': function (value, noChangeEvent) { + this.element.set('checked', value); + + if (noChangeEvent !== true) { + this.element.fireEvent('change'); + } + + return this; + } + }); + + Bundle.Slider = new Class({ + // label, max, min, step, display, displayModifier + // action -> change + 'Extends': Bundle, + + 'initialize': function (params) { + this.params = params; + this.params.searchString = '•' + this.params.tab + '•' + this.params.group + '•'; + + this.createDOM(); + this.setupDOM(); + this.addEvents(); + + if (this.params.name !== undefined) { + this.set((settings.get(this.params.name) || 0), true); + } else { + this.set(0, true); + } + + this.params.searchString = this.params.searchString.toLowerCase(); + }, + + 'createDOM': function () { + this.bundle = new Element('div', { + 'class': 'setting bundle slider' + }); + + this.container = new Element('div', { + 'class': 'setting container slider' + }); + + this.element = new Element('input', { + 'class': 'setting element slider', + 'type' : 'range' + }); + + this.label = new Element('label', { + 'class': 'setting label slider' + }); + + this.display = new Element('span', { + 'class': 'setting display slider' + }); + }, + + 'setupDOM': function () { + if (this.params.label !== undefined) { + this.label.set('html', this.params.label); + this.label.inject(this.container); + this.params.searchString += this.params.label + '•'; + } + + if (this.params.max !== undefined) { + this.element.set('max', this.params.max); + } + + if (this.params.min !== undefined) { + this.element.set('min', this.params.min); + } + + if (this.params.step !== undefined) { + this.element.set('step', this.params.step); + } + + this.element.inject(this.container); + if (this.params.display !== false) { + if (this.params.displayModifier !== undefined) { + this.display.set('text', this.params.displayModifier(0)); + } else { + this.display.set('text', 0); } - }); - - Bundle.Checkbox = new Class({ - // label - // action -> change - "Extends": Bundle, - - "createDOM": function () { - this.bundle = new Element("div", { - "class": "setting bundle checkbox" - }); - - this.container = new Element("div", { - "class": "setting container checkbox" - }); - - this.element = new Element("input", { - "id": String.uniqueID(), - "class": "setting element checkbox", - "type": "checkbox", - "value": "true" - }); - - this.label = new Element("label", { - "class": "setting label checkbox", - "for": this.element.get("id") - }); - }, - - "setupDOM": function () { - this.element.inject(this.container); - this.container.inject(this.bundle); - - if (this.params.label !== undefined) { - this.label.set("html", this.params.label); - this.label.inject(this.container); - this.params.searchString += this.params.label + "•"; - } - }, - - "get": function () { - return this.element.get("checked"); - }, - - "set": function (value, noChangeEvent) { - this.element.set("checked", value); - - if (noChangeEvent !== true) { - this.element.fireEvent("change"); - } - - return this; + this.display.inject(this.container); + } + this.container.inject(this.bundle); + }, + + 'addEvents': function () { + this.element.addEvent('change', (function (event) { + if (this.params.name !== undefined) { + settings.set(this.params.name, this.get()); } - }); - - Bundle.Slider = new Class({ - // label, max, min, step, display, displayModifier - // action -> change - "Extends": Bundle, - - "initialize": function (params) { - this.params = params; - this.params.searchString = "•" + this.params.tab + "•" + this.params.group + "•"; - - this.createDOM(); - this.setupDOM(); - this.addEvents(); - - if (this.params.name !== undefined) { - this.set((settings.get(this.params.name) || 0), true); - } else { - this.set(0, true); - } - - this.params.searchString = this.params.searchString.toLowerCase(); - }, - - "createDOM": function () { - this.bundle = new Element("div", { - "class": "setting bundle slider" - }); - - this.container = new Element("div", { - "class": "setting container slider" - }); - - this.element = new Element("input", { - "class": "setting element slider", - "type": "range" - }); - - this.label = new Element("label", { - "class": "setting label slider" - }); - - this.display = new Element("span", { - "class": "setting display slider" - }); - }, - - "setupDOM": function () { - if (this.params.label !== undefined) { - this.label.set("html", this.params.label); - this.label.inject(this.container); - this.params.searchString += this.params.label + "•"; - } - - if (this.params.max !== undefined) { - this.element.set("max", this.params.max); - } - - if (this.params.min !== undefined) { - this.element.set("min", this.params.min); - } - - if (this.params.step !== undefined) { - this.element.set("step", this.params.step); - } - - this.element.inject(this.container); - if (this.params.display !== false) { - if (this.params.displayModifier !== undefined) { - this.display.set("text", this.params.displayModifier(0)); - } else { - this.display.set("text", 0); - } - this.display.inject(this.container); - } - this.container.inject(this.bundle); - }, - - "addEvents": function () { - this.element.addEvent("change", (function (event) { - if (this.params.name !== undefined) { - settings.set(this.params.name, this.get()); - } - - if (this.params.displayModifier !== undefined) { - this.display.set("text", this.params.displayModifier(this.get())); - } else { - this.display.set("text", this.get()); - } - this.fireEvent("action", this.get()); - }).bind(this)); - }, - - "get": function () { - return Number.from(this.element.get("value")); - }, - - "set": function (value, noChangeEvent) { - this.element.set("value", value); - - if (noChangeEvent !== true) { - this.element.fireEvent("change"); - } else { - if (this.params.displayModifier !== undefined) { - this.display.set("text", this.params.displayModifier(Number.from(value))); - } else { - this.display.set("text", Number.from(value)); - } - } - - return this; + + if (this.params.displayModifier !== undefined) { + this.display.set('text', this.params.displayModifier(this.get())); + } else { + this.display.set('text', this.get()); } - }); - - Bundle.PopupButton = new Class({ - // label, options[{value, text}] - // action -> change - "Extends": Bundle, - - "createDOM": function () { - this.bundle = new Element("div", { - "class": "setting bundle popup-button" - }); - - this.container = new Element("div", { - "class": "setting container popup-button" - }); - - this.element = new Element("select", { - "class": "setting element popup-button" - }); - - this.label = new Element("label", { - "class": "setting label popup-button" - }); - - if (this.params.options === undefined) { return; } - - // convert array syntax into object syntax for options - function arrayToObject(option) { - if (typeOf(option) == "array") { - option = { - "value": option[0], - "text": option[1] || option[0], - }; - } - return option; - } + this.fireEvent('action', this.get()); + }).bind(this)); + }, - // convert arrays - if (typeOf(this.params.options) == "array") { - var values = []; - this.params.options.each((function(values, option) { - values.push(arrayToObject(option)); - }).bind(this, values)); - this.params.options = { "values": values }; - } + 'get': function () { + return Number.from(this.element.get('value')); + }, - var groups; - if (this.params.options.groups !== undefined) { - groups = {}; - this.params.options.groups.each((function (groups, group) { - this.params.searchString += (group) + "•"; - groups[group] = (new Element("optgroup", { - "label": group, - }).inject(this.element)); - }).bind(this, groups)); - } + 'set': function (value, noChangeEvent) { + this.element.set('value', value); - if (this.params.options.values !== undefined) { - this.params.options.values.each((function(groups, option) { - option = arrayToObject(option); - this.params.searchString += (option.text || option.value) + "•"; - - // find the parent of this option - either a group or the main element - var parent; - if (option.group && this.params.options.groups) { - if ((option.group - 1) in this.params.options.groups) { - option.group = this.params.options.groups[option.group-1]; - } - if (option.group in groups) { - parent = groups[option.group]; - } - else { - parent = this.element; - } - } - else { - parent = this.element; - } - - (new Element("option", { - "value": option.value, - "text": option.text || option.value, - })).inject(parent); - }).bind(this, groups)); - } - }, - - "setupDOM": function () { - if (this.params.label !== undefined) { - this.label.set("html", this.params.label); - this.label.inject(this.container); - this.params.searchString += this.params.label + "•"; - } - - this.element.inject(this.container); - this.container.inject(this.bundle); + if (noChangeEvent !== true) { + this.element.fireEvent('change'); + } else { + if (this.params.displayModifier !== undefined) { + this.display.set('text', this.params.displayModifier(Number.from(value))); + } else { + this.display.set('text', Number.from(value)); } - }); - - Bundle.ListBox = new Class({ - // label, options[{value, text}] - // action -> change - "Extends": Bundle.PopupButton, - - "createDOM": function () { - this.bundle = new Element("div", { - "class": "setting bundle list-box" - }); - - this.container = new Element("div", { - "class": "setting container list-box" - }); - - this.element = new Element("select", { - "class": "setting element list-box", - "size": "2" - }); - - this.label = new Element("label", { - "class": "setting label list-box" - }); - - if (this.params.options === undefined) { return; } - this.params.options.each((function (option) { - this.params.searchString += (option.text || option.value) + "•"; - - (new Element("option", { - "value": option.value, - "text": option.text || option.value - })).inject(this.element); - }).bind(this)); - }, - - "get": function () { - return (this.element.get("value") || undefined); + } + + return this; + } + }); + + Bundle.PopupButton = new Class({ + // label, options[{value, text}] + // action -> change + 'Extends': Bundle, + + 'createDOM': function () { + this.bundle = new Element('div', { + 'class': 'setting bundle popup-button' + }); + + this.container = new Element('div', { + 'class': 'setting container popup-button' + }); + + this.element = new Element('select', { + 'class': 'setting element popup-button' + }); + + this.label = new Element('label', { + 'class': 'setting label popup-button' + }); + + if (this.params.options === undefined) { + return; + } + + // convert array syntax into object syntax for options + function arrayToObject(option) { + if (typeOf(option) == 'array') { + option = { + 'value': option[0], + 'text' : option[1] || option[0], + }; } - }); - - Bundle.Textarea = new Class({ - // label, text, value - // action -> change & keyup - "Extends": Bundle, - - "createDOM": function () { - this.bundle = new Element("div", { - "class": "setting bundle textarea" - }); - - this.container = new Element("div", { - "class": "setting container textarea" - }); - - this.element = new Element("textarea", { - "class": "setting element textarea" - }); - - this.label = new Element("label", { - "class": "setting label textarea" - }); - }, - - "setupDOM": function () { - if (this.params.label !== undefined) { - this.label.set("html", this.params.label); - this.label.inject(this.container); - this.params.searchString += this.params.label + "•"; - } - - if (this.params.text !== undefined) { - this.element.set("placeholder", this.params.text); - this.params.searchString += this.params.text + "•"; - } + return option; + } + + // convert arrays + if (typeOf(this.params.options) == 'array') { + var values = []; + this.params.options.each((function (values, option) { + values.push(arrayToObject(option)); + }).bind(this, values)); + this.params.options = {'values': values}; + } + + var groups; + if (this.params.options.groups !== undefined) { + groups = {}; + this.params.options.groups.each((function (groups, group) { + this.params.searchString += (group) + '•'; + groups[group] = (new Element('optgroup', { + 'label': group, + }).inject(this.element)); + }).bind(this, groups)); + } - if (this.params.value !== undefined) { - this.element.appendText(this.params.text); + if (this.params.options.values !== undefined) { + this.params.options.values.each((function (groups, option) { + option = arrayToObject(option); + this.params.searchString += (option.text || option.value) + '•'; + + // find the parent of this option - either a group or the main element + var parent; + if (option.group && this.params.options.groups) { + if ((option.group - 1) in this.params.options.groups) { + option.group = this.params.options.groups[option.group - 1]; } - - this.element.inject(this.container); - this.container.inject(this.bundle); - }, - - "addEvents": function () { - var change = (function (event) { - if (this.params.name !== undefined) { - settings.set(this.params.name, this.get()); - } - - this.fireEvent("action", this.get()); - }).bind(this); - - this.element.addEvent("change", change); - this.element.addEvent("keyup", change); - } - }); - - Bundle.RadioButtons = new Class({ - // label, options[{value, text}] - // action -> change - "Extends": Bundle, - - "createDOM": function () { - var settingID = String.uniqueID(); - - this.bundle = new Element("div", { - "class": "setting bundle radio-buttons" - }); - - this.label = new Element("label", { - "class": "setting label radio-buttons" - }); - - this.containers = []; - this.elements = []; - this.labels = []; - - if (this.params.options === undefined) { return; } - this.params.options.each((function (option) { - var optionID, - container; - - this.params.searchString += (option.text || option.value) + "•"; - - optionID = String.uniqueID(); - container = (new Element("div", { - "class": "setting container radio-buttons" - })).inject(this.bundle); - this.containers.push(container); - - this.elements.push((new Element("input", { - "id": optionID, - "name": settingID, - "class": "setting element radio-buttons", - "type": "radio", - "value": option.value - })).inject(container)); - - this.labels.push((new Element("label", { - "class": "setting element-label radio-buttons", - "for": optionID, - "text": option.text || option.value - })).inject(container)); - }).bind(this)); - }, - - "setupDOM": function () { - if (this.params.label !== undefined) { - this.label.set("html", this.params.label); - this.label.inject(this.bundle, "top"); - this.params.searchString += this.params.label + "•"; + if (option.group in groups) { + parent = groups[option.group]; } - }, - - "addEvents": function () { - this.bundle.addEvent("change", (function (event) { - if (this.params.name !== undefined) { - settings.set(this.params.name, this.get()); - } - - this.fireEvent("action", this.get()); - }).bind(this)); - }, - - "get": function () { - var checkedEl = this.elements.filter((function (el) { - return el.get("checked"); - }).bind(this)); - return (checkedEl[0] && checkedEl[0].get("value")); - }, - - "set": function (value, noChangeEvent) { - var desiredEl = this.elements.filter((function (el) { - return (el.get("value") === value); - }).bind(this)); - desiredEl[0] && desiredEl[0].set("checked", true); - - if (noChangeEvent !== true) { - this.bundle.fireEvent("change"); + else { + parent = this.element; } - - return this; + } + else { + parent = this.element; + } + + (new Element('option', { + 'value': option.value, + 'text' : option.text || option.value, + })).inject(parent); + }).bind(this, groups)); + } + }, + + 'setupDOM': function () { + if (this.params.label !== undefined) { + this.label.set('html', this.params.label); + this.label.inject(this.container); + this.params.searchString += this.params.label + '•'; + } + + this.element.inject(this.container); + this.container.inject(this.bundle); + } + }); + + Bundle.ListBox = new Class({ + // label, options[{value, text}] + // action -> change + 'Extends': Bundle.PopupButton, + + 'createDOM': function () { + this.bundle = new Element('div', { + 'class': 'setting bundle list-box' + }); + + this.container = new Element('div', { + 'class': 'setting container list-box' + }); + + this.element = new Element('select', { + 'class': 'setting element list-box', + 'size' : '2' + }); + + this.label = new Element('label', { + 'class': 'setting label list-box' + }); + + if (this.params.options === undefined) { + return; + } + this.params.options.each((function (option) { + this.params.searchString += (option.text || option.value) + '•'; + + (new Element('option', { + 'value': option.value, + 'text' : option.text || option.value + })).inject(this.element); + }).bind(this)); + }, + + 'get': function () { + return (this.element.get('value') || undefined); + } + }); + + Bundle.Textarea = new Class({ + // label, text, value + // action -> change & keyup + 'Extends': Bundle, + + 'createDOM': function () { + this.bundle = new Element('div', { + 'class': 'setting bundle textarea' + }); + + this.container = new Element('div', { + 'class': 'setting container textarea' + }); + + this.element = new Element('textarea', { + 'class': 'setting element textarea' + }); + + this.label = new Element('label', { + 'class': 'setting label textarea' + }); + }, + + 'setupDOM': function () { + if (this.params.label !== undefined) { + this.label.set('html', this.params.label); + this.label.inject(this.container); + this.params.searchString += this.params.label + '•'; + } + + if (this.params.text !== undefined) { + this.element.set('placeholder', this.params.text); + this.params.searchString += this.params.text + '•'; + } + + if (this.params.value !== undefined) { + this.element.appendText(this.params.text); + } + + this.element.inject(this.container); + this.container.inject(this.bundle); + }, + + 'addEvents': function () { + var change = (function (event) { + if (this.params.name !== undefined) { + settings.set(this.params.name, this.get()); } - }); - - this.Setting = new Class({ - "initialize": function (container) { - this.container = container; - }, - - "create": function (params) { - var types, - bundle; - - // Available types - types = { - "description": "Description", - "button": "Button", - "text": "Text", - "textarea": "Textarea", - "checkbox": "Checkbox", - "slider": "Slider", - "popupButton": "PopupButton", - "listBox": "ListBox", - "radioButtons": "RadioButtons", - "modalButton": "ModalButton", - //"fileButton": "FileButton" - }; - - if (types.hasOwnProperty(params.type)) { - bundle = new Bundle[types[params.type]](params); - bundle.bundleContainer = this.container; - bundle.bundle.inject(this.container); - return bundle; - } else { - throw "invalidType"; - } + + this.fireEvent('action', this.get()); + }).bind(this); + + this.element.addEvent('change', change); + this.element.addEvent('keyup', change); + } + }); + + Bundle.RadioButtons = new Class({ + // label, options[{value, text}] + // action -> change + 'Extends': Bundle, + + 'createDOM': function () { + var settingID = String.uniqueID(); + + this.bundle = new Element('div', { + 'class': 'setting bundle radio-buttons' + }); + + this.label = new Element('label', { + 'class': 'setting label radio-buttons' + }); + + this.containers = []; + this.elements = []; + this.labels = []; + + if (this.params.options === undefined) { + return; + } + this.params.options.each((function (option) { + var optionID, + container; + + this.params.searchString += (option.text || option.value) + '•'; + + optionID = String.uniqueID(); + container = (new Element('div', { + 'class': 'setting container radio-buttons' + })).inject(this.bundle); + this.containers.push(container); + + this.elements.push((new Element('input', { + 'id' : optionID, + 'name' : settingID, + 'class': 'setting element radio-buttons', + 'type' : 'radio', + 'value': option.value + })).inject(container)); + + this.labels.push((new Element('label', { + 'class': 'setting element-label radio-buttons', + 'for' : optionID, + 'text' : option.text || option.value + })).inject(container)); + }).bind(this)); + }, + + 'setupDOM': function () { + if (this.params.label !== undefined) { + this.label.set('html', this.params.label); + this.label.inject(this.bundle, 'top'); + this.params.searchString += this.params.label + '•'; + } + }, + + 'addEvents': function () { + this.bundle.addEvent('change', (function (event) { + if (this.params.name !== undefined) { + settings.set(this.params.name, this.get()); } - }); + + this.fireEvent('action', this.get()); + }).bind(this)); + }, + + 'get': function () { + var checkedEl = this.elements.filter((function (el) { + return el.get('checked'); + }).bind(this)); + return (checkedEl[0] && checkedEl[0].get('value')); + }, + + 'set': function (value, noChangeEvent) { + var desiredEl = this.elements.filter((function (el) { + return (el.get('value') === value); + }).bind(this)); + desiredEl[0] && desiredEl[0].set('checked', true); + + if (noChangeEvent !== true) { + this.bundle.fireEvent('change'); + } + + return this; + } + }); + + this.Setting = new Class({ + 'initialize': function (container) { + this.container = container; + }, + + 'create': function (params) { + var types, + bundle; + + // Available types + types = { + 'description' : 'Description', + 'button' : 'Button', + 'text' : 'Text', + 'textarea' : 'Textarea', + 'checkbox' : 'Checkbox', + 'slider' : 'Slider', + 'popupButton' : 'PopupButton', + 'listBox' : 'ListBox', + 'radioButtons': 'RadioButtons', + 'modalButton' : 'ModalButton', + //'fileButton': 'FileButton' + }; + + if (types.hasOwnProperty(params.type)) { + bundle = new Bundle[types[params.type]](params); + bundle.bundleContainer = this.container; + bundle.bundle.inject(this.container); + return bundle; + } else { + throw 'invalidType'; + } + } + }); }()); diff --git a/source/js/classes/tab.js b/source/js/classes/tab.js index aafd3ef..92c64a1 100755 --- a/source/js/classes/tab.js +++ b/source/js/classes/tab.js @@ -4,48 +4,50 @@ // License: LGPL v2.1 // (function () { - var Bundle = new Class({ - "initialize": function (creator) { - this.creator = creator; - - // Create DOM elements - this.tab = new Element("div", {"class": "tab"}); - this.content = new Element("div", {"class": "tab-content"}); - - // Create event handlers - this.tab.addEvent("click", this.activate.bind(this)); - }, - - "activate": function () { - if (this.creator.activeBundle && this.creator.activeBundle !== this) { - this.creator.activeBundle.deactivate(); - } - this.tab.addClass("active"); - this.content.addClass("show"); - this.creator.activeBundle = this; - }, - - "deactivate": function () { - this.tab.removeClass("active"); - this.content.removeClass("show"); - this.creator.activeBundle = null; - } - }); - - this.Tab = new Class({ - "activeBundle": null, - - "initialize": function (tabContainer, tabContentContainer) { - this.tabContainer = tabContainer; - this.tabContentContainer = tabContentContainer; - }, - - "create": function () { - var bundle = new Bundle(this); - bundle.tab.inject(this.tabContainer); - bundle.content.inject(this.tabContentContainer); - if (!this.activeBundle) { bundle.activate(); } - return bundle; - } - }); + var Bundle = new Class({ + 'initialize': function (creator) { + this.creator = creator; + + // Create DOM elements + this.tab = new Element('div', {'class': 'tab'}); + this.content = new Element('div', {'class': 'tab-content'}); + + // Create event handlers + this.tab.addEvent('click', this.activate.bind(this)); + }, + + 'activate': function () { + if (this.creator.activeBundle && this.creator.activeBundle !== this) { + this.creator.activeBundle.deactivate(); + } + this.tab.addClass('active'); + this.content.addClass('show'); + this.creator.activeBundle = this; + }, + + 'deactivate': function () { + this.tab.removeClass('active'); + this.content.removeClass('show'); + this.creator.activeBundle = null; + } + }); + + this.Tab = new Class({ + 'activeBundle': null, + + 'initialize': function (tabContainer, tabContentContainer) { + this.tabContainer = tabContainer; + this.tabContentContainer = tabContentContainer; + }, + + 'create': function () { + var bundle = new Bundle(this); + bundle.tab.inject(this.tabContainer); + bundle.content.inject(this.tabContentContainer); + if (!this.activeBundle) { + bundle.activate(); + } + return bundle; + } + }); }()); diff --git a/source/js/i18n.js b/source/js/i18n.js index 20ac623..893261b 100755 --- a/source/js/i18n.js +++ b/source/js/i18n.js @@ -4,24 +4,26 @@ // License: LGPL v2.1 // (function () { - var lang = navigator.language; - if (this.i18n === undefined) { this.i18n = {}; } - this.i18n.get = function (value) { - if (value === "lang") { - return lang; - } + var lang = navigator.language; + if (this.i18n === undefined) { + this.i18n = {}; + } + this.i18n.get = function (value) { + if (value === 'lang') { + return lang; + } - if (this.hasOwnProperty(value)) { - value = this[value]; - if (value.hasOwnProperty(lang)) { - return value[lang]; - } else if (value.hasOwnProperty("en")) { - return value["en"]; - } else { - return Object.values(value)[0]; - } - } else { - return value; - } - }; + if (this.hasOwnProperty(value)) { + value = this[value]; + if (value.hasOwnProperty(lang)) { + return value[lang]; + } else if (value.hasOwnProperty('en')) { + return value['en']; + } else { + return Object.values(value)[0]; + } + } else { + return value; + } + }; }()); diff --git a/source/lib/store.js b/source/lib/store.js index 3d0efab..1e8061a 100644 --- a/source/lib/store.js +++ b/source/lib/store.js @@ -4,96 +4,102 @@ // License: MIT-license // (function () { - var Store = this.Store = function (name, defaults) { - var key; - this.name = name; - - if (defaults !== undefined) { - for (key in defaults) { - if (defaults.hasOwnProperty(key) && this.get(key) === undefined) { - this.set(key, defaults[key]); - } - } + var Store = this.Store = function (name, defaults) { + var key; + this.name = name; + + if (defaults !== undefined) { + for (key in defaults) { + if (defaults.hasOwnProperty(key) && this.get(key) === undefined) { + this.set(key, defaults[key]); } - }; - - Store.prototype.get = function (name) { - name = "store." + this.name + "." + name; - if (localStorage.getItem(name) === null) { return undefined; } + } + } + }; + + Store.prototype.get = function (name) { + name = 'store.' + this.name + '.' + name; + if (localStorage.getItem(name) === null) { + return undefined; + } + try { + return JSON.parse(localStorage.getItem(name)); + } catch (e) { + return null; + } + }; + + Store.prototype.set = function (name, value) { + if (value === undefined) { + this.remove(name); + } else { + if (typeof value === 'function') { + value = null; + } else { try { - return JSON.parse(localStorage.getItem(name)); + value = JSON.stringify(value); } catch (e) { - return null; + value = null; } - }; - - Store.prototype.set = function (name, value) { - if (value === undefined) { - this.remove(name); - } else { - if (typeof value === "function") { - value = null; - } else { - try { - value = JSON.stringify(value); - } catch (e) { - value = null; - } - } - - localStorage.setItem("store." + this.name + "." + name, value); + } + + localStorage.setItem('store.' + this.name + '.' + name, value); + } + + return this; + }; + + Store.prototype.remove = function (name) { + localStorage.removeItem('store.' + this.name + '.' + name); + return this; + }; + + Store.prototype.removeAll = function () { + var name, + i; + + name = 'store.' + this.name + '.'; + for (i = (localStorage.length - 1); i >= 0; i--) { + if (localStorage.key(i).substring(0, name.length) === name) { + localStorage.removeItem(localStorage.key(i)); + } + } + + return this; + }; + + Store.prototype.toObject = function () { + var values, + name, + i, + key, + value; + + values = {}; + name = 'store.' + this.name + '.'; + for (i = (localStorage.length - 1); i >= 0; i--) { + if (localStorage.key(i).substring(0, name.length) === name) { + key = localStorage.key(i).substring(name.length); + value = this.get(key); + if (value !== undefined) { + values[key] = value; } - - return this; - }; - - Store.prototype.remove = function (name) { - localStorage.removeItem("store." + this.name + "." + name); - return this; - }; - - Store.prototype.removeAll = function () { - var name, - i; - - name = "store." + this.name + "."; - for (i = (localStorage.length - 1); i >= 0; i--) { - if (localStorage.key(i).substring(0, name.length) === name) { - localStorage.removeItem(localStorage.key(i)); - } - } - - return this; - }; - - Store.prototype.toObject = function () { - var values, - name, - i, - key, - value; - - values = {}; - name = "store." + this.name + "."; - for (i = (localStorage.length - 1); i >= 0; i--) { - if (localStorage.key(i).substring(0, name.length) === name) { - key = localStorage.key(i).substring(name.length); - value = this.get(key); - if (value !== undefined) { values[key] = value; } - } - } - - return values; - }; - - Store.prototype.fromObject = function (values, merge) { - if (merge !== true) { this.removeAll(); } - for (var key in values) { - if (values.hasOwnProperty(key)) { - this.set(key, values[key]); - } - } - - return this; - }; + } + } + + return values; + }; + + Store.prototype.fromObject = function (values, merge) { + if (merge !== true) { + this.removeAll(); + } + for (var key in values) { + if (values.hasOwnProperty(key)) { + this.set(key, values[key]); + } + } + + return this; + }; }()); diff --git a/source/manifest.js b/source/manifest.js index 5adaa82..428f824 100755 --- a/source/manifest.js +++ b/source/manifest.js @@ -1,172 +1,172 @@ // SAMPLE this.manifest = { - "name": "My Extension", - "icon": "icon.png", - "settings": [ - { - tab: 'Manage Keys', - name: 'storedKeys', - type: 'listBox', - multiple: true, - label: 'Add all keys required for encryption/decryption of all passwords in your password store.', - group: 'Keys', - options: [] - }, - { - tab: 'Manage Keys', - name: 'addKey', - type: 'modalButton', - group: 'Keys', - text: 'Add Key...', - modal: { - title: 'Add Key', - contents: [ - { - name : 'keyName', - type : 'text', - label: 'Key name (email address):' - }, - { - name : 'keyText', - type : 'textarea', - label: 'Paste your gpg private key here:' - } - ] - } - }, - { - tab: 'Manage Keys', - name: 'removeKey', - type: 'button', - group: 'Keys', - text: 'Remove Selected Key(s)' - }, - { - "tab": i18n.get("information"), - "group": i18n.get("login"), - "name": "username", - "type": "text", - "label": i18n.get("username"), - "text": i18n.get("x-characters") - }, - { - "tab": i18n.get("information"), - "group": i18n.get("login"), - "name": "password", - "type": "text", - "label": i18n.get("password"), - "text": i18n.get("x-characters-pw"), - "masked": true - }, - { - "tab": i18n.get("information"), - "group": i18n.get("login"), - "name": "myDescription", - "type": "description", - "text": i18n.get("description") - }, - { - "tab": i18n.get("information"), - "group": i18n.get("logout"), - "name": "myCheckbox", - "type": "checkbox", - "label": i18n.get("enable") - }, - { - "tab": i18n.get("information"), - "group": i18n.get("logout"), - "name": "myButton", - "type": "button", - "label": i18n.get("disconnect"), - "text": i18n.get("logout") - }, - { - "tab": "Details", - "group": "Sound", - "name": "noti_volume", - "type": "slider", - "label": "Notification volume:", - "max": 1, - "min": 0, - "step": 0.01, - "display": true, - "displayModifier": function (value) { - return (value * 100).floor() + "%"; - } - }, - { - "tab": "Details", - "group": "Sound", - "name": "sound_volume", - "type": "slider", - "label": "Sound volume:", - "max": 100, - "min": 0, - "step": 1, - "display": true, - "displayModifier": function (value) { - return value + "%"; - } - }, - { - "tab": "Details", - "group": "Food", - "name": "myPopupButton", - "type": "popupButton", - "label": "Soup 1 should be:", - "options": { - "groups": [ - "Hot", "Cold", - ], - "values": [ - { - "value": "hot", - "text": "Very hot", - "group": "Hot", - }, - { - "value": "Medium", - "group": 1, - }, - { - "value": "Cold", - "group": 2, - }, - ["Non-existing"] - ], - }, - }, - { - "tab": "Details", - "group": "Food", - "name": "myListBox", - "type": "listBox", - "label": "Soup 2 should be:", - "options": [ - ["hot", "Hot and yummy"], - ["cold"] - ] - }, - { - "tab": "Details", - "group": "Food", - "name": "myRadioButtons", - "type": "radioButtons", - "label": "Soup 3 should be:", - "options": [ - ["hot", "Hot and yummy"], - ["cold"] - ] - } - ], - "alignment": [ - [ - "username", - "password" - ], - [ - "noti_volume", - "sound_volume" + name : 'My Extension', + icon : 'icon.png', + settings : [ + { + tab : 'Manage Keys', + name : 'storedKeys', + type : 'listBox', + multiple: true, + label : 'Add all keys required for encryption/decryption of all passwords in your password store.', + group : 'Keys', + options : [] + }, + { + tab : 'Manage Keys', + name : 'addKey', + type : 'modalButton', + group: 'Keys', + text : 'Add Key...', + modal: { + title : 'Add Key', + contents: [ + { + name : 'keyName', + type : 'text', + label: 'Key name (email address):' + }, + { + name : 'keyText', + type : 'textarea', + label: 'Paste your gpg private key here:' + } ] + } + }, + { + tab : 'Manage Keys', + name : 'removeKey', + type : 'button', + group: 'Keys', + text : 'Remove Selected Key(s)' + }, + { + tab : i18n.get('information'), + group: i18n.get('login'), + name : 'username', + type : 'text', + label: i18n.get('username'), + text : i18n.get('x-characters') + }, + { + tab : i18n.get('information'), + group : i18n.get('login'), + name : 'password', + type : 'text', + label : i18n.get('password'), + text : i18n.get('x-characters-pw'), + masked: true + }, + { + tab : i18n.get('information'), + group: i18n.get('login'), + name : 'myDescription', + type : 'description', + text : i18n.get('description') + }, + { + tab : i18n.get('information'), + group: i18n.get('logout'), + name : 'myCheckbox', + type : 'checkbox', + label: i18n.get('enable') + }, + { + tab : i18n.get('information'), + group: i18n.get('logout'), + name : 'myButton', + type : 'button', + label: i18n.get('disconnect'), + text : i18n.get('logout') + }, + { + tab : 'Details', + group : 'Sound', + name : 'noti_volume', + type : 'slider', + label : 'Notification volume:', + max : 1, + min : 0, + step : 0.01, + display : true, + displayModifier: function (value) { + return (value * 100).floor() + '%'; + } + }, + { + tab : 'Details', + group : 'Sound', + name : 'sound_volume', + type : 'slider', + label : 'Sound volume:', + max : 100, + min : 0, + step : 1, + display : true, + displayModifier: function (value) { + return value + '%'; + } + }, + { + tab : 'Details', + group : 'Food', + name : 'myPopupButton', + type : 'popupButton', + label : 'Soup 1 should be:', + options: { + groups: [ + 'Hot', 'Cold', + ], + values: [ + { + value: 'hot', + text : 'Very hot', + group: 'Hot', + }, + { + value: 'Medium', + group: 1, + }, + { + value: 'Cold', + group: 2, + }, + ['Non-existing'] + ], + }, + }, + { + tab : 'Details', + group : 'Food', + name : 'myListBox', + type : 'listBox', + label : 'Soup 2 should be:', + options: [ + ['hot', 'Hot and yummy'], + ['cold'] + ] + }, + { + tab : 'Details', + group : 'Food', + name : 'myRadioButtons', + type : 'radioButtons', + label : 'Soup 3 should be:', + options: [ + ['hot', 'Hot and yummy'], + ['cold'] + ] + } + ], + alignment: [ + [ + 'username', + 'password' + ], + [ + 'noti_volume', + 'sound_volume' ] + ] }; diff --git a/source/settings.js b/source/settings.js index 196e17a..b473326 100755 --- a/source/settings.js +++ b/source/settings.js @@ -1,114 +1,114 @@ -window.addEvent("domready", function () { - // Option 1: Use the manifest: - new FancySettings.initWithManifest(function (settings) { - //-- register 'action' event callback on the `myButton` setting from ./manifest:78 - settings.manifest.myButton.addEvent("action", function () { - alert("You clicked me!"); - }); - - //-- register 'action' event callback on the `myButton` setting from ./manifest:78 - settings.manifest.removeKey.addEvent('action', function(){ - var removeIds = []; - - [].slice.call(settings.manifest.storedKeys.element.options).forEach(function(option){ - if (option.selected) removeIds.push(option.value) - }); - - options.keys = options.keys.filter(function(key){ - return removeIds.every(function(id){ - return id !== key.id; - }) - }); - - chrome.storage.sync.set({options: options}, function(){ - location.reload(); - }) - }); - - //-- register 'modal_done' event callback on the `addKey` setting from ./manifest:17 - settings.manifest.addKey.addEvent('modal_done', function() { - var keyName = JSON.parse(localStorage.getItem('store.settings.keyName')) - , keyText = JSON.parse(localStorage.getItem('store.settings.keyText')) - ; - - //-- error if keyName or keyText is empty - if (!keyName || !keyText) { - return alert('Key name and key text must be provided!'); - } - - //-- add key to encrypted storage - var newKey = { - email: keyName, - id : keyName - }; - - if (options.keys && options.keys.length > 0) { - options.keys.push(newKey); - } else { - options.keys = [newKey] - } - - //-- persist options from setting page in `chrome.storage` (see https://developer.chrome.com/extensions/storage) - chrome.storage.sync.set({options: options}, function () { - //-- cleanup entered key from localStorage - localStorage.removeItem('store.settings.keyName'); - localStorage.removeItem('store.settings.keyText'); - - //-- reload page so storage retrieval can happen before `FancySettings` is init'ed - location.reload(); - }) - }) - }); - - // Option 2: Do everything manually: - /* - var settings = new FancySettings("My Extension", "icon.png"); - - var username = settings.create({ - "tab": i18n.get("information"), - "group": i18n.get("login"), - "name": "username", - "type": "text", - "label": i18n.get("username"), - "text": i18n.get("x-characters") - }); - - var password = settings.create({ - "tab": i18n.get("information"), - "group": i18n.get("login"), - "name": "password", - "type": "text", - "label": i18n.get("password"), - "text": i18n.get("x-characters-pw"), - "masked": true +window.addEvent('domready', function () { + // Option 1: Use the manifest: + new FancySettings.initWithManifest(function (settings) { + //-- register 'action' event callback on the `myButton` setting from ./manifest:78 + settings.manifest.myButton.addEvent('action', function () { + alert('You clicked me!'); }); - - var myDescription = settings.create({ - "tab": i18n.get("information"), - "group": i18n.get("login"), - "name": "myDescription", - "type": "description", - "text": i18n.get("description") - }); - - var myButton = settings.create({ - "tab": "Information", - "group": "Logout", - "name": "myButton", - "type": "button", - "label": "Disconnect:", - "text": "Logout" - }); - - // ... - - myButton.addEvent("action", function () { - alert("You clicked me!"); + + //-- register 'action' event callback on the `myButton` setting from ./manifest:78 + settings.manifest.removeKey.addEvent('action', function () { + var removeIds = []; + + [].slice.call(settings.manifest.storedKeys.element.options).forEach(function (option) { + if (option.selected) removeIds.push(option.value) + }); + + options.keys = options.keys.filter(function (key) { + return removeIds.every(function (id) { + return id !== key.id; + }) + }); + + chrome.storage.sync.set({options: options}, function () { + location.reload(); + }) }); - - settings.align([ - username, - password - ]); - */ + + //-- register 'modal_done' event callback on the `addKey` setting from ./manifest:17 + settings.manifest.addKey.addEvent('modal_done', function () { + var keyName = JSON.parse(localStorage.getItem('store.settings.keyName')) + , keyText = JSON.parse(localStorage.getItem('store.settings.keyText')) + ; + + //-- error if keyName or keyText is empty + if (!keyName || !keyText) { + return alert('Key name and key text must be provided!'); + } + + //-- add key to encrypted storage + var newKey = { + email: keyName, + id : keyName + }; + + if (options.keys && options.keys.length > 0) { + options.keys.push(newKey); + } else { + options.keys = [newKey] + } + + //-- persist options from setting page in `chrome.storage` (see https://developer.chrome.com/extensions/storage) + chrome.storage.sync.set({options: options}, function () { + //-- cleanup entered key from localStorage + localStorage.removeItem('store.settings.keyName'); + localStorage.removeItem('store.settings.keyText'); + + //-- reload page so storage retrieval can happen before `FancySettings` is init'ed + location.reload(); + }) + }) + }); + + // Option 2: Do everything manually: + /* + var settings = new FancySettings('My Extension', 'icon.png'); + + var username = settings.create({ + tab : i18n.get('information'), + group: i18n.get('login'), + name : 'username', + type : 'text', + label: i18n.get('username'), + text : i18n.get('x-characters') + }); + + var password = settings.create({ + tab : i18n.get('information'), + group : i18n.get('login'), + name : 'password', + type : 'text', + label : i18n.get('password'), + text : i18n.get('x-characters-pw'), + masked: true + }); + + var myDescription = settings.create({ + tab : i18n.get('information'), + group: i18n.get('login'), + name : 'myDescription', + type : 'description', + text : i18n.get('description') + }); + + var myButton = settings.create({ + tab : 'Information', + group: 'Logout', + name : 'myButton', + type : 'button', + label: 'Disconnect:', + text : 'Logout' + }); + + // ... + + myButton.addEvent('action', function () { + alert('You clicked me!'); + }); + + settings.align([ + username, + password + ]); + */ }); From 8bf6cb6f22fa59c3f8ab5110f2eb28040ca715ce Mon Sep 17 00:00:00 2001 From: Bryan White Date: Sat, 16 Apr 2016 04:43:17 +0200 Subject: [PATCH 5/8] documentation WIP --- README.md | 118 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9fe20c9..cecd1b9 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,113 @@ -# [Fancy Settings 1.2](https://github.com/frankkohlhepp/fancy-settings) +Fancy Settings 1.2 +================== + *Create fancy, chrome-look-alike settings for your Chrome or Safari extension in minutes!* -### Howto -Welcome to Fancy Settings! Are you ready for tabs, groups, search, good style? -Let's get started, it only takes a few minutes... -[Getting started](https://github.com/frankkohlhepp/fancy-settings/wiki) -[View Sample](http://frankkohlhepp.github.com/fancy-settings/) +Rationale +--------- + +The goal of this project is to provide a simple way to generate chrome-like settings pages for use in projects like +chrome extensions. Settings generation is done all via a javascript object, the "manifest", and event binding can be +easily customized via javascript. + +Ideally this framework contains enough variety of setting types that one only needs to edit [the "Manifest" +(`/source/manifest.js`)](#the-manifest) and the [settings initialization script (`/source/settings.js`)](#settings-initialization) +such that + + +How It Works +--------------- + +#### Project Structure +``` +├── css ─────────────────────────├─( framework css; if you're extending the framework you should add to these ) +│   ├── main.css ────────────────├─( main layout ) +│   └── setting.css ─────────────├─( styles for each "setting" (ListBox, Button, etc.) ) +├── custom.css ──────────────────├─( your css should go here, probably overriding default styles ) +├── i18n.js ─────────────────────├─( your internationalization data ) +├── icon.png ────────────────────├─( favicon shown on the settings tab in chrome ) +├── index.html ──────────────────├─( index page; loads all javascript and establishes main layout ) +├── js ──────────────────────────├─( framework javascript; if you're extending the framework you should add to these ) +│   ├── classes ─────────────────├─( mootools-backed framework classes ) +│   │   ├── fancy-settings.js ├─( main entry point; contains `FancySettings.initWithManifest` function, used to +│ │ │ │ build all settings and add them to the DOM ) +│   │   ├── search.js ───────────├─( provides management interface for the search index ) +│   │   ├── setting.js ──────────├─( classes for all setting types (e.g. ListBox, Button, etc.) and the Setting class +│ │ │ │ itself; includes DOM creation and event logic ) +│   │   └── tab.js ──────────────├─( class for `Tab`; includes DOM creation and tab switching ) +│   └── i18n.js ─────────────────├─( internationalization interface; retrieves i18n values registered in `/source/i18n.js` ) +├── lib ─────────────────────────├─( dependencies +│   ├── default.css ─────────────├─( base css for elements and utility classes ) +│   ├── mootools-core.js ────────├─( mootools ) +│   └── store.js ────────────────├─( localStorage interface ) +├── manifest.js ─────────────────├─( your settings manifest; see [The "Manifest"](#the--manifest) +└── settings.js ─────────────────└─( your settings pre-initialization; `FancySettings.initWithManifest` is called here + after any prerequisite async event (e.g. domready, retrieving values from + `chrome.storage`, etc.); see [Settings Initialization](#settings-initialization) ) +``` + +#### The "Manifest" +The "Manifest" (`/source/manifest.js`) is a simple javascript file with registers a global object: `manifest`. This +object contains the following properties: + +* `name`: Name of the manifest +* `icon`: Filename of the favicon to show for the options tab in chrome +* `alignment` _(optional)_: _WIP - not sure how to explain this yet_ +* `settings`: + + An array of a "flattened" settings structure; each element in this array describes one setting. All settings, + regardless of type have the following attributes: + + * `tab`: The name (and text) of the tab which this setting will belong to; multiple settings will use the same value here to be on the same tab + * `name`: The name of this setting; this name will be used to reference it later via javascript, usually as the key of an object + * `type`: The type of setting, see [setting types](#setting-types) below + * `label` _(optional)_: The text of a `