├── .gitignore ├── lint.sh ├── public ├── favicon.ico ├── css │ ├── Images │ │ ├── searchNext.png │ │ ├── searchPrev.png │ │ ├── toolbarItemSelected.png │ │ ├── statusbarButtonGlyphs.png │ │ ├── statusbarButtonGlyphs_2x.png │ │ └── src │ │ │ ├── svg2png.hashes │ │ │ ├── optimize_png.hashes │ │ │ ├── breakpoint.svg │ │ │ ├── breakpointConditional.svg │ │ │ ├── settingsListRemove.svg │ │ │ └── responsiveDesign.svg │ ├── jsh.css │ ├── inspector.css │ ├── suggestBox.css │ ├── tabbedPane.css │ ├── filter.css │ ├── splitView.css │ ├── inspectorCommon.css │ └── dataGrid.css ├── js │ ├── main │ │ ├── SimpleApp.js │ │ └── App.js │ ├── ui │ │ ├── ZoomManager.js │ │ ├── DropDownMenu.js │ │ ├── Context.js │ │ ├── ResizerWidget.js │ │ └── Dialog.js │ ├── common │ │ ├── WebInspector.js │ │ ├── UIString.js │ │ ├── Throttler.js │ │ ├── Console.js │ │ ├── Object.js │ │ ├── Geometry.js │ │ ├── TextRange.js │ │ ├── modules.js │ │ └── ParsedURL.js │ ├── components │ │ ├── PropertiesSection.js │ │ ├── ExecutionContextSelector.js │ │ ├── Section.js │ │ ├── Panel.js │ │ ├── DockController.js │ │ └── Drawer.js │ ├── InjectedScriptHost.js │ ├── console │ │ └── ConsolePanel.js │ ├── host │ │ └── Platform.js │ ├── jsh.js │ ├── jsh-console.js │ └── sdk │ │ └── ResourceUtils.js ├── index.html └── evalFrame.html ├── .jshintrc ├── app.yaml ├── templates ├── jession.min.jinja └── jession.jinja ├── concatCSS.js ├── jsh.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | #*# 3 | node_modules 4 | *.pyc 5 | -------------------------------------------------------------------------------- /lint.sh: -------------------------------------------------------------------------------- 1 | jshint public/js | grep -v 'everything.min.js' 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zirak/jsh/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/css/Images/searchNext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zirak/jsh/HEAD/public/css/Images/searchNext.png -------------------------------------------------------------------------------- /public/css/Images/searchPrev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zirak/jsh/HEAD/public/css/Images/searchPrev.png -------------------------------------------------------------------------------- /public/css/Images/toolbarItemSelected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zirak/jsh/HEAD/public/css/Images/toolbarItemSelected.png -------------------------------------------------------------------------------- /public/css/Images/statusbarButtonGlyphs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zirak/jsh/HEAD/public/css/Images/statusbarButtonGlyphs.png -------------------------------------------------------------------------------- /public/css/Images/statusbarButtonGlyphs_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zirak/jsh/HEAD/public/css/Images/statusbarButtonGlyphs_2x.png -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly" : true, 3 | "evil" : true, 4 | "immed" : true, 5 | "multistr" : true, 6 | "proto" : true 7 | } 8 | -------------------------------------------------------------------------------- /public/css/jsh.css: -------------------------------------------------------------------------------- 1 | .dark-theme-bar-item { 2 | margin : 4px; 3 | } 4 | .dark-theme-bar-item input[type="checkbox"] { 5 | vertical-align : text-top; 6 | } 7 | 8 | /* for some reason this isn't in the regular stylesheet */ 9 | .console-user-command { 10 | width : 100%; 11 | } 12 | -------------------------------------------------------------------------------- /public/css/Images/src/svg2png.hashes: -------------------------------------------------------------------------------- 1 | { 2 | "statusbarButtonGlyphs.svg": "3f642dca97434b4cbb6491e2854dc69f", 3 | "breakpoint.svg": "69cd92d807259c022791112809b97799", 4 | "responsiveDesign.svg": "bc00a0a7fb0a47453929f94c9a4e003c", 5 | "settingsListRemove.svg": "ce9e7c5c5cdaef28e6ee51d9478d5485", 6 | "breakpointConditional.svg": "4cf90210b2af2ed84db2f60b07bcde28" 7 | } -------------------------------------------------------------------------------- /public/css/Images/src/optimize_png.hashes: -------------------------------------------------------------------------------- 1 | { 2 | "statusbarButtonGlyphs.svg": "3f642dca97434b4cbb6491e2854dc69f", 3 | "breakpoint.svg": "69cd92d807259c022791112809b97799", 4 | "responsiveDesign.svg": "bc00a0a7fb0a47453929f94c9a4e003c", 5 | "settingsListRemove.svg": "ce9e7c5c5cdaef28e6ee51d9478d5485", 6 | "breakpointConditional.svg": "4cf90210b2af2ed84db2f60b07bcde28" 7 | } -------------------------------------------------------------------------------- /public/css/inspector.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The Chromium Authors. All rights reserved. 3 | * Use of this source code is governed by a BSD-style license that can be 4 | * found in the LICENSE file. 5 | */ 6 | 7 | @import url("inspectorStyle.css"); 8 | @import url("inspectorCommon.css"); 9 | @import url("splitView.css"); 10 | @import url("suggestBox.css"); 11 | @import url("tabbedPane.css"); 12 | @import url("jsh.css"); 13 | -------------------------------------------------------------------------------- /public/css/Images/src/breakpoint.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/css/Images/src/breakpointConditional.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: jshell 2 | version: 1 3 | runtime: python27 4 | api_version: 1 5 | threadsafe: true 6 | 7 | handlers: 8 | - url: /public 9 | static_dir: public 10 | 11 | - url: /favicon.ico 12 | static_files: public/favicon.ico 13 | upload: public/favicon.ico 14 | 15 | - url: /evalFrame.html 16 | static_files: public/evalFrame.html 17 | upload: public/evalFrame.html 18 | 19 | - url: /.* 20 | script: jsh.application 21 | 22 | libraries: 23 | - name: webapp2 24 | version: latest 25 | - name: jinja2 26 | version: latest 27 | -------------------------------------------------------------------------------- /public/js/main/SimpleApp.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /** 6 | * @constructor 7 | * @extends {WebInspector.App} 8 | */ 9 | WebInspector.SimpleApp = function() 10 | { 11 | WebInspector.App.call(this); 12 | }; 13 | 14 | WebInspector.SimpleApp.prototype = { 15 | createRootView: function() 16 | { 17 | var rootView = new WebInspector.RootView(); 18 | WebInspector.inspectorView.show(rootView.element); 19 | rootView.attachToBody(); 20 | }, 21 | 22 | __proto__: WebInspector.App.prototype 23 | }; 24 | -------------------------------------------------------------------------------- /templates/jession.min.jinja: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jsh 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /public/css/Images/src/settingsListRemove.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/css/Images/src/responsiveDesign.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/js/ui/ZoomManager.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /** 6 | * @constructor 7 | * @extends {WebInspector.Object} 8 | */ 9 | WebInspector.ZoomManager = function() 10 | { 11 | this._zoomFactor = InspectorFrontendHost.zoomFactor(); 12 | window.addEventListener("resize", this._onWindowResize.bind(this), true); 13 | }; 14 | 15 | WebInspector.ZoomManager.Events = { 16 | ZoomChanged: "ZoomChanged" 17 | }; 18 | 19 | WebInspector.ZoomManager.prototype = { 20 | /** 21 | * @return {number} 22 | */ 23 | zoomFactor: function() 24 | { 25 | return this._zoomFactor; 26 | }, 27 | 28 | _onWindowResize: function() 29 | { 30 | var oldZoomFactor = this._zoomFactor; 31 | this._zoomFactor = InspectorFrontendHost.zoomFactor(); 32 | if (oldZoomFactor !== this._zoomFactor) { 33 | this.dispatchEventToListeners(WebInspector.ZoomManager.Events.ZoomChanged, {from: oldZoomFactor, to: this._zoomFactor}); 34 | } 35 | }, 36 | 37 | __proto__: WebInspector.Object.prototype 38 | }; 39 | 40 | /** 41 | * @type {!WebInspector.ZoomManager} 42 | */ 43 | WebInspector.zoomManager = WebInspector.zoomManager; 44 | -------------------------------------------------------------------------------- /public/js/common/WebInspector.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The Chromium Authors. All rights reserved. 3 | * Use of this source code is governed by a BSD-style license that can be 4 | * found in the LICENSE file. 5 | */ 6 | 7 | self.WebInspector = { 8 | _queryParamsObject: {} 9 | }; 10 | 11 | WebInspector.Events = { 12 | InspectorLoaded: "InspectorLoaded" 13 | }; 14 | 15 | /** 16 | * @param {string} name 17 | * @return {?string} 18 | */ 19 | WebInspector.queryParam = function(name) 20 | { 21 | return WebInspector._queryParamsObject.hasOwnProperty(name) ? WebInspector._queryParamsObject[name] : null; 22 | }; 23 | 24 | {(function parseQueryParameters() 25 | { 26 | var queryParams = location.search; 27 | if (!queryParams) { 28 | return; 29 | } 30 | var params = queryParams.substring(1).split("&"); 31 | for (var i = 0; i < params.length; ++i) { 32 | var pair = params[i].split("="); 33 | WebInspector._queryParamsObject[pair[0]] = pair[1]; 34 | } 35 | 36 | // Patch settings from the URL param (for tests). 37 | var settingsParam = WebInspector.queryParam("settings"); 38 | if (settingsParam) { 39 | try { 40 | var settings = JSON.parse(window.decodeURI(settingsParam)); 41 | for (var key in settings) { 42 | window.localStorage[key] = settings[key]; 43 | } 44 | } catch(e) { 45 | // Ignore malformed settings. 46 | } 47 | } 48 | })();} 49 | 50 | // zirak: my patches here 51 | WebInspector.isWorkerFrontend = function() { 52 | return false; 53 | }; 54 | -------------------------------------------------------------------------------- /concatCSS.js: -------------------------------------------------------------------------------- 1 | // everything is terrible. 2 | var Promise = require('bluebird'), 3 | pathlib = require('path'), 4 | fs = Promise.promisifyAll(require('fs')); 5 | 6 | var chunkString = function (str, re) { 7 | var match, 8 | lastIndex = 0, 9 | ret = []; 10 | 11 | while (match = re.exec(str)) { 12 | if (match.index > lastIndex) { 13 | ret.push([str.slice(lastIndex, match.index)]); 14 | } 15 | 16 | lastIndex = match.index + match[0].length; 17 | ret.push(match); 18 | } 19 | 20 | if (lastIndex < str.length) { 21 | ret.push([str.slice(lastIndex)]); 22 | } 23 | 24 | return ret; 25 | }; 26 | 27 | var processFile = exports.processFile = function (path) { 28 | return fs.readFileAsync(path).then(function (imported) { 29 | return processString(imported, pathlib.dirname(path)); 30 | }); 31 | }; 32 | 33 | var processString = exports.processString = function (data, path) { 34 | // this is incomplete by design and lack of will to make it complete. 35 | var importRegex = /^@import\s+url\(\s*"(.+)"\s*\);$/gm; 36 | 37 | return Promise.map(chunkString(data, importRegex), function (part) { 38 | // part is either the match, or an array containing just a string. 39 | if (!part[1]) { 40 | return part[0]; 41 | } 42 | 43 | var importPath = part[1], 44 | relativePath; 45 | 46 | if (path) { 47 | importPath = pathlib.resolve(path, importPath); 48 | } 49 | 50 | return processFile(importPath); 51 | }).reduce(function (ret, val) { 52 | return ret + val; 53 | }, ''); 54 | }; 55 | -------------------------------------------------------------------------------- /public/js/common/UIString.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 | /** 33 | * @param {string} string 34 | * @param {...*} vararg 35 | * @return {string} 36 | */ 37 | WebInspector.UIString = function(string, vararg) 38 | { 39 | return String.vsprintf(string, Array.prototype.slice.call(arguments, 1)); 40 | }; 41 | -------------------------------------------------------------------------------- /public/js/ui/DropDownMenu.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /** 6 | * @constructor 7 | * @extends {WebInspector.Object} 8 | */ 9 | WebInspector.DropDownMenu = function() 10 | { 11 | this.element = document.createElementWithClass("select", "drop-down-menu"); 12 | this.element.addEventListener("mousedown", this._onBeforeMouseDown.bind(this), true); 13 | this.element.addEventListener("mousedown", consumeEvent, false); 14 | this.element.addEventListener("change", this._onChange.bind(this), false); 15 | }; 16 | 17 | WebInspector.DropDownMenu.Events = { 18 | BeforeShow: "BeforeShow", 19 | ItemSelected: "ItemSelected" 20 | }; 21 | 22 | WebInspector.DropDownMenu.prototype = { 23 | _onBeforeMouseDown: function() 24 | { 25 | this.dispatchEventToListeners(WebInspector.DropDownMenu.Events.BeforeShow, null); 26 | }, 27 | 28 | _onChange: function() 29 | { 30 | var options = this.element.options; 31 | var selectedOption = options[this.element.selectedIndex]; 32 | this.dispatchEventToListeners(WebInspector.DropDownMenu.Events.ItemSelected, selectedOption.id); 33 | }, 34 | 35 | /** 36 | * @param {string} id 37 | * @param {string} title 38 | */ 39 | addItem: function(id, title) 40 | { 41 | var option = new Option(title); 42 | option.id = id; 43 | this.element.appendChild(option); 44 | }, 45 | 46 | /** 47 | * @param {?string} id 48 | */ 49 | selectItem: function(id) 50 | { 51 | var children = this.element.children; 52 | for (var i = 0; i < children.length; ++i) { 53 | var child = children[i]; 54 | if (child.id === id) { 55 | this.element.selectedIndex = i; 56 | return; 57 | } 58 | } 59 | this.element.selectedIndex = -1; 60 | }, 61 | 62 | clear: function() 63 | { 64 | this.element.removeChildren(); 65 | }, 66 | 67 | __proto__: WebInspector.Object.prototype 68 | }; 69 | -------------------------------------------------------------------------------- /public/js/components/PropertiesSection.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 Apple Inc. All rights reserved. 3 | * Copyright (C) 2009 Google Inc. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 | * its contributors may be used to endorse or promote products derived 16 | * from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** 31 | * @constructor 32 | * @extends {WebInspector.Section} 33 | * @param {string|!Element} title 34 | * @param {string=} subtitle 35 | */ 36 | WebInspector.PropertiesSection = function(title, subtitle) 37 | { 38 | WebInspector.Section.call(this, title, subtitle); 39 | 40 | this.headerElement.classList.add("monospace"); 41 | this.propertiesElement = document.createElement("ol"); 42 | this.propertiesElement.className = "properties properties-tree monospace"; 43 | this.propertiesTreeOutline = new TreeOutline(this.propertiesElement, true); 44 | this.propertiesTreeOutline.setFocusable(false); 45 | this.propertiesTreeOutline.section = this; 46 | 47 | this.element.appendChild(this.propertiesElement); 48 | }; 49 | 50 | WebInspector.PropertiesSection.prototype = { 51 | __proto__: WebInspector.Section.prototype 52 | }; 53 | -------------------------------------------------------------------------------- /public/js/common/Throttler.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /** 6 | * @constructor 7 | * @param {number} timeout 8 | */ 9 | WebInspector.Throttler = function(timeout) 10 | { 11 | this._timeout = timeout; 12 | this._isRunningProcess = false; 13 | this._asSoonAsPossible = false; 14 | /** @type {?function(!WebInspector.Throttler.FinishCallback)} */ 15 | this._process = null; 16 | }; 17 | 18 | WebInspector.Throttler.prototype = { 19 | _processCompleted: function() 20 | { 21 | this._isRunningProcess = false; 22 | if (this._process) { 23 | this._innerSchedule(false); 24 | } 25 | }, 26 | 27 | _onTimeout: function() 28 | { 29 | delete this._processTimeout; 30 | this._asSoonAsPossible = false; 31 | this._isRunningProcess = true; 32 | 33 | // Process might issue synchronous calls to this throttler. 34 | var process = this._process; 35 | this._process = null; 36 | process(this._processCompleted.bind(this)); 37 | }, 38 | 39 | /** 40 | * @param {function(!WebInspector.Throttler.FinishCallback)} process 41 | * @param {boolean=} asSoonAsPossible 42 | */ 43 | schedule: function(process, asSoonAsPossible) 44 | { 45 | // Deliberately skip previous process. 46 | this._process = process; 47 | var force = !!asSoonAsPossible && !this._asSoonAsPossible; 48 | this._asSoonAsPossible = this._asSoonAsPossible || !!asSoonAsPossible; 49 | 50 | this._innerSchedule(force); 51 | }, 52 | 53 | /** 54 | * @param {boolean} force 55 | */ 56 | _innerSchedule: function(force) 57 | { 58 | if (this._isRunningProcess) { 59 | return; 60 | } 61 | if (this._processTimeout && !force) { 62 | return; 63 | } 64 | if (this._processTimeout) { 65 | this._clearTimeout(this._processTimeout); 66 | } 67 | 68 | var timeout = this._asSoonAsPossible ? 0 : this._timeout; 69 | this._processTimeout = this._setTimeout(this._onTimeout.bind(this), timeout); 70 | }, 71 | 72 | /** 73 | * @param {number} timeoutId 74 | */ 75 | _clearTimeout: function(timeoutId) 76 | { 77 | clearTimeout(timeoutId); 78 | }, 79 | 80 | /** 81 | * @param {function()} operation 82 | * @param {number} timeout 83 | * @return {number} 84 | */ 85 | _setTimeout: function(operation, timeout) 86 | { 87 | return setTimeout(operation, timeout); 88 | } 89 | }; 90 | 91 | /** @typedef {function()} */ 92 | WebInspector.Throttler.FinishCallback = WebInspector.Throttler.FinishCallback; 93 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jsh 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /jsh.py: -------------------------------------------------------------------------------- 1 | import webapp2, jinja2 2 | 3 | from google.appengine.ext import ndb 4 | 5 | import json 6 | import os 7 | 8 | # Models # 9 | 10 | # This name, like Britta, is the worst. 11 | # A Jession is a Session. Only with J instead of S. 12 | # And a session is really just an array of commands. 13 | class Jession(ndb.Model): 14 | commands = ndb.TextProperty(repeated=True) 15 | 16 | # Routes # 17 | 18 | class GetJession(webapp2.RequestHandler): 19 | def get(self, jession_id=None): 20 | if jession_id is not None: 21 | commands = self.get_commands(jession_id) 22 | else: 23 | commands = [] 24 | 25 | # TODO actually select between min and full based on some environment 26 | #variable - for some reason GAE is being weird about them. 27 | template_path = 'jession.min.jinja' 28 | template = JINJA.get_template(template_path) 29 | 30 | self.response.write(template.render({ 31 | 'commands' : json.dumps(commands) 32 | })) 33 | 34 | def get_commands(self, jession_id): 35 | # jession_id is (supposed to be) the base36 encoding of a db id 36 | try: 37 | db_id = int(jession_id, 36) 38 | except (ValueError, TypeError): 39 | return [] 40 | 41 | jession = Jession.get_by_id(db_id) 42 | return jession.commands if jession else [] 43 | 44 | class SaveJession(webapp2.RequestHandler): 45 | def post(self): 46 | try: 47 | data = json.loads(self.request.body) 48 | assert data['commands'], 'request has no commands' 49 | except (ValueError, AssertionError) as e: 50 | print e 51 | import traceback; traceback.format_exc() 52 | 53 | self.response.status = 400 54 | return 55 | 56 | jession = Jession(commands=data['commands']) 57 | jession.put() 58 | 59 | self.response.headers['Content-Type'] = 'application/json' 60 | 61 | self.response.write(json.dumps({ 62 | 'id' : int_to_base(jession.key.id(), 36) 63 | })) 64 | 65 | # Utility crap 66 | 67 | def int_to_base(n, base, chars=None): 68 | ''' 69 | Takes an integer and converts it to a given base (2 <= base <= 36). 70 | The opposite of int(n, base). And batman. 71 | ''' 72 | 73 | if chars is None: 74 | import string 75 | chars = string.digits + string.lowercase 76 | 77 | if base > len(chars): 78 | err = 'Base (%d) larger than amount of chars (%d)' % (base, len(chars)) 79 | raise ValueError(err) 80 | 81 | if n < base: 82 | return chars[n] 83 | 84 | mod = n % base 85 | return int_to_base(n / base, base, chars) + chars[mod] 86 | 87 | # Here be start of program 88 | 89 | JINJA = jinja2.Environment( 90 | loader=jinja2.FileSystemLoader('templates/'), 91 | extensions=['jinja2.ext.autoescape'], 92 | autoescape=True) 93 | 94 | application = webapp2.WSGIApplication([ 95 | (r'/', GetJession), 96 | (r'/save', SaveJession), 97 | # Always keep this last, as it's a catch-all 98 | (r'/([\da-z]+)', GetJession) 99 | ], debug=True) 100 | -------------------------------------------------------------------------------- /public/js/common/Console.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /** 6 | * @constructor 7 | * @extends {WebInspector.Object} 8 | */ 9 | WebInspector.Console = function() 10 | { 11 | /** @type {!Array.} */ 12 | this._messages = []; 13 | }; 14 | 15 | /** 16 | * @enum {string} 17 | */ 18 | WebInspector.Console.Events = { 19 | MessageAdded: "messageAdded" 20 | }; 21 | 22 | /** 23 | * @enum {string} 24 | */ 25 | WebInspector.Console.MessageLevel = { 26 | Log: "log", 27 | Warning: "warning", 28 | Error: "error" 29 | }; 30 | 31 | /** 32 | * @constructor 33 | * @param {string} text 34 | * @param {!WebInspector.Console.MessageLevel} level 35 | * @param {number} timestamp 36 | * @param {boolean} show 37 | */ 38 | WebInspector.Console.Message = function(text, level, timestamp, show) 39 | { 40 | this.text = text; 41 | this.level = level; 42 | this.timestamp = (typeof timestamp === "number") ? timestamp : Date.now(); 43 | this.show = show; 44 | }; 45 | 46 | /** 47 | * @interface 48 | */ 49 | WebInspector.Console.UIDelegate = function() 50 | { 51 | }; 52 | 53 | WebInspector.Console.UIDelegate.prototype = { 54 | showConsole: function() { } 55 | }; 56 | 57 | WebInspector.Console.prototype = { 58 | /** 59 | * @param {!WebInspector.Console.UIDelegate} uiDelegate 60 | */ 61 | setUIDelegate: function(uiDelegate) 62 | { 63 | this._uiDelegate = uiDelegate; 64 | }, 65 | 66 | /** 67 | * @param {string} text 68 | * @param {!WebInspector.Console.MessageLevel} level 69 | * @param {boolean=} show 70 | */ 71 | addMessage: function(text, level, show) 72 | { 73 | var message = new WebInspector.Console.Message(text, level || WebInspector.Console.MessageLevel.Log, Date.now(), show || false); 74 | this._messages.push(message); 75 | this.dispatchEventToListeners(WebInspector.Console.Events.MessageAdded, message); 76 | }, 77 | 78 | /** 79 | * @param {string} text 80 | */ 81 | log: function(text) 82 | { 83 | this.addMessage(text, WebInspector.Console.MessageLevel.Log); 84 | }, 85 | 86 | /** 87 | * @param {string} text 88 | */ 89 | warn: function(text) 90 | { 91 | this.addMessage(text, WebInspector.Console.MessageLevel.Warning); 92 | }, 93 | 94 | /** 95 | * @param {string} text 96 | */ 97 | error: function(text) 98 | { 99 | this.addMessage(text, WebInspector.Console.MessageLevel.Error, true); 100 | }, 101 | 102 | /** 103 | * @return {!Array.} 104 | */ 105 | messages: function() 106 | { 107 | return this._messages; 108 | }, 109 | 110 | show: function() 111 | { 112 | if (this._uiDelegate) { 113 | this._uiDelegate.showConsole(); 114 | } 115 | }, 116 | 117 | __proto__: WebInspector.Object.prototype 118 | }; 119 | 120 | WebInspector.console = new WebInspector.Console(); 121 | -------------------------------------------------------------------------------- /public/js/InjectedScriptHost.js: -------------------------------------------------------------------------------- 1 | // InjectedScript depends on some native methods. This is their simulation. 2 | 3 | var InjectedScriptHost = { 4 | eval : function (code) { 5 | /*jshint -W067*/ 6 | // the above stops jshint from complaining about indirect eval. 7 | return (':(', eval)(code); 8 | }, 9 | 10 | evaluateWithExceptionDetails : function (code) { 11 | /*jshint -W061*/ 12 | // the above stops jshint from complaining about eval. 13 | return { 14 | result : InjectedScriptHost.eval(code), 15 | exceptionDetails : undefined 16 | }; 17 | }, 18 | 19 | clearConsoleMessages : function () { 20 | console.clear(); 21 | }, 22 | 23 | isHTMLAllCollection : function (suspect) { 24 | return suspect === document.all; 25 | }, 26 | 27 | type : function (obj) { 28 | /* 29 | V8InjectedScriptHost::typeMethodCustom (found in 30 | chromium/third_party/WebKit/Source/bindings/core/v8/custom/V8InjectedScriptHostCustom.cpp) 31 | does a bunch of value->IsType() calls (->isString, ->isArray etc), and 32 | then some V8Type::hasInstance (V8Mode::hasInstance, V8NodeList::hasInstance, ...) 33 | 34 | isArrayLike above got patched to capture the array ones. 35 | 36 | The best we can repliate that is a Object..toString call. 37 | */ 38 | 39 | // [object Type] 40 | // ^--^ 41 | var exposedType = ({}).toString.call(obj).slice(8, -1); 42 | var verbatim = ['String', 'Array', 'Boolean', 'Number', 'Date', 'RegExp']; 43 | 44 | if (typeof Symbol !== 'undefined') { 45 | verbatim.push('Symbol'); 46 | } 47 | 48 | if (verbatim.indexOf(exposedType) > -1) { 49 | return exposedType.toLowerCase(); 50 | } 51 | 52 | // The only thing left is checking for Node. 53 | // Since the object came from another frame, we can't use instanceof. 54 | //This is "good enough". 55 | if ('nodeType' in obj && 'ATTRIBUTE_NODE' in obj) { 56 | return 'node'; 57 | } 58 | 59 | var arrayTypes = [ 60 | 'Int8Array', 'Int16Array', 'Int32Array', 61 | 'Uint8Array', 'Uint16Array', 'Uint32Array', 62 | 'Uint8ClampedArray', 63 | 'Float32Array', 'Float64Array' 64 | ]; 65 | // forgot anything? 66 | 67 | if (arrayTypes.indexOf(obj) > -1) { 68 | return isFinite(obj.length); 69 | } 70 | 71 | // fallback 72 | return 'object'; 73 | }, 74 | 75 | internalConstructorName : function (subject) { 76 | // The actual implementation does some...weird stuff. 77 | // Fuck that. 78 | 79 | var type = ({}).toString.call(subject).slice(8, -1); 80 | if (subject !== Object(subject)) { 81 | return type; 82 | } 83 | 84 | if (subject.constructor) { 85 | return subject.constructor.name; 86 | } 87 | return type; 88 | }, 89 | 90 | suppressWarningsAndCall : function (obj, method) { 91 | // yeah, fuck actual implementation. 92 | var args = [].slice.call(arguments, 2); 93 | return method.apply(obj, args); 94 | } 95 | }; 96 | -------------------------------------------------------------------------------- /public/js/main/App.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /** 6 | * @constructor 7 | * @implements {WebInspector.Console.UIDelegate} 8 | */ 9 | WebInspector.App = function() 10 | { 11 | /* 12 | if (WebInspector.overridesSupport.responsiveDesignAvailable()) { 13 | this._toggleEmulationButton = new WebInspector.StatusBarButton(WebInspector.UIString("Toggle device mode."), "emulation-status-bar-item"); 14 | this._toggleEmulationButton.toggled = WebInspector.overridesSupport.emulationEnabled(); 15 | this._toggleEmulationButton.addEventListener("click", this._toggleEmulationEnabled, this); 16 | WebInspector.overridesSupport.addEventListener(WebInspector.OverridesSupport.Events.EmulationStateChanged, this._emulationEnabledChanged, this); 17 | WebInspector.overridesSupport.addEventListener(WebInspector.OverridesSupport.Events.OverridesWarningUpdated, this._overridesWarningUpdated, this); 18 | } 19 | */ 20 | WebInspector.console.setUIDelegate(this); 21 | }; 22 | 23 | WebInspector.App.prototype = { 24 | _toggleEmulationEnabled: function() 25 | { 26 | WebInspector.overridesSupport.setEmulationEnabled(!this._toggleEmulationButton.toggled); 27 | }, 28 | 29 | _emulationEnabledChanged: function() 30 | { 31 | this._toggleEmulationButton.toggled = WebInspector.overridesSupport.emulationEnabled(); 32 | if (!WebInspector.overridesSupport.responsiveDesignAvailable() && WebInspector.overridesSupport.emulationEnabled()) { 33 | WebInspector.inspectorView.showViewInDrawer("emulation", true); 34 | } 35 | }, 36 | 37 | _overridesWarningUpdated: function() 38 | { 39 | if (!this._toggleEmulationButton) { 40 | return; 41 | } 42 | var message = WebInspector.overridesSupport.warningMessage(); 43 | this._toggleEmulationButton.title = message || WebInspector.UIString("Toggle device mode."); 44 | this._toggleEmulationButton.element.classList.toggle("warning", !!message); 45 | }, 46 | 47 | createRootView: function() 48 | { 49 | }, 50 | 51 | /** 52 | * @param {!WebInspector.Target} mainTarget 53 | */ 54 | presentUI: function(mainTarget) 55 | { 56 | WebInspector.inspectorView.showInitialPanel(); 57 | 58 | /* 59 | WebInspector.overridesSupport.applyInitialOverrides(); 60 | if (!WebInspector.overridesSupport.responsiveDesignAvailable() && WebInspector.overridesSupport.emulationEnabled()) 61 | WebInspector.inspectorView.showViewInDrawer("emulation", true); 62 | */ 63 | this._overridesWarningUpdated(); 64 | }, 65 | 66 | showConsole: function() 67 | { 68 | WebInspector.Revealer.reveal(WebInspector.console); 69 | } 70 | }; 71 | 72 | /** 73 | * @constructor 74 | * @implements {WebInspector.StatusBarButton.Provider} 75 | */ 76 | WebInspector.App.EmulationButtonProvider = function() 77 | { 78 | }; 79 | 80 | WebInspector.App.EmulationButtonProvider.prototype = { 81 | /** 82 | * @return {?WebInspector.StatusBarButton} 83 | */ 84 | button: function() 85 | { 86 | if (!(WebInspector.app instanceof WebInspector.App)) { 87 | return null; 88 | } 89 | return WebInspector.app._toggleEmulationButton || null; 90 | } 91 | }; 92 | 93 | /** 94 | * @type {!WebInspector.App} 95 | */ 96 | WebInspector.app = WebInspector.app; 97 | -------------------------------------------------------------------------------- /public/css/suggestBox.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Google Inc. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | .suggest-box-overlay { 32 | position: absolute; 33 | background-color: transparent; 34 | z-index: 1000; 35 | pointer-events: none; 36 | overflow: hidden; 37 | display: flex; 38 | flex-direction: row; 39 | } 40 | 41 | .suggest-box-overlay .suggest-box-left-spacer { 42 | flex: 0 1 auto; 43 | } 44 | 45 | .suggest-box-overlay .suggest-box-horizontal { 46 | display: flex; 47 | flex-direction: column; 48 | flex: 0 0 auto; 49 | max-width: 300px; 50 | } 51 | 52 | .suggest-box-overlay .suggest-box-top-spacer { 53 | flex: auto; 54 | } 55 | 56 | .suggest-box-overlay.under-anchor .suggest-box-top-spacer, 57 | .suggest-box-overlay:not(.under-anchor) .suggest-box-bottom-spacer { 58 | flex: 0 0 auto; 59 | } 60 | 61 | .suggest-box-overlay .suggest-box { 62 | background-color: #FFFFFF; 63 | border: 1px solid rgb(66%, 66%, 66%); 64 | pointer-events: auto; 65 | margin-left: -3px; 66 | overflow-x: hidden; 67 | overflow-y: auto; 68 | display: flex; 69 | flex-direction: column; 70 | flex: 0 0 auto; 71 | border-radius: 5px 5px 5px 0; 72 | } 73 | 74 | .suggest-box-overlay.under-anchor .suggest-box { 75 | border-radius: 0 5px 5px 5px; 76 | } 77 | 78 | .suggest-box-overlay .suggest-box .suggest-box-content-item { 79 | padding: 1px; 80 | margin: 0; 81 | overflow: hidden; 82 | text-overflow: ellipsis; 83 | border: 1px solid transparent; 84 | flex: 0 0 auto; 85 | padding-right: 0px; 86 | white-space: nowrap; 87 | } 88 | 89 | .suggest-box-overlay .suggest-box .suggest-box-content-item .prefix { 90 | font-weight: bold; 91 | } 92 | 93 | .suggest-box-overlay .suggest-box .suggest-box-content-item .spacer { 94 | display: inline-block; 95 | width: 20px; 96 | } 97 | 98 | .suggest-box-overlay .suggest-box .suggest-box-content-item.selected { 99 | background-color: rgba(56, 121, 217, 0.1); 100 | } 101 | 102 | .suggest-box-overlay .suggest-box .suggest-box-content-item:hover:not(.selected) { 103 | border: 1px solid rgb(204, 204, 204); 104 | } 105 | -------------------------------------------------------------------------------- /public/js/ui/Context.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /** 6 | * @constructor 7 | */ 8 | WebInspector.Context = function() 9 | { 10 | this._flavors = new Map(); 11 | this._eventDispatchers = new Map(); 12 | }; 13 | 14 | /** 15 | * @enum {string} 16 | */ 17 | WebInspector.Context.Events = { 18 | FlavorChanged: "FlavorChanged" 19 | }; 20 | 21 | WebInspector.Context.prototype = { 22 | /** 23 | * @param {function(new:T, ...)} flavorType 24 | * @param {?T} flavorValue 25 | * @template T 26 | */ 27 | setFlavor: function(flavorType, flavorValue) 28 | { 29 | var value = this._flavors.get(flavorType) || null; 30 | if (value === flavorValue) { 31 | return; 32 | } 33 | if (flavorValue) { 34 | this._flavors.put(flavorType, flavorValue); 35 | } 36 | else { 37 | this._flavors.remove(flavorType); 38 | } 39 | 40 | this._dispatchFlavorChange(flavorType, flavorValue); 41 | }, 42 | 43 | /** 44 | * @param {function(new:T, ...)} flavorType 45 | * @param {?T} flavorValue 46 | * @template T 47 | */ 48 | _dispatchFlavorChange: function(flavorType, flavorValue) 49 | { 50 | var dispatcher = this._eventDispatchers.get(flavorType); 51 | if (!dispatcher) { 52 | return; 53 | } 54 | dispatcher.dispatchEventToListeners(WebInspector.Context.Events.FlavorChanged, flavorValue); 55 | }, 56 | 57 | /** 58 | * @param {function(new:Object, ...)} flavorType 59 | * @param {function(!WebInspector.Event)} listener 60 | * @param {!Object=} thisObject 61 | */ 62 | addFlavorChangeListener: function(flavorType, listener, thisObject) 63 | { 64 | var dispatcher = this._eventDispatchers.get(flavorType); 65 | if (!dispatcher) { 66 | dispatcher = new WebInspector.Object(); 67 | this._eventDispatchers.put(flavorType, dispatcher); 68 | } 69 | dispatcher.addEventListener(WebInspector.Context.Events.FlavorChanged, listener, thisObject); 70 | }, 71 | 72 | /** 73 | * @param {function(new:Object, ...)} flavorType 74 | * @param {function(!WebInspector.Event)} listener 75 | * @param {!Object=} thisObject 76 | */ 77 | removeFlavorChangeListener: function(flavorType, listener, thisObject) 78 | { 79 | var dispatcher = this._eventDispatchers.get(flavorType); 80 | if (!dispatcher) { 81 | return; 82 | } 83 | dispatcher.removeEventListener(WebInspector.Context.Events.FlavorChanged, listener, thisObject); 84 | if (!dispatcher.hasEventListeners(WebInspector.Context.Events.FlavorChanged)) { 85 | this._eventDispatchers.remove(flavorType); 86 | } 87 | }, 88 | 89 | /** 90 | * @param {function(new:T, ...)} flavorType 91 | * @return {?T} 92 | * @template T 93 | */ 94 | flavor: function(flavorType) 95 | { 96 | return this._flavors.get(flavorType) || null; 97 | }, 98 | 99 | /** 100 | * @return {!Array.} 101 | */ 102 | flavors: function() 103 | { 104 | return this._flavors.keys(); 105 | }, 106 | 107 | /** 108 | * @param {!Array.} extensions 109 | * @return {!Set.} 110 | */ 111 | applicableExtensions: function(extensions) 112 | { 113 | var targetExtensionSet = new Set(); 114 | 115 | var availableFlavors = Set.fromArray(this.flavors()); 116 | extensions.forEach(function(extension) { 117 | if (WebInspector.moduleManager.isExtensionApplicableToContextTypes(extension, availableFlavors)) { 118 | targetExtensionSet.add(extension); 119 | } 120 | }); 121 | 122 | return targetExtensionSet; 123 | } 124 | }; 125 | 126 | WebInspector.context = new WebInspector.Context(); 127 | -------------------------------------------------------------------------------- /templates/jession.jinja: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jsh 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /public/css/tabbedPane.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. 3 | * Copyright (C) 2009 Anthony Ricaud 4 | * Copyright (C) 2011 Google Inc. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above 14 | * copyright notice, this list of conditions and the following disclaimer 15 | * in the documentation and/or other materials provided with the 16 | * distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. 22 | * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | .tabbed-pane { 32 | flex: auto; 33 | overflow: hidden; 34 | } 35 | 36 | .tabbed-pane-content { 37 | position: relative; 38 | overflow: auto; 39 | flex: auto; 40 | display: flex; 41 | flex-direction: column; 42 | } 43 | 44 | .tabbed-pane-content.has-no-tabs { 45 | background-color: lightgray; 46 | } 47 | 48 | .tabbed-pane-placeholder { 49 | font-size: 14px; 50 | text-align: center; 51 | margin-top: 20px; 52 | text-shadow: rgba(255, 255, 255, 0.75) 0 1px 0; 53 | } 54 | 55 | .tabbed-pane-header { 56 | flex: 0 0 23px; 57 | border-bottom: 1px solid rgb(163, 163, 163); 58 | overflow: hidden; 59 | width: 100%; 60 | } 61 | 62 | .tabbed-pane-header-contents { 63 | margin: 0 10px; 64 | } 65 | 66 | .tabbed-pane-header-tabs { 67 | float: left; 68 | } 69 | 70 | .tabbed-pane-header-tab { 71 | float: left; 72 | margin-top: 2px; 73 | padding: 2px 4px 2px 4px; 74 | height: 21px; 75 | border: 1px solid transparent; 76 | border-bottom: none; 77 | line-height: 15px; 78 | white-space: nowrap; 79 | text-overflow: ellipsis; 80 | overflow: hidden; 81 | cursor: default; 82 | } 83 | 84 | .tabbed-pane-header-tab.measuring { 85 | visibility: hidden; 86 | } 87 | 88 | .tabbed-pane-header-tab.selected { 89 | border: 1px solid rgb(163, 163, 163); 90 | border-bottom: none; 91 | } 92 | 93 | .tabbed-pane-header-tab.selected { 94 | background: white; 95 | border-top-color: #bbb; 96 | } 97 | 98 | .tabbed-pane-header-tab .close-button-gray { 99 | position: relative; 100 | top: 2px; 101 | left: 1px; 102 | margin-left: 2px; 103 | margin-top: -3px; 104 | visibility: hidden; 105 | } 106 | 107 | .tabbed-pane-header-tab:hover .close-button-gray, 108 | .tabbed-pane-header-tab.selected .close-button-gray { 109 | visibility: visible; 110 | } 111 | 112 | .tabbed-pane-header-tab-icon { 113 | width: 11px; 114 | height: 10px; 115 | margin-top: 3px; 116 | float: left; 117 | display: block; 118 | margin-right: 1px; 119 | } 120 | 121 | .tabbed-pane-header-tabs-drop-down-container { 122 | float: left; 123 | position: relative; 124 | vertical-align: bottom; 125 | padding-left: 3px; 126 | line-height: 20px; 127 | } 128 | 129 | .tabbed-pane-header-tabs-drop-down-container.measuring { 130 | visibility: hidden; 131 | } 132 | 133 | .tabbed-pane-header-tabs-drop-down { 134 | position: relative; 135 | opacity: 0.7; 136 | color: rgb(30, 30, 30); 137 | font-size: 133%; 138 | padding: 0 3px; 139 | } 140 | 141 | .tabbed-pane-header-tabs-drop-down:hover { 142 | opacity: 1.0; 143 | } 144 | 145 | .tabbed-pane-header-tabs-drop-down:active { 146 | opacity: 0.8; 147 | } 148 | 149 | .tabbed-pane-header-tabs-drop-down > select.drop-down-menu { 150 | position: absolute; 151 | top: 0; 152 | right: 0; 153 | bottom: 0; 154 | left: 0; 155 | opacity: 0; 156 | font-size: 75%; 157 | width: 20px; 158 | } 159 | -------------------------------------------------------------------------------- /public/css/filter.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Google Inc. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | .filter-text-filter { 32 | display: flex; 33 | margin-left: 2px; 34 | margin-right: 2px; 35 | flex: 0 1 120px; 36 | min-width: 40px; 37 | } 38 | 39 | .filter-text-filter.supports-regex { 40 | flex: 0 0 155px; 41 | } 42 | 43 | .filter-text-filter label { 44 | margin: auto 0; 45 | } 46 | 47 | .filter-bitset-filter { 48 | padding: 0 10px !important; 49 | overflow: hidden; 50 | display: flex !important; 51 | } 52 | 53 | .filter-bitset-filter li { 54 | display: inline-block; 55 | margin: auto 2px; 56 | padding: 4px 6px 3px 6px; 57 | background: transparent; 58 | text-shadow: rgba(255, 255, 255, 0.5) 0 1px 0; 59 | border-radius: 8px; 60 | } 61 | 62 | .filter-bitset-filter-divider { 63 | background-color: #ccc; 64 | height: 16px; 65 | width: 1px; 66 | margin: auto 2px; 67 | display: inline-block; 68 | } 69 | 70 | .filter-bitset-filter li.selected, 71 | .filter-bitset-filter li:hover, 72 | .filter-bitset-filter li:active { 73 | color: white; 74 | text-shadow: rgba(0, 0, 0, 0.4) 0 1px 0; 75 | } 76 | 77 | .filter-bitset-filter li:hover { 78 | background: rgba(0, 0, 0, 0.2); 79 | } 80 | 81 | .filter-bitset-filter li.selected { 82 | background: rgba(0, 0, 0, 0.3); 83 | } 84 | 85 | .filter-bitset-filter li:active { 86 | background: rgba(0, 0, 0, 0.5); 87 | } 88 | 89 | .filter-combobox-filter { 90 | margin-left: 5px; 91 | margin-right: 2px; 92 | flex: 0 0 auto; 93 | } 94 | 95 | .filter-checkbox-filter { 96 | padding-left: 4px; 97 | padding-right: 2px; 98 | white-space: nowrap; 99 | text-overflow: ellipsis; 100 | overflow: hidden; 101 | display: flex; 102 | } 103 | 104 | .filter-checkbox-filter > label { 105 | display: flex; 106 | margin: auto 0; 107 | } 108 | 109 | .filter-text-invalid { 110 | background-color: rgb(255, 200, 200); 111 | } 112 | 113 | .filter-checkbox-filter .checkbox-filter-checkbox { 114 | width: 10px; 115 | height: 10px; 116 | margin: auto 3px; 117 | padding: 0; 118 | border-radius: 2px; 119 | border: solid 1px; 120 | display: inline-block; 121 | overflow: visible; 122 | opacity: 0.8; 123 | flex-shrink: 0; 124 | } 125 | 126 | 127 | .filter-checkbox-filter .checkbox-filter-checkbox-check { 128 | -webkit-appearance: none; 129 | width: 11px; 130 | height: 11px; 131 | margin-top: -2px; 132 | margin-left: 1px; 133 | } 134 | 135 | .filter-checkbox-filter .checkbox-filter-checkbox-checked { 136 | background-image: url(Images/statusbarButtonGlyphs.png); 137 | background-size: 320px 144px; 138 | background-position: -129px -110px; 139 | } 140 | 141 | .filters-toggle > .glyph { 142 | -webkit-mask-position: -32px -48px; 143 | } 144 | 145 | .filters-toggle.toggled-shown .glyph { 146 | background-color: rgb(66, 129, 235); 147 | } 148 | 149 | .filters-toggle.toggled-active .glyph { 150 | background-color: rgb(216, 0, 0); 151 | } 152 | -------------------------------------------------------------------------------- /public/js/console/ConsolePanel.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Joseph Pecoraro 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 | * its contributors may be used to endorse or promote products derived 15 | * from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | //importScript("ConsoleViewMessage.js"); 30 | //importScript("ConsoleView.js"); 31 | 32 | /** 33 | * @constructor 34 | * @extends {WebInspector.Panel} 35 | */ 36 | WebInspector.ConsolePanel = function() 37 | { 38 | WebInspector.Panel.call(this, "console"); 39 | this._view = WebInspector.ConsolePanel._view(); 40 | }; 41 | 42 | /** 43 | * @return {!WebInspector.ConsoleView} 44 | */ 45 | WebInspector.ConsolePanel._view = function() 46 | { 47 | if (!WebInspector.ConsolePanel._consoleView) { 48 | WebInspector.ConsolePanel._consoleView = new WebInspector.ConsoleView(WebInspector.isWorkerFrontend()); 49 | } 50 | 51 | return WebInspector.ConsolePanel._consoleView; 52 | }; 53 | 54 | WebInspector.ConsolePanel.prototype = { 55 | /** 56 | * @return {!Element} 57 | */ 58 | defaultFocusedElement: function() 59 | { 60 | return this._view.defaultFocusedElement(); 61 | }, 62 | 63 | wasShown: function() 64 | { 65 | WebInspector.Panel.prototype.wasShown.call(this); 66 | this._view.show(this.element); 67 | }, 68 | 69 | willHide: function() 70 | { 71 | WebInspector.Panel.prototype.willHide.call(this); 72 | if (WebInspector.ConsolePanel.WrapperView._instance) { 73 | WebInspector.ConsolePanel.WrapperView._instance._showViewInWrapper(); 74 | } 75 | }, 76 | 77 | __proto__: WebInspector.Panel.prototype 78 | }; 79 | 80 | /** 81 | * @constructor 82 | * @extends {WebInspector.VBox} 83 | */ 84 | WebInspector.ConsolePanel.WrapperView = function() 85 | { 86 | WebInspector.VBox.call(this); 87 | this.element.classList.add("console-view-wrapper"); 88 | 89 | WebInspector.ConsolePanel.WrapperView._instance = this; 90 | 91 | this._view = WebInspector.ConsolePanel._view(); 92 | // FIXME: this won't be needed once drawer becomes a view. 93 | this.wasShown(); 94 | }; 95 | 96 | WebInspector.ConsolePanel.WrapperView.prototype = { 97 | wasShown: function() 98 | { 99 | if (!WebInspector.inspectorView.currentPanel() || WebInspector.inspectorView.currentPanel().name !== "console") { 100 | this._showViewInWrapper(); 101 | } 102 | }, 103 | 104 | /** 105 | * @return {!Element} 106 | */ 107 | defaultFocusedElement: function() 108 | { 109 | return this._view.defaultFocusedElement(); 110 | }, 111 | 112 | focus: function() 113 | { 114 | this._view.focus(); 115 | }, 116 | 117 | _showViewInWrapper: function() 118 | { 119 | this._view.show(this.element); 120 | }, 121 | 122 | __proto__: WebInspector.VBox.prototype 123 | }; 124 | 125 | /** 126 | * @constructor 127 | * @implements {WebInspector.Revealer} 128 | */ 129 | WebInspector.ConsolePanel.ConsoleRevealer = function() 130 | { 131 | }; 132 | 133 | WebInspector.ConsolePanel.ConsoleRevealer.prototype = { 134 | /** 135 | * @param {!Object} object 136 | */ 137 | reveal: function(object) 138 | { 139 | var consoleView = WebInspector.ConsolePanel._view(); 140 | if (consoleView.isShowing()) { 141 | consoleView.focus(); 142 | return; 143 | } 144 | WebInspector.inspectorView.showViewInDrawer("console"); 145 | } 146 | }; 147 | -------------------------------------------------------------------------------- /public/css/splitView.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Google Inc. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS 17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. 20 | * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | .split-view { 30 | display: flex; 31 | overflow: hidden; 32 | } 33 | 34 | .split-view-contents { 35 | overflow: auto; 36 | display: flex; 37 | position: relative; 38 | flex-direction: column; 39 | } 40 | 41 | .split-view-sidebar { 42 | flex: none; 43 | } 44 | 45 | .split-view-main, .split-view-sidebar.maximized { 46 | flex: auto; 47 | } 48 | 49 | .split-view.hbox > .split-view-resizer { 50 | position: absolute; 51 | top: 0; 52 | bottom: 0; 53 | width: 6px; 54 | z-index: 500; 55 | } 56 | 57 | .split-view.vbox > .split-view-resizer { 58 | position: absolute; 59 | left: 0; 60 | right: 0; 61 | height: 6px; 62 | z-index: 500; 63 | } 64 | 65 | .split-view-resizer-border { 66 | pointer-events: none; 67 | } 68 | 69 | .split-view.vbox > .split-view-resizer > .split-view-resizer-border { 70 | width: 100%; 71 | margin-top: 3px; 72 | height: 1px; 73 | border-top: 1px solid rgb(64%, 64%, 64%); 74 | } 75 | 76 | .split-view.hbox > .split-view-resizer > .split-view-resizer-border { 77 | height: 100%; 78 | margin-left: 3px; 79 | width: 1px; 80 | border-left: 1px solid rgb(64%, 64%, 64%); 81 | } 82 | 83 | .split-view button.sidebar-show-hide-button { 84 | position: absolute; 85 | background-image: none; 86 | height: 16px; 87 | width: 16px; 88 | border: none; 89 | z-index: 10; 90 | } 91 | 92 | .split-view button.left-sidebar-show-hide-button, 93 | .split-view button.top-sidebar-show-hide-button { 94 | top: 4px; 95 | left: 4px; 96 | } 97 | 98 | .split-view button.left-sidebar-show-hide-button:active, 99 | .split-view button.top-sidebar-show-hide-button:active { 100 | top: 5px; 101 | left: 3px; 102 | } 103 | 104 | .split-view button.right-sidebar-show-hide-button { 105 | top: 4px; 106 | right:2px; 107 | } 108 | 109 | .split-view button.right-sidebar-show-hide-button:active { 110 | top: 5px; 111 | right: 1px; 112 | } 113 | 114 | .split-view button.bottom-sidebar-show-hide-button { 115 | bottom: 0px; 116 | right: 1px; 117 | } 118 | 119 | .split-view button.bottom-sidebar-show-hide-button:active { 120 | bottom: 0; 121 | right: 0; 122 | } 123 | 124 | .split-view button.left-sidebar-show-hide-button.toggled-show > .glyph { 125 | -webkit-mask-position: -168px -76px; /* |> */ 126 | } 127 | 128 | .split-view button.left-sidebar-show-hide-button.toggled-hide > .glyph { 129 | -webkit-mask-position: -199px -76px; /* |< */ 130 | } 131 | 132 | .split-view button.right-sidebar-show-hide-button.toggled-show > .glyph { 133 | -webkit-mask-position: -296px -76px; /* <| */ 134 | } 135 | 136 | .split-view button.right-sidebar-show-hide-button.toggled-hide > .glyph { 137 | -webkit-mask-position: -264px -76px; /* >| */ 138 | } 139 | 140 | .split-view button.top-sidebar-show-hide-button.toggled-show > .glyph { 141 | -webkit-mask-position: -168px -76px; /* |> */ 142 | -webkit-transform: rotate(90deg); 143 | } 144 | 145 | .split-view button.top-sidebar-show-hide-button.toggled-hide > .glyph { 146 | -webkit-mask-position: -199px -76px; /* |< */ 147 | -webkit-transform: rotate(90deg); 148 | } 149 | 150 | .split-view button.bottom-sidebar-show-hide-button.toggled-show > .glyph { 151 | -webkit-mask-position: -296px -76px; /* <| */ 152 | -webkit-transform: rotate(90deg); 153 | } 154 | 155 | .split-view button.bottom-sidebar-show-hide-button.toggled-hide > .glyph { 156 | -webkit-mask-position: -264px -76px; /* >| */ 157 | -webkit-transform: rotate(90deg); 158 | } 159 | -------------------------------------------------------------------------------- /public/js/ui/ResizerWidget.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /** 6 | * @constructor 7 | * @extends {WebInspector.Object} 8 | */ 9 | WebInspector.ResizerWidget = function() 10 | { 11 | WebInspector.Object.call(this); 12 | 13 | this._isEnabled = true; 14 | this._isVertical = true; 15 | this._elements = []; 16 | this._installDragOnMouseDownBound = this._installDragOnMouseDown.bind(this); 17 | }; 18 | 19 | WebInspector.ResizerWidget.Events = { 20 | ResizeStart: "ResizeStart", 21 | ResizeUpdate: "ResizeUpdate", 22 | ResizeEnd: "ResizeEnd" 23 | }; 24 | 25 | WebInspector.ResizerWidget.prototype = { 26 | /** 27 | * @return {boolean} 28 | */ 29 | isEnabled: function() 30 | { 31 | return this._isEnabled; 32 | }, 33 | 34 | /** 35 | * @param {boolean} enabled 36 | */ 37 | setEnabled: function(enabled) 38 | { 39 | this._isEnabled = enabled; 40 | this._updateElementsClass(); 41 | }, 42 | 43 | /** 44 | * @return {boolean} 45 | */ 46 | isVertical: function() 47 | { 48 | return this._isVertical; 49 | }, 50 | 51 | /** 52 | * Vertical widget resizes height (along y-axis). 53 | * @param {boolean} vertical 54 | */ 55 | setVertical: function(vertical) 56 | { 57 | this._isVertical = vertical; 58 | this._updateElementsClass(); 59 | }, 60 | 61 | /** 62 | * @return {!Array.} 63 | */ 64 | elements: function() 65 | { 66 | return this._elements.slice(); 67 | }, 68 | 69 | /** 70 | * @param {!Element} element 71 | */ 72 | addElement: function(element) 73 | { 74 | if (this._elements.indexOf(element) !== -1) { 75 | return; 76 | } 77 | 78 | this._elements.push(element); 79 | element.addEventListener("mousedown", this._installDragOnMouseDownBound, false); 80 | element.classList.toggle("ns-resizer-widget", this._isVertical && this._isEnabled); 81 | element.classList.toggle("ew-resizer-widget", !this._isVertical && this._isEnabled); 82 | }, 83 | 84 | /** 85 | * @param {!Element} element 86 | */ 87 | removeElement: function(element) 88 | { 89 | if (this._elements.indexOf(element) === -1) { 90 | return; 91 | } 92 | 93 | this._elements.remove(element); 94 | element.removeEventListener("mousedown", this._installDragOnMouseDownBound, false); 95 | element.classList.remove("ns-resizer-widget"); 96 | element.classList.remove("ew-resizer-widget"); 97 | }, 98 | 99 | _updateElementsClass: function() 100 | { 101 | for (var i = 0; i < this._elements.length; ++i) { 102 | this._elements[i].classList.toggle("ns-resizer-widget", this._isVertical && this._isEnabled); 103 | this._elements[i].classList.toggle("ew-resizer-widget", !this._isVertical && this._isEnabled); 104 | } 105 | }, 106 | 107 | /** 108 | * @param {!Event} event 109 | */ 110 | _installDragOnMouseDown: function(event) 111 | { 112 | // Only handle drags of the nodes specified. 113 | if (this._elements.indexOf(event.target) === -1) { 114 | return false; 115 | } 116 | WebInspector.elementDragStart(this._dragStart.bind(this), this._drag.bind(this), this._dragEnd.bind(this), this._isVertical ? "ns-resize" : "ew-resize", event); 117 | }, 118 | 119 | /** 120 | * @param {!MouseEvent} event 121 | * @return {boolean} 122 | */ 123 | _dragStart: function(event) 124 | { 125 | if (!this._isEnabled) { 126 | return false; 127 | } 128 | this._startPosition = this._isVertical ? event.pageY : event.pageX; 129 | this.dispatchEventToListeners(WebInspector.ResizerWidget.Events.ResizeStart, { startPosition: this._startPosition, currentPosition: this._startPosition }); 130 | return true; 131 | }, 132 | 133 | /** 134 | * @param {!MouseEvent} event 135 | * @return {boolean} 136 | */ 137 | _drag: function(event) 138 | { 139 | if (!this._isEnabled) { 140 | this._dragEnd(event); 141 | return true; // Cancel drag. 142 | } 143 | 144 | var position = this._isVertical ? event.pageY : event.pageX; 145 | this.dispatchEventToListeners(WebInspector.ResizerWidget.Events.ResizeUpdate, { startPosition: this._startPosition, currentPosition: position, shiftKey: event.shiftKey }); 146 | event.preventDefault(); 147 | return false; // Continue drag. 148 | }, 149 | 150 | /** 151 | * @param {!MouseEvent} event 152 | */ 153 | _dragEnd: function(event) 154 | { 155 | this.dispatchEventToListeners(WebInspector.ResizerWidget.Events.ResizeEnd); 156 | delete this._startPosition; 157 | }, 158 | 159 | __proto__: WebInspector.Object.prototype 160 | }; 161 | -------------------------------------------------------------------------------- /public/css/inspectorCommon.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | cursor: default; 7 | position: relative; 8 | height: 100%; 9 | width: 100%; 10 | overflow: hidden; 11 | font-family: Lucida Grande, sans-serif; 12 | font-size: 12px; 13 | margin: 0; 14 | tab-size: 4; 15 | -webkit-user-select: none; 16 | color: #222; 17 | } 18 | 19 | body.platform-linux { 20 | color: rgb(48, 57, 66); 21 | font-family: Ubuntu, Arial, sans-serif; 22 | } 23 | 24 | body.platform-mac { 25 | color: rgb(48, 57, 66); 26 | font-family: 'Lucida Grande', sans-serif; 27 | } 28 | 29 | body.platform-windows { 30 | font-family: 'Segoe UI', Tahoma, sans-serif; 31 | } 32 | 33 | * { 34 | box-sizing: border-box; 35 | } 36 | 37 | :focus { 38 | outline: none; 39 | } 40 | 41 | img { 42 | -webkit-user-drag: none; 43 | } 44 | 45 | iframe, 46 | a img { 47 | border: none; 48 | } 49 | 50 | iframe.view { 51 | position: absolute; 52 | width: 100%; 53 | height: 100%; 54 | left: 0; 55 | right: 0; 56 | top: 0; 57 | bottom: 0; 58 | } 59 | 60 | .hidden { 61 | display: none !important; 62 | } 63 | 64 | .monospace { 65 | font-size: 11px !important; 66 | font-family: monospace; 67 | } 68 | 69 | .resources-dividers { 70 | position: absolute; 71 | left: 0; 72 | right: 0; 73 | top: 0; 74 | z-index: -100; 75 | bottom: 0; 76 | } 77 | 78 | .resources-event-dividers { 79 | position: absolute; 80 | left: 0; 81 | right: 0; 82 | height: 100%; 83 | top: 0; 84 | z-index: 300; 85 | pointer-events: none; 86 | } 87 | 88 | .resources-dividers-label-bar { 89 | position: absolute; 90 | top: 0; 91 | left: 0; 92 | right: 0; 93 | background-color: rgba(255, 255, 255, 0.85); 94 | background-clip: padding-box; 95 | height: 20px; 96 | z-index: 200; 97 | pointer-events: none; 98 | overflow: hidden; 99 | } 100 | 101 | .resources-divider { 102 | position: absolute; 103 | width: 1px; 104 | top: 0; 105 | bottom: 0; 106 | background-color: rgba(0, 0, 0, 0.1); 107 | } 108 | 109 | .resources-event-divider-padding { 110 | position: absolute; 111 | width: 8px; 112 | top: 0; 113 | bottom: 0; 114 | pointer-events: auto; 115 | } 116 | 117 | .resources-event-divider { 118 | position: absolute; 119 | width: 2px; 120 | top: 0; 121 | bottom: 0; 122 | z-index: 300; 123 | } 124 | 125 | .resources-divider-label { 126 | position: absolute; 127 | top: 4px; 128 | right: 3px; 129 | font-size: 80%; 130 | white-space: nowrap; 131 | pointer-events: none; 132 | } 133 | 134 | .overview-grid-window-selector { 135 | position: absolute; 136 | top: 0; 137 | bottom: 0; 138 | background-color: rgba(125, 173, 217, 0.5); 139 | z-index: 250; 140 | pointer-events: none; 141 | } 142 | 143 | .overview-grid-window { 144 | background-color: white; 145 | position: absolute; 146 | left: 0; 147 | right: 0; 148 | top: 0; 149 | height: 20px; 150 | z-index: 150; 151 | } 152 | 153 | .overview-grid-dividers-background { 154 | left: 0%; 155 | right: 0%; 156 | top: 0; 157 | height: 20px; 158 | background-color: black; 159 | position: absolute; 160 | } 161 | 162 | .overview-grid-window-rulers { 163 | top: 0; 164 | bottom: 0; 165 | position: absolute; 166 | opacity: 0.2; 167 | border-right: 1px solid black; 168 | border-left: 1px solid black; 169 | z-index: 250; 170 | pointer-events: none; 171 | } 172 | 173 | .overview-grid-window-resizer { 174 | position: absolute; 175 | top: 0; 176 | height: 20px; 177 | width: 5px; 178 | margin-left: -2px; 179 | margin-right: -3px; 180 | background-color: rgb(153, 153, 153); 181 | z-index: 500; 182 | border-radius: 2px; 183 | box-shadow: white 1px 0 0, white -1px 0 0, white 0 1px 0, white 0 -1px 0; 184 | } 185 | 186 | .overview-grid-window-resizer-right { 187 | margin-left: -3px; 188 | margin-right: -2px; 189 | } 190 | 191 | /* Network timing is shared between popover and network item view pane */ 192 | 193 | .network-timing-table td { 194 | padding: 0; 195 | } 196 | 197 | .network-timing-table td.caution { 198 | font-weight: bold; 199 | color: rgb(255, 128, 0); 200 | padding: 2px 0; 201 | } 202 | 203 | .network-timing-row { 204 | position: relative; 205 | height: 16px; 206 | } 207 | 208 | .network-timing-bar { 209 | position: absolute; 210 | background-color: red; 211 | border-left: 1px solid red; 212 | opacity: 0.4; 213 | top: 0; 214 | bottom: 0; 215 | } 216 | 217 | .network-timing-bar-title { 218 | position: absolute; 219 | color: #222; 220 | top: 1px; 221 | } 222 | 223 | .highlighted-search-result { 224 | border-radius: 1px; 225 | padding: 1px; 226 | margin: -1px; 227 | background-color: rgba(255, 255, 0, 0.8); 228 | } 229 | 230 | .sidebar-separator { 231 | background-color: rgb(230, 230, 230); 232 | padding: 0 5px; 233 | border-top: 1px solid rgb(189, 189, 189); 234 | border-bottom: 1px solid rgb(189, 189, 189); 235 | color: rgb(50, 50, 50); 236 | white-space: nowrap; 237 | text-overflow: ellipsis; 238 | overflow: hidden; 239 | line-height: 16px; 240 | } 241 | 242 | .sidebar-label { 243 | font-size: 11px; 244 | } 245 | 246 | .pie-chart { 247 | position: relative; 248 | } 249 | 250 | .pie-chart-foreground { 251 | position: absolute; 252 | width: 100%; 253 | height: 100%; 254 | text-align: center; 255 | z-index: 10; 256 | top: 0; 257 | } 258 | -------------------------------------------------------------------------------- /public/js/components/ExecutionContextSelector.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /** 6 | * @constructor 7 | * @implements {WebInspector.TargetManager.Observer} 8 | */ 9 | WebInspector.ExecutionContextSelector = function() 10 | { 11 | WebInspector.targetManager.observeTargets(this); 12 | WebInspector.context.addFlavorChangeListener(WebInspector.ExecutionContext, this._executionContextChanged, this); 13 | WebInspector.context.addFlavorChangeListener(WebInspector.Target, this._targetChanged, this); 14 | }; 15 | 16 | WebInspector.ExecutionContextSelector.prototype = { 17 | 18 | /** 19 | * @param {!WebInspector.Target} target 20 | */ 21 | targetAdded: function(target) 22 | { 23 | console.log('yaaay new target', target); 24 | if (!WebInspector.context.flavor(WebInspector.Target)) { 25 | WebInspector.context.setFlavor(WebInspector.Target, target); 26 | } 27 | 28 | target.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.ExecutionContextCreated, this._onExecutionContextCreated, this); 29 | target.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.ExecutionContextDestroyed, this._onExecutionContextDestroyed, this); 30 | }, 31 | 32 | /** 33 | * @param {!WebInspector.Target} target 34 | */ 35 | targetRemoved: function(target) 36 | { 37 | target.runtimeModel.removeEventListener(WebInspector.RuntimeModel.Events.ExecutionContextCreated, this._onExecutionContextCreated, this); 38 | target.runtimeModel.removeEventListener(WebInspector.RuntimeModel.Events.ExecutionContextDestroyed, this._onExecutionContextDestroyed, this); 39 | var currentExecutionContext = WebInspector.context.flavor(WebInspector.ExecutionContext); 40 | if (currentExecutionContext && currentExecutionContext.target() === target) { 41 | this._currentExecutionContextGone(); 42 | } 43 | 44 | var targets = WebInspector.targetManager.targets(); 45 | if (WebInspector.context.flavor(WebInspector.Target) === target && targets.length) { 46 | WebInspector.context.setFlavor(WebInspector.Target, targets[0]); 47 | } 48 | }, 49 | 50 | /** 51 | * @param {!WebInspector.Event} event 52 | */ 53 | _executionContextChanged: function(event) 54 | { 55 | var newContext = /** @type {?WebInspector.ExecutionContext} */ (event.data); 56 | if (newContext) { 57 | WebInspector.context.setFlavor(WebInspector.Target, newContext.target()); 58 | } 59 | }, 60 | 61 | /** 62 | * @param {!WebInspector.Event} event 63 | */ 64 | _targetChanged: function(event) 65 | { 66 | var newTarget = /** @type {?WebInspector.Target} */(event.data); 67 | var currentContext = WebInspector.context.flavor(WebInspector.ExecutionContext); 68 | 69 | if (!newTarget || (currentContext && currentContext.target() === newTarget)) { 70 | return; 71 | } 72 | 73 | var executionContexts = newTarget.runtimeModel.executionContexts(); 74 | console.log('execution contexts!', executionContexts); 75 | if (!executionContexts.length) { 76 | return; 77 | } 78 | 79 | var newContext = executionContexts[0]; 80 | for (var i = 1; i < executionContexts.length; ++i) { 81 | if (executionContexts[i].isMainWorldContext) { 82 | newContext = executionContexts[i]; 83 | } 84 | } 85 | WebInspector.context.setFlavor(WebInspector.ExecutionContext, newContext); 86 | }, 87 | 88 | /** 89 | * @param {!WebInspector.Event} event 90 | */ 91 | _onExecutionContextCreated: function(event) 92 | { 93 | var executionContext = /** @type {!WebInspector.ExecutionContext}*/ (event.data); 94 | if (!WebInspector.context.flavor(WebInspector.ExecutionContext)) { 95 | WebInspector.context.setFlavor(WebInspector.ExecutionContext, executionContext); 96 | } 97 | }, 98 | 99 | /** 100 | * @param {!WebInspector.Event} event 101 | */ 102 | _onExecutionContextDestroyed: function(event) 103 | { 104 | var executionContext = /** @type {!WebInspector.ExecutionContext}*/ (event.data); 105 | if (WebInspector.context.flavor(WebInspector.ExecutionContext) === executionContext) { 106 | this._currentExecutionContextGone(); 107 | } 108 | }, 109 | 110 | _currentExecutionContextGone: function() 111 | { 112 | var targets = WebInspector.targetManager.targets(); 113 | var newContext = null; 114 | for (var i = 0; i < targets.length; ++i) { 115 | var executionContexts = targets[i].runtimeModel.executionContexts(); 116 | if (executionContexts.length) { 117 | newContext = executionContexts[0]; 118 | break; 119 | } 120 | } 121 | WebInspector.context.setFlavor(WebInspector.ExecutionContext, newContext); 122 | } 123 | 124 | }; 125 | 126 | /** 127 | * @param {!Element} proxyElement 128 | * @param {!Range} wordRange 129 | * @param {boolean} force 130 | * @param {function(!Array., number=)} completionsReadyCallback 131 | */ 132 | WebInspector.ExecutionContextSelector.completionsForTextPromptInCurrentContext = function(proxyElement, wordRange, force, completionsReadyCallback) 133 | { 134 | var executionContext = WebInspector.context.flavor(WebInspector.ExecutionContext); 135 | if (!executionContext) { 136 | completionsReadyCallback([]); 137 | return; 138 | } 139 | 140 | // Pass less stop characters to rangeOfWord so the range will be a more complete expression. 141 | var expressionRange = wordRange.startContainer.rangeOfWord(wordRange.startOffset, " =:[({;,!+-*/&|^<>", proxyElement, "backward"); 142 | var expressionString = expressionRange.toString(); 143 | var prefix = wordRange.toString(); 144 | executionContext.completionsForExpression(expressionString, prefix, force, completionsReadyCallback); 145 | }; 146 | -------------------------------------------------------------------------------- /public/js/host/Platform.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Google Inc. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 | * its contributors may be used to endorse or promote products derived 15 | * from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | /** 30 | * @return {string} 31 | */ 32 | WebInspector.platform = function() 33 | { 34 | if (!WebInspector._platform) { 35 | // zirak: meh. to be improved. 36 | WebInspector._platform = 'linux'; /*InspectorFrontendHost.platform();*/ 37 | } 38 | return WebInspector._platform; 39 | }; 40 | 41 | /** 42 | * @return {boolean} 43 | */ 44 | WebInspector.isMac = function() 45 | { 46 | if (typeof WebInspector._isMac === "undefined") { 47 | WebInspector._isMac = WebInspector.platform() === "mac"; 48 | } 49 | 50 | return WebInspector._isMac; 51 | }; 52 | 53 | /** 54 | * @return {boolean} 55 | */ 56 | WebInspector.isWin = function() 57 | { 58 | if (typeof WebInspector._isWin === "undefined") { 59 | WebInspector._isWin = WebInspector.platform() === "windows"; 60 | } 61 | 62 | return WebInspector._isWin; 63 | }; 64 | 65 | WebInspector.PlatformFlavor = { 66 | WindowsVista: "windows-vista", 67 | MacTiger: "mac-tiger", 68 | MacLeopard: "mac-leopard", 69 | MacSnowLeopard: "mac-snowleopard", 70 | MacLion: "mac-lion" 71 | }; 72 | 73 | /** 74 | * @return {string} 75 | */ 76 | WebInspector.platformFlavor = function() 77 | { 78 | function detectFlavor() 79 | { 80 | var userAgent = navigator.userAgent, 81 | match; 82 | 83 | if (WebInspector.platform() === "windows") { 84 | match = userAgent.match(/Windows NT (\d+)\.(?:\d+)/); 85 | if (match && match[1] >= 6) { 86 | return WebInspector.PlatformFlavor.WindowsVista; 87 | } 88 | return null; 89 | } else if (WebInspector.platform() === "mac") { 90 | match = userAgent.match(/Mac OS X\s*(?:(\d+)_(\d+))?/); 91 | if (!match || match[1] != 10) { 92 | return WebInspector.PlatformFlavor.MacSnowLeopard; 93 | } 94 | switch (Number(match[2])) { 95 | case 4: 96 | return WebInspector.PlatformFlavor.MacTiger; 97 | case 5: 98 | return WebInspector.PlatformFlavor.MacLeopard; 99 | case 6: 100 | return WebInspector.PlatformFlavor.MacSnowLeopard; 101 | case 7: 102 | return WebInspector.PlatformFlavor.MacLion; 103 | /*jshint -W086*/ 104 | case 8: // Matches the default version 105 | case 9: // Matches the default version 106 | default: 107 | return ""; 108 | } 109 | } 110 | } 111 | 112 | if (!WebInspector._platformFlavor) { 113 | WebInspector._platformFlavor = detectFlavor(); 114 | } 115 | 116 | return WebInspector._platformFlavor; 117 | }; 118 | 119 | /** 120 | * @return {string} 121 | */ 122 | WebInspector.port = function() 123 | { 124 | if (!WebInspector._port) { 125 | WebInspector._port = InspectorFrontendHost.port(); 126 | } 127 | 128 | return WebInspector._port; 129 | }; 130 | 131 | /** 132 | * @return {string} 133 | */ 134 | WebInspector.fontFamily = function() 135 | { 136 | if (WebInspector._fontFamily) { 137 | return WebInspector._fontFamily; 138 | } 139 | switch (WebInspector.platform()) { 140 | case "linux": 141 | WebInspector._fontFamily = "Ubuntu, Arial, sans-serif"; 142 | break; 143 | case "mac": 144 | WebInspector._fontFamily = "'Lucida Grande', sans-serif"; 145 | break; 146 | case "windows": 147 | WebInspector._fontFamily = "'Segoe UI', Tahoma, sans-serif"; 148 | break; 149 | } 150 | return WebInspector._fontFamily; 151 | }; 152 | 153 | /** 154 | * @return {string} 155 | */ 156 | WebInspector.monospaceFontFamily = function() 157 | { 158 | if (WebInspector._monospaceFontFamily) { 159 | return WebInspector._monospaceFontFamily; 160 | } 161 | switch (WebInspector.platform()) { 162 | case "linux": 163 | WebInspector._monospaceFontFamily = "dejavu sans mono, monospace"; 164 | break; 165 | case "mac": 166 | WebInspector._monospaceFontFamily = "Menlo, monospace"; 167 | break; 168 | case "windows": 169 | WebInspector._monospaceFontFamily = "Consolas, monospace"; 170 | break; 171 | } 172 | return WebInspector._monospaceFontFamily; 173 | }; 174 | 175 | /** 176 | * @return {boolean} 177 | */ 178 | WebInspector.isWorkerFrontend = function() 179 | { 180 | return !!WebInspector.queryParam("dedicatedWorkerId") || !!WebInspector.queryParam("isSharedWorker"); 181 | }; 182 | -------------------------------------------------------------------------------- /public/css/dataGrid.css: -------------------------------------------------------------------------------- 1 | .data-grid { 2 | position: relative; 3 | border: 1px solid #aaa; 4 | font-size: 11px; 5 | line-height: 120%; 6 | } 7 | 8 | .data-grid .highlight { 9 | background-color: rgb(255, 230, 179); 10 | } 11 | 12 | .data-grid tr.selected .highlight { 13 | background-color: transparent; 14 | } 15 | 16 | .data-grid table { 17 | table-layout: fixed; 18 | border-spacing: 0; 19 | border-collapse: separate; 20 | height: 100%; 21 | width: 100%; 22 | } 23 | 24 | .data-grid .header-container, 25 | .data-grid .data-container { 26 | position: absolute; 27 | left: 0; 28 | right: 0; 29 | overflow-x: hidden; 30 | } 31 | 32 | .data-grid .header-container { 33 | top: 0; 34 | height: 17px; 35 | } 36 | 37 | .data-grid .data-container { 38 | top: 17px; 39 | bottom: 0; 40 | overflow-y: overlay; 41 | -webkit-transform: translateZ(0); 42 | } 43 | 44 | .data-grid.inline { 45 | border-left: none; 46 | } 47 | 48 | .data-grid.inline .header-container, 49 | .data-grid.inline .data-container { 50 | position: static; 51 | } 52 | 53 | .data-grid.inline col.corner, 54 | .data-grid.inline th.corner, 55 | .data-grid.inline td.corner { 56 | display: none; 57 | } 58 | 59 | .data-grid th.corner, 60 | .data-grid td.corner, 61 | .data-grid col.corner { 62 | width: 14px; 63 | padding-right: 0; 64 | padding-left: 0; 65 | border-left: 0 none transparent !important; 66 | } 67 | 68 | .data-grid tr.filler { 69 | display: table-row !important; 70 | height: 0; 71 | } 72 | 73 | .data-grid tr.filler td { 74 | height: auto !important; 75 | padding: 0 !important; 76 | } 77 | 78 | .data-grid table.data { 79 | position: absolute; 80 | left: 0; 81 | top: 0; 82 | right: 0; 83 | bottom: 0; 84 | border-top: 0 none transparent; 85 | background-image: linear-gradient(to bottom, white, white 50%, rgb(234, 243, 255) 50%, rgb(234, 243, 255)); 86 | background-size: 128px 32px; 87 | table-layout: fixed; 88 | } 89 | 90 | .data-grid.inline table.data { 91 | position: static; 92 | } 93 | 94 | .data-grid table.data tr { 95 | display: none; 96 | } 97 | 98 | .data-grid table.data tr.revealed { 99 | display: table-row; 100 | } 101 | 102 | .data-grid td, 103 | .data-grid th { 104 | white-space: nowrap; 105 | text-overflow: ellipsis; 106 | overflow: hidden; 107 | line-height: 14px; 108 | border-left: 1px solid #aaa; 109 | } 110 | 111 | .data-grid td { 112 | height: 16px; /* Keep in sync with .data-grid table.data @ background-size */ 113 | } 114 | 115 | .data-grid th { 116 | height: auto; 117 | } 118 | 119 | .data-grid:not(.inline) th:first-child, 120 | .data-grid:not(.inline) td:first-child { 121 | border-left: none !important; 122 | } 123 | 124 | .data-grid td { 125 | vertical-align: top; 126 | padding: 1px 4px; 127 | -webkit-user-select: text; 128 | } 129 | 130 | .data-grid th { 131 | text-align: left; 132 | background-color: rgb(236, 236, 236); 133 | border-bottom: 1px solid #aaa; 134 | font-weight: normal; 135 | vertical-align: middle; 136 | padding: 0 4px; 137 | } 138 | 139 | .data-grid td > div, 140 | .data-grid th > div { 141 | white-space: nowrap; 142 | text-overflow: ellipsis; 143 | overflow: hidden; 144 | } 145 | 146 | .data-grid th > div { 147 | overflow: hidden; 148 | } 149 | 150 | .data-grid td.editing > div { 151 | text-overflow: clip; 152 | } 153 | 154 | .data-grid .center { 155 | text-align: center; 156 | } 157 | 158 | .data-grid .right { 159 | text-align: right; 160 | } 161 | 162 | .data-grid th.sortable div { 163 | position: relative; 164 | } 165 | 166 | .data-grid th.sortable:active { 167 | background-color: rgba(0, 0, 0, 0.15); 168 | } 169 | 170 | .data-grid th.sort-ascending > div::after, 171 | .data-grid th.sort-descending > div::after { 172 | position: absolute; 173 | top: 1px; 174 | right: 0; 175 | background-image: url(Images/statusbarButtonGlyphs.png); 176 | background-size: 320px 144px; 177 | opacity: 0.5; 178 | width: 8px; 179 | height: 10px; 180 | content: "a"; 181 | color: transparent; 182 | } 183 | 184 | @media (-webkit-min-device-pixel-ratio: 1.5) { 185 | .data-grid th.sort-ascending > div::after, 186 | .data-grid th.sort-descending > div::after { 187 | background-image: url(Images/statusbarButtonGlyphs_2x.png); 188 | } 189 | } /* media */ 190 | 191 | .data-grid th.sort-ascending > div::after { 192 | background-position: -4px -108px; 193 | } 194 | 195 | .data-grid th.sort-descending > div::after { 196 | background-position: -20px -96px; 197 | } 198 | 199 | .data-grid th:hover { 200 | background-color: rgba(0, 0, 0, 0.1); 201 | } 202 | 203 | .data-grid button { 204 | line-height: 18px; 205 | color: inherit; 206 | } 207 | 208 | .data-grid tr.parent td.disclosure::before { 209 | -webkit-user-select: none; 210 | -webkit-mask-image: url(Images/statusbarButtonGlyphs.png); 211 | -webkit-mask-size: 320px 144px; 212 | float: left; 213 | width: 8px; 214 | margin-right: 2px; 215 | content: "a"; 216 | color: transparent; 217 | position: relative; 218 | top: 1px; 219 | } 220 | 221 | .data-grid tr.parent td.disclosure::before { 222 | background-color: rgb(110, 110, 110); 223 | -webkit-mask-position: -4px -96px; 224 | } 225 | 226 | @media (-webkit-min-device-pixel-ratio: 1.5) { 227 | .data-grid tr.parent td.disclosure::before { 228 | -webkit-mask-image: url(Images/statusbarButtonGlyphs_2x.png); 229 | } 230 | } /* media */ 231 | 232 | .data-grid tr.expanded td.disclosure::before { 233 | -webkit-mask-position: -20px -96px; 234 | } 235 | 236 | .data-grid tr.selected { 237 | background-color: rgb(212, 212, 212); 238 | color: inherit; 239 | } 240 | 241 | .data-grid:focus tr.selected { 242 | background-color: rgb(56, 121, 217); 243 | color: white; 244 | } 245 | 246 | .data-grid:focus tr.selected a { 247 | color: white; 248 | } 249 | 250 | .data-grid:focus tr.parent.selected td.disclosure::before { 251 | background-color: white; 252 | -webkit-mask-position: -4px -96px; 253 | } 254 | 255 | .data-grid:focus tr.expanded.selected td.disclosure::before { 256 | background-color: white; 257 | -webkit-mask-position: -20px -96px; 258 | } 259 | 260 | .data-grid tr:not(.parent) td.disclosure { 261 | text-indent: 10px; 262 | } 263 | 264 | .data-grid-resizer { 265 | position: absolute; 266 | top: 0; 267 | bottom: 0; 268 | width: 5px; 269 | z-index: 500; 270 | } 271 | -------------------------------------------------------------------------------- /public/js/jsh.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var jsh = window.jsh = {}; 3 | 4 | jsh.save = function () { 5 | var data = { 6 | commands : this.getCommandsText() 7 | }; 8 | 9 | // TODO add some XHR abstraction 10 | var xhr = new XMLHttpRequest(); 11 | xhr.open('POST', 'save'); 12 | 13 | xhr.setRequestHeader('Content-Type', 'application/json'); 14 | xhr.responseType = 'json'; 15 | 16 | xhr.onload = function () { 17 | // yeah... 18 | console.log(xhr); 19 | 20 | history.replaceState(null, '', '/' + xhr.response.id); 21 | WebInspector.console.log('Saved. ID: ' + xhr.response.id); 22 | }; 23 | 24 | xhr.send(JSON.stringify(data)); 25 | }; 26 | 27 | jsh.loadFromText = function (text) { 28 | if (!text) { 29 | console.warn('what do you want from me?'); 30 | return; 31 | } 32 | 33 | var commands = JSON.parse(text); 34 | if (!Array.isArray(commands) || !commands.length) { 35 | return; 36 | } 37 | 38 | // we can't just add the commands one after the other, since running a 39 | //command is an async operation. 40 | // what we should do is wait for a command to be evaluated before moving on 41 | //to the next. 42 | // what really *should* be done (TODO) is only run a command once the one 43 | //before has been printed. 44 | var consoleModel = WebInspector.targetManager.targets()[0].consoleModel, 45 | commandEvaluated = WebInspector.ConsoleModel.Events.CommandEvaluated; 46 | 47 | consoleModel.addEventListener(commandEvaluated, addNextCommand); 48 | 49 | addNextCommand(); 50 | 51 | function addNextCommand () { 52 | var cmd = commands.shift(); 53 | console.warn(cmd); 54 | if (!cmd) { 55 | consoleModel.removeEventListener(commandEvaluated, addNextCommand); 56 | return; 57 | } 58 | 59 | WebInspector.ConsolePanel._view()._appendCommand(cmd, true); 60 | } 61 | }; 62 | 63 | jsh.getCommandsText = function () { 64 | // Fuck me... 65 | var messages = WebInspector.ConsolePanel._view()._consoleMessages; 66 | 67 | return messages.filter(filterCommands).map(getCommandText); 68 | 69 | function filterCommands (msg) { 70 | return msg instanceof WebInspector.ConsoleCommand; 71 | } 72 | function getCommandText (cmd) { 73 | return cmd.text; 74 | } 75 | }; 76 | 77 | jsh.handleMessage = function (messageObject) { 78 | var method = messageObject.method, 79 | params = messageObject.params; 80 | 81 | console.warn('handleMessage', messageObject); 82 | 83 | var ignore = ['Runtime.enable', 'Console.enable', 'Runtime.isRunRequired']; 84 | if (ignore.indexOf(method) > -1) { 85 | return false; 86 | } 87 | 88 | messageObject.action = 'bridge'; 89 | sendMessageToEvalFrame(messageObject); 90 | }; 91 | 92 | // This is butt ugly. Find out when everything we need has been loaded. 93 | var jshReady = { 94 | evalFrame : false, 95 | webinspector : false 96 | }; 97 | 98 | jsh.maybeLoad = function () { 99 | if (!jshReady.evalFrame || !jshReady.webinspector) { 100 | return; 101 | } 102 | 103 | // Welcome, stranger! 104 | if (!localStorage.introduced) { 105 | localStorage.introduced = true; 106 | jsh.introduce(); 107 | } 108 | 109 | // Load up the commands (if there are any) 110 | var commands = document.getElementById('jsh-commands').textContent; 111 | 112 | if (commands) { 113 | jsh.loadFromText(commands); 114 | } 115 | }; 116 | 117 | // Eval frame! What we do with it is laid out in detail in public/evalFrame.html 118 | 119 | jsh.evalFrame = document.createElement('iframe'); 120 | jsh.evalFrame.src = 'evalFrame.html'; 121 | jsh.evalFrame.hidden = true; 122 | jsh.evalFrame.sandbox = 'allow-scripts'; 123 | document.body.appendChild(jsh.evalFrame); 124 | jsh.evalWindow = jsh.evalFrame.contentWindow; 125 | 126 | var frameSecret = (function () { 127 | var whyDoINeedThis = new Uint32Array(10); 128 | crypto.getRandomValues(whyDoINeedThis); 129 | 130 | var fuckayou = [].slice.call(whyDoINeedThis); 131 | return fuckayou.map(String.fromCharCode).join(''); 132 | })(); 133 | 134 | // sometimes, I wonder if variables are aware of their names, and my variables 135 | //are constantly walking around with their heads to the ground, being made fun 136 | //of by other programs with their fancy variables. 137 | // sometimes. 138 | 139 | jsh.evalFrame.onload = function () { 140 | jsh.evalWindow.postMessage({ 141 | action : 'secret', 142 | secret : '', 143 | newSecret : frameSecret 144 | }, '*'); 145 | 146 | jshReady.evalFrame = true; 147 | jsh.maybeLoad(); 148 | }; 149 | 150 | function sendMessageToEvalFrame (data) { 151 | data.secret = frameSecret; 152 | jsh.evalWindow.postMessage(data, '*'); 153 | } 154 | 155 | window.addEventListener('message', function messageListener (e) { 156 | var data = e.data; 157 | 158 | if (e.source !== jsh.evalWindow || data.secret !== frameSecret) { 159 | console.warn(e); 160 | return; 161 | } 162 | 163 | InspectorBackend._connection.dispatch(data); 164 | }); 165 | 166 | window.addEventListener('DOMContentLoaded', function () { 167 | // yes, this is horrible. I will not apologise. 168 | setTimeout(function () { 169 | jshReady.webinspector = true; 170 | jsh.maybeLoad(); 171 | }, 50); 172 | // ;-; 173 | }); 174 | 175 | // at the end because blobs of text. 176 | jsh.introduce = function () { 177 | var header = function () {/** @preserve 178 | _ _ 179 | (_) | | Welcome to jsh, an embedded Chrome dev-tools console! 180 | _ ___| |__ 181 | | / __| '_ \ 182 | | \__ \ | | | Play around with javascript, hit save (Ctrl+S), share with 183 | | |___/_| |_| friends and strangers! 184 | _/ | 185 | |__/ 186 | 187 | * Source code on https://github.com/Zirak/jsh. 188 | * Bug reports more than welcome: https://github.com/Zirak/jsh/issues 189 | * Hit me on twitter: @zirakertan 190 | 191 | Hit Ctrl+L to clear this message. 192 | */ 193 | console.lulz = 4; delete console.lulz; 194 | }.toString().split('\n').slice(1, -2).join('\n'); 195 | 196 | WebInspector.console.log(header); 197 | }; 198 | 199 | /* 200 | _____ _ _____ 201 | |_ _| | | __ \ 202 | | | | |__ ___ | | \/ __ _ _ __ ___ ___ 203 | | | | '_ \ / _ \ | | __ / _` | '_ ` _ \ / _ \ 204 | | | | | | | __/ | |_\ \ (_| | | | | | | __/ 205 | \_/ |_| |_|\___| \____/\__,_|_| |_| |_|\___| 206 | 207 | */ 208 | })(); 209 | -------------------------------------------------------------------------------- /public/js/components/Section.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 Apple Inc. All rights reserved. 3 | * Copyright (C) 2009 Google Inc. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 | * its contributors may be used to endorse or promote products derived 16 | * from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** 31 | * @constructor 32 | * @param {string|!Element} title 33 | * @param {string=} subtitle 34 | */ 35 | WebInspector.Section = function(title, subtitle) 36 | { 37 | this.element = document.createElement("div"); 38 | this.element.className = "section"; 39 | this.element._section = this; 40 | 41 | this.headerElement = document.createElement("div"); 42 | this.headerElement.className = "header"; 43 | 44 | this.titleElement = document.createElement("div"); 45 | this.titleElement.className = "title"; 46 | 47 | this.subtitleElement = document.createElement("div"); 48 | this.subtitleElement.className = "subtitle"; 49 | 50 | this.headerElement.appendChild(this.subtitleElement); 51 | this.headerElement.appendChild(this.titleElement); 52 | 53 | this.headerElement.addEventListener("click", this.handleClick.bind(this), false); 54 | this.element.appendChild(this.headerElement); 55 | 56 | this.title = title; 57 | this.subtitle = subtitle; 58 | this._expanded = false; 59 | }; 60 | 61 | WebInspector.Section.prototype = { 62 | get title() 63 | { 64 | return this._title; 65 | }, 66 | 67 | set title(x) 68 | { 69 | if (this._title === x) { 70 | return; 71 | } 72 | this._title = x; 73 | 74 | if (x instanceof Node) { 75 | this.titleElement.removeChildren(); 76 | this.titleElement.appendChild(x); 77 | } else { 78 | this.titleElement.textContent = x; 79 | } 80 | }, 81 | 82 | get subtitle() 83 | { 84 | return this._subtitle; 85 | }, 86 | 87 | set subtitle(x) 88 | { 89 | if (this._subtitle === x) { 90 | return; 91 | } 92 | this._subtitle = x; 93 | this.subtitleElement.textContent = x; 94 | }, 95 | 96 | get subtitleAsTextForTest() 97 | { 98 | var result = this.subtitleElement.textContent; 99 | var child = this.subtitleElement.querySelector("[data-uncopyable]"); 100 | if (child) { 101 | var linkData = child.getAttribute("data-uncopyable"); 102 | if (linkData) { 103 | result += linkData; 104 | } 105 | } 106 | return result; 107 | }, 108 | 109 | get expanded() 110 | { 111 | return this._expanded; 112 | }, 113 | 114 | set expanded(x) 115 | { 116 | if (x) { 117 | this.expand(); 118 | } 119 | else { 120 | this.collapse(); 121 | } 122 | }, 123 | 124 | get populated() 125 | { 126 | return this._populated; 127 | }, 128 | 129 | set populated(x) 130 | { 131 | this._populated = x; 132 | if (!x && this._expanded) { 133 | this.onpopulate(); 134 | this._populated = true; 135 | } 136 | }, 137 | 138 | onpopulate: function() 139 | { 140 | // Overriden by subclasses. 141 | }, 142 | 143 | get firstSibling() 144 | { 145 | var parent = this.element.parentElement; 146 | if (!parent) { 147 | return null; 148 | } 149 | 150 | var childElement = parent.firstChild; 151 | while (childElement) { 152 | if (childElement._section) { 153 | return childElement._section; 154 | } 155 | childElement = childElement.nextSibling; 156 | } 157 | 158 | return null; 159 | }, 160 | 161 | get lastSibling() 162 | { 163 | var parent = this.element.parentElement; 164 | if (!parent) { 165 | return null; 166 | } 167 | 168 | var childElement = parent.lastChild; 169 | while (childElement) { 170 | if (childElement._section) { 171 | return childElement._section; 172 | } 173 | childElement = childElement.previousSibling; 174 | } 175 | 176 | return null; 177 | }, 178 | 179 | get nextSibling() 180 | { 181 | var curElement = this.element; 182 | do { 183 | curElement = curElement.nextSibling; 184 | } while (curElement && !curElement._section); 185 | 186 | return curElement ? curElement._section : null; 187 | }, 188 | 189 | get previousSibling() 190 | { 191 | var curElement = this.element; 192 | do { 193 | curElement = curElement.previousSibling; 194 | } while (curElement && !curElement._section); 195 | 196 | return curElement ? curElement._section : null; 197 | }, 198 | 199 | expand: function() 200 | { 201 | if (this._expanded) { 202 | return; 203 | } 204 | this._expanded = true; 205 | this.element.classList.add("expanded"); 206 | 207 | if (!this._populated) { 208 | this.onpopulate(); 209 | this._populated = true; 210 | } 211 | }, 212 | 213 | collapse: function() 214 | { 215 | if (!this._expanded) { 216 | return; 217 | } 218 | this._expanded = false; 219 | this.element.classList.remove("expanded"); 220 | }, 221 | 222 | toggleExpanded: function() 223 | { 224 | this.expanded = !this.expanded; 225 | }, 226 | 227 | handleClick: function(event) 228 | { 229 | this.toggleExpanded(); 230 | event.consume(); 231 | } 232 | }; 233 | -------------------------------------------------------------------------------- /public/evalFrame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /public/js/common/Object.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | /** 27 | * @constructor 28 | * @implements {WebInspector.EventTarget} 29 | */ 30 | WebInspector.Object = function() { 31 | }; 32 | 33 | WebInspector.Object.prototype = { 34 | /** 35 | * @param {string} eventType 36 | * @param {function(!WebInspector.Event)} listener 37 | * @param {!Object=} thisObject 38 | */ 39 | addEventListener: function(eventType, listener, thisObject) 40 | { 41 | if (!listener) { 42 | console.assert(false); 43 | } 44 | 45 | if (!this._listeners) { 46 | this._listeners = {}; 47 | } 48 | if (!this._listeners[eventType]) { 49 | this._listeners[eventType] = []; 50 | } 51 | this._listeners[eventType].push({ thisObject: thisObject, listener: listener }); 52 | }, 53 | 54 | /** 55 | * @param {string} eventType 56 | * @param {function(!WebInspector.Event)} listener 57 | * @param {!Object=} thisObject 58 | */ 59 | removeEventListener: function(eventType, listener, thisObject) 60 | { 61 | console.assert(listener); 62 | 63 | if (!this._listeners || !this._listeners[eventType]) { 64 | return; 65 | } 66 | var listeners = this._listeners[eventType]; 67 | for (var i = 0; i < listeners.length; ++i) { 68 | if (listeners[i].listener === listener && listeners[i].thisObject === thisObject) { 69 | listeners.splice(i--, 1); 70 | } 71 | } 72 | 73 | if (!listeners.length) { 74 | delete this._listeners[eventType]; 75 | } 76 | }, 77 | 78 | removeAllListeners: function() 79 | { 80 | delete this._listeners; 81 | }, 82 | 83 | /** 84 | * @param {string} eventType 85 | * @return {boolean} 86 | */ 87 | hasEventListeners: function(eventType) 88 | { 89 | if (!this._listeners || !this._listeners[eventType]) { 90 | return false; 91 | } 92 | return true; 93 | }, 94 | 95 | /** 96 | * @param {string} eventType 97 | * @param {*=} eventData 98 | * @return {boolean} 99 | */ 100 | dispatchEventToListeners: function(eventType, eventData) 101 | { 102 | if (!this._listeners || !this._listeners[eventType]) { 103 | return false; 104 | } 105 | 106 | var event = new WebInspector.Event(this, eventType, eventData); 107 | var listeners = this._listeners[eventType].slice(0); 108 | for (var i = 0; i < listeners.length; ++i) { 109 | listeners[i].listener.call(listeners[i].thisObject, event); 110 | if (event._stoppedPropagation) { 111 | break; 112 | } 113 | } 114 | 115 | return event.defaultPrevented; 116 | } 117 | }; 118 | 119 | /** 120 | * @constructor 121 | * @param {!WebInspector.EventTarget} target 122 | * @param {string} type 123 | * @param {*=} data 124 | */ 125 | WebInspector.Event = function(target, type, data) 126 | { 127 | this.target = target; 128 | this.type = type; 129 | this.data = data; 130 | this.defaultPrevented = false; 131 | this._stoppedPropagation = false; 132 | }; 133 | 134 | WebInspector.Event.prototype = { 135 | stopPropagation: function() 136 | { 137 | this._stoppedPropagation = true; 138 | }, 139 | 140 | preventDefault: function() 141 | { 142 | this.defaultPrevented = true; 143 | }, 144 | 145 | /** 146 | * @param {boolean=} preventDefault 147 | */ 148 | consume: function(preventDefault) 149 | { 150 | this.stopPropagation(); 151 | if (preventDefault) { 152 | this.preventDefault(); 153 | } 154 | } 155 | }; 156 | 157 | /** 158 | * @constructor 159 | * @extends {WebInspector.Object} 160 | */ 161 | WebInspector.Lock = function() 162 | { 163 | this._count = 0; // Reentrant. 164 | }; 165 | 166 | /** 167 | * @enum {string} 168 | */ 169 | WebInspector.Lock.Events = { 170 | StateChanged: "StateChanged" 171 | }; 172 | 173 | WebInspector.Lock.prototype = { 174 | /** 175 | * @return {boolean} 176 | */ 177 | isAcquired: function() 178 | { 179 | return !!this._count; 180 | }, 181 | 182 | acquire: function() 183 | { 184 | if (++this._count === 1) { 185 | this.dispatchEventToListeners(WebInspector.Lock.Events.StateChanged); 186 | } 187 | }, 188 | 189 | release: function() 190 | { 191 | --this._count; 192 | if (this._count < 0) { 193 | console.error("WebInspector.Lock acquire/release calls are unbalanced " + new Error().stack); 194 | return; 195 | } 196 | if (!this._count) { 197 | this.dispatchEventToListeners(WebInspector.Lock.Events.StateChanged); 198 | } 199 | }, 200 | 201 | __proto__: WebInspector.Object.prototype 202 | }; 203 | 204 | /** 205 | * @interface 206 | */ 207 | WebInspector.EventTarget = function() 208 | { 209 | }; 210 | 211 | WebInspector.EventTarget.prototype = { 212 | /** 213 | * @param {string} eventType 214 | * @param {function(!WebInspector.Event)} listener 215 | * @param {!Object=} thisObject 216 | */ 217 | addEventListener: function(eventType, listener, thisObject) { }, 218 | 219 | /** 220 | * @param {string} eventType 221 | * @param {function(!WebInspector.Event)} listener 222 | * @param {!Object=} thisObject 223 | */ 224 | removeEventListener: function(eventType, listener, thisObject) { }, 225 | 226 | removeAllListeners: function() { }, 227 | 228 | /** 229 | * @param {string} eventType 230 | * @return {boolean} 231 | */ 232 | hasEventListeners: function(eventType) { }, 233 | 234 | /** 235 | * @param {string} eventType 236 | * @param {*=} eventData 237 | * @return {boolean} 238 | */ 239 | dispatchEventToListeners: function(eventType, eventData) { }, 240 | }; 241 | -------------------------------------------------------------------------------- /public/js/ui/Dialog.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Google Inc. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | /** 32 | * @constructor 33 | * @param {!Element} relativeToElement 34 | * @param {!WebInspector.DialogDelegate} delegate 35 | */ 36 | WebInspector.Dialog = function(relativeToElement, delegate) 37 | { 38 | this._delegate = delegate; 39 | this._relativeToElement = relativeToElement; 40 | 41 | this._glassPane = new WebInspector.GlassPane(); 42 | WebInspector.GlassPane.DefaultFocusedViewStack.push(this); 43 | 44 | // Install glass pane capturing events. 45 | this._glassPane.element.tabIndex = 0; 46 | this._glassPane.element.addEventListener("focus", this._onGlassPaneFocus.bind(this), false); 47 | 48 | this._element = this._glassPane.element.createChild("div"); 49 | this._element.tabIndex = 0; 50 | this._element.addEventListener("focus", this._onFocus.bind(this), false); 51 | this._element.addEventListener("keydown", this._onKeyDown.bind(this), false); 52 | this._closeKeys = [ 53 | WebInspector.KeyboardShortcut.Keys.Enter.code, 54 | WebInspector.KeyboardShortcut.Keys.Esc.code, 55 | ]; 56 | 57 | delegate.show(this._element); 58 | 59 | this._position(); 60 | this._delegate.focus(); 61 | }; 62 | 63 | /** 64 | * @return {?WebInspector.Dialog} 65 | */ 66 | WebInspector.Dialog.currentInstance = function() 67 | { 68 | return WebInspector.Dialog._instance; 69 | }; 70 | 71 | /** 72 | * @param {!Element} relativeToElement 73 | * @param {!WebInspector.DialogDelegate} delegate 74 | */ 75 | WebInspector.Dialog.show = function(relativeToElement, delegate) 76 | { 77 | if (WebInspector.Dialog._instance) { 78 | return; 79 | } 80 | WebInspector.Dialog._instance = new WebInspector.Dialog(relativeToElement, delegate); 81 | }; 82 | 83 | WebInspector.Dialog.hide = function() 84 | { 85 | if (!WebInspector.Dialog._instance) { 86 | return; 87 | } 88 | WebInspector.Dialog._instance._hide(); 89 | }; 90 | 91 | WebInspector.Dialog.prototype = { 92 | focus: function() 93 | { 94 | this._element.focus(); 95 | }, 96 | 97 | _hide: function() 98 | { 99 | if (this._isHiding) { 100 | return; 101 | } 102 | this._isHiding = true; 103 | 104 | this._delegate.willHide(); 105 | 106 | delete WebInspector.Dialog._instance; 107 | WebInspector.GlassPane.DefaultFocusedViewStack.pop(); 108 | this._glassPane.dispose(); 109 | }, 110 | 111 | _onGlassPaneFocus: function(event) 112 | { 113 | this._hide(); 114 | }, 115 | 116 | _onFocus: function(event) 117 | { 118 | this._delegate.focus(); 119 | }, 120 | 121 | _position: function() 122 | { 123 | this._delegate.position(this._element, this._relativeToElement); 124 | }, 125 | 126 | _onKeyDown: function(event) 127 | { 128 | if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Tab.code) { 129 | event.preventDefault(); 130 | return; 131 | } 132 | 133 | if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Enter.code) { 134 | this._delegate.onEnter(event); 135 | } 136 | 137 | if (!event.handled && this._closeKeys.indexOf(event.keyCode) >= 0) { 138 | this._hide(); 139 | event.consume(true); 140 | } 141 | } 142 | }; 143 | 144 | /** 145 | * @constructor 146 | * @extends {WebInspector.Object} 147 | */ 148 | WebInspector.DialogDelegate = function() 149 | { 150 | /** @type {!Element} */ 151 | this.element = this.element; 152 | }; 153 | 154 | WebInspector.DialogDelegate.prototype = { 155 | /** 156 | * @param {!Element} element 157 | */ 158 | show: function(element) 159 | { 160 | element.appendChild(this.element); 161 | this.element.classList.add("dialog-contents"); 162 | element.classList.add("dialog", "toolbar-colors"); 163 | }, 164 | 165 | /** 166 | * @param {!Element} element 167 | * @param {!Element} relativeToElement 168 | */ 169 | position: function(element, relativeToElement) 170 | { 171 | var container = WebInspector.Dialog._modalHostView.element; 172 | var box = relativeToElement.boxInWindow(window).relativeToElement(container); 173 | 174 | var positionX = box.x + (relativeToElement.offsetWidth - element.offsetWidth) / 2; 175 | positionX = Number.constrain(positionX, 0, container.offsetWidth - element.offsetWidth); 176 | 177 | var positionY = box.y + (relativeToElement.offsetHeight - element.offsetHeight) / 2; 178 | positionY = Number.constrain(positionY, 0, container.offsetHeight - element.offsetHeight); 179 | 180 | element.style.position = "absolute"; 181 | element.positionAt(positionX, positionY, container); 182 | }, 183 | 184 | focus: function() { }, 185 | 186 | onEnter: function(event) { }, 187 | 188 | willHide: function() { }, 189 | 190 | __proto__: WebInspector.Object.prototype 191 | }; 192 | 193 | /** @type {?WebInspector.View} */ 194 | WebInspector.Dialog._modalHostView = null; 195 | 196 | /** 197 | * @param {!WebInspector.View} view 198 | */ 199 | WebInspector.Dialog.setModalHostView = function(view) 200 | { 201 | WebInspector.Dialog._modalHostView = view; 202 | }; 203 | 204 | /** 205 | * FIXME: make utility method in Dialog, so clients use it instead of this getter. 206 | * Method should be like Dialog.showModalElement(position params, reposition callback). 207 | * @return {?WebInspector.View} 208 | */ 209 | WebInspector.Dialog.modalHostView = function() 210 | { 211 | return WebInspector.Dialog._modalHostView; 212 | }; 213 | 214 | WebInspector.Dialog.modalHostRepositioned = function() 215 | { 216 | if (WebInspector.Dialog._instance) { 217 | WebInspector.Dialog._instance._position(); 218 | } 219 | }; 220 | -------------------------------------------------------------------------------- /public/js/common/Geometry.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Google Inc. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | WebInspector.Geometry = {}; 32 | 33 | /** 34 | * @type {number} 35 | */ 36 | WebInspector.Geometry._Eps = 1e-5; 37 | 38 | /** 39 | * @constructor 40 | * @param {number} x 41 | * @param {number} y 42 | * @param {number} z 43 | */ 44 | WebInspector.Geometry.Vector = function(x, y, z) 45 | { 46 | this.x = x; 47 | this.y = y; 48 | this.z = z; 49 | }; 50 | 51 | WebInspector.Geometry.Vector.prototype = { 52 | /** 53 | * @return {number} 54 | */ 55 | length: function() 56 | { 57 | return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); 58 | }, 59 | 60 | normalize: function() 61 | { 62 | var length = this.length(); 63 | if (length <= WebInspector.Geometry._Eps) { 64 | return; 65 | } 66 | 67 | this.x /= length; 68 | this.y /= length; 69 | this.z /= length; 70 | } 71 | }; 72 | 73 | /** 74 | * @constructor 75 | * @param {number} alpha 76 | * @param {number} beta 77 | * @param {number} gamma 78 | */ 79 | WebInspector.Geometry.EulerAngles = function(alpha, beta, gamma) 80 | { 81 | this.alpha = alpha; 82 | this.beta = beta; 83 | this.gamma = gamma; 84 | }; 85 | 86 | /** 87 | * @param {!CSSMatrix} rotationMatrix 88 | * @return {!WebInspector.Geometry.EulerAngles} 89 | */ 90 | WebInspector.Geometry.EulerAngles.fromRotationMatrix = function(rotationMatrix) 91 | { 92 | var beta = Math.atan2(rotationMatrix.m23, rotationMatrix.m33); 93 | var gamma = Math.atan2(-rotationMatrix.m13, Math.sqrt(rotationMatrix.m11 * rotationMatrix.m11 + rotationMatrix.m12 * rotationMatrix.m12)); 94 | var alpha = Math.atan2(rotationMatrix.m12, rotationMatrix.m11); 95 | return new WebInspector.Geometry.EulerAngles(WebInspector.Geometry.radToDeg(alpha), WebInspector.Geometry.radToDeg(beta), WebInspector.Geometry.radToDeg(gamma)); 96 | }; 97 | 98 | /** 99 | * @param {!WebInspector.Geometry.Vector} u 100 | * @param {!WebInspector.Geometry.Vector} v 101 | * @return {number} 102 | */ 103 | WebInspector.Geometry.scalarProduct = function(u, v) 104 | { 105 | return u.x * v.x + u.y * v.y + u.z * v.z; 106 | }; 107 | 108 | /** 109 | * @param {!WebInspector.Geometry.Vector} u 110 | * @param {!WebInspector.Geometry.Vector} v 111 | * @return {!WebInspector.Geometry.Vector} 112 | */ 113 | WebInspector.Geometry.crossProduct = function(u, v) 114 | { 115 | var x = u.y * v.z - u.z * v.y; 116 | var y = u.z * v.x - u.x * v.z; 117 | var z = u.x * v.y - u.y * v.x; 118 | return new WebInspector.Geometry.Vector(x, y, z); 119 | }; 120 | 121 | /** 122 | * @param {!WebInspector.Geometry.Vector} u 123 | * @param {!WebInspector.Geometry.Vector} v 124 | * @return {!WebInspector.Geometry.Vector} 125 | */ 126 | WebInspector.Geometry.subtract = function(u, v) 127 | { 128 | var x = u.x - v.x; 129 | var y = u.y - v.y; 130 | var z = u.z - v.z; 131 | return new WebInspector.Geometry.Vector(x, y, z); 132 | }; 133 | 134 | /** 135 | * @param {!WebInspector.Geometry.Vector} v 136 | * @param {!CSSMatrix} m 137 | * @return {!WebInspector.Geometry.Vector} 138 | */ 139 | WebInspector.Geometry.multiplyVectorByMatrixAndNormalize = function(v, m) 140 | { 141 | var t = v.x * m.m14 + v.y * m.m24 + v.z * m.m34 + m.m44; 142 | var x = (v.x * m.m11 + v.y * m.m21 + v.z * m.m31 + m.m41) / t; 143 | var y = (v.x * m.m12 + v.y * m.m22 + v.z * m.m32 + m.m42) / t; 144 | var z = (v.x * m.m13 + v.y * m.m23 + v.z * m.m33 + m.m43) / t; 145 | return new WebInspector.Geometry.Vector(x, y, z); 146 | }; 147 | 148 | /** 149 | * @param {!WebInspector.Geometry.Vector} u 150 | * @param {!WebInspector.Geometry.Vector} v 151 | * @return {number} 152 | */ 153 | WebInspector.Geometry.calculateAngle = function(u, v) 154 | { 155 | var uLength = u.length(); 156 | var vLength = v.length(); 157 | if (uLength <= WebInspector.Geometry._Eps || vLength <= WebInspector.Geometry._Eps) { 158 | return 0; 159 | } 160 | var cos = WebInspector.Geometry.scalarProduct(u, v) / uLength / vLength; 161 | if (Math.abs(cos) > 1) { 162 | return 0; 163 | } 164 | return WebInspector.Geometry.radToDeg(Math.acos(cos)); 165 | }; 166 | 167 | /** 168 | * @param {number} rad 169 | * @return {number} 170 | */ 171 | WebInspector.Geometry.radToDeg = function(rad) 172 | { 173 | return rad * 180 / Math.PI; 174 | }; 175 | 176 | 177 | /** 178 | * @constructor 179 | * @param {number} width 180 | * @param {number} height 181 | */ 182 | function Size(width, height) 183 | { 184 | this.width = width; 185 | this.height = height; 186 | } 187 | 188 | /** 189 | * @param {?Size} size 190 | * @return {boolean} 191 | */ 192 | Size.prototype.isEqual = function(size) 193 | { 194 | return !!size && this.width === size.width && this.height === size.height; 195 | }; 196 | 197 | /** 198 | * @param {!Size|number} size 199 | * @return {!Size} 200 | */ 201 | Size.prototype.widthToMax = function(size) 202 | { 203 | return new Size(Math.max(this.width, (typeof size === "number" ? size : size.width)), this.height); 204 | }; 205 | 206 | /** 207 | * @param {!Size|number} size 208 | * @return {!Size} 209 | */ 210 | Size.prototype.addWidth = function(size) 211 | { 212 | return new Size(this.width + (typeof size === "number" ? size : size.width), this.height); 213 | }; 214 | 215 | /** 216 | * @param {!Size|number} size 217 | * @return {!Size} 218 | */ 219 | Size.prototype.heightToMax = function(size) 220 | { 221 | return new Size(this.width, Math.max(this.height, (typeof size === "number" ? size : size.height))); 222 | }; 223 | 224 | /** 225 | * @param {!Size|number} size 226 | * @return {!Size} 227 | */ 228 | Size.prototype.addHeight = function(size) 229 | { 230 | return new Size(this.width, this.height + (typeof size === "number" ? size : size.height)); 231 | }; 232 | -------------------------------------------------------------------------------- /public/js/components/Panel.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 | * its contributors may be used to endorse or promote products derived 15 | * from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | /** 30 | * @extends {WebInspector.VBox} 31 | * @constructor 32 | */ 33 | WebInspector.Panel = function(name) 34 | { 35 | WebInspector.VBox.call(this); 36 | 37 | this.element.classList.add("panel"); 38 | this.element.classList.add(name); 39 | this._panelName = name; 40 | 41 | this._shortcuts = /** !Object. */ ({}); 42 | }; 43 | 44 | // Should by in sync with style declarations. 45 | WebInspector.Panel.counterRightMargin = 25; 46 | 47 | WebInspector.Panel.prototype = { 48 | get name() 49 | { 50 | return this._panelName; 51 | }, 52 | 53 | reset: function() 54 | { 55 | }, 56 | 57 | /** 58 | * @return {!Element} 59 | */ 60 | defaultFocusedElement: function() 61 | { 62 | return this.element; 63 | }, 64 | 65 | /** 66 | * @return {?WebInspector.SearchableView} 67 | */ 68 | searchableView: function() 69 | { 70 | return null; 71 | }, 72 | 73 | /** 74 | * @param {string} text 75 | */ 76 | replaceSelectionWith: function(text) 77 | { 78 | }, 79 | 80 | /** 81 | * @param {string} query 82 | * @param {string} text 83 | */ 84 | replaceAllWith: function(query, text) 85 | { 86 | }, 87 | 88 | /** 89 | * @return {!Array.} 90 | */ 91 | elementsToRestoreScrollPositionsFor: function() 92 | { 93 | return []; 94 | }, 95 | 96 | /** 97 | * @param {!KeyboardEvent} event 98 | */ 99 | handleShortcut: function(event) 100 | { 101 | var shortcutKey = WebInspector.KeyboardShortcut.makeKeyFromEvent(event); 102 | var handler = this._shortcuts[shortcutKey]; 103 | if (handler && handler(event)) { 104 | event.handled = true; 105 | return; 106 | } 107 | 108 | var searchableView = this.searchableView(); 109 | if (!searchableView) { 110 | return; 111 | } 112 | 113 | function handleSearchShortcuts(shortcuts, handler) 114 | { 115 | for (var i = 0; i < shortcuts.length; ++i) { 116 | if (shortcuts[i].key !== shortcutKey) { 117 | continue; 118 | } 119 | return handler.call(searchableView); 120 | } 121 | return false; 122 | } 123 | 124 | if (handleSearchShortcuts(WebInspector.SearchableView.findShortcuts(), searchableView.handleFindShortcut)) { 125 | event.handled = true; 126 | } 127 | else if (handleSearchShortcuts(WebInspector.SearchableView.cancelSearchShortcuts(), searchableView.handleCancelSearchShortcut)) { 128 | event.handled = true; 129 | } 130 | }, 131 | 132 | /** 133 | * @param {!Array.} keys 134 | * @param {function(!Event=):boolean} handler 135 | */ 136 | registerShortcuts: function(keys, handler) 137 | { 138 | for (var i = 0; i < keys.length; ++i) { 139 | this._shortcuts[keys[i].key] = handler; 140 | } 141 | }, 142 | 143 | __proto__: WebInspector.VBox.prototype 144 | }; 145 | 146 | /** 147 | * @extends {WebInspector.Panel} 148 | * @param {string} name 149 | * @param {number=} defaultWidth 150 | * @constructor 151 | */ 152 | WebInspector.PanelWithSidebarTree = function(name, defaultWidth) 153 | { 154 | WebInspector.Panel.call(this, name); 155 | 156 | this._panelSplitView = new WebInspector.SplitView(true, false, this._panelName + "PanelSplitViewState", defaultWidth || 200); 157 | this._panelSplitView.show(this.element); 158 | 159 | var sidebarView = new WebInspector.VBox(); 160 | sidebarView.setMinimumSize(100, 25); 161 | sidebarView.show(this._panelSplitView.sidebarElement()); 162 | 163 | this._sidebarElement = sidebarView.element; 164 | this._sidebarElement.classList.add("sidebar"); 165 | var sidebarTreeElement = this._sidebarElement.createChild("ol", "sidebar-tree"); 166 | this.sidebarTree = new TreeOutline(sidebarTreeElement); 167 | }; 168 | 169 | WebInspector.PanelWithSidebarTree.prototype = { 170 | /** 171 | * @return {!Element} 172 | */ 173 | sidebarElement: function() 174 | { 175 | return this._sidebarElement; 176 | }, 177 | 178 | /** 179 | * @return {!Element} element 180 | */ 181 | mainElement: function() 182 | { 183 | return this._panelSplitView.mainElement(); 184 | }, 185 | 186 | /** 187 | * @return {!Element} 188 | */ 189 | defaultFocusedElement: function() 190 | { 191 | return this.sidebarTree.element || this.element; 192 | }, 193 | 194 | __proto__: WebInspector.Panel.prototype 195 | }; 196 | 197 | /** 198 | * @interface 199 | */ 200 | WebInspector.PanelDescriptor = function() 201 | { 202 | }; 203 | 204 | WebInspector.PanelDescriptor.prototype = { 205 | /** 206 | * @return {string} 207 | */ 208 | name: function() {}, 209 | 210 | /** 211 | * @return {string} 212 | */ 213 | title: function() {}, 214 | 215 | /** 216 | * @return {!WebInspector.Panel} 217 | */ 218 | panel: function() {} 219 | }; 220 | 221 | /** 222 | * @constructor 223 | * @param {!WebInspector.ModuleManager.Extension} extension 224 | * @implements {WebInspector.PanelDescriptor} 225 | */ 226 | WebInspector.ModuleManagerExtensionPanelDescriptor = function(extension) 227 | { 228 | this._name = extension.descriptor().name; 229 | this._title = WebInspector.UIString(extension.descriptor().title); 230 | this._extension = extension; 231 | }; 232 | 233 | WebInspector.ModuleManagerExtensionPanelDescriptor.prototype = { 234 | /** 235 | * @return {string} 236 | */ 237 | name: function() 238 | { 239 | return this._name; 240 | }, 241 | 242 | /** 243 | * @return {string} 244 | */ 245 | title: function() 246 | { 247 | return this._title; 248 | }, 249 | 250 | /** 251 | * @return {!WebInspector.Panel} 252 | */ 253 | panel: function() 254 | { 255 | return /** @type {!WebInspector.Panel} */ (this._extension.instance()); 256 | } 257 | }; 258 | -------------------------------------------------------------------------------- /public/js/components/DockController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Google Inc. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | /** 32 | * @constructor 33 | * @extends {WebInspector.Object} 34 | * @param {boolean} canDock 35 | */ 36 | WebInspector.DockController = function(canDock) 37 | { 38 | this._canDock = canDock; 39 | if (!canDock) { 40 | this._dockSide = WebInspector.DockController.State.Undocked; 41 | this._updateUI(); 42 | return; 43 | } 44 | 45 | WebInspector.settings.currentDockState = WebInspector.settings.createSetting("currentDockState", ""); 46 | WebInspector.settings.lastDockState = WebInspector.settings.createSetting("lastDockState", ""); 47 | }; 48 | 49 | WebInspector.DockController.State = { 50 | DockedToBottom: "bottom", 51 | DockedToRight: "right", 52 | DockedToLeft: "left", 53 | Undocked: "undocked" 54 | }; 55 | 56 | // Use BeforeDockSideChanged to do something before all the UI bits are updated, 57 | // DockSideChanged to update UI, and AfterDockSideChanged to perform actions 58 | // after frontend is docked/undocked in the browser. 59 | WebInspector.DockController.Events = { 60 | BeforeDockSideChanged: "BeforeDockSideChanged", 61 | DockSideChanged: "DockSideChanged", 62 | AfterDockSideChanged: "AfterDockSideChanged" 63 | }; 64 | 65 | WebInspector.DockController.prototype = { 66 | initialize: function() 67 | { 68 | if (!this._canDock) { 69 | return; 70 | } 71 | 72 | this._states = [WebInspector.DockController.State.DockedToBottom, WebInspector.DockController.State.Undocked, WebInspector.DockController.State.DockedToRight]; 73 | this._titles = [WebInspector.UIString("Dock to main window."), WebInspector.UIString("Undock into separate window."), WebInspector.UIString("Dock to main window.")]; 74 | if (WebInspector.experimentsSettings.dockToLeft.isEnabled()) { 75 | this._states.push(WebInspector.DockController.State.DockedToLeft); 76 | this._titles.push(WebInspector.UIString("Dock to main window.")); 77 | } 78 | var initialState = WebInspector.settings.currentDockState.get(); 79 | initialState = this._states.indexOf(initialState) >= 0 ? initialState : this._states[0]; 80 | this._dockSideChanged(initialState); 81 | }, 82 | 83 | /** 84 | * @return {string} 85 | */ 86 | dockSide: function() 87 | { 88 | return this._dockSide; 89 | }, 90 | 91 | /** 92 | * @return {boolean} 93 | */ 94 | canDock: function() 95 | { 96 | return this._canDock; 97 | }, 98 | 99 | /** 100 | * @return {boolean} 101 | */ 102 | isVertical: function() 103 | { 104 | return this._dockSide === WebInspector.DockController.State.DockedToRight || this._dockSide === WebInspector.DockController.State.DockedToLeft; 105 | }, 106 | 107 | /** 108 | * @param {string} dockSide 109 | */ 110 | _dockSideChanged: function(dockSide) 111 | { 112 | if (this._dockSide === dockSide) { 113 | return; 114 | } 115 | 116 | var eventData = { from: this._dockSide, to: dockSide }; 117 | this.dispatchEventToListeners(WebInspector.DockController.Events.BeforeDockSideChanged, eventData); 118 | console.timeStamp("DockController.setIsDocked"); 119 | InspectorFrontendHost.setIsDocked(dockSide !== WebInspector.DockController.State.Undocked, this._setIsDockedResponse.bind(this, eventData)); 120 | this._dockSide = dockSide; 121 | this._updateUI(); 122 | this.dispatchEventToListeners(WebInspector.DockController.Events.DockSideChanged, eventData); 123 | }, 124 | 125 | /** 126 | * @param {{from: string, to: string}} eventData 127 | */ 128 | _setIsDockedResponse: function(eventData) 129 | { 130 | this.dispatchEventToListeners(WebInspector.DockController.Events.AfterDockSideChanged, eventData); 131 | }, 132 | 133 | _updateUI: function() 134 | { 135 | var body = document.body; 136 | switch (this._dockSide) { 137 | case WebInspector.DockController.State.DockedToBottom: 138 | body.classList.remove("undocked"); 139 | body.classList.remove("dock-to-right"); 140 | body.classList.remove("dock-to-left"); 141 | body.classList.add("dock-to-bottom"); 142 | break; 143 | case WebInspector.DockController.State.DockedToRight: 144 | body.classList.remove("undocked"); 145 | body.classList.add("dock-to-right"); 146 | body.classList.remove("dock-to-left"); 147 | body.classList.remove("dock-to-bottom"); 148 | break; 149 | case WebInspector.DockController.State.DockedToLeft: 150 | body.classList.remove("undocked"); 151 | body.classList.remove("dock-to-right"); 152 | body.classList.add("dock-to-left"); 153 | body.classList.remove("dock-to-bottom"); 154 | break; 155 | case WebInspector.DockController.State.Undocked: 156 | body.classList.add("undocked"); 157 | body.classList.remove("dock-to-right"); 158 | body.classList.remove("dock-to-left"); 159 | body.classList.remove("dock-to-bottom"); 160 | break; 161 | } 162 | }, 163 | 164 | __proto__: WebInspector.Object.prototype 165 | }; 166 | 167 | /** 168 | * @constructor 169 | * @implements {WebInspector.StatusBarButton.Provider} 170 | */ 171 | WebInspector.DockController.ButtonProvider = function() 172 | { 173 | }; 174 | 175 | WebInspector.DockController.ButtonProvider.prototype = { 176 | /** 177 | * @return {?WebInspector.StatusBarButton} 178 | */ 179 | button: function() 180 | { 181 | if (!WebInspector.dockController.canDock()) { 182 | return null; 183 | } 184 | 185 | if (!this._dockToggleButton) { 186 | this._dockToggleButton = new WebInspector.StatusBarStatesSettingButton( 187 | "dock-status-bar-item", 188 | WebInspector.dockController._states, 189 | WebInspector.dockController._titles, 190 | WebInspector.dockController.dockSide(), 191 | WebInspector.settings.currentDockState, 192 | WebInspector.settings.lastDockState, 193 | WebInspector.dockController._dockSideChanged.bind(WebInspector.dockController)); 194 | } 195 | return this._dockToggleButton; 196 | } 197 | }; 198 | 199 | /** 200 | * @type {!WebInspector.DockController} 201 | */ 202 | WebInspector.dockController = WebInspector.dockController; 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Hello. 2 | Have you ever wanted to show someone something really cool in javascript, maybe 3 | an extra sexy `reduce` or a cool syntactical trick? Of course you did. You wrote 4 | your code, and it turned out fucking rad. Now how do you send this that special 5 | somebody? 6 | 7 | Sites like jsbin and codepen are cool for showing off full-blown demos. But you 8 | want to show off some juicy javascript. You probaly hacked it around in the dev 9 | tools of the browser of your choice, because they are a great environment for 10 | just playing around with javascript. 11 | 12 | But what if you could have a javascript console...in your browser? 13 | 14 | And then share that hacking around session with those you love (or hate (or 15 | feel no particular emotion for))? 16 | 17 | http://jsh.zirak.me . It could take a bit to load. 18 | 19 | 20 | ![image](https://cloud.githubusercontent.com/assets/39191/4653651/9a263098-54af-11e4-855d-561ad91eacbc.png) 21 | 22 | 23 | ## What is this? 24 | As the introduction above says, this is Chrome's dev tools javascript console in 25 | a regular web page. To see how it was done, see the [How stuff works](#how-stuff-works) 26 | section below. 27 | 28 | Write some javascript, invite your friends, write some javascript on your 29 | friends. You'll be the life of the party. 30 | 31 | It's quite alpha but usable. Things work generally pretty well on Chrome, 32 | Firefox has some jitters, IE will likely stay a mess for some time. 33 | 34 | ## Features 35 | What you'd expect of Chrome dev tools' excellent console: 36 | 37 | * JavaScript REPL 38 | * Object inspection 39 | * Completion suggestions 40 | * Nice stack traces 41 | * History 42 | * Monkeys 43 | 44 | ### Roadmap 45 | 46 | * [x] Introduction message 47 | * [x] Saving and sharing 48 | * [x] Implement ---all--- most console methods (dir, group, table, time, ...) 49 | * [x] Find functionality 50 | * [ ] Make it work across modern-ish browsers 51 | * [ ] Inspect nodes. 52 | * [ ] Stream a session (like [TogetherJS](https://togetherjs.com/)) 53 | * [ ] Themes (?) 54 | * [ ] Settings (?) 55 | * [ ] Better editing (tab button support) 56 | 57 | ## Running 58 | A bit unfortunately, this is meant to run on Google App Engine, so to run you'll 59 | need to [download the SDK for Python](https://cloud.google.com/appengine/downloads). 60 | 61 | Unzip/install/whatever, and run: 62 | 63 | ```sh 64 | $ python2.7 path/to/google_appengine/dev_appserver.py path/to/jsh 65 | ``` 66 | 67 | (if 2.7 is already your Python version you can omit that first part.) 68 | 69 | Pay a visit to localhost:8080 and have fun. 70 | 71 | ## How stuff works 72 | The best way to learn how the dev tools works is by looking at the source and 73 | playing around with it. The sources here are taken freshly squeezed from 74 | chromium/chromium@66fbff9d3e1c441db581024895a959efc32deac2 . You can find it in 75 | `third_party/WebKit/devtools/front_end`. May God have mercy on your soul. 76 | 77 | (Yes, I'm planning on writing my findings down.) 78 | 79 | Poke around, understand that `InspectorFrontendHost` and some other objects are 80 | actually native bindings, be angry that that's the case. Raaaargh!!!! 81 | 82 | You can actually inspect the dev-tools using the dev-tools. Hit Ctrl-Shift-J and 83 | undock them if necessary. Then, simply hit Ctrl-Shift-J again. Have fun. 84 | 85 | To make things even more fun, in your inceptioned dev tools, run: 86 | 87 | ```javascript 88 | InspectorBackendClass.Options.dumpInspectorProtocolMessages = true; 89 | ``` 90 | 91 | And proceed to do things. The bazingaload of messages you'll see are part of the 92 | [protocol](https://developer.chrome.com/devtools/docs/protocol/1.1/index). 93 | 94 | I wrote this clone by first including what seemed like obvious core console 95 | functionality (that is, everything in the console directory), followed by 96 | main/Main.js (because hey, it's main), and then started to slowly (ssllloooowwwllllyyyy) 97 | fix the `ReferenceError`s and `TypeError`s. 98 | 99 | This means that while at its base the console "works", there are a lot of hidden 100 | booby-traps and unimplemented functionality. At the same time, there is a lot of 101 | code which just isn't useful (why do we need a VBox and the SplitView and the 102 | tabs pane and so forth?). The way forward is implementing more and more console 103 | related functionality while trimming unnecessary portions. 104 | 105 | All messages to the native backend funnel down to `jsh.handleMessage`. 106 | 107 | ### Structure 108 | Frontend is in the `public/` directory; `/templates` and the rest are Google 109 | App Engine boilerplate. 110 | 111 | The directory structure is also of course copied directly from the dev tools' 112 | source. I don't quite get it either. It's a mess. 113 | 114 | If you want a starting point to hack things around, the index file is a bad 115 | place to start. It's just a blob of scripts. 116 | 117 | `jsh.js` is one place, as it contains the code for dealing with 118 | messages ("evaluate this expression", "give me the properties of this object"). 119 | 120 | `InjectedScript.js` contains most if not all of the interesting logic. 121 | 122 | `main/Main.js` is where most things happen: Settings up the UI, settings up the 123 | different objects the UI uses. It's mostly baloney, though. 124 | 125 | `sdk/ConsoleModel.js` is where the runtime/console API gets tied up to the UI, 126 | and the good friends in the `console/` folder are where all the console UI logic 127 | is done. 128 | 129 | It's a lot of being confusing and `grep`ing for things. 130 | 131 | ### What happens when I press enter? 132 | 133 | That's a very good question. It all starts from `public/js/concole/ConsoleView.js`, 134 | in a nice little function called `_promptKeyDown`. Oh look, `_enterKeyPressed`, 135 | that looks simple! 136 | 137 | And then it's HELL. Let's go through a partial trace (which is of course broken 138 | at times because async, and partial because I'm not doing a full call graph): 139 | 140 | 1. `ConsoleView.._promptKeyDown` 141 | 2. `ConsoleView.._enterKeyPressed` 142 | 3. `ConsoleView.._appendCommand` 143 | 4. `ConsoleModel.evaluateCommandInConsole` 144 | 5. `ExecutionContext..evaluate` 145 | 146 | (lots of redirections between this and next) 147 | 6. `InspectorBackendClass.AgentPrototype..registerCommand#sendMessage` 148 | 7. `InspectorBackendClass.AgentPrototype.._sendMessageToBackend` 149 | 8. `InspectorBackendClass.MainConnection.._wrapCallbackAndSendMessageObject` 150 | 9. `InspectorBackendClass.StubConnection..sendMessage` 151 | 152 | This is where we come in. We're a `StubConnection` because our backend isn't 153 | the real native backend Chrome has. But we'll show it! Next part will 154 | change as jsh changes. 155 | 10. `jsh.handleMessage` 156 | 11. `jsh#sendMessageToEvalFrame` 157 | 158 | And from now on we've passed control to the eval frame. Until further notice 159 | all functions are one there. 160 | 12. `messageListener` 161 | 13. `actions.bridge` 162 | 14. `bridge.evaluate` 163 | 15. `eval` (finally!) 164 | 16. `sendToParent` 165 | 166 | Aaannndd back to the jsh frame. 167 | 17. `messageListener` 168 | 169 | Aaannndd this is where jsh leaves off. 170 | 18. `InspectorBackendClass.MainConnection..dispatch` 171 | 172 | ...*lots* of indirections... 173 | 19. `ExecutionContext..evaluate#evalCallback` 174 | 20. `ConsoleModel.evaluateCommandInConsole#printResult` 175 | 21. `ConsoleView.._commandEvaluated` 176 | 22. `ConsoleView.._printResult` 177 | 178 | ...and it just goes boring UI from here. Real nice, right? 179 | 180 | ### wat 181 | 182 | To give you a hand, here are some useful variables to have. 183 | 184 | * You're going to see `target` this and `target` that a lot. You can get it like 185 | this: `WebInspector.targetManager.targets()[0]` 186 | * Same goes for `connection` or `_connection`: `InspectorBackend.connection()` 187 | * ConsoleView: `WebInspector.ConsolePanel._view()` 188 | 189 | And it's late now so I can't think of anything else. 190 | 191 | ## License 192 | 99% of the files here have a giant license block at the top. Basically, it's 193 | directly from the dev tools source. This goes for any images in `css/Images`. 194 | 195 | Favicon is from Github's [Octicon](https://octicons.github.com/) set. Expect a 196 | new one. 197 | 198 | Any code *I* write is under the [WTFPL](http://www.wtfpl.net/). 199 | -------------------------------------------------------------------------------- /public/js/common/TextRange.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Google Inc. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | /** 32 | * @constructor 33 | * @param {number} startLine 34 | * @param {number} startColumn 35 | * @param {number} endLine 36 | * @param {number} endColumn 37 | */ 38 | WebInspector.TextRange = function(startLine, startColumn, endLine, endColumn) 39 | { 40 | this.startLine = startLine; 41 | this.startColumn = startColumn; 42 | this.endLine = endLine; 43 | this.endColumn = endColumn; 44 | }; 45 | 46 | /** 47 | * @param {number} line 48 | * @param {number} column 49 | * @return {!WebInspector.TextRange} 50 | */ 51 | WebInspector.TextRange.createFromLocation = function(line, column) 52 | { 53 | return new WebInspector.TextRange(line, column, line, column); 54 | }; 55 | 56 | /** 57 | * @param {!Object} serializedTextRange 58 | * @return {!WebInspector.TextRange} 59 | */ 60 | WebInspector.TextRange.fromObject = function(serializedTextRange) 61 | { 62 | return new WebInspector.TextRange(serializedTextRange.startLine, serializedTextRange.startColumn, serializedTextRange.endLine, serializedTextRange.endColumn); 63 | }; 64 | 65 | /** 66 | * @param {!WebInspector.TextRange} range1 67 | * @param {!WebInspector.TextRange} range2 68 | * @return {number} 69 | */ 70 | WebInspector.TextRange.comparator = function(range1, range2) 71 | { 72 | return range1.compareTo(range2); 73 | }; 74 | 75 | WebInspector.TextRange.prototype = { 76 | /** 77 | * @return {boolean} 78 | */ 79 | isEmpty: function() 80 | { 81 | return this.startLine === this.endLine && this.startColumn === this.endColumn; 82 | }, 83 | 84 | /** 85 | * @param {!WebInspector.TextRange} range 86 | * @return {boolean} 87 | */ 88 | immediatelyPrecedes: function(range) 89 | { 90 | if (!range) { 91 | return false; 92 | } 93 | return this.endLine === range.startLine && this.endColumn === range.startColumn; 94 | }, 95 | 96 | /** 97 | * @param {!WebInspector.TextRange} range 98 | * @return {boolean} 99 | */ 100 | immediatelyFollows: function(range) 101 | { 102 | if (!range) { 103 | return false; 104 | } 105 | return range.immediatelyPrecedes(this); 106 | }, 107 | 108 | /** 109 | * @param {!WebInspector.TextRange} range 110 | * @return {boolean} 111 | */ 112 | follows: function(range) 113 | { 114 | return ( 115 | range.endLine === this.startLine && 116 | range.endColumn <= this.startColumn 117 | ) || range.endLine < this.startLine; 118 | }, 119 | 120 | /** 121 | * @return {number} 122 | */ 123 | get linesCount() 124 | { 125 | return this.endLine - this.startLine; 126 | }, 127 | 128 | /** 129 | * @return {!WebInspector.TextRange} 130 | */ 131 | collapseToEnd: function() 132 | { 133 | return new WebInspector.TextRange(this.endLine, this.endColumn, this.endLine, this.endColumn); 134 | }, 135 | 136 | /** 137 | * @return {!WebInspector.TextRange} 138 | */ 139 | collapseToStart: function() 140 | { 141 | return new WebInspector.TextRange(this.startLine, this.startColumn, this.startLine, this.startColumn); 142 | }, 143 | 144 | /** 145 | * @return {!WebInspector.TextRange} 146 | */ 147 | normalize: function() 148 | { 149 | if (this.startLine > this.endLine || (this.startLine === this.endLine && this.startColumn > this.endColumn)) { 150 | return new WebInspector.TextRange(this.endLine, this.endColumn, this.startLine, this.startColumn); 151 | } 152 | else { 153 | return this.clone(); 154 | } 155 | }, 156 | 157 | /** 158 | * @return {!WebInspector.TextRange} 159 | */ 160 | clone: function() 161 | { 162 | return new WebInspector.TextRange(this.startLine, this.startColumn, this.endLine, this.endColumn); 163 | }, 164 | 165 | /** 166 | * @return {!Object} 167 | */ 168 | serializeToObject: function() 169 | { 170 | var serializedTextRange = {}; 171 | serializedTextRange.startLine = this.startLine; 172 | serializedTextRange.startColumn = this.startColumn; 173 | serializedTextRange.endLine = this.endLine; 174 | serializedTextRange.endColumn = this.endColumn; 175 | return serializedTextRange; 176 | }, 177 | 178 | /** 179 | * @param {!WebInspector.TextRange} other 180 | * @return {number} 181 | */ 182 | compareTo: function(other) 183 | { 184 | if (this.startLine > other.startLine) { 185 | return 1; 186 | } 187 | if (this.startLine < other.startLine) { 188 | return -1; 189 | } 190 | if (this.startColumn > other.startColumn) { 191 | return 1; 192 | } 193 | if (this.startColumn < other.startColumn) { 194 | return -1; 195 | } 196 | return 0; 197 | }, 198 | 199 | /** 200 | * @param {!WebInspector.TextRange} other 201 | * @return {boolean} 202 | */ 203 | equal: function(other) 204 | { 205 | return this.startLine === other.startLine && this.endLine === other.endLine && 206 | this.startColumn === other.startColumn && this.endColumn === other.endColumn; 207 | }, 208 | 209 | /** 210 | * @param {number} lineOffset 211 | * @return {!WebInspector.TextRange} 212 | */ 213 | shift: function(lineOffset) 214 | { 215 | return new WebInspector.TextRange(this.startLine + lineOffset, this.startColumn, this.endLine + lineOffset, this.endColumn); 216 | }, 217 | 218 | /** 219 | * @param {!WebInspector.TextRange} originalRange 220 | * @param {!WebInspector.TextRange} editedRange 221 | * @return {!WebInspector.TextRange} 222 | */ 223 | rebaseAfterTextEdit: function(originalRange, editedRange) 224 | { 225 | console.assert(originalRange.startLine === editedRange.startLine); 226 | console.assert(originalRange.startColumn === editedRange.startColumn); 227 | var rebase = this.clone(); 228 | if (!this.follows(originalRange)) { 229 | return rebase; 230 | } 231 | var lineDelta = editedRange.endLine - originalRange.endLine; 232 | var columnDelta = editedRange.endColumn - originalRange.endColumn; 233 | rebase.startLine += lineDelta; 234 | rebase.endLine += lineDelta; 235 | if (rebase.startLine === editedRange.endLine) { 236 | rebase.startColumn += columnDelta; 237 | } 238 | if (rebase.endLine === editedRange.endLine) { 239 | rebase.endColumn += columnDelta; 240 | } 241 | return rebase; 242 | }, 243 | 244 | /** 245 | * @return {string} 246 | */ 247 | toString: function() 248 | { 249 | return JSON.stringify(this); 250 | } 251 | }; 252 | 253 | /** 254 | * @constructor 255 | * @param {number} offset 256 | * @param {number} length 257 | */ 258 | WebInspector.SourceRange = function(offset, length) 259 | { 260 | this.offset = offset; 261 | this.length = length; 262 | }; 263 | -------------------------------------------------------------------------------- /public/js/jsh-console.js: -------------------------------------------------------------------------------- 1 | var createConsole = function (bridge, realConsole, sendMessage) { 2 | /* 3 | TODO: 4 | * count 5 | */ 6 | 7 | var console = {}, 8 | times = {}; 9 | 10 | ['log', 'info', 'error'].forEach(function (level) { 11 | console[level] = function () { 12 | var message = { 13 | level : level, 14 | type : 'log', 15 | 16 | parameters : wrapObjects(arguments), 17 | text : [].join.call(arguments, ' ') 18 | }; 19 | 20 | sendConsoleMessage(message); 21 | }; 22 | }); 23 | 24 | console.debug = console.log; 25 | 26 | console.warn = function () { 27 | var message = { 28 | level : 'warning', 29 | type : 'log', 30 | 31 | parameters : wrapObjects(arguments), 32 | text : [].join.call(arguments, ' ') 33 | }; 34 | 35 | sendConsoleMessage(message); 36 | }; 37 | 38 | console.assert = function (condition) { 39 | if (condition) { 40 | return; 41 | } 42 | 43 | // TODO add Array.from to utilities.js 44 | var args = [].slice.call(arguments, 1); 45 | 46 | var message = { 47 | level : 'error', 48 | type : 'assert' 49 | }; 50 | 51 | if (args.length) { 52 | message.parameters = wrapObjects(args); 53 | message.text = String(args[0]); 54 | } 55 | 56 | sendConsoleMessage(message); 57 | }; 58 | 59 | console.dir = function dir () { 60 | if (!arguments.length) { 61 | return; 62 | } 63 | 64 | var message = { 65 | level : 'log', 66 | type : 'dir', 67 | 68 | parameters : wrapObjects(arguments), 69 | text : String(arguments[0]) 70 | }; 71 | 72 | sendConsoleMessage(message); 73 | }; 74 | 75 | console.table = function table (parameter) { 76 | var message = { 77 | level : 'log', 78 | type : 'table', 79 | parameters : [bridge.wrapObject({ 80 | object : parameter, 81 | columnNames : null, 82 | isTable : true 83 | })], 84 | text : String([].map.call(arguments, String)) 85 | }; 86 | 87 | sendConsoleMessage(message); 88 | }; 89 | 90 | // FIXME: there's a weird bug where console.trace calls are "taller" than other 91 | //lines. 92 | console.trace = function trace () { 93 | var message = { 94 | level : 'log', 95 | type : 'trace', 96 | text : '' 97 | }; 98 | 99 | if (arguments.length) { 100 | message.parameters = wrapObjects(arguments); 101 | message.text = String(arguments[0]); 102 | } 103 | 104 | sendConsoleMessage(message); 105 | }; 106 | 107 | // grouping 108 | 109 | console.group = function group () { 110 | var message = { 111 | level : 'log', 112 | type : 'startGroup', 113 | text : '' 114 | }; 115 | 116 | if (arguments.length) { 117 | message.parameters = wrapObjects(arguments); 118 | } 119 | 120 | sendConsoleMessage(message); 121 | }; 122 | 123 | console.groupEnd = function groupEnd () { 124 | sendConsoleMessage({ 125 | level : 'log', 126 | type : 'endGroup', 127 | text : '' 128 | }); 129 | }; 130 | 131 | console.groupCollapsed = function groupCollapsed () { 132 | var message = { 133 | level : 'log', 134 | type : 'startGroupCollapsed', 135 | text : '' 136 | }; 137 | 138 | if (arguments.length) { 139 | message.parameters = wrapObjects(arguments); 140 | } 141 | 142 | sendConsoleMessage(message); 143 | }; 144 | 145 | // TODO: what should this do? clear history? the user needs to be alerted if so. 146 | // do we even want this? 147 | console.clear = function clear () { 148 | sendMessage({ 149 | method : 'Console.messagesCleared' 150 | }); 151 | 152 | sendConsoleMessage({ 153 | level : 'log', 154 | type : 'clear', 155 | text : '' 156 | }); 157 | }; 158 | 159 | // timing 160 | 161 | console.time = function (name) { 162 | // Just following Firebug's convention... 163 | if (!name) { 164 | return; 165 | } 166 | 167 | times[name] = performance.now(); 168 | }; 169 | console.timeEnd = function (name) { 170 | var end = performance.now(); 171 | 172 | if (!name) { 173 | return; 174 | } 175 | 176 | var begin = times[name]; 177 | if (!begin) { 178 | return; 179 | } 180 | 181 | delete times[name]; 182 | 183 | var elapsed = end - begin, 184 | elapsedString = elapsed.toFixed(3) + 'ms'; 185 | 186 | // TODO should output be smarter? Instead of just showing ms, also show 187 | //seconds for larger inputs (e.g. 1355ms => 1s 355ms or 1.355s) 188 | 189 | sendConsoleMessage({ 190 | level : 'debug', 191 | type : 'log', 192 | text : name + ': ' + elapsedString 193 | }); 194 | }; 195 | 196 | // utility crap from here 197 | 198 | function sendConsoleMessage (consoleMessage) { 199 | if (!consoleMessage.source) { 200 | consoleMessage.source = 'console-api'; 201 | } 202 | 203 | if (!consoleMessage.stackTrace) { 204 | var stackTrace = parseLogStackTrace((new Error()).stack); 205 | consoleMessage.line = stackTrace[0].lineNumber; 206 | consoleMessage.column = stackTrace[0].columnNumber; 207 | consoleMessage.stackTrace = stackTrace; 208 | } 209 | 210 | consoleMessage.timestamp = Date.now() / 1000; 211 | 212 | 213 | var message = { 214 | method : 'Console.messageAdded', 215 | params : { message : consoleMessage } 216 | }; 217 | 218 | sendMessage(message); 219 | } 220 | 221 | function wrapObjects (objs) { 222 | return [].map.call(objs, bridge.wrapObject, bridge); 223 | } 224 | 225 | // Takes a strung stack trace (err.stack) and makes sense out of it: Extracts 226 | //function names, line and column numbers. 227 | function parseLogStackTrace (stack) { 228 | /* 229 | The stack trace in Chrome looks something like: 230 | Error 231 | at Object.jsh.sendConsoleMessage (http://localhost:8080/js/jsh-console.js:158:22) 232 | at Object.jsh.console.(anonymous function) (http://localhost:8080/js/jsh-console.js:25:24) 233 | at eval (eval at (http://localhost:8080/js/jsh.js:263:40), :1:9) 234 | at eval (native) 235 | at Object.jsh.eval (http://localhost:8080/js/jsh.js:263:40) 236 | at Object.jsh.bridge.evaluate (http://localhost:8080/js/jsh.js:126:22) 237 | at Object.jsh.handleMessage (http://localhost:8080/js/jsh.js:73:32) 238 | at Object.actualSendMessage (http://localhost:8080/js/sdk/InspectorBackend.js:698:27) 239 | 240 | We want to do away with the first and last three lines. 241 | 242 | OTOH, in Firefox, they look like this: 243 | jsh.sendConsoleMessage@http://localhost:8080/js/jsh-console.js:158:9 244 | jsh.console[level]@http://localhost:8080/js/jsh-console.js:25:9 245 | @http://localhost:8080/js/jsh.js line 263 > eval:1:1 246 | jsh.eval@http://localhost:8080/js/jsh.js:263:5 247 | jsh.bridge.evaluate@http://localhost:8080/js/jsh.js:126:9 248 | jsh.handleMessage@http://localhost:8080/js/jsh.js:73:9 249 | actualSendMessage@http://localhost:8080/js/sdk/InspectorBackend.js:698:17 250 | 251 | Where we want to do away with the firt two and last three. 252 | */ 253 | var stackLines = stack.split('\n'), 254 | parser; 255 | 256 | if (stackLines[0] === 'Error') { 257 | stackLines.shift(); 258 | parser = chromeParseLine; 259 | } 260 | else { 261 | parser = firefoxParseLine; 262 | } 263 | 264 | return stackLines.filter(Boolean).map(parser); 265 | 266 | function chromeParseLine (line) { 267 | realConsole.log(line); 268 | // at obj.funcName (crap) 269 | // at obj.funcName.(anonymous function) (crap) 270 | var funcMatch = /^\s*at ([^\(]*(?:\(anonymous function\))?)/.exec(line) || ['', '']; 271 | 272 | // (crap:line:column) 273 | var positionMatch = /:(\d+):(\d+)\)\s*$/.exec(line) || ['', '', '']; 274 | 275 | return { 276 | functionName : funcMatch[1], 277 | lineNumber : Number(positionMatch[1]), 278 | columnNumber : Number(positionMatch[2]), 279 | 280 | // protocol stuff. 281 | scriptId : 0, 282 | url : '' 283 | }; 284 | } 285 | function firefoxParseLine (line) { 286 | realConsole.log(line); 287 | // jsh.handleMessage@http://localhost:8080/js/jsh.js:123231273:12312319 288 | // ^---------------^ ^-----------------------------^ ^-------^ ^------^ 289 | // (.+) @ (.+) : (\d+) : (\d+) 290 | var match = (/^(.+)?@(.+):(\d+):(\d+)$/).exec(line); 291 | var func, file, col; 292 | 293 | if (match) { 294 | func = match[1]; 295 | file = match[2]; 296 | line = match[3]; 297 | col = match[4]; 298 | } 299 | else { 300 | // @http://localhost:8080/js/jsh.js line 263 > eval:1:1 301 | match = (/^@(.+) line (\d+) > eval:\d+:\d+$/).exec(line); 302 | 303 | if (!match) { 304 | realConsole.error('wat', line); 305 | } 306 | 307 | func = 'eval'; 308 | file = match[1]; 309 | line = match[2]; 310 | col = 0; // we receive no indication 311 | } 312 | 313 | return { 314 | functionName : func, 315 | lineNumber : file, 316 | columnNumber : col, 317 | 318 | scriptId : 0, 319 | url : '' 320 | }; 321 | } 322 | } 323 | 324 | return console; 325 | }; 326 | -------------------------------------------------------------------------------- /public/js/common/modules.js: -------------------------------------------------------------------------------- 1 | var allDescriptors = [ 2 | { 3 | "name" : "main", 4 | "extensions": [ 5 | { 6 | "type": "@WebInspector.ContextMenu.Provider", 7 | "contextTypes": ["WebInspector.UISourceCode", "WebInspector.Resource", "WebInspector.NetworkRequest", "Node"], 8 | "className": "WebInspector.HandlerRegistry.ContextMenuProvider" 9 | }, 10 | { 11 | "type": "@WebInspector.ActionDelegate", 12 | "actionId": "main.reload", 13 | "className": "WebInspector.Main.ReloadActionDelegate", 14 | "bindings": [ 15 | { 16 | "platform": "windows,linux", 17 | "shortcut": "F5 Ctrl+R" 18 | }, 19 | { 20 | "platform": "mac", 21 | "shortcut": "Meta+R" 22 | } 23 | ] 24 | }, 25 | { 26 | "type": "@WebInspector.ActionDelegate", 27 | "actionId": "main.hard-reload", 28 | "className": "WebInspector.Main.HardReloadActionDelegate", 29 | "bindings": [ 30 | { 31 | "platform": "windows,linux", 32 | "shortcut": "Shift+F5 Ctrl+F5 Ctrl+Shift+F5 Shift+Ctrl+R" 33 | }, 34 | { 35 | "platform": "mac", 36 | "shortcut": "Shift+Meta+R" 37 | } 38 | ] 39 | }, 40 | { 41 | "type": "@WebInspector.ActionDelegate", 42 | "actionId": "main.toggle-drawer", 43 | "className": "WebInspector.InspectorView.DrawerToggleActionDelegate", 44 | "bindings": [ 45 | { 46 | "shortcut": "Esc" 47 | } 48 | ] 49 | }, 50 | { 51 | "type": "@WebInspector.ActionDelegate", 52 | "actionId": "main.debug-reload", 53 | "className": "WebInspector.Main.DebugReloadActionDelegate", 54 | "bindings": [ 55 | { 56 | "shortcut": "Alt+R" 57 | } 58 | ] 59 | }, 60 | /* 61 | { 62 | "type": "@WebInspector.ActionDelegate", 63 | "actionId": "main.toggle-element-search", 64 | "className": "WebInspector.InspectElementModeController.ToggleSearchActionDelegate", 65 | "bindings": [ 66 | { 67 | "platform": "windows,linux", 68 | "shortcut": "Ctrl+Shift+C" 69 | }, 70 | { 71 | "platform": "mac", 72 | "shortcut": "Meta+Shift+C" 73 | } 74 | ] 75 | }, 76 | */ 77 | { 78 | "type": "@WebInspector.ActionDelegate", 79 | "actionId": "main.zoom-in", 80 | "className": "WebInspector.Main.ZoomInActionDelegate", 81 | "bindings": [ 82 | { 83 | "platform": "windows,linux", 84 | "shortcut": "Ctrl+Plus Ctrl+Shift+Plus Ctrl+NumpadPlus Ctrl+Shift+NumpadPlus" 85 | }, 86 | { 87 | "platform": "mac", 88 | "shortcut": "Meta+Plus Meta+Shift+Plus Meta+NumpadPlus Meta+Shift+NumpadPlus" 89 | } 90 | ] 91 | }, 92 | { 93 | "type": "@WebInspector.ActionDelegate", 94 | "actionId": "main.zoom-out", 95 | "className": "WebInspector.Main.ZoomOutActionDelegate", 96 | "bindings": [ 97 | { 98 | "platform": "windows,linux", 99 | "shortcut": "Ctrl+Minus Ctrl+Shift+Minus Ctrl+NumpadMinus Ctrl+Shift+NumpadMinus" 100 | }, 101 | { 102 | "platform": "mac", 103 | "shortcut": "Meta+Minus Meta+Shift+Minus Meta+NumpadMinus Meta+Shift+NumpadMinus" 104 | } 105 | ] 106 | }, 107 | { 108 | "type": "@WebInspector.ActionDelegate", 109 | "actionId": "main.zoom-reset", 110 | "className": "WebInspector.Main.ZoomResetActionDelegate", 111 | "bindings": [ 112 | { 113 | "platform": "windows,linux", 114 | "shortcut": "Ctrl+0 Ctrl+Numpad0" 115 | }, 116 | { 117 | "platform": "mac", 118 | "shortcut": "Meta+0 Meta+Numpad0" 119 | } 120 | ] 121 | }, 122 | /* 123 | { 124 | "type": "drawer-view", 125 | "name": "emulation", 126 | "title": "Emulation", 127 | "order": "10", 128 | "className": "WebInspector.OverridesView" 129 | }, 130 | { 131 | "type": "drawer-view", 132 | "name": "rendering", 133 | "title": "Rendering", 134 | "order": "11", 135 | "className": "WebInspector.RenderingOptions.View" 136 | }, 137 | */ 138 | { 139 | "type": "@WebInspector.Revealer", 140 | "contextTypes": ["WebInspector.OverridesSupport"], 141 | "className": "WebInspector.OverridesView.Revealer" 142 | }, 143 | /* 144 | { 145 | "type": "@WebInspector.StatusBarButton.Provider", 146 | "className": "WebInspector.InspectElementModeController.ToggleButtonProvider", 147 | "location": "toolbar-left", 148 | "order": 0, 149 | "actionId": "main.toggle-element-search" 150 | }, 151 | 152 | { 153 | "type": "@WebInspector.StatusBarButton.Provider", 154 | "className": "WebInspector.App.EmulationButtonProvider", 155 | "order": 1, 156 | "location": "toolbar-left" 157 | }, 158 | { 159 | "type": "@WebInspector.StatusBarButton.Provider", 160 | "className": "WebInspector.DockController.ButtonProvider", 161 | "order": 1, 162 | "location": "toolbar-right" 163 | }, 164 | */ 165 | /* 166 | { 167 | "type": "@WebInspector.StatusBarButton.Provider", 168 | "className": "WebInspector.ScreencastApp.StatusBarButtonProvider", 169 | "order": 2, 170 | "location": "toolbar-right" 171 | }, 172 | { 173 | "type": "ui-setting", 174 | "title": "Disable cache (while DevTools is open)", 175 | "settingName": "cacheDisabled", 176 | "settingType": "checkbox" 177 | }, 178 | { 179 | "type": "ui-setting", 180 | "section": "Appearance", 181 | "title": "Split panels vertically when docked to right", 182 | "settingName": "splitVerticallyWhenDockedToRight", 183 | "settingType": "checkbox" 184 | }, 185 | { 186 | "type": "ui-setting", 187 | "section": "Appearance", 188 | "settingType": "custom", 189 | "className": "WebInspector.Main.ShortcutPanelSwitchSettingDelegate" 190 | }, 191 | { 192 | "type": "ui-setting", 193 | "section": "Appearance", 194 | "title": "Don't show emulation warnings", 195 | "settingName": "disableOverridesWarning", 196 | "settingType": "checkbox" 197 | }, 198 | { 199 | "type": "ui-setting", 200 | "section": "Extensions", 201 | "settingType": "custom", 202 | "className": "WebInspector.HandlerRegistry.OpenAnchorLocationSettingDelegate" 203 | } 204 | */ 205 | ] 206 | }, 207 | { 208 | "name" : "console", 209 | "extensions": [ 210 | { 211 | "type": "@WebInspector.Panel", 212 | "name": "console", 213 | "title": "Console", 214 | "order": 20, 215 | "className": "WebInspector.ConsolePanel" 216 | }, 217 | { 218 | "type": "drawer-view", 219 | "name": "console", 220 | "title": "Console", 221 | "order": "0", 222 | "className": "WebInspector.ConsolePanel.WrapperView" 223 | }, 224 | { 225 | "type": "@WebInspector.Revealer", 226 | "contextTypes": ["WebInspector.Console"], 227 | "className": "WebInspector.ConsolePanel.ConsoleRevealer" 228 | }, 229 | { 230 | "type": "@WebInspector.ActionDelegate", 231 | "actionId": "console.show", 232 | "className": "WebInspector.ConsoleView.ShowConsoleActionDelegate", 233 | "bindings": [ 234 | { 235 | "shortcut": "Ctrl+`" 236 | } 237 | ] 238 | }, 239 | { 240 | "type": "ui-setting", 241 | "section": "Console", 242 | "title": "Log XMLHttpRequests", 243 | "settingName": "monitoringXHREnabled", 244 | "settingType": "checkbox" 245 | }, 246 | { 247 | "type": "ui-setting", 248 | "section": "Console", 249 | "title": "Preserve log upon navigation", 250 | "settingName": "preserveConsoleLog", 251 | "settingType": "checkbox" 252 | }, 253 | { 254 | "type": "ui-setting", 255 | "section": "Console", 256 | "title": "Show timestamps", 257 | "settingName": "consoleTimestampsEnabled", 258 | "settingType": "checkbox" 259 | } 260 | ], 261 | //"scripts": [ "ConsolePanel.js" ] 262 | } 263 | 264 | ]; 265 | -------------------------------------------------------------------------------- /public/js/sdk/ResourceUtils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. 3 | * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com). 4 | * Copyright (C) 2009 Joseph Pecoraro 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 16 | * its contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | /** 32 | * @param {string} url 33 | * @return {?WebInspector.Resource} 34 | */ 35 | WebInspector.resourceForURL = function(url) 36 | { 37 | // zirak 38 | /*jshint -W027*/ 39 | return null; 40 | return WebInspector.resourceTreeModel.resourceForURL(url); 41 | }; 42 | 43 | /** 44 | * @param {function(!WebInspector.Resource)} callback 45 | */ 46 | WebInspector.forAllResources = function(callback) 47 | { 48 | WebInspector.resourceTreeModel.forAllResources(callback); 49 | }; 50 | 51 | /** 52 | * @param {string} url 53 | * @return {string} 54 | */ 55 | WebInspector.displayNameForURL = function(url) 56 | { 57 | if (!url) { 58 | return ""; 59 | } 60 | 61 | var resource = WebInspector.resourceForURL(url); 62 | if (resource) { 63 | return resource.displayName; 64 | } 65 | 66 | var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(url); 67 | if (uiSourceCode) { 68 | return uiSourceCode.displayName(); 69 | } 70 | 71 | if (!WebInspector.resourceTreeModel.inspectedPageURL()) { 72 | return url.trimURL(""); 73 | } 74 | 75 | var parsedURL = WebInspector.resourceTreeModel.inspectedPageURL().asParsedURL(); 76 | var lastPathComponent = parsedURL ? parsedURL.lastPathComponent : parsedURL; 77 | var index = WebInspector.resourceTreeModel.inspectedPageURL().indexOf(lastPathComponent); 78 | if (index !== -1 && index + lastPathComponent.length === WebInspector.resourceTreeModel.inspectedPageURL().length) { 79 | var baseURL = WebInspector.resourceTreeModel.inspectedPageURL().substring(0, index); 80 | if (url.startsWith(baseURL)) { 81 | return url.substring(index); 82 | } 83 | } 84 | 85 | if (!parsedURL) { 86 | return url; 87 | } 88 | 89 | var displayName = url.trimURL(parsedURL.host); 90 | return displayName === "/" ? parsedURL.host + "/" : displayName; 91 | }; 92 | 93 | /** 94 | * @param {string} string 95 | * @param {function(string,string,number=,number=):!Node} linkifier 96 | * @return {!DocumentFragment} 97 | */ 98 | WebInspector.linkifyStringAsFragmentWithCustomLinkifier = function(string, linkifier) 99 | { 100 | var container = document.createDocumentFragment(); 101 | var linkStringRegEx = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/|data:|www\.)[\w$\-_+*'=\|\/\\(){}[\]^%@&#~,:;.!?]{2,}[\w$\-_+*=\|\/\\({^%@&#~]/; 102 | var lineColumnRegEx = /:(\d+)(:(\d+))?$/; 103 | 104 | while (string) { 105 | var linkString = linkStringRegEx.exec(string); 106 | if (!linkString) { 107 | break; 108 | } 109 | 110 | linkString = linkString[0]; 111 | var linkIndex = string.indexOf(linkString); 112 | var nonLink = string.substring(0, linkIndex); 113 | container.appendChild(document.createTextNode(nonLink)); 114 | 115 | var title = linkString; 116 | var realURL = (linkString.startsWith("www.") ? "http://" + linkString : linkString); 117 | var lineColumnMatch = lineColumnRegEx.exec(realURL); 118 | var lineNumber; 119 | var columnNumber; 120 | if (lineColumnMatch) { 121 | realURL = realURL.substring(0, realURL.length - lineColumnMatch[0].length); 122 | lineNumber = parseInt(lineColumnMatch[1], 10); 123 | // Immediately convert line and column to 0-based numbers. 124 | lineNumber = isNaN(lineNumber) ? undefined : lineNumber - 1; 125 | if (typeof(lineColumnMatch[3]) === "string") { 126 | columnNumber = parseInt(lineColumnMatch[3], 10); 127 | columnNumber = isNaN(columnNumber) ? undefined : columnNumber - 1; 128 | } 129 | } 130 | 131 | var linkNode = linkifier(title, realURL, lineNumber, columnNumber); 132 | container.appendChild(linkNode); 133 | string = string.substring(linkIndex + linkString.length, string.length); 134 | } 135 | 136 | if (string) { 137 | container.appendChild(document.createTextNode(string)); 138 | } 139 | 140 | return container; 141 | }; 142 | 143 | /** 144 | * @param {string} string 145 | * @return {!DocumentFragment} 146 | */ 147 | WebInspector.linkifyStringAsFragment = function(string) 148 | { 149 | /** 150 | * @param {string} title 151 | * @param {string} url 152 | * @param {number=} lineNumber 153 | * @param {number=} columnNumber 154 | * @return {!Node} 155 | */ 156 | function linkifier(title, url, lineNumber, columnNumber) 157 | { 158 | //var isExternal = !WebInspector.resourceForURL(url) && !WebInspector.workspace.uiSourceCodeForURL(url); 159 | var isExternal = false; 160 | var urlNode = WebInspector.linkifyURLAsNode(url, title, undefined, isExternal); 161 | if (typeof lineNumber !== "undefined") { 162 | urlNode.lineNumber = lineNumber; 163 | if (typeof columnNumber !== "undefined") { 164 | urlNode.columnNumber = columnNumber; 165 | } 166 | } 167 | 168 | return urlNode; 169 | } 170 | 171 | return WebInspector.linkifyStringAsFragmentWithCustomLinkifier(string, linkifier); 172 | }; 173 | 174 | /** 175 | * @param {string} url 176 | * @param {string=} linkText 177 | * @param {string=} classes 178 | * @param {boolean=} isExternal 179 | * @param {string=} tooltipText 180 | * @return {!Element} 181 | */ 182 | WebInspector.linkifyURLAsNode = function(url, linkText, classes, isExternal, tooltipText) 183 | { 184 | if (!linkText) { 185 | linkText = url; 186 | } 187 | classes = (classes ? classes + " " : ""); 188 | classes += isExternal ? "webkit-html-external-link" : "webkit-html-resource-link"; 189 | 190 | var a = document.createElement("a"); 191 | var href = sanitizeHref(url); 192 | if (href !== null) { 193 | a.href = href; 194 | } 195 | a.className = classes; 196 | if (typeof tooltipText === "undefined") { 197 | a.title = url; 198 | } 199 | else if (typeof tooltipText !== "string" || tooltipText.length) { 200 | a.title = tooltipText; 201 | } 202 | a.textContent = linkText.trimMiddle(WebInspector.Linkifier.MaxLengthForDisplayedURLs); 203 | if (isExternal) { 204 | a.setAttribute("target", "_blank"); 205 | } 206 | 207 | return a; 208 | }; 209 | 210 | /** 211 | * @param {string} url 212 | * @param {number=} lineNumber 213 | * @return {string} 214 | */ 215 | WebInspector.formatLinkText = function(url, lineNumber) 216 | { 217 | var text = url ? WebInspector.displayNameForURL(url) : WebInspector.UIString("(program)"); 218 | if (typeof lineNumber === "number") { 219 | text += ":" + (lineNumber + 1); 220 | } 221 | return text; 222 | }; 223 | 224 | /** 225 | * @param {string} url 226 | * @param {number=} lineNumber 227 | * @param {string=} classes 228 | * @param {string=} tooltipText 229 | * @return {!Element} 230 | */ 231 | WebInspector.linkifyResourceAsNode = function(url, lineNumber, classes, tooltipText) 232 | { 233 | var linkText = WebInspector.formatLinkText(url, lineNumber); 234 | var anchor = WebInspector.linkifyURLAsNode(url, linkText, classes, false, tooltipText); 235 | anchor.lineNumber = lineNumber; 236 | return anchor; 237 | }; 238 | 239 | /** 240 | * @param {!WebInspector.NetworkRequest} request 241 | * @return {!Element} 242 | */ 243 | WebInspector.linkifyRequestAsNode = function(request) 244 | { 245 | var anchor = WebInspector.linkifyURLAsNode(request.url); 246 | anchor.requestId = request.requestId; 247 | return anchor; 248 | }; 249 | 250 | /** 251 | * @param {?string} content 252 | * @param {string} mimeType 253 | * @param {boolean} contentEncoded 254 | * @return {?string} 255 | */ 256 | WebInspector.contentAsDataURL = function(content, mimeType, contentEncoded) 257 | { 258 | var maxDataUrlSize = 1024 * 1024; 259 | if (content === null || content.length > maxDataUrlSize) { 260 | return null; 261 | } 262 | 263 | return "data:" + mimeType + (contentEncoded ? ";base64," : ",") + content; 264 | }; 265 | -------------------------------------------------------------------------------- /public/js/common/ParsedURL.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Google Inc. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS 17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. 20 | * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | /** 30 | * @constructor 31 | * @param {string} url 32 | */ 33 | WebInspector.ParsedURL = function(url) 34 | { 35 | this.isValid = false; 36 | this.url = url; 37 | this.scheme = ""; 38 | this.host = ""; 39 | this.port = ""; 40 | this.path = ""; 41 | this.queryParams = ""; 42 | this.fragment = ""; 43 | this.folderPathComponents = ""; 44 | this.lastPathComponent = ""; 45 | 46 | // RegExp groups: 47 | // 1 - scheme (using the RFC3986 grammar) 48 | // 2 - hostname 49 | // 3 - ?port 50 | // 4 - ?path 51 | // 5 - ?fragment 52 | var match = url.match(/^([A-Za-z][A-Za-z0-9+.-]*):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i); 53 | if (match) { 54 | this.isValid = true; 55 | this.scheme = match[1].toLowerCase(); 56 | this.host = match[2]; 57 | this.port = match[3]; 58 | this.path = match[4] || "/"; 59 | this.fragment = match[5]; 60 | } else { 61 | if (this.url.startsWith("data:")) { 62 | this.scheme = "data"; 63 | return; 64 | } 65 | if (this.url === "about:blank") { 66 | this.scheme = "about"; 67 | return; 68 | } 69 | this.path = this.url; 70 | } 71 | 72 | // First cut the query params. 73 | var path = this.path; 74 | var indexOfQuery = path.indexOf("?"); 75 | if (indexOfQuery !== -1) { 76 | this.queryParams = path.substring(indexOfQuery + 1); 77 | path = path.substring(0, indexOfQuery); 78 | } 79 | 80 | // Then take last path component. 81 | var lastSlashIndex = path.lastIndexOf("/"); 82 | if (lastSlashIndex !== -1) { 83 | this.folderPathComponents = path.substring(0, lastSlashIndex); 84 | this.lastPathComponent = path.substring(lastSlashIndex + 1); 85 | } else { 86 | this.lastPathComponent = path; 87 | } 88 | }; 89 | 90 | /** 91 | * @param {string} url 92 | * @return {!Array.} 93 | */ 94 | WebInspector.ParsedURL.splitURL = function(url) 95 | { 96 | var parsedURL = new WebInspector.ParsedURL(url); 97 | var origin; 98 | var folderPath; 99 | var name; 100 | if (parsedURL.isValid) { 101 | origin = parsedURL.scheme + "://" + parsedURL.host; 102 | if (parsedURL.port) { 103 | origin += ":" + parsedURL.port; 104 | } 105 | folderPath = parsedURL.folderPathComponents; 106 | name = parsedURL.lastPathComponent; 107 | if (parsedURL.queryParams) { 108 | name += "?" + parsedURL.queryParams; 109 | } 110 | } else { 111 | origin = ""; 112 | folderPath = ""; 113 | name = url; 114 | } 115 | var result = [origin]; 116 | var splittedPath = folderPath.split("/"); 117 | for (var i = 1; i < splittedPath.length; ++i) { 118 | if (!splittedPath[i]) { 119 | continue; 120 | } 121 | result.push(splittedPath[i]); 122 | } 123 | result.push(name); 124 | return result; 125 | }; 126 | 127 | 128 | /** 129 | * http://tools.ietf.org/html/rfc3986#section-5.2.4 130 | * @param {string} path 131 | * @return {string} 132 | */ 133 | 134 | WebInspector.ParsedURL.normalizePath = function(path) 135 | { 136 | if (path.indexOf("..") === -1 && path.indexOf('.') === -1) { 137 | return path; 138 | } 139 | 140 | var normalizedSegments = []; 141 | var segments = path.split("/"); 142 | for (var i = 0; i < segments.length; i++) { 143 | var segment = segments[i]; 144 | if (segment === ".") { 145 | continue; 146 | } 147 | else if (segment === "..") { 148 | normalizedSegments.pop(); 149 | } 150 | else if (segment) { 151 | normalizedSegments.push(segment); 152 | } 153 | } 154 | var normalizedPath = normalizedSegments.join("/"); 155 | if (normalizedPath[normalizedPath.length - 1] === "/") { 156 | return normalizedPath; 157 | } 158 | if (path[0] === "/" && normalizedPath) { 159 | normalizedPath = "/" + normalizedPath; 160 | } 161 | if ((path[path.length - 1] === "/") || (segments[segments.length - 1] === ".") || (segments[segments.length - 1] === "..")) { 162 | normalizedPath = normalizedPath + "/"; 163 | } 164 | 165 | return normalizedPath; 166 | }; 167 | 168 | /** 169 | * @param {string} baseURL 170 | * @param {string} href 171 | * @return {?string} 172 | */ 173 | WebInspector.ParsedURL.completeURL = function(baseURL, href) 174 | { 175 | /*jshint -W107*/ 176 | // above ignores script urls (javascript:) 177 | if (href) { 178 | // Return special URLs as-is. 179 | var trimmedHref = href.trim(); 180 | if (trimmedHref.startsWith("data:") || trimmedHref.startsWith("blob:") || trimmedHref.startsWith("javascript:")) { 181 | return href; 182 | } 183 | 184 | // Return absolute URLs as-is. 185 | var parsedHref = trimmedHref.asParsedURL(); 186 | if (parsedHref && parsedHref.scheme) { 187 | return trimmedHref; 188 | } 189 | } else { 190 | return baseURL; 191 | } 192 | 193 | var parsedURL = baseURL.asParsedURL(); 194 | if (parsedURL) { 195 | if (parsedURL.isDataURL()) { 196 | return href; 197 | } 198 | var path = href; 199 | 200 | var query = path.indexOf("?"); 201 | var postfix = ""; 202 | if (query !== -1) { 203 | postfix = path.substring(query); 204 | path = path.substring(0, query); 205 | } else { 206 | var fragment = path.indexOf("#"); 207 | if (fragment !== -1) { 208 | postfix = path.substring(fragment); 209 | path = path.substring(0, fragment); 210 | } 211 | } 212 | 213 | if (!path) { // empty path, must be postfix 214 | var basePath = parsedURL.path; 215 | if (postfix.charAt(0) === "?") { 216 | // A href of "?foo=bar" implies "basePath?foo=bar". 217 | // With "basePath?a=b" and "?foo=bar" we should get "basePath?foo=bar". 218 | var baseQuery = parsedURL.path.indexOf("?"); 219 | if (baseQuery !== -1) { 220 | basePath = basePath.substring(0, baseQuery); 221 | } 222 | } // else it must be a fragment 223 | return parsedURL.scheme + "://" + parsedURL.host + (parsedURL.port ? (":" + parsedURL.port) : "") + basePath + postfix; 224 | } else if (path.charAt(0) !== "/") { // relative path 225 | var prefix = parsedURL.path; 226 | var prefixQuery = prefix.indexOf("?"); 227 | if (prefixQuery !== -1) { 228 | prefix = prefix.substring(0, prefixQuery); 229 | } 230 | prefix = prefix.substring(0, prefix.lastIndexOf("/")) + "/"; 231 | path = prefix + path; 232 | } else if (path.length > 1 && path.charAt(1) === "/") { 233 | // href starts with "//" which is a full URL with the protocol dropped (use the baseURL protocol). 234 | return parsedURL.scheme + ":" + path + postfix; 235 | } // else absolute path 236 | return parsedURL.scheme + "://" + parsedURL.host + (parsedURL.port ? (":" + parsedURL.port) : "") + WebInspector.ParsedURL.normalizePath(path) + postfix; 237 | } 238 | return null; 239 | }; 240 | 241 | WebInspector.ParsedURL.prototype = { 242 | get displayName() 243 | { 244 | if (this._displayName) { 245 | return this._displayName; 246 | } 247 | 248 | if (this.isDataURL()) { 249 | return this.dataURLDisplayName(); 250 | } 251 | if (this.isAboutBlank()) { 252 | return this.url; 253 | } 254 | 255 | this._displayName = this.lastPathComponent; 256 | if (!this._displayName) { 257 | this._displayName = (this.host || "") + "/"; 258 | } 259 | if (this._displayName === "/") { 260 | this._displayName = this.url; 261 | } 262 | return this._displayName; 263 | }, 264 | 265 | /** 266 | * @return {string} 267 | */ 268 | dataURLDisplayName: function() 269 | { 270 | if (this._dataURLDisplayName) { 271 | return this._dataURLDisplayName; 272 | } 273 | if (!this.isDataURL()) { 274 | return ""; 275 | } 276 | this._dataURLDisplayName = this.url.trimEnd(20); 277 | return this._dataURLDisplayName; 278 | }, 279 | 280 | /** 281 | * @return {boolean} 282 | */ 283 | isAboutBlank: function() 284 | { 285 | return this.url === "about:blank"; 286 | }, 287 | 288 | /** 289 | * @return {boolean} 290 | */ 291 | isDataURL: function() 292 | { 293 | return this.scheme === "data"; 294 | } 295 | }; 296 | 297 | /** 298 | * @return {?WebInspector.ParsedURL} 299 | */ 300 | String.prototype.asParsedURL = function() 301 | { 302 | var parsedURL = new WebInspector.ParsedURL(this.toString()); 303 | if (parsedURL.isValid) { 304 | return parsedURL; 305 | } 306 | return null; 307 | }; 308 | -------------------------------------------------------------------------------- /public/js/components/Drawer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 | * Copyright (C) 2009 Joseph Pecoraro 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 | * its contributors may be used to endorse or promote products derived 16 | * from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** 31 | * @constructor 32 | * @extends {WebInspector.VBox} 33 | * @param {!WebInspector.SplitView} splitView 34 | */ 35 | WebInspector.Drawer = function(splitView) 36 | { 37 | WebInspector.VBox.call(this); 38 | this.element.id = "drawer-contents"; 39 | 40 | this._splitView = splitView; 41 | splitView.hideDefaultResizer(); 42 | // this.show(splitView.sidebarElement()); 43 | 44 | this._drawerEditorSplitView = new WebInspector.SplitView(true, true, "editorInDrawerSplitViewState", 0.5, 0.5); 45 | this._drawerEditorSplitView.hideSidebar(); 46 | this._drawerEditorSplitView.addEventListener(WebInspector.SplitView.Events.ShowModeChanged, this._drawerEditorSplitViewShowModeChanged, this); 47 | this._drawerEditorShownSetting = WebInspector.settings.createSetting("drawerEditorShown", true); 48 | this._drawerEditorSplitView.show(this.element); 49 | 50 | this._toggleDrawerButton = new WebInspector.StatusBarButton(WebInspector.UIString("Show drawer."), "console-status-bar-item"); 51 | this._toggleDrawerButton.addEventListener("click", this.toggle, this); 52 | 53 | this._tabbedPane = new WebInspector.TabbedPane(); 54 | this._tabbedPane.element.id = "drawer-tabbed-pane"; 55 | this._tabbedPane.closeableTabs = false; 56 | this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this); 57 | new WebInspector.ExtensibleTabbedPaneController(this._tabbedPane, "drawer-view"); 58 | 59 | this._toggleDrawerEditorButton = this._drawerEditorSplitView.createShowHideSidebarButton("editor in drawer", "drawer-editor-show-hide-button"); 60 | this._tabbedPane.element.appendChild(this._toggleDrawerEditorButton.element); 61 | if (!WebInspector.experimentsSettings.editorInDrawer.isEnabled()) { 62 | // this.setDrawerEditorAvailable(false); 63 | } 64 | 65 | // splitView.installResizer(this._tabbedPane.headerElement()); 66 | this._lastSelectedViewSetting = WebInspector.settings.createSetting("WebInspector.Drawer.lastSelectedView", "console"); 67 | this._tabbedPane.show(this._drawerEditorSplitView.mainElement()); 68 | 69 | this.closeDrawer(); 70 | }; 71 | 72 | WebInspector.Drawer.prototype = { 73 | /** 74 | * @return {!WebInspector.StatusBarButton} 75 | */ 76 | toggleButton: function() 77 | { 78 | return this._toggleDrawerButton; 79 | }, 80 | 81 | /** 82 | * @param {string} id 83 | */ 84 | closeView: function(id) 85 | { 86 | this._tabbedPane.closeTab(id); 87 | }, 88 | 89 | /** 90 | * @param {string} id 91 | * @param {boolean=} immediate 92 | */ 93 | showView: function(id, immediate) 94 | { 95 | if (!this._tabbedPane.hasTab(id)) { 96 | // Hidden tab. 97 | this._innerShow(immediate); 98 | return; 99 | } 100 | this._innerShow(immediate); 101 | this._tabbedPane.selectTab(id, true); 102 | // In case this id is already selected, anyways persist it as the last saved value. 103 | this._lastSelectedViewSetting.set(id); 104 | }, 105 | 106 | /** 107 | * @param {string} id 108 | * @param {string} title 109 | * @param {!WebInspector.View} view 110 | */ 111 | showCloseableView: function(id, title, view) 112 | { 113 | if (!this._tabbedPane.hasTab(id)) { 114 | this._tabbedPane.appendTab(id, title, view, undefined, false, true); 115 | } else { 116 | this._tabbedPane.changeTabView(id, view); 117 | this._tabbedPane.changeTabTitle(id, title); 118 | } 119 | this._innerShow(); 120 | this._tabbedPane.selectTab(id, true); 121 | }, 122 | 123 | showDrawer: function() 124 | { 125 | this.showView(this._lastSelectedViewSetting.get()); 126 | }, 127 | 128 | wasShown: function() 129 | { 130 | this.showView(this._lastSelectedViewSetting.get()); 131 | this._toggleDrawerButton.toggled = true; 132 | this._toggleDrawerButton.title = WebInspector.UIString("Hide drawer."); 133 | this._ensureDrawerEditorExistsIfNeeded(); 134 | }, 135 | 136 | willHide: function() 137 | { 138 | this._toggleDrawerButton.toggled = false; 139 | this._toggleDrawerButton.title = WebInspector.UIString("Show drawer."); 140 | }, 141 | 142 | /** 143 | * @param {boolean=} immediate 144 | */ 145 | _innerShow: function(immediate) 146 | { 147 | if (this.isShowing()) { 148 | return; 149 | } 150 | 151 | this._splitView.showBoth(!immediate); 152 | 153 | if (this._visibleView()) { 154 | this._visibleView().focus(); 155 | } 156 | }, 157 | 158 | closeDrawer: function() 159 | { 160 | if (!this.isShowing()) { 161 | return; 162 | } 163 | 164 | WebInspector.restoreFocusFromElement(this.element); 165 | this._splitView.hideSidebar(true); 166 | }, 167 | 168 | /** 169 | * @return {?WebInspector.View} view 170 | */ 171 | _visibleView: function() 172 | { 173 | return this._tabbedPane.visibleView; 174 | }, 175 | 176 | /** 177 | * @param {!WebInspector.Event} event 178 | */ 179 | _tabSelected: function(event) 180 | { 181 | var tabId = this._tabbedPane.selectedTabId; 182 | if (tabId && event.data.isUserGesture && !this._tabbedPane.isTabCloseable(tabId)) { 183 | this._lastSelectedViewSetting.set(tabId); 184 | } 185 | }, 186 | 187 | toggle: function() 188 | { 189 | if (this._toggleDrawerButton.toggled) { 190 | this.closeDrawer(); 191 | } 192 | else { 193 | this.showDrawer(); 194 | } 195 | }, 196 | 197 | /** 198 | * @return {boolean} 199 | */ 200 | visible: function() 201 | { 202 | return this._toggleDrawerButton.toggled; 203 | }, 204 | 205 | /** 206 | * @return {?string} 207 | */ 208 | selectedViewId: function() 209 | { 210 | return this._tabbedPane.selectedTabId; 211 | }, 212 | 213 | /** 214 | * @param {!WebInspector.Event} event 215 | */ 216 | _drawerEditorSplitViewShowModeChanged: function(event) 217 | { 218 | var mode = /** @type {string} */ (event.data); 219 | var shown = mode === WebInspector.SplitView.ShowMode.Both; 220 | 221 | if (this._isHidingDrawerEditor) { 222 | return; 223 | } 224 | 225 | this._drawerEditorShownSetting.set(shown); 226 | 227 | if (!shown) { 228 | return; 229 | } 230 | 231 | this._ensureDrawerEditor(); 232 | this._drawerEditor.view().show(this._drawerEditorSplitView.sidebarElement()); 233 | }, 234 | 235 | initialPanelShown: function() 236 | { 237 | this._initialPanelWasShown = true; 238 | this._ensureDrawerEditorExistsIfNeeded(); 239 | }, 240 | 241 | _ensureDrawerEditorExistsIfNeeded: function() 242 | { 243 | if (!this._initialPanelWasShown || !this.isShowing() || !this._drawerEditorShownSetting.get() || !WebInspector.experimentsSettings.editorInDrawer.isEnabled()) { 244 | return; 245 | } 246 | this._ensureDrawerEditor(); 247 | }, 248 | 249 | _ensureDrawerEditor: function() 250 | { 251 | if (this._drawerEditor) { 252 | return; 253 | } 254 | this._drawerEditor = WebInspector.moduleManager.instance(WebInspector.DrawerEditor); 255 | this._drawerEditor.installedIntoDrawer(); 256 | }, 257 | 258 | /** 259 | * @param {boolean} available 260 | */ 261 | setDrawerEditorAvailable: function(available) 262 | { 263 | if (!WebInspector.experimentsSettings.editorInDrawer.isEnabled()) { 264 | available = false; 265 | } 266 | this._toggleDrawerEditorButton.element.classList.toggle("hidden", !available); 267 | }, 268 | 269 | showDrawerEditor: function() 270 | { 271 | if (!WebInspector.experimentsSettings.editorInDrawer.isEnabled()) { 272 | return; 273 | } 274 | 275 | this._splitView.showBoth(); 276 | this._drawerEditorSplitView.showBoth(); 277 | }, 278 | 279 | hideDrawerEditor: function() 280 | { 281 | this._isHidingDrawerEditor = true; 282 | this._drawerEditorSplitView.hideSidebar(); 283 | this._isHidingDrawerEditor = false; 284 | }, 285 | 286 | /** 287 | * @return {boolean} 288 | */ 289 | isDrawerEditorShown: function() 290 | { 291 | return this._drawerEditorShownSetting.get(); 292 | }, 293 | 294 | __proto__: WebInspector.VBox.prototype 295 | }; 296 | 297 | /** 298 | * @interface 299 | */ 300 | WebInspector.Drawer.ViewFactory = function() 301 | { 302 | }; 303 | 304 | WebInspector.Drawer.ViewFactory.prototype = { 305 | /** 306 | * @return {!WebInspector.View} 307 | */ 308 | createView: function() {} 309 | }; 310 | 311 | /** 312 | * @constructor 313 | * @implements {WebInspector.Drawer.ViewFactory} 314 | * @param {function(new:T)} constructor 315 | * @template T 316 | */ 317 | WebInspector.Drawer.SingletonViewFactory = function(constructor) 318 | { 319 | this._constructor = constructor; 320 | }; 321 | 322 | WebInspector.Drawer.SingletonViewFactory.prototype = { 323 | /** 324 | * @return {!WebInspector.View} 325 | */ 326 | createView: function() 327 | { 328 | if (!this._instance) { 329 | this._instance = /** @type {!WebInspector.View} */(new this._constructor()); 330 | } 331 | return this._instance; 332 | } 333 | }; 334 | 335 | /** 336 | * @interface 337 | */ 338 | WebInspector.DrawerEditor = function() 339 | { 340 | }; 341 | 342 | WebInspector.DrawerEditor.prototype = { 343 | /** 344 | * @return {!WebInspector.View} 345 | */ 346 | view: function() { }, 347 | 348 | installedIntoDrawer: function() { }, 349 | }; 350 | --------------------------------------------------------------------------------