diff options
author | Trevor Elliott <trevor@galois.com> | 2013-06-12 16:41:00 -0700 |
---|---|---|
committer | Trevor Elliott <trevor@galois.com> | 2013-06-12 16:41:00 -0700 |
commit | c7b493dcc46ac800ace472a580d5430a1b0c2e41 (patch) | |
tree | 1ae2ea9ad7b84b07387d41c2b6ca6c4eccc4366a /src/js | |
parent | 0decef2093c676d91b0b590a9b94f201c4995716 (diff) |
Group url patterns with rule set definitions
Diffstat (limited to 'src/js')
-rw-r--r-- | src/js/chrome/js/platform-ajax.js | 2 | ||||
-rw-r--r-- | src/js/chrome/js/platform-background.js | 4 | ||||
-rw-r--r-- | src/js/firefox/lib/main.js | 1 | ||||
-rw-r--r-- | src/js/fiveui/build.mk | 3 | ||||
-rw-r--r-- | src/js/fiveui/css/entry.css | 59 | ||||
-rw-r--r-- | src/js/fiveui/css/options.css | 66 | ||||
-rw-r--r-- | src/js/fiveui/injected/compute.js | 17 | ||||
-rw-r--r-- | src/js/fiveui/js/background.js | 13 | ||||
-rw-r--r-- | src/js/fiveui/js/entry.js | 300 | ||||
-rw-r--r-- | src/js/fiveui/js/messenger.js | 28 | ||||
-rw-r--r-- | src/js/fiveui/js/options.js | 310 | ||||
-rw-r--r-- | src/js/fiveui/js/rules.js | 130 | ||||
-rw-r--r-- | src/js/fiveui/js/settings.js | 283 | ||||
-rw-r--r-- | src/js/fiveui/options.html | 17 |
14 files changed, 556 insertions, 677 deletions
diff --git a/src/js/chrome/js/platform-ajax.js b/src/js/chrome/js/platform-ajax.js index 68ced39..32aac99 100644 --- a/src/js/chrome/js/platform-ajax.js +++ b/src/js/chrome/js/platform-ajax.js @@ -20,6 +20,8 @@ fiveui.ajax.get = function(url, options) { jQuery.ajax(url, { + cache: false, + dataType: 'text', success:function(text) { diff --git a/src/js/chrome/js/platform-background.js b/src/js/chrome/js/platform-background.js index 6ffecf8..0f92fb5 100644 --- a/src/js/chrome/js/platform-background.js +++ b/src/js/chrome/js/platform-background.js @@ -135,13 +135,15 @@ fiveui.chrome.background = function() { chrome.tabs.onCreated.addListener(function(tab) { - // console.log('in oncreated'); + console.log('in oncreated'); if (tab.url) { background.pageLoad(tab.id, tab.url); } }); + // check page load events against the generic background chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { + console.log('in onupdated'); if (changeInfo.status == 'complete') { background.pageLoad(tabId, tab.url); } diff --git a/src/js/firefox/lib/main.js b/src/js/firefox/lib/main.js index 7679fd2..dfcacb1 100644 --- a/src/js/firefox/lib/main.js +++ b/src/js/firefox/lib/main.js @@ -197,7 +197,6 @@ fiveui.firefox.main = function() { , data.url('js/options.js') , data.url('js/update-manager.js') , data.url('js/utils.js') - , data.url('js/entry.js') , data.url('js/rules.js') , data.url('js/url-pat.js') , data.url('js/platform-ajax.js') diff --git a/src/js/fiveui/build.mk b/src/js/fiveui/build.mk index 3cb1851..c9bbf7d 100644 --- a/src/js/fiveui/build.mk +++ b/src/js/fiveui/build.mk @@ -45,8 +45,7 @@ $2: $(patsubst $(fiveui-dir)/%,$1/data/%,$(wildcard $(fiveui-dir)/js/*)) $(call fiveui-files,$1/data,css) -$2: $1/data/css/entry.css \ - $1/data/css/options.css +$2: $1/data/css/options.css $(call fiveui-files,$1/data,images) diff --git a/src/js/fiveui/css/entry.css b/src/js/fiveui/css/entry.css deleted file mode 100644 index 5d91b3b..0000000 --- a/src/js/fiveui/css/entry.css +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Module : entry.css - * Copyright : (c) 2011-2012, Galois, Inc. - * - * Maintainer : - * Stability : Provisional - * Portability: Portable - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -ul.entries { - list-style: none; - padding-left: 5px; -} - -ul.entries li.entry { - border-top: 1px solid #CDCDCD; - margin-top: 5px; - padding-top: 10px; - padding-bottom: 10px; -} - -li.entry .error { - margin-top: 10px; - padding: 10px; - background-color: #FF4D4D; - color: #ffffff; -} - -ul.entries li.entry:first-child { - border-top: 0px; -} - -li.entry button { - padding: 0px; - font-size: 0.75em; -} - -li.entry .editable { - padding: 5px; - border: 1px solid #CDCDCD; -} - -li.entry .placeholder { - font-style: italic; - color: #CDCDCD; -} diff --git a/src/js/fiveui/css/options.css b/src/js/fiveui/css/options.css index efa4986..65540e2 100644 --- a/src/js/fiveui/css/options.css +++ b/src/js/fiveui/css/options.css @@ -135,6 +135,68 @@ div.title { float: left; } -.buttons { - display: inline; +.control .label { + color: #b2b2b2; + font-weight: italic; +} + +.control button { + font-weight: normal; + font-size: 0.75em; +} + + +ul.entries { + list-style: none; + padding-left: 2px; +} + +ul.entries li.entry { + border: 1px solid #CDCDCD; + margin-top: 5px; + padding: 10px; + background-color: #f0f0f0; +} + +li.entry .error { + margin-top: 10px; + padding: 10px; + background-color: #FF4D4D; + color: #ffffff; +} + +li.entry button { + padding: 0px; + font-size: 0.6em; +} + +li.entry .editable { + padding: 5px; + border: 1px solid #CDCDCD; +} + +li.entry .placeholder { + font-style: italic; + color: #CDCDCD; +} + + + + +li.entry div.pattern-control { + padding-top: 10px; +} + +ul.patterns { + list-style: none; + padding: 0.5em; + padding-left: 0.5em; +} + +ul.patterns li { + padding-top: 5px; +} + +li.entry div.pattern-input { + margin: 2px; } diff --git a/src/js/fiveui/injected/compute.js b/src/js/fiveui/injected/compute.js index 225c794..8efc8f7 100644 --- a/src/js/fiveui/injected/compute.js +++ b/src/js/fiveui/injected/compute.js @@ -60,7 +60,7 @@ var delta = new Date() - core.lastEvent; if(delta > core.timeout && !core.maskRules) { core.scheduled = false; - core.evaluate(core.rules.rules); + core.evaluate(core.rules); } else { setTimeout(check, core.timeout); } @@ -291,25 +291,18 @@ var registerBackendListeners = function(port) { port.on('SetRules', function(payload) { - var rules = payload.rules; + core.rules = []; - core.rules = payload; - core.rules.rules = []; - - for(var i=0; i<rules.length; ++i) { + for(var i=0; i<payload.length; ++i) { var moduleStr = [ '(function(){' , 'var exports = {};' - , rules[i].module + , payload[i] , 'return exports;' , '})()' ].join('\n'); - core.rules.rules.push(eval(moduleStr)); - } - - if (null == core.rules) { - debugger; + core.rules.push(eval(moduleStr)); } core.scheduleRules(); diff --git a/src/js/fiveui/js/background.js b/src/js/fiveui/js/background.js index c0d4e07..e662e95 100644 --- a/src/js/fiveui/js/background.js +++ b/src/js/fiveui/js/background.js @@ -113,13 +113,11 @@ fiveui.Background.prototype.connect = function(tabId, port, url, isUiPort) { this._registerComputeListeners(port, tabState); // get the rule set and send it down to the injected page: - var pat = this.settings.checkUrl(url); - if (!pat) { + var ruleSet = this.settings.checkUrl(url); + if (ruleSet == null) { console.err('could not find url pattern for tab.url, but one was strongly expected'); } else { - var ruleSet = this.settings.getRuleSet(pat.rule_id); - - port.emit('SetRules', ruleSet); + port.emit('SetRules', ruleSet.rules); } } }; @@ -130,9 +128,9 @@ fiveui.Background.prototype.connect = function(tabId, port, url, isUiPort) { * @param {*} data */ fiveui.Background.prototype.pageLoad = function(tabId, url, data) { - var pat = this.settings.checkUrl(url); + var ruleSet = this.settings.checkUrl(url); - if (null == pat) { + if (ruleSet == null) { this.updateWidget(null); } else { var tabState = this.state.acquireTabState(tabId); @@ -141,7 +139,6 @@ fiveui.Background.prototype.pageLoad = function(tabId, url, data) { this.updateWidget(tabState); var dependencies = []; - var ruleSet = this.settings.getRuleSet(pat.rule_id); if (ruleSet && ruleSet.dependencies ) { dependencies = ruleSet.dependencies; diff --git a/src/js/fiveui/js/entry.js b/src/js/fiveui/js/entry.js deleted file mode 100644 index 5d460b4..0000000 --- a/src/js/fiveui/js/entry.js +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Module : entry.js - * Copyright : (c) 2011-2012, Galois, Inc. - * - * Maintainer : - * Stability : Provisional - * Portability: Portable - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -var fiveui = fiveui || {}; - -(function() { - -var editable = function(el, model, placeholder) { - - el.prop('contenteditable', true).addClass('editable'); - - // prevent newlines - el.on('keypress', function(e) { - return e.which != 13; - }); - - var addPlaceholder = function() { - el.addClass('placeholder') - .text(placeholder) - .one('click keypress paste', remPlaceholder); - }; - - var remPlaceholder = function() { - el.removeClass('placeholder').text(''); - }; - - // if the model is new, set the placeholder, and a listener to clear it - if(model.isNew()) { - addPlaceholder(); - } - - el.on('blur', function() { - if(_.isEmpty(el.text())) { - addPlaceholder(); - } - }); - - el.focus(); - -}; - -var button = function(el, icon) { - el.button({ icons: icon, text: false }); -}; - - -/** UrlPat Entry Elements ****************************************************/ - -fiveui.UrlPatEntry = Backbone.View.extend({ - - tagName: 'li', - - className: 'entry', - - events: { - 'click .save' : 'save', - 'click .remove' : 'remove', - 'click .edit' : 'edit', - }, - - initialize:function() { - this.listenTo(this.model, 'remove', function() { - this.$el.remove(); - this.stopListening(); - }); - }, - - viewTemplate: _.template( - [ '<div>' - , ' <button class="remove">x</button>' - , ' <button class="edit">edit</button>' - , ' <span><%= regex %></span>' - , ' <span><%= rule_name %></span>' - , '</div>' - ].join('')), - - render:function() { - - var attrs = _.clone(this.model.attributes); - var ruleSet = this.options.rules.model.findWhere({ id: attrs.rule_id }) - attrs.rule_name = ruleSet.get('name'); - this.$el.html(this.viewTemplate(attrs)); - - // setup buttons - button(this.$el.find('.remove'), { primary: 'ui-icon-close' }); - button(this.$el.find('.edit'), { primary: 'ui-icon-pencil' }); - - return this; - }, - - editTemplate: _.template( - [ '<div>' - , ' <button class="remove">x</button>' - , ' <button class="save">save</button>' - , ' <span class="regex"></span>' - , ' <span class="rules"></span>' - , '</div>' - ].join('')), - - edit:function() { - var attrs = this.model.attributes; - this.$el.html(this.editTemplate(attrs)); - - this.$el.find('.rules').append(this.options.rules.render().$el); - editable(this.$el.find('.regex'), this.model, 'url pattern'); - - button(this.$el.find('.remove'), { primary: 'ui-icon-close' }); - button(this.$el.find('.save'), { primary: 'ui-icon-disk' }); - - return this; - }, - - remove: function() { - this.model.destroy(); - }, - - save:function() { - var regex = this.$el.find('.regex').text(); - var rule_id = parseInt(this.options.rules.$el.val()); - this.model.save({ regex : regex, rule_id : rule_id }, { - success: _.bind(this.render, this), - error: _.bind(this.edit, this) - }); - }, - -}); - - -/** Rule Set View ************************************************************/ - -fiveui.RulesView = Backbone.View.extend({ - - tagName: 'select', - - initialize:function() { - this.listenTo(this.model, 'sync', this.update); - this.listenTo(this.model, 'remove', this.update); - }, - - optionTemplate:_.template( - '<option value="<%= id %>"><%= name %></option>' - ), - - update:function() { - if(this.model.length == 0) { - return this.remove(); - } else { - return this.render(); - } - }, - - remove:function() { - this.stopListening(); - this.$el.remove(); - - this.trigger('remove'); - - return this; - }, - - render:function() { - - var scope = this; - - this.$el.children().remove(); - - var text = this.model.foldl(function(body,ruleSet) { - return body + scope.optionTemplate(ruleSet.attributes); - }, ''); - - this.$el.html(text); - - return this; - }, - -}); - - -/** Rule Entry Elements ******************************************************/ - -fiveui.RuleSetEntry = Backbone.View.extend({ - - tagName: 'li', - - className: 'entry', - - events: { - 'click .save' : 'save', - 'click .remove' : 'remove', - 'click .edit' : 'edit', - 'click .reload' : 'reload', - }, - - viewTemplate: _.template( - [ '<div class="content">' - , ' <button class="button remove">remove</button>' - , ' <button class="edit">edit</button>' - , ' <button class="reload">reload</button>' - , ' <span class="title"><%= name %></span>' - , '</div>' - ].join('')), - - render:function() { - - var attrs = this.model.attributes; - this.$el.html(this.viewTemplate(attrs)); - - button(this.$el.find('.remove'), { primary: 'ui-icon-close' }); - button(this.$el.find('.edit'), { primary: 'ui-icon-pencil' }); - button(this.$el.find('.reload'), { primary: 'ui-icon-refresh' }); - - return this; - }, - - editTemplate: _.template( - [ '<div class="content">' - , ' <button class="remove">x</button>' - , ' <button class="save">save</button>' - , ' <span class="source"><%= source %></span>' - , '</div>' - ].join('')), - - edit:function() { - var attrs = this.model.attributes; - this.$el.html(this.editTemplate(attrs)); - - button(this.$el.find('.remove'), { primary: 'ui-icon-close' }); - button(this.$el.find('.save'), { primary: 'ui-icon-disk' }); - - editable(this.$el.find('.source'), this.model, - 'http://example.com/manifest.json') - - return this; - }, - - errorTemplate: _.template('<div class="error"><%= message %></div>'), - - editError:function(target, message) { - this.edit(); - - this.$el.append(this.errorTemplate({ message: message })); - - return this; - }, - - save: function() { - var source = this.$el.find('.source').text(); - this.model.set('source', source); - this.model.save({}, { - success: _.bind(this.render, this), - error: _.bind(this.editError, this) - }); - }, - - reload:function() { - this.model.save({}, { - success: _.bind(this.render, this), - error: _.bind(this.edit, this) - }); - }, - - remove:function() { - var self = this; - - this.model.destroy({ - wait:true, - - success:function() { - self.$el.remove(); - self.stopListening(); - }, - - error:function() { - console.log('communicate failure somehow...'); - } - }); - }, - -}); - -})(); diff --git a/src/js/fiveui/js/messenger.js b/src/js/fiveui/js/messenger.js index fc1962c..fd1fb5b 100644 --- a/src/js/fiveui/js/messenger.js +++ b/src/js/fiveui/js/messenger.js @@ -127,14 +127,28 @@ _.extend(fiveui.Messenger.prototype, { */ fiveui.Messenger.Payload = function(isCallback, type, data, id) { this.isCallback = isCallback; - this.type = type; - this.id = id; + this.type = type; + this.id = id; + this.rawData = null; + this.__defineGetter__('data', function() { - return JSON.parse(this.rawData); - }); - this.__defineSetter__('data', function(obj){ - this.rawData = JSON.stringify(obj); - }); + if(_.isNull(this.rawData)) { + return null; + } else { + return JSON.parse(this.rawData); + } + }); + + this.__defineSetter__('data', function(obj) { + if(_.isUndefined(obj) || _.isNull(obj)) { + this.rawData = null; + } else { + this.rawData = JSON.stringify(obj); + } + }); + + + // use the setter defined above this.data = data; }; diff --git a/src/js/fiveui/js/options.js b/src/js/fiveui/js/options.js index 2b29f8d..598649f 100644 --- a/src/js/fiveui/js/options.js +++ b/src/js/fiveui/js/options.js @@ -113,9 +113,13 @@ fiveui.options.init = function(port) { var ruleSetEntries = jQuery('#ruleSetEntries'); // handle clicks to the 'add' button on the rule sets page - jQuery('#addRsButton').on('click', function() { - ruleSets.add(new fiveui.RuleSetModel({}, { url : msg })); - }); + jQuery('#addRsButton') + .button({ + icons: { primary: 'ui-icon-plus' }, + }) + .on('click', function() { + ruleSets.add(new fiveui.RuleSetModel({}, { url : msg })); + }); // render a ruleset added to the collection ruleSets.on('add', function(model) { @@ -191,13 +195,12 @@ fiveui.options.init = function(port) { }; // listen to click events on navigation elements - setClickHandler(jQuery('#url-defaults'), select('#tab-url-defaults')); setClickHandler(jQuery('#rule-sets'), select('#tab-rule-sets')); setClickHandler(jQuery('#basics'), select('#tab-basics')); // select the url patterns tab by default - selectNav(jQuery('#url-defaults')); - selectSection(jQuery('#tab-url-defaults')); + selectNav(jQuery('#rule-sets')); + selectSection(jQuery('#tab-rule-sets')); /** Pre-populate UI elements ***********************************************/ @@ -214,4 +217,299 @@ fiveui.options.init = function(port) { }); }; + + /** Rule-Set Views *********************************************************/ + +var editable = function(el, placeholder, onEnter) { + + el.prop('contenteditable', true).addClass('editable'); + + // prevent newlines + if(onEnter) { + el.on('keypress', function(e) { + if(e.which == 13) { + onEnter(); + return false; + } else { + return true; + } + }); + } else { + el.on('keypress', function(e) { + return e.which != 13; + }); + } + + var addPlaceholder = function() { + el.addClass('placeholder') + .text(placeholder) + .one('click keypress paste', remPlaceholder); + }; + + var remPlaceholder = function() { + el.removeClass('placeholder').text(''); + }; + + // if the model is new, set the placeholder, and a listener to clear it + if(el.text() == '') { + addPlaceholder(); + } + + el.on('blur', function() { + if(el.text() == '') { + addPlaceholder(); + } + }); + + el.focus(); + +}; + +var button = function(el, icon) { + el.button({ icons: icon, text: false }); +}; + + +/** Rule Set View ************************************************************/ + +fiveui.RulesView = Backbone.View.extend({ + + tagName: 'select', + + initialize:function() { + this.listenTo(this.model, 'sync', this.update); + this.listenTo(this.model, 'remove', this.update); + }, + + optionTemplate:_.template( + '<option value="<%= id %>"><%= name %></option>' + ), + + update:function() { + if(this.model.length == 0) { + return this.remove(); + } else { + return this.render(); + } + }, + + remove:function() { + this.stopListening(); + this.$el.remove(); + + this.trigger('remove'); + + return this; + }, + + render:function() { + + var scope = this; + + this.$el.children().remove(); + + var text = this.model.foldl(function(body,ruleSet) { + return body + scope.optionTemplate(ruleSet.attributes); + }, ''); + + this.$el.html(text); + + return this; + }, + +}); + + +/** Rule Entry Elements ******************************************************/ + +fiveui.RuleSetEntry = Backbone.View.extend({ + + tagName: 'li', + + className: 'entry', + + // setup the skeleton for the rule set editor. + initialize:function() { + this.$el.html( + [ '<div class="rule-set">' + , '</div>' + , '<ul class="patterns"></ul>' + , '<div class="pattern-control">' + , ' <div class="pattern-input"></div>' + , ' <button class="add-pattern">add url pattern</button' + , '</div>' + ].join('')); + + this.$rs = this.$el.find('.rule-set'); + this.$pat = this.$el.find('.patterns'); + this.$urlpat = this.$el.find('.pattern-input'); + this.$addpat = this.$el.find('.add-pattern'); + + // setup the url pattern editor + this.$addpat.button({ icons: { primary: 'ui-icon-plus' } }); + editable(this.$urlpat, 'http://example.com/*', + _.bind(this.$addpat.click, this.$addpat)) + }, + + events: { + 'click .save' : 'save', + 'click .remove' : 'remove', + 'click .edit' : 'edit', + 'click .reload' : 'reload', + 'click .add-pattern' : 'addPattern', + }, + + viewRsTemplate: _.template( + [ '<button class="remove">remove</button>' + , '<button class="edit">edit</button>' + , '<button class="reload">reload</button>' + , '<span class="title"><%= name %></span>' + ].join('')), + + // render the rule set as its title, with some buttons to edit/remove/reload + // it. + render:function() { + + // render the rule set + var attrs = _.clone(this.model.attributes); + this.$rs.html(this.viewRsTemplate(attrs)); + + button(this.$rs.find('.edit'), { primary: 'ui-icon-pencil' }); + button(this.$rs.find('.reload'), { primary: 'ui-icon-refresh' }); + button(this.$rs.find('.remove'), { primary: 'ui-icon-close' }); + + this.renderPats(this.model.get('patterns')); + + this.$addpat.prop('disabled', false); + + return this; + }, + + editTemplate: _.template( + [ '<button class="remove">x</button>' + , '<button class="save">save</button>' + , '<span class="source"><%= source %></span>' + ].join('')), + + // rework the rule set display area to a single input field for the url, and a + // remove and save button. + edit:function() { + var attrs = this.model.attributes; + this.$rs.html(this.editTemplate(attrs)); + + button(this.$rs.find('.remove'), { primary: 'ui-icon-close' }); + + var save = this.$rs.find('.save'); + button(save, { primary: 'ui-icon-disk' }); + + this.$addpat.prop('disabled', true); + + editable(this.$rs.find('.source'), 'http://example.com/manifest.json', + _.bind(save.click, save)); + + return this; + }, + + errorTemplate: _.template('<div class="error"><%= message %></div>'), + + // render an error message below the edit ui for the result sets, but before + // any url patterns. + editError:function(target, message) { + this.edit(); + this.$rs.append(this.errorTemplate({ message: message })); + + return this; + }, + + // save the current model, falling back on the editor dialog when errors show + // up. it's assumed that this is only called from the editor dialog. + save: function() { + var source = this.$el.find('.source').text(); + this.model.set('source', source); + this.model.save({}, { + success: _.bind(this.render, this), + error: _.bind(this.editError, this) + }); + }, + + // reaload the model, and render. on failure, display the edit dialog with a + // message. + reload:function() { + this.model.save({}, { + success: _.bind(this.render, this), + error: _.bind(this.editError, this) + }); + }, + + // remove the model, and remove the element from the list. + remove:function() { + this.model.destroy(); + this.$el.remove(); + this.stopListening(); + }, + + + + viewPatTemplate: _.template( + [ '<li>' + , ' <button class="remove-pat">remove</button>' + , ' <span class="pattern"><%= pattern %></span>' + , '</li>' + ].join('')), + + addUrlPat:function(pat) { + var el = jQuery(this.viewPatTemplate({ pattern: pat })); + + var remove = el.find('.remove-pat'); + + button(remove, { primary: 'ui-icon-close' }); + remove.on('click', _.bind(this.removePattern, this, pat)); + + this.$pat.append(el); + }, + + // render the url patterns of the rule set into this.$pat. + renderPats:function(pats) { + this.$pat.children().remove(); + + _.each(pats, _.bind(this.addUrlPat, this)) + return this; + }, + + // add a pattern to the underlying collection of patterns + addPattern:function() { + var pat = this.$urlpat.text(); + var pats = this.model.get('patterns'); + + pats.push(pat); + + this.model.save({ patterns : pats }, { + wait: true, + patch: true, + success: _.bind(function() { + this.$urlpat.text('').blur(); + this.render(); + }, this), + error: function(msg) { + debugger; + }, + }); + }, + + removePattern:function(pat) { + var pats = _.filter(this.model.get('patterns'), function(p) { + return p != pat; + }); + + this.model.save({ patterns: pats }, { + wait: true, + patch: true, + success:_.bind(this.render, this), + // XXX make this report an actual error + error: _.bind(this.render, this) + }); + }, + +}); + })(); diff --git a/src/js/fiveui/js/rules.js b/src/js/fiveui/js/rules.js index 314d50b..7bd2a10 100644 --- a/src/js/fiveui/js/rules.js +++ b/src/js/fiveui/js/rules.js @@ -25,45 +25,29 @@ var fiveui = fiveui || {}; /** * @constructor - * @param {!string} module A Javascript module that defines the rule. + * @param {!number} config Initializers for the rule set structure. */ -fiveui.Rule = function(module) { - this.module = module; +fiveui.RuleSet = function(config) { + // fill in fields + _.defaults(this, fiveui.RuleSet.sanitize(config)); }; -fiveui.Rule.defaults = function(obj) { - return _.defaults(obj, { - module: '', - }); -}; +fiveui.RuleSet.sanitize = function(obj) { + var defs = { + id: null, + name: '', + description: '', + source: '', + rules: [], + patterns: [], + dependencies: [], + }; -/** - * Create a Rule from a JSON object. - * - * @param {!Object} obj The object to take settings from. - * @return {!fiveui.Rule} A populated Rule object. - */ -fiveui.Rule.fromJSON = function(obj) { - return new fiveui.Rule(obj.module); + // scrub out any values that aren't in the defaults list, fill in any that are + // missing. + return _.defaults(_.pick(obj, _.keys(defs)), defs); }; -/** - * @constructor - * @param {!number} id The unique RuleSet identifier. - * @param {!string} name A human-readable name for this RuleSet. - * @param {!string} desc A human-readable description of the Rule Set. - * @param {!string} source The url where the manifest can be retrieved - * @param {!Array.<fiveui.Rule>} rules An Array of Rules. - * @param {?Array.<string>} deps Dependencies that this RuleSet requires. - */ -fiveui.RuleSet = function(id, name, desc, source, rules, deps) { - this.id = id; - this.name = name; - this.description = desc; - this.source = source; - this.rules = rules || []; - this.dependencies = deps || []; -}; /** * Create a Rule Setfrom a JSON object. @@ -73,21 +57,9 @@ fiveui.RuleSet = function(id, name, desc, source, rules, deps) { * @return {!fiveui.RuleSet} A populated RuleSet object. */ fiveui.RuleSet.fromJSON = function(id, obj) { - var rules = (/** @type {!Array.<!fiveui.Rule>} */ - _.map(obj.rules, fiveui.Rule.fromJSON)); - - return new fiveui.RuleSet(id, obj.name, obj.description, obj.source, - rules, obj.dependencies); -}; - - -fiveui.RuleSet.defaults = function(obj) { - return _.defaults(obj, { - name: '', - description: '', - rules: [], - dependencies: [] - }); + // make sure to override any id value passed in. + obj.id = id; + return new fiveui.RuleSet(obj); }; @@ -115,13 +87,13 @@ fiveui.RuleSet.load = function(manifest_url, options) { // XXX there's likely problems here, how should we make sure that the // url is what we expect? - var rule_file = fiveui.Rule.defaults(rules.pop()); + var rule_file = rules.pop(); var rule_url = base_url + '/' + rule_file; fiveui.ajax.get(rule_url, { success: function(text) { - manifest.rules.push(new fiveui.Rule(text)); + manifest.rules.push(text); loadRules(manifest, rules); }, @@ -135,21 +107,27 @@ fiveui.RuleSet.load = function(manifest_url, options) { fiveui.ajax.get(manifest_url, { success: function(text) { - try { - var sanitized = fiveui.utils.filterJSON(text,'json'); - var manifest = JSON.parse(sanitized); + // cleanup the parsed JSON object + var sanitized = fiveui.utils.filterJSON(text,'json'); + var obj = null; + try { + obj = JSON.parse(sanitized); } catch(e) { - // XXX incoming error continuation is empty - // (and we may have syntax error details in e) options.error('failed to parse manifest'); return; } - fiveui.RuleSet.defaults(manifest); + var manifest = fiveui.RuleSet.sanitize(obj); + - var rules = manifest.rules; - manifest.rules = []; + // explicitly zero out the patterns, they shouldn't be part of the + // manifest. + manifest.patterns = []; + + var rules = manifest.rules; + manifest.rules = []; + manifest.source = manifest_url; loadRules(manifest, rules); }, @@ -182,6 +160,7 @@ fiveui.RuleSetModel = Backbone.Model.extend({ source: '', rules: [], dependencies: [], + patterns: [], }, sync: function(method, model, options) { @@ -191,20 +170,34 @@ fiveui.RuleSetModel = Backbone.Model.extend({ error: function() {} }); + var attrs = _.clone(model.attributes); var msg = this.url; - var id = model.get('id'); - var source = model.get('source'); switch(method) { + // the patched fields are in options.attrs + case 'patch': + + var patch = options.attrs; + + // at the moment, we only support patching the patterns + if(!_.isEmpty(_.difference(_.keys(patch),['patterns']))) { + options.error('unable to patch more than the patterns field'); + } else { + attrs.patterns = patch.patterns; + msg.send('updateRuleSet', attrs, options.success); + } + + break; + case 'update': case 'create': var rsMethod = method == 'update' ? 'updateRuleSet' : 'addRuleSet'; - msg.send('loadRuleSet', source, function(obj) { + msg.send('loadRuleSet', attrs.source, function(obj) { if(!obj.error) { - obj.id = id; - obj.source = source; + obj.id = attrs.id; + obj.patterns = attrs.patterns; msg.send(rsMethod, obj, options.success); } else { @@ -214,17 +207,11 @@ fiveui.RuleSetModel = Backbone.Model.extend({ break; case 'delete': - msg.send('remRuleSet', id, function(obj) { - if(obj.removed) { - options.success(); - } else { - options.error(); - } - }); + msg.send('remRuleSet', attrs.id, options.success); break; case 'read': - msg.send('getRuleSet', id, function(rs) { + msg.send('getRuleSet', attrs.id, function(rs) { model.set({ title: rs.name, descr: rs.description, @@ -251,6 +238,7 @@ fiveui.RuleSetModel = Backbone.Model.extend({ rules: ruleSet.rules, dependencies:ruleSet.dependencies, source: ruleSet.source, + patterns: ruleSet.patterns, }, { url : msg }); }, diff --git a/src/js/fiveui/js/settings.js b/src/js/fiveui/js/settings.js index a17a9e0..96ec864 100644 --- a/src/js/fiveui/js/settings.js +++ b/src/js/fiveui/js/settings.js @@ -70,6 +70,43 @@ _.extend(fiveui.Settings.prototype, { }, /** + * @param {!number} id The nuber of the element to retrieve. + * @param {!string} listName The name of the portion of the localstorage hierarchy to search for id. + * @param {!function(number, !Object): *} fromJSON A deserialization function. + * + * @return {*} Either null, or the result of fromJSON. + */ + getById: function(id, listName, fromJSON) { + var obj = this.get(listName + '.' + id); + if (!obj) { + return null; + } + + return fromJSON(id, obj); + }, + + /** + * @param {!number} id The nuber of the element to retrieve. + * @param {!string} listName The name of the portion of the + * localstorage hierarchy to search for id. + */ + remById: function(id, listName) { + // remove it from the list of ids: + var ids = this.get(listName) || []; + + for (var i = 0; i < ids.length; ++i) { + if (ids[i] == id) { + ids.splice(i, 1); + this.set(listName, ids); + this.remove(listName + '.' + id); + break; + } + } + }, + + /** General Config **********************************************************/ + + /** * Set the display default. * * @param {!boolean} def Whether or not to display the FiveUI Window @@ -92,106 +129,39 @@ _.extend(fiveui.Settings.prototype, { return !!def; }, - /** - * Add a mapping from url patterns to RuleSets (via rule set ids) - * - * @param {!string} url_pat A regular expression (actually, a glob) to match URLs against. - * @param {!number} rule_id The id of the RuleSet to use with any matching URL. - * @return {!number} The id of the new url pattern. - */ - addUrl: function(url_pat, rule_id) { - var pats = this.getUrls(); - - var new_id = fiveui.utils.getNewId(pats); - - this.updateUrl(new_id, url_pat, rule_id); - - // add it to the patterns list - pats.push(new_id); - this.set('urls', pats); - - return new_id; - }, - - updateUrl: function(id, url_pat, rule_id) { - this.set('urls.' + id, new fiveui.UrlPat(id, url_pat, rule_id)); - return id; - }, - - /** - * Retrieve the list of url patterns. - * - * @return {Array.<number>} An ordered list of the currently active - * url patterns. - */ - getUrls: function() { - return (/** @type {Array.<number>} */ this.get('urls')) || []; - }, - /** - * Retrieve a specific url pattern. - * - * @param {!number} url_id The id of the url pattern to retrieve. - * @return {?fiveui.UrlPat} The matching UrlPat or null, - * if no pattern exists for url_id. - */ - getUrlPat: function(url_id) { - return /** @type {?fiveui.UrlPat} */ this.getById(url_id, 'urls', fiveui.UrlPat.fromJSON); - }, + /** Rule Sets ***************************************************************/ /** - * Remove a UrlPat from the persistent storage. + * Retrieve the list of rule set ids. * - * @param {!number} pat_id The id of the UrlPat to remove. - * @return {void} - */ - remUrlPat: function(pat_id) { - this.remById(pat_id, 'urls'); - }, - - /** - * @param {!string} url A url to compare against the list of ordered - * url patterns in local storage. - * @return {?fiveui.UrlPat} The matching pattern, or null, if no - * mapping was found. + * @return {!Array.<number>} An ordered list of the configured rule sets. */ - checkUrl: function(url) { - var pats = this.getUrls(); - - // check for a possible match - for (var i = 0; i < pats.length; ++i) { - var pat = this.getUrlPat(pats[i]); - if (pat.match(url)) { - return pat; - } - } - - return null; + getRuleSetIds: function() { + return (/** @type {!Array.<number>} */ this.get('ruleSet')) || []; }, /** - * Retrieve the list of rule sets. - * - * @return {!Array.<number>} An ordered list of the configured rule sets. + * Retrieve all rule set ids. */ - getRuleSets: function() { - return (/** @type {!Array.<number>} */ this.get('ruleSet')) || []; + getRuleSets:function() { + return _.map(this.getRuleSetIds(), _.bind(this.getRuleSet, this)); }, - /** * @param {!Object} ruleSet The new rule set, as an anonymous JSON object. - * @return {!fiveui.RuleSet} The new RuleSet object. + * @return {!fiveui.RuleSet} The id of the new rule set. */ addRuleSet: function(ruleSet) { - var ids = this.getRuleSets(); - var id = fiveui.utils.getNewId(ids); + var ids = this.getRuleSetIds(); + var id = fiveui.utils.getNewId(ids); + + this.updateRuleSet(id, ruleSet); - var newRS = this.updateRuleSet(id, ruleSet); ids.push(id); this.set('ruleSet', ids); - return newRS; + return id; }, /** @@ -199,12 +169,10 @@ _.extend(fiveui.Settings.prototype, { * * @param {!number} ruleSetId The id of the ruleset that is being modified. * @param {!Object} ruleSet The rule set, as an anonymous JSON object. - * @return {!fiveui.RuleSet} The new RuleSet object. */ updateRuleSet: function(ruleSetId, ruleSet) { var newRS = fiveui.RuleSet.fromJSON(ruleSetId, ruleSet); this.set('ruleSet.'+ruleSetId, newRS); - return newRS; }, /** @@ -212,79 +180,35 @@ _.extend(fiveui.Settings.prototype, { * @return {?fiveui.RuleSet} The RuleSet, or null, if no RuleSet was found. */ getRuleSet: function(id) { - return /** @type {?fiveui.RuleSet} */ this.getById(id, 'ruleSet', fiveui.RuleSet.fromJSON); + return this.getById(id, 'ruleSet', fiveui.RuleSet.fromJSON); }, /** * @param {!number} id The id of the rule set to remove. - * @return {!Array.<fiveui.UrlPat>} null if the remove succeeded, otherwise, - * returns the list of UrlPats that use this - * rule set, if any. */ remRuleSet: function(id) { - var matches = _.map(this.getRuleSetUrlPats(id), function(id) { - return this.getUrlPat(id); - }, this); - - if (0 == matches.length) { - this.remById(id, 'ruleSet'); - } - - return matches; + this.remById(id, 'ruleSet'); }, + /** URL Pattern Management **************************************************/ + /** - * @param {!number} ruleSetId The rule set to retrieve url patterns for. - * @return {Array.<number>} Url pattern ids associated with this rule set. + * Test a url agains the rule set database. Return the first rule set that + * matches, or null if none do. */ - getRuleSetUrlPats: function(ruleSetId) { - var urls = this.getUrls(); - var patIds = []; - - _.each(urls, function(patId) { - var pat = this.getUrlPat(patId); - if(pat.rule_id == ruleSetId) { - patIds.push(patId); - } - }, this); + checkUrl: function(url) { + return _.find(this.getRuleSets(), function(rs) { - return patIds; - }, + var pat = _.find(rs.patterns, function(pat) { + var regex = fiveui.UrlPat.compile(pat); + return regex.test(url); + }); - /** - * @param {!number} id The nuber of the element to retrieve. - * @param {!string} listName The name of the portion of the localstorage hierarchy to search for id. - * @param {!function(number, !Object): *} fromJSON A deserialization function. - * - * @return {*} Either null, or the result of fromJSON. - */ - getById: function(id, listName, fromJSON) { - var obj = this.get(listName + '.' + id); - if (!obj) { - return null; - } + return pat != null; - return fromJSON(id, obj); + }); }, - /** - * @param {!number} id The nuber of the element to retrieve. - * @param {!string} listName The name of the portion of the - * localstorage hierarchy to search for id. - */ - remById: function(id, listName) { - // remove it from the list of ids: - var ids = this.get(listName) || []; - - for (var i = 0; i < ids.length; ++i) { - if (ids[i] == id) { - ids.splice(i, 1); - this.set(listName, ids); - this.remove(listName + '.' + id); - break; - } - } - } }); @@ -297,41 +221,29 @@ fiveui.Settings.manager = function(chan, settings) { var msg = new fiveui.Messenger(chan); + // create a new rule set, and call the response continuation with the created + // object. msg.register('addRuleSet', function(ruleSet,respond){ - var newRS = settings.addRuleSet(ruleSet); - respond(newRS); + var id = settings.addRuleSet(ruleSet) + respond(settings.getRuleSet(id)); }); + // update a rule set, and call the response continuation with the updated + // object. msg.register('updateRuleSet', function(updatedRS,respond){ - var newRS = settings.updateRuleSet(updatedRS.id, updatedRS); - respond(newRS); + settings.updateRuleSet(updatedRS.id, updatedRS); + respond(settings.getRuleSet(updatedRS.id)); }); + // remove a rule set by id. the response continuation is called with no + // argument. msg.register('remRuleSet', function(ruleSetId, respond) { - var pats = settings.remRuleSet(ruleSetId); - respond({ - id: ruleSetId, - pats: pats, - removed: pats.length == 0 - }); - }); - - msg.register('getRuleSetUrlPats', function(ruleSetId, respond) { - var pats = settings.getUrls(); - var patIds = []; - - _.each(pats, function(patId) { - var pat = settings.getUrlPat(patId); - if(pat.rule_id == ruleSetId) { - patIds.push(patId); - } - }); - - respond(patIds); + settings.remRuleSet(ruleSetId); + respond(); }); // Retrieve the manifest, and return the object to the caller. Invokes the - // caller with `null` when the manifest fails to load. + // response continuation with an error object when rule set fails to load. msg.register('loadRuleSet', function(url, respond) { fiveui.RuleSet.load(url, { success:respond, @@ -342,44 +254,27 @@ fiveui.Settings.manager = function(chan, settings) { }); }); + // get a rule set structure by id. invoke the respond continuation with the + // rule set, if it exists, and null if it does not. msg.register('getRuleSet', function(ruleSetId, respond){ respond(settings.getRuleSet(ruleSetId)); }); + // Retrieve the list of all rule sets. invoke the respond continuation with + // the list of rule sets. msg.register('getRuleSets', function(unused, respond) { - var ruleSets = _.map(settings.getRuleSets(), - _.bind(settings.getRuleSet, settings)); - respond(ruleSets); - }); - - msg.register('getUrlPats', function(unused, respond){ - respond(_.map(settings.getUrls(), _.bind(settings.getUrlPat, settings))); - }); - - msg.register('addUrlPat', function(url, respond){ - var urlId = settings.addUrl(url.regex, url.rule_id); - respond(settings.getUrlPat(urlId)); - }); - - msg.register('updateUrlPat', function(pat, respond) { - var obj = settings.getUrlPat(pat.id); - settings.updateUrl(pat.id, pat.regex, pat.rule_id); - respond(pat); - }); - - msg.register('getUrlPat', function(urlPatId, respond){ - respond(settings.getUrlPat(urlPatId)); - }); - - msg.register('remUrlPat', function(urlPatId, respond){ - settings.remUrlPat(urlPatId); - respond(true); + respond(settings.getRuleSets()); }); - msg.register('setDisplayDefault', function(def) { + // sets the value of the 'display default' config option. invokes the respond + // callback with no argument. + msg.register('setDisplayDefault', function(def, respond) { settings.setDisplayDefault(def); + respond(); }); + // get the value of the 'display default' config option. invokes the respond + // callback with the value. msg.register('getDisplayDefault', function(ignored, respond) { respond(settings.getDisplayDefault()); }); diff --git a/src/js/fiveui/options.html b/src/js/fiveui/options.html index 3864a36..0115368 100644 --- a/src/js/fiveui/options.html +++ b/src/js/fiveui/options.html @@ -27,7 +27,6 @@ <link id="favicon" rel="icon" href="images/fiveui-icon-16.png" /> <link rel="stylesheet" href="css/options.css" /> - <link rel="stylesheet" href="css/entry.css" /> <link rel="stylesheet" href="jquery/bundled.css" /> <script src="jquery/jquery-1.8.3.js"></script> <script src="jquery/jquery-ui-1.9.2.custom.js"></script> @@ -39,7 +38,6 @@ <script src="js/options.js"></script> <script src="js/update-manager.js"></script> <script src="js/utils.js"></script> - <script src="js/entry.js"></script> <script src="js/rules.js"></script> <script src="js/url-pat.js"></script> <script src="js/platform-ajax.js"></script> @@ -52,29 +50,19 @@ <div id="navbar-container"> <div id="navbar-content-title">Settings</div> <nav> - <div id="url-defaults">URL Patterns</div> <div id="rule-sets">Rule Sets</div> <div id="basics">Basics</div> </nav> </div> <div id="content"> - <section id="tab-url-defaults"> - <div class="title">URL Patterns</div> - <section> - <ul id="urlPatEntries" class="entries"></ul> - <div> - <button id="addUrlPat">Add</button> - </div> - </section> - </section> <section id="tab-rule-sets"> <div class="title">Rule Sets</div> <section> <ul id="ruleSetEntries" class="entries"></ul> - <div> - <button id="addRsButton">Add</button> + <div class="control"> + <button id="addRsButton">add a rule set</button> </div> </section> </section> @@ -93,6 +81,7 @@ </div> </section> </section> + </div> </body> |