first commit

This commit is contained in:
2024-01-19 11:09:11 +01:00
commit b18af7a943
29473 changed files with 4500547 additions and 0 deletions

202
node_modules/browser-sync-ui/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2015] [Shane Osbourne]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,gs
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

7
node_modules/browser-sync-ui/README.md generated vendored Normal file
View File

@@ -0,0 +1,7 @@
# Browsersync UI [![Build Status](https://travis-ci.org/BrowserSync/UI.svg?branch=master)](https://travis-ci.org/BrowserSync/UI)
Comes bundled with the Browsersync module (version `2.0.0` onwards).
## License
Copyright (c) 2016 Shane Osbourne
Licensed under the Apache 2.0 license.

48
node_modules/browser-sync-ui/index.js generated vendored Normal file
View File

@@ -0,0 +1,48 @@
"use strict";
var UI = require("./lib/UI");
var config = require("./lib/config");
var Events = require("events").EventEmitter;
/**
* Hooks are for attaching functionality to BrowserSync
*/
module.exports.hooks = {
/**
* Client JS is added to each connected client
*/
"client:js": fileContent(config.defaults.clientJs)
};
/**
* BrowserSync Plugin interface
* @param {Object} opts
* @param {BrowserSync} bs
* @param {Function} cb
* @returns {UI}
*/
module.exports["plugin"] = function (opts, bs, cb) {
var ui = new UI(opts, bs, new Events());
bs.setOption("session", new Date().getTime());
ui.cb = cb || function () { /*noop*/ };
ui.init();
return ui;
};
module.exports["plugin:name"] = config.defaults.pluginName;
/**
* @param filepath
* @returns {*}
*/
function getPath (filepath) {
return require("path").join(__dirname, filepath);
}
/**
* @param filepath
* @returns {*}
*/
function fileContent (filepath) {
return require("fs").readFileSync(getPath(filepath));
}

251
node_modules/browser-sync-ui/lib/UI.js generated vendored Normal file
View File

@@ -0,0 +1,251 @@
var fs = require("fs");
var path = require("path");
var chalk = require("chalk");
var config = require("./config");
var eachSeries = require("async-each-series");
var asyncTasks = require("./async-tasks");
var hooks = require("./hooks");
var merge = require("./opts").merge;
var defaultPlugins = {
"sync-options": require("./plugins/sync-options/sync-options.plugin"),
"overview": require("./plugins/overview/overview.plugin"),
"history": require("./plugins/history/history.plugin"),
"plugins": require("./plugins/plugins/plugins.plugin"),
"remote-debug": require("./plugins/remote-debug/remote-debug.plugin"),
"help": require("./plugins/help/help.plugin"),
"connections": require("./plugins/connections/connections.plugin"),
"network-throttle": require("./plugins/network-throttle/network-throttle.plugin")
};
/**
* @param {Object} opts - Any options specifically
* passed to the control panel
* @param {BrowserSync} bs
* @param {EventEmitter} emitter
* @constructor
* @returns {UI}
*/
var UI = function (opts, bs, emitter) {
var ui = this;
ui.bs = bs;
ui.config = config.merge();
ui.events = emitter;
ui.options = merge(opts);
ui.logger = bs.getLogger(ui.config.get("pluginName"));
ui.defaultPlugins = defaultPlugins;
ui.listeners = {};
ui.clients = bs.io.of(bs.options.getIn(["socket", "namespace"]));
ui.socket = bs.io.of(ui.config.getIn(["socket", "namespace"]));
if (ui.options.get("logLevel")) {
ui.logger.setLevel(ui.options.get("logLevel"));
}
/**
*
*/
ui.pluginManager = new bs.utils.easyExtender(defaultPlugins, hooks).init();
/**
* Transform/save data RE: plugins
* @type {*}
*/
ui.bsPlugins = require("./resolve-plugins")(bs.getUserPlugins());
return ui;
};
/**
* Detect an available port
* @returns {UI}
*/
UI.prototype.init = function () {
var ui = this;
eachSeries(
asyncTasks,
taskRunner(ui),
tasksComplete(ui)
);
return this;
};
/**
* @param cb
*/
UI.prototype.getServer = function (cb) {
var ui = this;
if (ui.server) {
return ui.server;
}
this.events.on("ui:running", function () {
cb(null, ui.server);
});
};
/**
* @returns {Array}
*/
UI.prototype.getInitialTemplates = function () {
var prefix = path.resolve(__dirname, "../templates/directives");
return fs.readdirSync(prefix)
.map(function (name) {
return path.resolve(prefix, name);
});
};
/**
* @param event
*/
UI.prototype.delegateEvent = function (event) {
var ui = this;
var listeners = ui.listeners[event.namespace];
if (listeners) {
if (listeners.event) {
listeners.event.call(ui, event);
} else {
if (event.event && listeners[event.event]) {
listeners[event.event].call(ui, event.data);
}
}
}
};
/**
* @param cb
*/
UI.prototype.listen = function (ns, events) {
var ui = this;
if (Array.isArray(ns)) {
ns = ns.join(":");
}
if (!ui.listeners[ns]) {
ui.listeners[ns] = events;
}
};
/**
* @param name
* @param value
* @returns {Map|*}
*/
UI.prototype.setOption = function (name, value) {
var ui = this;
ui.options = ui.options.set(name, value);
return ui.options;
};
/**
* @param path
* @param value
* @returns {Map|*}
*/
UI.prototype.setOptionIn = function (path, value) {
this.options = this.options.setIn(path, value);
return this.options;
};
/**
* @param fn
*/
UI.prototype.setMany = function (fn) {
this.options = this.options.withMutations(fn);
return this.options;
};
/**
* @param path
* @returns {any|*}
*/
UI.prototype.getOptionIn = function (path) {
return this.options.getIn(path);
};
/**
* Run each setup task in sequence
* @param ui
* @returns {Function}
*/
function taskRunner (ui) {
return function (item, cb) {
ui.logger.debug("Starting Step: " + item.step);
/**
* Give each step access to the UI Instance
*/
item.fn(ui, function (err, out) {
if (err) {
return cb(err);
}
if (out) {
handleOut(ui, out);
}
ui.logger.debug(chalk.green("Step Complete: %s", item.step));
cb();
});
};
}
/**
* Setup tasks may return options or instance properties to be set
* @param {UI} ui
* @param {Object} out
*/
function handleOut (ui, out) {
if (out.options) {
Object.keys(out.options).forEach(function (key) {
ui.options = ui.options.set(key, out.options[key]);
});
}
if (out.optionsIn) {
out.optionsIn.forEach(function (item) {
ui.options = ui.options.setIn(item.path, item.value);
});
}
if (out.instance) {
Object.keys(out.instance).forEach(function (key) {
ui[key] = out.instance[key];
});
}
}
/**
* All async tasks complete at this point
* @param ui
*/
function tasksComplete (ui) {
return function (err) {
/**
* Log any error according to BrowserSync's Logging level
*/
if (err) {
ui.logger.setOnce("useLevelPrefixes", true).error(err.message || err);
}
/**
* Running event
*/
ui.events.emit("ui:running", {instance: ui, options: ui.options});
/**
* Finally call the user-provided callback
*/
ui.cb(null, ui);
};
}
module.exports = UI;

36
node_modules/browser-sync-ui/lib/async-tasks.js generated vendored Normal file
View File

@@ -0,0 +1,36 @@
var async = require("./async");
module.exports = [
{
step: "Setting default plugins",
fn: async.initDefaultHooks
},
{
step: "Finding a free port",
fn: async.findAFreePort
},
{
step: "Setting options also relevant to UI from BS",
fn: async.setBsOptions
},
{
step: "Setting available URLS for UI",
fn: async.setUrlOptions
},
{
step: "Starting the Control Panel Server",
fn: async.startServer
},
{
step: "Add element events",
fn: async.addElementEvents
},
{
step: "Registering default plugins",
fn: async.registerPlugins
},
{
step: "Add options setting event",
fn: async.addOptionsEvent
}
];

209
node_modules/browser-sync-ui/lib/async.js generated vendored Normal file
View File

@@ -0,0 +1,209 @@
var Immutable = require("immutable");
var url = require("url");
module.exports = {
/**
* The UI uses it's own server/port
* @param ui
* @param done
*/
findAFreePort: function (ui, done) {
var port = ui.options.get("port");
var listenHost = ui.options.get("listen", "localhost");
ui.bs.utils.portscanner.findAPortNotInUse(port, port + 100, {
host: listenHost,
timeout: 1000
}, function (err, port) {
if (err) {
return done(err);
}
done(null, {
options: {
port: port
}
});
});
},
/**
* Default hooks do things like creating/joining JS files &
* building angular config
* @param ui
* @param done
*/
initDefaultHooks: function (ui, done) {
var out = ui.pluginManager.hook("page", ui);
done(null, {
instance: {
clientJs: ui.pluginManager.hook("client:js", ui),
templates: ui.pluginManager.hook("templates", ui.getInitialTemplates(), ui),
pagesConfig: out.pagesConfig,
pages: out.pagesObj,
pageMarkup: out.pageMarkup
}
});
},
setBsOptions: function (ui, done) {
done(null, {
options: {
bs: Immutable.Map({
mode: ui.bs.options.get("mode"),
port: ui.bs.options.get("port")
})
}
});
},
/**
* @param ui
* @param done
*/
setUrlOptions: function (ui, done) {
var port = ui.options.get("port");
var bsUrls = ui.bs.getOptionIn(["urls"]).toJS();
var listenHost = ui.bs.options.get("listen", "localhost");
var urls = {
ui: "http://" + listenHost + ":" + port
};
if (bsUrls.external) {
var externalListen = listenHost || url.parse(bsUrls.external).hostname;
urls["ui-external"] = ["http://", externalListen, ":", port].join("");
}
done(null, {
options: {
urls: Immutable.fromJS(urls)
}
});
},
/**
* Simple static file server with some middlewares for custom
* scripts/routes.
* @param ui
* @param done
*/
startServer: function (ui, done) {
var bs = ui.bs;
var port = ui.options.get("port");
var listenHost = ui.options.get("listen");
ui.logger.debug("Using port %s", port);
var server = require("./server")(ui, {
middleware: {
socket: bs.getMiddleware("socket-js"),
connector: bs.getSocketConnector(bs.options.get("port"), {
path: bs.options.getIn(["socket", "path"]),
namespace: ui.config.getIn(["socket", "namespace"])
})
}
});
require('server-destroy')(server.server);
bs.registerCleanupTask(function () {
if (server.server) {
server.server.destroy();
}
if (ui.servers) {
Object.keys(ui.servers).forEach(function (key) {
if (ui.servers[key].server) {
ui.servers[key].server.destroy();
}
});
}
});
done(null, {
instance: {
server: server.server.listen(port, listenHost),
app: server.app
}
});
},
/**
* Allow an API for adding/removing elements to clients
* @param ui
* @param done
*/
addElementEvents: function (ui, done) {
var elems = ui.pluginManager.hook("elements");
var bs = ui.bs;
if (!Object.keys(elems).length) {
return done();
}
ui.setOption("clientFiles", Immutable.fromJS(elems));
done(null, {
instance: {
enableElement: require("./client-elements").enable(ui.clients, ui, bs),
disableElement: require("./client-elements").disable(ui.clients, ui, bs),
addElement: require("./client-elements").addElement
}
});
},
/**
* Run default plugins
* @param ui
* @param done
*/
registerPlugins: function (ui, done) {
Object.keys(ui.defaultPlugins).forEach(function (key) {
ui.pluginManager.get(key)(ui, ui.bs);
});
done();
},
/**
* The most important event is the initial connection where
* the options are received from the socket
* @param ui
* @param done
*/
addOptionsEvent: function (ui, done) {
var bs = ui.bs;
ui.clients.on("connection", function (client) {
client.emit("ui:connection", ui.options.toJS());
ui.options.get("clientFiles").map(function (item) {
if (item.get("active")) {
ui.addElement(client, item.toJS());
}
});
});
ui.socket.on("connection", function (client) {
client.emit("connection", bs.getOptions().toJS());
client.emit("ui:connection", ui.options.toJS());
client.on("ui:get:options", function () {
client.emit("ui:receive:options", {
bs: bs.getOptions().toJS(),
ui: ui.options.toJS()
});
});
// proxy client events
client.on("ui:client:proxy", function (evt) {
ui.clients.emit(evt.event, evt.data);
});
client.on("ui", function (data) {
ui.delegateEvent(data);
});
});
done();
}
};

94
node_modules/browser-sync-ui/lib/client-elements.js generated vendored Normal file
View File

@@ -0,0 +1,94 @@
var fs = require("fs");
const CLIENT_FILES_OPT = "clientFiles";
/**
* Enable a element on clients
* @param clients
* @param ui
* @param bs
* @returns {Function}
*/
var types = {
"css": "text/css",
"js": "application/javascript"
};
function enableElement (clients, ui, bs) {
return function (file) {
var uiItem = ui.getOptionIn([CLIENT_FILES_OPT, file.name]);
var item = uiItem.toJS();
var enableFn = uiItem.getIn(["callbacks", "enable"]);
if (item.active) {
return;
}
ui.setOptionIn([CLIENT_FILES_OPT, item.name, "active"], true, {silent: true});
if (enableFn) {
enableFn.call(ui, item);
}
if (item.file && !item.served) {
ui.setOptionIn([CLIENT_FILES_OPT, item.name, "served"], true, {silent: true});
bs.serveFile(item.src, {
type: types[item.type],
content: fs.readFileSync(item.file)
});
}
addElement(clients, ui.getOptionIn([CLIENT_FILES_OPT, item.name]).toJS());
};
}
/**
* @param clients
* @param ui
* @returns {Function}
*/
function disableElement (clients, ui) {
return function (file) {
var uiItem = ui.getOptionIn([CLIENT_FILES_OPT, file.name]);
var item = uiItem.toJS();
var disableFn = uiItem.getIn(["callbacks", "disable"]);
if (disableFn) {
disableFn.call(ui, item);
}
ui.setOptionIn([CLIENT_FILES_OPT, item.name, "active"], false, {silent: true});
removeElement(clients, item.id);
};
}
/**
* @param clients
* @param item
*/
function addElement (clients, item) {
clients.emit("ui:element:add", item);
}
/**
* @param clients
* @param id
*/
function removeElement(clients, id) {
clients.emit("ui:element:remove", {id: id});
}
module.exports.addElement = addElement;
module.exports.removeElement = removeElement;
module.exports.enable = enableElement;
module.exports.disable = disableElement;

120
node_modules/browser-sync-ui/lib/client-js.js generated vendored Normal file
View File

@@ -0,0 +1,120 @@
"use strict";
(function (window, document, bs, undefined) {
var socket = bs.socket;
var uiOptions = {
bs: {}
};
socket.on("ui:connection", function (options) {
uiOptions = options;
bs.socket.emit("ui:history:connected", {
href: window.location.href
});
});
socket.on("ui:element:remove", function (data) {
if (data.id) {
var elem = document.getElementById(data.id);
if (elem) {
removeElement(elem);
}
}
});
socket.on("highlight", function () {
var id = "__browser-sync-highlight__";
var elem = document.getElementById(id);
if (elem) {
return removeElement(elem);
}
(function (e) {
e.style.position = "fixed";
e.style.zIndex = "1000";
e.style.width = "100%";
e.style.height = "100%";
e.style.borderWidth = "5px";
e.style.borderColor = "red";
e.style.borderStyle = "solid";
e.style.top = "0";
e.style.left = "0";
e.setAttribute("id", id);
document.getElementsByTagName("body")[0].appendChild(e);
})(document.createElement("div"));
});
socket.on("ui:element:add", function (data) {
var elem = document.getElementById(data.id);
if (!elem) {
if (data.type === "css") {
return addCss(data);
}
if (data.type === "js") {
return addJs(data);
}
if (data.type === "dom") {
return addDomNode(data);
}
}
});
bs.addDomNode = addDomNode;
bs.addJs = addJs;
bs.addCss = addJs;
function addJs(data) {
(function (e) {
e.setAttribute("src", getAbsoluteUrl(data.src));
e.setAttribute("id", data.id);
document.getElementsByTagName("body")[0].appendChild(e);
})(document.createElement("script"));
}
function addCss(data) {
(function (e) {
e.setAttribute("rel", "stylesheet");
e.setAttribute("type", "text/css");
e.setAttribute("id", data.id);
e.setAttribute("media", "all");
e.setAttribute("href", getAbsoluteUrl(data.src));
document.getElementsByTagName("head")[0].appendChild(e);
})(document.createElement("link"));
}
function addDomNode(data) {
var elem = document.createElement(data.tagName);
for (var attr in data.attrs) {
elem.setAttribute(attr, data.attrs[attr]);
}
if (data.placement) {
document.getElementsByTagName(data.placement)[0].appendChild(elem);
} else {
document.getElementsByTagName("body")[0].appendChild(elem);
}
return elem;
}
function removeElement(element) {
if (element && element.parentNode) {
element.parentNode.removeChild(element);
}
}
function getAbsoluteUrl(path) {
if (path.match(/^h/)) {
return path;
}
return [window.location.protocol, "//", getHost(), path].join("");
}
function getHost () {
return uiOptions.bs.mode === "snippet" ? window.location.hostname + ":" + uiOptions.bs.port : window.location.host;
}
})(window, document, ___browserSync___);

40
node_modules/browser-sync-ui/lib/config.js generated vendored Normal file
View File

@@ -0,0 +1,40 @@
var Immutable = require("immutable");
/**
* Any configurable paths/config
* @type {{pluginName: string, indexPage: string, socketJs: string, appJs: string, connector: string}}
*/
var defaults = {
pluginName: "UI",
indexPage: "/index.html",
socketJs: "/js/vendor/socket.js",
appJs: "/js/dist/app.js",
app: "/app.js",
appExtraJs: "/js/app-extra.js",
connector: "/js/connector.js",
pagesConfig: "/js/pages-config.js",
public: {
svg: "/img/icons/icons.svg",
css: "/css/core.min.css"
},
clientJs: "/lib/client-js.js",
socket: {
namespace: "/browser-sync-cp"
},
components: {
header: "/components/header.html",
footer: "/components/footer.html"
}
};
module.exports.defaults = defaults;
/**
* @param [userConfig]
* @returns {Map}
*/
module.exports.merge = function (userConfig) {
return Immutable
.fromJS(defaults)
.mergeDeep(userConfig);
};

86
node_modules/browser-sync-ui/lib/directive-stripper.js generated vendored Normal file
View File

@@ -0,0 +1,86 @@
var tokenize = require("html-tokenize");
var through2 = require("through2");
var vinyl = require("vinyl");
var select = require("html-select");
/**
* @param config
* @param item
* @param markup
* @param done
*/
function directiveStripper(config, item, markup, done) {
var replacer = getReplacer(item, config);
var chunks = [];
new vinyl({
contents: new Buffer(markup)
})
.pipe(tokenize())
.pipe(replacer)
.pipe(through2.obj(function (row, buf, next) {
chunks.push(row[1]);
next();
}, function () {
done(null, chunks.join(""));
}));
replacer.resume();
}
/**
* @param name
* @param item
* @returns {*|exports}
*/
function getReplacer (name, markup) {
return select(name, function (e) {
var tr = through2.obj(function (row, buf, next) {
if (row[0] === "open") {
this.push([row[0], directive(name, String(row[1]), markup)]);
} else {
this.push([ row[0], "" ]);
}
next();
});
tr.pipe(e.createStream()).pipe(tr);
});
}
/**
* @param name
* @param content
* @param item
* @returns {*|string}
*/
function directive (name, content, item) {
var angularDir;
try {
angularDir = require("../src/scripts/directives/" + name)();
} catch (e) {
console.log("Directive not found, cannot re-use");
return content;
}
var scope = item;
scope = angularDir.link(scope, {}, {});
return angularDir.template.replace(/\{\{(.+?)\}\}/, function ($1, $2) {
if ($2 in scope) {
return scope[$2];
}
return $1;
});
}
module.exports.getReplacer = getReplacer;
module.exports.directive = directive;
module.exports.directiveStripper = directiveStripper;

271
node_modules/browser-sync-ui/lib/hooks.js generated vendored Normal file
View File

@@ -0,0 +1,271 @@
var fs = require("fs");
var path = require("path");
var pluginTmpl = templateFile("/plugin.tmpl");
var configTmpl = templateFile("/config.tmpl");
var configItem = templateFile("/config.item.tmpl");
var inlineTemp = templateFile("/inline.template.tmpl");
var pluginItemTmpl = fs.readFileSync(path.resolve(__dirname, "../", "templates/plugin.item.tmpl"), "utf-8");
function templateFile (filepath) {
return fs.readFileSync(path.join(__dirname, "/../templates", filepath || ""), "utf-8");
}
/**
* @type {{page: Function, markup: Function, client:js: Function, templates: Function}}
*/
module.exports = {
/**
* Create the url config for each section of the ui
* @param hooks
* @param ui
*/
"page": function (hooks, ui) {
var config = hooks
.map(transformConfig)
.reduce(createConfigItem, {});
return {
/**
* pagesConfig - This is the angular configuration such as routes
*/
pagesConfig: configTmpl
.replace("%when%", hooks.reduce(
createAngularRoutes,
""
))
.replace("%pages%", JSON.stringify(
config,
null,
4
)),
/**
* pagesConfig in object form
*/
pagesObj: config,
pageMarkup: function () {
return preAngular(ui.pluginManager.plugins, config, ui);
}
};
},
/**
* Controller markup for each plugin
* @param hooks
* @returns {*}
*/
"markup": function (hooks) {
return hooks.reduce(pluginTemplate, "");
},
/**
* @param hooks
* @param {UI} ui
* @returns {*|string}
*/
"client:js": function (hooks, ui) {
/**
* Add client JS from Browsersync Plugins
*/
ui.bsPlugins.forEach(function (plugin) {
if (plugin.has("client:js")) {
plugin.get("client:js").forEach(function (value) {
hooks.push(value);
});
}
});
var out = hooks.reduce(function (all, item) {
if (typeof item === "string") {
all += ";" + item;
} else if (Array.isArray(item)) {
item.forEach(function (item) {
all += ";" + item;
});
}
return all;
}, "");
return out;
},
/**
* @param hooks
* @param initial
* @param {UI} ui
* @returns {String}
*/
"templates": function (hooks, initial, ui) {
/**
* Add templates from each Browsersync registered plugin
* @type {string}
*/
var pluginDirectives = ui.bsPlugins.reduce(function (all, plugin) {
if (!plugin.has("templates")) {
return all;
}
/**
* Slugify-ish the plugin name
* eg: Test Browsersync Plugin
* = test-browsersync-plugin
* @type {string}
*/
var slug = plugin.get("name")
.trim()
.split(" ")
.map(function (word) {
return word.trim().toLowerCase();
})
.join("-");
/**
* For every plugin that has templates, wrap
* the markup in the <script type="text/ng-template" id="{{slug}}"></script>
* markup to result in the single output string.
*/
plugin.get("templates").forEach(function (value, key) {
all += angularWrap([slug, path.basename(key)].join("/"), value);
});
return all;
}, "");
/**
* Combine the markup from the plugins done above with any
* others registered via hooks + initial
* to create the final markup
*/
return [pluginDirectives, createInlineTemplates(hooks.concat([initial]))].join("");
},
/**
* Allow plugins to register toggle-able elements
* @param hooks
* @returns {{}}
*/
"elements": function (hooks) {
var obj = {};
hooks.forEach(function (elements) {
elements.forEach(function (item) {
if (!obj[item.name]) {
obj[item.name] = item;
}
});
});
return obj;
}
};
/**
* @param hooks
* @returns {String}
*/
function createInlineTemplates (hooks) {
return hooks.reduce(function (combined, item) {
return combined + item.reduce(function (all, filepath) {
return all + angularWrap(
path.basename(filepath),
fs.readFileSync(filepath));
}, "");
}, "");
}
/**
* @param item
* @returns {*}
*/
function transformConfig (item) {
return item;
}
/**
* @param {String} all
* @param {Object} item
* @returns {*}
*/
function createAngularRoutes(all, item) {
return all + configItem.replace(/%(.+)%/g, function () {
var key = arguments[1];
if (item[key]) {
return item[key];
}
});
}
/**
* @param joined
* @param item
* @returns {*}
*/
function createConfigItem (joined, item) {
if (item.path === "/") {
joined["overview"] = item;
} else {
joined[item.path.slice(1)] = item;
}
return joined;
}
/**
* @returns {*}
*/
function pluginTemplate (combined, item) {
return [combined, pluginTmpl.replace("%markup%", item)].join("\n");
}
/**
* @param plugins
* @param config
* @returns {*}
*/
function preAngular (plugins, config, ui) {
return Object.keys(plugins)
.filter(function (key) {
return config[key]; // only work on plugins that have pages
})
.map(function (key) {
if (key === "plugins") {
var pluginMarkup = ui.bsPlugins.reduce(function (all, item, i) {
all += pluginItemTmpl
.replace("%content%", item.get("markup") || "")
.replace(/%index%/g, i)
.replace(/%name%/g, item.get("name"));
return all;
}, "");
plugins[key].hooks.markup = plugins[key].hooks.markup.replace("%pluginlist%", pluginMarkup);
}
return angularWrap(config[key].template, bindOnce(plugins[key].hooks.markup, config[key]));
})
.reduce(function (combined, item) {
return combined + item;
}, "");
}
/**
* @param templateName
* @param markup
* @returns {*}
*/
function angularWrap (templateName, markup) {
return inlineTemp
.replace("%content%", markup)
.replace("%id%", templateName);
}
/**
* @param markup
* @param config
* @returns {*|string}
*/
function bindOnce (markup, config) {
return markup.toString().replace(/\{\{ctrl.section\.(.+?)\}\}/g, function ($1, $2) {
return config[$2] || "";
});
}
module.exports.bindOnce = bindOnce;

28
node_modules/browser-sync-ui/lib/opts.js generated vendored Normal file
View File

@@ -0,0 +1,28 @@
var Immutable = require("immutable");
var defaults = Immutable.fromJS({
port: 3001
});
/**
* @param {Object} obj
* @returns {Map}
*/
module.exports.merge = function (obj) {
return defaults.mergeDeep(Immutable.fromJS(obj));
};
/**
* @param {Immutable.Map} obj
* @returns {*}
*/
//function transformOptions(obj) {
//
// var out;
//
// Object.keys(transforms).forEach(function (key) {
// out = obj.set(key, transforms[key](obj));
// });
//
// return out;
//}

View File

@@ -0,0 +1,69 @@
(function (angular) {
const SECTION_NAME = "connections";
angular
.module("BrowserSync")
.controller("ConnectionsController", [
"pagesConfig",
ConnectionsControllers
]);
/**
* @param pagesConfig
* @constructor
*/
function ConnectionsControllers(pagesConfig) {
var ctrl = this;
ctrl.section = pagesConfig[SECTION_NAME];
}
angular
.module("BrowserSync")
.directive("connectionList", function () {
return {
restrict: "E",
scope: {
options: "="
},
templateUrl: "connections.directive.html",
controller: ["$scope", "Clients", "Socket", connectionListDirective],
controllerAs: "ctrl"
};
});
/**
* Controller for the URL sync
* @param $scope - directive scope
* @param Clients
* @param Socket
*/
function connectionListDirective($scope, Clients, Socket) {
var ctrl = this;
ctrl.connections = [];
ctrl.update = function (data) {
ctrl.connections = data;
$scope.$digest();
};
// Always try to retreive the sockets first time.
Socket.getData("clients").then(function (data) {
ctrl.connections = data;
});
// Listen to events to update the list on the fly
Socket.on("ui:connections:update", ctrl.update);
$scope.$on("$destroy", function () {
Socket.off("ui:connections:update", ctrl.update);
});
ctrl.highlight = function (connection) {
Clients.highlight(connection);
};
}
})(angular);

View File

@@ -0,0 +1,10 @@
<ul bs-list="basic" ng-show="ctrl.connections" id="bs-connection-list">
<li ng-repeat="connection in ctrl.connections track by connection.id">
<p>{{connection.browser.name}} - ({{connection.browser.version}})</p>
<!--<span bs-multi-controls="right">
<a href="#" ng-click="highlight(connection)" bs-button>
<svg bs-svg-icon><use xlink:href="#svg-target"></use></svg> Highlight
</a>
</span>-->
</li>
</ul>

View File

@@ -0,0 +1,18 @@
<div bs-panel="controls outline">
<h1 bs-heading><icon icon="{{section.icon}}"></icon> {{section.title}}</h1>
</div>
<div bs-panel ng-if="!ui.connections.length">
<div bs-panel-content="basic">
<p>Connected devices/browsers will be listed here. If you are not seeing your device in the list,
it's probably because the Browsersync script tag is not being loaded on your page.</p>
<p>
Browsersync works by injecting an asynchronous script tag (<code>&lt;script async&gt;...&lt;/script&gt;</code>) right after the &lt;body&gt; tag during initial request. In order for this to work properly the &lt;body&gt; tag must be present. Alternatively you can provide a custom rule for the snippet using snippetOptions
</p>
</div>
</div>
<div bs-skinny>
<connection-list ng-if="ui.connections"
options="options"
connections="ui.connections"></connection-list>
</div>

View File

@@ -0,0 +1,45 @@
var connections = require("./lib/connections");
const PLUGIN_NAME = "Connections";
/**
* @type {{plugin: Function, plugin:name: string, markup: string}}
*/
module.exports = {
/**
* @param {UI} ui
* @param {BrowserSync} bs
*/
"plugin": function (ui, bs) {
connections.init(ui, bs);
},
/**
* Hooks
*/
"hooks": {
"client:js": fileContent("/connections.client.js"),
"templates": [
getPath("/connections.directive.html")
]
},
/**
* Plugin name
*/
"plugin:name": PLUGIN_NAME
};
/**
* @param filepath
* @returns {*}
*/
function getPath (filepath) {
return require("path").join(__dirname, filepath);
}
/**
* @param filepath
* @returns {*}
*/
function fileContent (filepath) {
return require("fs").readFileSync(getPath(filepath), "utf-8");
}

View File

@@ -0,0 +1,132 @@
var Immutable = require("immutable");
/**
* Track connected clients
* @param {UI} ui
* @param {BrowserSync} bs
*/
module.exports.init = function (ui, bs) {
var uaParser = new bs.utils.UAParser();
var currentConnections = [];
ui.clients.on("connection", function (client) {
client.on("client:heartbeat", function (data) {
var match;
if (currentConnections.some(function (item, index) {
if (item.id === client.id) {
match = index;
return true;
}
return false;
})) {
if (typeof match === "number") {
currentConnections[match].timestamp = new Date().getTime();
currentConnections[match].data = data;
}
} else {
currentConnections.push({
id: client.id,
timestamp: new Date().getTime(),
browser: uaParser.setUA(client.handshake.headers["user-agent"]).getBrowser(),
data: data
});
}
});
});
var registry;
var temp;
var initialSent;
var int = setInterval(function () {
var sockets = ui.clients.sockets;
var keys = Object.keys(sockets);
if (keys.length) {
temp = Immutable.List(keys.map(function (clientKey) {
var currentClient = sockets[clientKey];
return Immutable.fromJS({
id: currentClient.id,
browser: uaParser.setUA(currentClient.handshake.headers["user-agent"]).getBrowser()
});
}));
if (!registry) {
registry = temp;
sendUpdated(ui.socket, decorateClients(registry.toJS(), currentConnections));
} else {
if (Immutable.is(registry, temp)) {
if (!initialSent) {
sendUpdated(ui.socket, decorateClients(registry.toJS(), currentConnections));
initialSent = true;
}
} else {
registry = temp;
sendUpdated(ui.socket, decorateClients(registry.toJS(), currentConnections));
}
}
} else {
sendUpdated(ui.socket, []);
}
}, 1000);
bs.registerCleanupTask(function () {
clearInterval(int);
});
};
/**
* Use heart-beated data to decorate clients
* @param clients
* @param clientsInfo
* @returns {*}
*/
function decorateClients(clients, clientsInfo) {
return clients.map(function (item) {
clientsInfo.forEach(function (client) {
if (client.id === item.id) {
item.data = client.data;
return false;
}
});
return item;
});
}
/**
* @param socket
* @param connectedClients
*/
function sendUpdated(socket, connectedClients) {
socket.emit("ui:connections:update", connectedClients);
}
/**
* @param clients
* @param data
*/
//function highlightClient (clients, data) {
// var socket = getClientById(clients, data.id);
// if (socket) {
// socket.emit("highlight");
// }
//}
/**
* @param clients
* @param id
*/
//function getClientById (clients, id) {
// var match;
// clients.sockets.some(function (item, i) {
// if (item.id === id) {
// match = clients.sockets[i];
// return true;
// }
// });
// return match;
//}

View File

@@ -0,0 +1,24 @@
(function (angular) {
const SECTION_NAME = "history";
angular
.module("BrowserSync")
.controller("HelpAboutController", [
"options",
"pagesConfig",
helpAboutController
]);
/**
* @param options
* @param pagesConfig
*/
function helpAboutController(options, pagesConfig) {
var ctrl = this;
ctrl.options = options.bs;
ctrl.section = pagesConfig[SECTION_NAME];
}
})(angular);

View File

View File

@@ -0,0 +1,8 @@
<div bs-panel="controls outline">
<h1 bs-heading><icon icon="{{ctrl.section.icon}}"></icon> {{ctrl.section.title}}</h1>
</div>
<div bs-panel id="bs-help">
<div bs-panel-content="basic">
<p>Help page</p>
</div>
</div>

View File

@@ -0,0 +1,49 @@
const PLUGIN_NAME = "Help / About";
/**
* @type {{plugin: Function, plugin:name: string, markup: string}}
*/
module.exports = {
/**
* Plugin init
*/
"plugin": function () {},
/**
* Hooks
*/
"hooks": {
"markup": fileContent("/../../../static/content/help.content.html"),
"client:js": fileContent("/help.client.js"),
"templates": [
getPath("/help.directive.html")
],
"page": {
path: "/help",
title: PLUGIN_NAME,
template: "help.html",
controller: "HelpAboutController",
order: 6,
icon: "help"
}
},
/**
* Plugin name
*/
"plugin:name": PLUGIN_NAME
};
/**
* @param filepath
* @returns {*}
*/
function getPath (filepath) {
return require("path").join(__dirname, filepath);
}
/**
* @param filepath
* @returns {*}
*/
function fileContent (filepath) {
return require("fs").readFileSync(getPath(filepath), "utf-8");
}

View File

@@ -0,0 +1,111 @@
(function (angular) {
const SECTION_NAME = "history";
angular
.module("BrowserSync")
.controller("HistoryController", [
"$scope",
"options",
"History",
"pagesConfig",
historyController
]);
/**
* @param $scope
* @param options
* @param History
* @param pagesConfig
*/
function historyController($scope, options, History, pagesConfig) {
var ctrl = this;
ctrl.options = options.bs;
ctrl.section = pagesConfig[SECTION_NAME];
ctrl.visited = [];
ctrl.update = function (items) {
ctrl.visited = items;
$scope.$digest();
};
History.get().then(function (items) {
ctrl.visited = items;
});
History.on("change", ctrl.update);
$scope.$on("$destroy", function () {
History.off(ctrl.update);
});
ctrl.clearVisited = function () {
History.clear();
};
}
angular
.module("BrowserSync")
.directive("historyList", function () {
return {
restrict: "E",
scope: {
options: "=",
visited: "="
},
templateUrl: "history.directive.html",
controller: ["$scope", "History", "Clients", historyDirective],
controllerAs: "ctrl"
};
});
/**
* Controller for the URL sync
* @param $scope - directive scope
* @param History
* @param Clients
*/
function historyDirective($scope, History, Clients) {
var ctrl = this;
ctrl.visited = [];
ctrl.utils = {};
ctrl.utils.localUrl = function (path) {
return [$scope.options.urls.local, path].join("");
};
ctrl.updateVisited = function (data) {
ctrl.visited = data;
$scope.$digest();
};
ctrl.sendAllTo = function (url) {
url.success = true;
Clients.sendAllTo(url.path);
setTimeout(function () {
url.success = false;
$scope.$digest();
}, 1000);
};
ctrl.removeVisited = function (item) {
History.remove(item);
};
History.get().then(function (items) {
ctrl.visited = items;
});
History.on("change", ctrl.updateVisited);
$scope.$on("$destroy", function () {
History.off(ctrl.updateVisited);
});
}
})(angular);

View File

@@ -0,0 +1,20 @@
<ul bs-list="bordered inline-controls" ng-if="ctrl.visited" id="bs-history-list">
<li ng-repeat="url in ctrl.visited track by $index">
<p>{{url.path}}</p>
<div bs-button-group>
<new-tab url="{{ctrl.utils.localUrl(url.path)}}" mode="options.mode"></new-tab>
<a href="#"
title="Sync all devices to this address."
bs-button="subtle-alt icon-left"
ng-click="ctrl.sendAllTo(url)"
ng-class="{success: url.success}"
>
<icon icon="circle-ok" bs-state="success"></icon>
<icon icon="syncall" bs-state="default"></icon> Sync all
</a>
<a href="#" bs-button="subtle-alt icon" bs-remove ng-click="ctrl.removeVisited(url)">
<icon icon="bin"></icon>
</a>
</div>
</li>
</ul>

View File

@@ -0,0 +1,16 @@
<div bs-panel="controls outline">
<h1 bs-heading><icon icon="{{ctrl.section.icon}}"></icon> {{ctrl.section.title}}</h1>
</div>
<div bs-button-row ng-if="ctrl.visited.length">
<button bs-button="icon-left inline" ng-click="ctrl.clearVisited()" ng-show="ctrl.visited.length">
<svg bs-svg-icon><use xlink:href="#svg-bin"></use></svg>
Clear all
</button>
</div>
<div bs-panel ng-if="!ctrl.visited.length" id="bs-history-empty">
<div bs-panel-content="basic">
<p>Pages you navigate to will appear here - making it easy
to sync all devices to a specific page</p>
</div>
</div>
<history-list options="ctrl.options"></history-list>

View File

@@ -0,0 +1,132 @@
var url = require("url");
var Immutable = require("immutable");
module.exports.init = function (ui, bs) {
var validUrls = Immutable.OrderedSet();
var methods = {
/**
* Send the url list to UI
* @param urls
*/
sendUpdatedUrls: function (urls) {
ui.socket.emit("ui:history:update", decorateUrls(urls));
},
/**
* Only send to UI if list changed
* @param current
* @param temp
*/
sendUpdatedIfChanged: function (current, temp) {
if (!Immutable.is(current, temp)) {
validUrls = temp;
methods.sendUpdatedUrls(validUrls);
}
},
/**
* Send all clients to a URL - this is a proxy
* in case we need to limit/check anything.
* @param data
*/
sendToUrl: function (data) {
var parsed = url.parse(data.path);
data.override = true;
data.path = parsed.path;
data.url = parsed.href;
ui.clients.emit("browser:location", data);
},
/**
* Add a new path
* @param data
*/
addPath: function (data) {
var temp = addPath(validUrls, url.parse(data.href), bs.options.get("mode"));
methods.sendUpdatedIfChanged(validUrls, temp, ui.socket);
},
/**
* Remove a path
* @param data
*/
removePath: function (data) {
var temp = removePath(validUrls, data.path);
methods.sendUpdatedIfChanged(validUrls, temp, ui.socket);
},
/**
* Get the current list
*/
getVisited: function () {
ui.socket.emit("ui:receive:visited", decorateUrls(validUrls));
}
};
ui.clients.on("connection", function (client) {
client.on("ui:history:connected", methods.addPath);
});
ui.socket.on("connection", function (uiClient) {
/**
* Send urls on first connection
*/
uiClient.on("ui:get:visited", methods.getVisited);
methods.sendUpdatedUrls(validUrls);
});
ui.listen("history", {
"sendAllTo": methods.sendToUrl,
"remove": methods.removePath,
"clear": function () {
validUrls = Immutable.OrderedSet([]);
methods.sendUpdatedUrls(validUrls);
}
});
return methods;
};
/**
* @param {Immutable.Set} urls
* @returns {Array}
*/
function decorateUrls (urls) {
var count = 0;
return urls.map(function (value) {
count += 1;
return {
path: value,
key: count
};
}).toJS().reverse();
}
/**
* If snippet mode, add the full URL
* if server/proxy, add JUST the path
* @param immSet
* @param urlObj
* @param mode
* @returns {Set}
*/
function addPath(immSet, urlObj, mode) {
return immSet.add(
mode === "snippet"
? urlObj.href
: urlObj.path
);
}
module.exports.addPath = addPath;
/**
* @param immSet
* @param urlPath
* @returns {*}
*/
function removePath(immSet, urlPath) {
return immSet.remove(url.parse(urlPath).path);
}
module.exports.removePath = removePath;

View File

@@ -0,0 +1,54 @@
var historyPlugin = require("./history");
const PLUGIN_NAME = "History";
/**
* @type {{plugin: Function, plugin:name: string, markup: string}}
*/
module.exports = {
/**
* @param ui
* @param bs
*/
"plugin": function (ui, bs) {
ui.history = historyPlugin.init(ui, bs);
},
/**
* Hooks
*/
"hooks": {
"markup": fileContent("history.html"),
"client:js": fileContent("/history.client.js"),
"templates": [
getPath("/history.directive.html")
],
"page": {
path: "/history",
title: PLUGIN_NAME,
template: "history.html",
controller: PLUGIN_NAME + "Controller",
order: 3,
icon: "list2"
}
},
/**
* Plugin name
*/
"plugin:name": PLUGIN_NAME
};
/**
* @param filepath
* @returns {*}
*/
function getPath (filepath) {
return require("path").join(__dirname, filepath);
}
/**
* @param filepath
* @returns {*}
*/
function fileContent (filepath) {
return require("fs").readFileSync(getPath(filepath), "utf-8");
}

View File

@@ -0,0 +1,201 @@
(function (angular) {
const SECTION_NAME = "network-throttle";
angular
.module("BrowserSync")
.controller("NetworkThrottleController", [
"options",
"pagesConfig",
"Socket",
"$scope",
NetworkThrottleController
]);
/**
* @param options
* @param pagesConfig
* @param Socket
* @param $scope
*/
function NetworkThrottleController (options, pagesConfig, Socket, $scope) {
var ctrl = this;
ctrl.section = pagesConfig[SECTION_NAME];
ctrl.options = options.bs;
ctrl.uiOptions = options.ui;
ctrl.clientFiles = options.ui.clientFiles || {};
ctrl.section = pagesConfig[SECTION_NAME];
ctrl.throttle = ctrl.uiOptions[SECTION_NAME];
ctrl.selected = ctrl.throttle.targets[0].id;
ctrl.servers = ctrl.throttle.servers;
ctrl.port = "";
ctrl.portEntry = "auto";
ctrl.serverCount = Object.keys(ctrl.servers).length;
ctrl.blurs = [];
ctrl.state = {
success: false,
waiting: false,
classname: "ready"
};
ctrl.createServer = function (selected, event) {
if (ctrl.blurs.indexOf(event.target) === -1) {
ctrl.blurs.push(event.target);
}
var item = getByProp(ctrl.throttle.targets, "id", ctrl.selected);
if (ctrl.portEntry === "auto") {
return send("");
}
if (!ctrl.port || !ctrl.port.length) {
setError();
return;
}
if (!ctrl.port.match(/\d{4,5}/)) {
setError();
return;
}
var port = parseInt(ctrl.port, 10);
if (port < 1024 || port > 65535) {
setError();
return;
}
send(ctrl.port);
function setError() {
ctrl.state.waiting = false;
ctrl.state.portError = true;
}
function send (port) {
ctrl.state.classname = "waiting";
ctrl.state.waiting = true;
Socket.uiEvent({
namespace: SECTION_NAME,
event: "server:create",
data: {
speed: item,
port: port
}
});
}
};
ctrl.destroyServer = function (item, port) {
Socket.uiEvent({
namespace: SECTION_NAME,
event: "server:destroy",
data: {
speed: item,
port: port
}
});
};
ctrl.toggleSpeed = function (item) {
if (!item.active) {
item.urls = [];
}
};
ctrl.update = function (data) {
ctrl.servers = data.servers;
ctrl.serverCount = Object.keys(ctrl.servers).length;
if (data.event === "server:create") {
updateButtonState();
}
$scope.$digest();
};
function updateButtonState() {
ctrl.state.success = true;
ctrl.state.classname = "success";
setTimeout(function () {
ctrl.blurs.forEach(function (elem) {
elem.blur();
});
setTimeout(function () {
ctrl.state.success = false;
ctrl.state.waiting = false;
ctrl.state.classname = "ready";
$scope.$digest();
}, 500);
}, 300);
}
/**
* @param collection
* @param prop
* @returns {*}
*/
function getByProp (collection, prop, name) {
var match = collection.filter(function (item) {
return item[prop] === name;
});
if (match.length) {
return match[0];
}
return false;
}
Socket.on("ui:network-throttle:update", ctrl.update);
$scope.$on("$destroy", function () {
Socket.off("ui:network-throttle:update", ctrl.update);
});
}
/**
* Display the snippet when in snippet mode
*/
angular
.module("BrowserSync")
.directive("throttle", function () {
return {
restrict: "E",
replace: true,
scope: {
"target": "=",
"options": "="
},
templateUrl: "network-throttle.directive.html",
controller: ["$scope", "Socket", throttleDirectiveControlller],
controllerAs: "ctrl"
};
});
/**
* @param $scope
*/
function throttleDirectiveControlller ($scope) {
var ctrl = this;
ctrl.throttle = $scope.options[SECTION_NAME];
}
})(angular);

View File

@@ -0,0 +1,12 @@
<section bs-panel-content>
<div ng-if="target.active">
<p ng-if="!target.urls.length">
Creating a throttled server, please wait...
</p>
<div ng-if="target.urls.length">
<ul bs-list>
<li ng-repeat="url in target.urls"><a href="{{url}}">{{url}}</a></li>
</ul>
</div>
</div>
</section>

View File

@@ -0,0 +1,93 @@
<article>
<div bs-panel="controls outline">
<h1 bs-heading>
<icon icon="{{ctrl.section.icon}}"></icon>
{{ctrl.section.title}}
</h1>
</div>
<div bs-panel="no-border" ng-if="ctrl.options.mode === 'snippet'">
<div bs-panel-content="basic">
<p class="lede">Sorry, Network Throttling is only available in Server or Proxy mode.</p>
</div>
</div>
<div bs-panel="no-border" ng-if="ctrl.options.mode !== 'snippet'">
<div bs-panel-content="basic">
<div bs-inputs bs-grid="wide-3 desk-2">
<div bs-grid-item>
<p bs-label-heading>Speed</p>
<div bs-input="inline" ng-repeat="(key, item) in ctrl.throttle.targets | orderObjectBy:'order'">
<input
type="radio"
id="speed-{{item.id}}"
checked name="speed"
ng-model="ctrl.selected"
value="{{item.id}}">
<label for="speed-{{item.id}}" bs-input-label="light">{{item.title}}</label>
</div>
</div>
<div bs-grid-item>
<p bs-label-heading>Port</p>
<div bs-input="text">
<div bs-input="inline">
<input type="radio" name="port-select" id="port-auto" checked value="auto"
ng-model="ctrl.portEntry">
<label for="port-auto" bs-input-label="light">Auto Detection</label>
</div>
<div bs-input="inline">
<input type="radio" id="port-manual" name="port-select" value="manual" ng-model="ctrl.portEntry">
<label for="port-manual" bs-input-label="light">User specified <span ng-if="ctrl.state.portError">(between
1024 & 65535)</span></label>
</div>
<input id="server-port"
type="text"
value=""
placeholder="Eg: 1024"
ng-model="ctrl.port"
ng-focus="ctrl.portEntry = 'manual'"
custom-validation>
</div>
<br/>
<div ng-class="[ctrl.state.classname]" bs-state-wrapper>
<button
id="create-server"
bs-button="size-small subtle-alt icon-left"
ng-click="ctrl.createServer(ctrl.selected, $event)"
ng-disabled="ctrl.state.waiting"
>
<icon icon="circle-plus"></icon>
Create Server
</button>
<div bs-state-icons>
<icon icon="circle-ok" bs-state="success inline"></icon>
<icon icon="circle-minus" bs-state="waiting inline" bs-anim="spin"></icon>
</div>
</div>
</div>
<div bs-grid-item>
</div>
</div>
</div>
<br/>
<div bs-panel-content="basic">
<h3 ng-if="ctrl.serverCount">Your Servers:</h3>
<h3 ng-if="!ctrl.serverCount">Your Servers will appear here...</h3>
</div>
<ul bs-list="bordered inline-controls" bs-offset="basic" id="throttle-server-list">
<li ng-repeat="(key, item) in ctrl.servers track by key">
<p bs-width="5">{{$index + 1}}.</p>
<p bs-width="10"><b>{{item.speed.id | uppercase}}</b></p>
<p><a href="{{item.urls[0]}}">{{item.urls[0]}}</a></p>
<p><a href="{{item.urls[1]}}">{{item.urls[1]}}</a></p>
<div bs-button-group>
<button href="#" bs-button="subtle-alt icon" ng-click="ctrl.destroyServer(item, key)">
<svg bs-svg-icon><use xlink:href="#svg-bin"></use></svg>
</button>
</div>
</li>
</ul>
</div>
</article>

View File

@@ -0,0 +1,160 @@
var Immutable = require("immutable");
module.exports.init = function (ui) {
var optPath = ["network-throttle"];
var serverOptPath = optPath.concat(["servers"]);
var listenHost = ui.options.get("listen");
ui.servers = {};
ui.setOptionIn(optPath, Immutable.fromJS({
name: "network-throttle",
title: "Network Throttle",
active: false,
targets: require("./targets")
}));
ui.setOptionIn(serverOptPath, Immutable.Map({}));
/**
* @param input
* @returns {number}
*/
function getPortArg(input) {
input = input.trim();
if (input.length && input.match(/\d{3,5}/)) {
input = parseInt(input, 10);
} else {
input = ui.bs.options.get("port") + 1;
}
return input;
}
/**
* @returns {string}
*/
function getTargetUrl() {
return require("url").parse(ui.bs.options.getIn(["urls", "local"]));
}
var methods = {
/**
* @param data
*/
"server:create": function (data) {
data.port = getPortArg(data.port);
data.cb = data.cb || function () { /* noop */};
/**
* @param opts
*/
function saveThrottleInfo (opts) {
var urls = getUrls(ui.bs.options.set("port", opts.port).toJS());
ui.setOptionIn(serverOptPath.concat([opts.port]), Immutable.fromJS({
urls: urls,
speed: opts.speed
}));
setTimeout(function () {
ui.socket.emit("ui:network-throttle:update", {
servers: ui.getOptionIn(serverOptPath).toJS(),
event: "server:create"
});
ui.servers[opts.port] = opts.server;
data.cb(null, opts);
}, 300);
}
/**
* @param err
* @param port
*/
function createThrottle (err, port) {
var target = getTargetUrl();
var args = {
port: port,
target: target,
speed: data.speed
};
if (ui.bs.getOption("scheme") === "https") {
var httpsOpts = require("browser-sync/lib/server/utils").getHttpsOptions(ui.bs.options);
args.key = httpsOpts.key;
args.cert = httpsOpts.cert;
}
args.server = require("./throttle-server")(args, listenHost);
require('server-destroy')(args.server);
args.server.listen(port, listenHost);
saveThrottleInfo(args);
}
/**
* Try for a free port
*/
ui.bs.utils.portscanner.findAPortNotInUse(data.port, data.port + 100, (listenHost || "127.0.0.1"), function (err, port) {
if (err) {
return createThrottle(err);
} else {
createThrottle(null, port);
}
});
},
/**
* @param data
*/
"server:destroy": function (data) {
if (ui.servers[data.port]) {
ui.servers[data.port].destroy();
ui.setMany(function (item) {
item.deleteIn(serverOptPath.concat([parseInt(data.port, 10)]));
});
delete ui.servers[data.port];
}
ui.socket.emit("ui:network-throttle:update", {
servers: ui.getOptionIn(serverOptPath).toJS(),
event: "server:destroy"
});
},
/**
* @param event
*/
event: function (event) {
methods[event.event](event.data);
}
};
return methods;
};
/**
* Get local + external urls with a different port
* @param opts
* @returns {List<T>|List<any>}
*/
function getUrls (opts) {
var list = [];
var bsLocal = require("url").parse(opts.urls.local);
list.push([bsLocal.protocol + "//", bsLocal.hostname, ":", opts.port].join(""));
if (opts.urls.external) {
var external = require("url").parse(opts.urls.external);
list.push([bsLocal.protocol + "//", external.hostname, ":", opts.port].join(""));
}
return Immutable.List(list);
}

View File

@@ -0,0 +1,53 @@
var networkThrottle = require("./network-throttle");
const PLUGIN_NAME = "Network Throttle";
/**
* @type {{plugin: Function, plugin:name: string, markup: string}}
*/
module.exports = {
/**
* Plugin init
*/
"plugin": function (ui, bs) {
ui.throttle = networkThrottle.init(ui, bs);
ui.listen("network-throttle", ui.throttle);
},
/**
* Hooks
*/
"hooks": {
"markup": fileContent("/network-throttle.html"),
"client:js": [fileContent("/network-throttle.client.js")],
"templates": [],
"page": {
path: "/network-throttle",
title: PLUGIN_NAME,
template: "network-throttle.html",
controller: "NetworkThrottleController",
order: 5,
icon: "time"
}
},
/**
* Plugin name
*/
"plugin:name": PLUGIN_NAME
};
/**
* @param filepath
* @returns {*}
*/
function getPath (filepath) {
return require("path").join(__dirname, filepath);
}
/**
* @param filepath
* @returns {*}
*/
function fileContent (filepath) {
return require("fs").readFileSync(getPath(filepath));
}

View File

@@ -0,0 +1,57 @@
module.exports = [
{
active: false,
title: "DSL (2Mbs, 5ms RTT)",
id: "dsl",
speed: 200,
latency: 5,
urls: [],
order: 1
},
{
active: false,
title: "4G (4Mbs, 20ms RTT)",
id: "4g",
speed: 400,
latency: 10,
urls: [],
order: 2
},
{
active: false,
title: "3G (750kbs, 100ms RTT)",
id: "3g",
speed: 75,
latency: 50,
urls: [],
order: 3
},
{
active: false,
id: "good-2g",
title: "Good 2G (450kbs, 150ms RTT)",
speed: 45,
latency: 75,
urls: [],
order: 4
},
{
active: false,
id: "2g",
title: "Regular 2G (250kbs, 300ms RTT)",
speed: 25,
latency: 150,
urls: [],
order: 5
},
{
active: false,
id: "gprs",
title: "GPRS (50kbs, 500ms RTT)",
speed: 5,
latency: 250,
urls: [],
order: 6
}
];

View File

@@ -0,0 +1,70 @@
var ThrottleGroup = require("stream-throttle").ThrottleGroup;
module.exports = throttle;
/**
*
*/
function throttle (opts, listenHost) {
var options = {
local_host: listenHost,
remote_host: listenHost,
upstream: 10*1024,
downstream: opts.speed.speed * 1024,
keepalive: false
};
var serverOpts = {
allowHalfOpen: true,
rejectUnauthorized: false
};
var module = "net";
var method = "createConnection";
if (opts.key) {
module = "tls";
method = "connect";
serverOpts.key = opts.key;
serverOpts.cert = opts.cert;
}
return require(module).createServer(serverOpts, function (local) {
var remote = require(module)[method]({
host: opts.target.hostname,
port: opts.target.port,
allowHalfOpen: true,
rejectUnauthorized: false
});
var upThrottle = new ThrottleGroup({ rate: options.upstream });
var downThrottle = new ThrottleGroup({ rate: options.downstream });
var localThrottle = upThrottle.throttle();
var remoteThrottle = downThrottle.throttle();
setTimeout(function () {
local
.pipe(localThrottle)
.pipe(remote);
}, opts.speed.latency);
setTimeout(function () {
remote
.pipe(remoteThrottle)
.pipe(local);
}, opts.speed.latency);
local.on("error", function() {
remote.destroy();
local.destroy();
});
remote.on("error", function() {
local.destroy();
remote.destroy();
});
});
}

View File

@@ -0,0 +1,131 @@
(function (angular) {
const SECTION_NAME = "overview";
angular
.module("BrowserSync")
.controller("OverviewController", [
"options",
"pagesConfig",
OverviewController
]);
/**
* @param options
* @param pagesConfig
*/
function OverviewController (options, pagesConfig) {
var ctrl = this;
ctrl.section = pagesConfig[SECTION_NAME];
ctrl.options = options.bs;
ctrl.ui = {
snippet: !ctrl.options.server && !ctrl.options.proxy
};
}
/**
* Url Info - this handles rendering of each server
* info item
*/
angular
.module("BrowserSync")
.directive("urlInfo", function () {
return {
restrict: "E",
replace: true,
scope: {
"options": "="
},
templateUrl: "url-info.html",
controller: [
"$scope",
"$rootScope",
"Clients",
urlInfoController
]
};
});
/**
* @param $scope
* @param $rootScope
* @param Clients
*/
function urlInfoController($scope, $rootScope, Clients) {
var options = $scope.options;
var urls = options.urls;
$scope.ui = {
server: false,
proxy: false
};
if ($scope.options.mode === "server") {
$scope.ui.server = true;
if (!Array.isArray($scope.options.server.baseDir)) {
$scope.options.server.baseDir = [$scope.options.server.baseDir];
}
}
if ($scope.options.mode === "proxy") {
$scope.ui.proxy = true;
}
$scope.urls = [];
$scope.urls.push({
title: "Local",
tagline: "URL for the machine you are running BrowserSync on",
url: urls.local,
icon: "imac"
});
if (urls.external) {
$scope.urls.push({
title: "External",
tagline: "Other devices on the same wifi network",
url: urls.external,
icon: "wifi"
});
}
if (urls.tunnel) {
$scope.urls.push({
title: "Tunnel",
tagline: "Secure HTTPS public url",
url: urls.tunnel,
icon: "globe"
});
}
/**
*
*/
$scope.sendAllTo = function (path) {
Clients.sendAllTo(path);
$rootScope.$emit("notify:flash", {
heading: "Instruction sent:",
message: "Sync all Browsers to: " + path
});
};
}
/**
* Display the snippet when in snippet mode
*/
angular
.module("BrowserSync")
.directive("snippetInfo", function () {
return {
restrict: "E",
replace: true,
scope: {
"options": "="
},
templateUrl: "snippet-info.html",
controller: ["$scope", function snippetInfoController() {/*noop*/}]
};
});
})(angular);

View File

@@ -0,0 +1,25 @@
<article>
<div bs-panel="controls outline">
<h1 bs-heading>
<icon icon="{{ctrl.section.icon}}"></icon>
{{ctrl.section.title}}
</h1>
</div>
<url-info ng-if="ctrl.options.server || ctrl.options.proxy" options="ctrl.options"></url-info>
<snippet-info ng-if="ctrl.options && ctrl.ui.snippet" options="ctrl.options"></snippet-info>
<div bs-panel="full">
<div bs-panel-content>
<div bs-panel-icon>
<svg bs-svg-icon><use xlink:href="#svg-devices"></use></svg>
</div>
<p bs-text="lede">Current Connections</p>
<p>Connected browsers will be listed here.</p>
<connection-list options="ctrl.options"></connection-list>
</div>
</div>
</article>

View File

@@ -0,0 +1,51 @@
const PLUGIN_NAME = "Overview";
/**
* @type {{plugin: Function, plugin:name: string, markup: string}}
*/
module.exports = {
/**
* Plugin init
*/
"plugin": function () { /* noop */ },
/**
* Hooks
*/
"hooks": {
"markup": fileContent("/overview.html"),
"client:js": fileContent("/overview.client.js"),
"templates": [
getPath("/snippet-info.html"),
getPath("/url-info.html")
],
"page": {
path: "/",
title: PLUGIN_NAME,
template: "overview.html",
controller: PLUGIN_NAME.replace(" ", "") + "Controller",
order: 1,
icon: "cog"
}
},
/**
* Plugin name
*/
"plugin:name": PLUGIN_NAME
};
/**
* @param filepath
* @returns {*}
*/
function getPath (filepath) {
return require("path").join(__dirname, filepath);
}
/**
* @param filepath
* @returns {*}
*/
function fileContent (filepath) {
return require("fs").readFileSync(getPath(filepath), "utf-8");
}

View File

@@ -0,0 +1,10 @@
<div bs-panel="full">
<div bs-panel-content>
<div bs-panel-icon>
<svg bs-svg-icon=""><use xlink:href="#svg-code"></use></svg>
</div>
<p bs-text="lede">Place this snippet somewhere before the closing <code>&lt;/body&gt;</code> tag in your website</p>
<pre><code>{{options.snippet}}</code></pre>
</div>
</div>

View File

@@ -0,0 +1,45 @@
<div>
<section bs-panel ng-repeat="url in urls">
<div bs-panel-content>
<div bs-panel-icon>
<icon icon="{{url.icon}}"></icon>
</div>
<p bs-text="lede">{{url.title}}</p>
<p><a href="{{url.url}}">{{url.url}}</a></p>
<div bs-button-group>
<a href="{{url.url}}" target="_blank" bs-button="size-small subtle-alt icon-left">
<icon icon="newtab"></icon>
new tab
</a>
<a href="#" ng-click="sendAllTo(url.url)" bs-button="size-small subtle-alt icon-left">
<icon icon="syncall"></icon>
sync all
</a>
</div>
</div>
</section>
<section ng-if="ui.server">
<div bs-panel="full">
<div bs-panel-content>
<div bs-panel-icon>
<icon icon="terminal"></icon>
</div>
<p bs-text="lede">Serving files from</p>
<ul bs-list="basic">
<li ng-repeat="url in options.server.baseDir">{{url}}</li>
</ul>
</div>
</div>
</section>
<section bs-panel ng-if="ui.proxy">
<div bs-panel-content>
<div bs-panel-icon>
<icon icon="target"></icon></svg>
</div>
<p bs-text="lede">Proxying:</p>
<p>
<a href="{{options.proxy.target}}" target="_blank">{{options.proxy.target}}</a>
</p>
</div>
</section>
</div>

View File

@@ -0,0 +1,85 @@
/**
*
*/
(function (angular) {
var SECTION_NAME = "plugins";
angular
.module("BrowserSync")
.controller("PluginsController", [
"options",
"Socket",
"pagesConfig",
PluginsPageController
]);
/**
* @param options
* @param Socket
* @param pagesConfig
* @constructor
*/
function PluginsPageController(options, Socket, pagesConfig) {
var ctrl = this;
ctrl.section = pagesConfig[SECTION_NAME];
ctrl.options = options.bs;
ctrl.uiOptions = options.ui;
/**
* Don't show this UI as user plugin
*/
var filtered = ctrl.options.userPlugins.filter(function (item) {
return item.name !== "UI";
}).map(function (item) {
item.title = item.name;
return item;
});
var named = filtered.reduce(function (all, item) {
all[item.name] = item;
return all;
}, {});
/**
* @type {{loading: boolean}}
*/
ctrl.ui = {
loading: false,
plugins: filtered,
named: named
};
/**
* Toggle a pluginrs
*/
ctrl.togglePlugin = function (plugin) {
Socket.uiEvent({
namespace: SECTION_NAME,
event: "set",
data: plugin
});
};
/**
* Set the state of many options
* @param value
*/
ctrl.setMany = function (value) {
Socket.uiEvent({
namespace: SECTION_NAME,
event: "setMany",
data: value
});
ctrl.ui.plugins = ctrl.ui.plugins.map(function (item) {
item.active = value;
return item;
});
};
}
})(angular);

View File

@@ -0,0 +1,33 @@
<div bs-panel="controls outline">
<h1 bs-heading>
<icon icon="{{ctrl.section.icon}}"></icon>
{{ctrl.section.title}}
</h1>
<div ng-if="ui.plugins.length" bs-button-row>
<button bs-button="inline success" ng-click="ctrl.setMany(true)">
<svg bs-svg-icon="">
<use xlink:href="#svg-circle-ok"></use>
</svg>
Enable All
</button>
<button bs-button="inline" ng-click="ctrl.setMany(false)">
<svg bs-svg-icon="">
<use xlink:href="#svg-circle-delete"></use>
</svg>
Disable all
</button>
</div>
</div>
%pluginlist%
<section ng-if="!ctrl.ui.plugins.length">
<div bs-panel>
<div bs-panel-content="basic">
<p bs-text="lede">Sorry, no plugins were loaded</p>
<p>You can either write your own plugin (guide coming soon!) or <a href="https://www.npmjs.org/search?q=browser%20sync%20plugin" target="_blank">Search NPM</a>
for packages that contain the keywords <code>browser sync plugin</code>
</p>
</div>
</div>
</section>

View File

@@ -0,0 +1,74 @@
const PLUGIN_NAME = "Plugins";
/**
* @type {{plugin: Function, plugin:name: string, markup: string}}
*/
module.exports = {
/**
* @param ui
* @param bs
*/
"plugin": function (ui, bs) {
ui.listen("plugins", {
"set": function (data) {
bs.events.emit("plugins:configure", data);
},
"setMany": function (data) {
if (data.value !== true) {
data.value = false;
}
bs.getUserPlugins()
.filter(function (item) {
return item.name !== "UI "; // todo dupe code server/client
})
.forEach(function (item) {
item.active = data.value;
bs.events.emit("plugins:configure", item);
});
}
});
},
/**
* Hooks
*/
"hooks": {
"markup": fileContent("plugins.html"),
"client:js": fileContent("/plugins.client.js"),
"templates": [
//getPath("plugins.directive.html")
],
"page": {
path: "/plugins",
title: PLUGIN_NAME,
template: "plugins.html",
controller: PLUGIN_NAME + "Controller",
order: 4,
icon: "plug"
}
},
/**
* Plugin name
*/
"plugin:name": PLUGIN_NAME
};
/**
* @param filepath
* @returns {*}
*/
function getPath (filepath) {
return require("path").join(__dirname, filepath);
}
/**
* @param filepath
* @returns {*}
*/
function fileContent (filepath) {
return require("fs").readFileSync(getPath(filepath), "utf-8");
}

View File

@@ -0,0 +1,40 @@
var files = [
{
type: "css",
context: "remote-debug",
id: "__browser-sync-pesticide__",
active: false,
file: __dirname + "/css/pesticide.min.css",
title: "CSS Outlining",
served: false,
name: "pesticide",
src: "/browser-sync/pesticide.css",
tagline: "Add simple CSS outlines to all elements. (powered by <span style='text-decoration: line-through'>Pesticide.io</span>)",
hidden: ""
},
{
type: "css",
context: "remote-debug",
id: "__browser-sync-pesticidedepth__",
active: false,
file: __dirname + "/css/pesticide-depth.css",
title: "CSS Depth Outlining",
served: false,
name: "pesticide-depth",
src: "/browser-sync/pesticide-depth.css",
tagline: "Add CSS box-shadows to all elements. (powered by <span style='text-decoration: line-through'>Pesticide.io</span>)",
hidden: ""
},
{
type: "js",
context: "n/a",
id: "__browser-sync-gridoverlay__",
active: false,
file: __dirname + "/overlay-grid/js/grid-overlay.js",
served: false,
name: "overlay-grid-js",
src: "/browser-sync/grid-overlay-js.js"
}
];
module.exports.files = files;

View File

@@ -0,0 +1,19 @@
<div bs-panel="switch" ng-class="{'disabled': !ctrl.compression.active}">
<div bs-panel-content>
<div bs-panel-icon="switch">
<div class="switch">
<input id="cmn-form-{{ctrl.compression.name}}"
ng-model="ctrl.compression.active"
ng-change="ctrl.toggleLatency(ctrl.compression)"
class="cmn-toggle cmn-toggle-round"
type="checkbox"
checked="">
<label for="cmn-form-{{ctrl.compression.name}}"></label>
</div>
</div>
<div>
<p bs-Text="lede">{{ctrl.compression.title}}</p>
<p ng-if="ctrl.compression.tagline.length" ng-bind-html="ctrl.compression.tagline"></p>
</div>
</div>
</div>

View File

@@ -0,0 +1,33 @@
var Immutable = require("immutable");
module.exports.init = function (ui, bs) {
var optPath = ["remote-debug", "compression"];
ui.setOptionIn(optPath, Immutable.Map({
name: "compression",
title: "Compression",
active: false,
tagline: "Add Gzip Compression to all responses"
}));
var methods = {
toggle: function (value) {
if (value !== true) {
value = false;
}
if (value) {
ui.setOptionIn(optPath.concat("active"), true);
bs.addMiddleware("", require("compression")(), {id: "ui-compression", override: true});
} else {
ui.setOptionIn(optPath.concat("active"), false);
bs.removeMiddleware("ui-compression");
}
},
event: function (event) {
methods[event.event](event.data);
}
};
return methods;
};

View File

@@ -0,0 +1,498 @@
/*
pesticide v1.0.0 . @mrmrs . MIT
*/
body {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
article {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
nav {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
aside {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
section {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
header {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
footer {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
h1 {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
h2 {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
h3 {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
h4 {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
h5 {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
h6 {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
main {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
address {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
div {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
p {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
hr {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
pre {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
blockquote {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
ol {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
ul {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
li {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
dl {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
dt {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
dd {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
figure {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
figcaption {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
table {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
caption {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
thead {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
tbody {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
tfoot {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
tr {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
th {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
td {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
col {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
colgroup {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
button {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
datalist {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
fieldset {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
form {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
input {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
keygen {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
label {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
legend {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
meter {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
optgroup {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
option {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
output {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
progress {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
select {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
textarea {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
details {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
summary {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
command {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
menu {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
del {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
ins {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
img {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
iframe {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
embed {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
object {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
param {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
video {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
audio {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
source {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
canvas {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
track {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
map {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
area {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
a {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
em {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
strong {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
i {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
b {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
u {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
s {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
small {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
abbr {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
q {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
cite {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
dfn {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
sub {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
sup {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
time {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
code {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
kbd {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
samp {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
var {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
mark {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
bdi {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
bdo {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
ruby {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
rt {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
rp {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
span {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
br {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}
wbr {
-webkit-box-shadow: 0 0 1rem rgba(0,0,0,0.6);
box-shadow: 0 0 1rem rgba(0,0,0,0.6);
background-color: rgba(255,255,255,0.25);
}

View File

@@ -0,0 +1,201 @@
/*
pesticide v1.0.0 . @mrmrs . MIT
*/
{
body
outline: 1px solid #2980b9 !important;
article
outline: 1px solid #3498db !important;
nav
outline: 1px solid #0088c3 !important;
aside
outline: 1px solid #33a0ce !important;
section
outline: 1px solid #66b8da !important;
header
outline: 1px solid #99cfe7 !important;
footer
outline: 1px solid #cce7f3 !important;
h1
outline: 1px solid #162544 !important;
h2
outline: 1px solid #314e6e !important;
h3
outline: 1px solid #3e5e85 !important;
h4
outline: 1px solid #449baf !important;
h5
outline: 1px solid #c7d1cb !important;
h6
outline: 1px solid #4371d0 !important;
main
outline: 1px solid #2f4f90 !important;
address
outline: 1px solid #1a2c51 !important;
div
outline: 1px solid #036cdb !important;
outline: 1px solid #ac050b !important;
hr
outline: 1px solid #ff063f !important;
pre
outline: 1px solid #850440 !important;
blockquote
outline: 1px solid #f1b8e7 !important;
ol
outline: 1px solid #ff050c !important;
ul
outline: 1px solid #d90416 !important;
li
outline: 1px solid #d90416 !important;
dl
outline: 1px solid #fd3427 !important;
dt
outline: 1px solid #ff0043 !important;
dd
outline: 1px solid #e80174 !important;
figure
outline: 1px solid #f0b !important;
figcaption
outline: 1px solid #bf0032 !important;
table
outline: 1px solid #0c9 !important;
caption
outline: 1px solid #37ffc4 !important;
thead
outline: 1px solid #98daca !important;
tbody
outline: 1px solid #64a7a0 !important;
tfoot
outline: 1px solid #22746b !important;
tr
outline: 1px solid #86c0b2 !important;
th
outline: 1px solid #a1e7d6 !important;
td
outline: 1px solid #3f5a54 !important;
col
outline: 1px solid #6c9a8f !important;
colgroup
outline: 1px solid #6c9a9d !important;
button
outline: 1px solid #da8301 !important;
datalist
outline: 1px solid #c06000 !important;
fieldset
outline: 1px solid #d95100 !important;
form
outline: 1px solid #d23600 !important;
input
outline: 1px solid #fca600 !important;
keygen
outline: 1px solid #b31e00 !important;
label
outline: 1px solid #ee8900 !important;
legend
outline: 1px solid #de6d00 !important;
meter
outline: 1px solid #e8630c !important;
optgroup
outline: 1px solid #b33600 !important;
option
outline: 1px solid #ff8a00 !important;
output
outline: 1px solid #ff9619 !important;
progress
outline: 1px solid #e57c00 !important;
select
outline: 1px solid #e26e0f !important;
textarea
outline: 1px solid #cc5400 !important;
details
outline: 1px solid #33848f !important;
summary
outline: 1px solid #60a1a6 !important;
command
outline: 1px solid #438da1 !important;
menu
outline: 1px solid #449da6 !important;
del
outline: 1px solid #bf0000 !important;
ins
outline: 1px solid #400000 !important;
img
outline: 1px solid #22746b !important;
iframe
outline: 1px solid #64a7a0 !important;
embed
outline: 1px solid #98daca !important;
object
outline: 1px solid #0c9 !important;
param
outline: 1px solid #37ffc4 !important;
video
outline: 1px solid #6ee866 !important;
audio
outline: 1px solid #027353 !important;
source
outline: 1px solid #012426 !important;
canvas
outline: 1px solid #a2f570 !important;
track
outline: 1px solid #59a600 !important;
map
outline: 1px solid #7be500 !important;
area
outline: 1px solid #305900 !important;
a
outline: 1px solid #ff62ab !important;
em
outline: 1px solid #800b41 !important;
strong
outline: 1px solid #ff1583 !important;
i
outline: 1px solid #803156 !important;
b
outline: 1px solid #cc1169 !important;
u
outline: 1px solid #ff0430 !important;
outline: 1px solid #f805e3 !important;
small
outline: 1px solid #d107b2 !important;
abbr
outline: 1px solid #4a0263 !important;
q
outline: 1px solid #240018 !important;
cite
outline: 1px solid #64003c !important;
dfn
outline: 1px solid #b4005a !important;
sub
outline: 1px solid #dba0c8 !important;
sup
outline: 1px solid #cc0256 !important;
time
outline: 1px solid #d6606d !important;
code
outline: 1px solid #e04251 !important;
kbd
outline: 1px solid #5e001f !important;
samp
outline: 1px solid #9c0033 !important;
var
outline: 1px solid #d90047 !important;
mark
outline: 1px solid #ff0053 !important;
bdi
outline: 1px solid #bf3668 !important;
bdo
outline: 1px solid #6f1400 !important;
ruby
outline: 1px solid #ff7b93 !important;
rt
outline: 1px solid #ff2f54 !important;
rp
outline: 1px solid #803e49 !important;
span
outline: 1px solid #cc2643 !important;
br
outline: 1px solid #db687d !important;
wbr
outline: 1px solid #db175b !important;
}

View File

@@ -0,0 +1,395 @@
body {
outline: 1px solid #2980b9 !important
}
article {
outline: 1px solid #3498db !important
}
nav {
outline: 1px solid #0088c3 !important
}
aside {
outline: 1px solid #33a0ce !important
}
section {
outline: 1px solid #66b8da !important
}
header {
outline: 1px solid #99cfe7 !important
}
footer {
outline: 1px solid #cce7f3 !important
}
h1 {
outline: 1px solid #162544 !important
}
h2 {
outline: 1px solid #314e6e !important
}
h3 {
outline: 1px solid #3e5e85 !important
}
h4 {
outline: 1px solid #449baf !important
}
h5 {
outline: 1px solid #c7d1cb !important
}
h6 {
outline: 1px solid #4371d0 !important
}
main {
outline: 1px solid #2f4f90 !important
}
address {
outline: 1px solid #1a2c51 !important
}
div {
outline: 1px solid #036cdb !important
}
p {
outline: 1px solid #ac050b !important
}
hr {
outline: 1px solid #ff063f !important
}
pre {
outline: 1px solid #850440 !important
}
blockquote {
outline: 1px solid #f1b8e7 !important
}
ol {
outline: 1px solid #ff050c !important
}
ul {
outline: 1px solid #d90416 !important
}
li {
outline: 1px solid #d90416 !important
}
dl {
outline: 1px solid #fd3427 !important
}
dt {
outline: 1px solid #ff0043 !important
}
dd {
outline: 1px solid #e80174 !important
}
figure {
outline: 1px solid #f0b !important
}
figcaption {
outline: 1px solid #bf0032 !important
}
table {
outline: 1px solid #0c9 !important
}
caption {
outline: 1px solid #37ffc4 !important
}
thead {
outline: 1px solid #98daca !important
}
tbody {
outline: 1px solid #64a7a0 !important
}
tfoot {
outline: 1px solid #22746b !important
}
tr {
outline: 1px solid #86c0b2 !important
}
th {
outline: 1px solid #a1e7d6 !important
}
td {
outline: 1px solid #3f5a54 !important
}
col {
outline: 1px solid #6c9a8f !important
}
colgroup {
outline: 1px solid #6c9a9d !important
}
button {
outline: 1px solid #da8301 !important
}
datalist {
outline: 1px solid #c06000 !important
}
fieldset {
outline: 1px solid #d95100 !important
}
form {
outline: 1px solid #d23600 !important
}
input {
outline: 1px solid #fca600 !important
}
keygen {
outline: 1px solid #b31e00 !important
}
label {
outline: 1px solid #ee8900 !important
}
legend {
outline: 1px solid #de6d00 !important
}
meter {
outline: 1px solid #e8630c !important
}
optgroup {
outline: 1px solid #b33600 !important
}
option {
outline: 1px solid #ff8a00 !important
}
output {
outline: 1px solid #ff9619 !important
}
progress {
outline: 1px solid #e57c00 !important
}
select {
outline: 1px solid #e26e0f !important
}
textarea {
outline: 1px solid #cc5400 !important
}
details {
outline: 1px solid #33848f !important
}
summary {
outline: 1px solid #60a1a6 !important
}
command {
outline: 1px solid #438da1 !important
}
menu {
outline: 1px solid #449da6 !important
}
del {
outline: 1px solid #bf0000 !important
}
ins {
outline: 1px solid #400000 !important
}
img {
outline: 1px solid #22746b !important
}
iframe {
outline: 1px solid #64a7a0 !important
}
embed {
outline: 1px solid #98daca !important
}
object {
outline: 1px solid #0c9 !important
}
param {
outline: 1px solid #37ffc4 !important
}
video {
outline: 1px solid #6ee866 !important
}
audio {
outline: 1px solid #027353 !important
}
source {
outline: 1px solid #012426 !important
}
canvas {
outline: 1px solid #a2f570 !important
}
track {
outline: 1px solid #59a600 !important
}
map {
outline: 1px solid #7be500 !important
}
area {
outline: 1px solid #305900 !important
}
a {
outline: 1px solid #ff62ab !important
}
em {
outline: 1px solid #800b41 !important
}
strong {
outline: 1px solid #ff1583 !important
}
i {
outline: 1px solid #803156 !important
}
b {
outline: 1px solid #cc1169 !important
}
u {
outline: 1px solid #ff0430 !important
}
s {
outline: 1px solid #f805e3 !important
}
small {
outline: 1px solid #d107b2 !important
}
abbr {
outline: 1px solid #4a0263 !important
}
q {
outline: 1px solid #240018 !important
}
cite {
outline: 1px solid #64003c !important
}
dfn {
outline: 1px solid #b4005a !important
}
sub {
outline: 1px solid #dba0c8 !important
}
sup {
outline: 1px solid #cc0256 !important
}
time {
outline: 1px solid #d6606d !important
}
code {
outline: 1px solid #e04251 !important
}
kbd {
outline: 1px solid #5e001f !important
}
samp {
outline: 1px solid #9c0033 !important
}
var {
outline: 1px solid #d90047 !important
}
mark {
outline: 1px solid #ff0053 !important
}
bdi {
outline: 1px solid #bf3668 !important
}
bdo {
outline: 1px solid #6f1400 !important
}
ruby {
outline: 1px solid #ff7b93 !important
}
rt {
outline: 1px solid #ff2f54 !important
}
rp {
outline: 1px solid #803e49 !important
}
span {
outline: 1px solid #cc2643 !important
}
br {
outline: 1px solid #db687d !important
}
wbr {
outline: 1px solid #db175b !important
}

View File

@@ -0,0 +1,43 @@
(function (angular) {
const SECTION_NAME = "remote-debug";
/**
* Display the snippet when in snippet mode
*/
angular
.module("BrowserSync")
.directive("latency", function () {
return {
restrict: "E",
replace: true,
scope: {
"options": "="
},
templateUrl: "latency.html",
controller: ["$scope", "Socket", latencyDirectiveControlller],
controllerAs: "ctrl"
};
});
/**
* @param $scope
* @param Socket
*/
function latencyDirectiveControlller($scope, Socket) {
var ctrl = this;
var ns = SECTION_NAME + ":latency";
ctrl.latency = $scope.options[SECTION_NAME]["latency"];
ctrl.alterLatency = function () {
Socket.emit("ui", {
namespace: ns,
event: "adjust",
data: {
rate: ctrl.latency.rate
}
});
};
}
})(angular);

View File

@@ -0,0 +1,12 @@
<div ng-show="ctrl.latency.active" bs-panel-content>
<input type="range"
max="5"
min="0"
step=".50"
id="latency-rate"
ng-change="ctrl.alterLatency()"
ng-model="ctrl.latency.rate"/>
<label for="latency-rate">{{ctrl.latency.rate | number:1}}s</label>
</div>

View File

@@ -0,0 +1,44 @@
var Immutable = require("immutable");
module.exports.init = function (ui, bs) {
var timeout = 0;
var optPath = ["remote-debug", "latency"];
ui.setOptionIn(optPath, Immutable.Map({
name: "latency",
title: "Latency",
active: false,
tagline: "Simulate slower connections by throttling the response time of each request.",
rate: 0
}));
var methods = {
toggle: function (value) {
if (value !== true) {
value = false;
}
if (value) {
ui.setOptionIn(optPath.concat("active"), true);
bs.addMiddleware("*", function (req, res, next) {
setTimeout(next, timeout);
}, {id: "cp-latency", override: true});
} else {
ui.setOptionIn(optPath.concat("active"), false);
bs.removeMiddleware("cp-latency");
}
},
adjust: function (data) {
timeout = parseFloat(data.rate) * 1000;
var saved = ui.options.getIn(optPath.concat("rate"));
if (saved !== data.rate) {
ui.setOptionIn(optPath.concat("rate"), timeout/1000);
}
},
event: function (event) {
methods[event.event](event.data);
}
};
return methods;
};

View File

@@ -0,0 +1,19 @@
<div bs-panel="switch" ng-class="{'disabled': !ctrl.noCache.active}">
<div bs-panel-content>
<div bs-panel-icon="switch">
<div class="switch">
<input id="cmn-form-{{ctrl.noCache.name}}"
ng-model="ctrl.noCache.active"
ng-change="ctrl.toggleLatency(ctrl.noCache)"
class="cmn-toggle cmn-toggle-round"
type="checkbox"
checked="">
<label for="cmn-form-{{ctrl.noCache.name}}"></label>
</div>
</div>
<div>
<p bs-Text="lede">{{ctrl.noCache.title}}</p>
<p ng-if="ctrl.noCache.tagline.length" ng-bind-html="ctrl.noCache.tagline"></p>
</div>
</div>
</div>

View File

@@ -0,0 +1,38 @@
var Immutable = require("immutable");
module.exports.init = function (ui, bs) {
var optPath = ["remote-debug", "no-cache"];
ui.setOptionIn(optPath, Immutable.Map({
name: "no-cache",
title: "No Cache",
active: false,
tagline: "Disable all Browser Caching"
}));
var methods = {
toggle: function (value) {
if (value !== true) {
value = false;
}
if (value) {
ui.setOptionIn(optPath.concat("active"), true);
bs.addMiddleware("*", function (req, res, next) {
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
res.setHeader("Pragma", "no-cache");
res.setHeader("Expires", "0");
next();
}, {id: "ui-no-cache", override: true});
} else {
ui.setOptionIn(optPath.concat("active"), false);
bs.removeMiddleware("ui-no-cache");
}
},
event: function (event) {
methods[event.event](event.data);
}
};
return methods;
};

View File

@@ -0,0 +1,16 @@
{{selector}}:after {
position: absolute;
width: auto;
height: auto;
z-index: 9999;
content: '';
display: block;
pointer-events: none;
top: {{offsetY}};
right: 0;
bottom: 0;
left: {{offsetX}};
background-color: transparent;
background-image: linear-gradient({{color}} 1px, transparent 1px);
background-size: 100% {{size}};
}

View File

@@ -0,0 +1,16 @@
{{selector}}:before {
position: absolute;
width: auto;
height: auto;
z-index: 9999;
content: '';
display: block;
pointer-events: none;
top: {{offsetY}};
right: 0;
bottom: 0;
left: {{offsetX}};
background-color: transparent;
background-image: linear-gradient(90deg, {{color}} 1px, transparent 1px);
background-size: {{size}} 100%;
}

View File

@@ -0,0 +1,18 @@
(function (window, bs, undefined) {
var styleElem = bs.addDomNode({
placement: "head",
attrs: {
"type": "text/css",
id: "__bs_overlay-grid-styles__"
},
tagName: "style"
});
bs.socket.on("ui:remote-debug:css-overlay-grid", function (data) {
styleElem.innerHTML = data.innerHTML;
});
bs.socket.emit("ui:remote-debug:css-overlay-grid:ready");
}(window, window.___browserSync___));

View File

@@ -0,0 +1,56 @@
(function (angular) {
const SECTION_NAME = "remote-debug";
/**
* Display the snippet when in snippet mode
*/
angular
.module("BrowserSync")
.directive("cssGrid", function () {
return {
restrict: "E",
replace: true,
scope: {
"options": "="
},
templateUrl: "overlay-grid.html",
controller: ["$scope", "Socket", overlayGridDirectiveControlller],
controllerAs: "ctrl"
};
});
/**
* @param $scope
* @param Socket
*/
function overlayGridDirectiveControlller($scope, Socket) {
var ctrl = this;
ctrl.overlayGrid = $scope.options[SECTION_NAME]["overlay-grid"];
ctrl.size = ctrl.overlayGrid.size;
var ns = SECTION_NAME + ":overlay-grid";
ctrl.alter = function (value) {
Socket.emit("ui", {
namespace: ns,
event: "adjust",
data: value
});
};
ctrl.toggleAxis = function (axis, value) {
Socket.emit("ui", {
namespace: ns,
event: "toggle:axis",
data: {
axis: axis,
value: value
}
});
};
}
})(angular);

View File

@@ -0,0 +1,106 @@
<div ng-show="ctrl.overlayGrid.active" bs-panel-content>
<div bs-inputs bs-grid="wide-4 desk-2">
<div bs-grid-item>
<div bs-input="text">
<label for="grid-size" bs-input-label>Grid Size</label>
<div bs-input>
<input type="text"
max="100"
min="0"
step="1"
id="grid-size"
size="20"
ng-change="ctrl.alter(ctrl.overlayGrid)"
ng-model="ctrl.overlayGrid.size"/>
</div>
</div>
</div>
<div bs-grid-item>
<div bs-input="text">
<label for="grid-color" bs-input-label>Grid Colour</label>
<div bs-input>
<input type="text"
max="100"
min="0"
step="1"
id="grid-color"
size="20"
ng-change="ctrl.alter(ctrl.overlayGrid)"
ng-model="ctrl.overlayGrid.color"/>
</div>
</div>
</div>
<div bs-grid-item>
<div bs-input="text">
<label for="grid-selector" bs-input-label>CSS Selector</label>
<div bs-input>
<input type="text"
max="100"
min="0"
step="1"
id="grid-selector"
size="20"
ng-change="ctrl.alter(ctrl.overlayGrid)"
ng-model="ctrl.overlayGrid.selector"/>
</div>
</div>
</div>
</div>
<div bs-inputs bs-grid="wide-4 desk-2">
<div bs-grid-item>
<div bs-input="text">
<label for="grid-offsetY" bs-input-label>Offset Top</label>
<div bs-input>
<input type="text"
id="grid-offsetY"
size="20"
ng-change="ctrl.alter(ctrl.overlayGrid)"
ng-model="ctrl.overlayGrid.offsetY"/>
</div>
</div>
</div>
<div bs-grid-item>
<div bs-input="text">
<label for="grid-offsetX" bs-input-label>Offset Left</label>
<div bs-input>
<input type="text"
id="grid-offsetX"
size="20"
ng-change="ctrl.alter(ctrl.overlayGrid)"
ng-model="ctrl.overlayGrid.offsetX"/>
</div>
</div>
</div>
</div>
<div bs-inputs bs-grid="wide-4 desk-2">
<div bs-grid-item>
<div bs-input="inline">
<input
type="checkbox"
id="grid-axis-y"
ng-model="ctrl.overlayGrid.vertical"
ng-change="ctrl.toggleAxis('vertical', ctrl.overlayGrid.vertical)"/>
<label for="grid-axis-y" bs-input-label>Vertical Axis</label>
</div>
</div>
<div bs-grid-item>
<div bs-input="inline">
<input
type="checkbox"
id="grid-axis-x"
ng-model="ctrl.overlayGrid.horizontal"
ng-change="ctrl.toggleAxis('horizontal', ctrl.overlayGrid.horizontal)"/>
<label for="grid-axis-x" bs-input-label>Horizontal Axis</label>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,101 @@
var Immutable = require("immutable");
var fs = require("fs");
var path = require("path");
var baseHorizontal = fs.readFileSync(path.resolve(__dirname, "css/grid-overlay-horizontal.css"), "utf8");
var baseVertical = fs.readFileSync(path.resolve(__dirname, "css/grid-overlay-vertical.css"), "utf8");
function template (string, obj) {
obj = obj || {};
return string.replace(/\{\{(.+?)\}\}/g, function () {
if (obj[arguments[1]]) {
return obj[arguments[1]];
}
return "";
});
}
function getCss(opts) {
var base = opts.selector + " {position:relative;}";
if (opts.horizontal) {
base += baseHorizontal;
}
if (opts.vertical) {
base += baseVertical;
}
return template(base, opts);
}
module.exports.init = function (ui) {
const TRANSMIT_EVENT = "ui:remote-debug:css-overlay-grid";
const READY_EVENT = "ui:remote-debug:css-overlay-grid:ready";
const OPT_PATH = ["remote-debug", "overlay-grid"];
var defaults = {
offsetY: "0",
offsetX: "0",
size: "16px",
selector: "body",
color: "rgba(0, 0, 0, .2)",
horizontal: true,
vertical: true
};
ui.clients.on("connection", function (client) {
client.on(READY_EVENT, function () {
client.emit(TRANSMIT_EVENT, {
innerHTML: getCss(ui.options.getIn(OPT_PATH).toJS())
});
});
});
ui.setOptionIn(OPT_PATH, Immutable.Map({
name: "overlay-grid",
title: "Overlay CSS Grid",
active: false,
tagline: "Add an adjustable CSS overlay grid to your webpage",
innerHTML: ""
}).merge(defaults));
var methods = {
toggle: function (value) {
if (value !== true) {
value = false;
}
if (value) {
ui.setOptionIn(OPT_PATH.concat("active"), true);
ui.enableElement({name: "overlay-grid-js"});
} else {
ui.setOptionIn(OPT_PATH.concat("active"), false);
ui.disableElement({name: "overlay-grid-js"});
ui.clients.emit("ui:element:remove", {id: "__bs_overlay-grid-styles__"});
}
},
adjust: function (data) {
ui.setOptionIn(OPT_PATH, ui.getOptionIn(OPT_PATH).merge(data));
ui.clients.emit(TRANSMIT_EVENT, {
innerHTML: getCss(ui.options.getIn(OPT_PATH).toJS())
});
},
"toggle:axis": function (item) {
ui.setOptionIn(OPT_PATH.concat([item.axis]), item.value);
ui.clients.emit(TRANSMIT_EVENT, {
innerHTML: getCss(ui.options.getIn(OPT_PATH).toJS())
});
},
event: function (event) {
methods[event.event](event.data);
}
};
return methods;
};

View File

@@ -0,0 +1,144 @@
(function (angular) {
const SECTION_NAME = "remote-debug";
angular
.module("BrowserSync")
.controller("RemoteDebugController", [
"options",
"Socket",
"pagesConfig",
RemoteDebugController
]);
/**
* @param options
* @param Socket
* @param pagesConfig
*/
function RemoteDebugController(options, Socket, pagesConfig) {
var ctrl = this;
ctrl.options = options.bs;
ctrl.uiOptions = options.ui;
ctrl.clientFiles = options.ui.clientFiles || {};
ctrl.section = pagesConfig[SECTION_NAME];
ctrl.overlayGrid = options.ui[SECTION_NAME]["overlay-grid"];
ctrl.items = [];
if (Object.keys(ctrl.clientFiles).length) {
Object.keys(ctrl.clientFiles).forEach(function (key) {
if (ctrl.clientFiles[key].context === SECTION_NAME) {
ctrl.items.push(ctrl.clientFiles[key]);
}
});
}
ctrl.toggleClientFile = function (item) {
if (item.active) {
return ctrl.enable(item);
}
return ctrl.disable(item);
};
ctrl.toggleOverlayGrid = function (item) {
var ns = SECTION_NAME + ":overlay-grid";
Socket.uiEvent({
namespace: ns,
event: "toggle",
data: item.active
});
};
ctrl.enable = function (item) {
Socket.uiEvent({
namespace: SECTION_NAME + ":files",
event: "enableFile",
data: item
});
};
ctrl.disable = function (item) {
Socket.uiEvent({
namespace: SECTION_NAME + ":files",
event: "disableFile",
data: item
});
};
}
/**
* Display the snippet when in snippet mode
*/
angular
.module("BrowserSync")
.directive("noCache", function () {
return {
restrict: "E",
replace: true,
scope: {
"options": "="
},
templateUrl: "no-cache.html",
controller: ["$scope", "Socket", noCacheDirectiveControlller],
controllerAs: "ctrl"
};
});
/**
* @param $scope
* @param Socket
*/
function noCacheDirectiveControlller ($scope, Socket) {
var ctrl = this;
ctrl.noCache = $scope.options[SECTION_NAME]["no-cache"];
ctrl.toggleLatency = function (item) {
Socket.emit("ui:no-cache", {
event: "toggle",
data: item.active
});
};
}
/**
* Display the snippet when in snippet mode
*/
angular
.module("BrowserSync")
.directive("compression", function () {
return {
restrict: "E",
replace: true,
scope: {
"options": "="
},
templateUrl: "compression.html",
controller: ["$scope", "Socket", compressionDirectiveControlller],
controllerAs: "ctrl"
};
});
/**
* @param $scope
* @param Socket
*/
function compressionDirectiveControlller ($scope, Socket) {
var ctrl = this;
ctrl.compression = $scope.options[SECTION_NAME]["compression"];
ctrl.toggleLatency = function (item) {
Socket.emit("ui:compression", {
event: "toggle",
data: item.active
});
};
}
})(angular);

View File

@@ -0,0 +1,23 @@
<div bs-panel="controls outline">
<h1 bs-heading>
<icon icon="{{ctrl.section.icon}}"></icon>
{{ctrl.section.title}}
</h1>
</div>
<switch toggle="ctrl.toggleClientFile(item)"
switchid="plugins-{{$index}}"
active="item.active"
prop="active"
ng-repeat="item in ctrl.items track by $index"
item="item">
<div bs-panel-content ng-if="item.active && item.hidden.length" ng-bind-html="item.hidden"></div>
</switch>
<switch toggle="ctrl.toggleOverlayGrid(ctrl.overlayGrid)"
switchid="css-overlay-grid"
active="ctrl.overlayGrid.active"
prop="active"
item="ctrl.overlayGrid">
<css-grid options="ctrl.uiOptions" ng-if="ctrl.options.mode !== 'snippet'"></css-grid>
</switch>

View File

@@ -0,0 +1,82 @@
//var compression = require("./compression");
//var noCachePlugin = require("./no-cache");
var overlayPlugin = require("./overlay-grid/overlay-grid");
var clientFiles = require("./client-files");
const PLUGIN_NAME = "Remote Debug";
/**
* @type {{plugin: Function, plugin:name: string, markup: string}}
*/
module.exports = {
/**
* @param ui
* @param bs
*/
"plugin": function (ui, bs) {
ui.overlayGrid = overlayPlugin.init(ui, bs);
//ui.noCache = noCachePlugin.init(ui, bs);
//ui.compression = compression.init(ui, bs);
/**
* Listen for file events
*/
ui.listen("remote-debug:files", {
"enableFile": function (file) {
ui.enableElement(file);
},
"disableFile": function (file) {
ui.disableElement(file);
}
});
/**
* Listen for overlay-grid events
*/
ui.listen("remote-debug:overlay-grid", ui.overlayGrid);
},
/**
* Hooks
*/
"hooks": {
"markup": fileContent("remote-debug.html"),
"client:js": [
fileContent("/remote-debug.client.js"),
fileContent("/overlay-grid/overlay-grid.client.js")
],
"templates": [
getPath("/overlay-grid/overlay-grid.html")
],
"page": {
path: "/remote-debug",
title: PLUGIN_NAME,
template: "remote-debug.html",
controller: PLUGIN_NAME.replace(" ", "") + "Controller",
order: 4,
icon: "bug"
},
elements: clientFiles.files
},
/**
* Plugin name
*/
"plugin:name": PLUGIN_NAME
};
/**
* @param filepath
* @returns {*}
*/
function getPath (filepath) {
return require("path").join(__dirname, filepath);
}
/**
* @param filepath
* @returns {*}
*/
function fileContent (filepath) {
return require("fs").readFileSync(getPath(filepath), "utf-8");
}

View File

@@ -0,0 +1,94 @@
(function (angular) {
const SECTION_NAME = "sync-options";
angular
.module("BrowserSync")
.controller("SyncOptionsController", [
"Socket",
"options",
"pagesConfig",
SyncOptionsController
]);
/**
* @param Socket
* @param options
* @param pagesConfig
* @constructor
*/
function SyncOptionsController(Socket, options, pagesConfig) {
var ctrl = this;
ctrl.options = options.bs;
ctrl.section = pagesConfig[SECTION_NAME];
ctrl.setMany = function (value) {
Socket.uiEvent({
namespace: SECTION_NAME,
event: "setMany",
data: {
value: value
}
});
ctrl.syncItems = ctrl.syncItems.map(function (item) {
item.value = value;
return item;
});
};
/**
* Toggle Options
* @param item
*/
ctrl.toggleSyncItem = function (item) {
Socket.uiEvent({
namespace: SECTION_NAME,
event: "set",
data: {
path: item.path,
value: item.value
}
});
};
ctrl.syncItems = [];
var taglines = {
clicks: "Mirror clicks across devices",
scroll: "Mirror scroll position across devices",
"ghostMode.submit": "Form Submissions will be synced",
"ghostMode.inputs": "Text inputs (including text-areas) will be synced",
"ghostMode.toggles": "Radio + Checkboxes changes will be synced",
codeSync: "Reload or Inject files when they change"
};
// If watching files, add the code-sync toggle
ctrl.syncItems.push(addItem("codeSync", ["codeSync"], ctrl.options.codeSync, taglines["codeSync"]));
Object.keys(ctrl.options.ghostMode).forEach(function (item) {
if (item !== "forms" && item !== "location") {
ctrl.syncItems.push(addItem(item, ["ghostMode", item], ctrl.options.ghostMode[item], taglines[item]));
}
});
Object.keys(ctrl.options.ghostMode.forms).forEach(function (item) {
ctrl.syncItems.push(addItem("Forms: " + item, ["ghostMode", "forms", item], ctrl.options.ghostMode["forms"][item], taglines["ghostMode." + item]));
});
function addItem (item, path, value, tagline) {
return {
value: value,
name: item,
path: path,
title: ucfirst(item),
tagline: tagline
};
}
}
function ucfirst (string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
})(angular);

View File

@@ -0,0 +1,25 @@
<article>
<div bs-panel="controls outline">
<h1 bs-heading>
<icon icon="{{ctrl.section.icon}}"></icon>
{{ctrl.section.title}}
</h1>
</div>
<div bs-button-row>
<button bs-button="icon-left inline success" ng-click="ctrl.setMany(true)">
<svg bs-svg-icon><use xlink:href="#svg-circle-ok"></use></svg>
Enable All
</button>
<button bs-button="icon-left inline" ng-click="ctrl.setMany(false)">
<svg bs-svg-icon><use xlink:href="#svg-circle-delete"></use></svg>
Disable all
</button>
</div>
<switch toggle="ctrl.toggleSyncItem(item)"
switchid="sync-options-{{$index}}"
active="item.value"
prop="value"
ng-repeat="item in ctrl.syncItems track by $index"
item="item"></switch>
</article>

View File

@@ -0,0 +1,66 @@
const PLUGIN_NAME = "Sync Options";
var chalk = require("chalk");
/**
* @type {{plugin: Function, plugin:name: string, hooks: object}}
*/
module.exports = {
"plugin": function (ui, bs) {
ui.listen("sync-options", {
"set": function (data) {
ui.logger.debug("Setting option: %s:%s", chalk.magenta(data.path.join(".")), chalk.cyan(data.value));
bs.setOptionIn(data.path, data.value);
},
"setMany": function (data) {
ui.logger.debug("Setting Many options...");
if (data.value !== true) {
data.value = false;
}
bs.setMany(function (item) {
[
["codeSync"],
["ghostMode", "clicks"],
["ghostMode", "scroll"],
["ghostMode", "forms", "inputs"],
["ghostMode", "forms", "toggles"],
["ghostMode", "forms", "submit"]
].forEach(function (option) {
item.setIn(option, data.value);
});
});
return bs;
}
});
},
"hooks": {
"markup": fileContent("sync-options.html"),
"client:js": fileContent("sync-options.client.js"),
"templates": [],
"page": {
path: "/sync-options",
title: PLUGIN_NAME,
template: "sync-options.html",
controller: PLUGIN_NAME.replace(" ", "") + "Controller",
order: 2,
icon: "sync"
}
},
"plugin:name": PLUGIN_NAME
};
function getPath (filepath) {
return require("path").join(__dirname, filepath);
}
function fileContent (filepath) {
return require("fs").readFileSync(getPath(filepath), "utf-8");
}

117
node_modules/browser-sync-ui/lib/resolve-plugins.js generated vendored Normal file
View File

@@ -0,0 +1,117 @@
var fs = require("fs");
var path = require("path");
var Immutable = require("immutable");
/**
* Take Browsersync plugins and determine if
* any UI is provided by looking at data in the the
* modules package.json file
* @param plugins
* @returns {*}
*/
module.exports = function (plugins) {
return require("immutable")
.fromJS(plugins)
/**
* Exclude the UI
*/
.filter(function (plugin) {
return plugin.get("name") !== "UI";
})
/**
* Attempt to retrieve a plugins package.json file
*/
.map(function (plugin) {
var moduleName = plugin.getIn(["opts", "moduleName"]);
var pkg = {};
if (!moduleName) {
return plugin;
}
try {
pkg = require("immutable").fromJS(require(path.join(moduleName, "package.json")));
} catch (e) {
console.error(e);
return plugin;
}
plugin = plugin.set("pkg", pkg);
return plugin.set("relpath", path.dirname(require.resolve(moduleName)));
})
/**
* Try to load markup for each plugin
*/
.map(function (plugin) {
if (!plugin.hasIn(["pkg", "browser-sync:ui"])) {
return plugin;
}
var markup = plugin.getIn(["pkg", "browser-sync:ui", "hooks", "markup"]);
if (markup) {
plugin = plugin.set("markup", fs.readFileSync(path.resolve(plugin.get("relpath"), markup), "utf8"));
}
return plugin;
})
/**
* Load any template files for the plugin
*/
.map(function (plugin) {
if (!plugin.hasIn(["pkg", "browser-sync:ui"])) {
return plugin;
}
return resolveIfPluginHas(["pkg", "browser-sync:ui", "hooks", "templates"], "templates", plugin);
})
/**
* Try to load Client JS for each plugin
*/
.map(function (plugin) {
if (!plugin.hasIn(["pkg", "browser-sync:ui"])) {
return plugin;
}
return resolveIfPluginHas(["pkg", "browser-sync:ui", "hooks", "client:js"], "client:js", plugin);
});
};
/**
* If a plugin contains this option path, resolve/read the files
* @param {Array} optPath - How to access the collection
* @param {String} propName - Key for property access
* @param {Immutable.Map} plugin
* @returns {*}
*/
function resolveIfPluginHas(optPath, propName, plugin) {
var opt = plugin.getIn(optPath);
if (opt.size) {
return plugin.set(
propName,
resolvePluginFiles(opt, plugin.get("relpath"))
);
}
return plugin;
}
/**
* Read & store a file from a plugin
* @param {Array|Immutable.List} collection
* @param {String} relPath
* @returns {any}
*/
function resolvePluginFiles (collection, relPath) {
return Immutable.fromJS(collection.reduce(function (all, item) {
var full = path.join(relPath, item);
if (fs.existsSync(full)) {
all[full] = fs.readFileSync(full, "utf8");
}
return all;
}, {}));
}

220
node_modules/browser-sync-ui/lib/server.js generated vendored Normal file
View File

@@ -0,0 +1,220 @@
var http = require("http");
var fs = require("fs");
var path = require("path");
var config = require("./config");
var svg = publicFile(config.defaults.public.svg);
var indexPage = publicFile(config.defaults.indexPage);
//var css = publicFile(config.defaults.public.css);
var header = staticFile(config.defaults.components.header);
var footer = staticFile(config.defaults.components.footer);
var zlib = require("zlib");
/**
* @param {UI} ui
* @returns {*}
*/
function startServer(ui) {
var connect = ui.bs.utils.connect;
var serveStatic = ui.bs.utils.serveStatic;
/**
* Create a connect server
*/
var app = connect();
var socketJs = getSocketJs(ui);
var jsFilename = "/" + md5(socketJs, 10) + ".js";
//var cssFilename = "/" + md5(css, 10) + ".css";
/**
* Create a single big file with all deps
*/
//app.use(serveFile(jsFilename, "js", socketJs));
app.use(serveFile(config.defaults.socketJs, "js", socketJs));
// also serve for convenience/testing
app.use(serveFile(config.defaults.pagesConfig, "js", ui.pagesConfig));
//
app.use(serveFile(config.defaults.clientJs, "js", ui.clientJs));
/**
* Add any markup from plugins/hooks/templates
*/
insertPageMarkupFromHooks(
app,
ui.pages,
indexPage
.replace("%pageMarkup%", ui.pageMarkup)
.replace("%templates%", ui.templates)
.replace("%svg%", svg)
.replace("%header%", header)
.replace(/%footer%/g, footer)
);
/**
* gzip css
*/
//app.use(serveFile(cssFilename, "css", css));
app.use(serveStatic(path.join(__dirname, "../public")));
/**
* all public dir as static
*/
app.use(serveStatic(publicDir("")));
/**
* History API fallback
*/
app.use(require("connect-history-api-fallback"));
/**
* Development use
*/
app.use("/node_modules", serveStatic(packageDir("node_modules")));
/**
* Return the server.
*/
return {
server: http.createServer(app),
app: app
};
}
/**
* @param app
* @param pages
* @param markup
*/
function insertPageMarkupFromHooks(app, pages, markup) {
var cached;
app.use(function (req, res, next) {
if (req.url === "/" || pages[req.url.slice(1)]) {
res.writeHead(200, {"Content-Type": "text/html", "Content-Encoding": "gzip"});
if (!cached) {
var buf = Buffer.from(markup, "utf-8");
zlib.gzip(buf, function (_, result) {
cached = result;
res.end(result);
});
} else {
res.end(cached);
}
} else {
next();
}
});
}
/**
* Serve Gzipped files & cache them
* @param app
* @param all
*/
var gzipCache = {};
function serveFile(path, type, string) {
var typemap = {
js: "application/javascript",
css: "text/css"
};
return function (req, res, next) {
if (req.url !== path) {
return next();
}
res.writeHead(200, {
"Content-Type": typemap[type],
"Content-Encoding": "gzip",
"Cache-Control": "no-cache, no-store, must-revalidate",
"Expires": 0,
"Pragma": "no-cache"
});
if (gzipCache[path]) {
return res.end(gzipCache[path]);
}
var buf = Buffer.from(string, "utf-8");
zlib.gzip(buf, function (_, result) {
gzipCache[path] = result;
res.end(result);
});
};
}
/**
* @param cp
* @returns {string}
*/
function getSocketJs (cp) {
return [
cp.bs.getExternalSocketConnector({namespace: "/browser-sync-cp"})
].join(";");
}
///**
// * @returns {*}
// * @param filepath
// */
//function fileContent (filepath) {
// return fs.readFileSync(require.resolve(filepath), "utf8");
//}
/**
* @param src
* @param length
*/
function md5(src, length) {
var crypto = require("crypto");
var hash = crypto.createHash("md5").update(src, "utf8").digest("hex");
return hash.slice(0, length);
}
/**
* CWD directory helper for static dir
* @param {string} filepath
* @returns {string}
*/
function publicDir (filepath) {
return path.join(__dirname, "/../public" + filepath) || "";
}
/**
* @param {string} filepath
* @returns {string|string}
*/
function staticDir (filepath) {
return path.join(__dirname, "/../static" + filepath) || "";
}
/**
* @param {string} filepath
* @returns {*}
*/
function publicFile(filepath) {
return fs.readFileSync(publicDir(filepath), "utf-8");
}
/**
* @param filepath
* @returns {*}
*/
function staticFile(filepath) {
return fs.readFileSync(staticDir(filepath), "utf-8");
}
/**
* @param {string} filepath
* @returns {string}
*/
function packageDir (filepath) {
return path.join(__dirname, "/../" + filepath);
}
module.exports = startServer;

25
node_modules/browser-sync-ui/lib/transform.options.js generated vendored Normal file
View File

@@ -0,0 +1,25 @@
var path = require("path");
module.exports = function (bs) {
/**
* Transform server options to offer additional functionality
* @param bs
*/
var options = bs.options;
var server = options.server;
var cwd = bs.cwd;
/**
* Transform server option
*/
if (server) {
if (Array.isArray(server.baseDir)) {
server.baseDirs = options.server.baseDir.map(function (item) {
return path.join(cwd, item);
});
} else {
server.baseDirs = [path.join(cwd, server.baseDir)];
}
}
};

11
node_modules/browser-sync-ui/lib/transforms.js generated vendored Normal file
View File

@@ -0,0 +1,11 @@
module.exports = {
"mode": function (obj) {
if (obj.get("server")) {
return "Server";
}
if (obj.get("proxy")) {
return "Proxy";
}
return "Snippet";
}
};

0
node_modules/browser-sync-ui/lib/urls.js generated vendored Normal file
View File

35
node_modules/browser-sync-ui/lib/utils.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
var url = require("url");
var http = require("http");
/**
* @param localUrl
* @param urlPath
* @returns {*}
*/
function createUrl(localUrl, urlPath) {
return url.parse(url.resolve(localUrl, urlPath));
}
/**
* @param url
* @param cb
*/
function verifyUrl(url, cb) {
url.headers = {
"accept": "text/html"
};
http.get(url, function (res) {
if (res.statusCode === 200) {
cb(null, res);
} else {
cb("not 200");
}
}).on("error", function(e) {
console.log("Got error: " + e.message);
});
}
module.exports.createUrl = createUrl;
module.exports.verifyUrl = verifyUrl;

View File

@@ -0,0 +1,345 @@
declare type CSSColor =
| 'aliceblue'
| 'antiquewhite'
| 'aqua'
| 'aquamarine'
| 'azure'
| 'beige'
| 'bisque'
| 'black'
| 'blanchedalmond'
| 'blue'
| 'blueviolet'
| 'brown'
| 'burlywood'
| 'cadetblue'
| 'chartreuse'
| 'chocolate'
| 'coral'
| 'cornflowerblue'
| 'cornsilk'
| 'crimson'
| 'cyan'
| 'darkblue'
| 'darkcyan'
| 'darkgoldenrod'
| 'darkgray'
| 'darkgreen'
| 'darkgrey'
| 'darkkhaki'
| 'darkmagenta'
| 'darkolivegreen'
| 'darkorange'
| 'darkorchid'
| 'darkred'
| 'darksalmon'
| 'darkseagreen'
| 'darkslateblue'
| 'darkslategray'
| 'darkslategrey'
| 'darkturquoise'
| 'darkviolet'
| 'deeppink'
| 'deepskyblue'
| 'dimgray'
| 'dimgrey'
| 'dodgerblue'
| 'firebrick'
| 'floralwhite'
| 'forestgreen'
| 'fuchsia'
| 'gainsboro'
| 'ghostwhite'
| 'gold'
| 'goldenrod'
| 'gray'
| 'green'
| 'greenyellow'
| 'grey'
| 'honeydew'
| 'hotpink'
| 'indianred'
| 'indigo'
| 'ivory'
| 'khaki'
| 'lavender'
| 'lavenderblush'
| 'lawngreen'
| 'lemonchiffon'
| 'lightblue'
| 'lightcoral'
| 'lightcyan'
| 'lightgoldenrodyellow'
| 'lightgray'
| 'lightgreen'
| 'lightgrey'
| 'lightpink'
| 'lightsalmon'
| 'lightseagreen'
| 'lightskyblue'
| 'lightslategray'
| 'lightslategrey'
| 'lightsteelblue'
| 'lightyellow'
| 'lime'
| 'limegreen'
| 'linen'
| 'magenta'
| 'maroon'
| 'mediumaquamarine'
| 'mediumblue'
| 'mediumorchid'
| 'mediumpurple'
| 'mediumseagreen'
| 'mediumslateblue'
| 'mediumspringgreen'
| 'mediumturquoise'
| 'mediumvioletred'
| 'midnightblue'
| 'mintcream'
| 'mistyrose'
| 'moccasin'
| 'navajowhite'
| 'navy'
| 'oldlace'
| 'olive'
| 'olivedrab'
| 'orange'
| 'orangered'
| 'orchid'
| 'palegoldenrod'
| 'palegreen'
| 'paleturquoise'
| 'palevioletred'
| 'papayawhip'
| 'peachpuff'
| 'peru'
| 'pink'
| 'plum'
| 'powderblue'
| 'purple'
| 'rebeccapurple'
| 'red'
| 'rosybrown'
| 'royalblue'
| 'saddlebrown'
| 'salmon'
| 'sandybrown'
| 'seagreen'
| 'seashell'
| 'sienna'
| 'silver'
| 'skyblue'
| 'slateblue'
| 'slategray'
| 'slategrey'
| 'snow'
| 'springgreen'
| 'steelblue'
| 'tan'
| 'teal'
| 'thistle'
| 'tomato'
| 'turquoise'
| 'violet'
| 'wheat'
| 'white'
| 'whitesmoke'
| 'yellow'
| 'yellowgreen';
declare namespace ansiStyles {
interface ColorConvert {
/**
The RGB color space.
@param red - (`0`-`255`)
@param green - (`0`-`255`)
@param blue - (`0`-`255`)
*/
rgb(red: number, green: number, blue: number): string;
/**
The RGB HEX color space.
@param hex - A hexadecimal string containing RGB data.
*/
hex(hex: string): string;
/**
@param keyword - A CSS color name.
*/
keyword(keyword: CSSColor): string;
/**
The HSL color space.
@param hue - (`0`-`360`)
@param saturation - (`0`-`100`)
@param lightness - (`0`-`100`)
*/
hsl(hue: number, saturation: number, lightness: number): string;
/**
The HSV color space.
@param hue - (`0`-`360`)
@param saturation - (`0`-`100`)
@param value - (`0`-`100`)
*/
hsv(hue: number, saturation: number, value: number): string;
/**
The HSV color space.
@param hue - (`0`-`360`)
@param whiteness - (`0`-`100`)
@param blackness - (`0`-`100`)
*/
hwb(hue: number, whiteness: number, blackness: number): string;
/**
Use a [4-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#3/4-bit) to set text color.
*/
ansi(ansi: number): string;
/**
Use an [8-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) to set text color.
*/
ansi256(ansi: number): string;
}
interface CSPair {
/**
The ANSI terminal control sequence for starting this style.
*/
readonly open: string;
/**
The ANSI terminal control sequence for ending this style.
*/
readonly close: string;
}
interface ColorBase {
readonly ansi: ColorConvert;
readonly ansi256: ColorConvert;
readonly ansi16m: ColorConvert;
/**
The ANSI terminal control sequence for ending this color.
*/
readonly close: string;
}
interface Modifier {
/**
Resets the current color chain.
*/
readonly reset: CSPair;
/**
Make text bold.
*/
readonly bold: CSPair;
/**
Emitting only a small amount of light.
*/
readonly dim: CSPair;
/**
Make text italic. (Not widely supported)
*/
readonly italic: CSPair;
/**
Make text underline. (Not widely supported)
*/
readonly underline: CSPair;
/**
Inverse background and foreground colors.
*/
readonly inverse: CSPair;
/**
Prints the text, but makes it invisible.
*/
readonly hidden: CSPair;
/**
Puts a horizontal line through the center of the text. (Not widely supported)
*/
readonly strikethrough: CSPair;
}
interface ForegroundColor {
readonly black: CSPair;
readonly red: CSPair;
readonly green: CSPair;
readonly yellow: CSPair;
readonly blue: CSPair;
readonly cyan: CSPair;
readonly magenta: CSPair;
readonly white: CSPair;
/**
Alias for `blackBright`.
*/
readonly gray: CSPair;
/**
Alias for `blackBright`.
*/
readonly grey: CSPair;
readonly blackBright: CSPair;
readonly redBright: CSPair;
readonly greenBright: CSPair;
readonly yellowBright: CSPair;
readonly blueBright: CSPair;
readonly cyanBright: CSPair;
readonly magentaBright: CSPair;
readonly whiteBright: CSPair;
}
interface BackgroundColor {
readonly bgBlack: CSPair;
readonly bgRed: CSPair;
readonly bgGreen: CSPair;
readonly bgYellow: CSPair;
readonly bgBlue: CSPair;
readonly bgCyan: CSPair;
readonly bgMagenta: CSPair;
readonly bgWhite: CSPair;
/**
Alias for `bgBlackBright`.
*/
readonly bgGray: CSPair;
/**
Alias for `bgBlackBright`.
*/
readonly bgGrey: CSPair;
readonly bgBlackBright: CSPair;
readonly bgRedBright: CSPair;
readonly bgGreenBright: CSPair;
readonly bgYellowBright: CSPair;
readonly bgBlueBright: CSPair;
readonly bgCyanBright: CSPair;
readonly bgMagentaBright: CSPair;
readonly bgWhiteBright: CSPair;
}
}
declare const ansiStyles: {
readonly modifier: ansiStyles.Modifier;
readonly color: ansiStyles.ForegroundColor & ansiStyles.ColorBase;
readonly bgColor: ansiStyles.BackgroundColor & ansiStyles.ColorBase;
readonly codes: ReadonlyMap<number, number>;
} & ansiStyles.BackgroundColor & ansiStyles.ForegroundColor & ansiStyles.Modifier;
export = ansiStyles;

View File

@@ -0,0 +1,163 @@
'use strict';
const wrapAnsi16 = (fn, offset) => (...args) => {
const code = fn(...args);
return `\u001B[${code + offset}m`;
};
const wrapAnsi256 = (fn, offset) => (...args) => {
const code = fn(...args);
return `\u001B[${38 + offset};5;${code}m`;
};
const wrapAnsi16m = (fn, offset) => (...args) => {
const rgb = fn(...args);
return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`;
};
const ansi2ansi = n => n;
const rgb2rgb = (r, g, b) => [r, g, b];
const setLazyProperty = (object, property, get) => {
Object.defineProperty(object, property, {
get: () => {
const value = get();
Object.defineProperty(object, property, {
value,
enumerable: true,
configurable: true
});
return value;
},
enumerable: true,
configurable: true
});
};
/** @type {typeof import('color-convert')} */
let colorConvert;
const makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => {
if (colorConvert === undefined) {
colorConvert = require('color-convert');
}
const offset = isBackground ? 10 : 0;
const styles = {};
for (const [sourceSpace, suite] of Object.entries(colorConvert)) {
const name = sourceSpace === 'ansi16' ? 'ansi' : sourceSpace;
if (sourceSpace === targetSpace) {
styles[name] = wrap(identity, offset);
} else if (typeof suite === 'object') {
styles[name] = wrap(suite[targetSpace], offset);
}
}
return styles;
};
function assembleStyles() {
const codes = new Map();
const styles = {
modifier: {
reset: [0, 0],
// 21 isn't widely supported and 22 does the same thing
bold: [1, 22],
dim: [2, 22],
italic: [3, 23],
underline: [4, 24],
inverse: [7, 27],
hidden: [8, 28],
strikethrough: [9, 29]
},
color: {
black: [30, 39],
red: [31, 39],
green: [32, 39],
yellow: [33, 39],
blue: [34, 39],
magenta: [35, 39],
cyan: [36, 39],
white: [37, 39],
// Bright color
blackBright: [90, 39],
redBright: [91, 39],
greenBright: [92, 39],
yellowBright: [93, 39],
blueBright: [94, 39],
magentaBright: [95, 39],
cyanBright: [96, 39],
whiteBright: [97, 39]
},
bgColor: {
bgBlack: [40, 49],
bgRed: [41, 49],
bgGreen: [42, 49],
bgYellow: [43, 49],
bgBlue: [44, 49],
bgMagenta: [45, 49],
bgCyan: [46, 49],
bgWhite: [47, 49],
// Bright color
bgBlackBright: [100, 49],
bgRedBright: [101, 49],
bgGreenBright: [102, 49],
bgYellowBright: [103, 49],
bgBlueBright: [104, 49],
bgMagentaBright: [105, 49],
bgCyanBright: [106, 49],
bgWhiteBright: [107, 49]
}
};
// Alias bright black as gray (and grey)
styles.color.gray = styles.color.blackBright;
styles.bgColor.bgGray = styles.bgColor.bgBlackBright;
styles.color.grey = styles.color.blackBright;
styles.bgColor.bgGrey = styles.bgColor.bgBlackBright;
for (const [groupName, group] of Object.entries(styles)) {
for (const [styleName, style] of Object.entries(group)) {
styles[styleName] = {
open: `\u001B[${style[0]}m`,
close: `\u001B[${style[1]}m`
};
group[styleName] = styles[styleName];
codes.set(style[0], style[1]);
}
Object.defineProperty(styles, groupName, {
value: group,
enumerable: false
});
}
Object.defineProperty(styles, 'codes', {
value: codes,
enumerable: false
});
styles.color.close = '\u001B[39m';
styles.bgColor.close = '\u001B[49m';
setLazyProperty(styles.color, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, false));
setLazyProperty(styles.color, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, false));
setLazyProperty(styles.color, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, false));
setLazyProperty(styles.bgColor, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, true));
setLazyProperty(styles.bgColor, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, true));
setLazyProperty(styles.bgColor, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, true));
return styles;
}
// Make the export immutable
Object.defineProperty(module, 'exports', {
enumerable: true,
get: assembleStyles
});

View File

@@ -0,0 +1,9 @@
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,56 @@
{
"name": "ansi-styles",
"version": "4.3.0",
"description": "ANSI escape codes for styling strings in the terminal",
"license": "MIT",
"repository": "chalk/ansi-styles",
"funding": "https://github.com/chalk/ansi-styles?sponsor=1",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"engines": {
"node": ">=8"
},
"scripts": {
"test": "xo && ava && tsd",
"screenshot": "svg-term --command='node screenshot' --out=screenshot.svg --padding=3 --width=55 --height=3 --at=1000 --no-cursor"
},
"files": [
"index.js",
"index.d.ts"
],
"keywords": [
"ansi",
"styles",
"color",
"colour",
"colors",
"terminal",
"console",
"cli",
"string",
"tty",
"escape",
"formatting",
"rgb",
"256",
"shell",
"xterm",
"log",
"logging",
"command-line",
"text"
],
"dependencies": {
"color-convert": "^2.0.1"
},
"devDependencies": {
"@types/color-convert": "^1.9.0",
"ava": "^2.3.0",
"svg-term-cli": "^2.1.1",
"tsd": "^0.11.0",
"xo": "^0.25.3"
}
}

View File

@@ -0,0 +1,152 @@
# ansi-styles [![Build Status](https://travis-ci.org/chalk/ansi-styles.svg?branch=master)](https://travis-ci.org/chalk/ansi-styles)
> [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal
You probably want the higher-level [chalk](https://github.com/chalk/chalk) module for styling your strings.
<img src="screenshot.svg" width="900">
## Install
```
$ npm install ansi-styles
```
## Usage
```js
const style = require('ansi-styles');
console.log(`${style.green.open}Hello world!${style.green.close}`);
// Color conversion between 16/256/truecolor
// NOTE: If conversion goes to 16 colors or 256 colors, the original color
// may be degraded to fit that color palette. This means terminals
// that do not support 16 million colors will best-match the
// original color.
console.log(style.bgColor.ansi.hsl(120, 80, 72) + 'Hello world!' + style.bgColor.close);
console.log(style.color.ansi256.rgb(199, 20, 250) + 'Hello world!' + style.color.close);
console.log(style.color.ansi16m.hex('#abcdef') + 'Hello world!' + style.color.close);
```
## API
Each style has an `open` and `close` property.
## Styles
### Modifiers
- `reset`
- `bold`
- `dim`
- `italic` *(Not widely supported)*
- `underline`
- `inverse`
- `hidden`
- `strikethrough` *(Not widely supported)*
### Colors
- `black`
- `red`
- `green`
- `yellow`
- `blue`
- `magenta`
- `cyan`
- `white`
- `blackBright` (alias: `gray`, `grey`)
- `redBright`
- `greenBright`
- `yellowBright`
- `blueBright`
- `magentaBright`
- `cyanBright`
- `whiteBright`
### Background colors
- `bgBlack`
- `bgRed`
- `bgGreen`
- `bgYellow`
- `bgBlue`
- `bgMagenta`
- `bgCyan`
- `bgWhite`
- `bgBlackBright` (alias: `bgGray`, `bgGrey`)
- `bgRedBright`
- `bgGreenBright`
- `bgYellowBright`
- `bgBlueBright`
- `bgMagentaBright`
- `bgCyanBright`
- `bgWhiteBright`
## Advanced usage
By default, you get a map of styles, but the styles are also available as groups. They are non-enumerable so they don't show up unless you access them explicitly. This makes it easier to expose only a subset in a higher-level module.
- `style.modifier`
- `style.color`
- `style.bgColor`
###### Example
```js
console.log(style.color.green.open);
```
Raw escape codes (i.e. without the CSI escape prefix `\u001B[` and render mode postfix `m`) are available under `style.codes`, which returns a `Map` with the open codes as keys and close codes as values.
###### Example
```js
console.log(style.codes.get(36));
//=> 39
```
## [256 / 16 million (TrueColor) support](https://gist.github.com/XVilka/8346728)
`ansi-styles` uses the [`color-convert`](https://github.com/Qix-/color-convert) package to allow for converting between various colors and ANSI escapes, with support for 256 and 16 million colors.
The following color spaces from `color-convert` are supported:
- `rgb`
- `hex`
- `keyword`
- `hsl`
- `hsv`
- `hwb`
- `ansi`
- `ansi256`
To use these, call the associated conversion function with the intended output, for example:
```js
style.color.ansi.rgb(100, 200, 15); // RGB to 16 color ansi foreground code
style.bgColor.ansi.rgb(100, 200, 15); // RGB to 16 color ansi background code
style.color.ansi256.hsl(120, 100, 60); // HSL to 256 color ansi foreground code
style.bgColor.ansi256.hsl(120, 100, 60); // HSL to 256 color ansi foreground code
style.color.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color foreground code
style.bgColor.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color background code
```
## Related
- [ansi-escapes](https://github.com/sindresorhus/ansi-escapes) - ANSI escape codes for manipulating the terminal
## Maintainers
- [Sindre Sorhus](https://github.com/sindresorhus)
- [Josh Junon](https://github.com/qix-)
## For enterprise
Available as part of the Tidelift Subscription.
The maintainers of `ansi-styles` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-ansi-styles?utm_source=npm-ansi-styles&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

View File

@@ -0,0 +1,415 @@
/**
Basic foreground colors.
[More colors here.](https://github.com/chalk/chalk/blob/master/readme.md#256-and-truecolor-color-support)
*/
declare type ForegroundColor =
| 'black'
| 'red'
| 'green'
| 'yellow'
| 'blue'
| 'magenta'
| 'cyan'
| 'white'
| 'gray'
| 'grey'
| 'blackBright'
| 'redBright'
| 'greenBright'
| 'yellowBright'
| 'blueBright'
| 'magentaBright'
| 'cyanBright'
| 'whiteBright';
/**
Basic background colors.
[More colors here.](https://github.com/chalk/chalk/blob/master/readme.md#256-and-truecolor-color-support)
*/
declare type BackgroundColor =
| 'bgBlack'
| 'bgRed'
| 'bgGreen'
| 'bgYellow'
| 'bgBlue'
| 'bgMagenta'
| 'bgCyan'
| 'bgWhite'
| 'bgGray'
| 'bgGrey'
| 'bgBlackBright'
| 'bgRedBright'
| 'bgGreenBright'
| 'bgYellowBright'
| 'bgBlueBright'
| 'bgMagentaBright'
| 'bgCyanBright'
| 'bgWhiteBright';
/**
Basic colors.
[More colors here.](https://github.com/chalk/chalk/blob/master/readme.md#256-and-truecolor-color-support)
*/
declare type Color = ForegroundColor | BackgroundColor;
declare type Modifiers =
| 'reset'
| 'bold'
| 'dim'
| 'italic'
| 'underline'
| 'inverse'
| 'hidden'
| 'strikethrough'
| 'visible';
declare namespace chalk {
/**
Levels:
- `0` - All colors disabled.
- `1` - Basic 16 colors support.
- `2` - ANSI 256 colors support.
- `3` - Truecolor 16 million colors support.
*/
type Level = 0 | 1 | 2 | 3;
interface Options {
/**
Specify the color support for Chalk.
By default, color support is automatically detected based on the environment.
Levels:
- `0` - All colors disabled.
- `1` - Basic 16 colors support.
- `2` - ANSI 256 colors support.
- `3` - Truecolor 16 million colors support.
*/
level?: Level;
}
/**
Return a new Chalk instance.
*/
type Instance = new (options?: Options) => Chalk;
/**
Detect whether the terminal supports color.
*/
interface ColorSupport {
/**
The color level used by Chalk.
*/
level: Level;
/**
Return whether Chalk supports basic 16 colors.
*/
hasBasic: boolean;
/**
Return whether Chalk supports ANSI 256 colors.
*/
has256: boolean;
/**
Return whether Chalk supports Truecolor 16 million colors.
*/
has16m: boolean;
}
interface ChalkFunction {
/**
Use a template string.
@remarks Template literals are unsupported for nested calls (see [issue #341](https://github.com/chalk/chalk/issues/341))
@example
```
import chalk = require('chalk');
log(chalk`
CPU: {red ${cpu.totalPercent}%}
RAM: {green ${ram.used / ram.total * 100}%}
DISK: {rgb(255,131,0) ${disk.used / disk.total * 100}%}
`);
```
@example
```
import chalk = require('chalk');
log(chalk.red.bgBlack`2 + 3 = {bold ${2 + 3}}`)
```
*/
(text: TemplateStringsArray, ...placeholders: unknown[]): string;
(...text: unknown[]): string;
}
interface Chalk extends ChalkFunction {
/**
Return a new Chalk instance.
*/
Instance: Instance;
/**
The color support for Chalk.
By default, color support is automatically detected based on the environment.
Levels:
- `0` - All colors disabled.
- `1` - Basic 16 colors support.
- `2` - ANSI 256 colors support.
- `3` - Truecolor 16 million colors support.
*/
level: Level;
/**
Use HEX value to set text color.
@param color - Hexadecimal value representing the desired color.
@example
```
import chalk = require('chalk');
chalk.hex('#DEADED');
```
*/
hex(color: string): Chalk;
/**
Use keyword color value to set text color.
@param color - Keyword value representing the desired color.
@example
```
import chalk = require('chalk');
chalk.keyword('orange');
```
*/
keyword(color: string): Chalk;
/**
Use RGB values to set text color.
*/
rgb(red: number, green: number, blue: number): Chalk;
/**
Use HSL values to set text color.
*/
hsl(hue: number, saturation: number, lightness: number): Chalk;
/**
Use HSV values to set text color.
*/
hsv(hue: number, saturation: number, value: number): Chalk;
/**
Use HWB values to set text color.
*/
hwb(hue: number, whiteness: number, blackness: number): Chalk;
/**
Use a [Select/Set Graphic Rendition](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters) (SGR) [color code number](https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit) to set text color.
30 <= code && code < 38 || 90 <= code && code < 98
For example, 31 for red, 91 for redBright.
*/
ansi(code: number): Chalk;
/**
Use a [8-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) to set text color.
*/
ansi256(index: number): Chalk;
/**
Use HEX value to set background color.
@param color - Hexadecimal value representing the desired color.
@example
```
import chalk = require('chalk');
chalk.bgHex('#DEADED');
```
*/
bgHex(color: string): Chalk;
/**
Use keyword color value to set background color.
@param color - Keyword value representing the desired color.
@example
```
import chalk = require('chalk');
chalk.bgKeyword('orange');
```
*/
bgKeyword(color: string): Chalk;
/**
Use RGB values to set background color.
*/
bgRgb(red: number, green: number, blue: number): Chalk;
/**
Use HSL values to set background color.
*/
bgHsl(hue: number, saturation: number, lightness: number): Chalk;
/**
Use HSV values to set background color.
*/
bgHsv(hue: number, saturation: number, value: number): Chalk;
/**
Use HWB values to set background color.
*/
bgHwb(hue: number, whiteness: number, blackness: number): Chalk;
/**
Use a [Select/Set Graphic Rendition](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters) (SGR) [color code number](https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit) to set background color.
30 <= code && code < 38 || 90 <= code && code < 98
For example, 31 for red, 91 for redBright.
Use the foreground code, not the background code (for example, not 41, nor 101).
*/
bgAnsi(code: number): Chalk;
/**
Use a [8-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) to set background color.
*/
bgAnsi256(index: number): Chalk;
/**
Modifier: Resets the current color chain.
*/
readonly reset: Chalk;
/**
Modifier: Make text bold.
*/
readonly bold: Chalk;
/**
Modifier: Emitting only a small amount of light.
*/
readonly dim: Chalk;
/**
Modifier: Make text italic. (Not widely supported)
*/
readonly italic: Chalk;
/**
Modifier: Make text underline. (Not widely supported)
*/
readonly underline: Chalk;
/**
Modifier: Inverse background and foreground colors.
*/
readonly inverse: Chalk;
/**
Modifier: Prints the text, but makes it invisible.
*/
readonly hidden: Chalk;
/**
Modifier: Puts a horizontal line through the center of the text. (Not widely supported)
*/
readonly strikethrough: Chalk;
/**
Modifier: Prints the text only when Chalk has a color support level > 0.
Can be useful for things that are purely cosmetic.
*/
readonly visible: Chalk;
readonly black: Chalk;
readonly red: Chalk;
readonly green: Chalk;
readonly yellow: Chalk;
readonly blue: Chalk;
readonly magenta: Chalk;
readonly cyan: Chalk;
readonly white: Chalk;
/*
Alias for `blackBright`.
*/
readonly gray: Chalk;
/*
Alias for `blackBright`.
*/
readonly grey: Chalk;
readonly blackBright: Chalk;
readonly redBright: Chalk;
readonly greenBright: Chalk;
readonly yellowBright: Chalk;
readonly blueBright: Chalk;
readonly magentaBright: Chalk;
readonly cyanBright: Chalk;
readonly whiteBright: Chalk;
readonly bgBlack: Chalk;
readonly bgRed: Chalk;
readonly bgGreen: Chalk;
readonly bgYellow: Chalk;
readonly bgBlue: Chalk;
readonly bgMagenta: Chalk;
readonly bgCyan: Chalk;
readonly bgWhite: Chalk;
/*
Alias for `bgBlackBright`.
*/
readonly bgGray: Chalk;
/*
Alias for `bgBlackBright`.
*/
readonly bgGrey: Chalk;
readonly bgBlackBright: Chalk;
readonly bgRedBright: Chalk;
readonly bgGreenBright: Chalk;
readonly bgYellowBright: Chalk;
readonly bgBlueBright: Chalk;
readonly bgMagentaBright: Chalk;
readonly bgCyanBright: Chalk;
readonly bgWhiteBright: Chalk;
}
}
/**
Main Chalk object that allows to chain styles together.
Call the last one as a method with a string argument.
Order doesn't matter, and later styles take precedent in case of a conflict.
This simply means that `chalk.red.yellow.green` is equivalent to `chalk.green`.
*/
declare const chalk: chalk.Chalk & chalk.ChalkFunction & {
supportsColor: chalk.ColorSupport | false;
Level: chalk.Level;
Color: Color;
ForegroundColor: ForegroundColor;
BackgroundColor: BackgroundColor;
Modifiers: Modifiers;
stderr: chalk.Chalk & {supportsColor: chalk.ColorSupport | false};
};
export = chalk;

View File

@@ -0,0 +1,9 @@
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,68 @@
{
"name": "chalk",
"version": "4.1.2",
"description": "Terminal string styling done right",
"license": "MIT",
"repository": "chalk/chalk",
"funding": "https://github.com/chalk/chalk?sponsor=1",
"main": "source",
"engines": {
"node": ">=10"
},
"scripts": {
"test": "xo && nyc ava && tsd",
"bench": "matcha benchmark.js"
},
"files": [
"source",
"index.d.ts"
],
"keywords": [
"color",
"colour",
"colors",
"terminal",
"console",
"cli",
"string",
"str",
"ansi",
"style",
"styles",
"tty",
"formatting",
"rgb",
"256",
"shell",
"xterm",
"log",
"logging",
"command-line",
"text"
],
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"devDependencies": {
"ava": "^2.4.0",
"coveralls": "^3.0.7",
"execa": "^4.0.0",
"import-fresh": "^3.1.0",
"matcha": "^0.7.0",
"nyc": "^15.0.0",
"resolve-from": "^5.0.0",
"tsd": "^0.7.4",
"xo": "^0.28.2"
},
"xo": {
"rules": {
"unicorn/prefer-string-slice": "off",
"unicorn/prefer-includes": "off",
"@typescript-eslint/member-ordering": "off",
"no-redeclare": "off",
"unicorn/string-content": "off",
"unicorn/better-regex": "off"
}
}
}

View File

@@ -0,0 +1,341 @@
<h1 align="center">
<br>
<br>
<img width="320" src="media/logo.svg" alt="Chalk">
<br>
<br>
<br>
</h1>
> Terminal string styling done right
[![Build Status](https://travis-ci.org/chalk/chalk.svg?branch=master)](https://travis-ci.org/chalk/chalk) [![Coverage Status](https://coveralls.io/repos/github/chalk/chalk/badge.svg?branch=master)](https://coveralls.io/github/chalk/chalk?branch=master) [![npm dependents](https://badgen.net/npm/dependents/chalk)](https://www.npmjs.com/package/chalk?activeTab=dependents) [![Downloads](https://badgen.net/npm/dt/chalk)](https://www.npmjs.com/package/chalk) [![](https://img.shields.io/badge/unicorn-approved-ff69b4.svg)](https://www.youtube.com/watch?v=9auOCbH5Ns4) [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/xojs/xo) ![TypeScript-ready](https://img.shields.io/npm/types/chalk.svg) [![run on repl.it](https://repl.it/badge/github/chalk/chalk)](https://repl.it/github/chalk/chalk)
<img src="https://cdn.jsdelivr.net/gh/chalk/ansi-styles@8261697c95bf34b6c7767e2cbe9941a851d59385/screenshot.svg" width="900">
<br>
---
<div align="center">
<p>
<p>
<sup>
Sindre Sorhus' open source work is supported by the community on <a href="https://github.com/sponsors/sindresorhus">GitHub Sponsors</a> and <a href="https://stakes.social/0x44d871aebF0126Bf646753E2C976Aa7e68A66c15">Dev</a>
</sup>
</p>
<sup>Special thanks to:</sup>
<br>
<br>
<a href="https://standardresume.co/tech">
<img src="https://sindresorhus.com/assets/thanks/standard-resume-logo.svg" width="160"/>
</a>
<br>
<br>
<a href="https://retool.com/?utm_campaign=sindresorhus">
<img src="https://sindresorhus.com/assets/thanks/retool-logo.svg" width="230"/>
</a>
<br>
<br>
<a href="https://doppler.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=chalk&utm_source=github">
<div>
<img src="https://dashboard.doppler.com/imgs/logo-long.svg" width="240" alt="Doppler">
</div>
<b>All your environment variables, in one place</b>
<div>
<span>Stop struggling with scattered API keys, hacking together home-brewed tools,</span>
<br>
<span>and avoiding access controls. Keep your team and servers in sync with Doppler.</span>
</div>
</a>
<br>
<a href="https://uibakery.io/?utm_source=chalk&utm_medium=sponsor&utm_campaign=github">
<div>
<img src="https://sindresorhus.com/assets/thanks/uibakery-logo.jpg" width="270" alt="UI Bakery">
</div>
</a>
</p>
</div>
---
<br>
## Highlights
- Expressive API
- Highly performant
- Ability to nest styles
- [256/Truecolor color support](#256-and-truecolor-color-support)
- Auto-detects color support
- Doesn't extend `String.prototype`
- Clean and focused
- Actively maintained
- [Used by ~50,000 packages](https://www.npmjs.com/browse/depended/chalk) as of January 1, 2020
## Install
```console
$ npm install chalk
```
## Usage
```js
const chalk = require('chalk');
console.log(chalk.blue('Hello world!'));
```
Chalk comes with an easy to use composable API where you just chain and nest the styles you want.
```js
const chalk = require('chalk');
const log = console.log;
// Combine styled and normal strings
log(chalk.blue('Hello') + ' World' + chalk.red('!'));
// Compose multiple styles using the chainable API
log(chalk.blue.bgRed.bold('Hello world!'));
// Pass in multiple arguments
log(chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz'));
// Nest styles
log(chalk.red('Hello', chalk.underline.bgBlue('world') + '!'));
// Nest styles of the same type even (color, underline, background)
log(chalk.green(
'I am a green line ' +
chalk.blue.underline.bold('with a blue substring') +
' that becomes green again!'
));
// ES2015 template literal
log(`
CPU: ${chalk.red('90%')}
RAM: ${chalk.green('40%')}
DISK: ${chalk.yellow('70%')}
`);
// ES2015 tagged template literal
log(chalk`
CPU: {red ${cpu.totalPercent}%}
RAM: {green ${ram.used / ram.total * 100}%}
DISK: {rgb(255,131,0) ${disk.used / disk.total * 100}%}
`);
// Use RGB colors in terminal emulators that support it.
log(chalk.keyword('orange')('Yay for orange colored text!'));
log(chalk.rgb(123, 45, 67).underline('Underlined reddish color'));
log(chalk.hex('#DEADED').bold('Bold gray!'));
```
Easily define your own themes:
```js
const chalk = require('chalk');
const error = chalk.bold.red;
const warning = chalk.keyword('orange');
console.log(error('Error!'));
console.log(warning('Warning!'));
```
Take advantage of console.log [string substitution](https://nodejs.org/docs/latest/api/console.html#console_console_log_data_args):
```js
const name = 'Sindre';
console.log(chalk.green('Hello %s'), name);
//=> 'Hello Sindre'
```
## API
### chalk.`<style>[.<style>...](string, [string...])`
Example: `chalk.red.bold.underline('Hello', 'world');`
Chain [styles](#styles) and call the last one as a method with a string argument. Order doesn't matter, and later styles take precedent in case of a conflict. This simply means that `chalk.red.yellow.green` is equivalent to `chalk.green`.
Multiple arguments will be separated by space.
### chalk.level
Specifies the level of color support.
Color support is automatically detected, but you can override it by setting the `level` property. You should however only do this in your own code as it applies globally to all Chalk consumers.
If you need to change this in a reusable module, create a new instance:
```js
const ctx = new chalk.Instance({level: 0});
```
| Level | Description |
| :---: | :--- |
| `0` | All colors disabled |
| `1` | Basic color support (16 colors) |
| `2` | 256 color support |
| `3` | Truecolor support (16 million colors) |
### chalk.supportsColor
Detect whether the terminal [supports color](https://github.com/chalk/supports-color). Used internally and handled for you, but exposed for convenience.
Can be overridden by the user with the flags `--color` and `--no-color`. For situations where using `--color` is not possible, use the environment variable `FORCE_COLOR=1` (level 1), `FORCE_COLOR=2` (level 2), or `FORCE_COLOR=3` (level 3) to forcefully enable color, or `FORCE_COLOR=0` to forcefully disable. The use of `FORCE_COLOR` overrides all other color support checks.
Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=16m` flags, respectively.
### chalk.stderr and chalk.stderr.supportsColor
`chalk.stderr` contains a separate instance configured with color support detected for `stderr` stream instead of `stdout`. Override rules from `chalk.supportsColor` apply to this too. `chalk.stderr.supportsColor` is exposed for convenience.
## Styles
### Modifiers
- `reset` - Resets the current color chain.
- `bold` - Make text bold.
- `dim` - Emitting only a small amount of light.
- `italic` - Make text italic. *(Not widely supported)*
- `underline` - Make text underline. *(Not widely supported)*
- `inverse`- Inverse background and foreground colors.
- `hidden` - Prints the text, but makes it invisible.
- `strikethrough` - Puts a horizontal line through the center of the text. *(Not widely supported)*
- `visible`- Prints the text only when Chalk has a color level > 0. Can be useful for things that are purely cosmetic.
### Colors
- `black`
- `red`
- `green`
- `yellow`
- `blue`
- `magenta`
- `cyan`
- `white`
- `blackBright` (alias: `gray`, `grey`)
- `redBright`
- `greenBright`
- `yellowBright`
- `blueBright`
- `magentaBright`
- `cyanBright`
- `whiteBright`
### Background colors
- `bgBlack`
- `bgRed`
- `bgGreen`
- `bgYellow`
- `bgBlue`
- `bgMagenta`
- `bgCyan`
- `bgWhite`
- `bgBlackBright` (alias: `bgGray`, `bgGrey`)
- `bgRedBright`
- `bgGreenBright`
- `bgYellowBright`
- `bgBlueBright`
- `bgMagentaBright`
- `bgCyanBright`
- `bgWhiteBright`
## Tagged template literal
Chalk can be used as a [tagged template literal](https://exploringjs.com/es6/ch_template-literals.html#_tagged-template-literals).
```js
const chalk = require('chalk');
const miles = 18;
const calculateFeet = miles => miles * 5280;
console.log(chalk`
There are {bold 5280 feet} in a mile.
In {bold ${miles} miles}, there are {green.bold ${calculateFeet(miles)} feet}.
`);
```
Blocks are delimited by an opening curly brace (`{`), a style, some content, and a closing curly brace (`}`).
Template styles are chained exactly like normal Chalk styles. The following three statements are equivalent:
```js
console.log(chalk.bold.rgb(10, 100, 200)('Hello!'));
console.log(chalk.bold.rgb(10, 100, 200)`Hello!`);
console.log(chalk`{bold.rgb(10,100,200) Hello!}`);
```
Note that function styles (`rgb()`, `hsl()`, `keyword()`, etc.) may not contain spaces between parameters.
All interpolated values (`` chalk`${foo}` ``) are converted to strings via the `.toString()` method. All curly braces (`{` and `}`) in interpolated value strings are escaped.
## 256 and Truecolor color support
Chalk supports 256 colors and [Truecolor](https://gist.github.com/XVilka/8346728) (16 million colors) on supported terminal apps.
Colors are downsampled from 16 million RGB values to an ANSI color format that is supported by the terminal emulator (or by specifying `{level: n}` as a Chalk option). For example, Chalk configured to run at level 1 (basic color support) will downsample an RGB value of #FF0000 (red) to 31 (ANSI escape for red).
Examples:
- `chalk.hex('#DEADED').underline('Hello, world!')`
- `chalk.keyword('orange')('Some orange text')`
- `chalk.rgb(15, 100, 204).inverse('Hello!')`
Background versions of these models are prefixed with `bg` and the first level of the module capitalized (e.g. `keyword` for foreground colors and `bgKeyword` for background colors).
- `chalk.bgHex('#DEADED').underline('Hello, world!')`
- `chalk.bgKeyword('orange')('Some orange text')`
- `chalk.bgRgb(15, 100, 204).inverse('Hello!')`
The following color models can be used:
- [`rgb`](https://en.wikipedia.org/wiki/RGB_color_model) - Example: `chalk.rgb(255, 136, 0).bold('Orange!')`
- [`hex`](https://en.wikipedia.org/wiki/Web_colors#Hex_triplet) - Example: `chalk.hex('#FF8800').bold('Orange!')`
- [`keyword`](https://www.w3.org/wiki/CSS/Properties/color/keywords) (CSS keywords) - Example: `chalk.keyword('orange').bold('Orange!')`
- [`hsl`](https://en.wikipedia.org/wiki/HSL_and_HSV) - Example: `chalk.hsl(32, 100, 50).bold('Orange!')`
- [`hsv`](https://en.wikipedia.org/wiki/HSL_and_HSV) - Example: `chalk.hsv(32, 100, 100).bold('Orange!')`
- [`hwb`](https://en.wikipedia.org/wiki/HWB_color_model) - Example: `chalk.hwb(32, 0, 50).bold('Orange!')`
- [`ansi`](https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit) - Example: `chalk.ansi(31).bgAnsi(93)('red on yellowBright')`
- [`ansi256`](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) - Example: `chalk.bgAnsi256(194)('Honeydew, more or less')`
## Windows
If you're on Windows, do yourself a favor and use [Windows Terminal](https://github.com/microsoft/terminal) instead of `cmd.exe`.
## Origin story
[colors.js](https://github.com/Marak/colors.js) used to be the most popular string styling module, but it has serious deficiencies like extending `String.prototype` which causes all kinds of [problems](https://github.com/yeoman/yo/issues/68) and the package is unmaintained. Although there are other packages, they either do too much or not enough. Chalk is a clean and focused alternative.
## chalk for enterprise
Available as part of the Tidelift Subscription.
The maintainers of chalk and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-chalk?utm_source=npm-chalk&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
## Related
- [chalk-cli](https://github.com/chalk/chalk-cli) - CLI for this module
- [ansi-styles](https://github.com/chalk/ansi-styles) - ANSI escape codes for styling strings in the terminal
- [supports-color](https://github.com/chalk/supports-color) - Detect whether a terminal supports color
- [strip-ansi](https://github.com/chalk/strip-ansi) - Strip ANSI escape codes
- [strip-ansi-stream](https://github.com/chalk/strip-ansi-stream) - Strip ANSI escape codes from a stream
- [has-ansi](https://github.com/chalk/has-ansi) - Check if a string has ANSI escape codes
- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes
- [wrap-ansi](https://github.com/chalk/wrap-ansi) - Wordwrap a string with ANSI escape codes
- [slice-ansi](https://github.com/chalk/slice-ansi) - Slice a string with ANSI escape codes
- [color-convert](https://github.com/qix-/color-convert) - Converts colors between different models
- [chalk-animation](https://github.com/bokub/chalk-animation) - Animate strings in the terminal
- [gradient-string](https://github.com/bokub/gradient-string) - Apply color gradients to strings
- [chalk-pipe](https://github.com/LitoMore/chalk-pipe) - Create chalk style schemes with simpler style strings
- [terminal-link](https://github.com/sindresorhus/terminal-link) - Create clickable links in the terminal
## Maintainers
- [Sindre Sorhus](https://github.com/sindresorhus)
- [Josh Junon](https://github.com/qix-)

View File

@@ -0,0 +1,229 @@
'use strict';
const ansiStyles = require('ansi-styles');
const {stdout: stdoutColor, stderr: stderrColor} = require('supports-color');
const {
stringReplaceAll,
stringEncaseCRLFWithFirstIndex
} = require('./util');
const {isArray} = Array;
// `supportsColor.level` → `ansiStyles.color[name]` mapping
const levelMapping = [
'ansi',
'ansi',
'ansi256',
'ansi16m'
];
const styles = Object.create(null);
const applyOptions = (object, options = {}) => {
if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
throw new Error('The `level` option should be an integer from 0 to 3');
}
// Detect level if not set manually
const colorLevel = stdoutColor ? stdoutColor.level : 0;
object.level = options.level === undefined ? colorLevel : options.level;
};
class ChalkClass {
constructor(options) {
// eslint-disable-next-line no-constructor-return
return chalkFactory(options);
}
}
const chalkFactory = options => {
const chalk = {};
applyOptions(chalk, options);
chalk.template = (...arguments_) => chalkTag(chalk.template, ...arguments_);
Object.setPrototypeOf(chalk, Chalk.prototype);
Object.setPrototypeOf(chalk.template, chalk);
chalk.template.constructor = () => {
throw new Error('`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.');
};
chalk.template.Instance = ChalkClass;
return chalk.template;
};
function Chalk(options) {
return chalkFactory(options);
}
for (const [styleName, style] of Object.entries(ansiStyles)) {
styles[styleName] = {
get() {
const builder = createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty);
Object.defineProperty(this, styleName, {value: builder});
return builder;
}
};
}
styles.visible = {
get() {
const builder = createBuilder(this, this._styler, true);
Object.defineProperty(this, 'visible', {value: builder});
return builder;
}
};
const usedModels = ['rgb', 'hex', 'keyword', 'hsl', 'hsv', 'hwb', 'ansi', 'ansi256'];
for (const model of usedModels) {
styles[model] = {
get() {
const {level} = this;
return function (...arguments_) {
const styler = createStyler(ansiStyles.color[levelMapping[level]][model](...arguments_), ansiStyles.color.close, this._styler);
return createBuilder(this, styler, this._isEmpty);
};
}
};
}
for (const model of usedModels) {
const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1);
styles[bgModel] = {
get() {
const {level} = this;
return function (...arguments_) {
const styler = createStyler(ansiStyles.bgColor[levelMapping[level]][model](...arguments_), ansiStyles.bgColor.close, this._styler);
return createBuilder(this, styler, this._isEmpty);
};
}
};
}
const proto = Object.defineProperties(() => {}, {
...styles,
level: {
enumerable: true,
get() {
return this._generator.level;
},
set(level) {
this._generator.level = level;
}
}
});
const createStyler = (open, close, parent) => {
let openAll;
let closeAll;
if (parent === undefined) {
openAll = open;
closeAll = close;
} else {
openAll = parent.openAll + open;
closeAll = close + parent.closeAll;
}
return {
open,
close,
openAll,
closeAll,
parent
};
};
const createBuilder = (self, _styler, _isEmpty) => {
const builder = (...arguments_) => {
if (isArray(arguments_[0]) && isArray(arguments_[0].raw)) {
// Called as a template literal, for example: chalk.red`2 + 3 = {bold ${2+3}}`
return applyStyle(builder, chalkTag(builder, ...arguments_));
}
// Single argument is hot path, implicit coercion is faster than anything
// eslint-disable-next-line no-implicit-coercion
return applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' '));
};
// We alter the prototype because we must return a function, but there is
// no way to create a function with a different prototype
Object.setPrototypeOf(builder, proto);
builder._generator = self;
builder._styler = _styler;
builder._isEmpty = _isEmpty;
return builder;
};
const applyStyle = (self, string) => {
if (self.level <= 0 || !string) {
return self._isEmpty ? '' : string;
}
let styler = self._styler;
if (styler === undefined) {
return string;
}
const {openAll, closeAll} = styler;
if (string.indexOf('\u001B') !== -1) {
while (styler !== undefined) {
// Replace any instances already present with a re-opening code
// otherwise only the part of the string until said closing code
// will be colored, and the rest will simply be 'plain'.
string = stringReplaceAll(string, styler.close, styler.open);
styler = styler.parent;
}
}
// We can move both next actions out of loop, because remaining actions in loop won't have
// any/visible effect on parts we add here. Close the styling before a linebreak and reopen
// after next line to fix a bleed issue on macOS: https://github.com/chalk/chalk/pull/92
const lfIndex = string.indexOf('\n');
if (lfIndex !== -1) {
string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
}
return openAll + string + closeAll;
};
let template;
const chalkTag = (chalk, ...strings) => {
const [firstString] = strings;
if (!isArray(firstString) || !isArray(firstString.raw)) {
// If chalk() was called by itself or with a string,
// return the string itself as a string.
return strings.join(' ');
}
const arguments_ = strings.slice(1);
const parts = [firstString.raw[0]];
for (let i = 1; i < firstString.length; i++) {
parts.push(
String(arguments_[i - 1]).replace(/[{}\\]/g, '\\$&'),
String(firstString.raw[i])
);
}
if (template === undefined) {
template = require('./templates');
}
return template(chalk, parts.join(''));
};
Object.defineProperties(Chalk.prototype, styles);
const chalk = Chalk(); // eslint-disable-line new-cap
chalk.supportsColor = stdoutColor;
chalk.stderr = Chalk({level: stderrColor ? stderrColor.level : 0}); // eslint-disable-line new-cap
chalk.stderr.supportsColor = stderrColor;
module.exports = chalk;

View File

@@ -0,0 +1,134 @@
'use strict';
const TEMPLATE_REGEX = /(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi;
const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g;
const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/;
const ESCAPE_REGEX = /\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi;
const ESCAPES = new Map([
['n', '\n'],
['r', '\r'],
['t', '\t'],
['b', '\b'],
['f', '\f'],
['v', '\v'],
['0', '\0'],
['\\', '\\'],
['e', '\u001B'],
['a', '\u0007']
]);
function unescape(c) {
const u = c[0] === 'u';
const bracket = c[1] === '{';
if ((u && !bracket && c.length === 5) || (c[0] === 'x' && c.length === 3)) {
return String.fromCharCode(parseInt(c.slice(1), 16));
}
if (u && bracket) {
return String.fromCodePoint(parseInt(c.slice(2, -1), 16));
}
return ESCAPES.get(c) || c;
}
function parseArguments(name, arguments_) {
const results = [];
const chunks = arguments_.trim().split(/\s*,\s*/g);
let matches;
for (const chunk of chunks) {
const number = Number(chunk);
if (!Number.isNaN(number)) {
results.push(number);
} else if ((matches = chunk.match(STRING_REGEX))) {
results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, character) => escape ? unescape(escape) : character));
} else {
throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`);
}
}
return results;
}
function parseStyle(style) {
STYLE_REGEX.lastIndex = 0;
const results = [];
let matches;
while ((matches = STYLE_REGEX.exec(style)) !== null) {
const name = matches[1];
if (matches[2]) {
const args = parseArguments(name, matches[2]);
results.push([name].concat(args));
} else {
results.push([name]);
}
}
return results;
}
function buildStyle(chalk, styles) {
const enabled = {};
for (const layer of styles) {
for (const style of layer.styles) {
enabled[style[0]] = layer.inverse ? null : style.slice(1);
}
}
let current = chalk;
for (const [styleName, styles] of Object.entries(enabled)) {
if (!Array.isArray(styles)) {
continue;
}
if (!(styleName in current)) {
throw new Error(`Unknown Chalk style: ${styleName}`);
}
current = styles.length > 0 ? current[styleName](...styles) : current[styleName];
}
return current;
}
module.exports = (chalk, temporary) => {
const styles = [];
const chunks = [];
let chunk = [];
// eslint-disable-next-line max-params
temporary.replace(TEMPLATE_REGEX, (m, escapeCharacter, inverse, style, close, character) => {
if (escapeCharacter) {
chunk.push(unescape(escapeCharacter));
} else if (style) {
const string = chunk.join('');
chunk = [];
chunks.push(styles.length === 0 ? string : buildStyle(chalk, styles)(string));
styles.push({inverse, styles: parseStyle(style)});
} else if (close) {
if (styles.length === 0) {
throw new Error('Found extraneous } in Chalk template literal');
}
chunks.push(buildStyle(chalk, styles)(chunk.join('')));
chunk = [];
styles.pop();
} else {
chunk.push(character);
}
});
chunks.push(chunk.join(''));
if (styles.length > 0) {
const errMessage = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`;
throw new Error(errMessage);
}
return chunks.join('');
};

View File

@@ -0,0 +1,39 @@
'use strict';
const stringReplaceAll = (string, substring, replacer) => {
let index = string.indexOf(substring);
if (index === -1) {
return string;
}
const substringLength = substring.length;
let endIndex = 0;
let returnValue = '';
do {
returnValue += string.substr(endIndex, index - endIndex) + substring + replacer;
endIndex = index + substringLength;
index = string.indexOf(substring, endIndex);
} while (index !== -1);
returnValue += string.substr(endIndex);
return returnValue;
};
const stringEncaseCRLFWithFirstIndex = (string, prefix, postfix, index) => {
let endIndex = 0;
let returnValue = '';
do {
const gotCR = string[index - 1] === '\r';
returnValue += string.substr(endIndex, (gotCR ? index - 1 : index) - endIndex) + prefix + (gotCR ? '\r\n' : '\n') + postfix;
endIndex = index + 1;
index = string.indexOf('\n', endIndex);
} while (index !== -1);
returnValue += string.substr(endIndex);
return returnValue;
};
module.exports = {
stringReplaceAll,
stringEncaseCRLFWithFirstIndex
};

View File

@@ -0,0 +1,54 @@
# 1.0.0 - 2016-01-07
- Removed: unused speed test
- Added: Automatic routing between previously unsupported conversions
([#27](https://github.com/Qix-/color-convert/pull/27))
- Removed: `xxx2xxx()` and `xxx2xxxRaw()` functions
([#27](https://github.com/Qix-/color-convert/pull/27))
- Removed: `convert()` class
([#27](https://github.com/Qix-/color-convert/pull/27))
- Changed: all functions to lookup dictionary
([#27](https://github.com/Qix-/color-convert/pull/27))
- Changed: `ansi` to `ansi256`
([#27](https://github.com/Qix-/color-convert/pull/27))
- Fixed: argument grouping for functions requiring only one argument
([#27](https://github.com/Qix-/color-convert/pull/27))
# 0.6.0 - 2015-07-23
- Added: methods to handle
[ANSI](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors) 16/256 colors:
- rgb2ansi16
- rgb2ansi
- hsl2ansi16
- hsl2ansi
- hsv2ansi16
- hsv2ansi
- hwb2ansi16
- hwb2ansi
- cmyk2ansi16
- cmyk2ansi
- keyword2ansi16
- keyword2ansi
- ansi162rgb
- ansi162hsl
- ansi162hsv
- ansi162hwb
- ansi162cmyk
- ansi162keyword
- ansi2rgb
- ansi2hsl
- ansi2hsv
- ansi2hwb
- ansi2cmyk
- ansi2keyword
([#18](https://github.com/harthur/color-convert/pull/18))
# 0.5.3 - 2015-06-02
- Fixed: hsl2hsv does not return `NaN` anymore when using `[0,0,0]`
([#15](https://github.com/harthur/color-convert/issues/15))
---
Check out commit logs for older releases

View File

@@ -0,0 +1,21 @@
Copyright (c) 2011-2016 Heather Arthur <fayearthur@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,68 @@
# color-convert
[![Build Status](https://travis-ci.org/Qix-/color-convert.svg?branch=master)](https://travis-ci.org/Qix-/color-convert)
Color-convert is a color conversion library for JavaScript and node.
It converts all ways between `rgb`, `hsl`, `hsv`, `hwb`, `cmyk`, `ansi`, `ansi16`, `hex` strings, and CSS `keyword`s (will round to closest):
```js
var convert = require('color-convert');
convert.rgb.hsl(140, 200, 100); // [96, 48, 59]
convert.keyword.rgb('blue'); // [0, 0, 255]
var rgbChannels = convert.rgb.channels; // 3
var cmykChannels = convert.cmyk.channels; // 4
var ansiChannels = convert.ansi16.channels; // 1
```
# Install
```console
$ npm install color-convert
```
# API
Simply get the property of the _from_ and _to_ conversion that you're looking for.
All functions have a rounded and unrounded variant. By default, return values are rounded. To get the unrounded (raw) results, simply tack on `.raw` to the function.
All 'from' functions have a hidden property called `.channels` that indicates the number of channels the function expects (not including alpha).
```js
var convert = require('color-convert');
// Hex to LAB
convert.hex.lab('DEADBF'); // [ 76, 21, -2 ]
convert.hex.lab.raw('DEADBF'); // [ 75.56213190997677, 20.653827952644754, -2.290532499330533 ]
// RGB to CMYK
convert.rgb.cmyk(167, 255, 4); // [ 35, 0, 98, 0 ]
convert.rgb.cmyk.raw(167, 255, 4); // [ 34.509803921568626, 0, 98.43137254901961, 0 ]
```
### Arrays
All functions that accept multiple arguments also support passing an array.
Note that this does **not** apply to functions that convert from a color that only requires one value (e.g. `keyword`, `ansi256`, `hex`, etc.)
```js
var convert = require('color-convert');
convert.rgb.hex(123, 45, 67); // '7B2D43'
convert.rgb.hex([123, 45, 67]); // '7B2D43'
```
## Routing
Conversions that don't have an _explicitly_ defined conversion (in [conversions.js](conversions.js)), but can be converted by means of sub-conversions (e.g. XYZ -> **RGB** -> CMYK), are automatically routed together. This allows just about any color model supported by `color-convert` to be converted to any other model, so long as a sub-conversion path exists. This is also true for conversions requiring more than one step in between (e.g. LCH -> **LAB** -> **XYZ** -> **RGB** -> Hex).
Keep in mind that extensive conversions _may_ result in a loss of precision, and exist only to be complete. For a list of "direct" (single-step) conversions, see [conversions.js](conversions.js).
# Contribute
If there is a new model you would like to support, or want to add a direct conversion between two existing models, please send us a pull request.
# License
Copyright &copy; 2011-2016, Heather Arthur and Josh Junon. Licensed under the [MIT License](LICENSE).

View File

@@ -0,0 +1,839 @@
/* MIT license */
/* eslint-disable no-mixed-operators */
const cssKeywords = require('color-name');
// NOTE: conversions should only return primitive values (i.e. arrays, or
// values that give correct `typeof` results).
// do not use box values types (i.e. Number(), String(), etc.)
const reverseKeywords = {};
for (const key of Object.keys(cssKeywords)) {
reverseKeywords[cssKeywords[key]] = key;
}
const convert = {
rgb: {channels: 3, labels: 'rgb'},
hsl: {channels: 3, labels: 'hsl'},
hsv: {channels: 3, labels: 'hsv'},
hwb: {channels: 3, labels: 'hwb'},
cmyk: {channels: 4, labels: 'cmyk'},
xyz: {channels: 3, labels: 'xyz'},
lab: {channels: 3, labels: 'lab'},
lch: {channels: 3, labels: 'lch'},
hex: {channels: 1, labels: ['hex']},
keyword: {channels: 1, labels: ['keyword']},
ansi16: {channels: 1, labels: ['ansi16']},
ansi256: {channels: 1, labels: ['ansi256']},
hcg: {channels: 3, labels: ['h', 'c', 'g']},
apple: {channels: 3, labels: ['r16', 'g16', 'b16']},
gray: {channels: 1, labels: ['gray']}
};
module.exports = convert;
// Hide .channels and .labels properties
for (const model of Object.keys(convert)) {
if (!('channels' in convert[model])) {
throw new Error('missing channels property: ' + model);
}
if (!('labels' in convert[model])) {
throw new Error('missing channel labels property: ' + model);
}
if (convert[model].labels.length !== convert[model].channels) {
throw new Error('channel and label counts mismatch: ' + model);
}
const {channels, labels} = convert[model];
delete convert[model].channels;
delete convert[model].labels;
Object.defineProperty(convert[model], 'channels', {value: channels});
Object.defineProperty(convert[model], 'labels', {value: labels});
}
convert.rgb.hsl = function (rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const min = Math.min(r, g, b);
const max = Math.max(r, g, b);
const delta = max - min;
let h;
let s;
if (max === min) {
h = 0;
} else if (r === max) {
h = (g - b) / delta;
} else if (g === max) {
h = 2 + (b - r) / delta;
} else if (b === max) {
h = 4 + (r - g) / delta;
}
h = Math.min(h * 60, 360);
if (h < 0) {
h += 360;
}
const l = (min + max) / 2;
if (max === min) {
s = 0;
} else if (l <= 0.5) {
s = delta / (max + min);
} else {
s = delta / (2 - max - min);
}
return [h, s * 100, l * 100];
};
convert.rgb.hsv = function (rgb) {
let rdif;
let gdif;
let bdif;
let h;
let s;
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const v = Math.max(r, g, b);
const diff = v - Math.min(r, g, b);
const diffc = function (c) {
return (v - c) / 6 / diff + 1 / 2;
};
if (diff === 0) {
h = 0;
s = 0;
} else {
s = diff / v;
rdif = diffc(r);
gdif = diffc(g);
bdif = diffc(b);
if (r === v) {
h = bdif - gdif;
} else if (g === v) {
h = (1 / 3) + rdif - bdif;
} else if (b === v) {
h = (2 / 3) + gdif - rdif;
}
if (h < 0) {
h += 1;
} else if (h > 1) {
h -= 1;
}
}
return [
h * 360,
s * 100,
v * 100
];
};
convert.rgb.hwb = function (rgb) {
const r = rgb[0];
const g = rgb[1];
let b = rgb[2];
const h = convert.rgb.hsl(rgb)[0];
const w = 1 / 255 * Math.min(r, Math.min(g, b));
b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));
return [h, w * 100, b * 100];
};
convert.rgb.cmyk = function (rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const k = Math.min(1 - r, 1 - g, 1 - b);
const c = (1 - r - k) / (1 - k) || 0;
const m = (1 - g - k) / (1 - k) || 0;
const y = (1 - b - k) / (1 - k) || 0;
return [c * 100, m * 100, y * 100, k * 100];
};
function comparativeDistance(x, y) {
/*
See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance
*/
return (
((x[0] - y[0]) ** 2) +
((x[1] - y[1]) ** 2) +
((x[2] - y[2]) ** 2)
);
}
convert.rgb.keyword = function (rgb) {
const reversed = reverseKeywords[rgb];
if (reversed) {
return reversed;
}
let currentClosestDistance = Infinity;
let currentClosestKeyword;
for (const keyword of Object.keys(cssKeywords)) {
const value = cssKeywords[keyword];
// Compute comparative distance
const distance = comparativeDistance(rgb, value);
// Check if its less, if so set as closest
if (distance < currentClosestDistance) {
currentClosestDistance = distance;
currentClosestKeyword = keyword;
}
}
return currentClosestKeyword;
};
convert.keyword.rgb = function (keyword) {
return cssKeywords[keyword];
};
convert.rgb.xyz = function (rgb) {
let r = rgb[0] / 255;
let g = rgb[1] / 255;
let b = rgb[2] / 255;
// Assume sRGB
r = r > 0.04045 ? (((r + 0.055) / 1.055) ** 2.4) : (r / 12.92);
g = g > 0.04045 ? (((g + 0.055) / 1.055) ** 2.4) : (g / 12.92);
b = b > 0.04045 ? (((b + 0.055) / 1.055) ** 2.4) : (b / 12.92);
const x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
const y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
const z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
return [x * 100, y * 100, z * 100];
};
convert.rgb.lab = function (rgb) {
const xyz = convert.rgb.xyz(rgb);
let x = xyz[0];
let y = xyz[1];
let z = xyz[2];
x /= 95.047;
y /= 100;
z /= 108.883;
x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116);
y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116);
z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116);
const l = (116 * y) - 16;
const a = 500 * (x - y);
const b = 200 * (y - z);
return [l, a, b];
};
convert.hsl.rgb = function (hsl) {
const h = hsl[0] / 360;
const s = hsl[1] / 100;
const l = hsl[2] / 100;
let t2;
let t3;
let val;
if (s === 0) {
val = l * 255;
return [val, val, val];
}
if (l < 0.5) {
t2 = l * (1 + s);
} else {
t2 = l + s - l * s;
}
const t1 = 2 * l - t2;
const rgb = [0, 0, 0];
for (let i = 0; i < 3; i++) {
t3 = h + 1 / 3 * -(i - 1);
if (t3 < 0) {
t3++;
}
if (t3 > 1) {
t3--;
}
if (6 * t3 < 1) {
val = t1 + (t2 - t1) * 6 * t3;
} else if (2 * t3 < 1) {
val = t2;
} else if (3 * t3 < 2) {
val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
} else {
val = t1;
}
rgb[i] = val * 255;
}
return rgb;
};
convert.hsl.hsv = function (hsl) {
const h = hsl[0];
let s = hsl[1] / 100;
let l = hsl[2] / 100;
let smin = s;
const lmin = Math.max(l, 0.01);
l *= 2;
s *= (l <= 1) ? l : 2 - l;
smin *= lmin <= 1 ? lmin : 2 - lmin;
const v = (l + s) / 2;
const sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s);
return [h, sv * 100, v * 100];
};
convert.hsv.rgb = function (hsv) {
const h = hsv[0] / 60;
const s = hsv[1] / 100;
let v = hsv[2] / 100;
const hi = Math.floor(h) % 6;
const f = h - Math.floor(h);
const p = 255 * v * (1 - s);
const q = 255 * v * (1 - (s * f));
const t = 255 * v * (1 - (s * (1 - f)));
v *= 255;
switch (hi) {
case 0:
return [v, t, p];
case 1:
return [q, v, p];
case 2:
return [p, v, t];
case 3:
return [p, q, v];
case 4:
return [t, p, v];
case 5:
return [v, p, q];
}
};
convert.hsv.hsl = function (hsv) {
const h = hsv[0];
const s = hsv[1] / 100;
const v = hsv[2] / 100;
const vmin = Math.max(v, 0.01);
let sl;
let l;
l = (2 - s) * v;
const lmin = (2 - s) * vmin;
sl = s * vmin;
sl /= (lmin <= 1) ? lmin : 2 - lmin;
sl = sl || 0;
l /= 2;
return [h, sl * 100, l * 100];
};
// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
convert.hwb.rgb = function (hwb) {
const h = hwb[0] / 360;
let wh = hwb[1] / 100;
let bl = hwb[2] / 100;
const ratio = wh + bl;
let f;
// Wh + bl cant be > 1
if (ratio > 1) {
wh /= ratio;
bl /= ratio;
}
const i = Math.floor(6 * h);
const v = 1 - bl;
f = 6 * h - i;
if ((i & 0x01) !== 0) {
f = 1 - f;
}
const n = wh + f * (v - wh); // Linear interpolation
let r;
let g;
let b;
/* eslint-disable max-statements-per-line,no-multi-spaces */
switch (i) {
default:
case 6:
case 0: r = v; g = n; b = wh; break;
case 1: r = n; g = v; b = wh; break;
case 2: r = wh; g = v; b = n; break;
case 3: r = wh; g = n; b = v; break;
case 4: r = n; g = wh; b = v; break;
case 5: r = v; g = wh; b = n; break;
}
/* eslint-enable max-statements-per-line,no-multi-spaces */
return [r * 255, g * 255, b * 255];
};
convert.cmyk.rgb = function (cmyk) {
const c = cmyk[0] / 100;
const m = cmyk[1] / 100;
const y = cmyk[2] / 100;
const k = cmyk[3] / 100;
const r = 1 - Math.min(1, c * (1 - k) + k);
const g = 1 - Math.min(1, m * (1 - k) + k);
const b = 1 - Math.min(1, y * (1 - k) + k);
return [r * 255, g * 255, b * 255];
};
convert.xyz.rgb = function (xyz) {
const x = xyz[0] / 100;
const y = xyz[1] / 100;
const z = xyz[2] / 100;
let r;
let g;
let b;
r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
// Assume sRGB
r = r > 0.0031308
? ((1.055 * (r ** (1.0 / 2.4))) - 0.055)
: r * 12.92;
g = g > 0.0031308
? ((1.055 * (g ** (1.0 / 2.4))) - 0.055)
: g * 12.92;
b = b > 0.0031308
? ((1.055 * (b ** (1.0 / 2.4))) - 0.055)
: b * 12.92;
r = Math.min(Math.max(0, r), 1);
g = Math.min(Math.max(0, g), 1);
b = Math.min(Math.max(0, b), 1);
return [r * 255, g * 255, b * 255];
};
convert.xyz.lab = function (xyz) {
let x = xyz[0];
let y = xyz[1];
let z = xyz[2];
x /= 95.047;
y /= 100;
z /= 108.883;
x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116);
y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116);
z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116);
const l = (116 * y) - 16;
const a = 500 * (x - y);
const b = 200 * (y - z);
return [l, a, b];
};
convert.lab.xyz = function (lab) {
const l = lab[0];
const a = lab[1];
const b = lab[2];
let x;
let y;
let z;
y = (l + 16) / 116;
x = a / 500 + y;
z = y - b / 200;
const y2 = y ** 3;
const x2 = x ** 3;
const z2 = z ** 3;
y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787;
x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787;
z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787;
x *= 95.047;
y *= 100;
z *= 108.883;
return [x, y, z];
};
convert.lab.lch = function (lab) {
const l = lab[0];
const a = lab[1];
const b = lab[2];
let h;
const hr = Math.atan2(b, a);
h = hr * 360 / 2 / Math.PI;
if (h < 0) {
h += 360;
}
const c = Math.sqrt(a * a + b * b);
return [l, c, h];
};
convert.lch.lab = function (lch) {
const l = lch[0];
const c = lch[1];
const h = lch[2];
const hr = h / 360 * 2 * Math.PI;
const a = c * Math.cos(hr);
const b = c * Math.sin(hr);
return [l, a, b];
};
convert.rgb.ansi16 = function (args, saturation = null) {
const [r, g, b] = args;
let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation; // Hsv -> ansi16 optimization
value = Math.round(value / 50);
if (value === 0) {
return 30;
}
let ansi = 30
+ ((Math.round(b / 255) << 2)
| (Math.round(g / 255) << 1)
| Math.round(r / 255));
if (value === 2) {
ansi += 60;
}
return ansi;
};
convert.hsv.ansi16 = function (args) {
// Optimization here; we already know the value and don't need to get
// it converted for us.
return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);
};
convert.rgb.ansi256 = function (args) {
const r = args[0];
const g = args[1];
const b = args[2];
// We use the extended greyscale palette here, with the exception of
// black and white. normal palette only has 4 greyscale shades.
if (r === g && g === b) {
if (r < 8) {
return 16;
}
if (r > 248) {
return 231;
}
return Math.round(((r - 8) / 247) * 24) + 232;
}
const ansi = 16
+ (36 * Math.round(r / 255 * 5))
+ (6 * Math.round(g / 255 * 5))
+ Math.round(b / 255 * 5);
return ansi;
};
convert.ansi16.rgb = function (args) {
let color = args % 10;
// Handle greyscale
if (color === 0 || color === 7) {
if (args > 50) {
color += 3.5;
}
color = color / 10.5 * 255;
return [color, color, color];
}
const mult = (~~(args > 50) + 1) * 0.5;
const r = ((color & 1) * mult) * 255;
const g = (((color >> 1) & 1) * mult) * 255;
const b = (((color >> 2) & 1) * mult) * 255;
return [r, g, b];
};
convert.ansi256.rgb = function (args) {
// Handle greyscale
if (args >= 232) {
const c = (args - 232) * 10 + 8;
return [c, c, c];
}
args -= 16;
let rem;
const r = Math.floor(args / 36) / 5 * 255;
const g = Math.floor((rem = args % 36) / 6) / 5 * 255;
const b = (rem % 6) / 5 * 255;
return [r, g, b];
};
convert.rgb.hex = function (args) {
const integer = ((Math.round(args[0]) & 0xFF) << 16)
+ ((Math.round(args[1]) & 0xFF) << 8)
+ (Math.round(args[2]) & 0xFF);
const string = integer.toString(16).toUpperCase();
return '000000'.substring(string.length) + string;
};
convert.hex.rgb = function (args) {
const match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);
if (!match) {
return [0, 0, 0];
}
let colorString = match[0];
if (match[0].length === 3) {
colorString = colorString.split('').map(char => {
return char + char;
}).join('');
}
const integer = parseInt(colorString, 16);
const r = (integer >> 16) & 0xFF;
const g = (integer >> 8) & 0xFF;
const b = integer & 0xFF;
return [r, g, b];
};
convert.rgb.hcg = function (rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const max = Math.max(Math.max(r, g), b);
const min = Math.min(Math.min(r, g), b);
const chroma = (max - min);
let grayscale;
let hue;
if (chroma < 1) {
grayscale = min / (1 - chroma);
} else {
grayscale = 0;
}
if (chroma <= 0) {
hue = 0;
} else
if (max === r) {
hue = ((g - b) / chroma) % 6;
} else
if (max === g) {
hue = 2 + (b - r) / chroma;
} else {
hue = 4 + (r - g) / chroma;
}
hue /= 6;
hue %= 1;
return [hue * 360, chroma * 100, grayscale * 100];
};
convert.hsl.hcg = function (hsl) {
const s = hsl[1] / 100;
const l = hsl[2] / 100;
const c = l < 0.5 ? (2.0 * s * l) : (2.0 * s * (1.0 - l));
let f = 0;
if (c < 1.0) {
f = (l - 0.5 * c) / (1.0 - c);
}
return [hsl[0], c * 100, f * 100];
};
convert.hsv.hcg = function (hsv) {
const s = hsv[1] / 100;
const v = hsv[2] / 100;
const c = s * v;
let f = 0;
if (c < 1.0) {
f = (v - c) / (1 - c);
}
return [hsv[0], c * 100, f * 100];
};
convert.hcg.rgb = function (hcg) {
const h = hcg[0] / 360;
const c = hcg[1] / 100;
const g = hcg[2] / 100;
if (c === 0.0) {
return [g * 255, g * 255, g * 255];
}
const pure = [0, 0, 0];
const hi = (h % 1) * 6;
const v = hi % 1;
const w = 1 - v;
let mg = 0;
/* eslint-disable max-statements-per-line */
switch (Math.floor(hi)) {
case 0:
pure[0] = 1; pure[1] = v; pure[2] = 0; break;
case 1:
pure[0] = w; pure[1] = 1; pure[2] = 0; break;
case 2:
pure[0] = 0; pure[1] = 1; pure[2] = v; break;
case 3:
pure[0] = 0; pure[1] = w; pure[2] = 1; break;
case 4:
pure[0] = v; pure[1] = 0; pure[2] = 1; break;
default:
pure[0] = 1; pure[1] = 0; pure[2] = w;
}
/* eslint-enable max-statements-per-line */
mg = (1.0 - c) * g;
return [
(c * pure[0] + mg) * 255,
(c * pure[1] + mg) * 255,
(c * pure[2] + mg) * 255
];
};
convert.hcg.hsv = function (hcg) {
const c = hcg[1] / 100;
const g = hcg[2] / 100;
const v = c + g * (1.0 - c);
let f = 0;
if (v > 0.0) {
f = c / v;
}
return [hcg[0], f * 100, v * 100];
};
convert.hcg.hsl = function (hcg) {
const c = hcg[1] / 100;
const g = hcg[2] / 100;
const l = g * (1.0 - c) + 0.5 * c;
let s = 0;
if (l > 0.0 && l < 0.5) {
s = c / (2 * l);
} else
if (l >= 0.5 && l < 1.0) {
s = c / (2 * (1 - l));
}
return [hcg[0], s * 100, l * 100];
};
convert.hcg.hwb = function (hcg) {
const c = hcg[1] / 100;
const g = hcg[2] / 100;
const v = c + g * (1.0 - c);
return [hcg[0], (v - c) * 100, (1 - v) * 100];
};
convert.hwb.hcg = function (hwb) {
const w = hwb[1] / 100;
const b = hwb[2] / 100;
const v = 1 - b;
const c = v - w;
let g = 0;
if (c < 1) {
g = (v - c) / (1 - c);
}
return [hwb[0], c * 100, g * 100];
};
convert.apple.rgb = function (apple) {
return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255];
};
convert.rgb.apple = function (rgb) {
return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535];
};
convert.gray.rgb = function (args) {
return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];
};
convert.gray.hsl = function (args) {
return [0, 0, args[0]];
};
convert.gray.hsv = convert.gray.hsl;
convert.gray.hwb = function (gray) {
return [0, 100, gray[0]];
};
convert.gray.cmyk = function (gray) {
return [0, 0, 0, gray[0]];
};
convert.gray.lab = function (gray) {
return [gray[0], 0, 0];
};
convert.gray.hex = function (gray) {
const val = Math.round(gray[0] / 100 * 255) & 0xFF;
const integer = (val << 16) + (val << 8) + val;
const string = integer.toString(16).toUpperCase();
return '000000'.substring(string.length) + string;
};
convert.rgb.gray = function (rgb) {
const val = (rgb[0] + rgb[1] + rgb[2]) / 3;
return [val / 255 * 100];
};

View File

@@ -0,0 +1,81 @@
const conversions = require('./conversions');
const route = require('./route');
const convert = {};
const models = Object.keys(conversions);
function wrapRaw(fn) {
const wrappedFn = function (...args) {
const arg0 = args[0];
if (arg0 === undefined || arg0 === null) {
return arg0;
}
if (arg0.length > 1) {
args = arg0;
}
return fn(args);
};
// Preserve .conversion property if there is one
if ('conversion' in fn) {
wrappedFn.conversion = fn.conversion;
}
return wrappedFn;
}
function wrapRounded(fn) {
const wrappedFn = function (...args) {
const arg0 = args[0];
if (arg0 === undefined || arg0 === null) {
return arg0;
}
if (arg0.length > 1) {
args = arg0;
}
const result = fn(args);
// We're assuming the result is an array here.
// see notice in conversions.js; don't use box types
// in conversion functions.
if (typeof result === 'object') {
for (let len = result.length, i = 0; i < len; i++) {
result[i] = Math.round(result[i]);
}
}
return result;
};
// Preserve .conversion property if there is one
if ('conversion' in fn) {
wrappedFn.conversion = fn.conversion;
}
return wrappedFn;
}
models.forEach(fromModel => {
convert[fromModel] = {};
Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels});
Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels});
const routes = route(fromModel);
const routeModels = Object.keys(routes);
routeModels.forEach(toModel => {
const fn = routes[toModel];
convert[fromModel][toModel] = wrapRounded(fn);
convert[fromModel][toModel].raw = wrapRaw(fn);
});
});
module.exports = convert;

View File

@@ -0,0 +1,48 @@
{
"name": "color-convert",
"description": "Plain color conversion functions",
"version": "2.0.1",
"author": "Heather Arthur <fayearthur@gmail.com>",
"license": "MIT",
"repository": "Qix-/color-convert",
"scripts": {
"pretest": "xo",
"test": "node test/basic.js"
},
"engines": {
"node": ">=7.0.0"
},
"keywords": [
"color",
"colour",
"convert",
"converter",
"conversion",
"rgb",
"hsl",
"hsv",
"hwb",
"cmyk",
"ansi",
"ansi16"
],
"files": [
"index.js",
"conversions.js",
"route.js"
],
"xo": {
"rules": {
"default-case": 0,
"no-inline-comments": 0,
"operator-linebreak": 0
}
},
"devDependencies": {
"chalk": "^2.4.2",
"xo": "^0.24.0"
},
"dependencies": {
"color-name": "~1.1.4"
}
}

View File

@@ -0,0 +1,97 @@
const conversions = require('./conversions');
/*
This function routes a model to all other models.
all functions that are routed have a property `.conversion` attached
to the returned synthetic function. This property is an array
of strings, each with the steps in between the 'from' and 'to'
color models (inclusive).
conversions that are not possible simply are not included.
*/
function buildGraph() {
const graph = {};
// https://jsperf.com/object-keys-vs-for-in-with-closure/3
const models = Object.keys(conversions);
for (let len = models.length, i = 0; i < len; i++) {
graph[models[i]] = {
// http://jsperf.com/1-vs-infinity
// micro-opt, but this is simple.
distance: -1,
parent: null
};
}
return graph;
}
// https://en.wikipedia.org/wiki/Breadth-first_search
function deriveBFS(fromModel) {
const graph = buildGraph();
const queue = [fromModel]; // Unshift -> queue -> pop
graph[fromModel].distance = 0;
while (queue.length) {
const current = queue.pop();
const adjacents = Object.keys(conversions[current]);
for (let len = adjacents.length, i = 0; i < len; i++) {
const adjacent = adjacents[i];
const node = graph[adjacent];
if (node.distance === -1) {
node.distance = graph[current].distance + 1;
node.parent = current;
queue.unshift(adjacent);
}
}
}
return graph;
}
function link(from, to) {
return function (args) {
return to(from(args));
};
}
function wrapConversion(toModel, graph) {
const path = [graph[toModel].parent, toModel];
let fn = conversions[graph[toModel].parent][toModel];
let cur = graph[toModel].parent;
while (graph[cur].parent) {
path.unshift(graph[cur].parent);
fn = link(conversions[graph[cur].parent][cur], fn);
cur = graph[cur].parent;
}
fn.conversion = path;
return fn;
}
module.exports = function (fromModel) {
const graph = deriveBFS(fromModel);
const conversion = {};
const models = Object.keys(graph);
for (let len = models.length, i = 0; i < len; i++) {
const toModel = models[i];
const node = graph[toModel];
if (node.parent === null) {
// No possible conversion, or this node is the source model.
continue;
}
conversion[toModel] = wrapConversion(toModel, graph);
}
return conversion;
};

View File

@@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright (c) 2015 Dmitry Ivanov
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,11 @@
A JSON with color names and its values. Based on http://dev.w3.org/csswg/css-color/#named-colors.
[![NPM](https://nodei.co/npm/color-name.png?mini=true)](https://nodei.co/npm/color-name/)
```js
var colors = require('color-name');
colors.red //[255,0,0]
```
<a href="LICENSE"><img src="https://upload.wikimedia.org/wikipedia/commons/0/0c/MIT_logo.svg" width="120"/></a>

View File

@@ -0,0 +1,152 @@
'use strict'
module.exports = {
"aliceblue": [240, 248, 255],
"antiquewhite": [250, 235, 215],
"aqua": [0, 255, 255],
"aquamarine": [127, 255, 212],
"azure": [240, 255, 255],
"beige": [245, 245, 220],
"bisque": [255, 228, 196],
"black": [0, 0, 0],
"blanchedalmond": [255, 235, 205],
"blue": [0, 0, 255],
"blueviolet": [138, 43, 226],
"brown": [165, 42, 42],
"burlywood": [222, 184, 135],
"cadetblue": [95, 158, 160],
"chartreuse": [127, 255, 0],
"chocolate": [210, 105, 30],
"coral": [255, 127, 80],
"cornflowerblue": [100, 149, 237],
"cornsilk": [255, 248, 220],
"crimson": [220, 20, 60],
"cyan": [0, 255, 255],
"darkblue": [0, 0, 139],
"darkcyan": [0, 139, 139],
"darkgoldenrod": [184, 134, 11],
"darkgray": [169, 169, 169],
"darkgreen": [0, 100, 0],
"darkgrey": [169, 169, 169],
"darkkhaki": [189, 183, 107],
"darkmagenta": [139, 0, 139],
"darkolivegreen": [85, 107, 47],
"darkorange": [255, 140, 0],
"darkorchid": [153, 50, 204],
"darkred": [139, 0, 0],
"darksalmon": [233, 150, 122],
"darkseagreen": [143, 188, 143],
"darkslateblue": [72, 61, 139],
"darkslategray": [47, 79, 79],
"darkslategrey": [47, 79, 79],
"darkturquoise": [0, 206, 209],
"darkviolet": [148, 0, 211],
"deeppink": [255, 20, 147],
"deepskyblue": [0, 191, 255],
"dimgray": [105, 105, 105],
"dimgrey": [105, 105, 105],
"dodgerblue": [30, 144, 255],
"firebrick": [178, 34, 34],
"floralwhite": [255, 250, 240],
"forestgreen": [34, 139, 34],
"fuchsia": [255, 0, 255],
"gainsboro": [220, 220, 220],
"ghostwhite": [248, 248, 255],
"gold": [255, 215, 0],
"goldenrod": [218, 165, 32],
"gray": [128, 128, 128],
"green": [0, 128, 0],
"greenyellow": [173, 255, 47],
"grey": [128, 128, 128],
"honeydew": [240, 255, 240],
"hotpink": [255, 105, 180],
"indianred": [205, 92, 92],
"indigo": [75, 0, 130],
"ivory": [255, 255, 240],
"khaki": [240, 230, 140],
"lavender": [230, 230, 250],
"lavenderblush": [255, 240, 245],
"lawngreen": [124, 252, 0],
"lemonchiffon": [255, 250, 205],
"lightblue": [173, 216, 230],
"lightcoral": [240, 128, 128],
"lightcyan": [224, 255, 255],
"lightgoldenrodyellow": [250, 250, 210],
"lightgray": [211, 211, 211],
"lightgreen": [144, 238, 144],
"lightgrey": [211, 211, 211],
"lightpink": [255, 182, 193],
"lightsalmon": [255, 160, 122],
"lightseagreen": [32, 178, 170],
"lightskyblue": [135, 206, 250],
"lightslategray": [119, 136, 153],
"lightslategrey": [119, 136, 153],
"lightsteelblue": [176, 196, 222],
"lightyellow": [255, 255, 224],
"lime": [0, 255, 0],
"limegreen": [50, 205, 50],
"linen": [250, 240, 230],
"magenta": [255, 0, 255],
"maroon": [128, 0, 0],
"mediumaquamarine": [102, 205, 170],
"mediumblue": [0, 0, 205],
"mediumorchid": [186, 85, 211],
"mediumpurple": [147, 112, 219],
"mediumseagreen": [60, 179, 113],
"mediumslateblue": [123, 104, 238],
"mediumspringgreen": [0, 250, 154],
"mediumturquoise": [72, 209, 204],
"mediumvioletred": [199, 21, 133],
"midnightblue": [25, 25, 112],
"mintcream": [245, 255, 250],
"mistyrose": [255, 228, 225],
"moccasin": [255, 228, 181],
"navajowhite": [255, 222, 173],
"navy": [0, 0, 128],
"oldlace": [253, 245, 230],
"olive": [128, 128, 0],
"olivedrab": [107, 142, 35],
"orange": [255, 165, 0],
"orangered": [255, 69, 0],
"orchid": [218, 112, 214],
"palegoldenrod": [238, 232, 170],
"palegreen": [152, 251, 152],
"paleturquoise": [175, 238, 238],
"palevioletred": [219, 112, 147],
"papayawhip": [255, 239, 213],
"peachpuff": [255, 218, 185],
"peru": [205, 133, 63],
"pink": [255, 192, 203],
"plum": [221, 160, 221],
"powderblue": [176, 224, 230],
"purple": [128, 0, 128],
"rebeccapurple": [102, 51, 153],
"red": [255, 0, 0],
"rosybrown": [188, 143, 143],
"royalblue": [65, 105, 225],
"saddlebrown": [139, 69, 19],
"salmon": [250, 128, 114],
"sandybrown": [244, 164, 96],
"seagreen": [46, 139, 87],
"seashell": [255, 245, 238],
"sienna": [160, 82, 45],
"silver": [192, 192, 192],
"skyblue": [135, 206, 235],
"slateblue": [106, 90, 205],
"slategray": [112, 128, 144],
"slategrey": [112, 128, 144],
"snow": [255, 250, 250],
"springgreen": [0, 255, 127],
"steelblue": [70, 130, 180],
"tan": [210, 180, 140],
"teal": [0, 128, 128],
"thistle": [216, 191, 216],
"tomato": [255, 99, 71],
"turquoise": [64, 224, 208],
"violet": [238, 130, 238],
"wheat": [245, 222, 179],
"white": [255, 255, 255],
"whitesmoke": [245, 245, 245],
"yellow": [255, 255, 0],
"yellowgreen": [154, 205, 50]
};

View File

@@ -0,0 +1,28 @@
{
"name": "color-name",
"version": "1.1.4",
"description": "A list of color names and its values",
"main": "index.js",
"files": [
"index.js"
],
"scripts": {
"test": "node test.js"
},
"repository": {
"type": "git",
"url": "git@github.com:colorjs/color-name.git"
},
"keywords": [
"color-name",
"color",
"color-keyword",
"keyword"
],
"author": "DY <dfcreative@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/colorjs/color-name/issues"
},
"homepage": "https://github.com/colorjs/color-name"
}

View File

@@ -0,0 +1,39 @@
/**
Check if [`argv`](https://nodejs.org/docs/latest/api/process.html#process_process_argv) has a specific flag.
@param flag - CLI flag to look for. The `--` prefix is optional.
@param argv - CLI arguments. Default: `process.argv`.
@returns Whether the flag exists.
@example
```
// $ ts-node foo.ts -f --unicorn --foo=bar -- --rainbow
// foo.ts
import hasFlag = require('has-flag');
hasFlag('unicorn');
//=> true
hasFlag('--unicorn');
//=> true
hasFlag('f');
//=> true
hasFlag('-f');
//=> true
hasFlag('foo=bar');
//=> true
hasFlag('foo');
//=> false
hasFlag('rainbow');
//=> false
```
*/
declare function hasFlag(flag: string, argv?: string[]): boolean;
export = hasFlag;

View File

@@ -0,0 +1,8 @@
'use strict';
module.exports = (flag, argv = process.argv) => {
const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--');
const position = argv.indexOf(prefix + flag);
const terminatorPosition = argv.indexOf('--');
return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
};

View File

@@ -0,0 +1,9 @@
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,46 @@
{
"name": "has-flag",
"version": "4.0.0",
"description": "Check if argv has a specific flag",
"license": "MIT",
"repository": "sindresorhus/has-flag",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"engines": {
"node": ">=8"
},
"scripts": {
"test": "xo && ava && tsd"
},
"files": [
"index.js",
"index.d.ts"
],
"keywords": [
"has",
"check",
"detect",
"contains",
"find",
"flag",
"cli",
"command-line",
"argv",
"process",
"arg",
"args",
"argument",
"arguments",
"getopt",
"minimist",
"optimist"
],
"devDependencies": {
"ava": "^1.4.1",
"tsd": "^0.7.2",
"xo": "^0.24.0"
}
}

View File

@@ -0,0 +1,89 @@
# has-flag [![Build Status](https://travis-ci.org/sindresorhus/has-flag.svg?branch=master)](https://travis-ci.org/sindresorhus/has-flag)
> Check if [`argv`](https://nodejs.org/docs/latest/api/process.html#process_process_argv) has a specific flag
Correctly stops looking after an `--` argument terminator.
---
<div align="center">
<b>
<a href="https://tidelift.com/subscription/pkg/npm-has-flag?utm_source=npm-has-flag&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
</b>
<br>
<sub>
Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
</sub>
</div>
---
## Install
```
$ npm install has-flag
```
## Usage
```js
// foo.js
const hasFlag = require('has-flag');
hasFlag('unicorn');
//=> true
hasFlag('--unicorn');
//=> true
hasFlag('f');
//=> true
hasFlag('-f');
//=> true
hasFlag('foo=bar');
//=> true
hasFlag('foo');
//=> false
hasFlag('rainbow');
//=> false
```
```
$ node foo.js -f --unicorn --foo=bar -- --rainbow
```
## API
### hasFlag(flag, [argv])
Returns a boolean for whether the flag exists.
#### flag
Type: `string`
CLI flag to look for. The `--` prefix is optional.
#### argv
Type: `string[]`<br>
Default: `process.argv`
CLI arguments.
## Security
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
## License
MIT © [Sindre Sorhus](https://sindresorhus.com)

View File

@@ -0,0 +1,5 @@
'use strict';
module.exports = {
stdout: false,
stderr: false
};

View File

@@ -0,0 +1,135 @@
'use strict';
const os = require('os');
const tty = require('tty');
const hasFlag = require('has-flag');
const {env} = process;
let forceColor;
if (hasFlag('no-color') ||
hasFlag('no-colors') ||
hasFlag('color=false') ||
hasFlag('color=never')) {
forceColor = 0;
} else if (hasFlag('color') ||
hasFlag('colors') ||
hasFlag('color=true') ||
hasFlag('color=always')) {
forceColor = 1;
}
if ('FORCE_COLOR' in env) {
if (env.FORCE_COLOR === 'true') {
forceColor = 1;
} else if (env.FORCE_COLOR === 'false') {
forceColor = 0;
} else {
forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3);
}
}
function translateLevel(level) {
if (level === 0) {
return false;
}
return {
level,
hasBasic: true,
has256: level >= 2,
has16m: level >= 3
};
}
function supportsColor(haveStream, streamIsTTY) {
if (forceColor === 0) {
return 0;
}
if (hasFlag('color=16m') ||
hasFlag('color=full') ||
hasFlag('color=truecolor')) {
return 3;
}
if (hasFlag('color=256')) {
return 2;
}
if (haveStream && !streamIsTTY && forceColor === undefined) {
return 0;
}
const min = forceColor || 0;
if (env.TERM === 'dumb') {
return min;
}
if (process.platform === 'win32') {
// Windows 10 build 10586 is the first Windows release that supports 256 colors.
// Windows 10 build 14931 is the first release that supports 16m/TrueColor.
const osRelease = os.release().split('.');
if (
Number(osRelease[0]) >= 10 &&
Number(osRelease[2]) >= 10586
) {
return Number(osRelease[2]) >= 14931 ? 3 : 2;
}
return 1;
}
if ('CI' in env) {
if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'GITHUB_ACTIONS', 'BUILDKITE'].some(sign => sign in env) || env.CI_NAME === 'codeship') {
return 1;
}
return min;
}
if ('TEAMCITY_VERSION' in env) {
return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
}
if (env.COLORTERM === 'truecolor') {
return 3;
}
if ('TERM_PROGRAM' in env) {
const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10);
switch (env.TERM_PROGRAM) {
case 'iTerm.app':
return version >= 3 ? 3 : 2;
case 'Apple_Terminal':
return 2;
// No default
}
}
if (/-256(color)?$/i.test(env.TERM)) {
return 2;
}
if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
return 1;
}
if ('COLORTERM' in env) {
return 1;
}
return min;
}
function getSupportLevel(stream) {
const level = supportsColor(stream, stream && stream.isTTY);
return translateLevel(level);
}
module.exports = {
supportsColor: getSupportLevel,
stdout: translateLevel(supportsColor(true, tty.isatty(1))),
stderr: translateLevel(supportsColor(true, tty.isatty(2)))
};

Some files were not shown because too many files have changed in this diff Show More