Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 var widgets = require('sdk/widget');
6 var pageMod = require('sdk/page-mod');
7 var data = require('sdk/self').data;
8 var panels = require('sdk/panel');
9 var simpleStorage = require('sdk/simple-storage');
10 var notifications = require("sdk/notifications");
12 /*
13 Global variables
14 * Boolean to indicate whether the add-on is switched on or not
15 * Array for all workers associated with the 'selector' page mod
16 * Array for all workers associated with the 'matcher' page mod
17 */
18 var annotatorIsOn = false;
19 var selectors = [];
20 var matchers = [];
22 if (!simpleStorage.storage.annotations)
23 simpleStorage.storage.annotations = [];
25 /*
26 Update the matchers: call this whenever the set of annotations changes
27 */
28 function updateMatchers() {
29 matchers.forEach(function (matcher) {
30 matcher.postMessage(simpleStorage.storage.annotations);
31 });
32 }
34 /*
35 Constructor for an Annotation object
36 */
37 function Annotation(annotationText, anchor) {
38 this.annotationText = annotationText;
39 this.url = anchor[0];
40 this.ancestorId = anchor[1];
41 this.anchorText = anchor[2];
42 }
44 /*
45 Function to deal with a new annotation.
46 Create a new annotation object, store it, and
47 notify all the annotators of the change.
48 */
49 function handleNewAnnotation(annotationText, anchor) {
50 var newAnnotation = new Annotation(annotationText, anchor);
51 simpleStorage.storage.annotations.push(newAnnotation);
52 updateMatchers();
53 }
55 /*
56 Function to tell the selector page mod that the add-on has become (in)active
57 */
58 function activateSelectors() {
59 selectors.forEach(
60 function (selector) {
61 selector.postMessage(annotatorIsOn);
62 });
63 }
65 /*
66 Toggle activation: update the on/off state and notify the selectors.
67 */
68 function toggleActivation() {
69 annotatorIsOn = !annotatorIsOn;
70 activateSelectors();
71 return annotatorIsOn;
72 }
74 function detachWorker(worker, workerArray) {
75 var index = workerArray.indexOf(worker);
76 if(index != -1) {
77 workerArray.splice(index, 1);
78 }
79 }
81 exports.main = function() {
83 /*
84 The widget provides a mechanism to switch the selector on or off, and to
85 view the list of annotations.
87 The selector is switched on/off with a left-click, and the list of annotations
88 is displayed on a right-click.
89 */
90 var widget = widgets.Widget({
91 id: 'toggle-switch',
92 label: 'Annotator',
93 contentURL: data.url('widget/pencil-off.png'),
94 contentScriptWhen: 'ready',
95 contentScriptFile: data.url('widget/widget.js')
96 });
98 widget.port.on('left-click', function() {
99 console.log('activate/deactivate');
100 widget.contentURL = toggleActivation() ?
101 data.url('widget/pencil-on.png') :
102 data.url('widget/pencil-off.png');
103 });
105 widget.port.on('right-click', function() {
106 console.log('show annotation list');
107 annotationList.show();
108 });
110 /*
111 The selector page-mod enables the user to select page elements to annotate.
113 It is attached to all pages but only operates if the add-on is active.
115 The content script highlights any page elements which can be annotated. If the
116 user clicks a highlighted element it sends a message to the add-on containing
117 information about the element clicked, which is called the anchor of the
118 annotation.
120 When we receive this message we assign the anchor to the annotationEditor and
121 display it.
122 */
123 var selector = pageMod.PageMod({
124 include: ['*'],
125 contentScriptWhen: 'ready',
126 contentScriptFile: [data.url('jquery-1.4.2.min.js'),
127 data.url('selector.js')],
128 onAttach: function(worker) {
129 worker.postMessage(annotatorIsOn);
130 selectors.push(worker);
131 worker.port.on('show', function(data) {
132 annotationEditor.annotationAnchor = data;
133 annotationEditor.show();
134 });
135 worker.on('detach', function () {
136 detachWorker(this, selectors);
137 });
138 }
139 });
141 /*
142 The annotationEditor panel is the UI component used for creating
143 new annotations. It contains a text area for the user to
144 enter the annotation.
146 When we are ready to display the editor we assign its 'anchor' property
147 and call its show() method.
149 Its content script sends the content of the text area to the add-on
150 when the user presses the return key.
152 When we receives this message we create a new annotation using the anchor
153 and the text the user entered, store it, and hide the panel.
154 */
155 var annotationEditor = panels.Panel({
156 width: 220,
157 height: 220,
158 contentURL: data.url('editor/annotation-editor.html'),
159 contentScriptFile: data.url('editor/annotation-editor.js'),
160 onMessage: function(annotationText) {
161 if (annotationText)
162 handleNewAnnotation(annotationText, this.annotationAnchor);
163 annotationEditor.hide();
164 },
165 onShow: function() {
166 this.postMessage('focus');
167 }
168 });
170 /*
171 The annotationList panel is the UI component that lists all the annotations
172 the user has entered.
174 On its 'show' event we pass it the array of annotations.
176 The content script creates the HTML elements for the annotations, and
177 intercepts clicks on the links, passing them back to the add-on to open them
178 in the browser.
179 */
180 var annotationList = panels.Panel({
181 width: 420,
182 height: 200,
183 contentURL: data.url('list/annotation-list.html'),
184 contentScriptFile: [data.url('jquery-1.4.2.min.js'),
185 data.url('list/annotation-list.js')],
186 contentScriptWhen: 'ready',
187 onShow: function() {
188 this.postMessage(simpleStorage.storage.annotations);
189 },
190 onMessage: function(message) {
191 require('sdk/tabs').open(message);
192 }
193 });
195 /*
196 We listen for the OverQuota event from simple-storage.
197 If it fires we just notify the user and delete the most
198 recent annotations until we are back in quota.
199 */
200 simpleStorage.on("OverQuota", function () {
201 notifications.notify({
202 title: 'Storage space exceeded',
203 text: 'Removing recent annotations'});
204 while (simpleStorage.quotaUsage > 1)
205 simpleStorage.storage.annotations.pop();
206 });
208 /*
209 The matcher page-mod locates anchors on web pages and prepares for the
210 annotation to be displayed.
212 It is attached to all pages, and when it is attached we pass it the complete
213 list of annotations. It looks for anchors in its page. If it finds one it
214 highlights the anchor and binds mouseenter/mouseout events to 'show' and 'hide'
215 messages to the add-on.
217 When the add-on receives the 'show' message it assigns the annotation text to
218 the annotation panel and shows it.
220 Note that the matcher is active whether or not the add-on is active:
221 'inactive' only means that the user can't create new add-ons, they can still
222 see old ones.
223 */
224 var matcher = pageMod.PageMod({
225 include: ['*'],
226 contentScriptWhen: 'ready',
227 contentScriptFile: [data.url('jquery-1.4.2.min.js'),
228 data.url('matcher.js')],
229 onAttach: function(worker) {
230 if(simpleStorage.storage.annotations) {
231 worker.postMessage(simpleStorage.storage.annotations);
232 }
233 worker.port.on('show', function(data) {
234 annotation.content = data;
235 annotation.show();
236 });
237 worker.port.on('hide', function() {
238 annotation.content = null;
239 annotation.hide();
240 });
241 worker.on('detach', function () {
242 detachWorker(this, matchers);
243 });
244 matchers.push(worker);
245 }
246 });
248 /*
249 The annotation panel is the UI component that displays an annotation.
251 When we are ready to show it we assign its 'content' attribute to contain
252 the annotation text, and that gets sent to the content process in onShow().
253 */
254 var annotation = panels.Panel({
255 width: 200,
256 height: 180,
257 contentURL: data.url('annotation/annotation.html'),
258 contentScriptFile: [data.url('jquery-1.4.2.min.js'),
259 data.url('annotation/annotation.js')],
260 contentScriptWhen: 'ready',
261 onShow: function() {
262 this.postMessage(this.content);
263 }
264 });
266 }