|
1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ |
|
3 /* Copyright 2012 Mozilla Foundation |
|
4 * |
|
5 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
6 * you may not use this file except in compliance with the License. |
|
7 * You may obtain a copy of the License at |
|
8 * |
|
9 * http://www.apache.org/licenses/LICENSE-2.0 |
|
10 * |
|
11 * Unless required by applicable law or agreed to in writing, software |
|
12 * distributed under the License is distributed on an "AS IS" BASIS, |
|
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 * See the License for the specific language governing permissions and |
|
15 * limitations under the License. |
|
16 */ |
|
17 /* globals PDFJS */ |
|
18 |
|
19 'use strict'; |
|
20 |
|
21 var FontInspector = (function FontInspectorClosure() { |
|
22 var fonts; |
|
23 var active = false; |
|
24 var fontAttribute = 'data-font-name'; |
|
25 function removeSelection() { |
|
26 var divs = document.querySelectorAll('div[' + fontAttribute + ']'); |
|
27 for (var i = 0, ii = divs.length; i < ii; ++i) { |
|
28 var div = divs[i]; |
|
29 div.className = ''; |
|
30 } |
|
31 } |
|
32 function resetSelection() { |
|
33 var divs = document.querySelectorAll('div[' + fontAttribute + ']'); |
|
34 for (var i = 0, ii = divs.length; i < ii; ++i) { |
|
35 var div = divs[i]; |
|
36 div.className = 'debuggerHideText'; |
|
37 } |
|
38 } |
|
39 function selectFont(fontName, show) { |
|
40 var divs = document.querySelectorAll('div[' + fontAttribute + '=' + |
|
41 fontName + ']'); |
|
42 for (var i = 0, ii = divs.length; i < ii; ++i) { |
|
43 var div = divs[i]; |
|
44 div.className = show ? 'debuggerShowText' : 'debuggerHideText'; |
|
45 } |
|
46 } |
|
47 function textLayerClick(e) { |
|
48 if (!e.target.dataset.fontName || |
|
49 e.target.tagName.toUpperCase() !== 'DIV') { |
|
50 return; |
|
51 } |
|
52 var fontName = e.target.dataset.fontName; |
|
53 var selects = document.getElementsByTagName('input'); |
|
54 for (var i = 0; i < selects.length; ++i) { |
|
55 var select = selects[i]; |
|
56 if (select.dataset.fontName != fontName) { |
|
57 continue; |
|
58 } |
|
59 select.checked = !select.checked; |
|
60 selectFont(fontName, select.checked); |
|
61 select.scrollIntoView(); |
|
62 } |
|
63 } |
|
64 return { |
|
65 // Properties/functions needed by PDFBug. |
|
66 id: 'FontInspector', |
|
67 name: 'Font Inspector', |
|
68 panel: null, |
|
69 manager: null, |
|
70 init: function init() { |
|
71 var panel = this.panel; |
|
72 panel.setAttribute('style', 'padding: 5px;'); |
|
73 var tmp = document.createElement('button'); |
|
74 tmp.addEventListener('click', resetSelection); |
|
75 tmp.textContent = 'Refresh'; |
|
76 panel.appendChild(tmp); |
|
77 |
|
78 fonts = document.createElement('div'); |
|
79 panel.appendChild(fonts); |
|
80 }, |
|
81 cleanup: function cleanup() { |
|
82 fonts.textContent = ''; |
|
83 }, |
|
84 enabled: false, |
|
85 get active() { |
|
86 return active; |
|
87 }, |
|
88 set active(value) { |
|
89 active = value; |
|
90 if (active) { |
|
91 document.body.addEventListener('click', textLayerClick, true); |
|
92 resetSelection(); |
|
93 } else { |
|
94 document.body.removeEventListener('click', textLayerClick, true); |
|
95 removeSelection(); |
|
96 } |
|
97 }, |
|
98 // FontInspector specific functions. |
|
99 fontAdded: function fontAdded(fontObj, url) { |
|
100 function properties(obj, list) { |
|
101 var moreInfo = document.createElement('table'); |
|
102 for (var i = 0; i < list.length; i++) { |
|
103 var tr = document.createElement('tr'); |
|
104 var td1 = document.createElement('td'); |
|
105 td1.textContent = list[i]; |
|
106 tr.appendChild(td1); |
|
107 var td2 = document.createElement('td'); |
|
108 td2.textContent = obj[list[i]].toString(); |
|
109 tr.appendChild(td2); |
|
110 moreInfo.appendChild(tr); |
|
111 } |
|
112 return moreInfo; |
|
113 } |
|
114 var moreInfo = properties(fontObj, ['name', 'type']); |
|
115 var m = /url\(['"]?([^\)"']+)/.exec(url); |
|
116 var fontName = fontObj.loadedName; |
|
117 var font = document.createElement('div'); |
|
118 var name = document.createElement('span'); |
|
119 name.textContent = fontName; |
|
120 var download = document.createElement('a'); |
|
121 download.href = m[1]; |
|
122 download.textContent = 'Download'; |
|
123 var logIt = document.createElement('a'); |
|
124 logIt.href = ''; |
|
125 logIt.textContent = 'Log'; |
|
126 logIt.addEventListener('click', function(event) { |
|
127 event.preventDefault(); |
|
128 console.log(fontObj); |
|
129 }); |
|
130 var select = document.createElement('input'); |
|
131 select.setAttribute('type', 'checkbox'); |
|
132 select.dataset.fontName = fontName; |
|
133 select.addEventListener('click', (function(select, fontName) { |
|
134 return (function() { |
|
135 selectFont(fontName, select.checked); |
|
136 }); |
|
137 })(select, fontName)); |
|
138 font.appendChild(select); |
|
139 font.appendChild(name); |
|
140 font.appendChild(document.createTextNode(' ')); |
|
141 font.appendChild(download); |
|
142 font.appendChild(document.createTextNode(' ')); |
|
143 font.appendChild(logIt); |
|
144 font.appendChild(moreInfo); |
|
145 fonts.appendChild(font); |
|
146 // Somewhat of a hack, should probably add a hook for when the text layer |
|
147 // is done rendering. |
|
148 setTimeout(function() { |
|
149 if (this.active) { |
|
150 resetSelection(); |
|
151 } |
|
152 }.bind(this), 2000); |
|
153 } |
|
154 }; |
|
155 })(); |
|
156 |
|
157 // Manages all the page steppers. |
|
158 var StepperManager = (function StepperManagerClosure() { |
|
159 var steppers = []; |
|
160 var stepperDiv = null; |
|
161 var stepperControls = null; |
|
162 var stepperChooser = null; |
|
163 var breakPoints = {}; |
|
164 return { |
|
165 // Properties/functions needed by PDFBug. |
|
166 id: 'Stepper', |
|
167 name: 'Stepper', |
|
168 panel: null, |
|
169 manager: null, |
|
170 init: function init() { |
|
171 var self = this; |
|
172 this.panel.setAttribute('style', 'padding: 5px;'); |
|
173 stepperControls = document.createElement('div'); |
|
174 stepperChooser = document.createElement('select'); |
|
175 stepperChooser.addEventListener('change', function(event) { |
|
176 self.selectStepper(this.value); |
|
177 }); |
|
178 stepperControls.appendChild(stepperChooser); |
|
179 stepperDiv = document.createElement('div'); |
|
180 this.panel.appendChild(stepperControls); |
|
181 this.panel.appendChild(stepperDiv); |
|
182 if (sessionStorage.getItem('pdfjsBreakPoints')) { |
|
183 breakPoints = JSON.parse(sessionStorage.getItem('pdfjsBreakPoints')); |
|
184 } |
|
185 }, |
|
186 cleanup: function cleanup() { |
|
187 stepperChooser.textContent = ''; |
|
188 stepperDiv.textContent = ''; |
|
189 steppers = []; |
|
190 }, |
|
191 enabled: false, |
|
192 active: false, |
|
193 // Stepper specific functions. |
|
194 create: function create(pageIndex) { |
|
195 var debug = document.createElement('div'); |
|
196 debug.id = 'stepper' + pageIndex; |
|
197 debug.setAttribute('hidden', true); |
|
198 debug.className = 'stepper'; |
|
199 stepperDiv.appendChild(debug); |
|
200 var b = document.createElement('option'); |
|
201 b.textContent = 'Page ' + (pageIndex + 1); |
|
202 b.value = pageIndex; |
|
203 stepperChooser.appendChild(b); |
|
204 var initBreakPoints = breakPoints[pageIndex] || []; |
|
205 var stepper = new Stepper(debug, pageIndex, initBreakPoints); |
|
206 steppers.push(stepper); |
|
207 if (steppers.length === 1) { |
|
208 this.selectStepper(pageIndex, false); |
|
209 } |
|
210 return stepper; |
|
211 }, |
|
212 selectStepper: function selectStepper(pageIndex, selectPanel) { |
|
213 var i; |
|
214 if (selectPanel) { |
|
215 this.manager.selectPanel(this); |
|
216 } |
|
217 for (i = 0; i < steppers.length; ++i) { |
|
218 var stepper = steppers[i]; |
|
219 if (stepper.pageIndex == pageIndex) { |
|
220 stepper.panel.removeAttribute('hidden'); |
|
221 } else { |
|
222 stepper.panel.setAttribute('hidden', true); |
|
223 } |
|
224 } |
|
225 var options = stepperChooser.options; |
|
226 for (i = 0; i < options.length; ++i) { |
|
227 var option = options[i]; |
|
228 option.selected = option.value == pageIndex; |
|
229 } |
|
230 }, |
|
231 saveBreakPoints: function saveBreakPoints(pageIndex, bps) { |
|
232 breakPoints[pageIndex] = bps; |
|
233 sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints)); |
|
234 } |
|
235 }; |
|
236 })(); |
|
237 |
|
238 // The stepper for each page's IRQueue. |
|
239 var Stepper = (function StepperClosure() { |
|
240 // Shorter way to create element and optionally set textContent. |
|
241 function c(tag, textContent) { |
|
242 var d = document.createElement(tag); |
|
243 if (textContent) { |
|
244 d.textContent = textContent; |
|
245 } |
|
246 return d; |
|
247 } |
|
248 |
|
249 function glyphsToString(glyphs) { |
|
250 var out = ''; |
|
251 for (var i = 0; i < glyphs.length; i++) { |
|
252 if (glyphs[i] === null) { |
|
253 out += ' '; |
|
254 } else { |
|
255 out += glyphs[i].fontChar; |
|
256 } |
|
257 } |
|
258 return out; |
|
259 } |
|
260 |
|
261 var opMap = null; |
|
262 |
|
263 var glyphCommands = { |
|
264 'showText': 0, |
|
265 'showSpacedText': 0, |
|
266 'nextLineShowText': 0, |
|
267 'nextLineSetSpacingShowText': 2 |
|
268 }; |
|
269 |
|
270 function simplifyArgs(args) { |
|
271 if (typeof args === 'string') { |
|
272 var MAX_STRING_LENGTH = 75; |
|
273 return args.length <= MAX_STRING_LENGTH ? args : |
|
274 args.substr(0, MAX_STRING_LENGTH) + '...'; |
|
275 } |
|
276 if (typeof args !== 'object' || args === null) { |
|
277 return args; |
|
278 } |
|
279 if ('length' in args) { // array |
|
280 var simpleArgs = [], i, ii; |
|
281 var MAX_ITEMS = 10; |
|
282 for (i = 0, ii = Math.min(MAX_ITEMS, args.length); i < ii; i++) { |
|
283 simpleArgs.push(simplifyArgs(args[i])); |
|
284 } |
|
285 if (i < args.length) { |
|
286 simpleArgs.push('...'); |
|
287 } |
|
288 return simpleArgs; |
|
289 } |
|
290 var simpleObj = {}; |
|
291 for (var key in args) { |
|
292 simpleObj[key] = simplifyArgs(args[key]); |
|
293 } |
|
294 return simpleObj; |
|
295 } |
|
296 |
|
297 function Stepper(panel, pageIndex, initialBreakPoints) { |
|
298 this.panel = panel; |
|
299 this.breakPoint = 0; |
|
300 this.nextBreakPoint = null; |
|
301 this.pageIndex = pageIndex; |
|
302 this.breakPoints = initialBreakPoints; |
|
303 this.currentIdx = -1; |
|
304 this.operatorListIdx = 0; |
|
305 } |
|
306 Stepper.prototype = { |
|
307 init: function init() { |
|
308 var panel = this.panel; |
|
309 var content = c('div', 'c=continue, s=step'); |
|
310 var table = c('table'); |
|
311 content.appendChild(table); |
|
312 table.cellSpacing = 0; |
|
313 var headerRow = c('tr'); |
|
314 table.appendChild(headerRow); |
|
315 headerRow.appendChild(c('th', 'Break')); |
|
316 headerRow.appendChild(c('th', 'Idx')); |
|
317 headerRow.appendChild(c('th', 'fn')); |
|
318 headerRow.appendChild(c('th', 'args')); |
|
319 panel.appendChild(content); |
|
320 this.table = table; |
|
321 if (!opMap) { |
|
322 opMap = Object.create(null); |
|
323 for (var key in PDFJS.OPS) { |
|
324 opMap[PDFJS.OPS[key]] = key; |
|
325 } |
|
326 } |
|
327 }, |
|
328 updateOperatorList: function updateOperatorList(operatorList) { |
|
329 var self = this; |
|
330 |
|
331 function cboxOnClick() { |
|
332 var x = +this.dataset.idx; |
|
333 if (this.checked) { |
|
334 self.breakPoints.push(x); |
|
335 } else { |
|
336 self.breakPoints.splice(self.breakPoints.indexOf(x), 1); |
|
337 } |
|
338 StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints); |
|
339 } |
|
340 |
|
341 var MAX_OPERATORS_COUNT = 15000; |
|
342 if (this.operatorListIdx > MAX_OPERATORS_COUNT) { |
|
343 return; |
|
344 } |
|
345 |
|
346 var chunk = document.createDocumentFragment(); |
|
347 var operatorsToDisplay = Math.min(MAX_OPERATORS_COUNT, |
|
348 operatorList.fnArray.length); |
|
349 for (var i = this.operatorListIdx; i < operatorsToDisplay; i++) { |
|
350 var line = c('tr'); |
|
351 line.className = 'line'; |
|
352 line.dataset.idx = i; |
|
353 chunk.appendChild(line); |
|
354 var checked = this.breakPoints.indexOf(i) != -1; |
|
355 var args = operatorList.argsArray[i] || []; |
|
356 |
|
357 var breakCell = c('td'); |
|
358 var cbox = c('input'); |
|
359 cbox.type = 'checkbox'; |
|
360 cbox.className = 'points'; |
|
361 cbox.checked = checked; |
|
362 cbox.dataset.idx = i; |
|
363 cbox.onclick = cboxOnClick; |
|
364 |
|
365 breakCell.appendChild(cbox); |
|
366 line.appendChild(breakCell); |
|
367 line.appendChild(c('td', i.toString())); |
|
368 var fn = opMap[operatorList.fnArray[i]]; |
|
369 var decArgs = args; |
|
370 if (fn in glyphCommands) { |
|
371 var glyphIndex = glyphCommands[fn]; |
|
372 var glyphs = args[glyphIndex]; |
|
373 decArgs = args.slice(); |
|
374 var newArg; |
|
375 if (fn === 'showSpacedText') { |
|
376 newArg = []; |
|
377 for (var j = 0; j < glyphs.length; j++) { |
|
378 if (typeof glyphs[j] === 'number') { |
|
379 newArg.push(glyphs[j]); |
|
380 } else { |
|
381 newArg.push(glyphsToString(glyphs[j])); |
|
382 } |
|
383 } |
|
384 } else { |
|
385 newArg = glyphsToString(glyphs); |
|
386 } |
|
387 decArgs[glyphIndex] = newArg; |
|
388 } |
|
389 line.appendChild(c('td', fn)); |
|
390 line.appendChild(c('td', JSON.stringify(simplifyArgs(decArgs)))); |
|
391 } |
|
392 if (operatorsToDisplay < operatorList.fnArray.length) { |
|
393 line = c('tr'); |
|
394 var lastCell = c('td', '...'); |
|
395 lastCell.colspan = 4; |
|
396 chunk.appendChild(lastCell); |
|
397 } |
|
398 this.operatorListIdx = operatorList.fnArray.length; |
|
399 this.table.appendChild(chunk); |
|
400 }, |
|
401 getNextBreakPoint: function getNextBreakPoint() { |
|
402 this.breakPoints.sort(function(a, b) { return a - b; }); |
|
403 for (var i = 0; i < this.breakPoints.length; i++) { |
|
404 if (this.breakPoints[i] > this.currentIdx) { |
|
405 return this.breakPoints[i]; |
|
406 } |
|
407 } |
|
408 return null; |
|
409 }, |
|
410 breakIt: function breakIt(idx, callback) { |
|
411 StepperManager.selectStepper(this.pageIndex, true); |
|
412 var self = this; |
|
413 var dom = document; |
|
414 self.currentIdx = idx; |
|
415 var listener = function(e) { |
|
416 switch (e.keyCode) { |
|
417 case 83: // step |
|
418 dom.removeEventListener('keydown', listener, false); |
|
419 self.nextBreakPoint = self.currentIdx + 1; |
|
420 self.goTo(-1); |
|
421 callback(); |
|
422 break; |
|
423 case 67: // continue |
|
424 dom.removeEventListener('keydown', listener, false); |
|
425 var breakPoint = self.getNextBreakPoint(); |
|
426 self.nextBreakPoint = breakPoint; |
|
427 self.goTo(-1); |
|
428 callback(); |
|
429 break; |
|
430 } |
|
431 }; |
|
432 dom.addEventListener('keydown', listener, false); |
|
433 self.goTo(idx); |
|
434 }, |
|
435 goTo: function goTo(idx) { |
|
436 var allRows = this.panel.getElementsByClassName('line'); |
|
437 for (var x = 0, xx = allRows.length; x < xx; ++x) { |
|
438 var row = allRows[x]; |
|
439 if (row.dataset.idx == idx) { |
|
440 row.style.backgroundColor = 'rgb(251,250,207)'; |
|
441 row.scrollIntoView(); |
|
442 } else { |
|
443 row.style.backgroundColor = null; |
|
444 } |
|
445 } |
|
446 } |
|
447 }; |
|
448 return Stepper; |
|
449 })(); |
|
450 |
|
451 var Stats = (function Stats() { |
|
452 var stats = []; |
|
453 function clear(node) { |
|
454 while (node.hasChildNodes()) { |
|
455 node.removeChild(node.lastChild); |
|
456 } |
|
457 } |
|
458 function getStatIndex(pageNumber) { |
|
459 for (var i = 0, ii = stats.length; i < ii; ++i) { |
|
460 if (stats[i].pageNumber === pageNumber) { |
|
461 return i; |
|
462 } |
|
463 } |
|
464 return false; |
|
465 } |
|
466 return { |
|
467 // Properties/functions needed by PDFBug. |
|
468 id: 'Stats', |
|
469 name: 'Stats', |
|
470 panel: null, |
|
471 manager: null, |
|
472 init: function init() { |
|
473 this.panel.setAttribute('style', 'padding: 5px;'); |
|
474 PDFJS.enableStats = true; |
|
475 }, |
|
476 enabled: false, |
|
477 active: false, |
|
478 // Stats specific functions. |
|
479 add: function(pageNumber, stat) { |
|
480 if (!stat) { |
|
481 return; |
|
482 } |
|
483 var statsIndex = getStatIndex(pageNumber); |
|
484 if (statsIndex !== false) { |
|
485 var b = stats[statsIndex]; |
|
486 this.panel.removeChild(b.div); |
|
487 stats.splice(statsIndex, 1); |
|
488 } |
|
489 var wrapper = document.createElement('div'); |
|
490 wrapper.className = 'stats'; |
|
491 var title = document.createElement('div'); |
|
492 title.className = 'title'; |
|
493 title.textContent = 'Page: ' + pageNumber; |
|
494 var statsDiv = document.createElement('div'); |
|
495 statsDiv.textContent = stat.toString(); |
|
496 wrapper.appendChild(title); |
|
497 wrapper.appendChild(statsDiv); |
|
498 stats.push({ pageNumber: pageNumber, div: wrapper }); |
|
499 stats.sort(function(a, b) { return a.pageNumber - b.pageNumber; }); |
|
500 clear(this.panel); |
|
501 for (var i = 0, ii = stats.length; i < ii; ++i) { |
|
502 this.panel.appendChild(stats[i].div); |
|
503 } |
|
504 }, |
|
505 cleanup: function () { |
|
506 stats = []; |
|
507 clear(this.panel); |
|
508 } |
|
509 }; |
|
510 })(); |
|
511 |
|
512 // Manages all the debugging tools. |
|
513 var PDFBug = (function PDFBugClosure() { |
|
514 var panelWidth = 300; |
|
515 var buttons = []; |
|
516 var activePanel = null; |
|
517 |
|
518 return { |
|
519 tools: [ |
|
520 FontInspector, |
|
521 StepperManager, |
|
522 Stats |
|
523 ], |
|
524 enable: function(ids) { |
|
525 var all = false, tools = this.tools; |
|
526 if (ids.length === 1 && ids[0] === 'all') { |
|
527 all = true; |
|
528 } |
|
529 for (var i = 0; i < tools.length; ++i) { |
|
530 var tool = tools[i]; |
|
531 if (all || ids.indexOf(tool.id) !== -1) { |
|
532 tool.enabled = true; |
|
533 } |
|
534 } |
|
535 if (!all) { |
|
536 // Sort the tools by the order they are enabled. |
|
537 tools.sort(function(a, b) { |
|
538 var indexA = ids.indexOf(a.id); |
|
539 indexA = indexA < 0 ? tools.length : indexA; |
|
540 var indexB = ids.indexOf(b.id); |
|
541 indexB = indexB < 0 ? tools.length : indexB; |
|
542 return indexA - indexB; |
|
543 }); |
|
544 } |
|
545 }, |
|
546 init: function init() { |
|
547 /* |
|
548 * Basic Layout: |
|
549 * PDFBug |
|
550 * Controls |
|
551 * Panels |
|
552 * Panel |
|
553 * Panel |
|
554 * ... |
|
555 */ |
|
556 var ui = document.createElement('div'); |
|
557 ui.id = 'PDFBug'; |
|
558 |
|
559 var controls = document.createElement('div'); |
|
560 controls.setAttribute('class', 'controls'); |
|
561 ui.appendChild(controls); |
|
562 |
|
563 var panels = document.createElement('div'); |
|
564 panels.setAttribute('class', 'panels'); |
|
565 ui.appendChild(panels); |
|
566 |
|
567 var container = document.getElementById('viewerContainer'); |
|
568 container.appendChild(ui); |
|
569 container.style.right = panelWidth + 'px'; |
|
570 |
|
571 // Initialize all the debugging tools. |
|
572 var tools = this.tools; |
|
573 var self = this; |
|
574 for (var i = 0; i < tools.length; ++i) { |
|
575 var tool = tools[i]; |
|
576 var panel = document.createElement('div'); |
|
577 var panelButton = document.createElement('button'); |
|
578 panelButton.textContent = tool.name; |
|
579 panelButton.addEventListener('click', (function(selected) { |
|
580 return function(event) { |
|
581 event.preventDefault(); |
|
582 self.selectPanel(selected); |
|
583 }; |
|
584 })(i)); |
|
585 controls.appendChild(panelButton); |
|
586 panels.appendChild(panel); |
|
587 tool.panel = panel; |
|
588 tool.manager = this; |
|
589 if (tool.enabled) { |
|
590 tool.init(); |
|
591 } else { |
|
592 panel.textContent = tool.name + ' is disabled. To enable add ' + |
|
593 ' "' + tool.id + '" to the pdfBug parameter ' + |
|
594 'and refresh (seperate multiple by commas).'; |
|
595 } |
|
596 buttons.push(panelButton); |
|
597 } |
|
598 this.selectPanel(0); |
|
599 }, |
|
600 cleanup: function cleanup() { |
|
601 for (var i = 0, ii = this.tools.length; i < ii; i++) { |
|
602 if (this.tools[i].enabled) { |
|
603 this.tools[i].cleanup(); |
|
604 } |
|
605 } |
|
606 }, |
|
607 selectPanel: function selectPanel(index) { |
|
608 if (typeof index !== 'number') { |
|
609 index = this.tools.indexOf(index); |
|
610 } |
|
611 if (index === activePanel) { |
|
612 return; |
|
613 } |
|
614 activePanel = index; |
|
615 var tools = this.tools; |
|
616 for (var j = 0; j < tools.length; ++j) { |
|
617 if (j == index) { |
|
618 buttons[j].setAttribute('class', 'active'); |
|
619 tools[j].active = true; |
|
620 tools[j].panel.removeAttribute('hidden'); |
|
621 } else { |
|
622 buttons[j].setAttribute('class', ''); |
|
623 tools[j].active = false; |
|
624 tools[j].panel.setAttribute('hidden', 'true'); |
|
625 } |
|
626 } |
|
627 } |
|
628 }; |
|
629 })(); |