tizen beta release
[profile/ivi/webkit-efl.git] / debian / libwebkit-engine / usr / share / ewebkit-0 / webinspector / UIUtils.js
1 /*
2  * Copyright (C) 2011 Google Inc.  All rights reserved.
3  * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
4  * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
5  * Copyright (C) 2009 Joseph Pecoraro
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1.  Redistributions of source code must retain the above copyright
12  *     notice, this list of conditions and the following disclaimer.
13  * 2.  Redistributions in binary form must reproduce the above copyright
14  *     notice, this list of conditions and the following disclaimer in the
15  *     documentation and/or other materials provided with the distribution.
16  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
17  *     its contributors may be used to endorse or promote products derived
18  *     from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor)
33 {
34     if (WebInspector._elementDraggingEventListener || WebInspector._elementEndDraggingEventListener)
35         WebInspector.elementDragEnd(event);
36
37     WebInspector._elementDraggingEventListener = dividerDrag;
38     WebInspector._elementEndDraggingEventListener = elementDragEnd;
39
40     var targetDocument = event.target.ownerDocument;
41     targetDocument.addEventListener("mousemove", dividerDrag, true);
42     targetDocument.addEventListener("mouseup", elementDragEnd, true);
43
44     targetDocument.body.style.cursor = cursor;
45
46     event.preventDefault();
47 }
48
49 WebInspector.elementDragEnd = function(event)
50 {
51     var targetDocument = event.target.ownerDocument;
52     targetDocument.removeEventListener("mousemove", WebInspector._elementDraggingEventListener, true);
53     targetDocument.removeEventListener("mouseup", WebInspector._elementEndDraggingEventListener, true);
54
55     targetDocument.body.style.removeProperty("cursor");
56
57     delete WebInspector._elementDraggingEventListener;
58     delete WebInspector._elementEndDraggingEventListener;
59
60     event.preventDefault();
61 }
62
63 WebInspector.animateStyle = function(animations, duration, callback)
64 {
65     var interval;
66     var complete = 0;
67     var hasCompleted = false;
68
69     const intervalDuration = (1000 / 30); // 30 frames per second.
70     const animationsLength = animations.length;
71     const propertyUnit = {opacity: ""};
72     const defaultUnit = "px";
73
74     function cubicInOut(t, b, c, d)
75     {
76         if ((t/=d/2) < 1) return c/2*t*t*t + b;
77         return c/2*((t-=2)*t*t + 2) + b;
78     }
79
80     // Pre-process animations.
81     for (var i = 0; i < animationsLength; ++i) {
82         var animation = animations[i];
83         var element = null, start = null, end = null, key = null;
84         for (key in animation) {
85             if (key === "element")
86                 element = animation[key];
87             else if (key === "start")
88                 start = animation[key];
89             else if (key === "end")
90                 end = animation[key];
91         }
92
93         if (!element || !end)
94             continue;
95
96         if (!start) {
97             var computedStyle = element.ownerDocument.defaultView.getComputedStyle(element);
98             start = {};
99             for (key in end)
100                 start[key] = parseInt(computedStyle.getPropertyValue(key), 10);
101             animation.start = start;
102         } else
103             for (key in start)
104                 element.style.setProperty(key, start[key] + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
105     }
106
107     function animateLoop()
108     {
109         if (hasCompleted)
110             return;
111         
112         // Advance forward.
113         complete += intervalDuration;
114         var next = complete + intervalDuration;
115
116         // Make style changes.
117         for (var i = 0; i < animationsLength; ++i) {
118             var animation = animations[i];
119             var element = animation.element;
120             var start = animation.start;
121             var end = animation.end;
122             if (!element || !end)
123                 continue;
124
125             var style = element.style;
126             for (key in end) {
127                 var endValue = end[key];
128                 if (next < duration) {
129                     var startValue = start[key];
130                     var newValue = cubicInOut(complete, startValue, endValue - startValue, duration);
131                     style.setProperty(key, newValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
132                 } else
133                     style.setProperty(key, endValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
134             }
135         }
136
137         // End condition.
138         if (complete >= duration) {
139             hasCompleted = true;
140             clearInterval(interval);
141             if (callback)
142                 callback();
143         }
144     }
145
146     function forceComplete()
147     {
148         if (hasCompleted)
149             return;
150
151         complete = duration;
152         animateLoop();
153     }
154
155     function cancel()
156     {
157         hasCompleted = true;
158         clearInterval(interval);
159     }
160
161     interval = setInterval(animateLoop, intervalDuration);
162     return {
163         cancel: cancel,
164         forceComplete: forceComplete
165     };
166 }
167
168 WebInspector.isBeingEdited = function(element)
169 {
170     return element.__editing;
171 }
172
173 WebInspector.markBeingEdited = function(element, value)
174 {
175     if (value) {
176         if (element.__editing)
177             return false;
178         element.__editing = true;
179         WebInspector.__editingCount = (WebInspector.__editingCount || 0) + 1;
180     } else {
181         if (!element.__editing)
182             return false;
183         delete element.__editing;
184         --WebInspector.__editingCount;
185     }
186     return true;
187 }
188
189 WebInspector.isEditingAnyField = function()
190 {
191     return !!WebInspector.__editingCount;
192 }
193
194 /**
195  * @constructor
196  * @param {function(Element,string,string,*,string)} commitHandler
197  * @param {function(Element,*)} cancelHandler
198  * @param {*=} context
199  */
200 WebInspector.EditingConfig = function(commitHandler, cancelHandler, context)
201 {
202     this.commitHandler = commitHandler;
203     this.cancelHandler = cancelHandler
204     this.context = context;
205
206     /**
207      * Handles the "paste" event, return values are the same as those for customFinishHandler
208      * @type {function(Element)|undefined}
209      */
210     this.pasteHandler;
211
212     /** 
213      * Whether the edited element is multiline
214      * @type {boolean|undefined}
215      */
216     this.multiline;
217
218     /**
219      * Custom finish handler for the editing session (invoked on keydown)
220      * @type {function(Element,*)|undefined}
221      */
222     this.customFinishHandler;
223 }
224
225 WebInspector.EditingConfig.prototype = {
226     setPasteHandler: function(pasteHandler)
227     {
228         this.pasteHandler = pasteHandler;
229     },
230
231     setMultiline: function(multiline)
232     {
233         this.multiline = multiline;
234     },
235
236     setCustomFinishHandler: function(customFinishHandler)
237     {
238         this.customFinishHandler = customFinishHandler;
239     }
240 }
241
242 /** 
243  * @param {Element} element
244  * @param {WebInspector.EditingConfig=} config
245  */
246 WebInspector.startEditing = function(element, config)
247 {
248     if (!WebInspector.markBeingEdited(element, true))
249         return;
250
251     config = config || new WebInspector.EditingConfig(function() {}, function() {});
252     var committedCallback = config.commitHandler;
253     var cancelledCallback = config.cancelHandler;
254     var pasteCallback = config.pasteHandler;
255     var context = config.context;
256     var oldText = getContent(element);
257     var moveDirection = "";
258
259     element.addStyleClass("editing");
260
261     var oldTabIndex = element.tabIndex;
262     if (element.tabIndex < 0)
263         element.tabIndex = 0;
264
265     function blurEventListener() {
266         editingCommitted.call(element);
267     }
268
269     function getContent(element) {
270         if (element.tagName === "INPUT" && element.type === "text")
271             return element.value;
272         else
273             return element.textContent;
274     }
275
276     /** @this {Element} */
277     function cleanUpAfterEditing()
278     {
279         WebInspector.markBeingEdited(element, false);
280
281         this.removeStyleClass("editing");
282         this.tabIndex = oldTabIndex;
283         this.scrollTop = 0;
284         this.scrollLeft = 0;
285
286         element.removeEventListener("blur", blurEventListener, false);
287         element.removeEventListener("keydown", keyDownEventListener, true);
288         if (pasteCallback)
289             element.removeEventListener("paste", pasteEventListener, true);
290
291         if (element === WebInspector.currentFocusElement() || element.isAncestor(WebInspector.currentFocusElement()))
292             WebInspector.setCurrentFocusElement(WebInspector.previousFocusElement());
293     }
294
295     /** @this {Element} */
296     function editingCancelled()
297     {
298         if (this.tagName === "INPUT" && this.type === "text")
299             this.value = oldText;
300         else
301             this.textContent = oldText;
302
303         cleanUpAfterEditing.call(this);
304
305         cancelledCallback(this, context);
306     }
307
308     /** @this {Element} */
309     function editingCommitted()
310     {
311         cleanUpAfterEditing.call(this);
312
313         committedCallback(this, getContent(this), oldText, context, moveDirection);
314     }
315
316     function defaultFinishHandler(event)
317     {
318         var isMetaOrCtrl = WebInspector.isMac() ?
319             event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey :
320             event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
321         if (isEnterKey(event) && (event.isMetaOrCtrlForTest || !config.multiline || isMetaOrCtrl))
322             return "commit";
323         else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B")
324             return "cancel";
325         else if (event.keyIdentifier === "U+0009") // Tab key
326             return "move-" + (event.shiftKey ? "backward" : "forward");
327     }
328
329     function handleEditingResult(result, event)
330     {
331         if (result === "commit") {
332             editingCommitted.call(element);
333             event.preventDefault();
334             event.stopPropagation();
335         } else if (result === "cancel") {
336             editingCancelled.call(element);
337             event.preventDefault();
338             event.stopPropagation();
339         } else if (result && result.indexOf("move-") === 0) {
340             moveDirection = result.substring(5);
341             if (event.keyIdentifier !== "U+0009")
342                 blurEventListener();
343         }
344     }
345
346     function pasteEventListener(event)
347     {
348         var result = pasteCallback(event);
349         handleEditingResult(result, event);
350     }
351
352     function keyDownEventListener(event)
353     {
354         var handler = config.customFinishHandler || defaultFinishHandler;
355         var result = handler(event);
356         handleEditingResult(result, event);
357     }
358
359     element.addEventListener("blur", blurEventListener, false);
360     element.addEventListener("keydown", keyDownEventListener, true);
361     if (pasteCallback)
362         element.addEventListener("paste", pasteEventListener, true);
363
364     WebInspector.setCurrentFocusElement(element);
365     return {
366         cancel: editingCancelled.bind(element),
367         commit: editingCommitted.bind(element)
368     };
369 }
370
371 /**
372  * @param {boolean=} higherResolution
373  */
374 Number.secondsToString = function(seconds, higherResolution)
375 {
376     if (seconds === 0)
377         return "0";
378
379     var ms = seconds * 1000;
380     if (higherResolution && ms < 1000)
381         return WebInspector.UIString("%.3fms", ms);
382     else if (ms < 1000)
383         return WebInspector.UIString("%.0fms", ms);
384
385     if (seconds < 60)
386         return WebInspector.UIString("%.2fs", seconds);
387
388     var minutes = seconds / 60;
389     if (minutes < 60)
390         return WebInspector.UIString("%.1fmin", minutes);
391
392     var hours = minutes / 60;
393     if (hours < 24)
394         return WebInspector.UIString("%.1fhrs", hours);
395
396     var days = hours / 24;
397     return WebInspector.UIString("%.1f days", days);
398 }
399
400 /**
401  * @param {boolean=} higherResolution
402  */
403 Number.bytesToString = function(bytes, higherResolution)
404 {
405     if (typeof higherResolution === "undefined")
406         higherResolution = true;
407
408     if (bytes < 1024)
409         return WebInspector.UIString("%.0fB", bytes);
410
411     var kilobytes = bytes / 1024;
412     if (higherResolution && kilobytes < 1024)
413         return WebInspector.UIString("%.2fKB", kilobytes);
414     else if (kilobytes < 1024)
415         return WebInspector.UIString("%.0fKB", kilobytes);
416
417     var megabytes = kilobytes / 1024;
418     if (higherResolution)
419         return WebInspector.UIString("%.2fMB", megabytes);
420     else
421         return WebInspector.UIString("%.0fMB", megabytes);
422 }
423
424 WebInspector._missingLocalizedStrings = {};
425
426 /**
427  * @param {string} string
428  * @param {...*} vararg
429  */
430 WebInspector.UIString = function(string, vararg)
431 {
432     if (Preferences.localizeUI) {
433         if (window.localizedStrings && string in window.localizedStrings)
434             string = window.localizedStrings[string];
435         else {
436             if (!(string in WebInspector._missingLocalizedStrings)) {
437                 console.warn("Localized string \"" + string + "\" not found.");
438                 WebInspector._missingLocalizedStrings[string] = true;
439             }
440     
441             if (Preferences.showMissingLocalizedStrings)
442                 string += " (not localized)";
443         }
444     }
445     return String.vsprintf(string, Array.prototype.slice.call(arguments, 1));
446 }
447
448 WebInspector.useLowerCaseMenuTitles = function()
449 {
450     return WebInspector.platform() === "windows" && Preferences.useLowerCaseMenuTitlesOnWindows;
451 }
452
453 WebInspector.formatLocalized = function(format, substitutions, formatters, initialValue, append)
454 {
455     return String.format(WebInspector.UIString(format), substitutions, formatters, initialValue, append);
456 }
457
458 WebInspector.openLinkExternallyLabel = function()
459 {
460     return WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Open link in new tab" : "Open Link in New Tab");
461 }
462
463 WebInspector.openInNetworkPanelLabel = function()
464 {
465     return WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Open in network panel" : "Open in Network Panel");
466 }
467
468 WebInspector.copyLinkAddressLabel = function()
469 {
470     return WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy link address" : "Copy Link Address");
471 }
472
473 WebInspector.platform = function()
474 {
475     if (!WebInspector._platform)
476         WebInspector._platform = InspectorFrontendHost.platform();
477     return WebInspector._platform;
478 }
479
480 WebInspector.isMac = function()
481 {
482     if (typeof WebInspector._isMac === "undefined")
483         WebInspector._isMac = WebInspector.platform() === "mac";
484
485     return WebInspector._isMac;
486 }
487
488 WebInspector.PlatformFlavor = {
489     WindowsVista: "windows-vista",
490     MacTiger: "mac-tiger",
491     MacLeopard: "mac-leopard",
492     MacSnowLeopard: "mac-snowleopard"
493 }
494
495 WebInspector.platformFlavor = function()
496 {
497     function detectFlavor()
498     {
499         const userAgent = navigator.userAgent;
500
501         if (WebInspector.platform() === "windows") {
502             var match = userAgent.match(/Windows NT (\d+)\.(?:\d+)/);
503             if (match && match[1] >= 6)
504                 return WebInspector.PlatformFlavor.WindowsVista;
505             return null;
506         } else if (WebInspector.platform() === "mac") {
507             var match = userAgent.match(/Mac OS X\s*(?:(\d+)_(\d+))?/);
508             if (!match || match[1] != 10)
509                 return WebInspector.PlatformFlavor.MacSnowLeopard;
510             switch (Number(match[2])) {
511                 case 4:
512                     return WebInspector.PlatformFlavor.MacTiger;
513                 case 5:
514                     return WebInspector.PlatformFlavor.MacLeopard;
515                 case 6:
516                 default:
517                     return WebInspector.PlatformFlavor.MacSnowLeopard;
518             }
519         }
520     }
521
522     if (!WebInspector._platformFlavor)
523         WebInspector._platformFlavor = detectFlavor();
524
525     return WebInspector._platformFlavor;
526 }
527
528 WebInspector.port = function()
529 {
530     if (!WebInspector._port)
531         WebInspector._port = InspectorFrontendHost.port();
532
533     return WebInspector._port;
534 }
535
536 WebInspector.installPortStyles = function()
537 {
538     var platform = WebInspector.platform();
539     document.body.addStyleClass("platform-" + platform);
540     var flavor = WebInspector.platformFlavor();
541     if (flavor)
542         document.body.addStyleClass("platform-" + flavor);
543     var port = WebInspector.port();
544     document.body.addStyleClass("port-" + port);
545 }
546
547 WebInspector._windowFocused = function(event)
548 {
549     if (event.target.document.nodeType === Node.DOCUMENT_NODE)
550         document.body.removeStyleClass("inactive");
551 }
552
553 WebInspector._windowBlurred = function(event)
554 {
555     if (event.target.document.nodeType === Node.DOCUMENT_NODE)
556         document.body.addStyleClass("inactive");
557 }
558
559 WebInspector.previousFocusElement = function()
560 {
561     return WebInspector._previousFocusElement;
562 }
563
564 WebInspector.currentFocusElement = function()
565 {
566     return WebInspector._currentFocusElement;
567 }
568
569 WebInspector._focusChanged = function(event)
570 {
571     WebInspector.setCurrentFocusElement(event.target);
572 }
573
574 WebInspector.setCurrentFocusElement = function(x)
575 {
576     if (WebInspector._currentFocusElement !== x)
577         WebInspector._previousFocusElement = WebInspector._currentFocusElement;
578     WebInspector._currentFocusElement = x;
579
580     if (WebInspector._currentFocusElement) {
581         WebInspector._currentFocusElement.focus();
582
583         // Make a caret selection inside the new element if there isn't a range selection and
584         // there isn't already a caret selection inside.
585         var selection = window.getSelection();
586         if (selection.isCollapsed && !WebInspector._currentFocusElement.isInsertionCaretInside()) {
587             var selectionRange = WebInspector._currentFocusElement.ownerDocument.createRange();
588             selectionRange.setStart(WebInspector._currentFocusElement, 0);
589             selectionRange.setEnd(WebInspector._currentFocusElement, 0);
590
591             selection.removeAllRanges();
592             selection.addRange(selectionRange);
593         }
594     } else if (WebInspector._previousFocusElement)
595         WebInspector._previousFocusElement.blur();
596 }
597
598 WebInspector.setToolbarColors = function(backgroundColor, color)
599 {
600     if (!WebInspector._themeStyleElement) {
601         WebInspector._themeStyleElement = document.createElement("style");
602         document.head.appendChild(WebInspector._themeStyleElement);
603     }
604     WebInspector._themeStyleElement.textContent =
605         "#toolbar {\
606              background-image: none !important;\
607              background-color: " + backgroundColor + " !important;\
608          }\
609          \
610          .toolbar-label {\
611              color: " + color + " !important;\
612              text-shadow: none;\
613          }";
614 }
615
616 WebInspector.resetToolbarColors = function()
617 {
618     if (WebInspector._themeStyleElement)
619         WebInspector._themeStyleElement.textContent = "";
620
621 }
622
623 ;(function() {
624
625 function windowLoaded()
626 {
627     window.addEventListener("focus", WebInspector._windowFocused, false);
628     window.addEventListener("blur", WebInspector._windowBlurred, false);
629     document.addEventListener("focus", WebInspector._focusChanged.bind(this), true);
630     window.removeEventListener("DOMContentLoaded", windowLoaded, false);
631 }
632
633 window.addEventListener("DOMContentLoaded", windowLoaded, false);
634
635 })();