addon-sdk/source/examples/annotator/lib/main.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/examples/annotator/lib/main.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,266 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +var widgets = require('sdk/widget');
     1.9 +var pageMod = require('sdk/page-mod');
    1.10 +var data = require('sdk/self').data;
    1.11 +var panels = require('sdk/panel');
    1.12 +var simpleStorage = require('sdk/simple-storage');
    1.13 +var notifications = require("sdk/notifications");
    1.14 +
    1.15 +/*
    1.16 +Global variables
    1.17 +* Boolean to indicate whether the add-on is switched on or not
    1.18 +* Array for all workers associated with the 'selector' page mod
    1.19 +* Array for all workers associated with the 'matcher' page mod
    1.20 +*/
    1.21 +var annotatorIsOn = false;
    1.22 +var selectors = [];
    1.23 +var matchers = [];
    1.24 +
    1.25 +if (!simpleStorage.storage.annotations)
    1.26 +  simpleStorage.storage.annotations = [];
    1.27 +
    1.28 +/*
    1.29 +Update the matchers: call this whenever the set of annotations changes
    1.30 +*/
    1.31 +function updateMatchers() {
    1.32 +  matchers.forEach(function (matcher) {
    1.33 +    matcher.postMessage(simpleStorage.storage.annotations);
    1.34 +  });
    1.35 +}
    1.36 +
    1.37 +/*
    1.38 +Constructor for an Annotation object
    1.39 +*/
    1.40 +function Annotation(annotationText, anchor) {
    1.41 +  this.annotationText = annotationText;
    1.42 +  this.url = anchor[0];
    1.43 +  this.ancestorId = anchor[1];
    1.44 +  this.anchorText = anchor[2];
    1.45 +}
    1.46 +
    1.47 +/*
    1.48 +Function to deal with a new annotation.
    1.49 +Create a new annotation object, store it, and
    1.50 +notify all the annotators of the change.
    1.51 +*/
    1.52 +function handleNewAnnotation(annotationText, anchor) {
    1.53 +  var newAnnotation = new Annotation(annotationText, anchor);
    1.54 +  simpleStorage.storage.annotations.push(newAnnotation);
    1.55 +  updateMatchers();
    1.56 +}
    1.57 +
    1.58 +/*
    1.59 +Function to tell the selector page mod that the add-on has become (in)active
    1.60 +*/
    1.61 +function activateSelectors() {
    1.62 +  selectors.forEach(
    1.63 +    function (selector) {
    1.64 +      selector.postMessage(annotatorIsOn);
    1.65 +  });
    1.66 +}
    1.67 +
    1.68 +/*
    1.69 +Toggle activation: update the on/off state and notify the selectors.
    1.70 +*/
    1.71 +function toggleActivation() {
    1.72 +  annotatorIsOn = !annotatorIsOn;
    1.73 +  activateSelectors();
    1.74 +  return annotatorIsOn;
    1.75 +}
    1.76 +
    1.77 +function detachWorker(worker, workerArray) {
    1.78 +  var index = workerArray.indexOf(worker);
    1.79 +  if(index != -1) {
    1.80 +    workerArray.splice(index, 1);
    1.81 +  }
    1.82 +}
    1.83 +
    1.84 +exports.main = function() {
    1.85 +
    1.86 +/*
    1.87 +The widget provides a mechanism to switch the selector on or off, and to
    1.88 +view the list of annotations.
    1.89 +
    1.90 +The selector is switched on/off with a left-click, and the list of annotations
    1.91 +is displayed on a right-click.
    1.92 +*/
    1.93 +  var widget = widgets.Widget({
    1.94 +    id: 'toggle-switch',
    1.95 +    label: 'Annotator',
    1.96 +    contentURL: data.url('widget/pencil-off.png'),
    1.97 +    contentScriptWhen: 'ready',
    1.98 +    contentScriptFile: data.url('widget/widget.js')
    1.99 +  });
   1.100 +
   1.101 +  widget.port.on('left-click', function() {
   1.102 +    console.log('activate/deactivate');
   1.103 +    widget.contentURL = toggleActivation() ?
   1.104 +              data.url('widget/pencil-on.png') :
   1.105 +              data.url('widget/pencil-off.png');
   1.106 +  });
   1.107 +
   1.108 +  widget.port.on('right-click', function() {
   1.109 +      console.log('show annotation list');
   1.110 +      annotationList.show();
   1.111 +  });
   1.112 +
   1.113 +/*
   1.114 +The selector page-mod enables the user to select page elements to annotate.
   1.115 +
   1.116 +It is attached to all pages but only operates if the add-on is active.
   1.117 +
   1.118 +The content script highlights any page elements which can be annotated. If the
   1.119 +user clicks a highlighted element it sends a message to the add-on containing
   1.120 +information about the element clicked, which is called the anchor of the
   1.121 +annotation.
   1.122 +
   1.123 +When we receive this message we assign the anchor to the annotationEditor and
   1.124 +display it.
   1.125 +*/
   1.126 +  var selector = pageMod.PageMod({
   1.127 +    include: ['*'],
   1.128 +    contentScriptWhen: 'ready',
   1.129 +    contentScriptFile: [data.url('jquery-1.4.2.min.js'),
   1.130 +                        data.url('selector.js')],
   1.131 +    onAttach: function(worker) {
   1.132 +      worker.postMessage(annotatorIsOn);
   1.133 +      selectors.push(worker);
   1.134 +      worker.port.on('show', function(data) {
   1.135 +        annotationEditor.annotationAnchor = data;
   1.136 +        annotationEditor.show();
   1.137 +      });
   1.138 +      worker.on('detach', function () {
   1.139 +        detachWorker(this, selectors);
   1.140 +      });
   1.141 +    }
   1.142 +  });
   1.143 +
   1.144 +/*
   1.145 +The annotationEditor panel is the UI component used for creating
   1.146 +new annotations. It contains a text area for the user to
   1.147 +enter the annotation.
   1.148 +
   1.149 +When we are ready to display the editor we assign its 'anchor' property
   1.150 +and call its show() method.
   1.151 +
   1.152 +Its content script sends the content of the text area to the add-on
   1.153 +when the user presses the return key.
   1.154 +
   1.155 +When we receives this message we create a new annotation using the anchor
   1.156 +and the text the user entered, store it, and hide the panel.
   1.157 +*/
   1.158 +  var annotationEditor = panels.Panel({
   1.159 +    width: 220,
   1.160 +    height: 220,
   1.161 +    contentURL: data.url('editor/annotation-editor.html'),
   1.162 +    contentScriptFile: data.url('editor/annotation-editor.js'),
   1.163 +    onMessage: function(annotationText) {
   1.164 +      if (annotationText)
   1.165 +        handleNewAnnotation(annotationText, this.annotationAnchor);
   1.166 +      annotationEditor.hide();
   1.167 +    },
   1.168 +    onShow: function() {
   1.169 +      this.postMessage('focus');
   1.170 +    }
   1.171 +  });
   1.172 +
   1.173 +/*
   1.174 +The annotationList panel is the UI component that lists all the annotations
   1.175 +the user has entered.
   1.176 +
   1.177 +On its 'show' event we pass it the array of annotations.
   1.178 +
   1.179 +The content script creates the HTML elements for the annotations, and
   1.180 +intercepts clicks on the links, passing them back to the add-on to open them
   1.181 +in the browser.
   1.182 +*/
   1.183 +  var annotationList = panels.Panel({
   1.184 +    width: 420,
   1.185 +    height: 200,
   1.186 +    contentURL: data.url('list/annotation-list.html'),
   1.187 +    contentScriptFile: [data.url('jquery-1.4.2.min.js'),
   1.188 +                        data.url('list/annotation-list.js')],
   1.189 +    contentScriptWhen: 'ready',
   1.190 +    onShow: function() {
   1.191 +      this.postMessage(simpleStorage.storage.annotations);
   1.192 +    },
   1.193 +    onMessage: function(message) {
   1.194 +      require('sdk/tabs').open(message);
   1.195 +    }
   1.196 +  });
   1.197 +
   1.198 +/*
   1.199 +We listen for the OverQuota event from simple-storage.
   1.200 +If it fires we just notify the user and delete the most
   1.201 +recent annotations until we are back in quota.
   1.202 +*/
   1.203 +  simpleStorage.on("OverQuota", function () {
   1.204 +    notifications.notify({
   1.205 +      title: 'Storage space exceeded',
   1.206 +      text: 'Removing recent annotations'});
   1.207 +    while (simpleStorage.quotaUsage > 1)
   1.208 +      simpleStorage.storage.annotations.pop();
   1.209 +  });
   1.210 +
   1.211 +/*
   1.212 +The matcher page-mod locates anchors on web pages and prepares for the
   1.213 +annotation to be displayed.
   1.214 +
   1.215 +It is attached to all pages, and when it is attached we pass it the complete
   1.216 +list of annotations. It looks for anchors in its page. If it finds one it
   1.217 +highlights the anchor and binds mouseenter/mouseout events to 'show' and 'hide'
   1.218 +messages to the add-on.
   1.219 +
   1.220 +When the add-on receives the 'show' message it assigns the annotation text to
   1.221 +the annotation panel and shows it.
   1.222 +
   1.223 +Note that the matcher is active whether or not the add-on is active:
   1.224 +'inactive' only means that the user can't create new add-ons, they can still
   1.225 +see old ones.
   1.226 +*/
   1.227 +  var matcher = pageMod.PageMod({
   1.228 +    include: ['*'],
   1.229 +    contentScriptWhen: 'ready',
   1.230 +    contentScriptFile: [data.url('jquery-1.4.2.min.js'),
   1.231 +                        data.url('matcher.js')],
   1.232 +    onAttach: function(worker) {
   1.233 +      if(simpleStorage.storage.annotations) {
   1.234 +        worker.postMessage(simpleStorage.storage.annotations);
   1.235 +      }
   1.236 +      worker.port.on('show', function(data) {
   1.237 +        annotation.content = data;
   1.238 +        annotation.show();
   1.239 +      });
   1.240 +      worker.port.on('hide', function() {
   1.241 +        annotation.content = null;
   1.242 +        annotation.hide();
   1.243 +      });
   1.244 +      worker.on('detach', function () {
   1.245 +        detachWorker(this, matchers);
   1.246 +      });
   1.247 +      matchers.push(worker);
   1.248 +    }
   1.249 +  });
   1.250 +
   1.251 +/*
   1.252 +The annotation panel is the UI component that displays an annotation.
   1.253 +
   1.254 +When we are ready to show it we assign its 'content' attribute to contain
   1.255 +the annotation text, and that gets sent to the content process in onShow().
   1.256 +*/
   1.257 +  var annotation = panels.Panel({
   1.258 +    width: 200,
   1.259 +    height: 180,
   1.260 +    contentURL: data.url('annotation/annotation.html'),
   1.261 +    contentScriptFile: [data.url('jquery-1.4.2.min.js'),
   1.262 +                        data.url('annotation/annotation.js')],
   1.263 +    contentScriptWhen: 'ready',
   1.264 +    onShow: function() {
   1.265 +      this.postMessage(this.content);
   1.266 +    }
   1.267 +  });
   1.268 +
   1.269 +}

mercurial