Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: static/js/animation.js

Issue 4661048523096064: Issue 2120 - Add support for animations. (Closed)
Patch Set: Addressed feedback. Created April 13, 2015, 9:03 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « filters/inline_file.py ('k') | templates/raw.tmpl » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * This file is part of the Adblock Plus website,
3 * Copyright (C) 2006-2015 Eyeo GmbH
4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 if (typeof window.addEventListener != "undefined") {
19 window.addEventListener("load", initAnimations, false);
20 document.addEventListener("click", loadAnimation, false);
21 }
22
23 var ns = "https://adblockplus.org/animation";
24 var loadPrefix = "animations/";
25 var loadSuffix = ".xml";
26
27 function initAnimations() {
28 var list = document.getElementsByTagNameNS(ns, "animation");
29 if (list.length == 0)
30 list = document.getElementsByTagName("animation"); // HTML mode, Anwiki
31 for (var i = 0; i < list.length; i++) {
32 var node = list[i];
33 if (!node.hasAttribute("name") || !node.hasAttribute("label"))
34 continue;
35
36 var animation = document.createElement("div");
37 animation.setAttribute("class", "animation");
38 animation.setAttribute("name", node.getAttribute("name"));
39 animation.setAttribute("started", false);
40 animation.style.margin = "5px";
41
42 var link = document.createElement("a");
43 link.appendChild(document.createTextNode(node.getAttribute("label")));
44 animation.appendChild(link);
45
46 node.parentNode.insertBefore(animation, node);
47 }
48 }
49
50 function loadAnimation(event) {
51 var animation = event.target;
52 while (animation && animation.nodeType == 1 && animation.className != "animati on")
53 animation = animation.parentNode;
54
55 if (!animation || animation.nodeType != 1 || animation.getAttribute("started") == "true")
56 return;
57
58 animation.setAttribute("started", "true");
59
60 while (animation.firstChild)
61 animation.removeChild(animation.firstChild);
62
63 animation.style.border = "1px dashed black";
64 animation.style.padding = "10px";
65 animation.style.width = "50px";
66 animation.style.height = "50px";
67
68 var container = document.createElement("div");
69 container.style.width = "0px";
70 container.style.height = "0px";
71 container.style.overflow = "visible";
72 container.style.position = "relative";
73 container.style.visibility = "hidden";
74
75 var image = document.createElement("img");
76 container.appendChild(image);
77 animation.appendChild(container);
78
79 image.onload = function() {
80 container.style.left = Math.round((50 - image.width) / 2) + "px";
81 container.style.top = Math.round((50 - image.height) / 2) + "px";
82 container.style.visibility = "";
83 }
84 image.src = "data:image/gif;base64,R0lGODlhGAAYAPQAAP%2F%2F%2FwCA%2F87m%2Fvr8% 2FuDv%2FrDX%2Fujz%2Fo7G%2Fsjj%2FpzN%2Ftjr%2FqjT%2FsDf%2FvL4%2Fna6%2FobC%2Frjb%2F miz%2FgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH%2BGkNyZWF0ZWQgd 2l0aCBhamF4bG9hZC5pbmZvACH5BAAHAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAGAAYAAAFriAgj iQAQWVaDgr5POSgkoTDjFE0NoQ8iw8HQZQTDQjDn4jhSABhAAOhoTqSDg7qSUQwxEaEwwFhXHhHgzOA1 xshxAnfTzotGRaHglJqkJcaVEqCgyoCBQkJBQKDDXQGDYaIioyOgYSXA36XIgYMBWRzXZoKBQUMmil0l galLSIClgBpO0g%2Bs26nUWddXyoEDIsACq5SsTMMDIECwUdJPw0Mzsu0qHYkw72bBmozIQAh%2BQQAB wABACwAAAAAGAAYAAAFsCAgjiTAMGVaDgR5HKQwqKNxIKPjjFCk0KNXC6ATKSI7oAhxWIhezwhENTCQE oeGCdWIPEgzESGxEIgGBWstEW4QCGGAIJEoxGmGt5ZkgCRQQHkGd2CESoeIIwoMBQUMP4cNeQQGDYuNj 4iSb5WJnmeGng0CDGaBlIQEJziHk3sABidDAHBgagButSKvAAoyuHuUYHgCkAZqebw0AgLBQyyzNKO3b yNuoSS8x8OfwIchACH5BAAHAAIALAAAAAAYABgAAAW4ICCOJIAgZVoOBJkkpDKoo5EI43GMjNPSokXCI NKJCI4HcCRIQEQvqIOhGhBHhUTDhGo4diOZyFAoKEQDxra2mAEgjghOpCgz3LTBIxJ5kgwMBShACREHZ 1V4Kg1rS44pBAgMDAg%2FSw0GBAQGDZGTlY%2BYmpyPpSQDiqYiDQoCliqZBqkGAgKIS5kEjQ21VwCyp 76dBHiNvz%2BMR74AqSOdVwbQuo%2Babppo10ssjdkAnc0rf8vgl8YqIQAh%2BQQABwADACwAAAAAGAA YAAAFrCAgjiQgCGVaDgZZFCQxqKNRKGOSjMjR0qLXTyciHA7AkaLACMIAiwOC1iAxCrMToHHYjWQiA4N BEA0Q1RpWxHg4cMXxNDk4OBxNUkPAQAEXDgllKgMzQA1pSYopBgonCj9JEA8REQ8QjY%2BRQJOVl4ugo YssBJuMpYYjDQSliwasiQOwNakALKqsqbWvIohFm7V6rQAGP6%2BJQLlFg7KDQLKJrLjBKbvAor3IKiE AIfkEAAcABAAsAAAAABgAGAAABbUgII4koChlmhokw5DEoI4NQ4xFMQoJO4uuhignMiQWvxGBIQC%2BA JBEUyUcIRiyE6CR0CllW4HABxBURTUw4nC4FcWo5CDBRpQaCoF7VjgsyCUDYDMNZ0mHdwYEBAaGMwwHD g4HDA2KjI4qkJKUiJ6faJkiA4qAKQkRB3E0i6YpAw8RERAjA4tnBoMApCMQDhFTuySKoSKMJAq6rD4Gz ASiJYtgi6PUcs9Kew0xh7rNJMqIhYchACH5BAAHAAUALAAAAAAYABgAAAW0ICCOJEAQZZo2JIKQxqCOj WCMDDMqxT2LAgELkBMZCoXfyCBQiFwiRsGpku0EshNgUNAtrYPT0GQVNRBWwSKBMp98P24iISgNDAS4i pGA6JUpA2WAhDR4eWM%2FCAkHBwkIDYcGiTOLjY%2BFmZkNlCN3eUoLDmwlDW%2BAAwcODl5bYl8wCVY MDw5UWzBtnAANEQ8kBIM0oAAGPgcREIQnVloAChEOqARjzgAQEbczg8YkWJq8nSUhACH5BAAHAAYALAA AAAAYABgAAAWtICCOJGAYZZoOpKKQqDoORDMKwkgwtiwSBBYAJ2owGL5RgxBziQQMgkwoMkhNqAEDARP SaiMDFdDIiRSFQowMXE8Z6RdpYHWnEAWGPVkajPmARVZMPUkCBQkJBQINgwaFPoeJi4GVlQ2Qc3VJBQc LV0ptfAMJBwdcIl%2BFYjALQgimoGNWIhAQZA4HXSpLMQ8PIgkOSHxAQhERPw7ASTSFyCMMDqBTJL8tf 3y2fCEAIfkEAAcABwAsAAAAABgAGAAABa8gII4k0DRlmg6kYZCoOg5EDBDEaAi2jLO3nEkgkMEIL4BLp BAkVy3hCTAQKGAznM0AFNFGBAbj2cA9jQixcGZAGgECBu%2F9HnTp%2BFGjjezJFAwFBQwKe2Z%2BKoC ChHmNjVMqA21nKQwJEJRlbnUFCQlFXlpeCWcGBUACCwlrdw8RKGImBwktdyMQEQciB7oACwcIeA4RVwA ODiIGvHQKERAjxyMIB5QlVSTLYLZ0sW8hACH5BAAHAAgALAAAAAAYABgAAAW0ICCOJNA0ZZoOpGGQrDo OBCoSxNgQsQzgMZyIlvOJdi%2BAS2SoyXrK4umWPM5wNiV0UDUIBNkdoepTfMkA7thIECiyRtUAGq8fm 2O4jIBgMBA1eAZ6Knx%2BgHaJR4QwdCMKBxEJRggFDGgQEREPjjAMBQUKIwIRDhBDC2QNDDEKoEkDoiM HDigICGkJBS2dDA6TAAnAEAkCdQ8ORQcHTAkLcQQODLPMIgIJaCWxJMIkPIoAt3EhACH5BAAHAAkALAA AAAAYABgAAAWtICCOJNA0ZZoOpGGQrDoOBCoSxNgQsQzgMZyIlvOJdi%2BAS2SoyXrK4umWHM5wNiV0U N3xdLiqr%2BmENcWpM9TIbrsBkEck8oC0DQqBQGGIz%2Bt3eXtob0ZTPgNrIwQJDgtGAgwCWSIMDg4Hi iUIDAxFAAoODwxDBWINCEGdSTQkCQcoegADBaQ6MggHjwAFBZUFCm0HB0kJCUy9bAYHCCPGIwqmRq0jy SMGmj6yRiEAIfkEAAcACgAsAAAAABgAGAAABbIgII4k0DRlmg6kYZCsOg4EKhLE2BCxDOAxnIiW84l2L 4BLZKipBopW8XRLDkeCiAMyMvQAA%2BuON4JEIo%2BvqukkKQ6RhLHplVGN%2BLyKcXA4Dgx5DWwGDXx %2BgIKENnqNdzIDaiMECwcFRgQCCowiCAcHCZIlCgICVgSfCEMMnA0CXaU2YSQFoQAKUQMMqjoyAglcA AyBAAIMRUYLCUkFlybDeAYJryLNk6xGNCTQXY0juHghACH5BAAHAAsALAAAAAAYABgAAAWzICCOJNA0Z VoOAmkY5KCSSgSNBDE2hDyLjohClBMNij8RJHIQvZwEVOpIekRQJyJs5AMoHA%2BGMbE1lnm9EcPhOHR nhpwUl3AsknHDm5RN%2Bv8qCAkHBwkIfw1xBAYNgoSGiIqMgJQifZUjBhAJYj95ewIJCQV7KYpzBAkLL QADCHOtOpY5PgNlAAykAEUsQ1wzCgWdCIdeArczBQVbDJ0NAqyeBb64nQAGArBTt8R8mLuyPyEAOwAAA AAAAAAAAA%3D%3D";
85
86 var request = new XMLHttpRequest();
87 request.open("GET", loadPrefix + encodeURIComponent(animation.getAttribute("na me")) + loadSuffix);
88 request.onload = function() {
89 if (!request.responseXML || !request.responseXML.documentElement)
90 return;
91
92 animation._data = request.responseXML.documentElement;
93 initAnimation(animation);
94 }
95 request.send(null);
96 }
97
98 function initAnimation(animation) {
99 while (animation.firstChild)
100 animation.removeChild(animation.firstChild);
101
102 var data = animation._data;
103 animation._width = (parseInt(data.getAttribute("width")) || 150);
104 animation._height = (parseInt(data.getAttribute("height")) || 150);
105 animation._stepSize = parseInt(data.getAttribute("step")) || 20;
106
107 animation.style.width = animation._width + "px";
108 animation.style.height = animation._height + "px";
109
110 var count = 0;
111 var initializer = function(event) {
112 count--;
113 if (count <= 0)
114 postInitAnimation(animation);
115 }
116
117 animation._objects = {};
118 var list = data.getElementsByTagNameNS(ns, "object");
119 for (var i = 0; i < list.length; i++) {
120 var node = list[i];
121 if (!node.hasAttribute("id") || !node.hasAttribute("src"))
122 continue;
123
124 count++;
125
126 var image = document.createElement("img");
127 image.addEventListener("load", initializer, false);
128 image.src = node.getAttribute("src");
129
130 var object = document.createElement("div");
131 object.style.width = "0px";
132 object.style.height = "0px";
133 object.style.overflow = "visible";
134 object.style.position = "relative";
135 object.style.visibility = "hidden";
136 object._anchor = node.getAttribute("anchor");
137 object._coords = [0,0];
138
139 object.appendChild(image);
140 animation.insertBefore(object, animation.firstChild);
141 animation._objects[node.getAttribute("id")] = object;
142 }
143 }
144
145 function postInitAnimation(animation) {
146 for (var key in animation._objects) {
147 var object = animation._objects[key];
148 var image = object.firstChild;
149
150 object._size = [image.width, image.height];
151 var offset = parseCoords(object._anchor);
152 if (offset) {
153 offset[0] = -offset[0];
154 offset[1] = -offset[1];
155 }
156 else {
157 offset = [0, 0];
158 if (object._anchor)
159 processAnchor(offset, object._anchor, [-image.width, -image.height]);
160 }
161 object._offset = offset;
162 }
163
164 animation._actions = [];
165
166 var getStepData = function(step) {
167 if (typeof animation._actions[step] == "undefined")
168 animation._actions[step] = [];
169
170 return animation._actions[step];
171 }
172
173 var list = animation._data.getElementsByTagNameNS(ns, "action");
174 var timestamp = 0;
175 for (var i = 0; i < list.length; i++) {
176 var node = list[i];
177 var prevTimestamp = timestamp;
178 timestamp += (parseInt(node.getAttribute("delay")) || 0);
179
180 // Update position for moving objects
181 for (var t = prevTimestamp + 1; t < timestamp; t++) {
182 if (typeof animation._actions[t] != "undefined") {
183 var curActions = animation._actions[t];
184 for (var j = 0; j < curActions.length; j++)
185 if (typeof curActions[j].obj != "undefined")
186 curActions[j].obj._coords = curActions[j].coords;
187 }
188 }
189
190 for (var child = node.firstChild; child; child = child.nextSibling) {
191 if (child.nodeType != 1)
192 continue;
193
194 var objects = child.getAttribute("object");
195 if (objects) {
196 objects = objects.split(/\s*,\s*/);
197 for (var j = 0; j < objects.length; j++) {
198 if (typeof animation._objects[objects[j]] == "undefined")
199 objects.splice(j--, 1);
200 else
201 objects[j] = animation._objects[objects[j]];
202 }
203 }
204 if (objects.length == 0)
205 continue;
206
207 if (child.localName == "show") {
208 var coords = parseRelativeCoords(animation._objects, child, "coords", "c oordsOf", "anchor", "offset");
209 if (coords) {
210 var data = getStepData(timestamp);
211 for (var j = 0; j < objects.length; j++) {
212 objects[j]._coords = coords;
213 data.push({obj: objects[j], coords: coords});
214 }
215 }
216 }
217 else if (child.localName == "hide" || child.localName == "replace") {
218 var data = getStepData(timestamp);
219 data.push.apply(getStepData(timestamp), objects);
220 if (child.localName == "replace" && typeof animation._objects[child.getA ttribute("with")] != "undefined") {
221 var object = animation._objects[child.getAttribute("with")];
222 object._coords = objects[0]._coords;
223 data.push({obj: object, coords: object._coords});
224 }
225 }
226 else if (child.localName == "move") {
227 var from = parseRelativeCoords(animation._objects, child, "fromCoords", "fromCoordsOf", "fromAnchor", "fromOffset");
228 var to = parseRelativeCoords(animation._objects, child, "toCoords", "toC oordsOf", "toAnchor", "toOffset");
229 var duration = parseInt(child.getAttribute("duration")) || 0;
230 if (to) {
231 if (!from)
232 from = objects[0]._coords;
233
234 for (var j = 0; j < objects.length; j++)
235 objects[j]._coords = from;
236
237 for (var t = timestamp; t <= timestamp + duration; t++) {
238 var curCoords = [
239 (timestamp + duration - t) / duration * from[0] + (t - timestamp) / duration * to[0],
240 (timestamp + duration - t) / duration * from[1] + (t - timestamp) / duration * to[1]
241 ];
242 var data = getStepData(t);
243 for (var j = 0; j < objects.length; j++)
244 data.push({obj: objects[j], coords: curCoords});
245 }
246 }
247 }
248 }
249 }
250
251 runAnimation(animation);
252 }
253
254 function executeAnimationStep(animation, timestamp) {
255 if (typeof animation._actions[timestamp] == "undefined")
256 return;
257
258 var action = animation._actions[timestamp];
259 for (var i = 0; i < action.length; i++) {
260 if (action[i] instanceof HTMLElement)
261 action[i].style.visibility = "hidden";
262 else {
263 action[i].obj.style.left = (action[i].coords[0] + action[i].obj._offset[0] ) + "px";
264 action[i].obj.style.top = (action[i].coords[1] + action[i].obj._offset[1]) + "px";
265 action[i].obj.style.visibility = "";
266 }
267 }
268 }
269
270 function runAnimation(animation) {
271 if (typeof animation.scrollIntoView == "function")
272 animation.scrollIntoView(true)
273
274 executeAnimationStep(animation, 0);
275
276 var curStep = 0;
277 var interval = setInterval(function() {
278 executeAnimationStep(animation, ++curStep);
279 if (curStep >= animation._actions.length) {
280 clearInterval(interval);
281 animation.style.cursor = "pointer";
282 animation.addEventListener("click", restartAnimation, false);
283 }
284 }, animation._stepSize);
285 }
286
287 function restartAnimation(event) {
288 var animation = event.target;
289 while (animation && animation.nodeType == 1 && animation.className != "animati on")
290 animation = animation.parentNode;
291
292 if (!animation || animation.nodeType != 1)
293 return;
294
295 animation.removeEventListener("click", restartAnimation, false);
296 animation.style.cursor = "";
297
298 for (var key in animation._objects)
299 animation._objects[key].style.visibility = "hidden";
300
301 runAnimation(animation);
302 }
303
304 function parseCoords(coords) {
305 if (!/^\s*([\-+]?\d+)\s*,\s*([\-+]?\d+)\s*$/.test(coords))
306 return null;
307
308 return [parseInt(RegExp.$1) || 0, parseInt(RegExp.$2) || 0];
309 }
310
311 function parseRelativeCoords(objects, node, coordsAttr, objectAttr, anchorAttr, offsetAttr) {
312 var coords = parseCoords(node.getAttribute(coordsAttr));
313 if (!coords) {
314 var object = node.getAttribute(objectAttr);
315 if (object && typeof objects[object] != "undefined") {
316 object = objects[object];
317 coords = object._coords.slice(0);
318
319 var anchor = node.getAttribute(anchorAttr);
320 if (anchor) {
321 coords[0] += object._offset[0];
322 coords[1] += object._offset[1];
323 processAnchor(coords, anchor, object._size);
324 }
325 }
326 }
327
328 if (coords) {
329 var offset = parseCoords(node.getAttribute(offsetAttr));
330 if (offset) {
331 coords[0] += offset[0];
332 coords[1] += offset[1];
333 }
334 }
335
336 return coords;
337 }
338
339 function processAnchor(coords, anchor, size) {
340 if (/right/i.test(anchor))
341 coords[0] += size[0];
342 else if (!/left/i.test(anchor))
343 coords[0] += Math.round(size[0]/2);
344
345 if (/bottom/i.test(anchor))
346 coords[1] += size[1];
347 else if (!/top/i.test(anchor))
348 coords[1] += Math.round(size[1]/2);
349 }
OLDNEW
« no previous file with comments | « filters/inline_file.py ('k') | templates/raw.tmpl » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld