Browse Source

Modulize popup views

Gerald 10 years ago
parent
commit
b4f5b8d625

+ 7 - 2
src/background/main.js

@@ -23,8 +23,13 @@ var commands = {
       return Object.assign(data, res);
     }) : Promise.resolve(data);
   },
-  UpdateMeta: function (data, src) {
-    return vmdb.updateScriptInfo(data.id, data);
+  UpdateScriptInfo: function (data, src) {
+    return vmdb.updateScriptInfo(data.id, data).then(function (script) {
+      _.messenger.post({
+        cmd: 'update',
+        data: script,
+      });
+    });
   },
   SetValue: function (data, src) {
     return vmdb.setValue(data.uri, data.values);

+ 46 - 0
src/cache.js

@@ -26,3 +26,49 @@
 
   _.cache = new Cache();
 }();
+
+var BaseView = Backbone.View.extend({
+  initialize: function () {
+    var _this = this;
+    var gotTemplate;
+    if (_this.templateUrl)
+      gotTemplate = _.cache.get(_this.templateUrl)
+      .then(function (fn) {
+        _this.templateFn = fn;
+      });
+    var render = _this.render.bind(_this);
+    var initI18n = _this.initI18n.bind(_this);
+    _this.render = function () {
+      gotTemplate
+        ? gotTemplate.then(render).then(initI18n)
+        : render();
+      return _this;
+    };
+    _this.render();
+  },
+  initI18n: function () {
+    _.forEach(this.$('[data-i18n]'), function (node) {
+      node.innerHTML = _.i18n(node.dataset.i18n);
+    });
+  },
+  getValue: function (target) {
+    var key = target.dataset.id;
+    var value;
+    switch (key[0]) {
+    case '!':
+      key = key.slice(1);
+      value = target.checked;
+      break;
+    case '[':
+      key = key.slice(1);
+      value = _.filter(target.value.split('\n').map(function (s) {return s.trim();}));
+      break;
+    default:
+      value = target.value;
+    }
+    return {
+      key: key,
+      value: value,
+    };
+  },
+});

+ 11 - 46
src/common.js

@@ -69,6 +69,17 @@ _.getUniqId = function () {
 	return Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
 };
 
+/**
+ * Get locale attributes such as `@name:zh-CN`
+ */
+_.getLocaleString = function (meta, key) {
+  var lang = navigator.languages.find(function (lang) {
+    return (key + ':' + lang) in meta;
+  });
+  if (lang) key += ':' + lang;
+  return meta[key] || '';
+};
+
 /*
 function format() {
   var args = arguments;
@@ -78,49 +89,3 @@ function format() {
   });
 }
 */
-
-var BaseView = Backbone.View.extend({
-  initialize: function () {
-    var _this = this;
-    var gotTemplate;
-    if (_this.templateUrl)
-      gotTemplate = _.cache.get(_this.templateUrl)
-      .then(function (fn) {
-        _this.templateFn = fn;
-      });
-    var render = _this.render.bind(_this);
-    var initI18n = _this.initI18n.bind(_this);
-    _this.render = function () {
-      gotTemplate
-        ? gotTemplate.then(render).then(initI18n)
-        : render();
-      return _this;
-    };
-    _this.render();
-  },
-  initI18n: function () {
-    _.forEach(this.$('[data-i18n]'), function (node) {
-      node.innerHTML = _.i18n(node.dataset.i18n);
-    });
-  },
-  getValue: function (target) {
-    var key = target.dataset.id;
-    var value;
-    switch (key[0]) {
-    case '!':
-      key = key.slice(1);
-      value = target.checked;
-      break;
-    case '[':
-      key = key.slice(1);
-      value = _.filter(target.value.split('\n').map(function (s) {return s.trim();}));
-      break;
-    default:
-      value = target.value;
-    }
-    return {
-      key: key,
-      value: value,
-    };
-  },
-});

+ 1 - 1
src/options/index.html

@@ -12,8 +12,8 @@
     <script src="/lib/zip.js/zip.js"></script>
     <script src="/common.js"></script>
     <script src="/cache.js"></script>
-    <script src="view.js"></script>
     <script src="model.js"></script>
+    <script src="view.js"></script>
   </head>
   <body>
     <div id="app"></div>

+ 1 - 8
src/options/model.js

@@ -6,17 +6,10 @@ var Meta = Backbone.Model.extend({
 });
 
 var Script = Backbone.Model.extend({
-  /**
-   * Get locale attributes such as `@name:zh-CN`
-   */
   getLocaleString: function (key) {
     var _this = this;
     var meta = _this.get('meta') || {};
-    var lang = navigator.languages.find(function (lang) {
-      return (key + ':' + lang) in meta;
-    });
-    if (lang) key += ':' + lang;
-    return meta[key] || '';
+    return _.getLocaleString(meta, key);
   },
   canUpdate: function () {
     var script = this.toJSON();

+ 0 - 0
src/options.js → src/options/options.js


+ 1 - 1
src/options/view.js

@@ -81,7 +81,7 @@ var ScriptView = BaseView.extend({
   onEnable: function () {
     var _this = this;
     _.sendMessage({
-      cmd: 'UpdateMeta',
+      cmd: 'UpdateScriptInfo',
       data: {
         id: _this.model.id,
         enabled: _this.model.get('enabled') ? 0 : 1,

+ 77 - 0
src/popup/app.js

@@ -0,0 +1,77 @@
+var App = Backbone.Router.extend({
+  routes: {
+    '': 'renderMenu',
+    'commands': 'renderCommands',
+  },
+  renderMenu: function () {
+    this.view = new MenuView;
+  },
+  renderCommands: function () {
+    this.view = new CommandsView;
+  },
+});
+var app = new App();
+if (!Backbone.history.start())
+  app.navigate('', {trigger: true, replace: true});
+
+BaseView.prototype.initI18n.call(window);
+
+!function () {
+  function commandClick(e, model) {
+    chrome.tabs.sendMessage(app.currentTab.id, {
+      cmd: 'Command',
+      data: model.get('name'),
+    });
+  }
+  function scriptSymbol(data) {
+    return data ? 'fa-check' : 'fa-times';
+  }
+  function scriptClick(e, model) {
+    var data = !model.get('data');
+    _.sendMessage({
+      cmd: 'UpdateScriptInfo',
+      data: {
+        id: model.get('id'),
+        enabled: data,
+      },
+    }).then(function () {
+      model.set({data: data});
+    });
+  }
+
+  var commands = {
+    SetPopup: function (data, src, callback) {
+      commandsMenu.reset(data.menus.map(function (menu) {
+        return new MenuItem({
+          name: menu[0],
+          symbol: 'fa-hand-o-right',
+          onClick: commandClick,
+        });
+      }));
+      _.sendMessage({
+        cmd: 'GetMetas',
+        data: data.ids,
+      }).then(function (scripts) {
+        scriptsMenu.reset(scripts.map(function (script) {
+          return new MenuItem({
+            id: script.id,
+            name: script.custom.name || _.getLocaleString(script.meta, 'name'),
+            data: script.enabled,
+            symbol: scriptSymbol,
+            title: true,
+            onClick: scriptClick,
+          });
+        }));
+      });
+    },
+  };
+  chrome.runtime.onMessage.addListener(function (req, src, callback) {
+    var func = commands[req.cmd];
+    if (func) func(req.data, src, callback);
+  });
+
+  chrome.tabs.query({currentWindow: true, active: true}, function (tabs) {
+    app.currentTab = tabs[0];
+    chrome.tabs.sendMessage(tabs[0].id, {cmd: 'GetPopup'});
+  });
+}();

+ 2 - 2
src/popup/index.html

@@ -10,11 +10,11 @@
     <script src="/lib/backbone-min.js"></script>
 		<script src="/common.js"></script>
     <script src="/cache.js"></script>
-    <script src="view.js"></script>
     <script src="model.js"></script>
+		<script src="view.js"></script>
 	</head>
   <body>
     <div id="popup"></div>
-		<script src=popup.js></script>
+		<script src="app.js"></script>
 	</body>
 </html>

+ 9 - 0
src/popup/model.js

@@ -0,0 +1,9 @@
+var MenuItem = Backbone.Model.extend({});
+
+
+var Menu = Backbone.Collection.extend({
+  model: MenuItem,
+});
+
+var scriptsMenu = new Menu;
+var commandsMenu = new Menu;

+ 0 - 182
src/popup/popup.js

@@ -1,182 +0,0 @@
-'use strict';
-
-function Menu(data) {
-	this.data = data;
-	var node = this.node = document.createElement('div');
-	if(data.ellipsis) node.classList.add('ellipsis');
-	node.innerHTML = '<i></i> ' + safeHTML(data.name);
-	if('title' in data)
-		node.title = typeof data.title == 'string' ? data.title : data.name;
-	this.bindEvents();
-	this.update(data.value);
-	data.parent.insertBefore(node, data.before);
-}
-Menu.prototype = {
-	update: function(value) {
-		var node = this.node;
-		var data = this.data;
-		if(typeof value != 'undefined') data.value = value;
-		if(data.symbols) {
-			node.firstChild.className = 'fa ' + data.symbols[data.value ? 1 : 0];
-			if(data.symbols.length > 1) {
-				if(value) node.classList.remove('disabled');
-				else node.classList.add('disabled');
-			}
-		}
-	},
-	bindEvents: function() {
-		var events = this.data.events;
-		for(var i in events)
-			this.node.addEventListener(i, events[i].bind(this), false);
-	},
-};
-
-var Popup = function() {
-	var main = $('#main');
-	var commands = $('#commands');
-	var scripts = {};
-	var main_top = main.firstElementChild;
-	var main_bot = main.lastElementChild;
-	var commands_top = commands.firstElementChild;
-	var commands_bot = commands.lastElementChild;
-	var nodeIsApplied;
-	var tab, sep;
-
-	function initMenu(){
-		new Menu({
-			name: _('menuManageScripts'),
-			parent: main_top,
-			symbols: ['fa-hand-o-right'],
-			events: {
-				click: function(e){
-					var url = chrome.extension.getURL('/options.html');
-					chrome.tabs.query({currentWindow: true, url: url}, function(tabs) {
-						if(tabs[0]) chrome.tabs.update(tabs[0].id, {active: true});
-						else chrome.tabs.create({url: url});
-					});
-				},
-			},
-		});
-		if(/^https?:\/\//i.test(tab.url))
-			new Menu({
-				name: _('menuFindScripts'),
-				parent: main_top,
-				symbols: ['fa-hand-o-right'],
-				events: {
-					click: function(){
-						var matches = tab.url.match(/:\/\/(?:www\.)?([^\/]*)/);
-						chrome.tabs.create({url: 'https://greasyfork.org/scripts/search?q=' + matches[1]});
-					},
-				},
-			});
-		nodeIsApplied = new Menu({
-			name: _('menuScriptEnabled'),
-			parent: main_top,
-			symbols: ['fa-times','fa-check'],
-			events: {
-				click: function(e) {
-					var value = !this.data.value;
-					setOption('isApplied', value);
-					this.update(value);
-					chrome.browserAction.setIcon({
-						path: 'images/icon19' + (value ? '' : 'w') + '.png',
-					});
-				},
-			},
-			value: getOption('isApplied'),
-		}).node;
-	}
-
-	function menuScript(script) {
-		if(script && !scripts[script.id]) {
-			scripts[script.id] = script;
-			var name = script.custom.name || getLocaleString(script.meta, 'name');
-			name = name ? safeHTML(name) : '<em>' + _('labelNoName') + '</em>';
-			new Menu({
-				name: name,
-				parent: main_bot,
-				symbols: ['fa-times','fa-check'],
-				title: script.meta.name,
-				events: {
-					click: function(e) {
-						var value = !this.data.value;
-						chrome.runtime.sendMessage({cmd: 'UpdateMeta', data: {id: script.id, enabled: value}});
-						this.update(value);
-					},
-				},
-				value: script.enabled,
-			});
-		}
-	}
-
-	function setData(data) {
-		if(!data) return;
-		if(data.menus && data.menus.length) {
-			new Menu({
-				name: _('menuBack'),
-				parent: commands_top,
-				symbols: ['fa-arrow-left'],
-				events: {
-					click: function(e) {
-						commands.classList.add('hide');
-						main.classList.remove('hide');
-					},
-				},
-			});
-			commands_top.appendChild(document.createElement('hr'));
-			data.menus.forEach(function(menu) {
-				new Menu({
-					name: menu[0],
-					parent: commands_bot,
-					symbols: ['fa-hand-o-right'],
-					events: {
-						click: function(e) {
-							chrome.tabs.sendMessage(tab.id, {cmd:'Command', data:this.data.name});
-						},
-					},
-				});
-			});
-			new Menu({
-				name: _('menuCommands'),
-				parent: main_top,
-				symbols: ['fa-arrow-right'],
-				events: {
-					click: function(e) {
-						main.classList.add('hide');
-						commands.classList.remove('hide');
-					},
-				},
-				before: nodeIsApplied,
-			});
-		}
-		if(data.ids && data.ids.length) {
-			var ids = data.ids.filter(function (id) {
-				return !scripts[id];
-			});
-			if(ids.length)
-				chrome.runtime.sendMessage({cmd: 'GetMetas', data: ids}, function(scripts) {
-					if(!sep)
-						main_top.appendChild(sep = document.createElement('hr'));
-					scripts.forEach(menuScript);
-				});
-		}
-	}
-
-	chrome.tabs.query({currentWindow: true, active: true}, function(tabs) {
-		tab = tabs[0];
-		initMenu();
-		chrome.tabs.sendMessage(tab.id, {cmd: 'GetPopup'});
-	});
-
-	return {
-		setData: setData,
-	};
-}();
-
-chrome.runtime.onMessage.addListener(function(req, src, callback) {
-	var maps = {
-		SetPopup: Popup.setData,
-	}
-	var func = maps[req.cmd];
-	if (func) func(req.data, src, callback);
-});

+ 37 - 0
src/popup/style.css

@@ -0,0 +1,37 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+body {
+  font: menu;
+  font-size: 12px;
+}
+.menu {
+  max-width: 300px;
+  overflow-x: hidden;
+  overflow-y: auto;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+}
+.fa {
+  width: 1em;
+}
+.menu-item {
+  cursor: pointer;
+  padding: 5px;
+}
+.menu-item:hover {
+  background: cornflowerblue;
+  color: white;
+}
+.menu-item.disabled {
+  color: gray;
+}
+.menu-item.disabled:hover {
+  color: silver;
+}
+hr {
+  border: none;
+  border-top: 1px solid silver;
+}

+ 0 - 2
src/popup/templates/list.html

@@ -1,2 +0,0 @@
-<div class="menu ellipsis"></div>
-<div class="menu ellipsis"></div>

+ 3 - 0
src/popup/templates/menu.html

@@ -0,0 +1,3 @@
+<div class="menu"></div>
+<%= it.hasSep ? '<hr>' : '' %>
+<div class="menu"></div>

+ 2 - 0
src/popup/templates/menuitem.html

@@ -0,0 +1,2 @@
+<i class="fa <%- it.symbol %>"></i>
+<%- it.name %>

+ 122 - 4
src/popup/view.js

@@ -1,11 +1,129 @@
-var MenuView = BaseView.extend({
-  templateUrl: '/popup/templates/list.html',
+var MenuItemView = BaseView.extend({
+  className: 'menu-item',
+  templateUrl: '/popup/templates/menuitem.html',
+  events: {
+    'click': 'onClick',
+  },
+  initialize: function () {
+    BaseView.prototype.initialize.call(this);
+    this.listenTo(this.model, 'change', this.render);
+  },
   render: function () {
+    var it = this.model.toJSON();
+    if (typeof it.symbol === 'function')
+      it.symbol = it.symbol(it.data);
+    this.$el.html(this.templateFn(it))
+    .attr('title', it.title === true ? it.name : it.title);
+    if (it.data === false) this.$el.addClass('disabled');
+    else this.$el.removeClass('disabled');
+  },
+  onClick: function (e) {
+    var onClick = this.model.get('onClick');
+    onClick && onClick(e, this.model);
+  },
+})
+
+var MenuBaseView = BaseView.extend({
+  el: '#popup',
+  templateUrl: '/popup/templates/menu.html',
+  addMenuItem: function (obj, parent) {
+    if (!(obj instanceof MenuItem)) obj = new MenuItem(obj);
+    var item = new MenuItemView({model: obj});
+    parent.append(item.$el);
   },
 });
 
-var CommandsView = BaseView.extend({
-  templateUrl: '/popup/templates/list.html',
+var MenuView = MenuBaseView.extend({
+  initialize: function () {
+    MenuBaseView.prototype.initialize.call(this);
+    this.listenTo(scriptsMenu, 'reset', this.render);
+    this.listenTo(commandsMenu, 'reset', this.render);
+  },
+  render: function () {
+    var _this = this;
+    _this.$el.html(_this.templateFn({
+      hasSep: !!scriptsMenu.length
+    }));
+    var children = _this.$el.children();
+    var top = children.first();
+    var bot = children.last();
+    _this.addMenuItem({
+      name: _.i18n('menuManageScripts'),
+      symbol: 'fa-hand-o-right',
+      onClick: function (e) {
+        var url = chrome.extension.getURL(chrome.app.getDetails().options_page);
+        chrome.tabs.query({
+          currentWindow: true,
+          url: url,
+        }, function (tabs) {
+          if (tabs[0]) chrome.tabs.update(tabs[0].id, {active: true});
+          else chrome.tabs.create({url: url});
+        });
+      },
+    }, top);
+    if (app.currentTab && /^https?:\/\//i.test(app.currentTab.url))
+      _this.addMenuItem({
+        name: _.i18n('menuFindScripts'),
+        symbol: 'fa-hand-o-right',
+        onClick: function (e) {
+          var matches = app.currentTab.url.match(/:\/\/(?:www\.)?([^\/]*)/);
+          chrome.tabs.create({
+            url: 'https://greasyfork.org/scripts/search?q=' + matches[1],
+          });
+        },
+      }, top);
+    if (commandsMenu.length) _this.addMenuItem({
+      name: _.i18n('menuCommands'),
+      symbol: 'fa-arrow-right',
+      onClick: function (e) {
+        app.navigate('commands', {trigger: true});
+      },
+    }, top);
+    _this.addMenuItem({
+      name: _.i18n('menuScriptEnabled'),
+      data: _.options.get('isApplied'),
+      symbol: function (data) {
+        return data ? 'fa-check' : 'fa-times';
+      },
+      onClick: function (e, model) {
+        var isApplied = !model.get('data');
+        _.options.set('isApplied', isApplied);
+        model.set({data: isApplied});
+        chrome.browserAction.setIcon({
+          path: 'images/icon19' + (isApplied ? '' : 'w') + '.png',
+        });
+      },
+    }, top);
+    scriptsMenu.each(function (item) {
+      _this.addMenuItem(item, bot);
+    });
+  },
+});
+
+var CommandsView = MenuBaseView.extend({
+  initialize: function () {
+    MenuBaseView.prototype.initialize.call(this);
+    this.listenTo(commandsMenu, 'reset', this.render);
+  },
   render: function () {
+    if (!commandsMenu.length)
+      return app.navigate('', {trigger: true, replace: true});
+    var _this = this;
+    _this.$el.html(_this.templateFn({
+      hasSep: true
+    }));
+    var children = _this.$el.children();
+    var top = children.first();
+    var bot = children.last();
+    _this.addMenuItem({
+      name: _.i18n('menuBack'),
+      symbol: 'fa-arrow-left',
+      onClick: function (e) {
+        app.navigate('', {trigger: true});
+      },
+    }, top);
+    commandsMenu.each(function (item) {
+      _this.addMenuItem(item, bot);
+    });
   },
 });