diff --git a/background.html b/background.html
index eadf145..7ffa114 100644
--- a/background.html
+++ b/background.html
@@ -3,6 +3,7 @@
Background Processes
+
diff --git a/background.js b/background.js
index c2d9eee..63e1d56 100644
--- a/background.js
+++ b/background.js
@@ -1,6 +1,8 @@
var db = openDatabase('mhdb', '1.0', 'Mostly Harmless Database', 5 * 1024 * 1024);
-var cacheTime;
-var over18;
+var settings = new Store("settings", {
+ "cacheTime": 1,
+ "freshCutoff": 90
+}).toObject();
var commentsMatchPattern = /https?:\/\/www\.reddit\.com(\/r\/(.+?))?\/comments\/(.+?)\/.*/;
init();
@@ -11,14 +13,6 @@ function init() {
installDefaults();
}
db.transaction(function(tx){
- // Load preferences
- tx.executeSql('SELECT * FROM prefs WHERE pref=?', ['cacheTime'], function(tx, results) {
- cacheTime = results.rows.item(0).choice;
- });
- tx.executeSql('SELECT * FROM prefs WHERE pref=?', ['over18'], function(tx, results) {
- over18 = results.rows.item(0).choice;
- });
-
// Clear cache and post data
tx.executeSql('DELETE FROM cache');
tx.executeSql('DELETE FROM posts');
@@ -28,11 +22,8 @@ function init() {
function installDefaults(tx) {
window.localStorage.setItem('installed','true');
db.transaction(function(tx) {
- tx.executeSql('CREATE TABLE IF NOT EXISTS cache (pageUrl unique, cacheTime, howManyPosts)');
+ tx.executeSql('CREATE TABLE IF NOT EXISTS cache (pageUrl unique, cacheDate, howManyPosts)');
tx.executeSql('CREATE TABLE IF NOT EXISTS posts (id unique, name, likes, domain, subreddit, author, score, over_18, hidden, thumbnail, downs, permalink, created_utc, url, title, num_comments, ups, modhash)')
- tx.executeSql('CREATE TABLE IF NOT EXISTS prefs (pref unique, choice)');
- tx.executeSql('INSERT INTO prefs (pref, choice) VALUES (?, ?)', ['cacheTime','1']);
- tx.executeSql('INSERT INTO prefs (pref, choice) VALUES (?, ?)', ['over18','false']);
});
}
@@ -77,7 +68,7 @@ function grabData(url,tabId) {
tx.executeSql('SELECT * FROM cache WHERE pageUrl=?', [url], function(tx, results) {
var cache = results.rows;
var isCommentsPage = commentsMatchPattern.test(url);
- if(cache.length === 0 || -(cache.item(0).cacheTime - epoch()) > 60 * cacheTime ) { // cacheTime in minutes
+ if(cache.length === 0 || -(cache.item(0).cacheDate - epoch()) > 60 * settings.cacheTime ) {
console.log('Loading from reddit api...');
var reqUrl = new String();
if(isCommentsPage) {
@@ -108,28 +99,37 @@ function epoch() {
}
function cacheData(response,pageUrl,tabId,isCommentsPage) {
- // add response to cache to reduce API calls
- if(response.data.children.length === 0) {
+ var wasCached = false;
+ var freshPosts = [];
+ for(var i = 0; i < response.data.children.length; i++) {
+ var data = response.data.children[i].data;
+ var isFreshEnough = data.created_utc >= epoch() - settings.freshCutoff * 24 * 60 * 60;
+ if(isFreshEnough) {
+ wasCached = true;
+ freshPosts.push(data);
+ }
+ }
+ if(wasCached === true) {
db.transaction(function(tx) {
- tx.executeSql('INSERT OR REPLACE INTO cache (pageUrl, cacheTime, howManyPosts) VALUES (?, ?, ?)',[pageUrl, epoch(), '0',]);
- });
- } else {
- db.transaction(function(tx) {
- for(var i = 0; i < response.data.children.length; i++) {
- var data = response.data.children[i].data;
- var insertUrl = isCommentsPage ? 'http://www.reddit.com' + data.permalink : data.url;
- var insertNum = isCommentsPage ? '...' : response.data.children.length;
+ var insertNum = isCommentsPage ? '...' : freshPosts.length;
+ tx.executeSql('INSERT OR REPLACE INTO cache (pageUrl, cacheDate, howManyPosts) VALUES (?, ?, ?)',[pageUrl, epoch(), freshPosts.length]);
+ for(var j = freshPosts.length - 1; j >= 0; j--) {
+ var insertUrl = isCommentsPage ? 'http://www.reddit.com' + freshPosts[j].permalink : freshPosts[j].url.split('#')[0];
tx.executeSql(
'INSERT OR REPLACE INTO posts' +
'(id, name, likes, domain, subreddit, author, score, over_18, hidden, thumbnail, downs, permalink, created_utc, url, title, num_comments, ups, modhash)' +
'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
- [data.id, data.name, data.likes, data.domain, data.subreddit, data.author, data.score, data.over_18, data.hidden, data.thumbnail, data.downs, data.permalink, data.created_utc, insertUrl, data.title, data.num_comments, data.ups, response.data.modhash],
- function(tx) {
- tx.executeSql('INSERT OR REPLACE INTO cache (pageUrl, cacheTime, howManyPosts) VALUES (?, ?, ?)',[pageUrl, epoch(), insertNum]);
- });
+ [freshPosts[j].id, freshPosts[j].name, freshPosts[j].likes, freshPosts[j].domain, freshPosts[j].subreddit, freshPosts[j].author, freshPosts[j].score, freshPosts[j].over_18, freshPosts[j].hidden, freshPosts[j].thumbnail, freshPosts[j].downs, freshPosts[j].permalink, freshPosts[j].created_utc, insertUrl, freshPosts[j].title, freshPosts[j].num_comments, freshPosts[j].ups, response.data.modhash]
+ );
}
});
+ } else {
+ db.transaction(function(tx) {
+ tx.executeSql('INSERT OR REPLACE INTO cache (pageUrl, cacheDate, howManyPosts) VALUES (?, ?, ?)',[pageUrl, epoch(), '0',]);
+ });
}
+ console.log(freshPosts);
+
preparePopup(pageUrl,tabId);
}
diff --git a/fancy-settings/css/main.css b/fancy-settings/css/main.css
new file mode 100755
index 0000000..5232eb7
--- /dev/null
+++ b/fancy-settings/css/main.css
@@ -0,0 +1,132 @@
+/*
+// Copyright (c) 2011 Frank Kohlhepp
+// https://github.com/frankkohlhepp/fancy-settings
+// License: LGPL v2.1
+*/
+.fancy {
+ text-shadow: #F5F5F5 0 1px 0;
+}
+
+#sidebar {
+ position: absolute;
+ background-color: #EDEDED;
+ background-image: linear-gradient(top, #EDEDED, #F5F5F5);
+ background-image: -webkit-gradient(
+ linear,
+ left top,
+ left 500,
+ color-stop(0, #EDEDED),
+ color-stop(1, #F5F5F5)
+ );
+ background-image: -moz-linear-gradient(
+ center top,
+ #EDEDED 0%,
+ #F5F5F5 100%
+ );
+ background-image: -o-linear-gradient(top, #EDEDED, #F5F5F5);
+ width: 219px;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ border-right: 1px solid #C2C2C2;
+ box-shadow: inset -8px 0 30px -30px black;
+}
+
+#icon {
+ position: absolute;
+ width: 30px;
+ height: 30px;
+ top: 12px;
+ left: 12px;
+}
+
+#sidebar h1 {
+ position: absolute;
+ top: 13px;
+ right: 25px;
+ font-size: 26px;
+ color: #707070;
+}
+
+#tab-container {
+ position: absolute;
+ top: 50px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ overflow-y: auto;
+ overflow-x: hidden;
+ text-align: right;
+}
+
+#tab-container .tab {
+ height: 28px;
+ padding-right: 25px;
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+ font-size: 12px;
+ line-height: 28px;
+ color: #808080;
+ cursor: pointer;
+}
+
+#search-container {
+ margin-top: 5px;
+ margin-bottom: 5px;
+ padding-right: 23px !important;
+ cursor: default !important;
+}
+
+#search-container input {
+ width: 130px;
+}
+
+#tab-container .tab.active, body.searching #search-container {
+ background-color: #D4D4D4;
+ border-color: #BFBFBF;
+ color: black;
+ text-shadow: #DBDBDB 0 1px 0;
+ box-shadow: inset -12px 0 30px -30px black;
+}
+
+body.searching #tab-container .tab.active {
+ background-color: transparent;
+ border-color: transparent;
+ color: #808080;
+ text-shadow: inherit;
+ box-shadow: none;
+}
+
+#content {
+ position: absolute;
+ top: 0;
+ left: 220px;
+ right: 0;
+ bottom: 0;
+ overflow: auto;
+}
+
+.tab-content {
+ display: none;
+ position: absolute;
+ width: 840px;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ padding: 20px;
+ padding-top: 15px;
+}
+
+body.searching .tab-content {
+ display: none !important;
+}
+
+body.searching #search-result-container {
+ display: block !important;
+}
+
+body.measuring .tab-content, body.measuring #search-result-container {
+ display: block !important;
+ opacity: 0;
+ overflow: hidden;
+}
diff --git a/fancy-settings/css/setting.css b/fancy-settings/css/setting.css
new file mode 100755
index 0000000..58a5388
--- /dev/null
+++ b/fancy-settings/css/setting.css
@@ -0,0 +1,81 @@
+/*
+// Copyright (c) 2011 Frank Kohlhepp
+// https://github.com/frankkohlhepp/fancy-settings
+// License: LGPL v2.1
+*/
+.tab-content h2 {
+ margin: 0;
+ padding-bottom: 5px;
+ font-size: 26px;
+ color: #707070;
+ line-height: 1;
+}
+
+.setting.group {
+ border-top: 1px solid #EEEEEE;
+ margin-top: 10px;
+ padding-top: 5px;
+ padding-left: 2px;
+}
+
+.setting.group-name {
+ width: 140px;
+ padding: 0;
+ font-size: 14px;
+ font-weight: bold;
+ vertical-align: top;
+}
+
+.setting.bundle {
+ max-width: 600px;
+ margin-bottom: 5px;
+}
+
+.setting.bundle.list-box {
+ margin-bottom: 10px;
+}
+
+.setting.label.radio-buttons + .setting.container.radio-buttons {
+ margin-top: 3px;
+}
+
+.setting.label, .setting.element-label {
+ margin-right: 15px;
+ font-size: 13px;
+ font-weight: normal;
+}
+
+.setting.label.checkbox, .setting.element-label {
+ margin-left: 5px;
+ margin-right: 0;
+}
+
+.setting.label.checkbox {
+ position: relative;
+ top: 1px;
+}
+
+.setting.element.slider {
+ position: relative;
+ width: 150px;
+ top: 4px;
+}
+
+.setting.element.list-box {
+ display: block;
+ height: 100px;
+ width: 100%;
+}
+
+.setting.display.slider {
+ margin-left: 5px;
+ color: #666666;
+}
+
+#nothing-found {
+ display: none;
+ margin-top: 10px;
+ font-size: 18px;
+ font-weight: lighter;
+ color: #999999;
+}
diff --git a/fancy-settings/custom.css b/fancy-settings/custom.css
new file mode 100755
index 0000000..8404e6a
--- /dev/null
+++ b/fancy-settings/custom.css
@@ -0,0 +1,13 @@
+/*
+// Add your own style rules here, not in css/main.css
+// or css/setting.css for easy updating reasons.
+*/
+#icon {
+ width: 19px;
+ height: 19px;
+ top: 15px;
+ left: 17px;
+}
+textarea {
+ font-family: Consolas, "Lucida Console", Monaco, monospace
+}
\ No newline at end of file
diff --git a/fancy-settings/i18n.js b/fancy-settings/i18n.js
new file mode 100755
index 0000000..13d7ebb
--- /dev/null
+++ b/fancy-settings/i18n.js
@@ -0,0 +1,71 @@
+// 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.
\
+ 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.
\
+ 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:"
+ }
+};
diff --git a/fancy-settings/icon.png b/fancy-settings/icon.png
new file mode 100644
index 0000000..9ffd4eb
Binary files /dev/null and b/fancy-settings/icon.png differ
diff --git a/fancy-settings/index.html b/fancy-settings/index.html
new file mode 100755
index 0000000..ec72db6
--- /dev/null
+++ b/fancy-settings/index.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fancy-settings/js/classes/fancy-settings.js b/fancy-settings/js/classes/fancy-settings.js
new file mode 100644
index 0000000..5c0223f
--- /dev/null
+++ b/fancy-settings/js/classes/fancy-settings.js
@@ -0,0 +1,152 @@
+//
+// Copyright (c) 2011 Frank Kohlhepp
+// https://github.com/frankkohlhepp/fancy-settings
+// 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");
+ }
+ }
+ });
+ }
+ });
+
+ 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/fancy-settings/js/classes/search.js b/fancy-settings/js/classes/search.js
new file mode 100755
index 0000000..7278219
--- /dev/null
+++ b/fancy-settings/js/classes/search.js
@@ -0,0 +1,126 @@
+//
+// Copyright (c) 2011 Frank Kohlhepp
+// https://github.com/frankkohlhepp/fancy-settings
+// 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("");
+ }
+ });
+}());
diff --git a/fancy-settings/js/classes/setting.js b/fancy-settings/js/classes/setting.js
new file mode 100755
index 0000000..cf57f51
--- /dev/null
+++ b/fancy-settings/js/classes/setting.js
@@ -0,0 +1,593 @@
+//
+// Copyright (c) 2011 Frank Kohlhepp
+// https://github.com/frankkohlhepp/fancy-settings
+// 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.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();
+
+ 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));
+ }
+ });
+
+ 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);
+ }
+ });
+
+ 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 === true) {
+ 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;
+ }
+ });
+
+ 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; }
+ this.params.options.each((function (option) {
+ this.params.searchString += (option[1] || option[0]) + "•";
+
+ (new Element("option", {
+ "value": option[0],
+ "text": option[1] || option[0]
+ })).inject(this.element);
+ }).bind(this));
+ },
+
+ "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[1] || option[0]) + "•";
+
+ (new Element("option", {
+ "value": option[0],
+ "text": option[1] || option[0]
+ })).inject(this.element);
+ }).bind(this));
+ },
+
+ "get": function () {
+ return (this.element.get("value") || undefined);
+ }
+ });
+
+ 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[1] || option[0]) + "•";
+
+ 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[0]
+ })).inject(container));
+
+ this.labels.push((new Element("label", {
+ "class": "setting element-label radio-buttons",
+ "for": optionID,
+ "text": option[1] || option[0]
+ })).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",
+ "checkbox": "Checkbox",
+ "slider": "Slider",
+ "popupButton": "PopupButton",
+ "listBox": "ListBox",
+ "radioButtons": "RadioButtons"
+ };
+
+ 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/fancy-settings/js/classes/tab.js b/fancy-settings/js/classes/tab.js
new file mode 100755
index 0000000..aafd3ef
--- /dev/null
+++ b/fancy-settings/js/classes/tab.js
@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2011 Frank Kohlhepp
+// https://github.com/frankkohlhepp/fancy-settings
+// 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;
+ }
+ });
+}());
diff --git a/fancy-settings/js/i18n.js b/fancy-settings/js/i18n.js
new file mode 100755
index 0000000..a352634
--- /dev/null
+++ b/fancy-settings/js/i18n.js
@@ -0,0 +1,27 @@
+//
+// Copyright (c) 2011 Frank Kohlhepp
+// https://github.com/frankkohlhepp/fancy-settings
+// License: LGPL v2.1
+//
+(function () {
+ var lang = navigator.language.split("-")[0];
+ 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;
+ }
+ };
+}());
diff --git a/fancy-settings/lib/default.css b/fancy-settings/lib/default.css
new file mode 100644
index 0000000..22e83b3
--- /dev/null
+++ b/fancy-settings/lib/default.css
@@ -0,0 +1,467 @@
+/*
+// Copyright (c) 2007 - 2010 blueprintcss.org
+// Modified and extended by Frank Kohlhepp in 2011
+// https://github.com/frankkohlhepp/default-css
+// License: MIT-license
+*/
+
+/*
+// Reset the default browser CSS
+*/
+html {
+ margin: 0;
+ padding: 0;
+ border: 0;
+}
+
+body, div, span, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, code,
+del, dfn, em, img, q, dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, dialog, figure, footer, header,
+hgroup, nav, section {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-family: inherit;
+ font-size: 100%;
+ font-weight: inherit;
+ font-style: inherit;
+ vertical-align: baseline;
+}
+
+article, aside, dialog, figure, footer, header,
+hgroup, nav, section {
+ display: block;
+}
+
+body {
+ background-color: white;
+ line-height: 1.5;
+}
+
+table {
+ border-collapse: separate;
+ border-spacing: 0;
+}
+
+caption, th, td {
+ text-align: left;
+ font-weight: normal;
+}
+
+table, th, td {
+ vertical-align: middle;
+}
+
+blockquote:before, blockquote:after, q:before, q:after {
+ content: "";
+}
+
+blockquote, q {
+ quotes: "" "";
+}
+
+a img {
+ border: none;
+}
+
+/*
+// Default typography
+*/
+html {
+ font-size: 100.01%;
+}
+
+body {
+ background-color: white;
+ font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
+ font-size: 75%;
+ color: #222222;
+}
+
+/* Headings */
+h1, h2, h3, h4, h5, h6 {
+ font-weight: normal;
+ color: #111111;
+}
+
+h1 {
+ margin-bottom: 0.5em;
+ font-size: 3em;
+ line-height: 1;
+}
+
+h2 {
+ margin-bottom: 0.75em;
+ font-size: 2em;
+}
+
+h3 {
+ margin-bottom: 1em;
+ font-size: 1.5em;
+ line-height: 1;
+}
+
+h4 {
+ margin-bottom: 1.25em;
+ font-size: 1.2em;
+ line-height: 1.25;
+}
+
+h5 {
+ margin-bottom: 1.5em;
+ font-size: 1em;
+ font-weight: bold;
+}
+
+h6 {
+ font-size: 1em;
+ font-weight: bold;
+}
+
+h1 img, h2 img, h3 img,
+h4 img, h5 img, h6 img {
+ margin: 0;
+}
+
+/* Text elements */
+p {
+ margin: 0 0 1.5em;
+}
+
+.left {
+ float: left !important;
+}
+
+p .left {
+ margin: 1.5em 1.5em 1.5em 0;
+ padding: 0;
+}
+
+.right {
+ float: right !important;
+}
+
+p .right {
+ margin: 1.5em 0 1.5em 1.5em;
+ padding: 0;
+}
+
+a:focus, a:hover {
+ color: #0099FF;
+}
+
+a {
+ color: #0066CC;
+ text-decoration: underline;
+}
+
+blockquote {
+ margin: 1.5em;
+ font-style: italic;
+ color: #666666;
+}
+
+strong, dfn {
+ font-weight: bold;
+}
+
+em, dfn {
+ font-style: italic;
+}
+
+sup, sub {
+ line-height: 0;
+}
+
+abbr, acronym {
+ border-bottom: 1px dotted #666666;
+}
+
+address {
+ margin: 0 0 1.5em;
+ font-style: italic;
+}
+
+del {
+ color: #666666;
+}
+
+pre {
+ margin: 1.5em 0;
+ white-space: pre;
+}
+
+pre, code, tt {
+ font: 1em "andale mono", "lucida console", monospace;
+ line-height: 1.5;
+}
+
+/* Lists */
+li ul, li ol {
+ margin: 0;
+}
+
+ul, ol {
+ margin: 0 1.5em 1.5em 0;
+ padding-left: 1.5em;
+}
+
+ul {
+ list-style-type: disc;
+}
+
+ol {
+ list-style-type: decimal;
+}
+
+dl {
+ margin: 0 0 1.5em 0;
+}
+
+dl dt {
+ font-weight: bold;
+}
+
+dd {
+ margin-left: 1.5em;
+}
+
+/* Tables */
+table {
+ width: 100%;
+ margin-bottom: 1.4em;
+}
+
+th {
+ font-weight: bold;
+}
+
+table.zebra thead th, table.zebra tfoot th {
+ background-color: #BFBFBF;
+}
+
+th, td, caption {
+ padding: 4px 10px 4px 5px;
+}
+
+table.zebra tbody tr:nth-child(even) td, table.zebra tbody tr.even td {
+ background-color: #E6E6E6;
+}
+
+caption {
+ background-color: #EEEEEE;
+}
+
+/* Misc classes */
+.fancy {
+ text-shadow: white 0 1px 0;
+}
+
+.bfancy {
+ text-shadow: black 0 1px 0;
+}
+
+.fancyt {
+ text-shadow: white 0 -1px 0;
+}
+
+.bfancyt {
+ text-shadow: black 0 -1px 0;
+}
+
+.no-fancy {
+ text-shadow: none;
+}
+
+.select {
+ cursor: auto;
+ user-select: auto;
+ -webkit-user-select: auto;
+ -moz-user-select: auto;
+ -o-user-select: auto;
+}
+
+img.select, .select img {
+ user-drag: auto;
+ -webkit-user-drag: auto;
+ -moz-user-drag: auto;
+ -o-user-drag: auto;
+}
+
+.no-select {
+ cursor: default;
+ user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -o-user-select: none;
+}
+
+img.no-select, .no-select img {
+ user-drag: none;
+ -webkit-user-drag: none;
+ -moz-user-drag: none;
+ -o-user-drag: none;
+}
+
+.focus:focus, .focus :focus {
+ outline: auto;
+}
+
+.no-focus:focus, .no-focus :focus {
+ outline: 0;
+}
+
+.small {
+ margin-bottom: 1.875em;
+ font-size: .8em;
+ line-height: 1.875em;
+}
+
+.large {
+ margin-bottom: 1.25em;
+ font-size: 1.2em;
+ line-height: 2.5em;
+}
+
+.show {
+ display: block !important;
+}
+
+.show-inline {
+ display: inline-block !important;
+}
+
+.hide {
+ display: none;
+}
+
+.quiet {
+ color: #666666;
+}
+
+.loud {
+ color: black;
+}
+
+.highlight {
+ background-color: yellow;
+}
+
+.added {
+ background-color: #006600;
+ color: white;
+}
+
+.removed {
+ background-color: #990000;
+ color: white;
+}
+
+.first {
+ margin-left: 0;
+ padding-left: 0;
+}
+
+.last {
+ margin-right: 0;
+ padding-right: 0;
+}
+
+.top {
+ margin-top: 0;
+ padding-top: 0;
+}
+
+.bottom {
+ margin-bottom: 0;
+ padding-bottom: 0;
+}
+
+/*
+// Default styling for forms
+*/
+fieldset {
+ margin: 0 0 1.5em 0;
+ padding: 0 1.4em 1.4em 1.4em;
+ border: 1px solid #CCCCCC;
+}
+
+legend {
+ margin-top: -0.2em;
+ margin-bottom: 1em;
+ font-weight: bold;
+ font-size: 1.2em;
+}
+
+/* Form fields */
+input[type=text], input[type=password], textarea {
+ background-color: white;
+ border: 1px solid #BBBBBB;
+}
+
+input[type=text], input[type=password],
+textarea, select {
+ margin: 0.5em 0;
+}
+
+input[type=text], input[type=password] {
+ width: 300px;
+ padding: 4px;
+}
+
+textarea {
+ width: 450px;
+ height: 170px;
+ padding: 5px;
+}
+
+/* success, info, notice and error boxes */
+.success, .info, .notice, .error {
+ margin-bottom: 1em;
+ padding: 0.8em;
+ border: 2px solid #DDDDDD;
+}
+
+.success {
+ background-color: #E6EFC2;
+ border-color: #C6D880;
+ color: #264409;
+}
+
+.info {
+ background-color: #D5EDF8;
+ border-color: #92CAE4;
+ color: #205791;
+}
+
+.notice {
+ background-color: #FFF6BF;
+ border-color: #FFD324;
+ color: #514721;
+}
+
+.error {
+ background-color: #FBE3E4;
+ border-color: #FBC2C4;
+ color: #8A1F11;
+}
+
+.success a {
+ color: #264409;
+}
+
+.info a {
+ color: #205791;
+}
+
+.notice a {
+ color: #514721;
+}
+
+.error a {
+ color: #8A1F11;
+}
diff --git a/fancy-settings/lib/mootools-core.js b/fancy-settings/lib/mootools-core.js
new file mode 100644
index 0000000..cba97ef
--- /dev/null
+++ b/fancy-settings/lib/mootools-core.js
@@ -0,0 +1,5515 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/core/7c56cfef9dddcf170a5d68e3fb61cfd7
+
+packager build:
+ - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff
+
+/*
+---
+
+name: Core
+
+description: The heart of MooTools.
+
+license: MIT-style license.
+
+copyright: Copyright (c) 2006-2010 [Valerio Proietti](http://mad4milk.net/).
+
+authors: The MooTools production team (http://mootools.net/developers/)
+
+inspiration:
+ - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
+ - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
+
+provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
+
+...
+*/
+
+(function(){
+
+this.MooTools = {
+ version: '1.3.2',
+ build: 'c9f1ff10e9e7facb65e9481049ed1b450959d587'
+};
+
+// typeOf, instanceOf
+
+var typeOf = this.typeOf = function(item){
+ if (item == null) return 'null';
+ if (item.$family) return item.$family();
+
+ if (item.nodeName){
+ if (item.nodeType == 1) return 'element';
+ if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
+ } else if (typeof item.length == 'number'){
+ if (item.callee) return 'arguments';
+ if ('item' in item) return 'collection';
+ }
+
+ return typeof item;
+};
+
+var instanceOf = this.instanceOf = function(item, object){
+ if (item == null) return false;
+ var constructor = item.$constructor || item.constructor;
+ while (constructor){
+ if (constructor === object) return true;
+ constructor = constructor.parent;
+ }
+ return item instanceof object;
+};
+
+// Function overloading
+
+var Function = this.Function;
+
+var enumerables = true;
+for (var i in {toString: 1}) enumerables = null;
+if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
+
+Function.prototype.overloadSetter = function(usePlural){
+ var self = this;
+ return function(a, b){
+ if (a == null) return this;
+ if (usePlural || typeof a != 'string'){
+ for (var k in a) self.call(this, k, a[k]);
+ if (enumerables) for (var i = enumerables.length; i--;){
+ k = enumerables[i];
+ if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
+ }
+ } else {
+ self.call(this, a, b);
+ }
+ return this;
+ };
+};
+
+Function.prototype.overloadGetter = function(usePlural){
+ var self = this;
+ return function(a){
+ var args, result;
+ if (usePlural || typeof a != 'string') args = a;
+ else if (arguments.length > 1) args = arguments;
+ if (args){
+ result = {};
+ for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
+ } else {
+ result = self.call(this, a);
+ }
+ return result;
+ };
+};
+
+Function.prototype.extend = function(key, value){
+ this[key] = value;
+}.overloadSetter();
+
+Function.prototype.implement = function(key, value){
+ this.prototype[key] = value;
+}.overloadSetter();
+
+// From
+
+var slice = Array.prototype.slice;
+
+Function.from = function(item){
+ return (typeOf(item) == 'function') ? item : function(){
+ return item;
+ };
+};
+
+Array.from = function(item){
+ if (item == null) return [];
+ return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
+};
+
+Number.from = function(item){
+ var number = parseFloat(item);
+ return isFinite(number) ? number : null;
+};
+
+String.from = function(item){
+ return item + '';
+};
+
+// hide, protect
+
+Function.implement({
+
+ hide: function(){
+ this.$hidden = true;
+ return this;
+ },
+
+ protect: function(){
+ this.$protected = true;
+ return this;
+ }
+
+});
+
+// Type
+
+var Type = this.Type = function(name, object){
+ if (name){
+ var lower = name.toLowerCase();
+ var typeCheck = function(item){
+ return (typeOf(item) == lower);
+ };
+
+ Type['is' + name] = typeCheck;
+ if (object != null){
+ object.prototype.$family = (function(){
+ return lower;
+ }).hide();
+
+ }
+ }
+
+ if (object == null) return null;
+
+ object.extend(this);
+ object.$constructor = Type;
+ object.prototype.$constructor = object;
+
+ return object;
+};
+
+var toString = Object.prototype.toString;
+
+Type.isEnumerable = function(item){
+ return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
+};
+
+var hooks = {};
+
+var hooksOf = function(object){
+ var type = typeOf(object.prototype);
+ return hooks[type] || (hooks[type] = []);
+};
+
+var implement = function(name, method){
+ if (method && method.$hidden) return;
+
+ var hooks = hooksOf(this);
+
+ for (var i = 0; i < hooks.length; i++){
+ var hook = hooks[i];
+ if (typeOf(hook) == 'type') implement.call(hook, name, method);
+ else hook.call(this, name, method);
+ }
+
+ var previous = this.prototype[name];
+ if (previous == null || !previous.$protected) this.prototype[name] = method;
+
+ if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
+ return method.apply(item, slice.call(arguments, 1));
+ });
+};
+
+var extend = function(name, method){
+ if (method && method.$hidden) return;
+ var previous = this[name];
+ if (previous == null || !previous.$protected) this[name] = method;
+};
+
+Type.implement({
+
+ implement: implement.overloadSetter(),
+
+ extend: extend.overloadSetter(),
+
+ alias: function(name, existing){
+ implement.call(this, name, this.prototype[existing]);
+ }.overloadSetter(),
+
+ mirror: function(hook){
+ hooksOf(this).push(hook);
+ return this;
+ }
+
+});
+
+new Type('Type', Type);
+
+// Default Types
+
+var force = function(name, object, methods){
+ var isType = (object != Object),
+ prototype = object.prototype;
+
+ if (isType) object = new Type(name, object);
+
+ for (var i = 0, l = methods.length; i < l; i++){
+ var key = methods[i],
+ generic = object[key],
+ proto = prototype[key];
+
+ if (generic) generic.protect();
+
+ if (isType && proto){
+ delete prototype[key];
+ prototype[key] = proto.protect();
+ }
+ }
+
+ if (isType) object.implement(prototype);
+
+ return force;
+};
+
+force('String', String, [
+ 'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
+ 'slice', 'split', 'substr', 'substring', 'toLowerCase', 'toUpperCase'
+])('Array', Array, [
+ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
+ 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
+])('Number', Number, [
+ 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
+])('Function', Function, [
+ 'apply', 'call', 'bind'
+])('RegExp', RegExp, [
+ 'exec', 'test'
+])('Object', Object, [
+ 'create', 'defineProperty', 'defineProperties', 'keys',
+ 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
+ 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
+])('Date', Date, ['now']);
+
+Object.extend = extend.overloadSetter();
+
+Date.extend('now', function(){
+ return +(new Date);
+});
+
+new Type('Boolean', Boolean);
+
+// fixes NaN returning as Number
+
+Number.prototype.$family = function(){
+ return isFinite(this) ? 'number' : 'null';
+}.hide();
+
+// Number.random
+
+Number.extend('random', function(min, max){
+ return Math.floor(Math.random() * (max - min + 1) + min);
+});
+
+// forEach, each
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+Object.extend('forEach', function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
+ }
+});
+
+Object.each = Object.forEach;
+
+Array.implement({
+
+ forEach: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (i in this) fn.call(bind, this[i], i, this);
+ }
+ },
+
+ each: function(fn, bind){
+ Array.forEach(this, fn, bind);
+ return this;
+ }
+
+});
+
+// Array & Object cloning, Object merging and appending
+
+var cloneOf = function(item){
+ switch (typeOf(item)){
+ case 'array': return item.clone();
+ case 'object': return Object.clone(item);
+ default: return item;
+ }
+};
+
+Array.implement('clone', function(){
+ var i = this.length, clone = new Array(i);
+ while (i--) clone[i] = cloneOf(this[i]);
+ return clone;
+});
+
+var mergeOne = function(source, key, current){
+ switch (typeOf(current)){
+ case 'object':
+ if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
+ else source[key] = Object.clone(current);
+ break;
+ case 'array': source[key] = current.clone(); break;
+ default: source[key] = current;
+ }
+ return source;
+};
+
+Object.extend({
+
+ merge: function(source, k, v){
+ if (typeOf(k) == 'string') return mergeOne(source, k, v);
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var object = arguments[i];
+ for (var key in object) mergeOne(source, key, object[key]);
+ }
+ return source;
+ },
+
+ clone: function(object){
+ var clone = {};
+ for (var key in object) clone[key] = cloneOf(object[key]);
+ return clone;
+ },
+
+ append: function(original){
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var extended = arguments[i] || {};
+ for (var key in extended) original[key] = extended[key];
+ }
+ return original;
+ }
+
+});
+
+// Object-less types
+
+['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
+ new Type(name);
+});
+
+// Unique ID
+
+var UID = Date.now();
+
+String.extend('uniqueID', function(){
+ return (UID++).toString(36);
+});
+
+
+
+})();
+
+
+/*
+---
+
+name: Array
+
+description: Contains Array Prototypes like each, contains, and erase.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Array
+
+...
+*/
+
+Array.implement({
+
+ /**/
+ every: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
+ }
+ return true;
+ },
+
+ filter: function(fn, bind){
+ var results = [];
+ for (var i = 0, l = this.length; i < l; i++){
+ if ((i in this) && fn.call(bind, this[i], i, this)) results.push(this[i]);
+ }
+ return results;
+ },
+
+ indexOf: function(item, from){
+ var len = this.length;
+ for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
+ if (this[i] === item) return i;
+ }
+ return -1;
+ },
+
+ map: function(fn, bind){
+ var results = [];
+ for (var i = 0, l = this.length; i < l; i++){
+ if (i in this) results[i] = fn.call(bind, this[i], i, this);
+ }
+ return results;
+ },
+
+ some: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if ((i in this) && fn.call(bind, this[i], i, this)) return true;
+ }
+ return false;
+ },
+ /*!ES5>*/
+
+ clean: function(){
+ return this.filter(function(item){
+ return item != null;
+ });
+ },
+
+ invoke: function(methodName){
+ var args = Array.slice(arguments, 1);
+ return this.map(function(item){
+ return item[methodName].apply(item, args);
+ });
+ },
+
+ associate: function(keys){
+ var obj = {}, length = Math.min(this.length, keys.length);
+ for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
+ return obj;
+ },
+
+ link: function(object){
+ var result = {};
+ for (var i = 0, l = this.length; i < l; i++){
+ for (var key in object){
+ if (object[key](this[i])){
+ result[key] = this[i];
+ delete object[key];
+ break;
+ }
+ }
+ }
+ return result;
+ },
+
+ contains: function(item, from){
+ return this.indexOf(item, from) != -1;
+ },
+
+ append: function(array){
+ this.push.apply(this, array);
+ return this;
+ },
+
+ getLast: function(){
+ return (this.length) ? this[this.length - 1] : null;
+ },
+
+ getRandom: function(){
+ return (this.length) ? this[Number.random(0, this.length - 1)] : null;
+ },
+
+ include: function(item){
+ if (!this.contains(item)) this.push(item);
+ return this;
+ },
+
+ combine: function(array){
+ for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
+ return this;
+ },
+
+ erase: function(item){
+ for (var i = this.length; i--;){
+ if (this[i] === item) this.splice(i, 1);
+ }
+ return this;
+ },
+
+ empty: function(){
+ this.length = 0;
+ return this;
+ },
+
+ flatten: function(){
+ var array = [];
+ for (var i = 0, l = this.length; i < l; i++){
+ var type = typeOf(this[i]);
+ if (type == 'null') continue;
+ array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
+ }
+ return array;
+ },
+
+ pick: function(){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (this[i] != null) return this[i];
+ }
+ return null;
+ },
+
+ hexToRgb: function(array){
+ if (this.length != 3) return null;
+ var rgb = this.map(function(value){
+ if (value.length == 1) value += value;
+ return value.toInt(16);
+ });
+ return (array) ? rgb : 'rgb(' + rgb + ')';
+ },
+
+ rgbToHex: function(array){
+ if (this.length < 3) return null;
+ if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
+ var hex = [];
+ for (var i = 0; i < 3; i++){
+ var bit = (this[i] - 0).toString(16);
+ hex.push((bit.length == 1) ? '0' + bit : bit);
+ }
+ return (array) ? hex : '#' + hex.join('');
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: String
+
+description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: String
+
+...
+*/
+
+String.implement({
+
+ test: function(regex, params){
+ return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
+ },
+
+ contains: function(string, separator){
+ return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
+ },
+
+ trim: function(){
+ return this.replace(/^\s+|\s+$/g, '');
+ },
+
+ clean: function(){
+ return this.replace(/\s+/g, ' ').trim();
+ },
+
+ camelCase: function(){
+ return this.replace(/-\D/g, function(match){
+ return match.charAt(1).toUpperCase();
+ });
+ },
+
+ hyphenate: function(){
+ return this.replace(/[A-Z]/g, function(match){
+ return ('-' + match.charAt(0).toLowerCase());
+ });
+ },
+
+ capitalize: function(){
+ return this.replace(/\b[a-z]/g, function(match){
+ return match.toUpperCase();
+ });
+ },
+
+ escapeRegExp: function(){
+ return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ hexToRgb: function(array){
+ var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
+ return (hex) ? hex.slice(1).hexToRgb(array) : null;
+ },
+
+ rgbToHex: function(array){
+ var rgb = this.match(/\d{1,3}/g);
+ return (rgb) ? rgb.rgbToHex(array) : null;
+ },
+
+ substitute: function(object, regexp){
+ return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
+ if (match.charAt(0) == '\\') return match.slice(1);
+ return (object[name] != null) ? object[name] : '';
+ });
+ }
+
+});
+
+
+/*
+---
+
+name: Number
+
+description: Contains Number Prototypes like limit, round, times, and ceil.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Number
+
+...
+*/
+
+Number.implement({
+
+ limit: function(min, max){
+ return Math.min(max, Math.max(min, this));
+ },
+
+ round: function(precision){
+ precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
+ return Math.round(this * precision) / precision;
+ },
+
+ times: function(fn, bind){
+ for (var i = 0; i < this; i++) fn.call(bind, i, this);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ }
+
+});
+
+Number.alias('each', 'times');
+
+(function(math){
+ var methods = {};
+ math.each(function(name){
+ if (!Number[name]) methods[name] = function(){
+ return Math[name].apply(null, [this].concat(Array.from(arguments)));
+ };
+ });
+ Number.implement(methods);
+})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
+
+
+/*
+---
+
+name: Function
+
+description: Contains Function Prototypes like create, bind, pass, and delay.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Function
+
+...
+*/
+
+Function.extend({
+
+ attempt: function(){
+ for (var i = 0, l = arguments.length; i < l; i++){
+ try {
+ return arguments[i]();
+ } catch (e){}
+ }
+ return null;
+ }
+
+});
+
+Function.implement({
+
+ attempt: function(args, bind){
+ try {
+ return this.apply(bind, Array.from(args));
+ } catch (e){}
+
+ return null;
+ },
+
+ /**/
+ bind: function(bind){
+ var self = this,
+ args = (arguments.length > 1) ? Array.slice(arguments, 1) : null;
+
+ return function(){
+ if (!args && !arguments.length) return self.call(bind);
+ if (args && arguments.length) return self.apply(bind, args.concat(Array.from(arguments)));
+ return self.apply(bind, args || arguments);
+ };
+ },
+ /*!ES5>*/
+
+ pass: function(args, bind){
+ var self = this;
+ if (args != null) args = Array.from(args);
+ return function(){
+ return self.apply(bind, args || arguments);
+ };
+ },
+
+ delay: function(delay, bind, args){
+ return setTimeout(this.pass((args == null ? [] : args), bind), delay);
+ },
+
+ periodical: function(periodical, bind, args){
+ return setInterval(this.pass((args == null ? [] : args), bind), periodical);
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: Object
+
+description: Object generic methods
+
+license: MIT-style license.
+
+requires: Type
+
+provides: [Object, Hash]
+
+...
+*/
+
+(function(){
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+Object.extend({
+
+ subset: function(object, keys){
+ var results = {};
+ for (var i = 0, l = keys.length; i < l; i++){
+ var k = keys[i];
+ if (k in object) results[k] = object[k];
+ }
+ return results;
+ },
+
+ map: function(object, fn, bind){
+ var results = {};
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
+ }
+ return results;
+ },
+
+ filter: function(object, fn, bind){
+ var results = {};
+ for (var key in object){
+ var value = object[key];
+ if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
+ }
+ return results;
+ },
+
+ every: function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
+ }
+ return true;
+ },
+
+ some: function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
+ }
+ return false;
+ },
+
+ keys: function(object){
+ var keys = [];
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) keys.push(key);
+ }
+ return keys;
+ },
+
+ values: function(object){
+ var values = [];
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) values.push(object[key]);
+ }
+ return values;
+ },
+
+ getLength: function(object){
+ return Object.keys(object).length;
+ },
+
+ keyOf: function(object, value){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && object[key] === value) return key;
+ }
+ return null;
+ },
+
+ contains: function(object, value){
+ return Object.keyOf(object, value) != null;
+ },
+
+ toQueryString: function(object, base){
+ var queryString = [];
+
+ Object.each(object, function(value, key){
+ if (base) key = base + '[' + key + ']';
+ var result;
+ switch (typeOf(value)){
+ case 'object': result = Object.toQueryString(value, key); break;
+ case 'array':
+ var qs = {};
+ value.each(function(val, i){
+ qs[i] = val;
+ });
+ result = Object.toQueryString(qs, key);
+ break;
+ default: result = key + '=' + encodeURIComponent(value);
+ }
+ if (value != null) queryString.push(result);
+ });
+
+ return queryString.join('&');
+ }
+
+});
+
+})();
+
+
+
+
+/*
+---
+
+name: Browser
+
+description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
+
+license: MIT-style license.
+
+requires: [Array, Function, Number, String]
+
+provides: [Browser, Window, Document]
+
+...
+*/
+
+(function(){
+
+var document = this.document;
+var window = document.window = this;
+
+var UID = 1;
+
+this.$uid = (window.ActiveXObject) ? function(item){
+ return (item.uid || (item.uid = [UID++]))[0];
+} : function(item){
+ return item.uid || (item.uid = UID++);
+};
+
+$uid(window);
+$uid(document);
+
+var ua = navigator.userAgent.toLowerCase(),
+ platform = navigator.platform.toLowerCase(),
+ UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0],
+ mode = UA[1] == 'ie' && document.documentMode;
+
+var Browser = this.Browser = {
+
+ extend: Function.prototype.extend,
+
+ name: (UA[1] == 'version') ? UA[3] : UA[1],
+
+ version: mode || parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
+
+ Platform: {
+ name: ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0]
+ },
+
+ Features: {
+ xpath: !!(document.evaluate),
+ air: !!(window.runtime),
+ query: !!(document.querySelector),
+ json: !!(window.JSON)
+ },
+
+ Plugins: {}
+
+};
+
+Browser[Browser.name] = true;
+Browser[Browser.name + parseInt(Browser.version, 10)] = true;
+Browser.Platform[Browser.Platform.name] = true;
+
+// Request
+
+Browser.Request = (function(){
+
+ var XMLHTTP = function(){
+ return new XMLHttpRequest();
+ };
+
+ var MSXML2 = function(){
+ return new ActiveXObject('MSXML2.XMLHTTP');
+ };
+
+ var MSXML = function(){
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ };
+
+ return Function.attempt(function(){
+ XMLHTTP();
+ return XMLHTTP;
+ }, function(){
+ MSXML2();
+ return MSXML2;
+ }, function(){
+ MSXML();
+ return MSXML;
+ });
+
+})();
+
+Browser.Features.xhr = !!(Browser.Request);
+
+// Flash detection
+
+var version = (Function.attempt(function(){
+ return navigator.plugins['Shockwave Flash'].description;
+}, function(){
+ return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
+}) || '0 r0').match(/\d+/g);
+
+Browser.Plugins.Flash = {
+ version: Number(version[0] || '0.' + version[1]) || 0,
+ build: Number(version[2]) || 0
+};
+
+// String scripts
+
+Browser.exec = function(text){
+ if (!text) return text;
+ if (window.execScript){
+ window.execScript(text);
+ } else {
+ var script = document.createElement('script');
+ script.setAttribute('type', 'text/javascript');
+ script.text = text;
+ document.head.appendChild(script);
+ document.head.removeChild(script);
+ }
+ return text;
+};
+
+String.implement('stripScripts', function(exec){
+ var scripts = '';
+ var text = this.replace(/
diff --git a/popup.js b/popup.js
index fe4a0de..d4d5e47 100644
--- a/popup.js
+++ b/popup.js
@@ -1,30 +1,17 @@
var db = openDatabase('mhdb', '1.0', 'Mostly Harmless Database', 5 * 1024 * 1024);
-var cacheTime;
-var over18;
+var settings = new Store("settings", {
+ "cacheTime": 1
+}).toObject();
var commentsMatchPattern = /https?:\/\/www\.reddit\.com(\/r\/(.+?))?\/comments\/(.+?)\/.*/;
var isCommentsPage = false;
-init();
-function init() {
- if(window.localStorage.getItem('installed') !== 'true') {
- installDefaults();
- }
- db.transaction(function(tx){
- tx.executeSql('SELECT * FROM prefs WHERE pref=?', ['cacheTime'], function(tx, results) {
- cacheTime = results.rows.item(0).choice;
- });
- tx.executeSql('SELECT * FROM prefs WHERE pref=?', ['over18'], function(tx, results) {
- over18 = results.rows.item(0).choice;
- });
- });
-}
chrome.tabs.getSelected(undefined, function(currTab) {
buildPage(currTab.url);
});
function buildPage(pageUrl) {
isCommentsPage = commentsMatchPattern.test(pageUrl);
var sqlQuery = isCommentsPage ? 'SELECT * FROM posts WHERE id=?' : 'SELECT * FROM posts WHERE url=?';
- var sqlSearch = isCommentsPage ? pageUrl.match(commentsMatchPattern)[3] : pageUrl;
+ var sqlSearch = isCommentsPage ? pageUrl.match(commentsMatchPattern)[3] : pageUrl.split('#')[0];
db.transaction(function(tx){
tx.executeSql(sqlQuery, [sqlSearch], function(tx, results) {
console.log(results.rows);