From 2dc9829898255be4f8747c2f941902f5f85d806d Mon Sep 17 00:00:00 2001 From: Philip Lawrence Date: Fri, 5 Jan 2018 20:06:09 -0700 Subject: [PATCH 001/111] Refresh code base back to working conditions across browsers --- Gruntfile.js | 17 +- platform/chromium/devtools.js | 934 +++++++++++++++++++++++++++++++- platform/chromium/eventPage.js | 98 ++-- platform/chromium/omnibugurl.js | 30 +- platform/chromium/options.html | 7 + platform/chromium/options.js | 275 ---------- platform/chromium/providers.js | 133 ++++- platform/edge/devtools.js | 30 +- platform/edge/eventPage.js | 98 ++-- platform/edge/manifest.json | 2 +- platform/edge/omnibugurl.js | 30 +- platform/edge/options.html | 7 + platform/edge/options.js | 24 +- platform/edge/providers.js | 133 ++++- platform/firefox/devtools.js | 30 +- platform/firefox/eventPage.js | 98 ++-- platform/firefox/manifest.json | 2 +- platform/firefox/omnibugurl.js | 30 +- platform/firefox/options.html | 7 + platform/firefox/options.js | 24 +- platform/firefox/providers.js | 133 ++++- src/browser-polyfill.js | 903 ++++++++++++++++++++++++++++++ src/devtools.js | 10 +- src/eventPage.js | 98 ++-- src/manifest.json | 2 +- src/options.html | 7 + src/options.js | 8 +- 27 files changed, 2411 insertions(+), 759 deletions(-) delete mode 100644 platform/chromium/options.js create mode 100644 src/browser-polyfill.js diff --git a/Gruntfile.js b/Gruntfile.js index 4c709767..f04d3905 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -271,15 +271,18 @@ module.exports = function(grunt) { grunt.registerTask("build-concat", "Concat build files for a browser", function(browser) { grunt.config.requires(browser); - let options = grunt.config(browser), - sourceFiles = ["src/options.js"]; - if(options.usePolyfill) { - sourceFiles.unshift("src/browser-polyfill.js"); - } + let options = grunt.config(browser); + let destFiles = {}, + baseFiles = []; + if(options.usePolyfill) + { + baseFiles.push("src/browser-polyfill.js"); + } + destFiles["platform/" + options.folder + "/options.js"] = baseFiles.concat(["src/options.js"]); + destFiles["platform/" + options.folder + "/devtools.js"] = baseFiles.concat(["src/devtools.js"]); grunt.config.set("concat." + browser, { - src: sourceFiles, - dest: "platform/" + options.folder + "/options.js" + files: destFiles }); grunt.task.run("concat:" + browser); }); diff --git a/platform/chromium/devtools.js b/platform/chromium/devtools.js index 7508cbb3..2de59222 100644 --- a/platform/chromium/devtools.js +++ b/platform/chromium/devtools.js @@ -1,13 +1,912 @@ +(function (global, factory) { + if (typeof define === "function" && define.amd) { + define("webextension-polyfill", ["module"], factory); + } else if (typeof exports !== "undefined") { + factory(module); + } else { + var mod = { + exports: {} + }; + factory(mod); + global.browser = mod.exports; + } +})(this, function (module) { + /* webextension-polyfill - v0.2.1 - Sun Nov 19 2017 02:44:45 */ + /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ + /* vim: set sts=2 sw=2 et tw=80: */ + /* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + "use strict"; + + if (typeof browser === "undefined") { + // Wrapping the bulk of this polyfill in a one-time-use function is a minor + // optimization for Firefox. Since Spidermonkey does not fully parse the + // contents of a function until the first time it's called, and since it will + // never actually need to be called, this allows the polyfill to be included + // in Firefox nearly for free. + const wrapAPIs = () => { + // NOTE: apiMetadata is associated to the content of the api-metadata.json file + // at build time by replacing the following "include" with the content of the + // JSON file. + const apiMetadata = { + "alarms": { + "clear": { + "minArgs": 0, + "maxArgs": 1 + }, + "clearAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "bookmarks": { + "create": { + "minArgs": 1, + "maxArgs": 1 + }, + "export": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getChildren": { + "minArgs": 1, + "maxArgs": 1 + }, + "getRecent": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTree": { + "minArgs": 0, + "maxArgs": 0 + }, + "getSubTree": { + "minArgs": 1, + "maxArgs": 1 + }, + "import": { + "minArgs": 0, + "maxArgs": 0 + }, + "move": { + "minArgs": 2, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeTree": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + }, + "browserAction": { + "getBadgeBackgroundColor": { + "minArgs": 1, + "maxArgs": 1 + }, + "getBadgeText": { + "minArgs": 1, + "maxArgs": 1 + }, + "getPopup": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTitle": { + "minArgs": 1, + "maxArgs": 1 + }, + "setIcon": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "commands": { + "getAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "contextMenus": { + "update": { + "minArgs": 2, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "cookies": { + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAllCookieStores": { + "minArgs": 0, + "maxArgs": 0 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "devtools": { + "inspectedWindow": { + "eval": { + "minArgs": 1, + "maxArgs": 2 + } + }, + "panels": { + "create": { + "minArgs": 3, + "maxArgs": 3, + "singleCallbackArg": true + } + } + }, + "downloads": { + "download": { + "minArgs": 1, + "maxArgs": 1 + }, + "cancel": { + "minArgs": 1, + "maxArgs": 1 + }, + "erase": { + "minArgs": 1, + "maxArgs": 1 + }, + "getFileIcon": { + "minArgs": 1, + "maxArgs": 2 + }, + "open": { + "minArgs": 1, + "maxArgs": 1 + }, + "pause": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeFile": { + "minArgs": 1, + "maxArgs": 1 + }, + "resume": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + }, + "show": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "extension": { + "isAllowedFileSchemeAccess": { + "minArgs": 0, + "maxArgs": 0 + }, + "isAllowedIncognitoAccess": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "history": { + "addUrl": { + "minArgs": 1, + "maxArgs": 1 + }, + "getVisits": { + "minArgs": 1, + "maxArgs": 1 + }, + "deleteAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "deleteRange": { + "minArgs": 1, + "maxArgs": 1 + }, + "deleteUrl": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "i18n": { + "detectLanguage": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAcceptLanguages": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "idle": { + "queryState": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "management": { + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "getSelf": { + "minArgs": 0, + "maxArgs": 0 + }, + "uninstallSelf": { + "minArgs": 0, + "maxArgs": 1 + } + }, + "notifications": { + "clear": { + "minArgs": 1, + "maxArgs": 1 + }, + "create": { + "minArgs": 1, + "maxArgs": 2 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "getPermissionLevel": { + "minArgs": 0, + "maxArgs": 0 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + }, + "pageAction": { + "getPopup": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTitle": { + "minArgs": 1, + "maxArgs": 1 + }, + "hide": { + "minArgs": 0, + "maxArgs": 0 + }, + "setIcon": { + "minArgs": 1, + "maxArgs": 1 + }, + "show": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "runtime": { + "getBackgroundPage": { + "minArgs": 0, + "maxArgs": 0 + }, + "getBrowserInfo": { + "minArgs": 0, + "maxArgs": 0 + }, + "getPlatformInfo": { + "minArgs": 0, + "maxArgs": 0 + }, + "openOptionsPage": { + "minArgs": 0, + "maxArgs": 0 + }, + "requestUpdateCheck": { + "minArgs": 0, + "maxArgs": 0 + }, + "sendMessage": { + "minArgs": 1, + "maxArgs": 3 + }, + "sendNativeMessage": { + "minArgs": 2, + "maxArgs": 2 + }, + "setUninstallURL": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "storage": { + "local": { + "clear": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "managed": { + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + } + }, + "sync": { + "clear": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + } + }, + "tabs": { + "create": { + "minArgs": 1, + "maxArgs": 1 + }, + "captureVisibleTab": { + "minArgs": 0, + "maxArgs": 2 + }, + "detectLanguage": { + "minArgs": 0, + "maxArgs": 1 + }, + "duplicate": { + "minArgs": 1, + "maxArgs": 1 + }, + "executeScript": { + "minArgs": 1, + "maxArgs": 2 + }, + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getCurrent": { + "minArgs": 0, + "maxArgs": 0 + }, + "getZoom": { + "minArgs": 0, + "maxArgs": 1 + }, + "getZoomSettings": { + "minArgs": 0, + "maxArgs": 1 + }, + "highlight": { + "minArgs": 1, + "maxArgs": 1 + }, + "insertCSS": { + "minArgs": 1, + "maxArgs": 2 + }, + "move": { + "minArgs": 2, + "maxArgs": 2 + }, + "reload": { + "minArgs": 0, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "query": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeCSS": { + "minArgs": 1, + "maxArgs": 2 + }, + "sendMessage": { + "minArgs": 2, + "maxArgs": 3 + }, + "setZoom": { + "minArgs": 1, + "maxArgs": 2 + }, + "setZoomSettings": { + "minArgs": 1, + "maxArgs": 2 + }, + "update": { + "minArgs": 1, + "maxArgs": 2 + } + }, + "webNavigation": { + "getAllFrames": { + "minArgs": 1, + "maxArgs": 1 + }, + "getFrame": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "webRequest": { + "handlerBehaviorChanged": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "windows": { + "create": { + "minArgs": 0, + "maxArgs": 1 + }, + "get": { + "minArgs": 1, + "maxArgs": 2 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 1 + }, + "getCurrent": { + "minArgs": 0, + "maxArgs": 1 + }, + "getLastFocused": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + } + }; + + if (Object.keys(apiMetadata).length === 0) { + throw new Error("api-metadata.json has not been included in browser-polyfill"); + } + + /** + * A WeakMap subclass which creates and stores a value for any key which does + * not exist when accessed, but behaves exactly as an ordinary WeakMap + * otherwise. + * + * @param {function} createItem + * A function which will be called in order to create the value for any + * key which does not exist, the first time it is accessed. The + * function receives, as its only argument, the key being created. + */ + class DefaultWeakMap extends WeakMap { + constructor(createItem, items = undefined) { + super(items); + this.createItem = createItem; + } + + get(key) { + if (!this.has(key)) { + this.set(key, this.createItem(key)); + } + + return super.get(key); + } + } + + /** + * Returns true if the given object is an object with a `then` method, and can + * therefore be assumed to behave as a Promise. + * + * @param {*} value The value to test. + * @returns {boolean} True if the value is thenable. + */ + const isThenable = value => { + return value && typeof value === "object" && typeof value.then === "function"; + }; + + /** + * Creates and returns a function which, when called, will resolve or reject + * the given promise based on how it is called: + * + * - If, when called, `chrome.runtime.lastError` contains a non-null object, + * the promise is rejected with that value. + * - If the function is called with exactly one argument, the promise is + * resolved to that value. + * - Otherwise, the promise is resolved to an array containing all of the + * function's arguments. + * + * @param {object} promise + * An object containing the resolution and rejection functions of a + * promise. + * @param {function} promise.resolve + * The promise's resolution function. + * @param {function} promise.rejection + * The promise's rejection function. + * @param {object} metadata + * Metadata about the wrapped method which has created the callback. + * @param {integer} metadata.maxResolvedArgs + * The maximum number of arguments which may be passed to the + * callback created by the wrapped async function. + * + * @returns {function} + * The generated callback function. + */ + const makeCallback = (promise, metadata) => { + return (...callbackArgs) => { + if (chrome.runtime.lastError) { + promise.reject(chrome.runtime.lastError); + } else if (metadata.singleCallbackArg || callbackArgs.length === 1) { + promise.resolve(callbackArgs[0]); + } else { + promise.resolve(callbackArgs); + } + }; + }; + + /** + * Creates a wrapper function for a method with the given name and metadata. + * + * @param {string} name + * The name of the method which is being wrapped. + * @param {object} metadata + * Metadata about the method being wrapped. + * @param {integer} metadata.minArgs + * The minimum number of arguments which must be passed to the + * function. If called with fewer than this number of arguments, the + * wrapper will raise an exception. + * @param {integer} metadata.maxArgs + * The maximum number of arguments which may be passed to the + * function. If called with more than this number of arguments, the + * wrapper will raise an exception. + * @param {integer} metadata.maxResolvedArgs + * The maximum number of arguments which may be passed to the + * callback created by the wrapped async function. + * + * @returns {function(object, ...*)} + * The generated wrapper function. + */ + const wrapAsyncFunction = (name, metadata) => { + const pluralizeArguments = numArgs => numArgs == 1 ? "argument" : "arguments"; + + return function asyncFunctionWrapper(target, ...args) { + if (args.length < metadata.minArgs) { + throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`); + } + + if (args.length > metadata.maxArgs) { + throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`); + } + + return new Promise((resolve, reject) => { + target[name](...args, makeCallback({ resolve, reject }, metadata)); + }); + }; + }; + + /** + * Wraps an existing method of the target object, so that calls to it are + * intercepted by the given wrapper function. The wrapper function receives, + * as its first argument, the original `target` object, followed by each of + * the arguments passed to the orginal method. + * + * @param {object} target + * The original target object that the wrapped method belongs to. + * @param {function} method + * The method being wrapped. This is used as the target of the Proxy + * object which is created to wrap the method. + * @param {function} wrapper + * The wrapper function which is called in place of a direct invocation + * of the wrapped method. + * + * @returns {Proxy} + * A Proxy object for the given method, which invokes the given wrapper + * method in its place. + */ + const wrapMethod = (target, method, wrapper) => { + return new Proxy(method, { + apply(targetMethod, thisObj, args) { + return wrapper.call(thisObj, target, ...args); + } + }); + }; + + let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); + + /** + * Wraps an object in a Proxy which intercepts and wraps certain methods + * based on the given `wrappers` and `metadata` objects. + * + * @param {object} target + * The target object to wrap. + * + * @param {object} [wrappers = {}] + * An object tree containing wrapper functions for special cases. Any + * function present in this object tree is called in place of the + * method in the same location in the `target` object tree. These + * wrapper methods are invoked as described in {@see wrapMethod}. + * + * @param {object} [metadata = {}] + * An object tree containing metadata used to automatically generate + * Promise-based wrapper functions for asynchronous. Any function in + * the `target` object tree which has a corresponding metadata object + * in the same location in the `metadata` tree is replaced with an + * automatically-generated wrapper function, as described in + * {@see wrapAsyncFunction} + * + * @returns {Proxy} + */ + const wrapObject = (target, wrappers = {}, metadata = {}) => { + let cache = Object.create(null); + + let handlers = { + has(target, prop) { + return prop in target || prop in cache; + }, + + get(target, prop, receiver) { + if (prop in cache) { + return cache[prop]; + } + + if (!(prop in target)) { + return undefined; + } + + let value = target[prop]; + + if (typeof value === "function") { + // This is a method on the underlying object. Check if we need to do + // any wrapping. + + if (typeof wrappers[prop] === "function") { + // We have a special-case wrapper for this method. + value = wrapMethod(target, target[prop], wrappers[prop]); + } else if (hasOwnProperty(metadata, prop)) { + // This is an async method that we have metadata for. Create a + // Promise wrapper for it. + let wrapper = wrapAsyncFunction(prop, metadata[prop]); + value = wrapMethod(target, target[prop], wrapper); + } else { + // This is a method that we don't know or care about. Return the + // original method, bound to the underlying object. + value = value.bind(target); + } + } else if (typeof value === "object" && value !== null && (hasOwnProperty(wrappers, prop) || hasOwnProperty(metadata, prop))) { + // This is an object that we need to do some wrapping for the children + // of. Create a sub-object wrapper for it with the appropriate child + // metadata. + value = wrapObject(value, wrappers[prop], metadata[prop]); + } else { + // We don't need to do any wrapping for this property, + // so just forward all access to the underlying object. + Object.defineProperty(cache, prop, { + configurable: true, + enumerable: true, + get() { + return target[prop]; + }, + set(value) { + target[prop] = value; + } + }); + + return value; + } + + cache[prop] = value; + return value; + }, + + set(target, prop, value, receiver) { + if (prop in cache) { + cache[prop] = value; + } else { + target[prop] = value; + } + return true; + }, + + defineProperty(target, prop, desc) { + return Reflect.defineProperty(cache, prop, desc); + }, + + deleteProperty(target, prop) { + return Reflect.deleteProperty(cache, prop); + } + }; + + return new Proxy(target, handlers); + }; + + /** + * Creates a set of wrapper functions for an event object, which handles + * wrapping of listener functions that those messages are passed. + * + * A single wrapper is created for each listener function, and stored in a + * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener` + * retrieve the original wrapper, so that attempts to remove a + * previously-added listener work as expected. + * + * @param {DefaultWeakMap} wrapperMap + * A DefaultWeakMap object which will create the appropriate wrapper + * for a given listener function when one does not exist, and retrieve + * an existing one when it does. + * + * @returns {object} + */ + const wrapEvent = wrapperMap => ({ + addListener(target, listener, ...args) { + target.addListener(wrapperMap.get(listener), ...args); + }, + + hasListener(target, listener) { + return target.hasListener(wrapperMap.get(listener)); + }, + + removeListener(target, listener) { + target.removeListener(wrapperMap.get(listener)); + } + }); + + const onMessageWrappers = new DefaultWeakMap(listener => { + if (typeof listener !== "function") { + return listener; + } + + /** + * Wraps a message listener function so that it may send responses based on + * its return value, rather than by returning a sentinel value and calling a + * callback. If the listener function returns a Promise, the response is + * sent when the promise either resolves or rejects. + * + * @param {*} message + * The message sent by the other end of the channel. + * @param {object} sender + * Details about the sender of the message. + * @param {function(*)} sendResponse + * A callback which, when called with an arbitrary argument, sends + * that value as a response. + * @returns {boolean} + * True if the wrapped listener returned a Promise, which will later + * yield a response. False otherwise. + */ + return function onMessage(message, sender, sendResponse) { + let result = listener(message, sender); + + if (isThenable(result)) { + result.then(sendResponse, error => { + console.error(error); + sendResponse(error); + }); + + return true; + } else if (result !== undefined) { + sendResponse(result); + } + }; + }); + + const staticWrappers = { + runtime: { + onMessage: wrapEvent(onMessageWrappers) + } + }; + + // Create a new empty object and copy the properties of the original chrome object + // to prevent a Proxy violation exception for the devtools API getter + // (which is a read-only non-configurable property on the original target). + const targetObject = Object.assign({}, chrome); + + return wrapObject(targetObject, staticWrappers, apiMetadata); + }; + + // The build process adds a UMD wrapper around this file, which makes the + // `module` variable available. + module.exports = wrapAPIs(); // eslint-disable-line no-undef + } else { + module.exports = browser; // eslint-disable-line no-undef + } +}); +//# sourceMappingURL=browser-polyfill.js.map + /* * Omnibug * Intermediary between eventPage and devTools panel * (used for message passing only) * - * This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. - * To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send - * a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, - * USA. - * */ ( function() { @@ -17,7 +916,6 @@ var panelCreated = function( panel ) { var queuedMessages = [], panelWindow, // reference to devtools_panel.html's `window` object - clearButton, port; port = browser.runtime.connect( { name: "omnibug-" + browser.devtools.inspectedWindow.tabId } ); @@ -51,25 +949,6 @@ port.postMessage( msg ); }; } ); - - /* - if(panel.createStatusBarButton) { - - // add a clear button - clearButton = panel.createStatusBarButton( "images/clear_button.png", "Clear events.", false ); - clearButton.onClicked.addListener( function() { - var tables = panelWindow.document.getElementsByTagName( "table" ); - while( tables.length > 0 ) { - for( var i=0; i { that.prefs = prefData.omnibug; var pattern = that.prefs.defaultPattern = getCurrentPattern( prefData.omnibug ); that.prefs.defaultRegex = new RegExp( that.prefs.defaultPattern ); - } ); - } + console.log('this.prefs.defaultRegex', that.prefs.defaultPattern); + }); + } /** * Receive updates when prefs change and broadcast them out */ browser.storage.onChanged.addListener( function( changes, namespace ) { + console.log('eventPage browser.storage.onChanged'); if( "omnibug" in changes ) { var newPrefs = changes["omnibug"].newValue; console.log( "Received updated prefs", newPrefs ); @@ -106,7 +105,6 @@ } } ); - /** * Return a pattern that matches the currently enabled providers */ @@ -124,15 +122,13 @@ return new RegExp( patterns.join( "|" ) ).source; } - /** * Quickly determine if a URL is a candidate for us or not */ function shouldProcess( url ) { - return url.match( this.prefs.defaultRegex ); + return this.prefs.defaultRegex.test( url ); } - /** * Callback for the onResponseStarted listener * @@ -151,15 +147,19 @@ * url: "https://0-act.channel.facebook.com/pull?cha... */ var beforeRequestCallback = function( details ) { + console.log('eventPage beforeRequestCallback', details.url); + // ignore browser:// requests and non-metrics URLs - if( details.tabId == -1 || !shouldProcess( details.url ) ) return; + if( details.tabId === -1 || !shouldProcess( details.url ) ) { + return; + } if( !( details.tabId in tabs ) ) { - /* disable this error message -- too numerous! - console.error( "Request for unknown tabId ", details.tabId ); */ return; } + console.log('eventPage beforeRequestCallback MATCH', details); + // look up provider and pass along var prov = OmnibugProvider.getProviderForUrl( details.url ); details.omnibugProvider = prov; @@ -167,23 +167,22 @@ // store the current tab's loading state into the details object details.omnibugLoading = tabs[details.tabId].loading; - browser.tabs.get( details.tabId, detailsProcessingCallbackFactory( details ) ); - }; + console.log('eventPage beforeRequestCallback MATCH AFTER', details); + browser.tabs.get( details.tabId).then((tab) => {detailsProcessingCallbackFactory(details, tab)}); + }; /** * Factory function returning a function which has access to details *and* tab */ - var detailsProcessingCallbackFactory = function( details ) { - return function( tab ) { - // save the tab's current URL into the details object - details.tabUrl = tab.url; + var detailsProcessingCallbackFactory = function( details, tab ) { + console.log('eventPage detailsProcessingCallbackFactory', details, tab); + // save the tab's current URL into the details object + details.tabUrl = tab.url; - sendToDevToolsForTab( details.tabId, { "type" : "webEvent", "payload" : decodeUrl( details ) } ); - } + sendToDevToolsForTab( details.tabId, { "type" : "webEvent", "payload" : decodeUrl( details ) } ); }; - browser.webRequest.onBeforeRequest.addListener( beforeRequestCallback, { urls: [""] }, @@ -191,7 +190,6 @@ // @TODO: filter these based on static patterns/config ? ); - /** * Return the tabId associated with a port */ @@ -199,7 +197,6 @@ return port.name.substring( port.name.indexOf( "-" ) + 1 ); } - /** * Accept connections from our devtools panels */ @@ -243,13 +240,13 @@ } ); } ); - /** * Send a message to the devtools panel on a given tab * Assumes the port is already connected */ function sendToDevToolsForTab( tabId, object ) { console.debug( "sending ", object.type, " message to tabId: ", tabId, ": ", object ); + console.log(tabs[tabId].port.postMessage); try { var payload = JSON.parse(JSON.stringify(object)); tabs[tabId].port.postMessage( payload ); @@ -308,7 +305,6 @@ return obj; } - /** * Takes a single name/value pair and delegates handling of it to the provider * Otherwise, inserts into the `other' bucket @@ -324,7 +320,6 @@ } } - /** * If the provider defines a custom URL handler, delegate to it */ @@ -334,7 +329,6 @@ } } - /** * Augments the data object with summary data * @param data the data object @@ -344,11 +338,11 @@ data["omnibug"] = {}; var eventType = ( data.state.omnibugLoading ? "load" : "click" ), - url = data.state.url, - urlLength = data.state.url.length; + url = data.state.url; // hacky: sometimes load events are being reported as click events. For Omniture, detect // the event type (pe= means a click event), and reset eventType accordingly. + // @TODO: Move this logic to the providers if( data.state.omnibugProvider.key.toUpperCase() === "OMNITURE" ) { var oldEventType = eventType; eventType = ( !!url.match( "[?&]pe=" ) ? "click" : "load" ); @@ -358,12 +352,7 @@ data.omnibug["Timestamp"] = data.state.timeStamp; data.omnibug["Provider"] = data.state.omnibugProvider.name; data.omnibug["Parent URL"] = data.state.tabUrl; - data.omnibug["Full URL"] = data.state.url - + "
(" + urlLength + " characters" - + ( urlLength > 2083 - ? ", *** too long for IE6/7! ***" - : "" ) - + ")"; + data.omnibug["Full URL"] = data.state.url; data.omnibug["Request ID"] = data.state.requestId; data.omnibug["Status Line"] = data.state.statusLine; data.omnibug["Request Type"] = data.state.type; @@ -375,5 +364,4 @@ // public return {}; -}() ); - +}() ); \ No newline at end of file diff --git a/platform/chromium/omnibugurl.js b/platform/chromium/omnibugurl.js index 836114f1..7fd02644 100644 --- a/platform/chromium/omnibugurl.js +++ b/platform/chromium/omnibugurl.js @@ -18,10 +18,10 @@ var OmnibugUrl = function( url, postData ) { OmnibugUrl.prototype = (function() { var U = { hasQueryValue: function( key ) { - return typeof this.query[key] !== 'undefined'; + return typeof this.query[key] !== "undefined"; }, getFirstQueryValue: function( key ) { - return this.query[key] ? this.query[key][0] : ''; + return this.query[key] ? this.query[key][0] : ""; }, getQueryValues: function( key ) { return this.query[key] ? this.query[key] : []; @@ -29,7 +29,9 @@ OmnibugUrl.prototype = (function() { getQueryNames: function() { var i, a = []; for( i in this.query ) { - a.push( i ); + if(this.query.hasOwnProperty(i)) { + a.push( i ); + } } return a; }, @@ -50,7 +52,7 @@ OmnibugUrl.prototype = (function() { decode: function( val ) { var retVal = val; try { - retVal = val ? decodeURIComponent( val.replace( /\+/g, "%20" ) ) : val === 0 ? val : ''; + retVal = val ? decodeURIComponent( val.replace( /\+/g, "%20" ) ) : val === 0 ? val : ""; } catch( e ) { try { retVal = unescape( val.replace( /\+/g, "%20" ) ); @@ -94,17 +96,17 @@ OmnibugUrl.prototype = (function() { } var pieces = this.smartSplit( url, sep, 1 ); - var p2 = pieces[0].split( ';' ); + var p2 = pieces[0].split( ";" ); this.query = {}; - this.queryString = ''; - this.anchor = ''; + this.queryString = ""; + this.anchor = ""; this.location = p2[0]; - this.paramString = ( p2[1] ? p2[1] : '' ); + this.paramString = ( p2[1] ? p2[1] : ""); if( pieces[1] ) { - var p3 = pieces[1].split( '#' ); + var p3 = pieces[1].split( "#" ); this.queryString = p3[0]; - this.anchor = ( p3[1] ? p3[1] : '' ); + this.anchor = ( p3[1] ? p3[1] : "" ); } var kvPairs = [], @@ -113,18 +115,18 @@ OmnibugUrl.prototype = (function() { kv = []; if( this.queryString ) { - var kvSep = ( this.queryString.indexOf( "&" ) != -1 ? "&" : ";" ); + var kvSep = ( this.queryString.indexOf( "&" ) !== -1 ? "&" : ";" ); kvPairs = this.queryString.split( kvSep ); for( i=0, l=kvPairs.length; i -1 ) { - cb.checked = true; - } - - cb.value = prov; - cb.addEventListener( "change", saveOptions ); - - var lbl = document.createElement( "label" ); - lbl.appendChild( cb ); - lbl.appendChild( document.createTextNode( OmnibugProvider[prov].name ) ); - - if( ++i <= halfway ) { - leftCol.appendChild( lbl ); - } else { - rightCol.appendChild( lbl ); - } - - cont.appendChild( leftCol ); - cont.appendChild( rightCol ); - } ); - } - } - - /** - * Update a color's "example" text with new color - */ - function updateExampleColor( elem, value ) { - var parentNode = elem.parentNode, - example = parentNode.querySelector( "span" ); - if( !! example ) { - example.style.backgroundColor = value; - } - } - - /** - * Restore state of options elements from prefs - */ - function restoreOptions( prefData ) { - var prefs = that.prefs = prefData.omnibug; - - for( var key in prefs ) { - if( prefs.hasOwnProperty( key ) ) { - var elem = document.querySelector( "#" + key ); - if( !! elem ) { - if( elem.type === "text" ) { - elem.value = prefs[key]; - - if( key.substring( 0, 6 ) === "color_" ) { - updateExampleColor( elem, prefs[key] ); - elem.addEventListener( "input", function( e ) { - updateExampleColor( e.target, e.target.value ); - saveOptions(); - } ); - } - - } else if( elem.type === "radio" ) { - setRadioButton( key, prefs[key] ); - } else if( elem.type === "hidden" ) { - var dataUse = elem.getAttribute( "data-use" ); - if( dataUse === "list" ) { - makeHiddenList( key, prefs[key], elem ); - } else if( dataUse === "checkbox" ) { - makeCheckboxList( key, prefs[key], elem ); - } - } else { - console.error( "Unknown options element type ", elem.type, " for option ", key ); - } - } - } - } - } - - // load prefs and update the HTML - document.addEventListener( 'DOMContentLoaded', function() { - chrome.storage.local.get( "omnibug", restoreOptions ); - } ); - -}() ); - diff --git a/platform/chromium/providers.js b/platform/chromium/providers.js index f541cbc5..184745fd 100644 --- a/platform/chromium/providers.js +++ b/platform/chromium/providers.js @@ -67,7 +67,7 @@ var OmnibugProvider = { handleQueryParam: function( name, value, rv, raw ) { return false; } - } + }; }, @@ -86,7 +86,6 @@ var OmnibugProvider = { , d_cb: "Callback property" }, handleQueryParam: function( name, value, rv, raw ) { - var _name; if( name in this.keys ) { rv[this.key] = rv[this.key] || {}; rv[this.key][this.name] = rv[this.key][this.name] || {}; @@ -114,7 +113,7 @@ var OmnibugProvider = { , keys: { }, handleQueryParam: function( name, value, rv, raw ) { - if( name == "data" ) { + if( name === "data" ) { var obj = atob( value ); try { var parsed = JSON.parse( obj ); @@ -249,7 +248,7 @@ var OmnibugProvider = { , keys: { }, handleQueryParam: function( name, value, rv, raw ) { - if( name == "perf" ) { + if( name === "perf" ) { try { var parsed = JSON.parse( value ); for( var k in parsed ) { @@ -417,7 +416,7 @@ var OmnibugProvider = { }, handleCustom: function( url, rv, raw ) { var matches = url.match( /\/([^\/]+)\/mbox\/([^\/\?]+)/ ); - if(matches !== null && matches.length == 3) { + if(matches !== null && matches.length === 3) { rv[this.key] = rv[this.key] || {}; rv[this.key][this.name] = rv[this.key][this.name] || {}; rv[this.key][this.name]["clientCode"] = matches[1]; @@ -557,14 +556,14 @@ var OmnibugProvider = { var _name; if( name.match( /^c(\d+)$/ ) || name.match( /^prop(\d+)$/i ) ) { // props - _name = "Custom Traffic Variables" + _name = "Custom Traffic Variables"; rv[this.key] = rv[this.key] || {}; rv[this.key][_name] = rv[this.key][_name] || {}; rv[this.key][_name]["prop"+RegExp.$1] = value; raw["prop"+RegExp.$1] = value; } else if( name.match( /^v(\d+)$/ ) || name.match( /^evar(\d+)$/i ) ) { // eVars - _name = "Conversion Variables" + _name = "Conversion Variables"; rv[this.key] = rv[this.key] || {}; rv[this.key][_name] = rv[this.key][_name] || {}; rv[this.key][_name]["eVar"+RegExp.$1] = value; @@ -603,7 +602,6 @@ var OmnibugProvider = { , callback: "Callback property" }, handleQueryParam: function( name, value, rv, raw ) { - var _name; if( name in this.keys ) { rv[this.key] = rv[this.key] || {}; rv[this.key][this.name] = rv[this.key][this.name] || {}; @@ -785,52 +783,52 @@ var OmnibugProvider = { handleQueryParam: function( name, value, rv, raw ) { var _name; if( name.match( /^rg(\d+)$/ ) ) { - _name = "Registration Tag Attributes" + _name = "Registration Tag Attributes"; rv[this.key] = rv[this.key] || {}; rv[this.key][_name] = rv[this.key][_name] || {}; rv[this.key][_name]["rg"+RegExp.$1] = value; } else if( name.match( /^c_a(\d+)$/ ) ) { - _name = "Conversion Event Tag Attributes" + _name = "Conversion Event Tag Attributes"; rv[this.key] = rv[this.key] || {}; rv[this.key][_name] = rv[this.key][_name] || {}; rv[this.key][_name]["c_a"+RegExp.$1] = value; } else if( name.match( /^cm_mmca(\d+)$/ ) ) { - _name = "Marketing Tags" + _name = "Marketing Tags"; rv[this.key] = rv[this.key] || {}; rv[this.key][_name] = rv[this.key][_name] || {}; rv[this.key][_name]["cm_mmca"+RegExp.$1] = value; } else if( name.match( /^cx(\d+)$/ ) ) { - _name = "Conversion Events" + _name = "Conversion Events"; rv[this.key] = rv[this.key] || {}; rv[this.key][_name] = rv[this.key][_name] || {}; rv[this.key][_name]["cx"+RegExp.$1] = value; } else if( name.match( /^e_a(\d+)$/ ) ) { - _name = "Element Tag Attributes" + _name = "Element Tag Attributes"; rv[this.key] = rv[this.key] || {}; rv[this.key][_name] = rv[this.key][_name] || {}; rv[this.key][_name]["e_a"+RegExp.$1] = value; } else if( name.match( /^np(\d+)$/ ) ) { - _name = "Technical Browser Properties" + _name = "Technical Browser Properties"; rv[this.key] = rv[this.key] || {}; rv[this.key][_name] = rv[this.key][_name] || {}; rv[this.key][_name]["np"+RegExp.$1] = unescape( value ); } else if( name.match( /^o_a(\d+)$/ ) ) { - _name = "Order Tag Attributes" + _name = "Order Tag Attributes"; rv[this.key] = rv[this.key] || {}; rv[this.key][_name] = rv[this.key][_name] || {}; rv[this.key][_name]["o_a"+RegExp.$1] = value; } else if( name.match( /^pr_a(\d+)$/ ) ) { - _name = "Product View Tag Attributes" + _name = "Product View Tag Attributes"; rv[this.key] = rv[this.key] || {}; rv[this.key][_name] = rv[this.key][_name] || {}; rv[this.key][_name]["pr_a"+RegExp.$1] = value; } else if( name.match( /^pv_a(\d+)$/ ) ) { - _name = "Page View Tag Attributes" + _name = "Page View Tag Attributes"; rv[this.key] = rv[this.key] || {}; rv[this.key][_name] = rv[this.key][_name] || {}; rv[this.key][_name]["pv_a"+RegExp.$1] = value; } else if( name.match( /^s_a(\d+)$/ ) ) { - _name = "Shop Tag Attributes" + _name = "Shop Tag Attributes"; rv[this.key] = rv[this.key] || {}; rv[this.key][_name] = rv[this.key][_name] || {}; rv[this.key][_name]["s_a"+RegExp.$1] = value; @@ -1007,7 +1005,6 @@ var OmnibugProvider = { u: "Time" }, handleQueryParam: function( name, value, rv, raw ) { - var match; if ( name in this.keys ) { rv[this.key] = rv[this.key] || {}; rv[this.key][this.name] = rv[this.key][this.name] || {}; @@ -1020,11 +1017,11 @@ var OmnibugProvider = { }, UNIVERSALANALYTICS : { - key: "UNIVERSALANALYTICS" + key: "UNIVERSALANALYTICS" , name: "Universal Analytics" , pattern: /\/collect\/?\?/ , keys: { - v: "Protocol Version" + v: "Protocol Version" , tid: "Tracking ID" , aip: "Anonymize IP" , qt: "Queue Time" @@ -1074,7 +1071,10 @@ var OmnibugProvider = { , sn: "Social Network" , sa: "Social Action" , st: "Social Action Target" - , utl: "User timing label" + , utc: "User Timing Category" + , utv: "User Timing Variable Name" + , utt: "User Timing Time" + , utl: "User timing Label" , plt: "Page load time" , dns: "DNS time" , pdt: "Page download time" @@ -1083,18 +1083,99 @@ var OmnibugProvider = { , srt: "Server response time" , exd: "Exception description" , exf: "Is exception fatal?" + , ds: "Data Source" + , uid: "User ID" + , linkid: "Link ID" + , pa: "Product Action" + , tcc: "Coupon Code" + , pal: "Product Action List" + , cos: "Checkout Step" + , col: "Checkout Step Option" + , promoa: "Promotion Action" + , xid: "Content Experiment ID" + , xvar: "Content Experiment Variant" }, handleQueryParam: function( name, value, rv, raw ) { - if( name in this.keys ) { + var groupName, groupItem, lookup = {}; + if( /^cd(\d+)$/.test( name ) ) { + groupName = "Custom Dimensions"; + groupItem = "Custom Dimension "; + rv[this.key] = rv[this.key] || {}; + rv[this.key][groupName] = rv[this.key][groupName] || {}; + rv[this.key][groupName][groupItem + RegExp.$1] = value; + raw[groupItem + RegExp.$1] = value; + } else if( /^cm(\d+)$/.test( name ) ) { + groupName = "Custom Metrics"; + groupItem = "Custom Metric "; + rv[this.key] = rv[this.key] || {}; + rv[this.key][groupName] = rv[this.key][groupName] || {}; + rv[this.key][groupName][groupItem + RegExp.$1] = value; + raw[groupItem + RegExp.$1] = value; + } else if( /^cg(\d+)$/.test( name ) ) { + groupName = "Content Groups"; + groupItem = "Content Group "; + rv[this.key] = rv[this.key] || {}; + rv[this.key][groupName] = rv[this.key][groupName] || {}; + rv[this.key][groupName][groupItem + RegExp.$1] = value; + raw[groupItem + RegExp.$1] = value; + } else if( /^promo(\d+)([a-z]{2})$/.test( name ) ) { + lookup = {"id": "ID", "nm": "Name", "cr": "Creative", "ps": "Position"}; + groupName = "Promotions"; + rv[this.key] = rv[this.key] || {}; + rv[this.key][groupName] = rv[this.key][groupName] || {}; + groupItem = "Promo " + RegExp.$1 + " " + (lookup[RegExp.$2]||""); + rv[this.key][groupName][groupItem] = value; + raw[groupItem] = value; + } else if( /^pr(\d+)([a-z]{2})$/.test( name ) ) { + lookup = {"id": "ID", "nm": "Name", "br": "Brand", "ca": "Category", "va": "Variant", "pr": "Price", + "qt": "Quantity", "cc": "Coupon Code", "ps": "Position"}; + groupName = "Products"; + rv[this.key] = rv[this.key] || {}; + rv[this.key][groupName] = rv[this.key][groupName] || {}; + groupItem = "Product " + RegExp.$1 + " " + (lookup[RegExp.$2]||""); + rv[this.key][groupName][groupItem] = value; + raw[groupItem] = value; + } else if( /^pr(\d+)(cd|cm)(\d+)$/.test( name ) ) { + lookup = {"cd": "Custom Dimension", "cm": "Custom Metric"}; + groupName = "Products"; + rv[this.key] = rv[this.key] || {}; + rv[this.key][groupName] = rv[this.key][groupName] || {}; + groupItem = "Product " + RegExp.$1 + " " + (lookup[RegExp.$2]||"") + " " + RegExp.$3; + rv[this.key][groupName][groupItem] = value; + raw[groupItem] = value; + } else if( /^il(\d+)nm$/.test( name ) ) { + groupName = "Product Impressions"; + groupItem = "Impression List "; + rv[this.key] = rv[this.key] || {}; + rv[this.key][groupName] = rv[this.key][groupName] || {}; + rv[this.key][groupName][groupItem + RegExp.$1] = value; + raw[groupItem + RegExp.$1] = value; + } else if( /^il(\d+)pi(\d+)(cd|cm)(\d+)$/.test( name ) ) { + lookup = {"cd": "Custom Dimension", "cm": "Custom Metric"}; + groupName = "Product Impressions"; + rv[this.key] = rv[this.key] || {}; + rv[this.key][groupName] = rv[this.key][groupName] || {}; + groupItem = "Impression List " + RegExp.$1 + " Product " + RegExp.$2 + " " + (lookup[RegExp.$3]||"") + " " + RegExp.$4; + rv[this.key][groupName][groupItem] = value; + raw[groupItem] = value; + } else if( /^il(\d+)pi(\d+)([a-z]{2})$/.test( name ) ) { + lookup = {"id": "ID", "nm": "Name", "br": "Brand", "ca": "Category", "va": "Variant", "pr": "Price", "ps": "Position"}; + groupName = "Product Impressions"; + rv[this.key] = rv[this.key] || {}; + rv[this.key][groupName] = rv[this.key][groupName] || {}; + groupItem = "Impression List " + RegExp.$1 + " Product " + RegExp.$2 + " " + (lookup[RegExp.$3]||""); + rv[this.key][groupName][groupItem] = value; + raw[groupItem] = value; + } else if( name in this.keys ) { rv[this.key] = rv[this.key] || {}; rv[this.key][this.name] = rv[this.key][this.name] || {}; rv[this.key][this.name][name] = value; raw[name] = value; - return true; + } else { + return false; } - return false; + return true; } } - }; diff --git a/platform/edge/devtools.js b/platform/edge/devtools.js index 7508cbb3..654a1d4e 100644 --- a/platform/edge/devtools.js +++ b/platform/edge/devtools.js @@ -3,11 +3,6 @@ * Intermediary between eventPage and devTools panel * (used for message passing only) * - * This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. - * To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send - * a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, - * USA. - * */ ( function() { @@ -17,7 +12,6 @@ var panelCreated = function( panel ) { var queuedMessages = [], panelWindow, // reference to devtools_panel.html's `window` object - clearButton, port; port = browser.runtime.connect( { name: "omnibug-" + browser.devtools.inspectedWindow.tabId } ); @@ -51,25 +45,6 @@ port.postMessage( msg ); }; } ); - - /* - if(panel.createStatusBarButton) { - - // add a clear button - clearButton = panel.createStatusBarButton( "images/clear_button.png", "Clear events.", false ); - clearButton.onClicked.addListener( function() { - var tables = panelWindow.document.getElementsByTagName( "table" ); - while( tables.length > 0 ) { - for( var i=0; i { that.prefs = prefData.omnibug; var pattern = that.prefs.defaultPattern = getCurrentPattern( prefData.omnibug ); that.prefs.defaultRegex = new RegExp( that.prefs.defaultPattern ); - } ); - } + console.log('this.prefs.defaultRegex', that.prefs.defaultPattern); + }); + } /** * Receive updates when prefs change and broadcast them out */ browser.storage.onChanged.addListener( function( changes, namespace ) { + console.log('eventPage browser.storage.onChanged'); if( "omnibug" in changes ) { var newPrefs = changes["omnibug"].newValue; console.log( "Received updated prefs", newPrefs ); @@ -106,7 +105,6 @@ } } ); - /** * Return a pattern that matches the currently enabled providers */ @@ -124,15 +122,13 @@ return new RegExp( patterns.join( "|" ) ).source; } - /** * Quickly determine if a URL is a candidate for us or not */ function shouldProcess( url ) { - return url.match( this.prefs.defaultRegex ); + return this.prefs.defaultRegex.test( url ); } - /** * Callback for the onResponseStarted listener * @@ -151,15 +147,19 @@ * url: "https://0-act.channel.facebook.com/pull?cha... */ var beforeRequestCallback = function( details ) { + console.log('eventPage beforeRequestCallback', details.url); + // ignore browser:// requests and non-metrics URLs - if( details.tabId == -1 || !shouldProcess( details.url ) ) return; + if( details.tabId === -1 || !shouldProcess( details.url ) ) { + return; + } if( !( details.tabId in tabs ) ) { - /* disable this error message -- too numerous! - console.error( "Request for unknown tabId ", details.tabId ); */ return; } + console.log('eventPage beforeRequestCallback MATCH', details); + // look up provider and pass along var prov = OmnibugProvider.getProviderForUrl( details.url ); details.omnibugProvider = prov; @@ -167,23 +167,22 @@ // store the current tab's loading state into the details object details.omnibugLoading = tabs[details.tabId].loading; - browser.tabs.get( details.tabId, detailsProcessingCallbackFactory( details ) ); - }; + console.log('eventPage beforeRequestCallback MATCH AFTER', details); + browser.tabs.get( details.tabId).then((tab) => {detailsProcessingCallbackFactory(details, tab)}); + }; /** * Factory function returning a function which has access to details *and* tab */ - var detailsProcessingCallbackFactory = function( details ) { - return function( tab ) { - // save the tab's current URL into the details object - details.tabUrl = tab.url; + var detailsProcessingCallbackFactory = function( details, tab ) { + console.log('eventPage detailsProcessingCallbackFactory', details, tab); + // save the tab's current URL into the details object + details.tabUrl = tab.url; - sendToDevToolsForTab( details.tabId, { "type" : "webEvent", "payload" : decodeUrl( details ) } ); - } + sendToDevToolsForTab( details.tabId, { "type" : "webEvent", "payload" : decodeUrl( details ) } ); }; - browser.webRequest.onBeforeRequest.addListener( beforeRequestCallback, { urls: [""] }, @@ -191,7 +190,6 @@ // @TODO: filter these based on static patterns/config ? ); - /** * Return the tabId associated with a port */ @@ -199,7 +197,6 @@ return port.name.substring( port.name.indexOf( "-" ) + 1 ); } - /** * Accept connections from our devtools panels */ @@ -243,13 +240,13 @@ } ); } ); - /** * Send a message to the devtools panel on a given tab * Assumes the port is already connected */ function sendToDevToolsForTab( tabId, object ) { console.debug( "sending ", object.type, " message to tabId: ", tabId, ": ", object ); + console.log(tabs[tabId].port.postMessage); try { var payload = JSON.parse(JSON.stringify(object)); tabs[tabId].port.postMessage( payload ); @@ -308,7 +305,6 @@ return obj; } - /** * Takes a single name/value pair and delegates handling of it to the provider * Otherwise, inserts into the `other' bucket @@ -324,7 +320,6 @@ } } - /** * If the provider defines a custom URL handler, delegate to it */ @@ -334,7 +329,6 @@ } } - /** * Augments the data object with summary data * @param data the data object @@ -344,11 +338,11 @@ data["omnibug"] = {}; var eventType = ( data.state.omnibugLoading ? "load" : "click" ), - url = data.state.url, - urlLength = data.state.url.length; + url = data.state.url; // hacky: sometimes load events are being reported as click events. For Omniture, detect // the event type (pe= means a click event), and reset eventType accordingly. + // @TODO: Move this logic to the providers if( data.state.omnibugProvider.key.toUpperCase() === "OMNITURE" ) { var oldEventType = eventType; eventType = ( !!url.match( "[?&]pe=" ) ? "click" : "load" ); @@ -358,12 +352,7 @@ data.omnibug["Timestamp"] = data.state.timeStamp; data.omnibug["Provider"] = data.state.omnibugProvider.name; data.omnibug["Parent URL"] = data.state.tabUrl; - data.omnibug["Full URL"] = data.state.url - + "
(" + urlLength + " characters" - + ( urlLength > 2083 - ? ", *** too long for IE6/7! ***" - : "" ) - + ")"; + data.omnibug["Full URL"] = data.state.url; data.omnibug["Request ID"] = data.state.requestId; data.omnibug["Status Line"] = data.state.statusLine; data.omnibug["Request Type"] = data.state.type; @@ -375,5 +364,4 @@ // public return {}; -}() ); - +}() ); \ No newline at end of file diff --git a/platform/edge/manifest.json b/platform/edge/manifest.json index 4918a034..73359ead 100644 --- a/platform/edge/manifest.json +++ b/platform/edge/manifest.json @@ -31,7 +31,7 @@ "short_name": "Omnibug", "applications": { "gecko": { - "id": "{Omnibug@rosssimpson.com}" + "id": "Omnibug@rosssimpson.com" } } } \ No newline at end of file diff --git a/platform/edge/omnibugurl.js b/platform/edge/omnibugurl.js index 836114f1..7fd02644 100644 --- a/platform/edge/omnibugurl.js +++ b/platform/edge/omnibugurl.js @@ -18,10 +18,10 @@ var OmnibugUrl = function( url, postData ) { OmnibugUrl.prototype = (function() { var U = { hasQueryValue: function( key ) { - return typeof this.query[key] !== 'undefined'; + return typeof this.query[key] !== "undefined"; }, getFirstQueryValue: function( key ) { - return this.query[key] ? this.query[key][0] : ''; + return this.query[key] ? this.query[key][0] : ""; }, getQueryValues: function( key ) { return this.query[key] ? this.query[key] : []; @@ -29,7 +29,9 @@ OmnibugUrl.prototype = (function() { getQueryNames: function() { var i, a = []; for( i in this.query ) { - a.push( i ); + if(this.query.hasOwnProperty(i)) { + a.push( i ); + } } return a; }, @@ -50,7 +52,7 @@ OmnibugUrl.prototype = (function() { decode: function( val ) { var retVal = val; try { - retVal = val ? decodeURIComponent( val.replace( /\+/g, "%20" ) ) : val === 0 ? val : ''; + retVal = val ? decodeURIComponent( val.replace( /\+/g, "%20" ) ) : val === 0 ? val : ""; } catch( e ) { try { retVal = unescape( val.replace( /\+/g, "%20" ) ); @@ -94,17 +96,17 @@ OmnibugUrl.prototype = (function() { } var pieces = this.smartSplit( url, sep, 1 ); - var p2 = pieces[0].split( ';' ); + var p2 = pieces[0].split( ";" ); this.query = {}; - this.queryString = ''; - this.anchor = ''; + this.queryString = ""; + this.anchor = ""; this.location = p2[0]; - this.paramString = ( p2[1] ? p2[1] : '' ); + this.paramString = ( p2[1] ? p2[1] : ""); if( pieces[1] ) { - var p3 = pieces[1].split( '#' ); + var p3 = pieces[1].split( "#" ); this.queryString = p3[0]; - this.anchor = ( p3[1] ? p3[1] : '' ); + this.anchor = ( p3[1] ? p3[1] : "" ); } var kvPairs = [], @@ -113,18 +115,18 @@ OmnibugUrl.prototype = (function() { kv = []; if( this.queryString ) { - var kvSep = ( this.queryString.indexOf( "&" ) != -1 ? "&" : ";" ); + var kvSep = ( this.queryString.indexOf( "&" ) !== -1 ? "&" : ";" ); kvPairs = this.queryString.split( kvSep ); for( i=0, l=kvPairs.length; i 0 ) { - for( var i=0; i { that.prefs = prefData.omnibug; var pattern = that.prefs.defaultPattern = getCurrentPattern( prefData.omnibug ); that.prefs.defaultRegex = new RegExp( that.prefs.defaultPattern ); - } ); - } + console.log('this.prefs.defaultRegex', that.prefs.defaultPattern); + }); + } /** * Receive updates when prefs change and broadcast them out */ browser.storage.onChanged.addListener( function( changes, namespace ) { + console.log('eventPage browser.storage.onChanged'); if( "omnibug" in changes ) { var newPrefs = changes["omnibug"].newValue; console.log( "Received updated prefs", newPrefs ); @@ -106,7 +105,6 @@ } } ); - /** * Return a pattern that matches the currently enabled providers */ @@ -124,15 +122,13 @@ return new RegExp( patterns.join( "|" ) ).source; } - /** * Quickly determine if a URL is a candidate for us or not */ function shouldProcess( url ) { - return url.match( this.prefs.defaultRegex ); + return this.prefs.defaultRegex.test( url ); } - /** * Callback for the onResponseStarted listener * @@ -151,15 +147,19 @@ * url: "https://0-act.channel.facebook.com/pull?cha... */ var beforeRequestCallback = function( details ) { + console.log('eventPage beforeRequestCallback', details.url); + // ignore browser:// requests and non-metrics URLs - if( details.tabId == -1 || !shouldProcess( details.url ) ) return; + if( details.tabId === -1 || !shouldProcess( details.url ) ) { + return; + } if( !( details.tabId in tabs ) ) { - /* disable this error message -- too numerous! - console.error( "Request for unknown tabId ", details.tabId ); */ return; } + console.log('eventPage beforeRequestCallback MATCH', details); + // look up provider and pass along var prov = OmnibugProvider.getProviderForUrl( details.url ); details.omnibugProvider = prov; @@ -167,23 +167,22 @@ // store the current tab's loading state into the details object details.omnibugLoading = tabs[details.tabId].loading; - browser.tabs.get( details.tabId, detailsProcessingCallbackFactory( details ) ); - }; + console.log('eventPage beforeRequestCallback MATCH AFTER', details); + browser.tabs.get( details.tabId).then((tab) => {detailsProcessingCallbackFactory(details, tab)}); + }; /** * Factory function returning a function which has access to details *and* tab */ - var detailsProcessingCallbackFactory = function( details ) { - return function( tab ) { - // save the tab's current URL into the details object - details.tabUrl = tab.url; + var detailsProcessingCallbackFactory = function( details, tab ) { + console.log('eventPage detailsProcessingCallbackFactory', details, tab); + // save the tab's current URL into the details object + details.tabUrl = tab.url; - sendToDevToolsForTab( details.tabId, { "type" : "webEvent", "payload" : decodeUrl( details ) } ); - } + sendToDevToolsForTab( details.tabId, { "type" : "webEvent", "payload" : decodeUrl( details ) } ); }; - browser.webRequest.onBeforeRequest.addListener( beforeRequestCallback, { urls: [""] }, @@ -191,7 +190,6 @@ // @TODO: filter these based on static patterns/config ? ); - /** * Return the tabId associated with a port */ @@ -199,7 +197,6 @@ return port.name.substring( port.name.indexOf( "-" ) + 1 ); } - /** * Accept connections from our devtools panels */ @@ -243,13 +240,13 @@ } ); } ); - /** * Send a message to the devtools panel on a given tab * Assumes the port is already connected */ function sendToDevToolsForTab( tabId, object ) { console.debug( "sending ", object.type, " message to tabId: ", tabId, ": ", object ); + console.log(tabs[tabId].port.postMessage); try { var payload = JSON.parse(JSON.stringify(object)); tabs[tabId].port.postMessage( payload ); @@ -308,7 +305,6 @@ return obj; } - /** * Takes a single name/value pair and delegates handling of it to the provider * Otherwise, inserts into the `other' bucket @@ -324,7 +320,6 @@ } } - /** * If the provider defines a custom URL handler, delegate to it */ @@ -334,7 +329,6 @@ } } - /** * Augments the data object with summary data * @param data the data object @@ -344,11 +338,11 @@ data["omnibug"] = {}; var eventType = ( data.state.omnibugLoading ? "load" : "click" ), - url = data.state.url, - urlLength = data.state.url.length; + url = data.state.url; // hacky: sometimes load events are being reported as click events. For Omniture, detect // the event type (pe= means a click event), and reset eventType accordingly. + // @TODO: Move this logic to the providers if( data.state.omnibugProvider.key.toUpperCase() === "OMNITURE" ) { var oldEventType = eventType; eventType = ( !!url.match( "[?&]pe=" ) ? "click" : "load" ); @@ -358,12 +352,7 @@ data.omnibug["Timestamp"] = data.state.timeStamp; data.omnibug["Provider"] = data.state.omnibugProvider.name; data.omnibug["Parent URL"] = data.state.tabUrl; - data.omnibug["Full URL"] = data.state.url - + "
(" + urlLength + " characters" - + ( urlLength > 2083 - ? ", *** too long for IE6/7! ***" - : "" ) - + ")"; + data.omnibug["Full URL"] = data.state.url; data.omnibug["Request ID"] = data.state.requestId; data.omnibug["Status Line"] = data.state.statusLine; data.omnibug["Request Type"] = data.state.type; @@ -375,5 +364,4 @@ // public return {}; -}() ); - +}() ); \ No newline at end of file diff --git a/platform/firefox/manifest.json b/platform/firefox/manifest.json index aecaa98f..43b6b229 100644 --- a/platform/firefox/manifest.json +++ b/platform/firefox/manifest.json @@ -31,7 +31,7 @@ "short_name": "Omnibug", "applications": { "gecko": { - "id": "{9e00ccd0-bf33-4038-929d-833a4b8d723b}" + "id": "Omnibug@rosssimpson.com" } } } \ No newline at end of file diff --git a/platform/firefox/omnibugurl.js b/platform/firefox/omnibugurl.js index 836114f1..7fd02644 100644 --- a/platform/firefox/omnibugurl.js +++ b/platform/firefox/omnibugurl.js @@ -18,10 +18,10 @@ var OmnibugUrl = function( url, postData ) { OmnibugUrl.prototype = (function() { var U = { hasQueryValue: function( key ) { - return typeof this.query[key] !== 'undefined'; + return typeof this.query[key] !== "undefined"; }, getFirstQueryValue: function( key ) { - return this.query[key] ? this.query[key][0] : ''; + return this.query[key] ? this.query[key][0] : ""; }, getQueryValues: function( key ) { return this.query[key] ? this.query[key] : []; @@ -29,7 +29,9 @@ OmnibugUrl.prototype = (function() { getQueryNames: function() { var i, a = []; for( i in this.query ) { - a.push( i ); + if(this.query.hasOwnProperty(i)) { + a.push( i ); + } } return a; }, @@ -50,7 +52,7 @@ OmnibugUrl.prototype = (function() { decode: function( val ) { var retVal = val; try { - retVal = val ? decodeURIComponent( val.replace( /\+/g, "%20" ) ) : val === 0 ? val : ''; + retVal = val ? decodeURIComponent( val.replace( /\+/g, "%20" ) ) : val === 0 ? val : ""; } catch( e ) { try { retVal = unescape( val.replace( /\+/g, "%20" ) ); @@ -94,17 +96,17 @@ OmnibugUrl.prototype = (function() { } var pieces = this.smartSplit( url, sep, 1 ); - var p2 = pieces[0].split( ';' ); + var p2 = pieces[0].split( ";" ); this.query = {}; - this.queryString = ''; - this.anchor = ''; + this.queryString = ""; + this.anchor = ""; this.location = p2[0]; - this.paramString = ( p2[1] ? p2[1] : '' ); + this.paramString = ( p2[1] ? p2[1] : ""); if( pieces[1] ) { - var p3 = pieces[1].split( '#' ); + var p3 = pieces[1].split( "#" ); this.queryString = p3[0]; - this.anchor = ( p3[1] ? p3[1] : '' ); + this.anchor = ( p3[1] ? p3[1] : "" ); } var kvPairs = [], @@ -113,18 +115,18 @@ OmnibugUrl.prototype = (function() { kv = []; if( this.queryString ) { - var kvSep = ( this.queryString.indexOf( "&" ) != -1 ? "&" : ";" ); + var kvSep = ( this.queryString.indexOf( "&" ) !== -1 ? "&" : ";" ); kvPairs = this.queryString.split( kvSep ); for( i=0, l=kvPairs.length; i { + // NOTE: apiMetadata is associated to the content of the api-metadata.json file + // at build time by replacing the following "include" with the content of the + // JSON file. + const apiMetadata = { + "alarms": { + "clear": { + "minArgs": 0, + "maxArgs": 1 + }, + "clearAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "bookmarks": { + "create": { + "minArgs": 1, + "maxArgs": 1 + }, + "export": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getChildren": { + "minArgs": 1, + "maxArgs": 1 + }, + "getRecent": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTree": { + "minArgs": 0, + "maxArgs": 0 + }, + "getSubTree": { + "minArgs": 1, + "maxArgs": 1 + }, + "import": { + "minArgs": 0, + "maxArgs": 0 + }, + "move": { + "minArgs": 2, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeTree": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + }, + "browserAction": { + "getBadgeBackgroundColor": { + "minArgs": 1, + "maxArgs": 1 + }, + "getBadgeText": { + "minArgs": 1, + "maxArgs": 1 + }, + "getPopup": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTitle": { + "minArgs": 1, + "maxArgs": 1 + }, + "setIcon": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "commands": { + "getAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "contextMenus": { + "update": { + "minArgs": 2, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "cookies": { + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAllCookieStores": { + "minArgs": 0, + "maxArgs": 0 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "devtools": { + "inspectedWindow": { + "eval": { + "minArgs": 1, + "maxArgs": 2 + } + }, + "panels": { + "create": { + "minArgs": 3, + "maxArgs": 3, + "singleCallbackArg": true + } + } + }, + "downloads": { + "download": { + "minArgs": 1, + "maxArgs": 1 + }, + "cancel": { + "minArgs": 1, + "maxArgs": 1 + }, + "erase": { + "minArgs": 1, + "maxArgs": 1 + }, + "getFileIcon": { + "minArgs": 1, + "maxArgs": 2 + }, + "open": { + "minArgs": 1, + "maxArgs": 1 + }, + "pause": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeFile": { + "minArgs": 1, + "maxArgs": 1 + }, + "resume": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + }, + "show": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "extension": { + "isAllowedFileSchemeAccess": { + "minArgs": 0, + "maxArgs": 0 + }, + "isAllowedIncognitoAccess": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "history": { + "addUrl": { + "minArgs": 1, + "maxArgs": 1 + }, + "getVisits": { + "minArgs": 1, + "maxArgs": 1 + }, + "deleteAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "deleteRange": { + "minArgs": 1, + "maxArgs": 1 + }, + "deleteUrl": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "i18n": { + "detectLanguage": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAcceptLanguages": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "idle": { + "queryState": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "management": { + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "getSelf": { + "minArgs": 0, + "maxArgs": 0 + }, + "uninstallSelf": { + "minArgs": 0, + "maxArgs": 1 + } + }, + "notifications": { + "clear": { + "minArgs": 1, + "maxArgs": 1 + }, + "create": { + "minArgs": 1, + "maxArgs": 2 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "getPermissionLevel": { + "minArgs": 0, + "maxArgs": 0 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + }, + "pageAction": { + "getPopup": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTitle": { + "minArgs": 1, + "maxArgs": 1 + }, + "hide": { + "minArgs": 0, + "maxArgs": 0 + }, + "setIcon": { + "minArgs": 1, + "maxArgs": 1 + }, + "show": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "runtime": { + "getBackgroundPage": { + "minArgs": 0, + "maxArgs": 0 + }, + "getBrowserInfo": { + "minArgs": 0, + "maxArgs": 0 + }, + "getPlatformInfo": { + "minArgs": 0, + "maxArgs": 0 + }, + "openOptionsPage": { + "minArgs": 0, + "maxArgs": 0 + }, + "requestUpdateCheck": { + "minArgs": 0, + "maxArgs": 0 + }, + "sendMessage": { + "minArgs": 1, + "maxArgs": 3 + }, + "sendNativeMessage": { + "minArgs": 2, + "maxArgs": 2 + }, + "setUninstallURL": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "storage": { + "local": { + "clear": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "managed": { + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + } + }, + "sync": { + "clear": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + } + }, + "tabs": { + "create": { + "minArgs": 1, + "maxArgs": 1 + }, + "captureVisibleTab": { + "minArgs": 0, + "maxArgs": 2 + }, + "detectLanguage": { + "minArgs": 0, + "maxArgs": 1 + }, + "duplicate": { + "minArgs": 1, + "maxArgs": 1 + }, + "executeScript": { + "minArgs": 1, + "maxArgs": 2 + }, + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getCurrent": { + "minArgs": 0, + "maxArgs": 0 + }, + "getZoom": { + "minArgs": 0, + "maxArgs": 1 + }, + "getZoomSettings": { + "minArgs": 0, + "maxArgs": 1 + }, + "highlight": { + "minArgs": 1, + "maxArgs": 1 + }, + "insertCSS": { + "minArgs": 1, + "maxArgs": 2 + }, + "move": { + "minArgs": 2, + "maxArgs": 2 + }, + "reload": { + "minArgs": 0, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "query": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeCSS": { + "minArgs": 1, + "maxArgs": 2 + }, + "sendMessage": { + "minArgs": 2, + "maxArgs": 3 + }, + "setZoom": { + "minArgs": 1, + "maxArgs": 2 + }, + "setZoomSettings": { + "minArgs": 1, + "maxArgs": 2 + }, + "update": { + "minArgs": 1, + "maxArgs": 2 + } + }, + "webNavigation": { + "getAllFrames": { + "minArgs": 1, + "maxArgs": 1 + }, + "getFrame": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "webRequest": { + "handlerBehaviorChanged": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "windows": { + "create": { + "minArgs": 0, + "maxArgs": 1 + }, + "get": { + "minArgs": 1, + "maxArgs": 2 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 1 + }, + "getCurrent": { + "minArgs": 0, + "maxArgs": 1 + }, + "getLastFocused": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + } + }; + + if (Object.keys(apiMetadata).length === 0) { + throw new Error("api-metadata.json has not been included in browser-polyfill"); + } + + /** + * A WeakMap subclass which creates and stores a value for any key which does + * not exist when accessed, but behaves exactly as an ordinary WeakMap + * otherwise. + * + * @param {function} createItem + * A function which will be called in order to create the value for any + * key which does not exist, the first time it is accessed. The + * function receives, as its only argument, the key being created. + */ + class DefaultWeakMap extends WeakMap { + constructor(createItem, items = undefined) { + super(items); + this.createItem = createItem; + } + + get(key) { + if (!this.has(key)) { + this.set(key, this.createItem(key)); + } + + return super.get(key); + } + } + + /** + * Returns true if the given object is an object with a `then` method, and can + * therefore be assumed to behave as a Promise. + * + * @param {*} value The value to test. + * @returns {boolean} True if the value is thenable. + */ + const isThenable = value => { + return value && typeof value === "object" && typeof value.then === "function"; + }; + + /** + * Creates and returns a function which, when called, will resolve or reject + * the given promise based on how it is called: + * + * - If, when called, `chrome.runtime.lastError` contains a non-null object, + * the promise is rejected with that value. + * - If the function is called with exactly one argument, the promise is + * resolved to that value. + * - Otherwise, the promise is resolved to an array containing all of the + * function's arguments. + * + * @param {object} promise + * An object containing the resolution and rejection functions of a + * promise. + * @param {function} promise.resolve + * The promise's resolution function. + * @param {function} promise.rejection + * The promise's rejection function. + * @param {object} metadata + * Metadata about the wrapped method which has created the callback. + * @param {integer} metadata.maxResolvedArgs + * The maximum number of arguments which may be passed to the + * callback created by the wrapped async function. + * + * @returns {function} + * The generated callback function. + */ + const makeCallback = (promise, metadata) => { + return (...callbackArgs) => { + if (chrome.runtime.lastError) { + promise.reject(chrome.runtime.lastError); + } else if (metadata.singleCallbackArg || callbackArgs.length === 1) { + promise.resolve(callbackArgs[0]); + } else { + promise.resolve(callbackArgs); + } + }; + }; + + /** + * Creates a wrapper function for a method with the given name and metadata. + * + * @param {string} name + * The name of the method which is being wrapped. + * @param {object} metadata + * Metadata about the method being wrapped. + * @param {integer} metadata.minArgs + * The minimum number of arguments which must be passed to the + * function. If called with fewer than this number of arguments, the + * wrapper will raise an exception. + * @param {integer} metadata.maxArgs + * The maximum number of arguments which may be passed to the + * function. If called with more than this number of arguments, the + * wrapper will raise an exception. + * @param {integer} metadata.maxResolvedArgs + * The maximum number of arguments which may be passed to the + * callback created by the wrapped async function. + * + * @returns {function(object, ...*)} + * The generated wrapper function. + */ + const wrapAsyncFunction = (name, metadata) => { + const pluralizeArguments = numArgs => numArgs == 1 ? "argument" : "arguments"; + + return function asyncFunctionWrapper(target, ...args) { + if (args.length < metadata.minArgs) { + throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`); + } + + if (args.length > metadata.maxArgs) { + throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`); + } + + return new Promise((resolve, reject) => { + target[name](...args, makeCallback({ resolve, reject }, metadata)); + }); + }; + }; + + /** + * Wraps an existing method of the target object, so that calls to it are + * intercepted by the given wrapper function. The wrapper function receives, + * as its first argument, the original `target` object, followed by each of + * the arguments passed to the orginal method. + * + * @param {object} target + * The original target object that the wrapped method belongs to. + * @param {function} method + * The method being wrapped. This is used as the target of the Proxy + * object which is created to wrap the method. + * @param {function} wrapper + * The wrapper function which is called in place of a direct invocation + * of the wrapped method. + * + * @returns {Proxy} + * A Proxy object for the given method, which invokes the given wrapper + * method in its place. + */ + const wrapMethod = (target, method, wrapper) => { + return new Proxy(method, { + apply(targetMethod, thisObj, args) { + return wrapper.call(thisObj, target, ...args); + } + }); + }; + + let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); + + /** + * Wraps an object in a Proxy which intercepts and wraps certain methods + * based on the given `wrappers` and `metadata` objects. + * + * @param {object} target + * The target object to wrap. + * + * @param {object} [wrappers = {}] + * An object tree containing wrapper functions for special cases. Any + * function present in this object tree is called in place of the + * method in the same location in the `target` object tree. These + * wrapper methods are invoked as described in {@see wrapMethod}. + * + * @param {object} [metadata = {}] + * An object tree containing metadata used to automatically generate + * Promise-based wrapper functions for asynchronous. Any function in + * the `target` object tree which has a corresponding metadata object + * in the same location in the `metadata` tree is replaced with an + * automatically-generated wrapper function, as described in + * {@see wrapAsyncFunction} + * + * @returns {Proxy} + */ + const wrapObject = (target, wrappers = {}, metadata = {}) => { + let cache = Object.create(null); + + let handlers = { + has(target, prop) { + return prop in target || prop in cache; + }, + + get(target, prop, receiver) { + if (prop in cache) { + return cache[prop]; + } + + if (!(prop in target)) { + return undefined; + } + + let value = target[prop]; + + if (typeof value === "function") { + // This is a method on the underlying object. Check if we need to do + // any wrapping. + + if (typeof wrappers[prop] === "function") { + // We have a special-case wrapper for this method. + value = wrapMethod(target, target[prop], wrappers[prop]); + } else if (hasOwnProperty(metadata, prop)) { + // This is an async method that we have metadata for. Create a + // Promise wrapper for it. + let wrapper = wrapAsyncFunction(prop, metadata[prop]); + value = wrapMethod(target, target[prop], wrapper); + } else { + // This is a method that we don't know or care about. Return the + // original method, bound to the underlying object. + value = value.bind(target); + } + } else if (typeof value === "object" && value !== null && (hasOwnProperty(wrappers, prop) || hasOwnProperty(metadata, prop))) { + // This is an object that we need to do some wrapping for the children + // of. Create a sub-object wrapper for it with the appropriate child + // metadata. + value = wrapObject(value, wrappers[prop], metadata[prop]); + } else { + // We don't need to do any wrapping for this property, + // so just forward all access to the underlying object. + Object.defineProperty(cache, prop, { + configurable: true, + enumerable: true, + get() { + return target[prop]; + }, + set(value) { + target[prop] = value; + } + }); + + return value; + } + + cache[prop] = value; + return value; + }, + + set(target, prop, value, receiver) { + if (prop in cache) { + cache[prop] = value; + } else { + target[prop] = value; + } + return true; + }, + + defineProperty(target, prop, desc) { + return Reflect.defineProperty(cache, prop, desc); + }, + + deleteProperty(target, prop) { + return Reflect.deleteProperty(cache, prop); + } + }; + + return new Proxy(target, handlers); + }; + + /** + * Creates a set of wrapper functions for an event object, which handles + * wrapping of listener functions that those messages are passed. + * + * A single wrapper is created for each listener function, and stored in a + * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener` + * retrieve the original wrapper, so that attempts to remove a + * previously-added listener work as expected. + * + * @param {DefaultWeakMap} wrapperMap + * A DefaultWeakMap object which will create the appropriate wrapper + * for a given listener function when one does not exist, and retrieve + * an existing one when it does. + * + * @returns {object} + */ + const wrapEvent = wrapperMap => ({ + addListener(target, listener, ...args) { + target.addListener(wrapperMap.get(listener), ...args); + }, + + hasListener(target, listener) { + return target.hasListener(wrapperMap.get(listener)); + }, + + removeListener(target, listener) { + target.removeListener(wrapperMap.get(listener)); + } + }); + + const onMessageWrappers = new DefaultWeakMap(listener => { + if (typeof listener !== "function") { + return listener; + } + + /** + * Wraps a message listener function so that it may send responses based on + * its return value, rather than by returning a sentinel value and calling a + * callback. If the listener function returns a Promise, the response is + * sent when the promise either resolves or rejects. + * + * @param {*} message + * The message sent by the other end of the channel. + * @param {object} sender + * Details about the sender of the message. + * @param {function(*)} sendResponse + * A callback which, when called with an arbitrary argument, sends + * that value as a response. + * @returns {boolean} + * True if the wrapped listener returned a Promise, which will later + * yield a response. False otherwise. + */ + return function onMessage(message, sender, sendResponse) { + let result = listener(message, sender); + + if (isThenable(result)) { + result.then(sendResponse, error => { + console.error(error); + sendResponse(error); + }); + + return true; + } else if (result !== undefined) { + sendResponse(result); + } + }; + }); + + const staticWrappers = { + runtime: { + onMessage: wrapEvent(onMessageWrappers) + } + }; + + // Create a new empty object and copy the properties of the original chrome object + // to prevent a Proxy violation exception for the devtools API getter + // (which is a read-only non-configurable property on the original target). + const targetObject = Object.assign({}, chrome); + + return wrapObject(targetObject, staticWrappers, apiMetadata); + }; + + // The build process adds a UMD wrapper around this file, which makes the + // `module` variable available. + module.exports = wrapAPIs(); // eslint-disable-line no-undef + } else { + module.exports = browser; // eslint-disable-line no-undef + } +}); +//# sourceMappingURL=browser-polyfill.js.map diff --git a/src/devtools.js b/src/devtools.js index bc7d8d22..654a1d4e 100644 --- a/src/devtools.js +++ b/src/devtools.js @@ -3,11 +3,6 @@ * Intermediary between eventPage and devTools panel * (used for message passing only) * - * This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. - * To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send - * a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, - * USA. - * */ ( function() { @@ -58,9 +53,8 @@ */ browser.devtools.panels.create( "Omnibug", "images/o-32.png", - "devtools_panel.html", - panelCreated - ); + "devtools_panel.html" + ).then(panelCreated); // public return {}; diff --git a/src/eventPage.js b/src/eventPage.js index 342ee7d1..279d9366 100644 --- a/src/eventPage.js +++ b/src/eventPage.js @@ -2,12 +2,9 @@ * Omnibug * Persistent event page, running in background (controller) * - * This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. - * To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send - * a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, - * USA. - * + * https://omnibug.io */ + (function() { var prefs, tabs = {}, @@ -22,14 +19,14 @@ } browser.runtime.onInstalled.addListener( onInit ); - /** * Store preferences (on extension installation) */ function initPrefs() { + console.log('eventPage initPrefs'); var prefs = { // pattern to match in request url - defaultPattern : OmnibugProvider.getDefaultPattern().source + defaultPattern : OmnibugProvider.getDefaultPattern().source // all providers (initially) , enabledProviders : Object.keys( OmnibugProvider.getProviders() ).sort() @@ -43,54 +40,56 @@ // surround values with quotes? , showQuotes : true + // show redirected entries? + , showRedirects : false + // show full variable names? , showFullNames : true // colors - , color_load : "dbedff" - , color_click : "f1ffdb" - , color_prev : "ffd5de" - , color_quotes : "f00" - , color_hilite : "ff0" - , color_hover : "ccc" + , color_load : "dbedff" + , color_click : "f1ffdb" + , color_prev : "ffd5de" + , color_quotes : "ff0000" + , color_hilite : "ffff00" + , color_redirect: "eeeeee" + , color_hover : "cccccc" }; - browser.storage.local.set( { "omnibug" : prefs }, function() { - if( !! browser.runtime.lastError ) { - console.error( "Error setting prefs: ", browser.runtime.lastError ); - } - } ); + browser.storage.local.set( { "omnibug" : prefs }); // force a (re)load of prefs, now that they may have changed loadPrefsFromStorage( "initPrefs" ); } - /** * Browser startup callback */ browser.runtime.onStartup.addListener( function() { + console.log('eventPage browser.runtime.onStartup'); loadPrefsFromStorage( "onStartup" ); } ); - /** * Grab prefs data from storage */ function loadPrefsFromStorage( whence ) { - browser.storage.local.get( "omnibug", function( prefData ) { + console.log('eventPage loadPrefsFromStorage', whence); + chrome.storage.local.get("omnibug", (prefData) => { that.prefs = prefData.omnibug; var pattern = that.prefs.defaultPattern = getCurrentPattern( prefData.omnibug ); that.prefs.defaultRegex = new RegExp( that.prefs.defaultPattern ); - } ); - } + console.log('this.prefs.defaultRegex', that.prefs.defaultPattern); + }); + } /** * Receive updates when prefs change and broadcast them out */ browser.storage.onChanged.addListener( function( changes, namespace ) { + console.log('eventPage browser.storage.onChanged'); if( "omnibug" in changes ) { var newPrefs = changes["omnibug"].newValue; console.log( "Received updated prefs", newPrefs ); @@ -106,7 +105,6 @@ } } ); - /** * Return a pattern that matches the currently enabled providers */ @@ -124,15 +122,13 @@ return new RegExp( patterns.join( "|" ) ).source; } - /** * Quickly determine if a URL is a candidate for us or not */ function shouldProcess( url ) { - return url.match( this.prefs.defaultRegex ); + return this.prefs.defaultRegex.test( url ); } - /** * Callback for the onResponseStarted listener * @@ -151,15 +147,19 @@ * url: "https://0-act.channel.facebook.com/pull?cha... */ var beforeRequestCallback = function( details ) { + console.log('eventPage beforeRequestCallback', details.url); + // ignore browser:// requests and non-metrics URLs - if( details.tabId == -1 || !shouldProcess( details.url ) ) return; + if( details.tabId === -1 || !shouldProcess( details.url ) ) { + return; + } if( !( details.tabId in tabs ) ) { - /* disable this error message -- too numerous! - console.error( "Request for unknown tabId ", details.tabId ); */ return; } + console.log('eventPage beforeRequestCallback MATCH', details); + // look up provider and pass along var prov = OmnibugProvider.getProviderForUrl( details.url ); details.omnibugProvider = prov; @@ -167,23 +167,22 @@ // store the current tab's loading state into the details object details.omnibugLoading = tabs[details.tabId].loading; - browser.tabs.get( details.tabId, detailsProcessingCallbackFactory( details ) ); - }; + console.log('eventPage beforeRequestCallback MATCH AFTER', details); + browser.tabs.get( details.tabId).then((tab) => {detailsProcessingCallbackFactory(details, tab)}); + }; /** * Factory function returning a function which has access to details *and* tab */ - var detailsProcessingCallbackFactory = function( details ) { - return function( tab ) { - // save the tab's current URL into the details object - details.tabUrl = tab.url; + var detailsProcessingCallbackFactory = function( details, tab ) { + console.log('eventPage detailsProcessingCallbackFactory', details, tab); + // save the tab's current URL into the details object + details.tabUrl = tab.url; - sendToDevToolsForTab( details.tabId, { "type" : "webEvent", "payload" : decodeUrl( details ) } ); - } + sendToDevToolsForTab( details.tabId, { "type" : "webEvent", "payload" : decodeUrl( details ) } ); }; - browser.webRequest.onBeforeRequest.addListener( beforeRequestCallback, { urls: [""] }, @@ -191,7 +190,6 @@ // @TODO: filter these based on static patterns/config ? ); - /** * Return the tabId associated with a port */ @@ -199,7 +197,6 @@ return port.name.substring( port.name.indexOf( "-" ) + 1 ); } - /** * Accept connections from our devtools panels */ @@ -243,13 +240,13 @@ } ); } ); - /** * Send a message to the devtools panel on a given tab * Assumes the port is already connected */ function sendToDevToolsForTab( tabId, object ) { console.debug( "sending ", object.type, " message to tabId: ", tabId, ": ", object ); + console.log(tabs[tabId].port.postMessage); try { var payload = JSON.parse(JSON.stringify(object)); tabs[tabId].port.postMessage( payload ); @@ -308,7 +305,6 @@ return obj; } - /** * Takes a single name/value pair and delegates handling of it to the provider * Otherwise, inserts into the `other' bucket @@ -324,7 +320,6 @@ } } - /** * If the provider defines a custom URL handler, delegate to it */ @@ -334,7 +329,6 @@ } } - /** * Augments the data object with summary data * @param data the data object @@ -344,11 +338,11 @@ data["omnibug"] = {}; var eventType = ( data.state.omnibugLoading ? "load" : "click" ), - url = data.state.url, - urlLength = data.state.url.length; + url = data.state.url; // hacky: sometimes load events are being reported as click events. For Omniture, detect // the event type (pe= means a click event), and reset eventType accordingly. + // @TODO: Move this logic to the providers if( data.state.omnibugProvider.key.toUpperCase() === "OMNITURE" ) { var oldEventType = eventType; eventType = ( !!url.match( "[?&]pe=" ) ? "click" : "load" ); @@ -358,12 +352,7 @@ data.omnibug["Timestamp"] = data.state.timeStamp; data.omnibug["Provider"] = data.state.omnibugProvider.name; data.omnibug["Parent URL"] = data.state.tabUrl; - data.omnibug["Full URL"] = data.state.url - + "
(" + urlLength + " characters" - + ( urlLength > 2083 - ? ", *** too long for IE6/7! ***" - : "" ) - + ")"; + data.omnibug["Full URL"] = data.state.url; data.omnibug["Request ID"] = data.state.requestId; data.omnibug["Status Line"] = data.state.statusLine; data.omnibug["Request Type"] = data.state.type; @@ -375,5 +364,4 @@ // public return {}; -}() ); - +}() ); \ No newline at end of file diff --git a/src/manifest.json b/src/manifest.json index 7c13b5c1..1d7593a2 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Omnibug", - "version": "0.5.515", + "version": "0.7.0", "description": "Omnibug is a browser extension to decode and display outgoing web metrics requests.", "author": "Philip Lawrence", "icons": { diff --git a/src/options.html b/src/options.html index 7586377a..5df35782 100644 --- a/src/options.html +++ b/src/options.html @@ -1,6 +1,7 @@ Omnibug Options + @@ -140,6 +141,12 @@

Omnibug Options

Example +
+ Background color for redirect entries + # + Example +
+
Color for quotes around values # diff --git a/src/options.js b/src/options.js index 32923df8..3c573a30 100644 --- a/src/options.js +++ b/src/options.js @@ -89,11 +89,7 @@ // save the new values into local storage try { - browser.storage.local.set( { "omnibug" : prefs }, function() { - if( !! browser.runtime.lastError ) { - console.error( "Error setting prefs: ", browser.runtime.lastError ); - } - } ); + browser.storage.local.set({"omnibug" : prefs}); } catch( ex ) { console.error( "Error saving prefs: ", ex.message ); } @@ -260,7 +256,7 @@ // load prefs and update the HTML document.addEventListener( 'DOMContentLoaded', function() { - browser.storage.local.get( "omnibug", restoreOptions ); + browser.storage.local.get( "omnibug" ).then( restoreOptions ); } ); }() ); From a34006696d2e1b83436a507f7c51450a4fab88ee Mon Sep 17 00:00:00 2001 From: Philip Lawrence Date: Fri, 5 Jan 2018 20:29:15 -0700 Subject: [PATCH 002/111] Styling options page --- platform/chromium/options.html | 30 ++++++++++--------- platform/edge/options.html | 30 ++++++++++--------- platform/edge/options.js | 53 +++++++++++++++++++++------------- platform/firefox/options.html | 30 ++++++++++--------- platform/firefox/options.js | 53 +++++++++++++++++++++------------- src/options.html | 30 ++++++++++--------- src/options.js | 53 +++++++++++++++++++++------------- 7 files changed, 167 insertions(+), 112 deletions(-) diff --git a/platform/chromium/options.html b/platform/chromium/options.html index 5df35782..0aaab69f 100644 --- a/platform/chromium/options.html +++ b/platform/chromium/options.html @@ -8,7 +8,7 @@ - -
- O -

Omnibug Options

-
- -

Note: changes to preferences are saved immediately

- -
-
- Highlight parameter names -
    - - - -

    Parameter names to be highlighted

    -
    - -
    - Enabled providers - -
    - -
    - Expand entries? -
    - - -
    -
    - - -
    -

    Automatically expand events in the Omnibug panel

    -
    - -
    - Quote values? -
    - - -
    -
    - - + +
    +
    + O +

    Omnibug Options

    -

    Surround values in entries with quotes

    -
    - -
    - Parameter display style -
    - - -
    -
    - - -
    -

    Show parameter names (e.g. "sr") or their descriptions ("Screen resolution")

    -
    - -
    - Background color for page load events - - Example -
    - -
    - Background color for click events - - Example -
    - -
    - Background color for highlight - - Example -
    - -
    - Background color for row hover - - Example -
    - -
    - Background color for redirect entries - - Example -
    - -
    - Color for quotes around values - - Example -
    - -
    - Reset Defaults - -

    Use this to reset the default options if you're having troubles or just want to revert.

    -
    +

    Note: changes to preferences are saved immediately

    + +
    + +
    +
    +
    Highlight parameters
    +
    + + + + +
    +
    +

    Select parameters to highlight to easily see values.

    +
    + +
    +
    +
    Enabled providers
    +
    + +
    +
    {{ groupName }} ({{ providers.length }} available)
    + +
    + +
    +
    +
    +
    +
    +

    Select the providers (marketing / analytics tools) to be shown within Omnibug.

    +
    + +
    +
    + +
    + +
    +
    +

    Omnibug can automatically expand all entries as they appear.

    +
    + +
    + +
    + +
    + +
    +
    +

    By default, values are shown with quotes to highlight any extra whitespaces or other characters.

    +
    + +
    +
    +
    Parameter display style
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    +

    Show the raw parameter name found in the URL, or the user-friendly description for each request.

    +
    + +
    +
    +
    Page load event
    +
    + + Example +
    +
    +

    The background color for page load events.

    +
    +
    +
    +
    Click event
    +
    + + Example +
    +
    +

    The background color for click events.

    +
    + +
    +
    +
    Highlighted parameter
    +
    + + Example +
    +
    +

    The background color for parameters that are highlighted (see "Highlight parameters" setting above).

    +
    + +
    +
    +
    Row hover
    +
    + + Example +
    +
    +

    The background color for when you hover over a row.

    +
    + +
    +
    +
    Redirected event
    +
    + + Example +
    +
    +

    The background color requests that were redirected (meaning there could be multiple of the same requests).

    +
    + +
    +
    +
    Previous page
    +
    + + Example +
    +
    +

    The background color for events that happened on the last page.

    +
    + +
    +
    +
    Quotes
    +
    + + "Example" +
    +
    +

    The color for quotes that surround values (if enabled in the "Add quotes around values" setting above).

    +
    + +
    +
    +
    Reset Settings
    +
    + +
    +
    +

    If you want to reset your settings, or are having issues with Omnibug, you can click the button to reset all settings to their default state.

    +
    +
    + + + + + + + + diff --git a/src/options.js b/src/options.js index 7a2fcde1..1636bf56 100644 --- a/src/options.js +++ b/src/options.js @@ -1,329 +1,169 @@ /* globals OmnibugProvider */ (function() { - var prefs, - that = this; - - /** - * Cast values from the form to their correct type - */ - function processFormValue( key, value ) { - if( value === "true" ) { - return true; - } else if( value === "false" ) { - return false; - } else { - return value; - } + let settings = new OmnibugSettings(), + cached = {}; + window.app = null; + + let providers = OmnibugProvider.getProviders(), + availableParams = []; + for(let provider in providers) + { + if(!providers.hasOwnProperty(provider)) { continue; } + availableParams.push({ + "provider": providers[provider].name, + "parameters": Object.keys(providers[provider].keys).map((key) => { + return { + "parameter": key, + "name": providers[provider].keys[key].name, + "provider": providers[provider].key + } + }) + }); } - - /** - * Create an array from a list of li values - */ - function getVisualListValues( elem ) { - var vals = [], - list = elem.parentNode.querySelectorAll( "li" ); - - for( var key in list ) { - if( list.hasOwnProperty( key ) && list[key] instanceof HTMLLIElement ) { - vals.push( list[key].firstChild.nodeValue ); + console.log(availableParams); + + function init(loadedSettings) + { + window.settings = loadedSettings; + let highlightedKeys = []; + + loadedSettings.highlightKeys.forEach((parameter) => { + if(typeof providers[parameter.provider] === "object" && typeof providers[parameter.provider].keys[parameter.parameter] === "object") + { + highlightedKeys.push({ + "parameter": parameter.parameter, + "name": providers[parameter.provider].keys[parameter.parameter].name, + "provider": parameter.provider + }); } - } - return vals; - } - - /** - * Get the list of enabled providers - */ - function getCheckboxValues( elem ) { - var provs = [], - targ = document.querySelector( "#providers" ), - cbs = targ.querySelectorAll( "input[type='checkbox']" ); - - for( var cbIdx in cbs ) { - if( cbs.hasOwnProperty( cbIdx ) && cbs[cbIdx] instanceof HTMLInputElement ) { - var cb = cbs[cbIdx]; - if( cb.checked ) { - provs.push( cb.value ); - } + else + { + highlightedKeys.push({ + "parameter": parameter.parameter, + "name": parameter.parameter, + "provider": parameter.provider + }); } - } - return provs; - } - - function restoreDefaultOptions( ) { - var defaults = { - // pattern to match in request url - defaultPattern : OmnibugProvider.getDefaultPattern().source - - // all providers (initially) - , enabledProviders : Object.keys( OmnibugProvider.getProviders() ).sort() - - // keys to highlight - , highlightKeys : [ "pageName", "ch", "events", "products" ] - - // show entries expanded? - , alwaysExpand : false - - // surround values with quotes? - , showQuotes : true - - // show redirected entries? - , showRedirects : false - - // show full variable names? - , showFullNames : true - - // colors - , color_load : "dbedff" - , color_click : "f1ffdb" - , color_prev : "ffd5de" - , color_quotes : "ff0000" - , color_hilite : "ffff00" - , color_redirect: "eeeeee" - , color_hover : "cccccc" - }; - try { - browser.storage.local.set({"omnibug" : defaults}); - } catch( ex ) { - console.error( "Error saving prefs: ", ex.message ); - } - - restoreOptions({"omnibug" : defaults}); - } - - /** - * Save new prefs back to local storage - */ - function saveOptions( evt ) { - if( !! evt ) { - evt.preventDefault(); - } + }); - // update our prefs object with new values - var prefs = that.prefs; - for( var key in prefs ) { - if( prefs.hasOwnProperty( key ) ) { - var elem = document.querySelector( "#" + key ); - if( !! elem ) { - if( elem.type === "text" ) { - prefs[key] = elem.value; - } else if( elem.type === "radio" ) { - var active = document.querySelector( "input[type='radio'][name='" + key + "']:checked" ); - prefs[key] = processFormValue( key, active.value ); - } else if( elem.type === "hidden" ) { - var dataUse = elem.getAttribute("data-use"); - var values = ''; - if (dataUse === "list") + app = new Vue({ + "el": "#app", + "components": { + "Multiselect": VueMultiselect.Multiselect + }, + "data": { + "settings": loadedSettings, + "availableProviders": providers, + "availableParams": availableParams, + "highlightedKeys": highlightedKeys, + "providerFilter": "" + }, + "watch": { + "highlightedKeys": function(updatedParams) { + let params = []; + updatedParams.forEach((param) => { + params.push({ + "parameter": param.parameter, + "provider": param.provider + }); + }); + this.settings.highlightKeys = params; + }, + "settings": { + "handler": function(newSettings, orgSettings) { + settings.save(newSettings); + }, + "deep": true + } + }, + "computed": { + "groupedProviders": function() { + let app = this, + groups = {}; + + for(let provider in providers) + { + if(!providers.hasOwnProperty(provider) || provider === "CUSTOM") { continue; } + + let name = providers[provider].name.toLowerCase(), + filter = (this.providerFilter || "").toLowerCase(), + show = !(this.providerFilter && name.indexOf(filter) === -1); + if(!groups[providers[provider].type]) { - values = getVisualListValues(elem); - prefs[key] = values; + if(show) + { + groups[providers[provider].type] = [ + providers[provider] + ]; + } + else + { + groups[providers[provider].type] = []; + } } - else if (dataUse === "checkbox") + else if(show) { - values = getCheckboxValues(elem); - prefs[key] = values; + groups[providers[provider].type].push(providers[provider]); } - } else if( elem.type === "color" ) { - if(elem.value.charAt(0) === '#') { - prefs[key] = elem.value.substring(1); - } else { - prefs[key] = elem.value; - } - } else { - console.error( "Unknown options element type ", elem.type, " for option ", key ); } - } - } - } - // save the new values into local storage - try { - browser.storage.local.set({"omnibug" : prefs}); - } catch( ex ) { - console.error( "Error saving prefs: ", ex.message ); - } - } - - /** - * Selects the proper radio button - */ - function setRadioButton( key, value ) { - var elem = document.querySelector( "input[type='radio'][name='" + key + "'][value='" + value + "']" ); - if( !! elem ) { - elem.checked = true; - - while( elem.type !== "fieldset" ) { - elem = elem.parentNode; - } - - var buttons = elem.querySelectorAll( "input[type='radio']" ); - for( var btn in buttons ) { - if( buttons.hasOwnProperty( btn ) && buttons[btn] instanceof HTMLInputElement ) { - buttons[btn].addEventListener( "input", saveOptions ); - buttons[btn].addEventListener( "change", saveOptions ); - } - } - } - } - - /** - * Creates a list item for the visual list, with a remove button - */ - function createListItem( value ) { - var li = document.createElement( "li" ); - li.textContent = value; - var rem = document.createElement( "a" ); - rem.className = "rem"; - rem.href="#"; - rem.innerHTML = "x"; - rem.title = "Remove item"; - rem.addEventListener( "click", function( e ) { - e.preventDefault(); - var elem = e.target.parentNode; - elem.parentNode.removeChild( elem ); - saveOptions(); - } ); - li.appendChild( rem ); - return li; - } - - /** - * Creates a visual list from an array - * Also adds behavior to add new entries - */ - function makeHiddenList( key, value, elem ) { - var p = elem.parentNode; - var list = p.querySelector( "ul" ); - // Remove previous entries - while(list.firstChild) { list.removeChild(list.firstChild); } - value.forEach( function( v ) { - list.appendChild( createListItem( v ) ); - } ); - - var add = p.querySelector( "input[type='button']" ); - if( !! add ) { - add.addEventListener( "click", function( e ) { - var p = e.target.parentNode, - inp = p.querySelector( "input[type='text']" ); - - if( !! inp.value ) { - p.querySelector( "ul" ).appendChild( createListItem( inp.value ) ); - inp.value = ""; - } - saveOptions(); - } ); - } - } - - /** - * Create a set of checkboxes - * Adds behavior to clicks on the checkboxes - */ - function makeCheckboxList( key, value, elem ) { - if( key === "enabledProviders" ) { - var i = 0, - cont = elem.parentNode, - leftCol = document.createElement( "p" ), - rightCol = document.createElement( "p" ), - providers = OmnibugProvider.getProviders(), - halfway = Math.round( Object.keys( providers ).length / 2 ); - - // Remove previous entries - while(cont.firstChild) { cont.removeChild(cont.firstChild); } - - Object.keys( providers ).sort().forEach( function( prov ) { - var cb = document.createElement( "input" ); - cb.type = "checkbox"; - cb.name = "enabledProvList"; - - if( value.indexOf( prov ) > -1 ) { - cb.checked = true; - } - - cb.value = prov; - cb.addEventListener( "change", saveOptions ); - - var lbl = document.createElement( "label" ); - lbl.appendChild( cb ); - lbl.appendChild( document.createTextNode( OmnibugProvider[prov].name ) ); - - if( ++i <= halfway ) { - leftCol.appendChild( lbl ); - } else { - rightCol.appendChild( lbl ); + for(let group in groups) + { + if(!groups.hasOwnProperty(group)) { continue; } + groups[group].sort(); + } + return groups; } - - cont.appendChild( leftCol ); - cont.appendChild( rightCol ); - } ); - } - } - - /** - * Update a color's "example" text with new color - */ - function updateExampleColor( elem, value ) { - var parentNode = elem.parentNode, - example = parentNode.querySelector( "span" ); - if( !! example ) { - if(value.charAt(0) === '#') { - value = value.substring(1); - } - example.style.backgroundColor = value; - } - } - - /** - * Restore state of options elements from prefs - */ - function restoreOptions( prefData ) { - console.log('restore prefs', prefData); - var prefs = that.prefs = prefData.omnibug; - - for( var key in prefs ) { - if( prefs.hasOwnProperty( key ) ) { - var elem = document.querySelector( "#" + key ); - if( !! elem ) { - if( elem.type === "text" ) { - elem.value = prefs[key]; - } else if( elem.type === "radio" ) { - setRadioButton( key, prefs[key] ); - } else if( elem.type === "hidden" ) { - var dataUse = elem.getAttribute("data-use"); - if (dataUse === "list") - { - makeHiddenList(key, prefs[key], elem); - } - else if (dataUse === "checkbox") - { - makeCheckboxList(key, prefs[key], elem); - } - } else if( elem.type === "color" ) { - elem.value = '#' + prefs[key]; - updateExampleColor( elem, prefs[key] ); - elem.addEventListener( "input", function( e ) { - updateExampleColor( e.target, e.target.value ); - saveOptions(); - } ); + }, + "methods": { + "log": function(a) { + if(a.option && a.option.parameter === "pageName") { + console.log(a); + } + }, + "getProviderName": function(provider) { + return this.availableProviders[provider].name; + }, + "labelParameterWithName": function(parameter) { + return parameter.name + " (" + parameter.parameter + ")"; + }, + "addCustomProviderKey": function(value) { + const parameter = { + "parameter": value, + "name": value, + "provider": "CUSTOM" + }; + if(!this.availableProviders.CUSTOM) { + this.availableProviders.CUSTOM = { + "name": "Custom", + "key": parameter.provider + }; } else { - console.error( "Unknown options element type ", elem.type, " for option ", key ); + // At least 1 custom provider was added, so check to make sure we don't have a dupe + if(this.settings.highlightKeys.findIndex((param) => { + return param.parameter === parameter.parameter && param.provider === "CUSTOM"; + }) > -1) { + // Ignore it + return; + } } - } + this.highlightedKeys.push(parameter); + this.settings.highlightKeys.push({ + "parameter": parameter.parameter, + "provider": parameter.provider + }); + }, + "reset": function(event) { + event.preventDefault(); + this.settings = settings.defaults; + }, } - } + }); } - // load prefs and update the HTML document.addEventListener( 'DOMContentLoaded', function() { - browser.storage.local.get( "omnibug" ).then( restoreOptions ); - - let defaultBtn = document.getElementById('reset-defaults'); - defaultBtn.addEventListener('click', (event) => { - event.preventDefault(); - restoreDefaultOptions(); - }); - } ); - -}() ); + settings.load().then(init); + }); +}() ); \ No newline at end of file From 5767286e762e7bb5be1c88969d33446fe643dc02 Mon Sep 17 00:00:00 2001 From: Philip Lawrence Date: Sun, 4 Feb 2018 21:07:53 -0700 Subject: [PATCH 026/111] Updating contact email address --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 69f3c91e..09b0149f 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "keywords": [ "omnibug" ], - "author": "Philip Lawrence ", + "author": "Philip Lawrence ", "license": "MIT", "readmeFilename": "README.md", "bugs": { From 6faef420d7663271de18bebc5b202bcca7e086aa Mon Sep 17 00:00:00 2001 From: Philip Lawrence Date: Sat, 17 Feb 2018 15:53:08 -0700 Subject: [PATCH 027/111] Removing vue (again), in favor of vanilla js --- Gruntfile.js | 4 +- src/OmnibugSettings.js | 11 +- src/lib/pretty-checkbox.min.css | 12 - src/lib/vue-multiselect.min.css | 1 - src/lib/vue-multiselect.min.css.map | 1 - src/lib/vue-multiselect.min.js | 1 - src/lib/vue.js | 10798 -------------------------- src/lib/vue.min.js | 6 - src/manifest.json | 6 +- src/options.html | 476 -- src/options.js | 169 - src/options/options.css | 209 + src/options/options.html | 195 + src/options/options.js | 180 + 14 files changed, 591 insertions(+), 11478 deletions(-) delete mode 100644 src/lib/pretty-checkbox.min.css delete mode 100644 src/lib/vue-multiselect.min.css delete mode 100644 src/lib/vue-multiselect.min.css.map delete mode 100644 src/lib/vue-multiselect.min.js delete mode 100644 src/lib/vue.js delete mode 100644 src/lib/vue.min.js delete mode 100644 src/options.html delete mode 100644 src/options.js create mode 100644 src/options/options.css create mode 100644 src/options/options.html create mode 100644 src/options/options.js diff --git a/Gruntfile.js b/Gruntfile.js index a4a74e53..f110c45b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -224,7 +224,7 @@ module.exports = function(grunt) { grunt.registerTask("build-copy", "Copy over the source files to the build directory", function(browser) { grunt.config.requires(browser); let options = grunt.config(browser), - filesToCopy = ["eventPage.js", "providers.js", "omnibugurl.js", "options.*", "devtools*", "images/*.*"]; + filesToCopy = ["eventPage.js", "providers.js", "omnibugurl.js", "options/*.*", "devtools*", "images/*.*", "Omnibug*.js"]; if(options.usePolyfill) { filesToCopy.push("browser-polyfill.js"); } @@ -251,7 +251,7 @@ module.exports = function(grunt) { { baseFiles.push("src/browser-polyfill.js"); } - destFiles["platform/" + options.folder + "/options.js"] = baseFiles.concat(["src/options.js"]); + destFiles["platform/" + options.folder + "/options/options.js"] = baseFiles.concat(["src/options/options.js"]); destFiles["platform/" + options.folder + "/devtools.js"] = baseFiles.concat(["src/devtools.js"]); grunt.config.set("concat." + browser, { files: destFiles diff --git a/src/OmnibugSettings.js b/src/OmnibugSettings.js index 53d0d9e2..88d88780 100644 --- a/src/OmnibugSettings.js +++ b/src/OmnibugSettings.js @@ -22,7 +22,7 @@ class OmnibugSettings /** * Get default setting values * - * @return {{defaultPattern: string, enabledProviders: string[], highlightKeys: [{}], alwaysExpand: boolean, showQuotes: boolean, showRedirects: boolean, showFullNames: boolean, color_load: string, color_click: string, color_prev: string, color_quotes: string, color_hilite: string, color_redirect: string, color_hover: string}} + * @return {{defaultPattern: string, enabledProviders: string[], highlightKeys: string[], alwaysExpand: boolean, showQuotes: boolean, showRedirects: boolean, showFullNames: boolean, color_load: string, color_click: string, color_prev: string, color_quotes: string, color_hilite: string, color_redirect: string, color_hover: string}} */ get defaults() { @@ -34,12 +34,7 @@ class OmnibugSettings , enabledProviders : Object.keys( OmnibugProvider.getProviders() ).sort() // keys to highlight - , highlightKeys : [ - {"parameter": "pageName", "provider": "ADOBEANALYTICS"}, - {"parameter": "ch", "provider": "ADOBEANALYTICS"}, - {"parameter": "events", "provider": "ADOBEANALYTICS"}, - {"parameter": "products", "provider": "ADOBEANALYTICS"} - ] + , highlightKeys : ["pageName", "ch", "events", "products"] // show entries expanded? , alwaysExpand : false @@ -48,7 +43,7 @@ class OmnibugSettings , showQuotes : true // show redirected entries? - , showRedirects : false + , showRedirects : true // show full variable names? , showFullNames : true diff --git a/src/lib/pretty-checkbox.min.css b/src/lib/pretty-checkbox.min.css deleted file mode 100644 index 2f5f2b17..00000000 --- a/src/lib/pretty-checkbox.min.css +++ /dev/null @@ -1,12 +0,0 @@ -/** - * pretty-checkbox.css - * - * A pure CSS library to beautify checkbox and radio buttons - * - * Source: https://github.com/lokesh-coder/pretty-checkbox - * Demo: https://lokesh-coder.github.io/pretty-checkbox - * - * Copyright (c) 2017 Lokesh rajendran - */ - -.pretty *{box-sizing:border-box}.pretty input:not([type=checkbox]):not([type=radio]){display:none}.pretty{position:relative;display:inline-block;margin-right:1em;white-space:nowrap;line-height:1}.pretty input{position:absolute;left:0;top:0;min-width:1em;width:100%;height:100%;z-index:2;opacity:0;margin:0;padding:0;cursor:pointer}.pretty .state label{position:initial;display:inline-block;font-weight:400;margin:0;text-indent:1.5em;min-width:calc(1em + 2px)}.pretty .state label:after,.pretty .state label:before{content:'';width:calc(1em + 2px);height:calc(1em + 2px);display:block;box-sizing:border-box;border-radius:0;border:1px solid transparent;z-index:0;position:absolute;left:0;top:calc((0% - (100% - 1em)) - 8%);background-color:transparent}.pretty .state label:before{border-color:#bdc3c7}.pretty .state.p-is-hover,.pretty .state.p-is-indeterminate{display:none}@-webkit-keyframes zoom{0%{opacity:0;-webkit-transform:scale(0);transform:scale(0)}}@keyframes zoom{0%{opacity:0;-webkit-transform:scale(0);transform:scale(0)}}@-webkit-keyframes tada{0%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0;-webkit-transform:scale(7);transform:scale(7)}38%{-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out;opacity:1;-webkit-transform:scale(1);transform:scale(1)}55%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;-webkit-transform:scale(1.5);transform:scale(1.5)}72%{-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out;-webkit-transform:scale(1);transform:scale(1)}81%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;-webkit-transform:scale(1.24);transform:scale(1.24)}89%{-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out;-webkit-transform:scale(1);transform:scale(1)}95%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;-webkit-transform:scale(1.04);transform:scale(1.04)}100%{-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out;-webkit-transform:scale(1);transform:scale(1)}}@keyframes tada{0%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0;-webkit-transform:scale(7);transform:scale(7)}38%{-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out;opacity:1;-webkit-transform:scale(1);transform:scale(1)}55%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;-webkit-transform:scale(1.5);transform:scale(1.5)}72%{-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out;-webkit-transform:scale(1);transform:scale(1)}81%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;-webkit-transform:scale(1.24);transform:scale(1.24)}89%{-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out;-webkit-transform:scale(1);transform:scale(1)}95%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;-webkit-transform:scale(1.04);transform:scale(1.04)}100%{-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out;-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes jelly{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}40%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}50%{-webkit-transform:scale3d(.85,1.15,1);transform:scale3d(.85,1.15,1)}65%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}75%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes jelly{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}40%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}50%{-webkit-transform:scale3d(.85,1.15,1);transform:scale3d(.85,1.15,1)}65%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}75%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@-webkit-keyframes rotate{0%{opacity:0;-webkit-transform:translateZ(-200px) rotate(-45deg);transform:translateZ(-200px) rotate(-45deg)}100%{opacity:1;-webkit-transform:translateZ(0) rotate(0);transform:translateZ(0) rotate(0)}}@keyframes rotate{0%{opacity:0;-webkit-transform:translateZ(-200px) rotate(-45deg);transform:translateZ(-200px) rotate(-45deg)}100%{opacity:1;-webkit-transform:translateZ(0) rotate(0);transform:translateZ(0) rotate(0)}}@-webkit-keyframes pulse{0%{box-shadow:0 0 0 0 #bdc3c7}100%{box-shadow:0 0 0 1.5em rgba(189,195,199,0)}}@keyframes pulse{0%{box-shadow:0 0 0 0 #bdc3c7}100%{box-shadow:0 0 0 1.5em rgba(189,195,199,0)}}.pretty.p-default.p-fill .state label:after{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}.pretty.p-default .state label:after{-webkit-transform:scale(.6);-ms-transform:scale(.6);transform:scale(.6)}.pretty.p-default input:checked~.state label:after{background-color:#bdc3c7!important}.pretty.p-default.p-thick .state label:after,.pretty.p-default.p-thick .state label:before{border-width:calc(1em / 7)}.pretty.p-default.p-thick .state label:after{-webkit-transform:scale(.4)!important;-ms-transform:scale(.4)!important;transform:scale(.4)!important}.pretty.p-icon .state .icon{position:absolute;font-size:1em;width:calc(1em + 2px);height:calc(1em + 2px);left:0;z-index:1;text-align:center;line-height:normal;top:calc((0% - (100% - 1em)) - 8%);border:1px solid transparent;opacity:0}.pretty.p-icon .state .icon:before{margin:0;width:100%;height:100%;text-align:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1;flex:1;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;line-height:1}.pretty.p-icon input:checked~.state .icon{opacity:1}.pretty.p-icon input:checked~.state label:before{border-color:#5a656b}.pretty.p-svg .state .svg{position:absolute;font-size:1em;width:calc(1em + 2px);height:calc(1em + 2px);left:0;z-index:1;text-align:center;line-height:normal;top:calc((0% - (100% - 1em)) - 8%);border:1px solid transparent;opacity:0}.pretty.p-svg .state svg{margin:0;width:100%;height:100%;text-align:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1;flex:1;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;line-height:1}.pretty.p-svg input:checked~.state .svg{opacity:1}.pretty.p-image .state img{opacity:0;position:absolute;width:calc(1em + 2px);height:calc(1em + 2px);top:0;top:calc((0% - (100% - 1em)) - 8%);left:0;z-index:0;text-align:center;line-height:normal;-webkit-transform:scale(.8);-ms-transform:scale(.8);transform:scale(.8)}.pretty.p-image input:checked~.state img{opacity:1}.pretty.p-switch input{min-width:2em}.pretty.p-switch .state{position:relative}.pretty.p-switch .state:before{content:'';border:1px solid #bdc3c7;border-radius:60px;width:2em;box-sizing:unset;height:calc(1em + 2px);position:absolute;top:0;top:calc((0% - (100% - 1em)) - 16%);z-index:0;transition:all .5s ease}.pretty.p-switch .state label{text-indent:2.5em}.pretty.p-switch .state label:after,.pretty.p-switch .state label:before{transition:all .5s ease;border-radius:100%;left:0;border-color:transparent;-webkit-transform:scale(.8);-ms-transform:scale(.8);transform:scale(.8)}.pretty.p-switch .state label:after{background-color:#bdc3c7!important}.pretty.p-switch input:checked~.state:before{border-color:#5a656b}.pretty.p-switch input:checked~.state label:before{opacity:0}.pretty.p-switch input:checked~.state label:after{background-color:#5a656b!important;left:1em}.pretty.p-switch.p-fill input:checked~.state:before{border-color:#5a656b;background-color:#5a656b!important}.pretty.p-switch.p-fill input:checked~.state label:before{opacity:0}.pretty.p-switch.p-fill input:checked~.state label:after{background-color:#fff!important;left:1em}.pretty.p-switch.p-slim .state:before{height:.1em;background:#bdc3c7!important;top:calc(50% - .1em)}.pretty.p-switch.p-slim input:checked~.state:before{border-color:#5a656b;background-color:#5a656b!important}.pretty.p-has-hover input:hover~.state:not(.p-is-hover){display:none}.pretty.p-has-hover input:hover~.state.p-is-hover{display:block}.pretty.p-has-hover input:hover~.state.p-is-hover .icon{display:block}.pretty.p-has-focus input:focus~.state label:before{box-shadow:0 0 3px 0 #bdc3c7}.pretty.p-has-indeterminate input[type=checkbox]:indeterminate~.state:not(.p-is-indeterminate){display:none}.pretty.p-has-indeterminate input[type=checkbox]:indeterminate~.state.p-is-indeterminate{display:block}.pretty.p-has-indeterminate input[type=checkbox]:indeterminate~.state.p-is-indeterminate .icon{display:block;opacity:1}.pretty.p-toggle .state.p-on{opacity:0;display:none}.pretty.p-toggle .state .icon,.pretty.p-toggle .state .svg,.pretty.p-toggle .state img,.pretty.p-toggle .state.p-off{opacity:1;display:inherit}.pretty.p-toggle .state.p-off .icon{color:#bdc3c7}.pretty.p-toggle input:checked~.state.p-on{opacity:1;display:inherit}.pretty.p-toggle input:checked~.state.p-off{opacity:0;display:none}.pretty.p-plain input:checked~.state label:before,.pretty.p-plain.p-toggle .state label:before{content:none}.pretty.p-plain.p-plain .icon{-webkit-transform:scale(1.1);-ms-transform:scale(1.1);transform:scale(1.1)}.pretty.p-round .state label:after,.pretty.p-round .state label:before{border-radius:100%}.pretty.p-round.p-icon .state .icon{border-radius:100%;overflow:hidden}.pretty.p-round.p-icon .state .icon:before{-webkit-transform:scale(.8);-ms-transform:scale(.8);transform:scale(.8)}.pretty.p-curve .state label:after,.pretty.p-curve .state label:before{border-radius:20%}.pretty.p-smooth .icon,.pretty.p-smooth .svg,.pretty.p-smooth label:after,.pretty.p-smooth label:before{transition:all .5s ease}.pretty.p-smooth input:checked+.state label:after{transition:all .3s ease}.pretty.p-smooth input:checked+.state .icon,.pretty.p-smooth input:checked+.state .svg,.pretty.p-smooth input:checked+.state img{-webkit-animation:zoom .2s ease;animation:zoom .2s ease}.pretty.p-smooth.p-default input:checked+.state label:after{-webkit-animation:zoom .2s ease;animation:zoom .2s ease}.pretty.p-smooth.p-plain input:checked+.state label:before{content:'';-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);transition:all .5s ease}.pretty.p-tada:not(.p-default) input:checked+.state .icon,.pretty.p-tada:not(.p-default) input:checked+.state .svg,.pretty.p-tada:not(.p-default) input:checked+.state img,.pretty.p-tada:not(.p-default) input:checked+.state label:after,.pretty.p-tada:not(.p-default) input:checked+.state label:before{-webkit-animation:tada .7s cubic-bezier(.25,.46,.45,.94) 1 alternate;animation:tada .7s cubic-bezier(.25,.46,.45,.94) 1 alternate;opacity:1}.pretty.p-jelly:not(.p-default) input:checked+.state .icon,.pretty.p-jelly:not(.p-default) input:checked+.state .svg,.pretty.p-jelly:not(.p-default) input:checked+.state img,.pretty.p-jelly:not(.p-default) input:checked+.state label:after,.pretty.p-jelly:not(.p-default) input:checked+.state label:before{-webkit-animation:jelly .7s cubic-bezier(.25,.46,.45,.94);animation:jelly .7s cubic-bezier(.25,.46,.45,.94);opacity:1}.pretty.p-jelly:not(.p-default) input:checked+.state label:before{border-color:transparent}.pretty.p-rotate:not(.p-default) input:checked~.state .icon,.pretty.p-rotate:not(.p-default) input:checked~.state .svg,.pretty.p-rotate:not(.p-default) input:checked~.state img,.pretty.p-rotate:not(.p-default) input:checked~.state label:after,.pretty.p-rotate:not(.p-default) input:checked~.state label:before{-webkit-animation:rotate .7s cubic-bezier(.25,.46,.45,.94);animation:rotate .7s cubic-bezier(.25,.46,.45,.94);opacity:1}.pretty.p-rotate:not(.p-default) input:checked~.state label:before{border-color:transparent}.pretty.p-pulse:not(.p-switch) input:checked~.state label:before{-webkit-animation:pulse 1s;animation:pulse 1s}.pretty input[disabled]{cursor:not-allowed;display:none}.pretty input[disabled]~*{opacity:.5}.pretty.p-locked input{display:none;cursor:not-allowed}.pretty input:checked~.state.p-primary label:after,.pretty.p-toggle .state.p-primary label:after{background-color:#428bca!important}.pretty input:checked~.state.p-primary .icon,.pretty input:checked~.state.p-primary .svg,.pretty.p-toggle .state.p-primary .icon,.pretty.p-toggle .state.p-primary .svg{color:#fff;stroke:#fff}.pretty input:checked~.state.p-primary-o label:before,.pretty.p-toggle .state.p-primary-o label:before{border-color:#428bca}.pretty input:checked~.state.p-primary-o label:after,.pretty.p-toggle .state.p-primary-o label:after{background-color:transparent}.pretty input:checked~.state.p-primary-o .icon,.pretty input:checked~.state.p-primary-o .svg,.pretty input:checked~.state.p-primary-o svg,.pretty.p-toggle .state.p-primary-o .icon,.pretty.p-toggle .state.p-primary-o .svg,.pretty.p-toggle .state.p-primary-o svg{color:#428bca;stroke:#428bca}.pretty.p-default:not(.p-fill) input:checked~.state.p-primary-o label:after{background-color:#428bca!important}.pretty.p-switch input:checked~.state.p-primary:before{border-color:#428bca}.pretty.p-switch.p-fill input:checked~.state.p-primary:before{background-color:#428bca!important}.pretty.p-switch.p-slim input:checked~.state.p-primary:before{border-color:#245682;background-color:#245682!important}.pretty input:checked~.state.p-info label:after,.pretty.p-toggle .state.p-info label:after{background-color:#5bc0de!important}.pretty input:checked~.state.p-info .icon,.pretty input:checked~.state.p-info .svg,.pretty.p-toggle .state.p-info .icon,.pretty.p-toggle .state.p-info .svg{color:#fff;stroke:#fff}.pretty input:checked~.state.p-info-o label:before,.pretty.p-toggle .state.p-info-o label:before{border-color:#5bc0de}.pretty input:checked~.state.p-info-o label:after,.pretty.p-toggle .state.p-info-o label:after{background-color:transparent}.pretty input:checked~.state.p-info-o .icon,.pretty input:checked~.state.p-info-o .svg,.pretty input:checked~.state.p-info-o svg,.pretty.p-toggle .state.p-info-o .icon,.pretty.p-toggle .state.p-info-o .svg,.pretty.p-toggle .state.p-info-o svg{color:#5bc0de;stroke:#5bc0de}.pretty.p-default:not(.p-fill) input:checked~.state.p-info-o label:after{background-color:#5bc0de!important}.pretty.p-switch input:checked~.state.p-info:before{border-color:#5bc0de}.pretty.p-switch.p-fill input:checked~.state.p-info:before{background-color:#5bc0de!important}.pretty.p-switch.p-slim input:checked~.state.p-info:before{border-color:#2390b0;background-color:#2390b0!important}.pretty input:checked~.state.p-success label:after,.pretty.p-toggle .state.p-success label:after{background-color:#5cb85c!important}.pretty input:checked~.state.p-success .icon,.pretty input:checked~.state.p-success .svg,.pretty.p-toggle .state.p-success .icon,.pretty.p-toggle .state.p-success .svg{color:#fff;stroke:#fff}.pretty input:checked~.state.p-success-o label:before,.pretty.p-toggle .state.p-success-o label:before{border-color:#5cb85c}.pretty input:checked~.state.p-success-o label:after,.pretty.p-toggle .state.p-success-o label:after{background-color:transparent}.pretty input:checked~.state.p-success-o .icon,.pretty input:checked~.state.p-success-o .svg,.pretty input:checked~.state.p-success-o svg,.pretty.p-toggle .state.p-success-o .icon,.pretty.p-toggle .state.p-success-o .svg,.pretty.p-toggle .state.p-success-o svg{color:#5cb85c;stroke:#5cb85c}.pretty.p-default:not(.p-fill) input:checked~.state.p-success-o label:after{background-color:#5cb85c!important}.pretty.p-switch input:checked~.state.p-success:before{border-color:#5cb85c}.pretty.p-switch.p-fill input:checked~.state.p-success:before{background-color:#5cb85c!important}.pretty.p-switch.p-slim input:checked~.state.p-success:before{border-color:#357935;background-color:#357935!important}.pretty input:checked~.state.p-warning label:after,.pretty.p-toggle .state.p-warning label:after{background-color:#f0ad4e!important}.pretty input:checked~.state.p-warning .icon,.pretty input:checked~.state.p-warning .svg,.pretty.p-toggle .state.p-warning .icon,.pretty.p-toggle .state.p-warning .svg{color:#fff;stroke:#fff}.pretty input:checked~.state.p-warning-o label:before,.pretty.p-toggle .state.p-warning-o label:before{border-color:#f0ad4e}.pretty input:checked~.state.p-warning-o label:after,.pretty.p-toggle .state.p-warning-o label:after{background-color:transparent}.pretty input:checked~.state.p-warning-o .icon,.pretty input:checked~.state.p-warning-o .svg,.pretty input:checked~.state.p-warning-o svg,.pretty.p-toggle .state.p-warning-o .icon,.pretty.p-toggle .state.p-warning-o .svg,.pretty.p-toggle .state.p-warning-o svg{color:#f0ad4e;stroke:#f0ad4e}.pretty.p-default:not(.p-fill) input:checked~.state.p-warning-o label:after{background-color:#f0ad4e!important}.pretty.p-switch input:checked~.state.p-warning:before{border-color:#f0ad4e}.pretty.p-switch.p-fill input:checked~.state.p-warning:before{background-color:#f0ad4e!important}.pretty.p-switch.p-slim input:checked~.state.p-warning:before{border-color:#c77c11;background-color:#c77c11!important}.pretty input:checked~.state.p-danger label:after,.pretty.p-toggle .state.p-danger label:after{background-color:#d9534f!important}.pretty input:checked~.state.p-danger .icon,.pretty input:checked~.state.p-danger .svg,.pretty.p-toggle .state.p-danger .icon,.pretty.p-toggle .state.p-danger .svg{color:#fff;stroke:#fff}.pretty input:checked~.state.p-danger-o label:before,.pretty.p-toggle .state.p-danger-o label:before{border-color:#d9534f}.pretty input:checked~.state.p-danger-o label:after,.pretty.p-toggle .state.p-danger-o label:after{background-color:transparent}.pretty input:checked~.state.p-danger-o .icon,.pretty input:checked~.state.p-danger-o .svg,.pretty input:checked~.state.p-danger-o svg,.pretty.p-toggle .state.p-danger-o .icon,.pretty.p-toggle .state.p-danger-o .svg,.pretty.p-toggle .state.p-danger-o svg{color:#d9534f;stroke:#d9534f}.pretty.p-default:not(.p-fill) input:checked~.state.p-danger-o label:after{background-color:#d9534f!important}.pretty.p-switch input:checked~.state.p-danger:before{border-color:#d9534f}.pretty.p-switch.p-fill input:checked~.state.p-danger:before{background-color:#d9534f!important}.pretty.p-switch.p-slim input:checked~.state.p-danger:before{border-color:#a02622;background-color:#a02622!important}.pretty.p-bigger .icon,.pretty.p-bigger .img,.pretty.p-bigger .svg,.pretty.p-bigger label:after,.pretty.p-bigger label:before{font-size:1.2em!important;top:calc((0% - (100% - 1em)) - 35%)!important}.pretty.p-bigger label{text-indent:1.7em}@media print{.pretty .state .icon,.pretty .state label:after,.pretty .state label:before,.pretty .state:before{color-adjust:exact;-webkit-print-color-adjust:exact;print-color-adjust:exact}} \ No newline at end of file diff --git a/src/lib/vue-multiselect.min.css b/src/lib/vue-multiselect.min.css deleted file mode 100644 index 68dcb7d5..00000000 --- a/src/lib/vue-multiselect.min.css +++ /dev/null @@ -1 +0,0 @@ -fieldset[disabled] .multiselect{pointer-events:none}.multiselect__spinner{position:absolute;right:1px;top:1px;width:48px;height:35px;background:#fff;display:block}.multiselect__spinner:after,.multiselect__spinner:before{position:absolute;content:"";top:50%;left:50%;margin:-8px 0 0 -8px;width:16px;height:16px;border-radius:100%;border-color:#41b883 transparent transparent;border-style:solid;border-width:2px;box-shadow:0 0 0 1px transparent}.multiselect__spinner:before{animation:a 2.4s cubic-bezier(.41,.26,.2,.62);animation-iteration-count:infinite}.multiselect__spinner:after{animation:a 2.4s cubic-bezier(.51,.09,.21,.8);animation-iteration-count:infinite}.multiselect__loading-enter-active,.multiselect__loading-leave-active{transition:opacity .4s ease-in-out;opacity:1}.multiselect__loading-enter,.multiselect__loading-leave-active{opacity:0}.multiselect,.multiselect__input,.multiselect__single{font-family:inherit;font-size:14px;-ms-touch-action:manipulation;touch-action:manipulation}.multiselect{box-sizing:content-box;display:block;position:relative;width:100%;min-height:40px;text-align:left;color:#35495e}.multiselect *{box-sizing:border-box}.multiselect:focus{outline:none}.multiselect--disabled{opacity:.6}.multiselect--active{z-index:1}.multiselect--active:not(.multiselect--above) .multiselect__current,.multiselect--active:not(.multiselect--above) .multiselect__input,.multiselect--active:not(.multiselect--above) .multiselect__tags{border-bottom-left-radius:0;border-bottom-right-radius:0}.multiselect--active .multiselect__select{transform:rotate(180deg)}.multiselect--above.multiselect--active .multiselect__current,.multiselect--above.multiselect--active .multiselect__input,.multiselect--above.multiselect--active .multiselect__tags{border-top-left-radius:0;border-top-right-radius:0}.multiselect__input,.multiselect__single{position:relative;display:inline-block;min-height:20px;line-height:20px;border:none;border-radius:5px;background:#fff;padding:0 0 0 5px;width:100%;transition:border .1s ease;box-sizing:border-box;margin-bottom:8px;vertical-align:top}.multiselect__tag~.multiselect__input,.multiselect__tag~.multiselect__single{width:auto}.multiselect__input:hover,.multiselect__single:hover{border-color:#cfcfcf}.multiselect__input:focus,.multiselect__single:focus{border-color:#a8a8a8;outline:none}.multiselect__single{padding-left:6px;margin-bottom:8px}.multiselect__tags-wrap{display:inline}.multiselect__tags{min-height:40px;display:block;padding:8px 40px 0 8px;border-radius:5px;border:1px solid #e8e8e8;background:#fff}.multiselect__tag{position:relative;display:inline-block;padding:4px 26px 4px 10px;border-radius:5px;margin-right:10px;color:#fff;line-height:1;background:#41b883;margin-bottom:5px;white-space:nowrap;overflow:hidden;max-width:100%;text-overflow:ellipsis}.multiselect__tag-icon{cursor:pointer;margin-left:7px;position:absolute;right:0;top:0;bottom:0;font-weight:700;font-style:normal;width:22px;text-align:center;line-height:22px;transition:all .2s ease;border-radius:5px}.multiselect__tag-icon:after{content:"\D7";color:#266d4d;font-size:14px}.multiselect__tag-icon:focus,.multiselect__tag-icon:hover{background:#369a6e}.multiselect__tag-icon:focus:after,.multiselect__tag-icon:hover:after{color:#fff}.multiselect__current{min-height:40px;overflow:hidden;padding:8px 12px 0;padding-right:30px;white-space:nowrap;border-radius:5px;border:1px solid #e8e8e8}.multiselect__current,.multiselect__select{line-height:16px;box-sizing:border-box;display:block;margin:0;text-decoration:none;cursor:pointer}.multiselect__select{position:absolute;width:40px;height:38px;right:1px;top:1px;padding:4px 8px;text-align:center;transition:transform .2s ease}.multiselect__select:before{position:relative;right:0;top:65%;color:#999;margin-top:4px;border-style:solid;border-width:5px 5px 0;border-color:#999 transparent transparent;content:""}.multiselect__placeholder{color:#adadad;display:inline-block;margin-bottom:10px;padding-top:2px}.multiselect--active .multiselect__placeholder{display:none}.multiselect__content-wrapper{position:absolute;display:block;background:#fff;width:100%;max-height:240px;overflow:auto;border:1px solid #e8e8e8;border-top:none;border-bottom-left-radius:5px;border-bottom-right-radius:5px;z-index:1;-webkit-overflow-scrolling:touch}.multiselect__content{list-style:none;display:inline-block;padding:0;margin:0;min-width:100%;vertical-align:top}.multiselect--above .multiselect__content-wrapper{bottom:100%;border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:5px;border-top-right-radius:5px;border-bottom:none;border-top:1px solid #e8e8e8}.multiselect__content::webkit-scrollbar{display:none}.multiselect__element{display:block}.multiselect__option{display:block;padding:12px;min-height:40px;line-height:16px;text-decoration:none;text-transform:none;vertical-align:middle;position:relative;cursor:pointer;white-space:nowrap}.multiselect__option:after{top:0;right:0;position:absolute;line-height:40px;padding-right:12px;padding-left:20px}.multiselect__option--highlight{background:#41b883;outline:none;color:#fff}.multiselect__option--highlight:after{content:attr(data-select);background:#41b883;color:#fff}.multiselect__option--selected{background:#f3f3f3;color:#35495e;font-weight:700}.multiselect__option--selected:after{content:attr(data-selected);color:silver}.multiselect__option--selected.multiselect__option--highlight{background:#ff6a6a;color:#fff}.multiselect__option--selected.multiselect__option--highlight:after{background:#ff6a6a;content:attr(data-deselect);color:#fff}.multiselect--disabled{background:#ededed;pointer-events:none}.multiselect--disabled .multiselect__current,.multiselect--disabled .multiselect__select,.multiselect__option--disabled{background:#ededed;color:#a6a6a6}.multiselect__option--disabled{cursor:text;pointer-events:none}.multiselect__option--disabled.multiselect__option--highlight{background:#dedede!important}.multiselect-enter-active,.multiselect-leave-active{transition:all .15s ease}.multiselect-enter,.multiselect-leave-active{opacity:0}.multiselect__strong{margin-bottom:8px;line-height:20px;display:inline-block;vertical-align:top}[dir=rtl] .multiselect{text-align:right}[dir=rtl] .multiselect__select{right:auto;left:1px}[dir=rtl] .multiselect__tags{padding:8px 8px 0 40px}[dir=rtl] .multiselect__content{text-align:right}[dir=rtl] .multiselect__option:after{right:auto;left:0}[dir=rtl] .multiselect__clear{right:auto;left:12px}[dir=rtl] .multiselect__spinner{right:auto;left:1px}@keyframes a{0%{transform:rotate(0)}to{transform:rotate(2turn)}} \ No newline at end of file diff --git a/src/lib/vue-multiselect.min.css.map b/src/lib/vue-multiselect.min.css.map deleted file mode 100644 index 7a0a20f1..00000000 --- a/src/lib/vue-multiselect.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///webpack:///src/Multiselect.vue"],"names":[],"mappings":";AACA;EACE,qBAAqB;CACtB;AACD;EACE,mBAAmB;EACnB,WAAW;EACX,SAAS;EACT,YAAY;EACZ,aAAa;EACb,iBAAiB;EACjB,eAAe;CAChB;AACD;;EAEE,mBAAmB;EACnB,YAAY;EACZ,SAAS;EACT,UAAU;EACV,sBAAsB;EACtB,YAAY;EACZ,aAAa;EACb,oBAAoB;EACpB,8CAA8C;EAC9C,oBAAoB;EACpB,kBAAkB;EAClB,kCAAkC;CACnC;AACD;EACE,6DAA6D;EAC7D,oCAAoC;CACrC;AACD;EACE,6DAA6D;EAC7D,oCAAoC;CACrC;AACD;;EAEE,qCAAqC;EACrC,WAAW;CACZ;AACD;;EAEE,WAAW;CACZ;AACD;;;EAGE,qBAAqB;EACrB,gBAAgB;EAChB,+BAA+B;MAC3B,2BAA2B;CAChC;AACD;EACE,wBAAwB;EACxB,eAAe;EACf,mBAAmB;EACnB,YAAY;EACZ,iBAAiB;EACjB,iBAAiB;EACjB,eAAe;CAChB;AACD;EACE,uBAAuB;CACxB;AACD;EACE,cAAc;CACf;AACD;EACE,qBAAqB;EACrB,aAAa;CACd;AACD;EACE,YAAY;CACb;AACD;;;EAGE,6BAA6B;EAC7B,8BAA8B;CAC/B;AACD;EACE,2BAA2B;CAC5B;AACD;;;EAGE,0BAA0B;EAC1B,2BAA2B;CAC5B;AACD;;EAEE,mBAAmB;EACnB,sBAAsB;EACtB,iBAAiB;EACjB,kBAAkB;EAClB,aAAa;EACb,mBAAmB;EACnB,iBAAiB;EACjB,mBAAmB;EACnB,kBAAkB;EAClB,6BAA6B;EAC7B,uBAAuB;EACvB,mBAAmB;EACnB,oBAAoB;CACrB;AACD;;EAEE,YAAY;CACb;AACD;;EAEE,sBAAsB;CACvB;AACD;;EAEE,sBAAsB;EACtB,cAAc;CACf;AACD;EACE,kBAAkB;EAClB,mBAAmB;CACpB;AACD;EACE,eAAe;CAChB;AACD;EACE,iBAAiB;EACjB,eAAe;EACf,wBAAwB;EACxB,mBAAmB;EACnB,0BAA0B;EAC1B,iBAAiB;CAClB;AACD;EACE,mBAAmB;EACnB,sBAAsB;EACtB,2BAA2B;EAC3B,mBAAmB;EACnB,mBAAmB;EACnB,YAAY;EACZ,eAAe;EACf,oBAAoB;EACpB,mBAAmB;EACnB,oBAAoB;EACpB,iBAAiB;EACjB,gBAAgB;EAChB,wBAAwB;CACzB;AACD;EACE,gBAAgB;EAChB,iBAAiB;EACjB,mBAAmB;EACnB,SAAS;EACT,OAAO;EACP,UAAU;EACV,iBAAiB;EACjB,oBAAoB;EACpB,YAAY;EACZ,mBAAmB;EACnB,kBAAkB;EAClB,0BAA0B;EAC1B,mBAAmB;CACpB;AACD;EACE,eAAa;EACb,eAAe;EACf,gBAAgB;CACjB;AACD;;EAEE,oBAAoB;CACrB;AACD;;EAEE,aAAa;CACd;AACD;EACE,kBAAkB;EAClB,iBAAiB;EACjB,uBAAuB;EACvB,eAAe;EACf,iBAAiB;EACjB,oBAAoB;EACpB,oBAAoB;EACpB,oBAAoB;EACpB,UAAU;EACV,sBAAsB;EACtB,mBAAmB;EACnB,0BAA0B;EAC1B,gBAAgB;CACjB;AACD;EACE,kBAAkB;EAClB,eAAe;EACf,mBAAmB;EACnB,uBAAuB;EACvB,YAAY;EACZ,aAAa;EACb,WAAW;EACX,SAAS;EACT,iBAAiB;EACjB,UAAU;EACV,sBAAsB;EACtB,mBAAmB;EACnB,gBAAgB;EAChB,gCAAgC;CACjC;AACD;EACE,mBAAmB;EACnB,SAAS;EACT,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,oBAAoB;EACpB,4BAA4B;EAC5B,0DAA0D;EAC1D,YAAY;CACb;AACD;EACE,eAAe;EACf,sBAAsB;EACtB,oBAAoB;EACpB,iBAAiB;CAClB;AACD;EACE,cAAc;CACf;AACD;EACE,mBAAmB;EACnB,eAAe;EACf,iBAAiB;EACjB,YAAY;EACZ,kBAAkB;EAClB,eAAe;EACf,0BAA0B;EAC1B,iBAAiB;EACjB,+BAA+B;EAC/B,gCAAgC;EAChC,YAAY;EACZ,kCAAkC;CACnC;AACD;EACE,iBAAiB;EACjB,sBAAsB;EACtB,WAAW;EACX,UAAU;EACV,gBAAgB;EAChB,oBAAoB;CACrB;AACD;EACE,aAAa;EACb,6BAA6B;EAC7B,8BAA8B;EAC9B,4BAA4B;EAC5B,6BAA6B;EAC7B,oBAAoB;EACpB,8BAA8B;CAC/B;AACD;EACE,cAAc;CACf;AACD;EACE,eAAe;CAChB;AACD;EACE,eAAe;EACf,cAAc;EACd,iBAAiB;EACjB,kBAAkB;EAClB,sBAAsB;EACtB,qBAAqB;EACrB,uBAAuB;EACvB,mBAAmB;EACnB,gBAAgB;EAChB,oBAAoB;CACrB;AACD;EACE,OAAO;EACP,SAAS;EACT,mBAAmB;EACnB,kBAAkB;EAClB,oBAAoB;EACpB,mBAAmB;CACpB;AACD;EACE,oBAAoB;EACpB,cAAc;EACd,aAAa;CACd;AACD;EACE,2BAA2B;EAC3B,oBAAoB;EACpB,aAAa;CACd;AACD;EACE,oBAAoB;EACpB,eAAe;EACf,kBAAkB;CACnB;AACD;EACE,6BAA6B;EAC7B,cAAc;CACf;AACD;EACE,oBAAoB;EACpB,YAAY;CACb;AACD;EACE,oBAAoB;EACpB,6BAA6B;EAC7B,YAAY;CACb;AACD;EACE,oBAAoB;EACpB,qBAAqB;CACtB;AACD;;EAEE,oBAAoB;EACpB,eAAe;CAChB;AACD;EACE,oBAAoB;EACpB,eAAe;EACf,aAAa;EACb,qBAAqB;CACtB;AACD;EACE,+BAA+B;CAChC;AACD;;EAEE,2BAA2B;CAC5B;AACD;;EAEE,WAAW;CACZ;AACD;EACE,mBAAmB;EACnB,kBAAkB;EAClB,sBAAsB;EACtB,oBAAoB;CACrB;AACD;IACI,kBAAkB;CACrB;AACD;IACI,YAAY;IACZ,UAAU;CACb;AACD;IACI,0BAA0B;CAC7B;AACD;IACI,kBAAkB;CACrB;AACD;IACI,YAAY;IACZ,QAAQ;CACX;AACD;IACI,YAAY;IACZ,WAAW;CACd;AACD;IACI,YAAY;IACZ,UAAU;CACb;AACD;AACA,OAAO,mBAAmB;CACzB;AACD,KAAK,uBAAuB;CAC3B;CACA","file":"vue-multiselect.min.css","sourcesContent":["\nfieldset[disabled] .multiselect {\n pointer-events: none;\n}\n.multiselect__spinner {\n position: absolute;\n right: 1px;\n top: 1px;\n width: 48px;\n height: 35px;\n background: #fff;\n display: block;\n}\n.multiselect__spinner:before,\n.multiselect__spinner:after {\n position: absolute;\n content: \"\";\n top: 50%;\n left: 50%;\n margin: -8px 0 0 -8px;\n width: 16px;\n height: 16px;\n border-radius: 100%;\n border-color: #41B883 transparent transparent;\n border-style: solid;\n border-width: 2px;\n box-shadow: 0 0 0 1px transparent;\n}\n.multiselect__spinner:before {\n animation: spinning 2.4s cubic-bezier(0.41, 0.26, 0.2, 0.62);\n animation-iteration-count: infinite;\n}\n.multiselect__spinner:after {\n animation: spinning 2.4s cubic-bezier(0.51, 0.09, 0.21, 0.8);\n animation-iteration-count: infinite;\n}\n.multiselect__loading-enter-active,\n.multiselect__loading-leave-active {\n transition: opacity 0.4s ease-in-out;\n opacity: 1;\n}\n.multiselect__loading-enter,\n.multiselect__loading-leave-active {\n opacity: 0;\n}\n.multiselect,\n.multiselect__input,\n.multiselect__single {\n font-family: inherit;\n font-size: 14px;\n -ms-touch-action: manipulation;\n touch-action: manipulation;\n}\n.multiselect {\n box-sizing: content-box;\n display: block;\n position: relative;\n width: 100%;\n min-height: 40px;\n text-align: left;\n color: #35495E;\n}\n.multiselect * {\n box-sizing: border-box;\n}\n.multiselect:focus {\n outline: none;\n}\n.multiselect--disabled {\n pointer-events: none;\n opacity: 0.6;\n}\n.multiselect--active {\n z-index: 50;\n}\n.multiselect--active:not(.multiselect--above) .multiselect__current,\n.multiselect--active:not(.multiselect--above) .multiselect__input,\n.multiselect--active:not(.multiselect--above) .multiselect__tags {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n}\n.multiselect--active .multiselect__select {\n transform: rotateZ(180deg);\n}\n.multiselect--above.multiselect--active .multiselect__current,\n.multiselect--above.multiselect--active .multiselect__input,\n.multiselect--above.multiselect--active .multiselect__tags {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.multiselect__input,\n.multiselect__single {\n position: relative;\n display: inline-block;\n min-height: 20px;\n line-height: 20px;\n border: none;\n border-radius: 5px;\n background: #fff;\n padding: 0 0 0 5px;\n width: calc(100%);\n transition: border 0.1s ease;\n box-sizing: border-box;\n margin-bottom: 8px;\n vertical-align: top;\n}\n.multiselect__tag ~ .multiselect__input,\n.multiselect__tag ~ .multiselect__single {\n width: auto;\n}\n.multiselect__input:hover,\n.multiselect__single:hover {\n border-color: #cfcfcf;\n}\n.multiselect__input:focus,\n.multiselect__single:focus {\n border-color: #a8a8a8;\n outline: none;\n}\n.multiselect__single {\n padding-left: 6px;\n margin-bottom: 8px;\n}\n.multiselect__tags-wrap {\n display: inline\n}\n.multiselect__tags {\n min-height: 40px;\n display: block;\n padding: 8px 40px 0 8px;\n border-radius: 5px;\n border: 1px solid #E8E8E8;\n background: #fff;\n}\n.multiselect__tag {\n position: relative;\n display: inline-block;\n padding: 4px 26px 4px 10px;\n border-radius: 5px;\n margin-right: 10px;\n color: #fff;\n line-height: 1;\n background: #41B883;\n margin-bottom: 5px;\n white-space: nowrap;\n overflow: hidden;\n max-width: 100%;\n text-overflow: ellipsis;\n}\n.multiselect__tag-icon {\n cursor: pointer;\n margin-left: 7px;\n position: absolute;\n right: 0;\n top: 0;\n bottom: 0;\n font-weight: 700;\n font-style: initial;\n width: 22px;\n text-align: center;\n line-height: 22px;\n transition: all 0.2s ease;\n border-radius: 5px;\n}\n.multiselect__tag-icon:after {\n content: \"×\";\n color: #266d4d;\n font-size: 14px;\n}\n.multiselect__tag-icon:focus,\n.multiselect__tag-icon:hover {\n background: #369a6e;\n}\n.multiselect__tag-icon:focus:after,\n.multiselect__tag-icon:hover:after {\n color: white;\n}\n.multiselect__current {\n line-height: 16px;\n min-height: 40px;\n box-sizing: border-box;\n display: block;\n overflow: hidden;\n padding: 8px 12px 0;\n padding-right: 30px;\n white-space: nowrap;\n margin: 0;\n text-decoration: none;\n border-radius: 5px;\n border: 1px solid #E8E8E8;\n cursor: pointer;\n}\n.multiselect__select {\n line-height: 16px;\n display: block;\n position: absolute;\n box-sizing: border-box;\n width: 40px;\n height: 38px;\n right: 1px;\n top: 1px;\n padding: 4px 8px;\n margin: 0;\n text-decoration: none;\n text-align: center;\n cursor: pointer;\n transition: transform 0.2s ease;\n}\n.multiselect__select:before {\n position: relative;\n right: 0;\n top: 65%;\n color: #999;\n margin-top: 4px;\n border-style: solid;\n border-width: 5px 5px 0 5px;\n border-color: #999999 transparent transparent transparent;\n content: \"\";\n}\n.multiselect__placeholder {\n color: #ADADAD;\n display: inline-block;\n margin-bottom: 10px;\n padding-top: 2px;\n}\n.multiselect--active .multiselect__placeholder {\n display: none;\n}\n.multiselect__content-wrapper {\n position: absolute;\n display: block;\n background: #fff;\n width: 100%;\n max-height: 240px;\n overflow: auto;\n border: 1px solid #E8E8E8;\n border-top: none;\n border-bottom-left-radius: 5px;\n border-bottom-right-radius: 5px;\n z-index: 50;\n -webkit-overflow-scrolling: touch;\n}\n.multiselect__content {\n list-style: none;\n display: inline-block;\n padding: 0;\n margin: 0;\n min-width: 100%;\n vertical-align: top;\n}\n.multiselect--above .multiselect__content-wrapper {\n bottom: 100%;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n border-top-left-radius: 5px;\n border-top-right-radius: 5px;\n border-bottom: none;\n border-top: 1px solid #E8E8E8;\n}\n.multiselect__content::webkit-scrollbar {\n display: none;\n}\n.multiselect__element {\n display: block;\n}\n.multiselect__option {\n display: block;\n padding: 12px;\n min-height: 40px;\n line-height: 16px;\n text-decoration: none;\n text-transform: none;\n vertical-align: middle;\n position: relative;\n cursor: pointer;\n white-space: nowrap;\n}\n.multiselect__option:after {\n top: 0;\n right: 0;\n position: absolute;\n line-height: 40px;\n padding-right: 12px;\n padding-left: 20px;\n}\n.multiselect__option--highlight {\n background: #41B883;\n outline: none;\n color: white;\n}\n.multiselect__option--highlight:after {\n content: attr(data-select);\n background: #41B883;\n color: white;\n}\n.multiselect__option--selected {\n background: #F3F3F3;\n color: #35495E;\n font-weight: bold;\n}\n.multiselect__option--selected:after {\n content: attr(data-selected);\n color: silver;\n}\n.multiselect__option--selected.multiselect__option--highlight {\n background: #FF6A6A;\n color: #fff;\n}\n.multiselect__option--selected.multiselect__option--highlight:after {\n background: #FF6A6A;\n content: attr(data-deselect);\n color: #fff;\n}\n.multiselect--disabled {\n background: #ededed;\n pointer-events: none;\n}\n.multiselect--disabled .multiselect__current,\n.multiselect--disabled .multiselect__select {\n background: #ededed;\n color: #a6a6a6;\n}\n.multiselect__option--disabled {\n background: #ededed;\n color: #a6a6a6;\n cursor: text;\n pointer-events: none;\n}\n.multiselect__option--disabled.multiselect__option--highlight {\n background: #dedede !important;\n}\n.multiselect-enter-active,\n.multiselect-leave-active {\n transition: all 0.15s ease;\n}\n.multiselect-enter,\n.multiselect-leave-active {\n opacity: 0;\n}\n.multiselect__strong {\n margin-bottom: 8px;\n line-height: 20px;\n display: inline-block;\n vertical-align: top;\n}\n*[dir=\"rtl\"] .multiselect {\n text-align: right;\n}\n*[dir=\"rtl\"] .multiselect__select {\n right: auto;\n left: 1px;\n}\n*[dir=\"rtl\"] .multiselect__tags {\n padding: 8px 8px 0px 40px;\n}\n*[dir=\"rtl\"] .multiselect__content {\n text-align: right;\n}\n*[dir=\"rtl\"] .multiselect__option:after {\n right: auto;\n left: 0;\n}\n*[dir=\"rtl\"] .multiselect__clear {\n right: auto;\n left: 12px;\n}\n*[dir=\"rtl\"] .multiselect__spinner {\n right: auto;\n left: 1px;\n}\n@keyframes spinning {\nfrom { transform:rotate(0)\n}\nto { transform:rotate(2turn)\n}\n}\n\n\n\n// WEBPACK FOOTER //\n// webpack:///src/Multiselect.vue"],"sourceRoot":""} \ No newline at end of file diff --git a/src/lib/vue-multiselect.min.js b/src/lib/vue-multiselect.min.js deleted file mode 100644 index de38867f..00000000 --- a/src/lib/vue-multiselect.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.VueMultiselect=t():e.VueMultiselect=t()}(this,function(){return function(e){function t(n){if(i[n])return i[n].exports;var s=i[n]={i:n,l:!1,exports:{}};return e[n].call(s.exports,s,s.exports,t),s.l=!0,s.exports}var i={};return t.m=e,t.c=i,t.i=function(e){return e},t.d=function(e,i,n){t.o(e,i)||Object.defineProperty(e,i,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var i=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(i,"a",i),i},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/",t(t.s=4)}([function(e,t,i){"use strict";function n(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function s(e){return 0!==e&&(!(!Array.isArray(e)||0!==e.length)||!e)}function o(e,t){return void 0===e&&(e="undefined"),null===e&&(e="null"),!1===e&&(e="false"),-1!==e.toString().toLowerCase().indexOf(t.trim())}function l(e,t,i,n){return e.filter(function(e){return o(n(e,i),t)})}function r(e){return e.filter(function(e){return!e.$isLabel})}function a(e,t){return function(i){return i.reduce(function(i,n){return n[e]&&n[e].length?(i.push({$groupLabel:n[t],$isLabel:!0}),i.concat(n[e])):i},[])}}function u(e,t,i,s,o){return function(r){return r.map(function(r){var a;if(!r[i])return console.warn("Options passed to vue-multiselect do not contain groups, despite the config."),[];var u=l(r[i],e,t,o);return u.length?(a={},n(a,s,r[s]),n(a,i,u),a):[]})}}Object.defineProperty(t,"__esModule",{value:!0});var c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},h=i(2),p=function(e){return e&&e.__esModule?e:{default:e}}(h),d=function(){for(var e=arguments.length,t=Array(e),i=0;i-1},isSelected:function(e){var t=this.trackBy?e[this.trackBy]:e;return this.valueKeys.indexOf(t)>-1},isNotSelected:function(e){return!this.isSelected(e)},getOptionLabel:function(e){if(s(e))return"";if(e.isTag)return e.label;if(e.$isLabel)return e.$groupLabel;var t=this.customLabel(e,this.label);return s(t)?"":t},select:function(e,t){if(!(-1!==this.blockKeys.indexOf(t)||this.disabled||e.$isLabel||e.$isDisabled)&&(!this.max||!this.multiple||this.internalValue.length!==this.max)&&("Tab"!==t||this.pointerDirty)){if(e.isTag)this.$emit("tag",e.label,this.id),this.search="",this.closeOnSelect&&!this.multiple&&this.deactivate();else{if(this.isSelected(e))return void("Tab"!==t&&this.removeElement(e));this.multiple?this.internalValue.push(e):this.internalValue=[e],this.$emit("select",(0,p.default)(e),this.id),this.$emit("input",this.getValue(),this.id),this.clearOnSelect&&(this.search="")}this.closeOnSelect&&this.deactivate()}},removeElement:function(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if(!this.disabled){if(!this.allowEmpty&&this.internalValue.length<=1)return void this.deactivate();var i="object"===(void 0===e?"undefined":c(e))?this.valueKeys.indexOf(e[this.trackBy]):this.valueKeys.indexOf(e);this.internalValue.splice(i,1),this.$emit("input",this.getValue(),this.id),this.$emit("remove",(0,p.default)(e),this.id),this.closeOnSelect&&t&&this.deactivate()}},removeLastElement:function(){-1===this.blockKeys.indexOf("Delete")&&0===this.search.length&&Array.isArray(this.internalValue)&&this.removeElement(this.internalValue[this.internalValue.length-1],!1)},activate:function(){var e=this;this.isOpen||this.disabled||(this.adjustPosition(),this.groupValues&&0===this.pointer&&this.filteredOptions.length&&(this.pointer=1),this.isOpen=!0,this.searchable?(this.preserveSearch||(this.search=""),this.$nextTick(function(){return e.$refs.search.focus()})):this.$el.focus(),this.$emit("open",this.id))},deactivate:function(){this.isOpen&&(this.isOpen=!1,this.searchable?this.$refs.search.blur():this.$el.blur(),this.preserveSearch||(this.search=""),this.$emit("close",this.getValue(),this.id))},toggle:function(){this.isOpen?this.deactivate():this.activate()},adjustPosition:function(){if("undefined"!=typeof window){var e=this.$el.getBoundingClientRect().top,t=window.innerHeight-this.$el.getBoundingClientRect().bottom;t>this.maxHeight||t>e||"below"===this.openDirection||"bottom"===this.openDirection?(this.prefferedOpenDirection="below",this.optimizedHeight=Math.min(t-40,this.maxHeight)):(this.prefferedOpenDirection="above",this.optimizedHeight=Math.min(e-40,this.maxHeight))}}}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={data:function(){return{pointer:0,pointerDirty:!1}},props:{showPointer:{type:Boolean,default:!0},optionHeight:{type:Number,default:40}},computed:{pointerPosition:function(){return this.pointer*this.optionHeight},visibleElements:function(){return this.optimizedHeight/this.optionHeight}},watch:{filteredOptions:function(){this.pointerAdjust()},isOpen:function(){this.pointerDirty=!1}},methods:{optionHighlight:function(e,t){return{"multiselect__option--highlight":e===this.pointer&&this.showPointer,"multiselect__option--selected":this.isSelected(t)}},addPointerElement:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"Enter",t=e.key;this.filteredOptions.length>0&&this.select(this.filteredOptions[this.pointer],t),this.pointerReset()},pointerForward:function(){this.pointer0?(this.pointer--,this.$refs.list.scrollTop>=this.pointerPosition&&(this.$refs.list.scrollTop=this.pointerPosition),this.filteredOptions[this.pointer].$isLabel&&this.pointerBackward()):this.filteredOptions[0].$isLabel&&this.pointerForward(),this.pointerDirty=!0},pointerReset:function(){this.closeOnSelect&&(this.pointer=0,this.$refs.list&&(this.$refs.list.scrollTop=0))},pointerAdjust:function(){this.pointer>=this.filteredOptions.length-1&&(this.pointer=this.filteredOptions.length?this.filteredOptions.length-1:0)},pointerSet:function(e){this.pointer=e,this.pointerDirty=!0}}}},function(e,t,i){"use strict";function n(e){if(Array.isArray(e))return e.map(n);if(e&&"object"===(void 0===e?"undefined":s(e))){for(var t={},i=Object.keys(e),o=0,l=i.length;o0,expression:"visibleValue.length > 0"}],staticClass:"multiselect__tags-wrap"},[e._l(e.visibleValue,function(t){return[e._t("tag",[i("span",{staticClass:"multiselect__tag"},[i("span",{domProps:{textContent:e._s(e.getOptionLabel(t))}}),e._v(" "),i("i",{staticClass:"multiselect__tag-icon",attrs:{"aria-hidden":"true",tabindex:"1"},on:{keydown:function(i){if(!("button"in i)&&e._k(i.keyCode,"enter",13,i.key))return null;i.preventDefault(),e.removeElement(t)},mousedown:function(i){i.preventDefault(),e.removeElement(t)}}})])],{option:t,search:e.search,remove:e.removeElement})]})],2),e._v(" "),e.internalValue&&e.internalValue.length>e.limit?[i("strong",{staticClass:"multiselect__strong",domProps:{textContent:e._s(e.limitText(e.internalValue.length-e.limit))}})]:e._e(),e._v(" "),i("transition",{attrs:{name:"multiselect__loading"}},[e._t("loading",[i("div",{directives:[{name:"show",rawName:"v-show",value:e.loading,expression:"loading"}],staticClass:"multiselect__spinner"})])],2),e._v(" "),e.searchable?i("input",{ref:"search",staticClass:"multiselect__input",style:e.inputStyle,attrs:{name:e.name,id:e.id,type:"text",autocomplete:"off",placeholder:e.placeholder,disabled:e.disabled,tabindex:e.tabindex},domProps:{value:e.isOpen?e.search:e.currentOptionLabel},on:{input:function(t){e.updateSearch(t.target.value)},focus:function(t){t.preventDefault(),e.activate()},blur:function(t){t.preventDefault(),e.deactivate()},keyup:function(t){if(!("button"in t)&&e._k(t.keyCode,"esc",27,t.key))return null;e.deactivate()},keydown:[function(t){if(!("button"in t)&&e._k(t.keyCode,"down",40,t.key))return null;t.preventDefault(),e.pointerForward()},function(t){if(!("button"in t)&&e._k(t.keyCode,"up",38,t.key))return null;t.preventDefault(),e.pointerBackward()},function(t){return"button"in t||!e._k(t.keyCode,"enter",13,t.key)?(t.preventDefault(),t.stopPropagation(),t.target!==t.currentTarget?null:void e.addPointerElement(t)):null},function(t){if(!("button"in t)&&e._k(t.keyCode,"delete",[8,46],t.key))return null;t.stopPropagation(),e.removeLastElement()}]}}):e._e(),e._v(" "),e.searchable?e._e():i("span",{staticClass:"multiselect__single",domProps:{textContent:e._s(e.currentOptionLabel)},on:{mousedown:function(t){t.preventDefault(),e.toggle(t)}}})],2),e._v(" "),i("transition",{attrs:{name:"multiselect"}},[i("div",{directives:[{name:"show",rawName:"v-show",value:e.isOpen,expression:"isOpen"}],ref:"list",staticClass:"multiselect__content-wrapper",style:{maxHeight:e.optimizedHeight+"px"},on:{focus:e.activate,mousedown:function(e){e.preventDefault()}}},[i("ul",{staticClass:"multiselect__content",style:e.contentStyle},[e._t("beforeList"),e._v(" "),e.multiple&&e.max===e.internalValue.length?i("li",[i("span",{staticClass:"multiselect__option"},[e._t("maxElements",[e._v("Maximum of "+e._s(e.max)+" options selected. First remove a selected option to select another.")])],2)]):e._e(),e._v(" "),!e.max||e.internalValue.length= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ -function toString (val) { - return val == null - ? '' - : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) -} - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ -function toNumber (val) { - var n = parseFloat(val); - return isNaN(n) ? val : n -} - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ -function bind (fn, ctx) { - function boundFn (a) { - var l = arguments.length; - return l - ? l > 1 - ? fn.apply(ctx, arguments) - : fn.call(ctx, a) - : fn.call(ctx) - } - // record original fn length - boundFn._length = fn.length; - return boundFn -} - -/** - * Convert an Array-like object to a real Array. - */ -function toArray (list, start) { - start = start || 0; - var i = list.length - start; - var ret = new Array(i); - while (i--) { - ret[i] = list[i + start]; - } - return ret -} - -/** - * Mix properties into target object. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ -function toObject (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res -} - -/** - * Perform no operation. - * Stubbing args to make Flow happy without leaving useless transpiled code - * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ -function genStaticKeys (modules) { - return modules.reduce(function (keys, m) { - return keys.concat(m.staticKeys || []) - }, []).join(',') -} - -/** - * Check if two values are loosely equal - that is, - * if they are plain objects, do they have the same shape? - */ -function looseEqual (a, b) { - if (a === b) { return true } - var isObjectA = isObject(a); - var isObjectB = isObject(b); - if (isObjectA && isObjectB) { - try { - var isArrayA = Array.isArray(a); - var isArrayB = Array.isArray(b); - if (isArrayA && isArrayB) { - return a.length === b.length && a.every(function (e, i) { - return looseEqual(e, b[i]) - }) - } else if (!isArrayA && !isArrayB) { - var keysA = Object.keys(a); - var keysB = Object.keys(b); - return keysA.length === keysB.length && keysA.every(function (key) { - return looseEqual(a[key], b[key]) - }) - } else { - /* istanbul ignore next */ - return false - } - } catch (e) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } -} - -function looseIndexOf (arr, val) { - for (var i = 0; i < arr.length; i++) { - if (looseEqual(arr[i], val)) { return i } - } - return -1 -} - -/** - * Ensure a function is called only once. - */ -function once (fn) { - var called = false; - return function () { - if (!called) { - called = true; - fn.apply(this, arguments); - } - } -} - -var SSR_ATTR = 'data-server-rendered'; - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - // $flow-disable-line - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: "development" !== 'production', - - /** - * Whether to enable devtools - */ - devtools: "development" !== 'production', - - /** - * Whether to record perf - */ - performance: false, - - /** - * Error handler for watcher errors - */ - errorHandler: null, - - /** - * Warn handler for watcher warns - */ - warnHandler: null, - - /** - * Ignore certain custom elements - */ - ignoredElements: [], - - /** - * Custom user key aliases for v-on - */ - // $flow-disable-line - keyCodes: Object.create(null), - - /** - * Check if a tag is reserved so that it cannot be registered as a - * component. This is platform-dependent and may be overwritten. - */ - isReservedTag: no, - - /** - * Check if an attribute is reserved so that it cannot be used as a component - * prop. This is platform-dependent and may be overwritten. - */ - isReservedAttr: no, - - /** - * Check if a tag is an unknown element. - * Platform-dependent. - */ - isUnknownElement: no, - - /** - * Get the namespace of an element - */ - getTagNamespace: noop, - - /** - * Parse the real tag name for the specific platform. - */ - parsePlatformTagName: identity, - - /** - * Check if an attribute must be bound using property, e.g. value - * Platform-dependent. - */ - mustUseProp: no, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -/** - * Check if a string starts with $ or _ - */ -function isReserved (str) { - var c = (str + '').charCodeAt(0); - return c === 0x24 || c === 0x5F -} - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/** - * Parse simple path. - */ -var bailRE = /[^\w.$]/; -function parsePath (path) { - if (bailRE.test(path)) { - return - } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) { return } - obj = obj[segments[i]]; - } - return obj - } -} - -/* */ - - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform; -var weexPlatform = inWeex && WXEnvironment.platform.toLowerCase(); -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android'); -var isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios'); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - -var supportsPassive = false; -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - supportsPassive = true; - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - if (_isServer === undefined) { - /* istanbul ignore if */ - if (!inBrowser && typeof global !== 'undefined') { - // detect presence of vue-server-renderer and avoid - // Webpack shimming the process - _isServer = global['process'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools -var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -if (typeof Set !== 'undefined' && isNative(Set)) { - // use native Set when available. - _Set = Set; -} else { - // a non-standard Set polyfill that only works with primitive keys. - _Set = (function () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -{ - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var trace = vm ? generateComponentTrace(vm) : ''; - - if (config.warnHandler) { - config.warnHandler.call(null, msg, vm, trace); - } else if (hasConsole && (!config.silent)) { - console.error(("[Vue warn]: " + msg + trace)); - } - }; - - tip = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var last = tree[tree.length - 1]; - if (last.constructor === vm.constructor) { - currentRecursiveSequence++; - vm = vm.$parent; - continue - } else if (currentRecursiveSequence > 0) { - tree[tree.length - 1] = [last, currentRecursiveSequence]; - currentRecursiveSequence = 0; - } - } - tree.push(vm); - vm = vm.$parent; - } - return '\n\nfound in\n\n' + tree - .map(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - - -var uid = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// the current target watcher being evaluated. -// this is globally unique because there could be only one -// watcher being evaluated at any time. -Dep.target = null; -var targetStack = []; - -function pushTarget (_target) { - if (Dep.target) { targetStack.push(Dep.target); } - Dep.target = _target; -} - -function popTarget () { - Dep.target = targetStack.pop(); -} - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.fnContext = undefined; - this.fnOptions = undefined; - this.fnScopeId = undefined; - this.key = data && data.key; - this.componentOptions = componentOptions; - this.componentInstance = undefined; - this.parent = undefined; - this.raw = false; - this.isStatic = false; - this.isRootInsert = true; - this.isComment = false; - this.isCloned = false; - this.isOnce = false; - this.asyncFactory = asyncFactory; - this.asyncMeta = undefined; - this.isAsyncPlaceholder = false; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - -var createEmptyVNode = function (text) { - if ( text === void 0 ) text = ''; - - var node = new VNode(); - node.text = text; - node.isComment = true; - return node -}; - -function createTextVNode (val) { - return new VNode(undefined, undefined, undefined, String(val)) -} - -// optimized shallow clone -// used for static nodes and slot nodes because they may be reused across -// multiple renders, cloning them avoids errors when DOM manipulations rely -// on their elm reference. -function cloneVNode (vnode, deep) { - var componentOptions = vnode.componentOptions; - var cloned = new VNode( - vnode.tag, - vnode.data, - vnode.children, - vnode.text, - vnode.elm, - vnode.context, - componentOptions, - vnode.asyncFactory - ); - cloned.ns = vnode.ns; - cloned.isStatic = vnode.isStatic; - cloned.key = vnode.key; - cloned.isComment = vnode.isComment; - cloned.fnContext = vnode.fnContext; - cloned.fnOptions = vnode.fnOptions; - cloned.fnScopeId = vnode.fnScopeId; - cloned.isCloned = true; - if (deep) { - if (vnode.children) { - cloned.children = cloneVNodes(vnode.children, true); - } - if (componentOptions && componentOptions.children) { - componentOptions.children = cloneVNodes(componentOptions.children, true); - } - } - return cloned -} - -function cloneVNodes (vnodes, deep) { - var len = vnodes.length; - var res = new Array(len); - for (var i = 0; i < len; i++) { - res[i] = cloneVNode(vnodes[i], deep); - } - return res -} - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -].forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - shouldConvert: true -}; - -/** - * Observer class that are attached to each observed - * object. Once attached, the observer converts target - * object's property keys into getter/setters that - * collect dependencies and dispatches updates. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -function copyAugment (target, src, keys) { - for (var i = 0, l = keys.length; i < l; i++) { - var key = keys[i]; - def(target, key, src[key]); - } -} - -/** - * Attempt to create an observer instance for a value, - * returns the new observer if successfully observed, - * or the existing observer if the value already has one. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - var dep = new Dep(); - - var property = Object.getOwnPropertyDescriptor(obj, key); - if (property && property.configurable === false) { - return - } - - // cater for pre-defined getter/setters - var getter = property && property.get; - var setter = property && property.set; - - var childOb = !shallow && observe(val); - Object.defineProperty(obj, key, { - enumerable: true, - configurable: true, - get: function reactiveGetter () { - var value = getter ? getter.call(obj) : val; - if (Dep.target) { - dep.depend(); - if (childOb) { - childOb.dep.depend(); - if (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if ("development" !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (key in target && !(key in Object.prototype)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - "development" !== 'production' && warn( - 'Avoid adding reactive properties to a Vue instance or its root $data ' + - 'at runtime - declare it upfront in the data option.' - ); - return val - } - if (!ob) { - target[key] = val; - return val - } - defineReactive(ob.value, key, val); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ -function del (target, key) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.splice(key, 1); - return - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - "development" !== 'production' && warn( - 'Avoid deleting properties on a Vue instance or its root $data ' + - '- just set it to null.' - ); - return - } - if (!hasOwn(target, key)) { - return - } - delete target[key]; - if (!ob) { - return - } - ob.dep.notify(); -} - -/** - * Collect dependencies on array elements when the array is touched, since - * we cannot intercept array element access like property getters. - */ -function dependArray (value) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -{ - strats.el = strats.propsData = function (parent, child, vm, key) { - if (!vm) { - warn( - "option \"" + key + "\" can only be used during instance " + - 'creation with the `new` keyword.' - ); - } - return defaultStrat(parent, child) - }; -} - -/** - * Helper that recursively merges two data objects together. - */ -function mergeData (to, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - if (!vm) { - // in a Vue.extend merge, both should be functions - if (!childVal) { - return parentVal - } - if (!parentVal) { - return childVal - } - // when parentVal & childVal are both present, - // we need to return a function that returns the - // merged result of both functions... no need to - // check if parentVal is a function here because - // it has to be a function to pass previous merges. - return function mergedDataFn () { - return mergeData( - typeof childVal === 'function' ? childVal.call(this, this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal - ) - } - } else { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm, vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm, vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && typeof childVal !== 'function') { - "development" !== 'production' && warn( - 'The "data" option should be a function ' + - 'that returns a per-instance value in component ' + - 'definitions.', - vm - ); - - return parentVal - } - return mergeDataOrFn(parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * Assets - * - * When a vm is present (instance creation), we need to do - * a three-way merge between constructor options, instance - * options and parent options. - */ -function mergeAssets ( - parentVal, - childVal, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - "development" !== 'production' && assertObjectType(key, childVal, vm); - return extend(res, childVal) - } else { - return res - } -} - -ASSET_TYPES.forEach(function (type) { - strats[type + 's'] = mergeAssets; -}); - -/** - * Watchers. - * - * Watchers hashes should not overwrite one - * another, so we merge them as arrays. - */ -strats.watch = function ( - parentVal, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && "development" !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -/** - * Validate component names - */ -function checkComponents (options) { - for (var key in options.components) { - validateComponentName(key); - } -} - -function validateComponentName (name) { - if (!/^[a-zA-Z][\w-]*$/.test(name)) { - warn( - 'Invalid component name: "' + name + '". Component names ' + - 'can only contain alphanumeric characters and the hyphen, ' + - 'and must start with a letter.' - ); - } - if (isBuiltInTag(name) || config.isReservedTag(name)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + name - ); - } -} - -/** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ -function normalizeProps (options, vm) { - var props = options.props; - if (!props) { return } - var res = {}; - var i, val, name; - if (Array.isArray(props)) { - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - name = camelize(val); - res[name] = { type: null }; - } else { - warn('props must be strings when using array syntax.'); - } - } - } else if (isPlainObject(props)) { - for (var key in props) { - val = props[key]; - name = camelize(key); - res[name] = isPlainObject(val) - ? val - : { type: val }; - } - } else { - warn( - "Invalid value for option \"props\": expected an Array or an Object, " + - "but got " + (toRawType(props)) + ".", - vm - ); - } - options.props = res; -} - -/** - * Normalize all injections into Object-based format - */ -function normalizeInject (options, vm) { - var inject = options.inject; - if (!inject) { return } - var normalized = options.inject = {}; - if (Array.isArray(inject)) { - for (var i = 0; i < inject.length; i++) { - normalized[inject[i]] = { from: inject[i] }; - } - } else if (isPlainObject(inject)) { - for (var key in inject) { - var val = inject[key]; - normalized[key] = isPlainObject(val) - ? extend({ from: key }, val) - : { from: val }; - } - } else { - warn( - "Invalid value for option \"inject\": expected an Array or an Object, " + - "but got " + (toRawType(inject)) + ".", - vm - ); - } -} - -/** - * Normalize raw function directives into object format. - */ -function normalizeDirectives (options) { - var dirs = options.directives; - if (dirs) { - for (var key in dirs) { - var def = dirs[key]; - if (typeof def === 'function') { - dirs[key] = { bind: def, update: def }; - } - } - } -} - -function assertObjectType (name, value, vm) { - if (!isPlainObject(value)) { - warn( - "Invalid value for option \"" + name + "\": expected an Object, " + - "but got " + (toRawType(value)) + ".", - vm - ); - } -} - -/** - * Merge two option objects into a new one. - * Core utility used in both instantiation and inheritance. - */ -function mergeOptions ( - parent, - child, - vm -) { - { - checkComponents(child); - } - - if (typeof child === 'function') { - child = child.options; - } - - normalizeProps(child, vm); - normalizeInject(child, vm); - normalizeDirectives(child); - var extendsFrom = child.extends; - if (extendsFrom) { - parent = mergeOptions(parent, extendsFrom, vm); - } - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - var options = {}; - var key; - for (key in parent) { - mergeField(key); - } - for (key in child) { - if (!hasOwn(parent, key)) { - mergeField(key); - } - } - function mergeField (key) { - var strat = strats[key] || defaultStrat; - options[key] = strat(parent[key], child[key], vm, key); - } - return options -} - -/** - * Resolve an asset. - * This function is used because child instances need access - * to assets defined in its ancestor chain. - */ -function resolveAsset ( - options, - type, - id, - warnMissing -) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return - } - var assets = options[type]; - // check local registration variations first - if (hasOwn(assets, id)) { return assets[id] } - var camelizedId = camelize(id); - if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } - var PascalCaseId = capitalize(camelizedId); - if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } - // fallback to prototype chain - var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; - if ("development" !== 'production' && warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ); - } - return res -} - -/* */ - -function validateProp ( - key, - propOptions, - propsData, - vm -) { - var prop = propOptions[key]; - var absent = !hasOwn(propsData, key); - var value = propsData[key]; - // handle boolean props - if (isType(Boolean, prop.type)) { - if (absent && !hasOwn(prop, 'default')) { - value = false; - } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) { - value = true; - } - } - // check default value - if (value === undefined) { - value = getPropDefaultValue(vm, prop, key); - // since the default value is a fresh copy, - // make sure to observe it. - var prevShouldConvert = observerState.shouldConvert; - observerState.shouldConvert = true; - observe(value); - observerState.shouldConvert = prevShouldConvert; - } - { - assertProp(prop, key, value, vm, absent); - } - return value -} - -/** - * Get the default value of a prop. - */ -function getPropDefaultValue (vm, prop, key) { - // no default, return undefined - if (!hasOwn(prop, 'default')) { - return undefined - } - var def = prop.default; - // warn against non-factory defaults for Object & Array - if ("development" !== 'production' && isObject(def)) { - warn( - 'Invalid default value for prop "' + key + '": ' + - 'Props with type Object/Array must use a factory function ' + - 'to return the default value.', - vm - ); - } - // the raw prop value was also undefined from previous render, - // return previous default value to avoid unnecessary watcher trigger - if (vm && vm.$options.propsData && - vm.$options.propsData[key] === undefined && - vm._props[key] !== undefined - ) { - return vm._props[key] - } - // call factory function for non-Function types - // a value is Function if its prototype is function even across different execution context - return typeof def === 'function' && getType(prop.type) !== 'Function' - ? def.call(vm) - : def -} - -/** - * Assert whether a prop is valid. - */ -function assertProp ( - prop, - name, - value, - vm, - absent -) { - if (prop.required && absent) { - warn( - 'Missing required prop: "' + name + '"', - vm - ); - return - } - if (value == null && !prop.required) { - return - } - var type = prop.type; - var valid = !type || type === true; - var expectedTypes = []; - if (type) { - if (!Array.isArray(type)) { - type = [type]; - } - for (var i = 0; i < type.length && !valid; i++) { - var assertedType = assertType(value, type[i]); - expectedTypes.push(assertedType.expectedType || ''); - valid = assertedType.valid; - } - } - if (!valid) { - warn( - "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')) + - ", got " + (toRawType(value)) + ".", - vm - ); - return - } - var validator = prop.validator; - if (validator) { - if (!validator(value)) { - warn( - 'Invalid prop: custom validator check failed for prop "' + name + '".', - vm - ); - } - } -} - -var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; - -function assertType (value, type) { - var valid; - var expectedType = getType(type); - if (simpleCheckRE.test(expectedType)) { - var t = typeof value; - valid = t === expectedType.toLowerCase(); - // for primitive wrapper objects - if (!valid && t === 'object') { - valid = value instanceof type; - } - } else if (expectedType === 'Object') { - valid = isPlainObject(value); - } else if (expectedType === 'Array') { - valid = Array.isArray(value); - } else { - valid = value instanceof type; - } - return { - valid: valid, - expectedType: expectedType - } -} - -/** - * Use function string name to check built-in types, - * because a simple equality check will fail when running - * across different vms / iframes. - */ -function getType (fn) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); - return match ? match[1] : '' -} - -function isType (type, fn) { - if (!Array.isArray(fn)) { - return getType(fn) === getType(type) - } - for (var i = 0, len = fn.length; i < len; i++) { - if (getType(fn[i]) === getType(type)) { - return true - } - } - /* istanbul ignore next */ - return false -} - -/* */ - -function handleError (err, vm, info) { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler'); - } - } - logError(err, vm, info); -} - -function logError (err, vm, info) { - { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if ((inBrowser || inWeex) && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } -} - -/* */ -/* globals MessageChannel */ - -var callbacks = []; -var pending = false; - -function flushCallbacks () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } -} - -// Here we have async deferring wrappers using both micro and macro tasks. -// In < 2.4 we used micro tasks everywhere, but there are some scenarios where -// micro tasks have too high a priority and fires in between supposedly -// sequential events (e.g. #4521, #6690) or even between bubbling of the same -// event (#6566). However, using macro tasks everywhere also has subtle problems -// when state is changed right before repaint (e.g. #6813, out-in transitions). -// Here we use micro task by default, but expose a way to force macro task when -// needed (e.g. in event handlers attached by v-on). -var microTimerFunc; -var macroTimerFunc; -var useMacroTask = false; - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - macroTimerFunc = function () { - setImmediate(flushCallbacks); - }; -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = flushCallbacks; - macroTimerFunc = function () { - port.postMessage(1); - }; -} else { - /* istanbul ignore next */ - macroTimerFunc = function () { - setTimeout(flushCallbacks, 0); - }; -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - var p = Promise.resolve(); - microTimerFunc = function () { - p.then(flushCallbacks); - // in problematic UIWebViews, Promise.then doesn't completely break, but - // it can get stuck in a weird state where callbacks are pushed into the - // microtask queue but the queue isn't being flushed, until the browser - // needs to do some other work, e.g. handle a timer. Therefore we can - // "force" the microtask queue to be flushed by adding an empty timer. - if (isIOS) { setTimeout(noop); } - }; -} else { - // fallback to macro - microTimerFunc = macroTimerFunc; -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ -function withMacroTask (fn) { - return fn._withTask || (fn._withTask = function () { - useMacroTask = true; - var res = fn.apply(null, arguments); - useMacroTask = false; - return res - }) -} - -function nextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - handleError(e, ctx, 'nextTick'); - } - } else if (_resolve) { - _resolve(ctx); - } - }); - if (!pending) { - pending = true; - if (useMacroTask) { - macroTimerFunc(); - } else { - microTimerFunc(); - } - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(function (resolve) { - _resolve = resolve; - }) - } -} - -/* */ - -var mark; -var measure; - -{ - var perf = inBrowser && window.performance; - /* istanbul ignore if */ - if ( - perf && - perf.mark && - perf.measure && - perf.clearMarks && - perf.clearMeasures - ) { - mark = function (tag) { return perf.mark(tag); }; - measure = function (name, startTag, endTag) { - perf.measure(name, startTag, endTag); - perf.clearMarks(startTag); - perf.clearMarks(endTag); - perf.clearMeasures(name); - }; - } -} - -/* not type checking this file because flow doesn't play well with Proxy */ - -var initProxy; - -{ - var allowedGlobals = makeMap( - 'Infinity,undefined,NaN,isFinite,isNaN,' + - 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + - 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' + - 'require' // for Webpack/Browserify - ); - - var warnNonPresent = function (target, key) { - warn( - "Property or method \"" + key + "\" is not defined on the instance but " + - 'referenced during render. Make sure that this property is reactive, ' + - 'either in the data option, or for class-based components, by ' + - 'initializing the property. ' + - 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', - target - ); - }; - - var hasProxy = - typeof Proxy !== 'undefined' && - Proxy.toString().match(/native code/); - - if (hasProxy) { - var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); - config.keyCodes = new Proxy(config.keyCodes, { - set: function set (target, key, value) { - if (isBuiltInModifier(key)) { - warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); - return false - } else { - target[key] = value; - return true - } - } - }); - } - - var hasHandler = { - has: function has (target, key) { - var has = key in target; - var isAllowed = allowedGlobals(key) || key.charAt(0) === '_'; - if (!has && !isAllowed) { - warnNonPresent(target, key); - } - return has || !isAllowed - } - }; - - var getHandler = { - get: function get (target, key) { - if (typeof key === 'string' && !(key in target)) { - warnNonPresent(target, key); - } - return target[key] - } - }; - - initProxy = function initProxy (vm) { - if (hasProxy) { - // determine which proxy handler to use - var options = vm.$options; - var handlers = options.render && options.render._withStripped - ? getHandler - : hasHandler; - vm._renderProxy = new Proxy(vm, handlers); - } else { - vm._renderProxy = vm; - } - }; -} - -/* */ - -var seenObjects = new _Set(); - -/** - * Recursively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. - */ -function traverse (val) { - _traverse(val, seenObjects); - seenObjects.clear(); -} - -function _traverse (val, seen) { - var i, keys; - var isA = Array.isArray(val); - if ((!isA && !isObject(val)) || Object.isFrozen(val)) { - return - } - if (val.__ob__) { - var depId = val.__ob__.dep.id; - if (seen.has(depId)) { - return - } - seen.add(depId); - } - if (isA) { - i = val.length; - while (i--) { _traverse(val[i], seen); } - } else { - keys = Object.keys(val); - i = keys.length; - while (i--) { _traverse(val[keys[i]], seen); } - } -} - -/* */ - -var normalizeEvent = cached(function (name) { - var passive = name.charAt(0) === '&'; - name = passive ? name.slice(1) : name; - var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first - name = once$$1 ? name.slice(1) : name; - var capture = name.charAt(0) === '!'; - name = capture ? name.slice(1) : name; - return { - name: name, - once: once$$1, - capture: capture, - passive: passive - } -}); - -function createFnInvoker (fns) { - function invoker () { - var arguments$1 = arguments; - - var fns = invoker.fns; - if (Array.isArray(fns)) { - var cloned = fns.slice(); - for (var i = 0; i < cloned.length; i++) { - cloned[i].apply(null, arguments$1); - } - } else { - // return handler return value for single handlers - return fns.apply(null, arguments) - } - } - invoker.fns = fns; - return invoker -} - -function updateListeners ( - on, - oldOn, - add, - remove$$1, - vm -) { - var name, def, cur, old, event; - for (name in on) { - def = cur = on[name]; - old = oldOn[name]; - event = normalizeEvent(name); - /* istanbul ignore if */ - if (isUndef(cur)) { - "development" !== 'production' && warn( - "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), - vm - ); - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur); - } - add(event.name, cur, event.once, event.capture, event.passive, event.params); - } else if (cur !== old) { - old.fns = cur; - on[name] = old; - } - } - for (name in oldOn) { - if (isUndef(on[name])) { - event = normalizeEvent(name); - remove$$1(event.name, oldOn[name], event.capture); - } - } -} - -/* */ - -function mergeVNodeHook (def, hookKey, hook) { - if (def instanceof VNode) { - def = def.data.hook || (def.data.hook = {}); - } - var invoker; - var oldHook = def[hookKey]; - - function wrappedHook () { - hook.apply(this, arguments); - // important: remove merged hook to ensure it's called only once - // and prevent memory leak - remove(invoker.fns, wrappedHook); - } - - if (isUndef(oldHook)) { - // no existing hook - invoker = createFnInvoker([wrappedHook]); - } else { - /* istanbul ignore if */ - if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { - // already a merged invoker - invoker = oldHook; - invoker.fns.push(wrappedHook); - } else { - // existing plain hook - invoker = createFnInvoker([oldHook, wrappedHook]); - } - } - - invoker.merged = true; - def[hookKey] = invoker; -} - -/* */ - -function extractPropsFromVNodeData ( - data, - Ctor, - tag -) { - // we are only extracting raw values here. - // validation and default values are handled in the child - // component itself. - var propOptions = Ctor.options.props; - if (isUndef(propOptions)) { - return - } - var res = {}; - var attrs = data.attrs; - var props = data.props; - if (isDef(attrs) || isDef(props)) { - for (var key in propOptions) { - var altKey = hyphenate(key); - { - var keyInLowerCase = key.toLowerCase(); - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - "Prop \"" + keyInLowerCase + "\" is passed to component " + - (formatComponentName(tag || Ctor)) + ", but the declared prop name is" + - " \"" + key + "\". " + - "Note that HTML attributes are case-insensitive and camelCased " + - "props need to use their kebab-case equivalents when using in-DOM " + - "templates. You should probably use \"" + altKey + "\" instead of \"" + key + "\"." - ); - } - } - checkProp(res, props, key, altKey, true) || - checkProp(res, attrs, key, altKey, false); - } - } - return res -} - -function checkProp ( - res, - hash, - key, - altKey, - preserve -) { - if (isDef(hash)) { - if (hasOwn(hash, key)) { - res[key] = hash[key]; - if (!preserve) { - delete hash[key]; - } - return true - } else if (hasOwn(hash, altKey)) { - res[key] = hash[altKey]; - if (!preserve) { - delete hash[altKey]; - } - return true - } - } - return false -} - -/* */ - -// The template compiler attempts to minimize the need for normalization by -// statically analyzing the template at compile time. -// -// For plain HTML markup, normalization can be completely skipped because the -// generated render function is guaranteed to return Array. There are -// two cases where extra normalization is needed: - -// 1. When the children contains components - because a functional component -// may return an Array instead of a single root. In this case, just a simple -// normalization is needed - if any child is an Array, we flatten the whole -// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep -// because functional components already normalize their own children. -function simpleNormalizeChildren (children) { - for (var i = 0; i < children.length; i++) { - if (Array.isArray(children[i])) { - return Array.prototype.concat.apply([], children) - } - } - return children -} - -// 2. When the children contains constructs that always generated nested Arrays, -// e.g.