66029 lines
2.0 MiB
66029 lines
2.0 MiB
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
|
/// <reference path="../../localtypings/pxtpackage.d.ts"/>
|
|
/// <reference path="../../built/pxtlib.d.ts"/>
|
|
/// <reference path="../../built/pxtblocks.d.ts"/>
|
|
/// <reference path="../../built/pxtsim.d.ts"/>
|
|
/// <reference path="../../built/pxtwinrt.d.ts"/>
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var ReactDOM = require("react-dom");
|
|
var workspace = require("./workspace");
|
|
var data = require("./data");
|
|
var pkg = require("./package");
|
|
var core = require("./core");
|
|
var sui = require("./sui");
|
|
var simulator = require("./simulator");
|
|
var compiler = require("./compiler");
|
|
var tdlegacy = require("./tdlegacy");
|
|
var cmds = require("./cmds");
|
|
var appcache = require("./appcache");
|
|
var screenshot = require("./screenshot");
|
|
var hidbridge = require("./hidbridge");
|
|
var share = require("./share");
|
|
var tutorial = require("./tutorial");
|
|
var editortoolbar = require("./editortoolbar");
|
|
var filelist = require("./filelist");
|
|
var container = require("./container");
|
|
var scriptsearch = require("./scriptsearch");
|
|
var projects = require("./projects");
|
|
var monaco = require("./monaco");
|
|
var pxtjson = require("./pxtjson");
|
|
var blocks = require("./blocks");
|
|
var logview = require("./logview");
|
|
var draganddrop = require("./draganddrop");
|
|
var hwdbg = require("./hwdbg");
|
|
var electron = require("./electron");
|
|
var Cloud = pxt.Cloud;
|
|
var Util = pxt.Util;
|
|
var lf = Util.lf;
|
|
pxsim.util.injectPolyphils();
|
|
var theEditor;
|
|
/*
|
|
class CloudSyncButton extends data.Component<ISettingsProps, {}> {
|
|
renderCore() {
|
|
Util.assert(pxt.appTarget.cloud && pxt.appTarget.cloud.workspaces);
|
|
|
|
let par = this.props.parent
|
|
let hd = par.state.header
|
|
let hdId = hd ? hd.id : ""
|
|
let btnClass = !hd || this.getData("pkg-status:" + hdId) == "saving" ? " disabled" : ""
|
|
let save = () => {
|
|
par.saveFileAsync()
|
|
.then(() => par.state.currFile.epkg.savePkgAsync())
|
|
.then(() => {
|
|
return workspace.syncAsync()
|
|
})
|
|
.done()
|
|
}
|
|
let needsUpload = hd && !hd.blobCurrent
|
|
return <sui.Button class={btnClass} onClick={save}
|
|
icon={"cloud " + (needsUpload ? "upload" : "") }
|
|
popup={btnClass ? lf("Uploading...") : needsUpload ? lf("Will upload. Click to sync.") : lf("Stored in the cloud. Click to sync.") }
|
|
/>
|
|
}
|
|
}*/
|
|
var ProjectView = (function (_super) {
|
|
__extends(ProjectView, _super);
|
|
function ProjectView(props) {
|
|
var _this = this;
|
|
_super.call(this, props);
|
|
this.allEditors = [];
|
|
this.autoRunBlocksSimulator = pxtc.Util.debounce(function () {
|
|
if (Util.now() - _this.lastChangeTime < 1000)
|
|
return;
|
|
if (!_this.state.active)
|
|
return;
|
|
_this.runSimulator({ background: true });
|
|
}, 1000, true);
|
|
this.autoRunSimulator = pxtc.Util.debounce(function () {
|
|
if (Util.now() - _this.lastChangeTime < 1000)
|
|
return;
|
|
if (!_this.state.active)
|
|
return;
|
|
_this.runSimulator({ background: true });
|
|
}, 2000, true);
|
|
this.typecheck = pxtc.Util.debounce(function () {
|
|
var state = _this.editor.snapshotState();
|
|
compiler.typecheckAsync()
|
|
.done(function (resp) {
|
|
_this.editor.setDiagnostics(_this.editorFile, state);
|
|
if (pxt.appTarget.simulator && pxt.appTarget.simulator.autoRun) {
|
|
var output = pkg.mainEditorPkg().outputPkg.files["output.txt"];
|
|
if (output && !output.numDiagnosticsOverride
|
|
&& !simulator.driver.runOptions.debug
|
|
&& (simulator.driver.state == pxsim.SimulatorState.Running
|
|
|| simulator.driver.state == pxsim.SimulatorState.Unloaded)) {
|
|
if (_this.editor == _this.blocksEditor)
|
|
_this.autoRunBlocksSimulator();
|
|
else
|
|
_this.autoRunSimulator();
|
|
}
|
|
}
|
|
});
|
|
}, 1000, false);
|
|
this.markdownChangeHandler = Util.debounce(function () {
|
|
if (_this.state.currFile && /\.md$/i.test(_this.state.currFile.name))
|
|
_this.setSideMarkdown(_this.editor.getCurrentSource());
|
|
}, 4000, false);
|
|
this.editorChangeHandler = Util.debounce(function () {
|
|
if (!_this.editor.isIncomplete()) {
|
|
_this.saveFile(); // don't wait till save is done
|
|
_this.typecheck();
|
|
}
|
|
_this.markdownChangeHandler();
|
|
}, 500, false);
|
|
// Close on escape
|
|
this.closeOnEscape = function (e) {
|
|
if (e.keyCode !== 27)
|
|
return;
|
|
e.preventDefault();
|
|
_this.toggleSimulatorFullscreen();
|
|
};
|
|
this.debouncedSaveProjectName = Util.debounce(function () {
|
|
_this.saveProjectName();
|
|
}, 2000, false);
|
|
document.title = pxt.appTarget.title || pxt.appTarget.name;
|
|
this.reload = false; //set to true in case of reset of the project where we are going to reload the page.
|
|
this.settings = JSON.parse(pxt.storage.getLocal("editorSettings") || "{}");
|
|
this.state = {
|
|
showFiles: false,
|
|
active: document.visibilityState == 'visible',
|
|
collapseEditorTools: pxt.appTarget.simulator.headless
|
|
};
|
|
if (!this.settings.editorFontSize)
|
|
this.settings.editorFontSize = /mobile/i.test(navigator.userAgent) ? 15 : 20;
|
|
if (!this.settings.fileHistory)
|
|
this.settings.fileHistory = [];
|
|
}
|
|
ProjectView.prototype.updateVisibility = function () {
|
|
var _this = this;
|
|
var active = document.visibilityState == 'visible';
|
|
pxt.debug("page visibility: " + active);
|
|
this.setState({ active: active });
|
|
if (!active) {
|
|
this.stopSimulator();
|
|
this.saveFileAsync().done();
|
|
}
|
|
else {
|
|
if (workspace.isSessionOutdated()) {
|
|
pxt.debug('workspace changed, reloading...');
|
|
var id_1 = this.state.header ? this.state.header.id : '';
|
|
workspace.initAsync()
|
|
.done(function () { return id_1 ? _this.loadHeaderAsync(workspace.getHeader(id_1)) : Promise.resolve(); });
|
|
}
|
|
else if (pxt.appTarget.simulator.autoRun && !this.state.running)
|
|
this.runSimulator();
|
|
}
|
|
};
|
|
ProjectView.prototype.saveSettings = function () {
|
|
var sett = this.settings;
|
|
if (this.reload) {
|
|
return;
|
|
}
|
|
var f = this.editorFile;
|
|
if (f && f.epkg.getTopHeader()) {
|
|
var n_1 = {
|
|
id: f.epkg.getTopHeader().id,
|
|
name: f.getName(),
|
|
pos: this.editor.getViewState()
|
|
};
|
|
sett.fileHistory = sett.fileHistory.filter(function (e) { return e.id != n_1.id || e.name != n_1.name; });
|
|
while (sett.fileHistory.length > 100)
|
|
sett.fileHistory.pop();
|
|
sett.fileHistory.unshift(n_1);
|
|
}
|
|
pxt.storage.setLocal("editorSettings", JSON.stringify(this.settings));
|
|
};
|
|
ProjectView.prototype.componentDidUpdate = function () {
|
|
this.saveSettings();
|
|
this.editor.domUpdate();
|
|
simulator.setState(this.state.header ? this.state.header.editor : '');
|
|
this.editor.resize();
|
|
};
|
|
ProjectView.prototype.fireResize = function () {
|
|
if (document.createEvent) {
|
|
var event_1 = document.createEvent('Event');
|
|
event_1.initEvent('resize', true, true);
|
|
window.dispatchEvent(event_1);
|
|
}
|
|
else {
|
|
document.fireEvent('onresize');
|
|
}
|
|
};
|
|
ProjectView.prototype.saveFile = function () {
|
|
this.saveFileAsync().done();
|
|
};
|
|
ProjectView.prototype.saveFileAsync = function () {
|
|
var _this = this;
|
|
if (!this.editorFile)
|
|
return Promise.resolve();
|
|
return this.saveTypeScriptAsync()
|
|
.then(function () {
|
|
var txt = _this.editor.getCurrentSource();
|
|
if (txt != _this.editorFile.content)
|
|
simulator.makeDirty();
|
|
return _this.editorFile.setContentAsync(txt);
|
|
});
|
|
};
|
|
ProjectView.prototype.isBlocksActive = function () {
|
|
return this.editor == this.blocksEditor
|
|
&& this.editorFile && this.editorFile.name == "main.blocks";
|
|
};
|
|
ProjectView.prototype.isJavaScriptActive = function () {
|
|
return this.editor == this.textEditor
|
|
&& this.editorFile && this.editorFile.name == "main.ts";
|
|
};
|
|
ProjectView.prototype.openJavaScript = function () {
|
|
pxt.tickEvent("menu.javascript");
|
|
if (this.isJavaScriptActive())
|
|
return;
|
|
if (this.isBlocksActive())
|
|
this.blocksEditor.openTypeScript();
|
|
else
|
|
this.setFile(pkg.mainEditorPkg().files["main.ts"]);
|
|
};
|
|
ProjectView.prototype.openBlocks = function () {
|
|
pxt.tickEvent("menu.blocks");
|
|
if (this.isBlocksActive())
|
|
return;
|
|
if (this.isJavaScriptActive())
|
|
this.textEditor.openBlocks();
|
|
else
|
|
this.setFile(pkg.mainEditorPkg().files["main.blocks"]);
|
|
};
|
|
ProjectView.prototype.openTypeScriptAsync = function () {
|
|
var _this = this;
|
|
return this.saveTypeScriptAsync(true)
|
|
.then(function () {
|
|
var header = _this.state.header;
|
|
if (header) {
|
|
header.editor = pxt.JAVASCRIPT_PROJECT_NAME;
|
|
header.pubCurrent = false;
|
|
}
|
|
});
|
|
};
|
|
ProjectView.prototype.typecheckNow = function () {
|
|
this.saveFile(); // don't wait for saving to backend store to finish before typechecking
|
|
this.typecheck();
|
|
};
|
|
ProjectView.prototype.initEditors = function () {
|
|
var _this = this;
|
|
this.textEditor = new monaco.Editor(this);
|
|
this.pxtJsonEditor = new pxtjson.Editor(this);
|
|
this.blocksEditor = new blocks.Editor(this);
|
|
var changeHandler = function () {
|
|
if (_this.editorFile) {
|
|
if (_this.editorFile.inSyncWithEditor)
|
|
pxt.tickActivity("edit", "edit." + _this.editor.getId().replace(/Editor$/, ''));
|
|
_this.editorFile.markDirty();
|
|
}
|
|
_this.lastChangeTime = Util.now();
|
|
if (_this.state.running
|
|
&& pxt.appTarget.simulator && pxt.appTarget.simulator.stopOnChange)
|
|
_this.stopSimulator();
|
|
_this.editorChangeHandler();
|
|
};
|
|
this.allEditors = [this.pxtJsonEditor, this.blocksEditor, this.textEditor];
|
|
this.allEditors.forEach(function (e) { return e.changeCallback = changeHandler; });
|
|
this.editor = this.allEditors[this.allEditors.length - 1];
|
|
};
|
|
ProjectView.prototype.componentWillMount = function () {
|
|
this.initEditors();
|
|
this.initDragAndDrop();
|
|
};
|
|
ProjectView.prototype.componentDidMount = function () {
|
|
var _this = this;
|
|
this.allEditors.forEach(function (e) { return e.prepare(); });
|
|
simulator.init($("#boardview")[0], {
|
|
highlightStatement: function (stmt) {
|
|
if (_this.editor)
|
|
_this.editor.highlightStatement(stmt);
|
|
},
|
|
restartSimulator: function () {
|
|
core.hideDialog();
|
|
_this.runSimulator();
|
|
},
|
|
editor: this.state.header ? this.state.header.editor : ''
|
|
});
|
|
if (pxt.appTarget.appTheme.allowParentController)
|
|
pxt.editor.bindEditorMessages(this);
|
|
this.forceUpdate(); // we now have editors prepared
|
|
};
|
|
ProjectView.prototype.pickEditorFor = function (f) {
|
|
return this.allEditors.filter(function (e) { return e.acceptsFile(f); })[0];
|
|
};
|
|
ProjectView.prototype.updateEditorFile = function (editorOverride) {
|
|
var _this = this;
|
|
if (editorOverride === void 0) { editorOverride = null; }
|
|
if (!this.state.active)
|
|
return;
|
|
if (this.state.currFile == this.editorFile && !editorOverride)
|
|
return;
|
|
this.saveSettings();
|
|
// save file before change
|
|
this.saveFileAsync()
|
|
.then(function () {
|
|
_this.editorFile = _this.state.currFile; // TODO
|
|
var previousEditor = _this.editor;
|
|
_this.editor = editorOverride || _this.pickEditorFor(_this.editorFile);
|
|
_this.allEditors.forEach(function (e) { return e.setVisible(e == _this.editor); });
|
|
return previousEditor ? previousEditor.unloadFileAsync() : Promise.resolve();
|
|
})
|
|
.then(function () { return _this.editor.loadFileAsync(_this.editorFile); })
|
|
.then(function () {
|
|
_this.saveFile(); // make sure state is up to date
|
|
_this.typecheck();
|
|
var e = _this.settings.fileHistory.filter(function (e) { return e.id == _this.state.header.id && e.name == _this.editorFile.getName(); })[0];
|
|
if (e)
|
|
_this.editor.setViewState(e.pos);
|
|
container.SideDocs.notify({
|
|
type: "fileloaded",
|
|
name: _this.editorFile.getName(),
|
|
locale: pxt.Util.localeInfo()
|
|
});
|
|
if (_this.state.showBlocks && _this.editor == _this.textEditor)
|
|
_this.textEditor.openBlocks();
|
|
}).finally(function () {
|
|
_this.forceUpdate();
|
|
});
|
|
};
|
|
ProjectView.prototype.setFile = function (fn) {
|
|
if (!fn)
|
|
return;
|
|
this.setState({
|
|
currFile: fn,
|
|
showBlocks: false
|
|
});
|
|
//this.fireResize();
|
|
};
|
|
ProjectView.prototype.setSideFile = function (fn) {
|
|
var _this = this;
|
|
var header = this.state.header;
|
|
if (header) {
|
|
header.editor = this.getPreferredEditor();
|
|
header.pubCurrent = false;
|
|
}
|
|
var fileName = fn.name;
|
|
var currFile = this.state.currFile.name;
|
|
if (fileName != currFile && pkg.File.blocksFileNameRx.test(fileName)) {
|
|
// Going from ts -> blocks
|
|
pxt.tickEvent("sidebar.showBlocks");
|
|
var tsFileName = fn.getVirtualFileName();
|
|
var tsFile_1 = pkg.mainEditorPkg().lookupFile("this/" + tsFileName);
|
|
if (currFile == tsFileName) {
|
|
// current file is the ts file, so just switch
|
|
this.textEditor.openBlocks();
|
|
}
|
|
else if (tsFile_1) {
|
|
this.textEditor.decompileAsync(tsFile_1.name).then(function (success) {
|
|
if (!success) {
|
|
_this.setFile(tsFile_1);
|
|
_this.textEditor.showConversionFailedDialog(fn.name);
|
|
}
|
|
else {
|
|
_this.setFile(fn);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
else {
|
|
this.setFile(fn);
|
|
}
|
|
};
|
|
ProjectView.prototype.removeFile = function (fn, skipConfirm) {
|
|
var _this = this;
|
|
if (skipConfirm === void 0) { skipConfirm = false; }
|
|
var removeIt = function () {
|
|
pkg.mainEditorPkg().removeFileAsync(fn.name)
|
|
.then(function () { return pkg.mainEditorPkg().saveFilesAsync(true); })
|
|
.then(function () { return _this.reloadHeaderAsync(); })
|
|
.done();
|
|
};
|
|
if (skipConfirm) {
|
|
removeIt();
|
|
return;
|
|
}
|
|
core.confirmAsync({
|
|
header: lf("Remove {0}", fn.name),
|
|
body: lf("You are about to remove a file from your project. You can't undo this. Are you sure?"),
|
|
agreeClass: "red",
|
|
agreeIcon: "trash",
|
|
agreeLbl: lf("Remove it"),
|
|
}).done(function (res) {
|
|
if (res)
|
|
removeIt();
|
|
});
|
|
};
|
|
ProjectView.prototype.setSideMarkdown = function (md) {
|
|
var sd = this.refs["sidedoc"];
|
|
if (!sd)
|
|
return;
|
|
sd.setMarkdown(md);
|
|
};
|
|
ProjectView.prototype.setSideDoc = function (path) {
|
|
var sd = this.refs["sidedoc"];
|
|
if (!sd)
|
|
return;
|
|
if (path)
|
|
sd.setPath(path);
|
|
else
|
|
sd.collapse();
|
|
};
|
|
ProjectView.prototype.setTutorialStep = function (step) {
|
|
// save and typecheck
|
|
this.typecheckNow();
|
|
// Notify tutorial content pane
|
|
var tc = this.refs["tutorialcard"];
|
|
if (!tc)
|
|
return;
|
|
if (step > -1) {
|
|
tutorial.TutorialContent.notify({
|
|
type: "tutorial",
|
|
tutorial: this.state.tutorial,
|
|
subtype: "stepchange",
|
|
step: step
|
|
});
|
|
}
|
|
};
|
|
ProjectView.prototype.handleMessage = function (msg) {
|
|
switch (msg.type) {
|
|
case "tutorial":
|
|
var t = msg;
|
|
switch (t.subtype) {
|
|
case 'steploaded':
|
|
var tt = msg;
|
|
var showCategories = tt.showCategories ? tt.showCategories : Object.keys(tt.data).length > 7;
|
|
this.editor.filterToolbox(tt.data, showCategories, false);
|
|
this.setState({ tutorialReady: true, tutorialCardLocation: tt.location });
|
|
tutorial.TutorialContent.refresh();
|
|
core.hideLoading();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
};
|
|
ProjectView.prototype.reloadHeaderAsync = function () {
|
|
return this.loadHeaderAsync(this.state.header);
|
|
};
|
|
ProjectView.prototype.loadHeaderAsync = function (h) {
|
|
var _this = this;
|
|
if (!h)
|
|
return Promise.resolve();
|
|
this.stopSimulator(true);
|
|
pxt.blocks.cleanBlocks();
|
|
var logs = this.refs["logs"];
|
|
logs.clear();
|
|
this.setState({
|
|
showFiles: false
|
|
});
|
|
return pkg.loadPkgAsync(h.id)
|
|
.then(function () {
|
|
simulator.makeDirty();
|
|
compiler.newProject();
|
|
var e = _this.settings.fileHistory.filter(function (e) { return e.id == h.id; })[0];
|
|
var main = pkg.getEditorPkg(pkg.mainPkg);
|
|
var file = main.getMainFile();
|
|
if (e)
|
|
file = main.lookupFile(e.name) || file;
|
|
if (!e && h.editor == pxt.JAVASCRIPT_PROJECT_NAME && !pkg.File.tsFileNameRx.test(file.getName()) && file.getVirtualFileName())
|
|
file = main.lookupFile("this/" + file.getVirtualFileName()) || file;
|
|
if (pkg.File.blocksFileNameRx.test(file.getName()) && file.getVirtualFileName()) {
|
|
if (!file.content)
|
|
file = main.lookupFile("this/" + file.getVirtualFileName()) || file;
|
|
else
|
|
_this.textEditor.decompileAsync(file.getVirtualFileName()).then(function (success) {
|
|
if (!success)
|
|
file = main.lookupFile("this/" + file.getVirtualFileName()) || file;
|
|
});
|
|
}
|
|
_this.setState({
|
|
header: h,
|
|
projectName: h.name,
|
|
currFile: file,
|
|
sideDocsLoadUrl: ''
|
|
});
|
|
pkg.getEditorPkg(pkg.mainPkg).onupdate = function () {
|
|
_this.loadHeaderAsync(h).done();
|
|
};
|
|
pkg.mainPkg.getCompileOptionsAsync()
|
|
.catch(function (e) {
|
|
if (e instanceof pxt.cpp.PkgConflictError) {
|
|
var confl = e;
|
|
var remove = function (lib) { return ({
|
|
label: lf("Remove {0}", lib.id),
|
|
class: "pink",
|
|
icon: "trash",
|
|
onclick: function () {
|
|
core.showLoading(lf("Removing {0}...", lib.id));
|
|
pkg.mainEditorPkg().removeDepAsync(lib.id)
|
|
.then(function () { return _this.reloadHeaderAsync(); })
|
|
.done(function () { return core.hideLoading(); });
|
|
}
|
|
}); };
|
|
core.dialogAsync({
|
|
hideCancel: true,
|
|
buttons: [
|
|
remove(confl.pkg1),
|
|
remove(confl.pkg0)
|
|
],
|
|
header: lf("Packages cannot be used together"),
|
|
body: lf("Packages '{0}' and '{1}' cannot be used together, because they use incompatible settings ({2}).", confl.pkg1.id, confl.pkg0.id, confl.settingName)
|
|
});
|
|
}
|
|
})
|
|
.done();
|
|
var readme = main.lookupFile("this/README.md");
|
|
if (readme && readme.content && readme.content.trim())
|
|
_this.setSideMarkdown(readme.content);
|
|
else if (pkg.mainPkg.config.documentation)
|
|
_this.setSideDoc(pkg.mainPkg.config.documentation);
|
|
});
|
|
};
|
|
ProjectView.prototype.removeProject = function () {
|
|
var _this = this;
|
|
if (!pkg.mainEditorPkg().header)
|
|
return;
|
|
core.confirmDelete(pkg.mainEditorPkg().header.name, function () {
|
|
var curr = pkg.mainEditorPkg().header;
|
|
curr.isDeleted = true;
|
|
return workspace.saveAsync(curr, {})
|
|
.then(function () {
|
|
if (workspace.getHeaders().length > 0) {
|
|
_this.projects.showOpenProject();
|
|
}
|
|
else {
|
|
_this.newProject();
|
|
}
|
|
});
|
|
});
|
|
};
|
|
ProjectView.prototype.importHexFile = function (file) {
|
|
var _this = this;
|
|
if (!file)
|
|
return;
|
|
pxt.cpp.unpackSourceFromHexFileAsync(file)
|
|
.done(function (data) { return _this.importHex(data); });
|
|
};
|
|
ProjectView.prototype.importBlocksFiles = function (file) {
|
|
var _this = this;
|
|
if (!file)
|
|
return;
|
|
fileReadAsTextAsync(file)
|
|
.done(function (contents) {
|
|
_this.newProject({
|
|
filesOverride: { "main.blocks": contents, "main.ts": " " },
|
|
name: file.name.replace(/\.blocks$/i, '') || lf("Untitled")
|
|
});
|
|
});
|
|
};
|
|
ProjectView.prototype.importTypescriptFile = function (file) {
|
|
var _this = this;
|
|
if (!file)
|
|
return;
|
|
fileReadAsTextAsync(file)
|
|
.done(function (contents) {
|
|
_this.newProject({
|
|
filesOverride: { "main.blocks": '', "main.ts": contents || " " },
|
|
name: file.name.replace(/\.ts$/i, '') || lf("Untitled")
|
|
});
|
|
});
|
|
};
|
|
ProjectView.prototype.importHex = function (data) {
|
|
var _this = this;
|
|
var targetId = pxt.appTarget.id;
|
|
if (!data || !data.meta) {
|
|
core.warningNotification(lf("Sorry, we could not recognize this file."));
|
|
return;
|
|
}
|
|
if (data.meta.cloudId == "microbit.co.uk" && data.meta.editor == "blockly") {
|
|
pxt.tickEvent("import.blocks");
|
|
pxt.debug('importing microbit.co.uk blocks project');
|
|
core.showLoading(lf("loading project..."));
|
|
this.createProjectAsync({
|
|
filesOverride: {
|
|
"main.blocks": data.source
|
|
}, name: data.meta.name
|
|
}).done(function () { return core.hideLoading(); });
|
|
return;
|
|
}
|
|
else if (data.meta.cloudId == "microbit.co.uk" && data.meta.editor == "touchdevelop") {
|
|
pxt.tickEvent("import.td");
|
|
pxt.debug('importing microbit.co.uk TD project');
|
|
core.showLoading("loading project...");
|
|
this.createProjectAsync({
|
|
filesOverride: { "main.blocks": "", "main.ts": " " },
|
|
name: data.meta.name
|
|
})
|
|
.then(function () { return tdlegacy.td2tsAsync(data.source); })
|
|
.then(function (text) { return _this.textEditor.overrideFile(text); })
|
|
.done(function () { return core.hideLoading(); });
|
|
return;
|
|
}
|
|
else if (data.meta.cloudId == "ks/" + targetId || data.meta.cloudId == pxt.CLOUD_ID + targetId // match on targetid
|
|
|| (Util.startsWith(data.meta.cloudId, pxt.CLOUD_ID + targetId)) // trying to load white-label file into main target
|
|
) {
|
|
pxt.tickEvent("import.pxt");
|
|
pxt.debug("importing project");
|
|
var h = {
|
|
target: targetId,
|
|
editor: data.meta.editor,
|
|
name: data.meta.name,
|
|
meta: {},
|
|
pubId: "",
|
|
pubCurrent: false
|
|
};
|
|
var files = JSON.parse(data.source);
|
|
// we cannot load the workspace until we've loaded the project
|
|
workspace.installAsync(h, files)
|
|
.done(function (hd) { return _this.loadHeaderAsync(hd); });
|
|
return;
|
|
}
|
|
core.warningNotification(lf("Sorry, we could not import this project."));
|
|
pxt.tickEvent("warning.importfailed");
|
|
};
|
|
ProjectView.prototype.importProjectFile = function (file) {
|
|
var _this = this;
|
|
if (!file)
|
|
return;
|
|
fileReadAsBufferAsync(file)
|
|
.then(function (buf) { return pxt.lzmaDecompressAsync(buf); })
|
|
.done(function (contents) {
|
|
var data = JSON.parse(contents);
|
|
_this.importHex(data);
|
|
}, function (e) {
|
|
core.warningNotification(lf("Sorry, we could not import this project."));
|
|
});
|
|
};
|
|
ProjectView.prototype.importFile = function (file) {
|
|
if (!file || pxt.shell.isReadOnly())
|
|
return;
|
|
if (isHexFile(file.name)) {
|
|
this.importHexFile(file);
|
|
}
|
|
else if (isBlocksFile(file.name)) {
|
|
this.importBlocksFiles(file);
|
|
}
|
|
else if (isTypescriptFile(file.name)) {
|
|
this.importTypescriptFile(file);
|
|
}
|
|
else if (isProjectFile(file.name)) {
|
|
this.importProjectFile(file);
|
|
}
|
|
else
|
|
core.warningNotification(lf("Oops, don't know how to load this file!"));
|
|
};
|
|
ProjectView.prototype.initDragAndDrop = function () {
|
|
var _this = this;
|
|
draganddrop.setupDragAndDrop(document.body, function (file) { return file.size < 1000000 && isHexFile(file.name) || isBlocksFile(file.name); }, function (files) {
|
|
if (files) {
|
|
pxt.tickEvent("dragandrop.open");
|
|
_this.importFile(files[0]);
|
|
}
|
|
});
|
|
};
|
|
ProjectView.prototype.openProject = function () {
|
|
pxt.tickEvent("menu.open");
|
|
this.projects.showOpenProject();
|
|
};
|
|
ProjectView.prototype.exportProjectToFileAsync = function () {
|
|
var _this = this;
|
|
var mpkg = pkg.mainPkg;
|
|
return this.saveFileAsync()
|
|
.then(function () { return mpkg.filesToBePublishedAsync(true); })
|
|
.then(function (files) {
|
|
var project = {
|
|
meta: {
|
|
cloudId: pxt.CLOUD_ID + pxt.appTarget.id,
|
|
targetVersions: pxt.appTarget.versions,
|
|
editor: _this.getPreferredEditor(),
|
|
name: mpkg.config.name
|
|
},
|
|
source: JSON.stringify(files, null, 2)
|
|
};
|
|
return pxt.lzmaCompressAsync(JSON.stringify(project, null, 2));
|
|
});
|
|
};
|
|
ProjectView.prototype.getPreferredEditor = function () {
|
|
return this.editor == this.blocksEditor ? pxt.BLOCKS_PROJECT_NAME : pxt.JAVASCRIPT_PROJECT_NAME;
|
|
};
|
|
ProjectView.prototype.exportAsync = function () {
|
|
pxt.debug("exporting project");
|
|
return this.exportProjectToFileAsync()
|
|
.then(function (buf) {
|
|
return window.btoa(Util.uint8ArrayToString(buf));
|
|
});
|
|
};
|
|
ProjectView.prototype.importProjectFromFileAsync = function (buf) {
|
|
var _this = this;
|
|
return pxt.lzmaDecompressAsync(buf)
|
|
.then(function (project) {
|
|
var hexFile = JSON.parse(project);
|
|
return _this.importHex(hexFile);
|
|
}).catch(function () {
|
|
return _this.newProject();
|
|
});
|
|
};
|
|
ProjectView.prototype.saveProjectToFile = function () {
|
|
var mpkg = pkg.mainPkg;
|
|
this.exportProjectToFileAsync()
|
|
.done(function (buf) {
|
|
var fn = pkg.genFileName(".pxt");
|
|
pxt.BrowserUtils.browserDownloadUInt8Array(buf, fn, 'application/octet-stream');
|
|
});
|
|
};
|
|
ProjectView.prototype.addPackage = function () {
|
|
pxt.tickEvent("menu.addpackage");
|
|
this.scriptSearch.showAddPackages();
|
|
};
|
|
ProjectView.prototype.newEmptyProject = function (name, documentation) {
|
|
this.newProject({
|
|
filesOverride: { "main.blocks": "<xml xmlns=\"http://www.w3.org/1999/xhtml\"></xml>" },
|
|
name: name, documentation: documentation
|
|
});
|
|
};
|
|
ProjectView.prototype.newProject = function (options) {
|
|
if (options === void 0) { options = {}; }
|
|
pxt.tickEvent("menu.newproject");
|
|
core.showLoading(lf("creating new project..."));
|
|
this.createProjectAsync(options)
|
|
.then(function () { return Promise.delay(500); })
|
|
.done(function () { return core.hideLoading(); });
|
|
};
|
|
ProjectView.prototype.createProjectAsync = function (options) {
|
|
var _this = this;
|
|
this.setSideDoc(undefined);
|
|
if (!options.prj)
|
|
options.prj = pxt.appTarget.blocksprj;
|
|
var cfg = pxt.U.clone(options.prj.config);
|
|
cfg.name = options.name || lf("Untitled"); // pxt.U.fmt(cfg.name, Util.getAwesomeAdj());
|
|
cfg.documentation = options.documentation;
|
|
var files = Util.clone(options.prj.files);
|
|
if (options.filesOverride)
|
|
Util.jsonCopyFrom(files, options.filesOverride);
|
|
files["pxt.json"] = JSON.stringify(cfg, null, 4) + "\n";
|
|
return workspace.installAsync({
|
|
name: cfg.name,
|
|
meta: {},
|
|
editor: options.prj.id,
|
|
pubId: "",
|
|
pubCurrent: false,
|
|
target: pxt.appTarget.id,
|
|
temporary: options.temporary
|
|
}, files).then(function (hd) { return _this.loadHeaderAsync(hd); });
|
|
};
|
|
ProjectView.prototype.switchTypeScript = function () {
|
|
var mainPkg = pkg.mainEditorPkg();
|
|
var tsName = this.editorFile.getVirtualFileName();
|
|
var f = mainPkg.files[tsName];
|
|
this.setFile(f);
|
|
};
|
|
ProjectView.prototype.saveBlocksToTypeScript = function () {
|
|
return this.blocksEditor.saveToTypeScript();
|
|
};
|
|
ProjectView.prototype.saveTypeScriptAsync = function (open) {
|
|
var _this = this;
|
|
if (open === void 0) { open = false; }
|
|
if (!this.editor || !this.state.currFile || this.editorFile.epkg != pkg.mainEditorPkg() || this.reload)
|
|
return Promise.resolve();
|
|
var promise = Promise.resolve().then(function () {
|
|
return open ? _this.textEditor.loadMonacoAsync() : Promise.resolve();
|
|
}).then(function () {
|
|
var src = _this.editor.saveToTypeScript();
|
|
if (!src)
|
|
return Promise.resolve();
|
|
// format before saving
|
|
//src = pxtc.format(src, 0).formatted;
|
|
var mainPkg = pkg.mainEditorPkg();
|
|
var tsName = _this.editorFile.getVirtualFileName();
|
|
Util.assert(tsName != _this.editorFile.name);
|
|
return mainPkg.setContentAsync(tsName, src).then(function () {
|
|
if (open) {
|
|
var f = mainPkg.files[tsName];
|
|
_this.setFile(f);
|
|
}
|
|
});
|
|
});
|
|
if (open) {
|
|
return core.showLoadingAsync(lf("switching to JavaScript..."), promise, 0);
|
|
}
|
|
else {
|
|
return promise;
|
|
}
|
|
};
|
|
ProjectView.prototype.reset = function () {
|
|
var _this = this;
|
|
pxt.tickEvent("reset");
|
|
core.confirmAsync({
|
|
header: lf("Reset"),
|
|
body: lf("You are about to clear all projects. Are you sure? This operation cannot be undone."),
|
|
agreeLbl: lf("Reset"),
|
|
agreeClass: "red",
|
|
agreeIcon: "sign out",
|
|
disagreeLbl: lf("Cancel")
|
|
}).then(function (r) {
|
|
if (!r)
|
|
return;
|
|
_this.reload = true; //Indicate we are goint to reload next.
|
|
workspace.resetAsync()
|
|
.done(function () { return window.location.reload(); }, function () { return window.location.reload(); });
|
|
});
|
|
};
|
|
ProjectView.prototype.saveAndCompile = function () {
|
|
this.saveFile();
|
|
if (!pxt.appTarget.compile.hasHex) {
|
|
this.saveProjectToFile();
|
|
}
|
|
else {
|
|
this.compile(true);
|
|
}
|
|
};
|
|
ProjectView.prototype.compile = function (saveOnly) {
|
|
var _this = this;
|
|
if (saveOnly === void 0) { saveOnly = false; }
|
|
// the USB init has to be called from an event handler
|
|
if (/webusb=1/i.test(window.location.href)) {
|
|
pxt.usb.initAsync().catch(function (e) { });
|
|
}
|
|
pxt.tickEvent("compile");
|
|
pxt.debug('compiling...');
|
|
if (this.state.compiling) {
|
|
pxt.tickEvent("compile.double");
|
|
return;
|
|
}
|
|
var simRestart = this.state.running;
|
|
this.setState({ compiling: true });
|
|
this.clearLog();
|
|
this.editor.beforeCompile();
|
|
if (simRestart)
|
|
this.stopSimulator();
|
|
var state = this.editor.snapshotState();
|
|
compiler.compileAsync({ native: true, forceEmit: true, preferredEditor: this.getPreferredEditor() })
|
|
.then(function (resp) {
|
|
_this.editor.setDiagnostics(_this.editorFile, state);
|
|
var fn = pxt.appTarget.compile.useUF2 ? pxtc.BINARY_UF2 : pxtc.BINARY_HEX;
|
|
if (!resp.outfiles[fn]) {
|
|
pxt.tickEvent("compile.noemit");
|
|
core.warningNotification(lf("Compilation failed, please check your code for errors."));
|
|
return Promise.resolve();
|
|
}
|
|
resp.saveOnly = saveOnly;
|
|
return pxt.commands.deployCoreAsync(resp)
|
|
.catch(function (e) {
|
|
core.warningNotification(lf(".hex file upload failed, please try again."));
|
|
pxt.reportException(e);
|
|
});
|
|
}).catch(function (e) {
|
|
pxt.reportException(e);
|
|
core.errorNotification(lf("Compilation failed, please contact support."));
|
|
}).finally(function () {
|
|
_this.setState({ compiling: false });
|
|
if (simRestart)
|
|
_this.runSimulator();
|
|
})
|
|
.done();
|
|
};
|
|
ProjectView.prototype.startStopSimulator = function () {
|
|
if (this.state.running) {
|
|
pxt.tickEvent('simulator.stop');
|
|
this.stopSimulator();
|
|
}
|
|
else {
|
|
pxt.tickEvent('simulator.start');
|
|
this.startSimulator();
|
|
}
|
|
};
|
|
ProjectView.prototype.restartSimulator = function () {
|
|
pxt.tickEvent('simulator.restart');
|
|
this.stopSimulator();
|
|
this.startSimulator();
|
|
};
|
|
ProjectView.prototype.startSimulator = function () {
|
|
var _this = this;
|
|
pxt.tickEvent('simulator.start');
|
|
this.saveFileAsync()
|
|
.then(function () { return _this.runSimulator(); });
|
|
};
|
|
ProjectView.prototype.stopSimulator = function (unload) {
|
|
simulator.stop(unload);
|
|
this.setState({ running: false });
|
|
};
|
|
ProjectView.prototype.proxySimulatorMessage = function (content) {
|
|
simulator.proxy({
|
|
type: "custom",
|
|
content: content
|
|
});
|
|
};
|
|
ProjectView.prototype.toggleSimulatorCollapse = function () {
|
|
var state = this.state;
|
|
if (!state.running && state.collapseEditorTools)
|
|
this.startStopSimulator();
|
|
if (state.collapseEditorTools) {
|
|
this.expandSimulator();
|
|
}
|
|
else {
|
|
this.collapseSimulator();
|
|
}
|
|
};
|
|
ProjectView.prototype.expandSimulator = function () {
|
|
if (pxt.appTarget.simulator.headless) {
|
|
simulator.unhide();
|
|
}
|
|
else {
|
|
this.startSimulator();
|
|
}
|
|
this.setState({ collapseEditorTools: false });
|
|
};
|
|
ProjectView.prototype.collapseSimulator = function () {
|
|
var _this = this;
|
|
simulator.hide(function () {
|
|
_this.setState({ collapseEditorTools: true });
|
|
});
|
|
};
|
|
ProjectView.prototype.toggleSimulatorFullscreen = function () {
|
|
pxt.tickEvent("simulator.fullscreen", { view: 'computer', fullScreenTo: '' + !this.state.fullscreen });
|
|
if (!this.state.fullscreen) {
|
|
document.addEventListener('keydown', this.closeOnEscape);
|
|
}
|
|
else {
|
|
document.removeEventListener('keydown', this.closeOnEscape);
|
|
}
|
|
this.setState({ fullscreen: !this.state.fullscreen });
|
|
};
|
|
ProjectView.prototype.toggleMute = function () {
|
|
pxt.tickEvent("simulator.mute", { view: 'computer', muteTo: '' + !this.state.mute });
|
|
simulator.mute(!this.state.mute);
|
|
this.setState({ mute: !this.state.mute });
|
|
};
|
|
ProjectView.prototype.openInstructions = function () {
|
|
pxt.tickEvent("simulator.make");
|
|
compiler.compileAsync({ native: true })
|
|
.done(function (resp) {
|
|
var p = pkg.mainEditorPkg();
|
|
var code = p.files["main.ts"];
|
|
var data = {
|
|
name: p.header.name || lf("Untitled"),
|
|
code: code ? code.content : "basic.showString(\"Hi!\");",
|
|
board: JSON.stringify(pxt.appTarget.simulator.boardDefinition)
|
|
};
|
|
var parts = ts.pxtc.computeUsedParts(resp);
|
|
if (parts.length) {
|
|
data.parts = parts.join(" ");
|
|
data.partdefs = JSON.stringify(pkg.mainPkg.computePartDefinitions(parts));
|
|
}
|
|
var fnArgs = resp.usedArguments;
|
|
if (fnArgs)
|
|
data.fnArgs = JSON.stringify(fnArgs);
|
|
data.package = Util.values(pkg.mainPkg.deps).filter(function (p) { return p.id != "this"; }).map(function (p) { return (p.id + "=" + p._verspec); }).join('\n');
|
|
var urlData = Object.keys(data).map(function (k) { return (k + "=" + encodeURIComponent(data[k])); }).join('&');
|
|
var url = pxt.webConfig.partsUrl + "?" + urlData;
|
|
window.open(url, '_blank');
|
|
});
|
|
};
|
|
ProjectView.prototype.clearLog = function () {
|
|
var logs = this.refs["logs"];
|
|
logs.clear();
|
|
};
|
|
ProjectView.prototype.hwDebug = function () {
|
|
var start = Promise.resolve();
|
|
if (!this.state.running || !simulator.driver.runOptions.debug)
|
|
start = this.runSimulator({ debug: true });
|
|
return start.then(function () {
|
|
simulator.driver.setHwDebugger({
|
|
postMessage: function (msg) {
|
|
hwdbg.handleMessage(msg);
|
|
}
|
|
});
|
|
hwdbg.postMessage = function (msg) { return simulator.driver.handleHwDebuggerMsg(msg); };
|
|
return hwdbg.startDebugAsync();
|
|
});
|
|
};
|
|
ProjectView.prototype.runSimulator = function (opts) {
|
|
var _this = this;
|
|
if (opts === void 0) { opts = {}; }
|
|
var editorId = this.editor ? this.editor.getId().replace(/Editor$/, '') : "unknown";
|
|
if (opts.background)
|
|
pxt.tickActivity("autorun", "autorun." + editorId);
|
|
else
|
|
pxt.tickEvent(opts.debug ? "debug" : "run", { editor: editorId });
|
|
if (!opts.background)
|
|
this.editor.beforeCompile();
|
|
this.stopSimulator();
|
|
this.clearLog();
|
|
var state = this.editor.snapshotState();
|
|
return compiler.compileAsync(opts)
|
|
.then(function (resp) {
|
|
_this.editor.setDiagnostics(_this.editorFile, state);
|
|
if (resp.outfiles[pxtc.BINARY_JS]) {
|
|
simulator.run(pkg.mainPkg, opts.debug, resp, _this.state.mute);
|
|
_this.setState({ running: true, showParts: simulator.driver.runOptions.parts.length > 0 });
|
|
}
|
|
else if (!opts.background) {
|
|
core.warningNotification(lf("Oops, we could not run this project. Please check your code for errors."));
|
|
}
|
|
});
|
|
};
|
|
ProjectView.prototype.editText = function () {
|
|
if (this.editor != this.textEditor) {
|
|
this.updateEditorFile(this.textEditor);
|
|
this.forceUpdate();
|
|
}
|
|
};
|
|
ProjectView.prototype.importFileDialog = function () {
|
|
var _this = this;
|
|
var input;
|
|
core.confirmAsync({
|
|
header: lf("Open .hex file"),
|
|
onLoaded: function ($el) {
|
|
input = $el.find('input')[0];
|
|
},
|
|
htmlBody: "<div class=\"ui form\">\n <div class=\"ui field\">\n <label>" + lf("Select a .hex file to open.") + "</label>\n <input type=\"file\" class=\"ui button blue fluid\"></input>\n </div>\n</div>",
|
|
}).done(function (res) {
|
|
if (res) {
|
|
pxt.tickEvent("menu.open.file");
|
|
_this.importFile(input.files[0]);
|
|
}
|
|
});
|
|
};
|
|
ProjectView.prototype.importUrlDialog = function () {
|
|
var input;
|
|
var shareUrl = pxt.appTarget.appTheme.embedUrl || pxt.appTarget.appTheme.homeUrl;
|
|
core.confirmAsync({
|
|
header: lf("Open project URL"),
|
|
onLoaded: function ($el) {
|
|
input = $el.find('input')[0];
|
|
},
|
|
htmlBody: "<div class=\"ui form\">\n <div class=\"ui field\">\n <label>" + lf("Copy the URL of the project.") + "</label>\n <input type=\"url\" placeholder=\"" + shareUrl + "...\" class=\"ui button blue fluid\"></input>\n </div>\n</div>",
|
|
}).done(function (res) {
|
|
if (res) {
|
|
pxt.tickEvent("menu.open.url");
|
|
var id = pxt.Cloud.parseScriptId(input.value);
|
|
if (id) {
|
|
loadHeaderBySharedId(id);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
ProjectView.prototype.launchFullEditor = function () {
|
|
pxt.tickEvent("sandbox.openfulleditor");
|
|
Util.assert(pxt.shell.isSandboxMode());
|
|
var editUrl = pxt.appTarget.appTheme.embedUrl;
|
|
if (!/\/$/.test(editUrl))
|
|
editUrl += '/';
|
|
var mpkg = pkg.mainPkg;
|
|
var epkg = pkg.getEditorPkg(mpkg);
|
|
if (pxt.shell.isReadOnly()) {
|
|
if (epkg.header.pubId) { }
|
|
editUrl += "#pub:" + epkg.header.pubId;
|
|
window.open(editUrl, '_blank');
|
|
}
|
|
else
|
|
this.exportAsync()
|
|
.done(function (fileContent) {
|
|
pxt.tickEvent("sandbox.openfulleditor");
|
|
editUrl += "#project:" + fileContent;
|
|
window.open(editUrl, '_blank');
|
|
});
|
|
};
|
|
ProjectView.prototype.anonymousPublishAsync = function () {
|
|
var _this = this;
|
|
pxt.tickEvent("publish");
|
|
this.setState({ publishing: true });
|
|
var mpkg = pkg.mainPkg;
|
|
var epkg = pkg.getEditorPkg(mpkg);
|
|
return this.saveFileAsync()
|
|
.then(function () { return mpkg.filesToBePublishedAsync(true); })
|
|
.then(function (files) {
|
|
if (epkg.header.pubCurrent)
|
|
return Promise.resolve(epkg.header.pubId);
|
|
var meta = {
|
|
description: mpkg.config.description,
|
|
};
|
|
var blocksSize = _this.blocksEditor.contentSize();
|
|
if (blocksSize) {
|
|
meta.blocksHeight = blocksSize.height;
|
|
meta.blocksWidth = blocksSize.width;
|
|
}
|
|
return workspace.anonymousPublishAsync(epkg.header, files, meta)
|
|
.then(function (inf) { return inf.id; });
|
|
}).finally(function () {
|
|
_this.setState({ publishing: false });
|
|
})
|
|
.catch(function (e) {
|
|
core.errorNotification(e.message);
|
|
return undefined;
|
|
});
|
|
};
|
|
ProjectView.prototype.updateHeaderName = function (name) {
|
|
this.setState({
|
|
projectName: name
|
|
});
|
|
this.debouncedSaveProjectName();
|
|
};
|
|
ProjectView.prototype.saveProjectName = function () {
|
|
var _this = this;
|
|
if (!this.state.projectName || !this.state.header)
|
|
return;
|
|
pxt.debug('saving project name to ' + this.state.projectName);
|
|
try {
|
|
//Save the name in the target MainPackage as well
|
|
pkg.mainPkg.config.name = this.state.projectName;
|
|
var f = pkg.mainEditorPkg().lookupFile("this/" + pxt.CONFIG_NAME);
|
|
var config = JSON.parse(f.content);
|
|
config.name = this.state.projectName;
|
|
f.setContentAsync(JSON.stringify(config, null, 4) + "\n").done(function () {
|
|
if (_this.state.header)
|
|
_this.setState({
|
|
projectName: _this.state.header.name
|
|
});
|
|
});
|
|
}
|
|
catch (e) {
|
|
console.error('failed to read pxt.json');
|
|
}
|
|
};
|
|
ProjectView.prototype.isTextEditor = function () {
|
|
return this.editor == this.textEditor;
|
|
};
|
|
ProjectView.prototype.isBlocksEditor = function () {
|
|
return this.editor == this.blocksEditor;
|
|
};
|
|
ProjectView.prototype.about = function () {
|
|
pxt.tickEvent("menu.about");
|
|
var compileService = pxt.appTarget.compileService;
|
|
core.confirmAsync({
|
|
header: lf("About {0}", pxt.appTarget.name),
|
|
hideCancel: true,
|
|
agreeLbl: lf("Ok"),
|
|
htmlBody: "\n<p>" + Util.htmlEscape(pxt.appTarget.description) + "</p>\n<p>" + lf("{0} version:", Util.htmlEscape(pxt.appTarget.name)) + " <a href=\"" + Util.htmlEscape(pxt.appTarget.appTheme.githubUrl) + "/releases/tag/v" + Util.htmlEscape(pxt.appTarget.versions.target) + "\" target=\"_blank\">" + Util.htmlEscape(pxt.appTarget.versions.target) + "</a></p>\n<p>" + lf("{0} version:", "PXT") + " <a href=\"https://github.com/Microsoft/pxt/releases/tag/v" + Util.htmlEscape(pxt.appTarget.versions.pxt) + "\" target=\"_blank\">" + Util.htmlEscape(pxt.appTarget.versions.pxt) + "</a></p>\n" + (compileService ? "<p>" + lf("{0} version:", "C++ runtime") + " <a href=\"" + Util.htmlEscape("https://github.com/" + compileService.githubCorePackage + '/releases/tag/' + compileService.gittag) + "\" target=\"_blank\">" + Util.htmlEscape(compileService.gittag) + "</a></p>" : "") + "\n"
|
|
}).done();
|
|
};
|
|
ProjectView.prototype.embed = function () {
|
|
pxt.tickEvent("menu.embed");
|
|
var header = this.state.header;
|
|
this.shareEditor.show(header);
|
|
};
|
|
ProjectView.prototype.gettingStarted = function () {
|
|
pxt.tickEvent("btn.gettingstarted");
|
|
var targetTheme = pxt.appTarget.appTheme;
|
|
Util.assert(!this.state.sideDocsLoadUrl && targetTheme && !!targetTheme.sideDoc);
|
|
this.startTutorial(targetTheme.sideDoc);
|
|
};
|
|
ProjectView.prototype.startTutorial = function (tutorialId) {
|
|
pxt.tickEvent("tutorial.start");
|
|
core.showLoading(lf("starting tutorial..."));
|
|
this.startTutorialAsync(tutorialId)
|
|
.then(function () { return Promise.delay(500); });
|
|
};
|
|
ProjectView.prototype.startTutorialAsync = function (tutorialId) {
|
|
var _this = this;
|
|
var title = tutorialId;
|
|
var result = [];
|
|
return pxt.Cloud.downloadMarkdownAsync(tutorialId)
|
|
.then(function (md) {
|
|
var titleRegex = /^#(.*)/g.exec(md);
|
|
if (!titleRegex || titleRegex.length < 1)
|
|
return;
|
|
title = titleRegex[1];
|
|
var steps = md.split('###');
|
|
for (var step = 1; step < steps.length; step++) {
|
|
var stepmd = "###" + steps[step];
|
|
result.push(stepmd);
|
|
}
|
|
//TODO: parse for tutorial options, mainly initial blocks
|
|
}).then(function () {
|
|
_this.setState({ tutorial: tutorialId, tutorialName: title, tutorialStep: 0, tutorialSteps: result });
|
|
var tc = _this.refs["tutorialcard"];
|
|
tc.setPath(tutorialId);
|
|
}).then(function () {
|
|
return _this.createProjectAsync({
|
|
filesOverride: {
|
|
"main.blocks": "<xml xmlns=\"http://www.w3.org/1999/xhtml\"><block type=\"" + ts.pxtc.ON_START_TYPE + "\"></block></xml>",
|
|
"main.ts": " "
|
|
},
|
|
name: tutorialId,
|
|
temporary: true
|
|
});
|
|
});
|
|
};
|
|
ProjectView.prototype.exitTutorial = function () {
|
|
pxt.tickEvent("tutorial.exit");
|
|
core.showLoading(lf("exiting tutorial..."));
|
|
this.exitTutorialAsync()
|
|
.then(function () { return Promise.delay(500); })
|
|
.done(function () { return core.hideLoading(); });
|
|
};
|
|
ProjectView.prototype.exitTutorialAsync = function () {
|
|
var _this = this;
|
|
// tutorial project is temporary, no need to delete
|
|
var curr = pkg.mainEditorPkg().header;
|
|
curr.isDeleted = true;
|
|
this.setState({ active: false });
|
|
return workspace.saveAsync(curr, {})
|
|
.then(function () {
|
|
if (workspace.getHeaders().length > 0) {
|
|
_this.loadHeaderAsync(workspace.getHeaders()[0]);
|
|
}
|
|
else {
|
|
_this.newProject();
|
|
}
|
|
}).finally(function () {
|
|
_this.setState({ active: true, tutorial: null, tutorialName: null, tutorialSteps: null, tutorialStep: -1 });
|
|
});
|
|
};
|
|
ProjectView.prototype.renderCore = function () {
|
|
var _this = this;
|
|
theEditor = this;
|
|
if (this.editor && this.editor.isReady) {
|
|
this.updateEditorFile();
|
|
}
|
|
// ${targetTheme.accentColor ? "inverted accent " : ''}
|
|
var settings = (Cloud.isLoggedIn() ? this.getData("cloud:me/settings?format=nonsensitive") : {}) || {};
|
|
var targetTheme = pxt.appTarget.appTheme;
|
|
var workspaces = pxt.appTarget.cloud && pxt.appTarget.cloud.workspaces;
|
|
var packages = pxt.appTarget.cloud && pxt.appTarget.cloud.packages;
|
|
var sharingEnabled = pxt.appTarget.cloud && pxt.appTarget.cloud.sharing;
|
|
var compile = pxt.appTarget.compile;
|
|
var compileBtn = compile.hasHex;
|
|
var simOpts = pxt.appTarget.simulator;
|
|
var sandbox = pxt.shell.isSandboxMode();
|
|
var make = !sandbox && this.state.showParts && simOpts && (simOpts.instructions || (simOpts.parts && pxt.options.debug));
|
|
var rightLogo = sandbox ? targetTheme.portraitLogo : targetTheme.rightLogo;
|
|
var compileTooltip = lf("Download your code to the {0}", targetTheme.boardName);
|
|
var compileLoading = !!this.state.compiling;
|
|
var runTooltip = this.state.running ? lf("Stop the simulator") : lf("Start the simulator");
|
|
var makeTooltip = lf("Open assembly instructions");
|
|
var restartTooltip = lf("Restart the simulator");
|
|
var fullscreenTooltip = this.state.fullscreen ? lf("Exit fullscreen mode") : lf("Launch in fullscreen");
|
|
var muteTooltip = this.state.mute ? lf("Unmute audio") : lf("Mute audio");
|
|
var isBlocks = !this.editor.isVisible || this.getPreferredEditor() == pxt.BLOCKS_PROJECT_NAME;
|
|
var sideDocs = !(sandbox || pxt.options.light || targetTheme.hideSideDocs);
|
|
var inTutorial = !!this.state.tutorial;
|
|
var tutorialName = this.state.tutorialName;
|
|
var docMenu = targetTheme.docMenu && targetTheme.docMenu.length && !sandbox && !inTutorial;
|
|
var gettingStarted = !sandbox && !inTutorial && !this.state.sideDocsLoadUrl && targetTheme && targetTheme.sideDoc && isBlocks;
|
|
var gettingStartedTooltip = lf("Open beginner tutorial");
|
|
var run = true; // !compileBtn || !pxt.appTarget.simulator.autoRun || !isBlocks;
|
|
var restart = run && !simOpts.hideRestart;
|
|
var fullscreen = run && !simOpts.hideFullscreen;
|
|
var showMenuBar = !targetTheme.layoutOptions || !targetTheme.layoutOptions.hideMenuBar;
|
|
var cookieKey = "cookieconsent";
|
|
var cookieConsented = !!pxt.storage.getLocal(cookieKey) || electron.isElectron;
|
|
var blockActive = this.isBlocksActive();
|
|
var javascriptActive = this.isJavaScriptActive();
|
|
var consentCookie = function () {
|
|
pxt.storage.setLocal(cookieKey, "1");
|
|
_this.forceUpdate();
|
|
};
|
|
// update window title
|
|
document.title = this.state.header ? this.state.header.name + " - " + pxt.appTarget.name : pxt.appTarget.name;
|
|
var rootClasses = sui.cx([
|
|
this.state.hideEditorFloats || this.state.collapseEditorTools ? " hideEditorFloats" : '',
|
|
this.state.collapseEditorTools ? " collapsedEditorTools" : '',
|
|
this.state.fullscreen ? 'fullscreensim' : '',
|
|
!sideDocs || !this.state.sideDocsLoadUrl || this.state.sideDocsCollapsed ? '' : 'sideDocs',
|
|
pxt.shell.layoutTypeClass(),
|
|
inTutorial ? 'tutorial' : '',
|
|
pxt.options.light ? 'light' : '',
|
|
pxt.BrowserUtils.isTouchEnabled() ? 'has-touch' : '',
|
|
showMenuBar ? '' : 'hideMenuBar',
|
|
'full-abs'
|
|
]);
|
|
return (React.createElement("div", {id: 'root', className: rootClasses}, showMenuBar ?
|
|
React.createElement("div", {id: "menubar", role: "banner"}, React.createElement("div", {className: "ui borderless fixed " + (targetTheme.invertedMenu ? "inverted" : '') + " menu", role: "menubar"}, !sandbox ? React.createElement("div", {className: "left menu"}, React.createElement("span", {id: "logo", className: "ui item logo"}, targetTheme.logo || targetTheme.portraitLogo
|
|
? React.createElement("a", {className: "ui image", target: "_blank", href: targetTheme.logoUrl}, React.createElement("img", {className: "ui logo " + (targetTheme.portraitLogo ? " portrait hide" : ''), src: Util.toDataUri(targetTheme.logo || targetTheme.portraitLogo)}))
|
|
: React.createElement("span", {className: "name"}, targetTheme.name), targetTheme.portraitLogo ? (React.createElement("a", {className: "ui", target: "_blank", href: targetTheme.logoUrl}, React.createElement("img", {className: 'ui mini image portrait only', src: Util.toDataUri(targetTheme.portraitLogo)}))) : null), !inTutorial ? React.createElement(sui.Item, {class: "openproject", role: "menuitem", textClass: "landscape only", icon: "folder open large", text: lf("Projects"), onClick: function () { return _this.openProject(); }}) : null, !inTutorial && this.state.header && sharingEnabled ? React.createElement(sui.Item, {class: "shareproject", role: "menuitem", textClass: "widedesktop only", text: lf("Share"), icon: "share alternate large", onClick: function () { return _this.embed(); }}) : null, inTutorial ? React.createElement(sui.Item, {class: "tutorialname", role: "menuitem", textClass: "landscape only", text: tutorialName}) : null) : undefined, !inTutorial && !targetTheme.blocksOnly ? React.createElement(sui.Item, {class: "editor-menuitem"}, React.createElement(sui.Item, {class: "blocks-menuitem", textClass: "landscape only", text: lf("Blocks"), icon: "puzzle", active: blockActive, onClick: function () { return _this.openBlocks(); }, title: lf("Convert code to Blocks")}), React.createElement(sui.Item, {class: "javascript-menuitem", textClass: "landscape only", text: lf("JavaScript"), icon: "align left", active: javascriptActive, onClick: function () { return _this.openJavaScript(); }, title: lf("Convert code to JavaScript")})) : undefined, inTutorial ? React.createElement(tutorial.TutorialMenuItem, {parent: this}) : undefined, React.createElement("div", {className: "right menu"}, docMenu ? React.createElement(container.DocsMenuItem, {parent: this}) : undefined, sandbox || inTutorial ? undefined :
|
|
React.createElement(sui.DropdownMenuItem, {icon: 'setting large', title: lf("More..."), class: "more-dropdown-menuitem"}, this.state.header ? React.createElement(sui.Item, {role: "menuitem", icon: "options", text: lf("Project Settings"), onClick: function () { return _this.setFile(pkg.mainEditorPkg().lookupFile("this/pxt.json")); }}) : undefined, this.state.header && packages ? React.createElement(sui.Item, {role: "menuitem", icon: "disk outline", text: lf("Add Package..."), onClick: function () { return _this.addPackage(); }}) : undefined, this.state.header ? React.createElement(sui.Item, {role: "menuitem", icon: "trash", text: lf("Delete Project"), onClick: function () { return _this.removeProject(); }}) : undefined, React.createElement("div", {className: "ui divider"}), React.createElement("a", {className: "ui item thin only", href: "/docs", role: "menuitem", target: "_blank"}, React.createElement("i", {className: "help icon"}), lf("Help")), React.createElement(sui.Item, {role: "menuitem", icon: 'sign out', text: lf("Reset"), onClick: function () { return _this.reset(); }}), React.createElement("div", {className: "ui divider"}), targetTheme.privacyUrl ? React.createElement("a", {className: "ui item", href: targetTheme.privacyUrl, role: "menuitem", title: lf("Privacy & Cookies"), target: "_blank"}, lf("Privacy & Cookies")) : undefined, targetTheme.termsOfUseUrl ? React.createElement("a", {className: "ui item", href: targetTheme.termsOfUseUrl, role: "menuitem", title: lf("Terms Of Use"), target: "_blank"}, lf("Terms Of Use")) : undefined, React.createElement(sui.Item, {role: "menuitem", text: lf("About..."), onClick: function () { return _this.about(); }}), electron.isElectron ? React.createElement(sui.Item, {role: "menuitem", text: lf("Check for updates..."), onClick: function () { return electron.checkForUpdate(); }}) : undefined, targetTheme.feedbackUrl ? React.createElement("div", {className: "ui divider"}) : undefined, targetTheme.feedbackUrl ? React.createElement("a", {className: "ui item", href: targetTheme.feedbackUrl, role: "menuitem", title: lf("Give Feedback"), target: "_blank"}, lf("Give Feedback")) : undefined), sandbox ? React.createElement(sui.Item, {role: "menuitem", icon: "external", text: lf("Edit"), onClick: function () { return _this.launchFullEditor(); }}) : undefined, sandbox ? React.createElement("span", {className: "ui item logo"}, React.createElement("img", {className: "ui mini image", src: Util.toDataUri(rightLogo)})) : undefined, !sandbox && gettingStarted ? React.createElement("span", {className: "ui item tablet only"}, React.createElement(sui.Button, {class: "small getting-started-btn", title: gettingStartedTooltip, text: lf("Getting Started"), onClick: function () { return _this.gettingStarted(); }})) : undefined, inTutorial ? React.createElement(sui.Item, {role: "menuitem", icon: "external", text: lf("Exit tutorial"), textClass: "landscape only", onClick: function () { return _this.exitTutorial(); }}) : undefined, !sandbox ? React.createElement("a", {id: "organization", href: targetTheme.organizationUrl, target: "blank", className: "ui item logo", onClick: function () { return pxt.tickEvent("menu.org"); }}, targetTheme.organizationWideLogo || targetTheme.organizationLogo
|
|
? React.createElement("img", {className: "ui logo " + (targetTheme.portraitLogo ? " portrait hide" : ''), src: Util.toDataUri(targetTheme.organizationWideLogo || targetTheme.organizationLogo)})
|
|
: React.createElement("span", {className: "name"}, targetTheme.organization), targetTheme.organizationLogo ? (React.createElement("img", {className: 'ui mini image portrait only', src: Util.toDataUri(targetTheme.organizationLogo)})) : null) : undefined))) : undefined, gettingStarted ?
|
|
React.createElement("div", {id: "getting-started-btn"}, React.createElement(sui.Button, {class: "portrait hide bottom attached small getting-started-btn", title: gettingStartedTooltip, text: lf("Getting Started"), onClick: function () { return _this.gettingStarted(); }}))
|
|
: undefined, React.createElement("div", {id: "simulator"}, React.createElement("div", {id: "filelist", className: "ui items", role: "complementary"}, React.createElement("div", {id: "boardview", className: "ui vertical editorFloat"}), React.createElement("div", {className: "ui item grid centered portrait hide simtoolbar"}, React.createElement("div", {className: "ui icon buttons " + (this.state.fullscreen ? 'massive' : ''), style: { padding: "0" }}, make ? React.createElement(sui.Button, {icon: 'configure', class: "fluid sixty secondary", text: lf("Make"), title: makeTooltip, onClick: function () { return _this.openInstructions(); }}) : undefined, run ? React.createElement(sui.Button, {key: 'runbtn', class: "play-button", icon: this.state.running ? "stop" : "play", title: runTooltip, onClick: function () { return _this.startStopSimulator(); }}) : undefined, restart ? React.createElement(sui.Button, {key: 'restartbtn', class: "restart-button", icon: "refresh", title: restartTooltip, onClick: function () { return _this.restartSimulator(); }}) : undefined), React.createElement("div", {className: "ui icon buttons " + (this.state.fullscreen ? 'massive' : ''), style: { padding: "0" }}, run && targetTheme.hasAudio ? React.createElement(sui.Button, {key: 'mutebtn', class: "mute-button", icon: "" + (this.state.mute ? 'volume off' : 'volume up'), title: muteTooltip, onClick: function () { return _this.toggleMute(); }}) : undefined, fullscreen ? React.createElement(sui.Button, {key: 'fullscreenbtn', class: "fullscreen-button", icon: "" + (this.state.fullscreen ? 'compress' : 'maximize'), title: fullscreenTooltip, onClick: function () { return _this.toggleSimulatorFullscreen(); }}) : undefined)), React.createElement("div", {className: "ui item portrait hide"}, pxt.options.debug && !this.state.running ? React.createElement(sui.Button, {key: 'debugbtn', class: 'teal', icon: "xicon bug", text: "Sim Debug", onClick: function () { return _this.runSimulator({ debug: true }); }}) : '', pxt.options.debug ? React.createElement(sui.Button, {key: 'hwdebugbtn', class: 'teal', icon: "xicon chip", text: "Dev Debug", onClick: function () { return _this.hwDebug(); }}) : ''), React.createElement("div", {className: "ui editorFloat portrait hide"}, React.createElement(logview.LogView, {ref: "logs"})), sandbox || isBlocks ? undefined : React.createElement(filelist.FileList, {parent: this}))), React.createElement("div", {id: "maineditor", className: sandbox ? "sandbox" : "", role: "main"}, inTutorial ? React.createElement(tutorial.TutorialCard, {ref: "tutorialcard", parent: this}) : undefined, this.allEditors.map(function (e) { return e.displayOuter(); })), React.createElement("div", {id: "editortools", role: "complementary"}, React.createElement(editortoolbar.EditorToolbar, {ref: "editortools", parent: this})), sideDocs ? React.createElement(container.SideDocs, {ref: "sidedoc", parent: this}) : undefined, sandbox ? undefined : React.createElement(scriptsearch.ScriptSearch, {parent: this, ref: function (v) { return _this.scriptSearch = v; }}), sandbox ? undefined : React.createElement(projects.Projects, {parent: this, ref: function (v) { return _this.projects = v; }}), sandbox || !sharingEnabled ? undefined : React.createElement(share.ShareEditor, {parent: this, ref: function (v) { return _this.shareEditor = v; }}), sandbox ? React.createElement("div", {className: "ui horizontal small divided link list sandboxfooter"}, targetTheme.organizationUrl && targetTheme.organization ? React.createElement("a", {className: "item", target: "_blank", href: targetTheme.organizationUrl}, lf("Powered by {0}", targetTheme.organization)) : undefined, React.createElement("a", {target: "_blank", className: "item", href: targetTheme.termsOfUseUrl}, lf("Terms of Use")), React.createElement("a", {target: "_blank", className: "item", href: targetTheme.privacyUrl}, lf("Privacy"))) : undefined, cookieConsented ? undefined : React.createElement("div", {id: 'cookiemsg', className: "ui teal inverted black segment"}, React.createElement("button", {"arial-label": lf("Ok"), className: "ui right floated icon button", onClick: consentCookie}, React.createElement("i", {className: "remove icon"})), lf("By using this site you agree to the use of cookies for analytics."), React.createElement("a", {target: "_blank", className: "ui link", href: pxt.appTarget.appTheme.privacyUrl}, lf("Learn more")))));
|
|
};
|
|
return ProjectView;
|
|
}(data.Component));
|
|
exports.ProjectView = ProjectView;
|
|
function render() {
|
|
ReactDOM.render(React.createElement(ProjectView, null), $('#content')[0]);
|
|
}
|
|
function getEditor() {
|
|
return theEditor;
|
|
}
|
|
function isHexFile(filename) {
|
|
return /\.(hex|uf2)$/i.test(filename);
|
|
}
|
|
function isBlocksFile(filename) {
|
|
return /\.blocks$/i.test(filename);
|
|
}
|
|
function isTypescriptFile(filename) {
|
|
return /\.ts$/i.test(filename);
|
|
}
|
|
function isProjectFile(filename) {
|
|
return /\.pxt$/i.test(filename);
|
|
}
|
|
function fileReadAsBufferAsync(f) {
|
|
if (!f)
|
|
return Promise.resolve(null);
|
|
else {
|
|
return new Promise(function (resolve, reject) {
|
|
var reader = new FileReader();
|
|
reader.onerror = function (ev) { return resolve(null); };
|
|
reader.onload = function (ev) { return resolve(new Uint8Array(reader.result)); };
|
|
reader.readAsArrayBuffer(f);
|
|
});
|
|
}
|
|
}
|
|
function fileReadAsTextAsync(f) {
|
|
if (!f)
|
|
return Promise.resolve(null);
|
|
else {
|
|
return new Promise(function (resolve, reject) {
|
|
var reader = new FileReader();
|
|
reader.onerror = function (ev) { return resolve(null); };
|
|
reader.onload = function (ev) { return resolve(reader.result); };
|
|
reader.readAsText(f);
|
|
});
|
|
}
|
|
}
|
|
function initLogin() {
|
|
{
|
|
var qs = core.parseQueryString((location.hash || "#").slice(1).replace(/%23access_token/, "access_token"));
|
|
if (qs["access_token"]) {
|
|
var ex = pxt.storage.getLocal("oauthState");
|
|
if (ex && ex == qs["state"]) {
|
|
pxt.storage.setLocal("access_token", qs["access_token"]);
|
|
pxt.storage.removeLocal("oauthState");
|
|
}
|
|
location.hash = location.hash.replace(/(%23)?[\#\&\?]*access_token.*/, "");
|
|
}
|
|
Cloud.accessToken = pxt.storage.getLocal("access_token") || "";
|
|
}
|
|
{
|
|
var qs = core.parseQueryString((location.hash || "#").slice(1).replace(/%local_token/, "local_token"));
|
|
if (qs["local_token"]) {
|
|
pxt.storage.setLocal("local_token", qs["local_token"]);
|
|
location.hash = location.hash.replace(/(%23)?[\#\&\?]*local_token.*/, "");
|
|
}
|
|
Cloud.localToken = pxt.storage.getLocal("local_token") || "";
|
|
}
|
|
}
|
|
function initSerial() {
|
|
if (!pxt.appTarget.serial || !Cloud.isLocalHost() || !Cloud.localToken)
|
|
return;
|
|
if (hidbridge.shouldUse()) {
|
|
hidbridge.initAsync()
|
|
.then(function (dev) {
|
|
dev.onSerial = function (buf, isErr) {
|
|
window.postMessage({
|
|
type: 'serial',
|
|
id: 'n/a',
|
|
data: Util.fromUTF8(Util.uint8ArrayToString(buf))
|
|
}, "*");
|
|
};
|
|
})
|
|
.catch(function (e) {
|
|
pxt.log("hidbridge failed to load, " + e);
|
|
});
|
|
return;
|
|
}
|
|
pxt.debug('initializing serial pipe');
|
|
var ws = new WebSocket("ws://localhost:" + pxt.options.wsPort + "/" + Cloud.localToken + "/serial");
|
|
ws.onopen = function (ev) {
|
|
pxt.debug('serial: socket opened');
|
|
};
|
|
ws.onclose = function (ev) {
|
|
pxt.debug('serial: socket closed');
|
|
};
|
|
ws.onmessage = function (ev) {
|
|
try {
|
|
var msg = JSON.parse(ev.data);
|
|
if (msg && msg.type == 'serial')
|
|
window.postMessage(msg, "*");
|
|
}
|
|
catch (e) {
|
|
pxt.debug('unknown message: ' + ev.data);
|
|
}
|
|
};
|
|
}
|
|
function getsrc() {
|
|
pxt.log(theEditor.editor.getCurrentSource());
|
|
}
|
|
function initScreenshots() {
|
|
window.addEventListener('message', function (ev) {
|
|
var msg = ev.data;
|
|
if (msg && msg.type == "screenshot") {
|
|
pxt.tickEvent("sim.screenshot");
|
|
var scmsg = msg;
|
|
console.log('received screenshot');
|
|
screenshot.saveAsync(theEditor.state.header, scmsg.data)
|
|
.done(function () { pxt.debug('screenshot saved'); });
|
|
}
|
|
;
|
|
}, false);
|
|
}
|
|
function enableAnalytics() {
|
|
pxt.analytics.enable();
|
|
var stats = {};
|
|
if (typeof window !== "undefined") {
|
|
var screen_1 = window.screen;
|
|
stats["screen.width"] = screen_1.width;
|
|
stats["screen.height"] = screen_1.height;
|
|
stats["screen.availwidth"] = screen_1.availWidth;
|
|
stats["screen.availheight"] = screen_1.availHeight;
|
|
stats["screen.devicepixelratio"] = pxt.BrowserUtils.devicePixelRatio();
|
|
}
|
|
pxt.tickEvent("editor.loaded", stats);
|
|
}
|
|
function showIcons() {
|
|
var usedIcons = [
|
|
"cancel", "certificate", "checkmark", "cloud", "cloud upload", "copy", "disk outline", "download",
|
|
"dropdown", "edit", "file outline", "find", "folder", "folder open", "help circle",
|
|
"keyboard", "lock", "play", "puzzle", "search", "setting", "settings",
|
|
"share alternate", "sign in", "sign out", "square", "stop", "translate", "trash", "undo", "upload",
|
|
"user", "wizard", "configure", "align left"
|
|
];
|
|
core.confirmAsync({
|
|
header: "Icons",
|
|
htmlBody: usedIcons.map(function (s) { return ("<i style='font-size:2em' class=\"ui icon " + s + "\"></i> " + s + " "); }).join("\n")
|
|
});
|
|
}
|
|
function assembleCurrent() {
|
|
compiler.compileAsync({ native: true })
|
|
.then(function () { return compiler.assembleAsync(getEditor().editorFile.content); })
|
|
.then(function (v) {
|
|
var nums = v.words;
|
|
pxt.debug("[" + nums.map(function (n) { return "0x" + n.toString(16); }).join(",") + "]");
|
|
});
|
|
}
|
|
function log(v) {
|
|
console.log(v);
|
|
}
|
|
// This is for usage from JS console
|
|
var myexports = {
|
|
workspace: workspace,
|
|
require: require,
|
|
core: core,
|
|
getEditor: getEditor,
|
|
monaco: monaco,
|
|
blocks: blocks,
|
|
compiler: compiler,
|
|
pkg: pkg,
|
|
getsrc: getsrc,
|
|
sim: simulator,
|
|
apiAsync: core.apiAsync,
|
|
showIcons: showIcons,
|
|
hwdbg: hwdbg,
|
|
assembleCurrent: assembleCurrent,
|
|
log: log
|
|
};
|
|
window.E = myexports;
|
|
function initTheme() {
|
|
var theme = pxt.appTarget.appTheme;
|
|
if (theme.accentColor) {
|
|
var style = document.createElement('style');
|
|
style.type = 'text/css';
|
|
style.innerHTML = ".ui.accent { color: " + theme.accentColor + "; }\n .ui.inverted.menu .accent.active.item, .ui.inverted.accent.menu { background-color: " + theme.accentColor + "; }";
|
|
document.getElementsByTagName('head')[0].appendChild(style);
|
|
}
|
|
// RTL languages
|
|
if (/^ar/i.test(Util.userLanguage())) {
|
|
pxt.debug("rtl layout");
|
|
pxsim.U.addClass(document.body, "rtl");
|
|
document.body.style.direction = "rtl";
|
|
}
|
|
function patchCdn(url) {
|
|
if (!url)
|
|
return url;
|
|
return url.replace("@pxtCdnUrl@", pxt.getOnlineCdnUrl())
|
|
.replace("@cdnUrl@", pxt.getOnlineCdnUrl());
|
|
}
|
|
theme.appLogo = patchCdn(theme.appLogo);
|
|
theme.cardLogo = patchCdn(theme.cardLogo);
|
|
if (pxt.appTarget.simulator
|
|
&& pxt.appTarget.simulator.boardDefinition
|
|
&& pxt.appTarget.simulator.boardDefinition.visual) {
|
|
var boardDef = pxt.appTarget.simulator.boardDefinition.visual;
|
|
if (boardDef.image) {
|
|
boardDef.image = patchCdn(boardDef.image);
|
|
if (boardDef.outlineImage)
|
|
boardDef.outlineImage = patchCdn(boardDef.outlineImage);
|
|
}
|
|
}
|
|
}
|
|
function parseHash() {
|
|
var hashCmd = "";
|
|
var hashArg = "";
|
|
var hashM = /^#(\w+)(:([\/\-\+\=\w]+))?$/.exec(window.location.hash);
|
|
if (hashM) {
|
|
return { cmd: hashM[1], arg: hashM[3] || '' };
|
|
}
|
|
return { cmd: '', arg: '' };
|
|
}
|
|
function handleHash(hash) {
|
|
if (!hash)
|
|
return false;
|
|
var editor = theEditor;
|
|
if (!editor)
|
|
return false;
|
|
switch (hash.cmd) {
|
|
case "doc":
|
|
pxt.tickEvent("hash.doc");
|
|
editor.setSideDoc(hash.arg);
|
|
break;
|
|
case "follow":
|
|
pxt.tickEvent("hash.follow");
|
|
editor.newEmptyProject(undefined, hash.arg);
|
|
return true;
|
|
case "newproject":
|
|
pxt.tickEvent("hash.newproject");
|
|
editor.newProject();
|
|
window.location.hash = "";
|
|
return true;
|
|
case "newjavascript":
|
|
pxt.tickEvent("hash.newjavascript");
|
|
editor.newProject({
|
|
prj: pxt.appTarget.blocksprj,
|
|
filesOverride: {
|
|
"main.blocks": ""
|
|
}
|
|
});
|
|
window.location.hash = "";
|
|
return true;
|
|
case "gettingstarted":
|
|
pxt.tickEvent("hash.gettingstarted");
|
|
editor.newProject();
|
|
window.location.hash = "";
|
|
return true;
|
|
case "tutorial":
|
|
pxt.tickEvent("hash.tutorial");
|
|
editor.startTutorial(hash.arg);
|
|
return true;
|
|
case "sandbox":
|
|
case "pub":
|
|
case "edit":
|
|
pxt.tickEvent("hash." + hash.cmd);
|
|
window.location.hash = "";
|
|
loadHeaderBySharedId(hash.arg);
|
|
return true;
|
|
case "sandboxproject":
|
|
case "project":
|
|
pxt.tickEvent("hash." + hash.cmd);
|
|
var fileContents = Util.stringToUint8Array(atob(hash.arg));
|
|
window.location.hash = "";
|
|
core.showLoading(lf("loading project..."));
|
|
theEditor.importProjectFromFileAsync(fileContents)
|
|
.done(function () { return core.hideLoading(); });
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
function loadHeaderBySharedId(id) {
|
|
var existing = workspace.getHeaders()
|
|
.filter(function (h) { return h.pubCurrent && h.pubId == id; })[0];
|
|
core.showLoading(lf("loading project..."));
|
|
(existing
|
|
? theEditor.loadHeaderAsync(existing)
|
|
: workspace.installByIdAsync(id)
|
|
.then(function (hd) { return theEditor.loadHeaderAsync(hd); }))
|
|
.finally(function () { return core.hideLoading(); });
|
|
}
|
|
function initHashchange() {
|
|
window.addEventListener("hashchange", function (e) {
|
|
handleHash(parseHash());
|
|
});
|
|
}
|
|
$(document).ready(function () {
|
|
pxt.setupWebConfig(window.pxtConfig);
|
|
var config = pxt.webConfig;
|
|
pxt.options.debug = /dbg=1/i.test(window.location.href);
|
|
pxt.options.light = /light=1/i.test(window.location.href) || pxt.BrowserUtils.isARM() || pxt.BrowserUtils.isIE();
|
|
var wsPortMatch = /ws=(\d+)/i.exec(window.location.href);
|
|
if (wsPortMatch) {
|
|
pxt.options.wsPort = parseInt(wsPortMatch[1]) || 3233;
|
|
window.location.hash = window.location.hash.replace(wsPortMatch[0], "");
|
|
}
|
|
else {
|
|
pxt.options.wsPort = 3233;
|
|
}
|
|
enableAnalytics();
|
|
appcache.init();
|
|
initLogin();
|
|
var hash = parseHash();
|
|
var hm = /^(https:\/\/[^/]+)/.exec(window.location.href);
|
|
if (hm)
|
|
Cloud.apiRoot = hm[1] + "/api/";
|
|
var ws = /ws=(\w+)/.exec(window.location.href);
|
|
if (ws)
|
|
workspace.setupWorkspace(ws[1]);
|
|
else if (pxt.shell.isSandboxMode() || pxt.shell.isReadOnly())
|
|
workspace.setupWorkspace("mem");
|
|
else if (Cloud.isLocalHost())
|
|
workspace.setupWorkspace("fs");
|
|
pxt.docs.requireMarked = function () { return require("marked"); };
|
|
var ih = function (hex) { return theEditor.importHex(hex); };
|
|
var cfg = pxt.webConfig;
|
|
pkg.setupAppTarget(window.pxtTargetBundle);
|
|
if (!pxt.BrowserUtils.isBrowserSupported()) {
|
|
pxt.tickEvent("unsupported");
|
|
var redirect = pxt.BrowserUtils.suggestedBrowserPath();
|
|
if (redirect) {
|
|
window.location.href = redirect;
|
|
}
|
|
}
|
|
Promise.resolve()
|
|
.then(function () {
|
|
var mlang = /(live)?lang=([a-z]{2,}(-[A-Z]+)?)/i.exec(window.location.href);
|
|
var lang = mlang ? mlang[2] : (pxt.appTarget.appTheme.defaultLocale || navigator.userLanguage || navigator.language);
|
|
var live = mlang && !!mlang[1];
|
|
if (lang)
|
|
pxt.tickEvent("locale." + lang + (live ? ".live" : ""));
|
|
return Util.updateLocalizationAsync(cfg.pxtCdnUrl, lang, live);
|
|
})
|
|
.then(function () { return initTheme(); })
|
|
.then(function () { return cmds.initCommandsAsync(); })
|
|
.then(function () { return compiler.init(); })
|
|
.then(function () { return workspace.initAsync(); })
|
|
.then(function () {
|
|
$("#loading").remove();
|
|
render();
|
|
return workspace.syncAsync();
|
|
})
|
|
.then(function () {
|
|
initSerial();
|
|
initScreenshots();
|
|
initHashchange();
|
|
}).then(function () { return pxt.winrt.initAsync(ih); })
|
|
.then(function () {
|
|
electron.init();
|
|
if (hash.cmd && handleHash(hash))
|
|
return Promise.resolve();
|
|
// default handlers
|
|
var ent = theEditor.settings.fileHistory.filter(function (e) { return !!workspace.getHeader(e.id); })[0];
|
|
var hd = workspace.getHeaders()[0];
|
|
if (ent)
|
|
hd = workspace.getHeader(ent.id);
|
|
if (hd)
|
|
return theEditor.loadHeaderAsync(hd);
|
|
else
|
|
theEditor.newProject();
|
|
return Promise.resolve();
|
|
}).done(function () { });
|
|
document.addEventListener("visibilitychange", function (ev) {
|
|
if (theEditor)
|
|
theEditor.updateVisibility();
|
|
});
|
|
window.addEventListener("unload", function (ev) {
|
|
if (theEditor)
|
|
theEditor.saveSettings();
|
|
});
|
|
window.addEventListener("resize", function (ev) {
|
|
if (theEditor && theEditor.editor)
|
|
theEditor.editor.resize(ev);
|
|
}, false);
|
|
var ipcRenderer = window.ipcRenderer;
|
|
if (ipcRenderer)
|
|
ipcRenderer.on('responseFromApp', function (event, message) {
|
|
// IPC renderer sends a string, we need to convert to an object to send to the simulator iframe
|
|
try {
|
|
simulator.driver.postMessage(JSON.parse(message));
|
|
}
|
|
catch (e) {
|
|
}
|
|
});
|
|
window.addEventListener("message", function (ev) {
|
|
var m = ev.data;
|
|
if (!m) {
|
|
return;
|
|
}
|
|
if (ev.data.__proxy == "parent") {
|
|
pxt.debug("received parent proxy message" + ev.data);
|
|
delete ev.data.__proxy;
|
|
var ipcRenderer_1 = window.ipcRenderer;
|
|
if (ipcRenderer_1)
|
|
ipcRenderer_1.sendToHost("sendToApp", ev.data);
|
|
else if (window.parent && window != window.parent)
|
|
window.parent.postMessage(ev.data, "*");
|
|
}
|
|
if (m.type == "tutorial") {
|
|
if (theEditor && theEditor.editor)
|
|
theEditor.handleMessage(m);
|
|
}
|
|
if (m.type === "sidedocready" && Cloud.isLocalHost() && Cloud.localToken) {
|
|
container.SideDocs.notify({
|
|
type: "localtoken",
|
|
localToken: Cloud.localToken
|
|
});
|
|
tutorial.TutorialContent.notify({
|
|
type: "localtoken",
|
|
localToken: Cloud.localToken
|
|
});
|
|
}
|
|
}, false);
|
|
});
|
|
|
|
},{"./appcache":2,"./blocks":3,"./cmds":6,"./compiler":8,"./container":9,"./core":10,"./data":11,"./draganddrop":13,"./editortoolbar":14,"./electron":15,"./filelist":16,"./hidbridge":19,"./hwdbg":20,"./logview":21,"./monaco":23,"./package":24,"./projects":25,"./pxtjson":26,"./screenshot":27,"./scriptsearch":28,"./share":29,"./simulator":30,"./sui":32,"./tdlegacy":33,"./tutorial":35,"./workspace":37,"marked":108,"react":264,"react-dom":135}],2:[function(require,module,exports){
|
|
"use strict";
|
|
var core = require("./core");
|
|
function init() {
|
|
var appCache = window.applicationCache;
|
|
appCache.addEventListener('updateready', function () {
|
|
core.infoNotification(lf("Update download complete. Reloading..."));
|
|
setTimeout(function () { return location.reload(); }, 3000);
|
|
}, false);
|
|
}
|
|
exports.init = init;
|
|
|
|
},{"./core":10}],3:[function(require,module,exports){
|
|
/// <reference path="../../localtypings/blockly.d.ts" />
|
|
/// <reference path="../../typings/globals/jquery/index.d.ts" />
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var core = require("./core");
|
|
var srceditor = require("./srceditor");
|
|
var compiler = require("./compiler");
|
|
var toolbox_1 = require("./toolbox");
|
|
var Util = pxt.Util;
|
|
var lf = Util.lf;
|
|
var Editor = (function (_super) {
|
|
__extends(Editor, _super);
|
|
function Editor() {
|
|
_super.apply(this, arguments);
|
|
this.isFirstBlocklyLoad = true;
|
|
this.showToolboxCategories = true;
|
|
}
|
|
Editor.prototype.setVisible = function (v) {
|
|
_super.prototype.setVisible.call(this, v);
|
|
this.isVisible = v;
|
|
var classes = '.blocklyToolboxDiv, .blocklyWidgetDiv, .blocklyToolboxDiv';
|
|
if (this.isVisible) {
|
|
$(classes).show();
|
|
// Fire a resize event since the toolbox may have changed width and height.
|
|
this.parent.fireResize();
|
|
}
|
|
else
|
|
$(classes).hide();
|
|
};
|
|
Editor.prototype.saveToTypeScript = function () {
|
|
if (!this.typeScriptSaveable)
|
|
return undefined;
|
|
try {
|
|
this.compilationResult = pxt.blocks.compile(this.editor, this.blockInfo);
|
|
return this.compilationResult.source;
|
|
}
|
|
catch (e) {
|
|
pxt.reportException(e);
|
|
core.errorNotification(lf("Sorry, we were not able to convert this program."));
|
|
return undefined;
|
|
}
|
|
};
|
|
Editor.prototype.domUpdate = function () {
|
|
var _this = this;
|
|
if (this.delayLoadXml) {
|
|
if (this.loadingXml)
|
|
return;
|
|
this.loadingXml = true;
|
|
var loading_1 = document.createElement("div");
|
|
loading_1.className = "ui inverted loading";
|
|
var editorArea = document.getElementById('blocksArea');
|
|
var editorDiv_1 = document.getElementById("blocksEditor");
|
|
editorDiv_1.appendChild(loading_1);
|
|
this.loadingXmlPromise = compiler.getBlocksAsync()
|
|
.finally(function () { _this.loadingXml = false; })
|
|
.then(function (bi) {
|
|
_this.blockInfo = bi;
|
|
var showCategories = _this.showToolboxCategories;
|
|
var showSearch = true;
|
|
var toolbox = _this.getDefaultToolbox(showCategories);
|
|
var tb = pxt.blocks.initBlocks(_this.blockInfo, toolbox, showCategories, _this.blockSubset);
|
|
_this.updateToolbox(tb, showCategories);
|
|
if (showCategories && showSearch) {
|
|
pxt.blocks.initSearch(_this.editor, tb, function (searchFor) { return compiler.apiSearchAsync(searchFor)
|
|
.then(function (fns) { return fns; }); }, function (searchTb) { return _this.updateToolbox(searchTb, showCategories); });
|
|
}
|
|
var xml = _this.delayLoadXml;
|
|
_this.delayLoadXml = undefined;
|
|
_this.loadBlockly(xml);
|
|
_this.resize();
|
|
Blockly.svgResize(_this.editor);
|
|
_this.isFirstBlocklyLoad = false;
|
|
}).finally(function () {
|
|
editorDiv_1.removeChild(loading_1);
|
|
});
|
|
if (this.isFirstBlocklyLoad) {
|
|
core.showLoadingAsync(lf("loading..."), this.loadingXmlPromise).done();
|
|
}
|
|
else {
|
|
this.loadingXmlPromise.done();
|
|
}
|
|
this.loadingXmlPromise = null;
|
|
}
|
|
};
|
|
Editor.prototype.saveBlockly = function () {
|
|
// make sure we don't return an empty document before we get started
|
|
// otherwise it may get saved and we're in trouble
|
|
if (this.delayLoadXml)
|
|
return this.delayLoadXml;
|
|
return this.serializeBlocks();
|
|
};
|
|
Editor.prototype.serializeBlocks = function (normalize) {
|
|
var xml = pxt.blocks.saveWorkspaceXml(this.editor);
|
|
// strip out id, x, y attributes
|
|
if (normalize)
|
|
xml = xml.replace(/(x|y|id)="[^"]*"/g, '');
|
|
pxt.debug(xml);
|
|
return xml;
|
|
};
|
|
Editor.prototype.loadBlockly = function (s) {
|
|
if (this.serializeBlocks() == s) {
|
|
this.typeScriptSaveable = true;
|
|
pxt.debug('blocks already loaded...');
|
|
return false;
|
|
}
|
|
this.typeScriptSaveable = false;
|
|
this.editor.clear();
|
|
try {
|
|
var text = pxt.blocks.importXml(s || "<block type=\"" + ts.pxtc.ON_START_TYPE + "\"></block>", this.blockInfo, true);
|
|
var xml = Blockly.Xml.textToDom(text);
|
|
Blockly.Xml.domToWorkspace(xml, this.editor);
|
|
this.initLayout();
|
|
this.editor.clearUndo();
|
|
this.reportDeprecatedBlocks();
|
|
this.typeScriptSaveable = true;
|
|
}
|
|
catch (e) {
|
|
pxt.log(e);
|
|
this.editor.clear();
|
|
this.switchToTypeScript();
|
|
this.changeCallback();
|
|
return false;
|
|
}
|
|
this.changeCallback();
|
|
return true;
|
|
};
|
|
Editor.prototype.initLayout = function () {
|
|
// layout on first load if no data info
|
|
var needsLayout = this.editor.getTopBlocks(false).some(function (b) {
|
|
var tp = b.getBoundingRectangle().topLeft;
|
|
return b.type != ts.pxtc.ON_START_TYPE && tp.x == 0 && tp.y == 0;
|
|
});
|
|
if (needsLayout)
|
|
pxt.blocks.layout.flow(this.editor);
|
|
};
|
|
Editor.prototype.initPrompts = function () {
|
|
// Overriding blockly prompts to use semantic modals
|
|
/**
|
|
* Wrapper to window.alert() that app developers may override to
|
|
* provide alternatives to the modal browser window.
|
|
* @param {string} message The message to display to the user.
|
|
* @param {function()=} opt_callback The callback when the alert is dismissed.
|
|
*/
|
|
Blockly.alert = function (message, opt_callback) {
|
|
return core.dialogAsync({
|
|
hideCancel: true,
|
|
header: lf("Alert"),
|
|
body: message,
|
|
size: "small"
|
|
}).then(function () {
|
|
if (opt_callback) {
|
|
opt_callback();
|
|
}
|
|
});
|
|
};
|
|
/**
|
|
* Wrapper to window.confirm() that app developers may override to
|
|
* provide alternatives to the modal browser window.
|
|
* @param {string} message The message to display to the user.
|
|
* @param {!function(boolean)} callback The callback for handling user response.
|
|
*/
|
|
Blockly.confirm = function (message, callback) {
|
|
return core.confirmAsync({
|
|
header: lf("Confirm"),
|
|
body: message,
|
|
agreeLbl: lf("Yes"),
|
|
agreeClass: "cancel",
|
|
agreeIcon: "cancel",
|
|
disagreeLbl: lf("No"),
|
|
disagreeClass: "positive",
|
|
disagreeIcon: "checkmark",
|
|
size: "small"
|
|
}).then(function (b) {
|
|
callback(b == 0);
|
|
});
|
|
};
|
|
/**
|
|
* Wrapper to window.prompt() that app developers may override to provide
|
|
* alternatives to the modal browser window. Built-in browser prompts are
|
|
* often used for better text input experience on mobile device. We strongly
|
|
* recommend testing mobile when overriding this.
|
|
* @param {string} message The message to display to the user.
|
|
* @param {string} defaultValue The value to initialize the prompt with.
|
|
* @param {!function(string)} callback The callback for handling user reponse.
|
|
*/
|
|
Blockly.prompt = function (message, defaultValue, callback) {
|
|
return core.promptAsync({
|
|
header: message,
|
|
defaultValue: defaultValue,
|
|
agreeLbl: lf("Ok"),
|
|
disagreeLbl: lf("Cancel"),
|
|
size: "small"
|
|
}).then(function (value) {
|
|
callback(value);
|
|
});
|
|
};
|
|
};
|
|
Editor.prototype.reportDeprecatedBlocks = function () {
|
|
var deprecatedMap = {};
|
|
var deprecatedBlocksFound = false;
|
|
this.blockInfo.blocks.forEach(function (symbolInfo) {
|
|
if (symbolInfo.attributes.deprecated) {
|
|
deprecatedMap[symbolInfo.attributes.blockId] = 0;
|
|
}
|
|
});
|
|
this.editor.getAllBlocks().forEach(function (block) {
|
|
if (deprecatedMap[block.type] >= 0) {
|
|
deprecatedMap[block.type]++;
|
|
deprecatedBlocksFound = true;
|
|
}
|
|
});
|
|
for (var block in deprecatedMap) {
|
|
if (deprecatedMap[block] === 0) {
|
|
delete deprecatedMap[block];
|
|
}
|
|
}
|
|
if (deprecatedBlocksFound) {
|
|
pxt.tickEvent("blocks.usingDeprecated", deprecatedMap);
|
|
}
|
|
};
|
|
Editor.prototype.contentSize = function () {
|
|
return this.editor ? pxt.blocks.blocksMetrics(this.editor) : undefined;
|
|
};
|
|
/**
|
|
* Takes the XML definition of the block that will be shown on the help card and modifies the XML
|
|
* so that the field names are updated to match any field names of dropdowns on the selected block
|
|
*/
|
|
Editor.prototype.updateFields = function (originalXML, newFieldValues, mutation) {
|
|
var parser = new DOMParser();
|
|
var doc = parser.parseFromString(originalXML, "application/xml");
|
|
var blocks = doc.getElementsByTagName("block");
|
|
if (blocks.length >= 1) {
|
|
//Setting innerText doesn't work if there are no children on the node
|
|
var setInnerText = function (c, newValue) {
|
|
//Remove any existing children
|
|
while (c.firstChild) {
|
|
c.removeChild(c.firstChild);
|
|
}
|
|
var tn = doc.createTextNode(newValue);
|
|
c.appendChild(tn);
|
|
};
|
|
var block = blocks[0];
|
|
if (newFieldValues) {
|
|
//Depending on the source, the nodeName may be capitalised
|
|
var fieldNodes = Array.prototype.filter.call(block.childNodes, function (c) { return c.nodeName == 'field' || c.nodeName == 'FIELD'; });
|
|
for (var i = 0; i < fieldNodes.length; i++) {
|
|
if (newFieldValues.hasOwnProperty(fieldNodes[i].getAttribute('name'))) {
|
|
setInnerText(fieldNodes[i], newFieldValues[fieldNodes[i].getAttribute('name')]);
|
|
delete newFieldValues[fieldNodes[i].getAttribute('name')];
|
|
}
|
|
}
|
|
//Now that existing field values have been reset, we can create new field values as appropriate
|
|
for (var p in newFieldValues) {
|
|
var c = doc.createElement('field');
|
|
c.setAttribute('name', p);
|
|
setInnerText(c, newFieldValues[p]);
|
|
block.appendChild(c);
|
|
}
|
|
}
|
|
else if (mutation) {
|
|
var existingMutation = Array.prototype.filter.call(block.childNodes, function (c) { return c.nodeName == 'mutation' || c.nodeName == 'MUTATION'; });
|
|
if (existingMutation.length) {
|
|
block.replaceChild(mutation, existingMutation[0]);
|
|
}
|
|
else {
|
|
block.appendChild(mutation);
|
|
}
|
|
}
|
|
var serializer = new XMLSerializer();
|
|
return serializer.serializeToString(doc);
|
|
}
|
|
else {
|
|
return originalXML;
|
|
}
|
|
};
|
|
Editor.prototype.isIncomplete = function () {
|
|
return this.editor ? this.editor.isDragging() : false;
|
|
};
|
|
Editor.prototype.prepare = function () {
|
|
this.prepareBlockly();
|
|
this.isReady = true;
|
|
};
|
|
Editor.prototype.prepareBlockly = function (showCategories) {
|
|
var _this = this;
|
|
if (showCategories === void 0) { showCategories = true; }
|
|
var blocklyDiv = document.getElementById('blocksEditor');
|
|
blocklyDiv.innerHTML = '';
|
|
var blocklyOptions = this.getBlocklyOptions(showCategories);
|
|
Util.jsonMergeFrom(blocklyOptions, pxt.appTarget.appTheme.blocklyOptions || {});
|
|
this.editor = Blockly.inject(blocklyDiv, blocklyOptions);
|
|
// zoom out on mobile by default
|
|
if (pxt.BrowserUtils.isMobile())
|
|
this.editor.zoomCenter(-4);
|
|
this.editor.addChangeListener(function (ev) {
|
|
Blockly.Events.disableOrphans(ev);
|
|
if (ev.type != 'ui') {
|
|
_this.changeCallback();
|
|
}
|
|
if (ev.type == 'create') {
|
|
var lastCategory = _this.editor.toolbox_ ?
|
|
(_this.editor.toolbox_.lastCategory_ ?
|
|
_this.editor.toolbox_.lastCategory_.element_.innerText.trim()
|
|
: 'unknown')
|
|
: 'flyout';
|
|
var blockId = ev.xml.getAttribute('type');
|
|
pxt.tickEvent("blocks.create", { category: lastCategory, block: blockId });
|
|
if (ev.xml.tagName == 'SHADOW')
|
|
_this.cleanUpShadowBlocks();
|
|
}
|
|
if (ev.type == 'ui') {
|
|
if (ev.element == 'category') {
|
|
var toolboxVisible = !!ev.newValue;
|
|
_this.parent.setState({ hideEditorFloats: toolboxVisible });
|
|
if (ev.newValue == lf("{id:category}Add Package")) {
|
|
_this.editor.toolbox_.clearSelection();
|
|
_this.parent.addPackage();
|
|
}
|
|
}
|
|
else if (ev.element == 'commentOpen'
|
|
|| ev.element == 'warningOpen') {
|
|
/*
|
|
* We override the default selection behavior so that when a block is selected, its
|
|
* comment is expanded. However, if a user selects a block by clicking on its comment
|
|
* icon (the blue question mark), there is a chance that the comment will be expanded
|
|
* and immediately collapse again because the icon click toggled the state. This hack
|
|
* prevents two events caused by the same click from opening and then closing a comment
|
|
*/
|
|
if (ev.group) {
|
|
// newValue is true if the comment has been expanded
|
|
if (ev.newValue) {
|
|
_this.selectedEventGroup = ev.group;
|
|
}
|
|
else if (ev.group == _this.selectedEventGroup && _this.currentCommentOrWarning) {
|
|
_this.currentCommentOrWarning.setVisible(true);
|
|
_this.selectedEventGroup = undefined;
|
|
}
|
|
}
|
|
}
|
|
else if (ev.element == 'selected') {
|
|
if (_this.currentCommentOrWarning) {
|
|
_this.currentCommentOrWarning.setVisible(false);
|
|
}
|
|
var selected = Blockly.selected;
|
|
if (selected && selected.warning && typeof (selected.warning) !== "string") {
|
|
selected.warning.setVisible(true);
|
|
_this.currentCommentOrWarning = selected.warning;
|
|
}
|
|
else if (selected && selected.comment && typeof (selected.comment) !== "string") {
|
|
selected.comment.setVisible(true);
|
|
_this.currentCommentOrWarning = selected.comment;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
this.initPrompts();
|
|
this.resize();
|
|
};
|
|
Editor.prototype.resize = function (e) {
|
|
var blocklyArea = document.getElementById('blocksArea');
|
|
var blocklyDiv = document.getElementById('blocksEditor');
|
|
// Position blocklyDiv over blocklyArea.
|
|
if (blocklyArea && this.editor) {
|
|
blocklyDiv.style.width = blocklyArea.offsetWidth + 'px';
|
|
blocklyDiv.style.height = blocklyArea.offsetHeight + 'px';
|
|
Blockly.svgResize(this.editor);
|
|
}
|
|
};
|
|
Editor.prototype.hasUndo = function () {
|
|
return this.editor ? this.editor.undoStack_.length != 0 : false;
|
|
};
|
|
Editor.prototype.undo = function () {
|
|
if (!this.editor)
|
|
return;
|
|
this.editor.undo();
|
|
this.parent.forceUpdate();
|
|
};
|
|
Editor.prototype.hasRedo = function () {
|
|
return this.editor ? this.editor.redoStack_.length != 0 : false;
|
|
};
|
|
Editor.prototype.redo = function () {
|
|
if (!this.editor)
|
|
return;
|
|
this.editor.undo(true);
|
|
this.parent.forceUpdate();
|
|
};
|
|
Editor.prototype.zoomIn = function () {
|
|
if (!this.editor)
|
|
return;
|
|
this.editor.zoomCenter(1);
|
|
};
|
|
Editor.prototype.zoomOut = function () {
|
|
if (!this.editor)
|
|
return;
|
|
this.editor.zoomCenter(-1);
|
|
};
|
|
Editor.prototype.getId = function () {
|
|
return "blocksArea";
|
|
};
|
|
Editor.prototype.display = function () {
|
|
return (React.createElement("div", null, React.createElement("div", {id: "blocksEditor"})));
|
|
};
|
|
Editor.prototype.getViewState = function () {
|
|
// ZOOM etc
|
|
return {};
|
|
};
|
|
Editor.prototype.setViewState = function (pos) { };
|
|
Editor.prototype.getCurrentSource = function () {
|
|
return this.editor ? this.saveBlockly() : this.currSource;
|
|
};
|
|
Editor.prototype.acceptsFile = function (file) {
|
|
return file.getExtension() == "blocks";
|
|
};
|
|
Editor.prototype.loadFileAsync = function (file) {
|
|
this.currSource = file.content;
|
|
this.typeScriptSaveable = false;
|
|
this.setDiagnostics(file);
|
|
this.delayLoadXml = file.content;
|
|
this.editor.clearUndo();
|
|
if (this.currFile && this.currFile != file) {
|
|
this.filterToolbox(null);
|
|
}
|
|
this.currFile = file;
|
|
return Promise.resolve();
|
|
};
|
|
Editor.prototype.switchToTypeScript = function () {
|
|
pxt.tickEvent("blocks.switchjavascript");
|
|
this.parent.switchTypeScript();
|
|
};
|
|
Editor.prototype.setDiagnostics = function (file) {
|
|
var _this = this;
|
|
Util.assert(this.editor != undefined); // Guarded
|
|
if (!this.compilationResult || this.delayLoadXml || this.loadingXml)
|
|
return;
|
|
// clear previous warnings on non-disabled blocks
|
|
this.editor.getAllBlocks().filter(function (b) { return !b.disabled; }).forEach(function (b) { return b.setWarningText(null); });
|
|
var tsfile = file.epkg.files[file.getVirtualFileName()];
|
|
if (!tsfile || !tsfile.diagnostics)
|
|
return;
|
|
// only show errors
|
|
var diags = tsfile.diagnostics.filter(function (d) { return d.category == ts.DiagnosticCategory.Error; });
|
|
var sourceMap = this.compilationResult.sourceMap;
|
|
diags.filter(function (diag) { return diag.category == ts.DiagnosticCategory.Error; }).forEach(function (diag) {
|
|
var bid = pxt.blocks.findBlockId(sourceMap, diag);
|
|
if (bid) {
|
|
var b = _this.editor.getBlockById(bid);
|
|
if (b) {
|
|
var txt = ts.flattenDiagnosticMessageText(diag.messageText, "\n");
|
|
b.setWarningText(txt);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
Editor.prototype.highlightStatement = function (brk) {
|
|
if (!this.compilationResult || this.delayLoadXml || this.loadingXml)
|
|
return;
|
|
var bid = pxt.blocks.findBlockId(this.compilationResult.sourceMap, brk);
|
|
this.editor.highlightBlock(bid);
|
|
};
|
|
Editor.prototype.openTypeScript = function () {
|
|
pxt.tickEvent("blocks.showjavascript");
|
|
this.parent.openTypeScriptAsync().done();
|
|
};
|
|
Editor.prototype.cleanUpShadowBlocks = function () {
|
|
var blocks = this.editor.getTopBlocks(false);
|
|
blocks.filter(function (b) { return b.isShadow_; }).forEach(function (b) { return b.dispose(false); });
|
|
};
|
|
Editor.prototype.getBlocklyOptions = function (showCategories) {
|
|
if (showCategories === void 0) { showCategories = true; }
|
|
var readOnly = pxt.shell.isReadOnly();
|
|
var toolbox = showCategories ?
|
|
document.getElementById('blocklyToolboxDefinitionCategory')
|
|
: document.getElementById('blocklyToolboxDefinitionFlyout');
|
|
var blocklyOptions = {
|
|
toolbox: readOnly ? undefined : toolbox,
|
|
scrollbars: true,
|
|
media: pxt.webConfig.pxtCdnUrl + "blockly/media/",
|
|
sound: true,
|
|
trashcan: false,
|
|
collapse: false,
|
|
comments: true,
|
|
disable: false,
|
|
readOnly: readOnly,
|
|
toolboxType: pxt.appTarget.appTheme.coloredToolbox ? 'coloured' : pxt.appTarget.appTheme.invertedToolbox ? 'inverted' : 'normal',
|
|
zoom: {
|
|
enabled: false,
|
|
controls: false,
|
|
wheel: true,
|
|
maxScale: 2.5,
|
|
minScale: .2,
|
|
scaleSpeed: 1.05
|
|
},
|
|
rtl: Util.userLanguageRtl()
|
|
};
|
|
return blocklyOptions;
|
|
};
|
|
Editor.prototype.getDefaultToolbox = function (showCategories) {
|
|
if (showCategories === void 0) { showCategories = true; }
|
|
return showCategories ?
|
|
toolbox_1.default.documentElement
|
|
: new DOMParser().parseFromString("<xml id=\"blocklyToolboxDefinition\" style=\"display: none\"></xml>", "text/xml").documentElement;
|
|
};
|
|
Editor.prototype.filterToolbox = function (blockSubset, showCategories) {
|
|
if (showCategories === void 0) { showCategories = true; }
|
|
this.blockSubset = blockSubset;
|
|
this.showToolboxCategories = showCategories;
|
|
var toolbox = this.getDefaultToolbox(showCategories);
|
|
if (!this.blockInfo)
|
|
return;
|
|
var tb = pxt.blocks.createToolbox(this.blockInfo, toolbox, showCategories, blockSubset);
|
|
this.updateToolbox(tb, showCategories);
|
|
pxt.blocks.cachedSearchTb = tb;
|
|
return tb;
|
|
};
|
|
Editor.prototype.updateToolbox = function (tb, showCategories) {
|
|
if (showCategories === void 0) { showCategories = true; }
|
|
// no toolbox when readonly
|
|
if (pxt.shell.isReadOnly())
|
|
return;
|
|
pxt.debug('updating toolbox');
|
|
if ((this.editor.toolbox_ && showCategories) || (this.editor.flyout_ && !showCategories)) {
|
|
// Toolbox is consistent with current mode, safe to update
|
|
var tbString = new XMLSerializer().serializeToString(tb);
|
|
if (tbString == this.cachedToolbox)
|
|
return;
|
|
this.cachedToolbox = tbString;
|
|
this.editor.updateToolbox(tb);
|
|
}
|
|
else {
|
|
// Toolbox mode is different, need to refresh.
|
|
this.delayLoadXml = this.getCurrentSource();
|
|
this.editor = undefined;
|
|
this.loadingXml = false;
|
|
if (this.loadingXmlPromise) {
|
|
this.loadingXmlPromise.cancel();
|
|
this.loadingXmlPromise = null;
|
|
}
|
|
this.prepareBlockly(showCategories);
|
|
this.domUpdate();
|
|
this.editor.scrollCenter();
|
|
}
|
|
};
|
|
return Editor;
|
|
}(srceditor.Editor));
|
|
exports.Editor = Editor;
|
|
|
|
},{"./compiler":8,"./core":10,"./srceditor":31,"./toolbox":34,"react":264}],4:[function(require,module,exports){
|
|
/// <reference path="../../typings/globals/jquery/index.d.ts" />
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var ReactDOM = require("react-dom");
|
|
var BlocksPreview = (function (_super) {
|
|
__extends(BlocksPreview, _super);
|
|
function BlocksPreview(props) {
|
|
_super.call(this, props);
|
|
this.state = {};
|
|
}
|
|
BlocksPreview.prototype.renderSvg = function () {
|
|
var el = $(ReactDOM.findDOMNode(this));
|
|
var svg = pxt.blocks.render(this.props.xml);
|
|
el.children().remove();
|
|
el.append(svg);
|
|
};
|
|
BlocksPreview.prototype.componentDidMount = function () {
|
|
this.renderSvg();
|
|
};
|
|
BlocksPreview.prototype.componentDidUpdate = function () {
|
|
this.renderSvg();
|
|
};
|
|
BlocksPreview.prototype.render = function () {
|
|
return (React.createElement("div", {style: { width: "100%", minHeight: "10em", direction: "ltr" }}));
|
|
};
|
|
return BlocksPreview;
|
|
}(React.Component));
|
|
exports.BlocksPreview = BlocksPreview;
|
|
|
|
},{"react":264,"react-dom":135}],5:[function(require,module,exports){
|
|
"use strict";
|
|
var db = require("./db");
|
|
var core = require("./core");
|
|
var pkg = require("./package");
|
|
var data = require("./data");
|
|
var ws = require("./workspace");
|
|
var headers = new db.Table("header");
|
|
var texts = new db.Table("text");
|
|
var U = pxt.Util;
|
|
var Cloud = pxt.Cloud;
|
|
var lf = U.lf;
|
|
var allScripts = [];
|
|
function lookup(id) {
|
|
return allScripts.filter(function (x) { return x.id == id; })[0];
|
|
}
|
|
function getHeaders() {
|
|
return allScripts.map(function (e) { return e.header; });
|
|
}
|
|
function getHeader(id) {
|
|
var e = lookup(id);
|
|
if (e && !e.header.isDeleted)
|
|
return e.header;
|
|
return null;
|
|
}
|
|
function initAsync(target) {
|
|
// TODO getAllAsync aware of target?
|
|
return headers.getAllAsync().then(function (h) {
|
|
allScripts = h
|
|
.filter(function (hh) {
|
|
if (!hh.target)
|
|
hh.target = "microbit";
|
|
return hh.target == pxt.appTarget.id;
|
|
})
|
|
.map(function (hh) {
|
|
return {
|
|
id: hh.id,
|
|
header: hh,
|
|
text: null
|
|
};
|
|
});
|
|
});
|
|
}
|
|
function fetchTextAsync(e) {
|
|
return texts.getAsync(e.id)
|
|
.then(function (resp) {
|
|
if (!e.text) {
|
|
// otherwise we were beaten to it
|
|
e.text = ws.fixupFileNames(resp.files);
|
|
}
|
|
e.textRev = resp._rev;
|
|
return e.text;
|
|
});
|
|
}
|
|
var headerQ = new U.PromiseQueue();
|
|
function getTextAsync(id) {
|
|
var e = lookup(id);
|
|
if (!e)
|
|
return Promise.resolve(null);
|
|
if (e.text)
|
|
return Promise.resolve(e.text);
|
|
return headerQ.enqueue(id, function () { return fetchTextAsync(e); });
|
|
}
|
|
function fetchTextRevAsync(e) {
|
|
if (e.textRev)
|
|
return Promise.resolve();
|
|
return fetchTextAsync(e).then(function () { }, function (err) { });
|
|
}
|
|
function saveCoreAsync(h, text) {
|
|
if (h.temporary)
|
|
return Promise.resolve();
|
|
var e = lookup(h.id);
|
|
U.assert(e.header === h);
|
|
if (text) {
|
|
h.saveId = null;
|
|
e.textNeedsSave = true;
|
|
e.text = text;
|
|
}
|
|
return headerQ.enqueue(h.id, function () {
|
|
return (!text ? Promise.resolve() :
|
|
fetchTextRevAsync(e)
|
|
.then(function () {
|
|
e.textNeedsSave = false;
|
|
return texts.setAsync({
|
|
id: e.id,
|
|
files: e.text,
|
|
_rev: e.textRev
|
|
}).then(function (rev) {
|
|
e.textRev = rev;
|
|
});
|
|
}))
|
|
.then(function () { return headers.setAsync(e.header); })
|
|
.then(function (rev) {
|
|
h._rev = rev;
|
|
data.invalidate("header:" + h.id);
|
|
data.invalidate("header:*");
|
|
if (text) {
|
|
data.invalidate("text:" + h.id);
|
|
h.saveId = null;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
function saveAsync(h, text) {
|
|
return saveCoreAsync(h, text);
|
|
}
|
|
function installAsync(h0, text) {
|
|
var h = h0;
|
|
h.id = U.guidGen();
|
|
h.recentUse = U.nowSeconds();
|
|
h.modificationTime = h.recentUse;
|
|
h.target = pxt.appTarget.id;
|
|
var e = {
|
|
id: h.id,
|
|
header: h,
|
|
text: text,
|
|
};
|
|
allScripts.push(e);
|
|
return saveCoreAsync(h, text)
|
|
.then(function () { return h; });
|
|
}
|
|
function isProject(h) {
|
|
return /prj$/.test(h.editor);
|
|
}
|
|
function saveToCloudAsync(h) {
|
|
if (!Cloud.isLoggedIn())
|
|
return Promise.resolve();
|
|
return syncOneUpAsync(h);
|
|
}
|
|
function syncOneUpAsync(h) {
|
|
var saveId = {};
|
|
var e = lookup(h.id);
|
|
return getTextAsync(h.id)
|
|
.then(function () {
|
|
var scr = "";
|
|
var files = e.text;
|
|
if (isProject(h))
|
|
scr = JSON.stringify(files);
|
|
else
|
|
scr = files[Object.keys(files)[0]] || "";
|
|
var body = {
|
|
guid: h.id,
|
|
name: h.name,
|
|
scriptId: h.pubId,
|
|
scriptVersion: { time: h.modificationTime, baseSnapshot: "*" },
|
|
meta: JSON.stringify(h.meta),
|
|
status: h.pubCurrent ? "published" : "unpublished",
|
|
recentUse: h.recentUse,
|
|
editor: h.editor,
|
|
script: scr,
|
|
target: pxt.appTarget.id
|
|
};
|
|
pxt.debug("sync up " + h.id + "; " + body.script.length + " chars");
|
|
h.saveId = saveId;
|
|
return Cloud.privatePostAsync("me/installed", { bodies: [body] });
|
|
})
|
|
.then(function (resp) {
|
|
var chd = resp.headers[0];
|
|
h.blobId = chd.scriptVersion.baseSnapshot;
|
|
if (h.saveId === saveId)
|
|
h.blobCurrent = true;
|
|
return saveCoreAsync(h);
|
|
});
|
|
}
|
|
function syncAsync() {
|
|
var numUp = 0;
|
|
var numDown = 0;
|
|
var blobConatiner = "";
|
|
var updated = {};
|
|
if (!Cloud.hasAccessToken())
|
|
return Promise.resolve();
|
|
function uninstallAsync(h) {
|
|
pxt.debug("uninstall local " + h.id);
|
|
var e = lookup(h.id);
|
|
var idx = allScripts.indexOf(e);
|
|
U.assert(idx >= 0);
|
|
allScripts.splice(idx, 1);
|
|
h.isDeleted = true;
|
|
return headerQ.enqueue(h.id, function () {
|
|
return headers.deleteAsync(h)
|
|
.then(function () { return fetchTextRevAsync(e); })
|
|
.then(function () { return texts.deleteAsync({ id: h.id, _rev: e.textRev }); });
|
|
});
|
|
}
|
|
function syncDownAsync(header0, cloudHeader) {
|
|
if (cloudHeader.status == "deleted") {
|
|
if (!header0)
|
|
return Promise.resolve();
|
|
else
|
|
return uninstallAsync(header0);
|
|
}
|
|
var header = header0;
|
|
if (!header) {
|
|
header = {
|
|
id: cloudHeader.guid
|
|
};
|
|
}
|
|
numDown++;
|
|
U.assert(header.id == cloudHeader.guid);
|
|
var blobId = cloudHeader.scriptVersion.baseSnapshot;
|
|
pxt.debug("sync down " + header.id + " - " + blobId);
|
|
return U.httpGetJsonAsync(blobConatiner + blobId)
|
|
.catch(core.handleNetworkError)
|
|
.then(function (resp) {
|
|
U.assert(resp.guid == header.id);
|
|
header.blobCurrent = true;
|
|
header.blobId = blobId;
|
|
header.modificationTime = cloudHeader.scriptVersion.time;
|
|
header.editor = resp.editor;
|
|
header.name = resp.name;
|
|
var files = { "_default_": resp.script };
|
|
if (isProject(header))
|
|
files = JSON.parse(resp.script);
|
|
header.recentUse = cloudHeader.recentUse;
|
|
delete header.isDeleted;
|
|
header.pubId = resp.scriptId;
|
|
header.pubCurrent = (resp.status == "published");
|
|
header.saveId = null;
|
|
header.target = resp.target;
|
|
if (!header0)
|
|
allScripts.push({
|
|
header: header,
|
|
text: null,
|
|
id: header.id
|
|
});
|
|
updated[header.id] = 1;
|
|
return saveCoreAsync(header, files);
|
|
})
|
|
.then(function () { return progress(--numDown); });
|
|
}
|
|
function progressMsg(m) {
|
|
core.infoNotification(m);
|
|
}
|
|
function progress(dummy) {
|
|
var msg = "";
|
|
if (numDown == 0 && numUp == 0)
|
|
msg = lf("All synced");
|
|
else {
|
|
msg = lf("Syncing") + " (";
|
|
if (numDown)
|
|
msg += lf("{0} down", numDown);
|
|
if (numUp)
|
|
msg += (numDown ? ", " : "") + lf("{0} up", numUp);
|
|
msg += ")";
|
|
}
|
|
progressMsg(msg);
|
|
}
|
|
function syncUpAsync(h) {
|
|
numUp++;
|
|
return syncOneUpAsync(h)
|
|
.then(function () { return progress(--numUp); });
|
|
}
|
|
function syncDeleteAsync(h) {
|
|
var body = {
|
|
guid: h.id,
|
|
status: "deleted",
|
|
scriptVersion: { time: U.nowSeconds(), baseSnapshot: "*" }
|
|
};
|
|
return Cloud.privatePostAsync("me/installed", { bodies: [body] })
|
|
.then(function () { return uninstallAsync(h); });
|
|
}
|
|
return Cloud.privateGetAsync("me/installed?format=short")
|
|
.then(function (resp) {
|
|
blobConatiner = resp.blobcontainer;
|
|
var cloudHeaders = U.toDictionary(resp.headers, function (h) { return h.guid; });
|
|
var existingHeaders = U.toDictionary(allScripts, function (h) { return h.id; });
|
|
var waitFor = allScripts.map(function (e) {
|
|
var hd = e.header;
|
|
if (cloudHeaders.hasOwnProperty(hd.id)) {
|
|
var chd = cloudHeaders[hd.id];
|
|
if (hd.isDeleted)
|
|
return syncDeleteAsync(hd);
|
|
if (chd.scriptVersion.baseSnapshot == hd.blobId) {
|
|
if (hd.blobCurrent) {
|
|
if (hd.recentUse != chd.recentUse) {
|
|
hd.recentUse = chd.recentUse;
|
|
return saveCoreAsync(hd);
|
|
}
|
|
else {
|
|
// nothing to do
|
|
return Promise.resolve();
|
|
}
|
|
}
|
|
else {
|
|
return syncUpAsync(hd);
|
|
}
|
|
}
|
|
else {
|
|
if (hd.blobCurrent) {
|
|
return syncDownAsync(hd, chd);
|
|
}
|
|
else {
|
|
return syncUpAsync(hd);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (hd.blobId)
|
|
// this has been pushed once to the cloud - uninstall wins
|
|
return uninstallAsync(hd);
|
|
else
|
|
// never pushed before
|
|
return syncUpAsync(hd);
|
|
}
|
|
});
|
|
waitFor = waitFor.concat(resp.headers.filter(function (h) { return !existingHeaders[h.guid]; }).map(function (h) { return syncDownAsync(null, h); }));
|
|
progress(0);
|
|
return Promise.all(waitFor);
|
|
})
|
|
.then(function () { return progressMsg(lf("Syncing done")); })
|
|
.then(function () { return pkg.notifySyncDone(updated); })
|
|
.catch(core.handleNetworkError);
|
|
}
|
|
function resetAsync() {
|
|
return db.destroyAsync()
|
|
.then(function () {
|
|
pxt.storage.clearLocal();
|
|
data.clearCache();
|
|
});
|
|
}
|
|
exports.provider = {
|
|
getHeaders: getHeaders,
|
|
getHeader: getHeader,
|
|
getTextAsync: getTextAsync,
|
|
initAsync: initAsync,
|
|
saveAsync: saveAsync,
|
|
installAsync: installAsync,
|
|
saveToCloudAsync: saveToCloudAsync,
|
|
syncAsync: syncAsync,
|
|
resetAsync: resetAsync
|
|
};
|
|
|
|
},{"./core":10,"./data":11,"./db":12,"./package":24,"./workspace":37}],6:[function(require,module,exports){
|
|
"use strict";
|
|
/// <reference path="../../built/pxtlib.d.ts"/>
|
|
var core = require("./core");
|
|
var pkg = require("./package");
|
|
var hidbridge = require("./hidbridge");
|
|
var Cloud = pxt.Cloud;
|
|
function browserDownloadAsync(text, name, contentType) {
|
|
var url = pxt.BrowserUtils.browserDownloadBinText(text, name, contentType, function (e) { return core.errorNotification(lf("saving file failed...")); });
|
|
return Promise.resolve();
|
|
}
|
|
function browserDownloadDeployCoreAsync(resp) {
|
|
var url = "";
|
|
var fn = "";
|
|
if (pxt.appTarget.compile.useUF2) {
|
|
var uf2 = resp.outfiles[pxtc.BINARY_UF2];
|
|
fn = pkg.genFileName(".uf2");
|
|
pxt.debug('saving ' + fn);
|
|
url = pxt.BrowserUtils.browserDownloadBase64(uf2, fn, "application/x-uf2", function (e) { return core.errorNotification(lf("saving file failed...")); });
|
|
}
|
|
else {
|
|
var hex = resp.outfiles[pxtc.BINARY_HEX];
|
|
fn = pkg.genFileName(".hex");
|
|
pxt.debug('saving ' + fn);
|
|
url = pxt.BrowserUtils.browserDownloadBinText(hex, fn, pxt.appTarget.compile.hexMimeType, function (e) { return core.errorNotification(lf("saving file failed...")); });
|
|
}
|
|
if (!resp.success) {
|
|
return core.confirmAsync({
|
|
header: lf("Compilation failed"),
|
|
body: lf("Ooops, looks like there are errors in your program."),
|
|
hideAgree: true,
|
|
disagreeLbl: lf("Close")
|
|
}).then(function () { });
|
|
}
|
|
if (resp.saveOnly || pxt.BrowserUtils.isBrowserDownloadInSameWindow())
|
|
return Promise.resolve();
|
|
else
|
|
return showUploadInstructionsAsync(fn, url);
|
|
}
|
|
exports.browserDownloadDeployCoreAsync = browserDownloadDeployCoreAsync;
|
|
function showUploadInstructionsAsync(fn, url) {
|
|
var boardName = pxt.appTarget.appTheme.boardName || "???";
|
|
var boardDriveName = pxt.appTarget.compile.driveName || "???";
|
|
var docUrl = pxt.appTarget.appTheme.usbDocs;
|
|
return core.confirmAsync({
|
|
header: lf("Download completed..."),
|
|
body: lf("Move the {0} file to the {1} drive to transfer the code into your {2}.", pxt.appTarget.compile.useUF2 ? ".uf2" : ".hex", boardDriveName, boardName),
|
|
hideCancel: true,
|
|
agreeLbl: lf("Done!"),
|
|
buttons: [{
|
|
label: lf("Download again"),
|
|
icon: "download",
|
|
class: "lightgrey",
|
|
url: url,
|
|
fileName: fn
|
|
}, !docUrl ? undefined : {
|
|
label: lf("Help"),
|
|
icon: "help",
|
|
class: "lightgrey",
|
|
url: docUrl
|
|
}],
|
|
timeout: 7000
|
|
}).then(function () { });
|
|
}
|
|
function webusbDeployCoreAsync(resp) {
|
|
pxt.debug('webusb deployment...');
|
|
core.infoNotification(lf("Flashing device..."));
|
|
var f = resp.outfiles[pxtc.BINARY_UF2];
|
|
var blocks = pxtc.UF2.parseFile(Util.stringToUint8Array(atob(f)));
|
|
return pxt.usb.initAsync()
|
|
.then(function (dev) { return dev.reflashAsync(blocks); });
|
|
}
|
|
function hidDeployCoreAsync(resp) {
|
|
pxt.debug('HID deployment...');
|
|
core.infoNotification(lf("Flashing device..."));
|
|
var f = resp.outfiles[pxtc.BINARY_UF2];
|
|
var blocks = pxtc.UF2.parseFile(Util.stringToUint8Array(atob(f)));
|
|
return hidbridge.initAsync()
|
|
.then(function (dev) { return dev.reflashAsync(blocks); });
|
|
}
|
|
function localhostDeployCoreAsync(resp) {
|
|
pxt.debug('local deployment...');
|
|
core.infoNotification(lf("Uploading .hex file..."));
|
|
var deploy = function () { return Util.requestAsync({
|
|
url: "/api/deploy",
|
|
headers: { "Authorization": Cloud.localToken },
|
|
method: "POST",
|
|
data: resp,
|
|
allowHttpErrors: true // To prevent "Network request failed" warning in case of error. We're not actually doing network requests in localhost scenarios
|
|
}).then(function (r) {
|
|
if (r.statusCode !== 200) {
|
|
core.errorNotification(lf("There was a problem, please try again"));
|
|
}
|
|
else if (r.json["boardCount"] === 0) {
|
|
core.warningNotification(lf("Please connect your {0} to your computer and try again", pxt.appTarget.appTheme.boardName));
|
|
}
|
|
}); };
|
|
return deploy();
|
|
}
|
|
function initCommandsAsync() {
|
|
pxt.commands.browserDownloadAsync = browserDownloadAsync;
|
|
var forceHexDownload = /forceHexDownload/i.test(window.location.href);
|
|
if (/webusb=1/i.test(window.location.href) && pxt.appTarget.compile.useUF2) {
|
|
pxt.commands.deployCoreAsync = webusbDeployCoreAsync;
|
|
}
|
|
else if (hidbridge.shouldUse() && !forceHexDownload) {
|
|
pxt.commands.deployCoreAsync = hidDeployCoreAsync;
|
|
}
|
|
else if (pxt.winrt.isWinRT()) {
|
|
pxt.commands.deployCoreAsync = pxt.winrt.deployCoreAsync;
|
|
pxt.commands.browserDownloadAsync = pxt.winrt.browserDownloadAsync;
|
|
}
|
|
else if (Cloud.isLocalHost() && Cloud.localToken && !forceHexDownload) {
|
|
pxt.commands.deployCoreAsync = localhostDeployCoreAsync;
|
|
}
|
|
else {
|
|
pxt.commands.deployCoreAsync = browserDownloadDeployCoreAsync;
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
exports.initCommandsAsync = initCommandsAsync;
|
|
|
|
},{"./core":10,"./hidbridge":19,"./package":24}],7:[function(require,module,exports){
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var blockspreview = require("./blockspreview");
|
|
var lf = pxt.Util.lf;
|
|
var repeat = pxt.Util.repeatMap;
|
|
var CodeCardView = (function (_super) {
|
|
__extends(CodeCardView, _super);
|
|
function CodeCardView(props) {
|
|
_super.call(this, props);
|
|
this.state = {};
|
|
}
|
|
CodeCardView.prototype.componentDidUpdate = function () {
|
|
$('.ui.embed').embed();
|
|
};
|
|
CodeCardView.prototype.render = function () {
|
|
var card = this.props;
|
|
var color = card.color || "";
|
|
if (!color) {
|
|
if (card.hardware && !card.software)
|
|
color = 'black';
|
|
else if (card.software && !card.hardware)
|
|
color = 'teal';
|
|
}
|
|
var renderMd = function (md) { return md.replace(/`/g, ''); };
|
|
var url = card.url ? /^[^:]+:\/\//.test(card.url) ? card.url : ('/' + card.url.replace(/^\.?\/?/, ''))
|
|
: undefined;
|
|
var sideUrl = url && /^\//.test(url) ? "#doc:" + url : url;
|
|
var className = card.className;
|
|
var cardDiv = React.createElement("div", {className: "ui card " + color + " " + (card.onClick ? "link" : '') + " " + (className ? className : ''), title: card.title, onClick: function (e) { return card.onClick ? card.onClick(e) : undefined; }}, card.header || card.blocks || card.javascript || card.hardware || card.software || card.any ?
|
|
React.createElement("div", {key: "header", className: "ui content " + (card.responsive ? " tall desktop only" : "")}, React.createElement("div", {className: "right floated meta"}, card.any ? (React.createElement("i", {key: "costany", className: "ui grey circular label tiny"}, card.any > 0 ? card.any : null)) : null, repeat(card.blocks, function (k) { return React.createElement("i", {key: "costblocks" + k, className: "puzzle orange icon"}); }), repeat(card.javascript, function (k) { return React.createElement("i", {key: "costjs" + k, className: "align left blue icon"}); }), repeat(card.hardware, function (k) { return React.createElement("i", {key: "costhardware" + k, className: "certificate black icon"}); }), repeat(card.software, function (k) { return React.createElement("i", {key: "costsoftware" + k, className: "square teal icon"}); })), card.header) : null, React.createElement("div", {className: "ui image"}, card.blocksXml ? React.createElement(blockspreview.BlocksPreview, {key: "promoblocks", xml: card.blocksXml}) : null, card.typeScript ? React.createElement("pre", {key: "promots"}, card.typeScript) : null, card.imageUrl ? React.createElement("img", {src: card.imageUrl, className: "ui image"}) : null), card.icon ?
|
|
React.createElement("div", {className: "" + ('ui button massive ' + card.iconColor)}, " ", React.createElement("i", {className: "" + ('icon ' + card.icon)}), " ") : null, card.shortName || card.name || card.description ?
|
|
React.createElement("div", {className: "content"}, card.shortName || card.name ? React.createElement("div", {className: "header"}, card.shortName || card.name) : null, card.time ? React.createElement("div", {className: "meta tall"}, card.time ? React.createElement("span", {key: "date", className: "date"}, pxt.Util.timeSince(card.time)) : null) : undefined, card.description ? React.createElement("div", {className: "description tall"}, renderMd(card.description)) : null) : undefined);
|
|
if (!card.onClick && url) {
|
|
return (React.createElement("div", null, React.createElement("a", {href: url, target: "docs", className: "ui widedesktop hide"}, cardDiv), React.createElement("a", {href: sideUrl, className: "ui widedesktop only"}, cardDiv)));
|
|
}
|
|
else {
|
|
return (cardDiv);
|
|
}
|
|
};
|
|
return CodeCardView;
|
|
}(React.Component));
|
|
exports.CodeCardView = CodeCardView;
|
|
|
|
},{"./blockspreview":4,"react":264}],8:[function(require,module,exports){
|
|
"use strict";
|
|
var pkg = require("./package");
|
|
var core = require("./core");
|
|
var workeriface = require("./workeriface");
|
|
var U = pxt.Util;
|
|
var iface;
|
|
function init() {
|
|
if (!iface) {
|
|
iface = workeriface.makeWebWorker(pxt.webConfig.workerjs);
|
|
}
|
|
}
|
|
exports.init = init;
|
|
function setDiagnostics(diagnostics) {
|
|
var mainPkg = pkg.mainEditorPkg();
|
|
mainPkg.forEachFile(function (f) { return f.diagnostics = []; });
|
|
var output = "";
|
|
var _loop_1 = function(diagnostic) {
|
|
if (diagnostic.fileName) {
|
|
output += (diagnostic.category == ts.DiagnosticCategory.Error ? lf("error") : diagnostic.category == ts.DiagnosticCategory.Warning ? lf("warning") : lf("message")) + ": " + diagnostic.fileName + "(" + (diagnostic.line + 1) + "," + (diagnostic.column + 1) + "): ";
|
|
var f_1 = mainPkg.filterFiles(function (f) { return f.getTypeScriptName() == diagnostic.fileName; })[0];
|
|
if (f_1)
|
|
f_1.diagnostics.push(diagnostic);
|
|
}
|
|
var category = ts.DiagnosticCategory[diagnostic.category].toLowerCase();
|
|
output += category + " TS" + diagnostic.code + ": " + ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n") + "\n";
|
|
};
|
|
for (var _i = 0, diagnostics_1 = diagnostics; _i < diagnostics_1.length; _i++) {
|
|
var diagnostic = diagnostics_1[_i];
|
|
_loop_1(diagnostic);
|
|
}
|
|
if (!output)
|
|
output = U.lf("Everything seems fine!\n");
|
|
var f = mainPkg.outputPkg.setFile("output.txt", output);
|
|
// display total number of errors on the output file
|
|
f.numDiagnosticsOverride = diagnostics.filter(function (d) { return d.category == ts.DiagnosticCategory.Error; }).length;
|
|
}
|
|
var hang = new Promise(function () { });
|
|
function catchUserErrorAndSetDiags(r) {
|
|
return function (v) {
|
|
if (v.isUserError) {
|
|
core.errorNotification(v.message);
|
|
var mainPkg = pkg.mainEditorPkg();
|
|
var f = mainPkg.outputPkg.setFile("output.txt", v.message);
|
|
f.numDiagnosticsOverride = 1;
|
|
return r;
|
|
}
|
|
else
|
|
return Promise.reject(v);
|
|
};
|
|
}
|
|
function compileAsync(options) {
|
|
if (options === void 0) { options = {}; }
|
|
var trg = pkg.mainPkg.getTargetOptions();
|
|
trg.isNative = options.native;
|
|
trg.preferredEditor = options.preferredEditor;
|
|
return pkg.mainPkg.getCompileOptionsAsync(trg)
|
|
.then(function (opts) {
|
|
if (options.debug) {
|
|
opts.breakpoints = true;
|
|
opts.justMyCode = true;
|
|
}
|
|
opts.computeUsedSymbols = true;
|
|
if (options.forceEmit)
|
|
opts.forceEmit = true;
|
|
if (/test=1/i.test(window.location.href))
|
|
opts.testMode = true;
|
|
return opts;
|
|
})
|
|
.then(compileCoreAsync)
|
|
.then(function (resp) {
|
|
var outpkg = pkg.mainEditorPkg().outputPkg;
|
|
// keep the assembly file - it is only generated when user hits "Download"
|
|
// and is usually overwritten by the autorun very quickly, so it's impossible to see it
|
|
var prevasm = outpkg.files[pxtc.BINARY_ASM];
|
|
if (prevasm && !resp.outfiles[pxtc.BINARY_ASM]) {
|
|
resp.outfiles[pxtc.BINARY_ASM] = prevasm.content;
|
|
}
|
|
pkg.mainEditorPkg().outputPkg.setFiles(resp.outfiles);
|
|
setDiagnostics(resp.diagnostics);
|
|
return ensureApisInfoAsync()
|
|
.then(function () {
|
|
if (!resp.usedSymbols)
|
|
return resp;
|
|
for (var _i = 0, _a = Object.keys(resp.usedSymbols); _i < _a.length; _i++) {
|
|
var k = _a[_i];
|
|
resp.usedSymbols[k] = U.lookup(cachedApis.byQName, k);
|
|
}
|
|
return resp;
|
|
});
|
|
})
|
|
.catch(catchUserErrorAndSetDiags(hang));
|
|
}
|
|
exports.compileAsync = compileAsync;
|
|
function assembleCore(src) {
|
|
return workerOpAsync("assemble", { fileContent: src });
|
|
}
|
|
function assembleAsync(src) {
|
|
var stackBase = 0x20004000;
|
|
return assembleCore(".startaddr " + (stackBase - 256) + "\n" + src)
|
|
.then(function (r) {
|
|
return assembleCore(".startaddr " + (stackBase - (r.words.length + 1) * 4) + "\n" + src)
|
|
.then(function (rr) {
|
|
U.assert(rr.words.length == r.words.length);
|
|
return rr;
|
|
});
|
|
});
|
|
}
|
|
exports.assembleAsync = assembleAsync;
|
|
function compileCoreAsync(opts) {
|
|
return workerOpAsync("compile", { options: opts });
|
|
}
|
|
function decompileAsync(fileName, blockInfo, oldWorkspace, blockFile) {
|
|
var trg = pkg.mainPkg.getTargetOptions();
|
|
return pkg.mainPkg.getCompileOptionsAsync(trg)
|
|
.then(function (opts) {
|
|
opts.ast = true;
|
|
return decompileCoreAsync(opts, fileName);
|
|
})
|
|
.then(function (resp) {
|
|
// try to patch event locations
|
|
if (resp.success && blockInfo && oldWorkspace && blockFile) {
|
|
var newXml = pxt.blocks.layout.patchBlocksFromOldWorkspace(blockInfo, oldWorkspace, resp.outfiles[blockFile]);
|
|
resp.outfiles[blockFile] = newXml;
|
|
}
|
|
pkg.mainEditorPkg().outputPkg.setFiles(resp.outfiles);
|
|
setDiagnostics(resp.diagnostics);
|
|
return resp;
|
|
});
|
|
}
|
|
exports.decompileAsync = decompileAsync;
|
|
function decompileCoreAsync(opts, fileName) {
|
|
return workerOpAsync("decompile", { options: opts, fileName: fileName });
|
|
}
|
|
function workerOpAsync(op, arg) {
|
|
init();
|
|
return iface.opAsync(op, arg);
|
|
}
|
|
exports.workerOpAsync = workerOpAsync;
|
|
var firstTypecheck;
|
|
var cachedApis;
|
|
var cachedBlocks;
|
|
var refreshApis = false;
|
|
function waitForFirstTypecheckAsync() {
|
|
if (firstTypecheck)
|
|
return firstTypecheck;
|
|
else
|
|
return typecheckAsync();
|
|
}
|
|
function ensureApisInfoAsync() {
|
|
if (refreshApis || !cachedApis)
|
|
return workerOpAsync("apiInfo", {})
|
|
.then(function (apis) {
|
|
refreshApis = false;
|
|
return ts.pxtc.localizeApisAsync(apis, pkg.mainPkg);
|
|
}).then(function (apis) {
|
|
cachedApis = apis;
|
|
});
|
|
else
|
|
return Promise.resolve();
|
|
}
|
|
function apiSearchAsync(searchFor) {
|
|
return ensureApisInfoAsync()
|
|
.then(function () { return workerOpAsync("apiSearch", { search: searchFor }); });
|
|
}
|
|
exports.apiSearchAsync = apiSearchAsync;
|
|
function typecheckAsync() {
|
|
var p = pkg.mainPkg.getCompileOptionsAsync()
|
|
.then(function (opts) {
|
|
opts.testMode = true; // show errors in all top-level code
|
|
return workerOpAsync("setOptions", { options: opts });
|
|
})
|
|
.then(function () { return workerOpAsync("allDiags", {}); })
|
|
.then(setDiagnostics)
|
|
.then(ensureApisInfoAsync)
|
|
.catch(catchUserErrorAndSetDiags(null));
|
|
if (!firstTypecheck)
|
|
firstTypecheck = p;
|
|
return p;
|
|
}
|
|
exports.typecheckAsync = typecheckAsync;
|
|
function getApisInfoAsync() {
|
|
return waitForFirstTypecheckAsync()
|
|
.then(function () { return cachedApis; });
|
|
}
|
|
exports.getApisInfoAsync = getApisInfoAsync;
|
|
function getBlocksAsync() {
|
|
return cachedBlocks
|
|
? Promise.resolve(cachedBlocks)
|
|
: getApisInfoAsync().then(function (info) { return cachedBlocks = pxtc.getBlocksInfo(info); });
|
|
}
|
|
exports.getBlocksAsync = getBlocksAsync;
|
|
function newProject() {
|
|
firstTypecheck = null;
|
|
cachedApis = null;
|
|
cachedBlocks = null;
|
|
workerOpAsync("reset", {}).done();
|
|
}
|
|
exports.newProject = newProject;
|
|
|
|
},{"./core":10,"./package":24,"./workeriface":36}],9:[function(require,module,exports){
|
|
/// <reference path="../../typings/globals/react/index.d.ts" />
|
|
/// <reference path="../../typings/globals/react-dom/index.d.ts" />
|
|
/// <reference path="../../built/pxtlib.d.ts" />
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var data = require("./data");
|
|
var sui = require("./sui");
|
|
var DocsMenuItem = (function (_super) {
|
|
__extends(DocsMenuItem, _super);
|
|
function DocsMenuItem(props) {
|
|
_super.call(this, props);
|
|
}
|
|
DocsMenuItem.prototype.openDoc = function (path) {
|
|
pxt.tickEvent("docs", { path: path });
|
|
this.props.parent.setSideDoc(path);
|
|
};
|
|
DocsMenuItem.prototype.openTutorial = function (path) {
|
|
pxt.tickEvent("docstutorial", { path: path });
|
|
this.props.parent.startTutorial(path);
|
|
};
|
|
DocsMenuItem.prototype.render = function () {
|
|
var _this = this;
|
|
var targetTheme = pxt.appTarget.appTheme;
|
|
var sideDocs = !(pxt.shell.isSandboxMode() || pxt.options.light || targetTheme.hideSideDocs);
|
|
return React.createElement(sui.DropdownMenuItem, {icon: "help circle large", class: "help-dropdown-menuitem", textClass: "landscape only", title: lf("Reference, lessons, ...")}, targetTheme.docMenu.map(function (m) {
|
|
return !m.tutorial ? React.createElement("a", {href: m.path, target: "docs", key: "docsmenu" + m.path, role: "menuitem", title: m.name, className: "ui item " + (sideDocs && !/^https?:/i.test(m.path) ? "widedesktop hide" : "")}, m.name)
|
|
: React.createElement(sui.Item, {key: "docsmenututorial" + m.path, role: "menuitem", text: m.name, class: "", onClick: function () { return _this.openTutorial(m.path); }});
|
|
}), sideDocs ? targetTheme.docMenu.filter(function (m) { return !/^https?:/i.test(m.path) && !m.tutorial; }).map(function (m) { return React.createElement(sui.Item, {key: "docsmenuwide" + m.path, role: "menuitem", text: m.name, class: "widedesktop only", onClick: function () { return _this.openDoc(m.path); }}); }) : undefined);
|
|
};
|
|
return DocsMenuItem;
|
|
}(data.Component));
|
|
exports.DocsMenuItem = DocsMenuItem;
|
|
var SideDocs = (function (_super) {
|
|
__extends(SideDocs, _super);
|
|
function SideDocs(props) {
|
|
_super.call(this, props);
|
|
}
|
|
SideDocs.notify = function (message) {
|
|
var sd = document.getElementById("sidedocs");
|
|
if (sd && sd.contentWindow)
|
|
sd.contentWindow.postMessage(message, "*");
|
|
};
|
|
SideDocs.prototype.setPath = function (path) {
|
|
var docsUrl = pxt.webConfig.docsUrl || '/--docs';
|
|
var mode = this.props.parent.isBlocksEditor() ? "blocks" : "js";
|
|
var url = docsUrl + "#doc:" + path + ":" + mode + ":" + pxt.Util.localeInfo();
|
|
this.setUrl(url);
|
|
};
|
|
SideDocs.prototype.setMarkdown = function (md) {
|
|
var docsUrl = pxt.webConfig.docsUrl || '/--docs';
|
|
var mode = this.props.parent.isBlocksEditor() ? "blocks" : "js";
|
|
var url = docsUrl + "#md:" + encodeURIComponent(md) + ":" + mode + ":" + pxt.Util.localeInfo();
|
|
this.setUrl(url);
|
|
};
|
|
SideDocs.prototype.setUrl = function (url) {
|
|
var el = document.getElementById("sidedocs");
|
|
if (el)
|
|
el.src = url;
|
|
else
|
|
this.props.parent.setState({ sideDocsLoadUrl: url });
|
|
this.props.parent.setState({ sideDocsCollapsed: false });
|
|
};
|
|
SideDocs.prototype.collapse = function () {
|
|
this.props.parent.setState({ sideDocsCollapsed: true });
|
|
};
|
|
SideDocs.prototype.popOut = function () {
|
|
SideDocs.notify({
|
|
type: "popout"
|
|
});
|
|
};
|
|
SideDocs.prototype.toggleVisibility = function () {
|
|
var state = this.props.parent.state;
|
|
this.props.parent.setState({ sideDocsCollapsed: !state.sideDocsCollapsed });
|
|
};
|
|
SideDocs.prototype.componentDidUpdate = function () {
|
|
this.props.parent.editor.resize();
|
|
};
|
|
SideDocs.prototype.renderCore = function () {
|
|
var _this = this;
|
|
var state = this.props.parent.state;
|
|
var docsUrl = state.sideDocsLoadUrl;
|
|
if (!docsUrl)
|
|
return null;
|
|
var icon = !docsUrl || state.sideDocsCollapsed ? "expand" : "compress";
|
|
return React.createElement("div", null, React.createElement("iframe", {id: "sidedocs", src: docsUrl, role: "complementary", sandbox: "allow-scripts allow-same-origin allow-popups"}), React.createElement("button", {id: "sidedocspopout", role: "button", title: lf("Open documentation in new tab"), className: "circular ui icon button " + (state.sideDocsCollapsed ? "hidden" : ""), onClick: function () { return _this.popOut(); }}, React.createElement("i", {className: "external icon"})), React.createElement("button", {id: "sidedocsexpand", role: "button", title: lf("Show/Hide side documentation"), className: "circular ui icon button", onClick: function () { return _this.toggleVisibility(); }}, React.createElement("i", {className: icon + " icon"})));
|
|
};
|
|
return SideDocs;
|
|
}(data.Component));
|
|
exports.SideDocs = SideDocs;
|
|
|
|
},{"./data":11,"./sui":32,"react":264}],10:[function(require,module,exports){
|
|
/// <reference path="../../typings/globals/react/index.d.ts" />
|
|
/// <reference path="../../typings/globals/react-dom/index.d.ts" />
|
|
/// <reference path="../../built/pxtlib.d.ts" />
|
|
"use strict";
|
|
var ReactDOM = require("react-dom");
|
|
var Cloud = pxt.Cloud;
|
|
var Util = pxt.Util;
|
|
var lf = Util.lf;
|
|
function hideLoading() {
|
|
$('.ui.page.dimmer .loadingcontent').remove();
|
|
$('body.main').dimmer('hide');
|
|
}
|
|
exports.hideLoading = hideLoading;
|
|
function showLoading(msg) {
|
|
$('body.main').dimmer({
|
|
closable: false
|
|
});
|
|
$('body.main').dimmer('show');
|
|
$('.ui.page.dimmer').html("\n <div class=\"content loadingcontent\">\n <div class=\"ui text large loader msg\">{lf(\"Please wait\")}</div>\n </div>\n");
|
|
$('.ui.page.dimmer .msg').text(msg);
|
|
}
|
|
exports.showLoading = showLoading;
|
|
var asyncLoadingTimeout;
|
|
function showLoadingAsync(msg, operation, delay) {
|
|
if (delay === void 0) { delay = 700; }
|
|
clearTimeout(asyncLoadingTimeout);
|
|
asyncLoadingTimeout = setTimeout(function () {
|
|
showLoading(msg);
|
|
}, delay);
|
|
return operation.finally(function () {
|
|
cancelAsyncLoading();
|
|
});
|
|
}
|
|
exports.showLoadingAsync = showLoadingAsync;
|
|
function cancelAsyncLoading() {
|
|
clearTimeout(asyncLoadingTimeout);
|
|
hideLoading();
|
|
}
|
|
exports.cancelAsyncLoading = cancelAsyncLoading;
|
|
function navigateInWindow(url) {
|
|
window.location.href = url;
|
|
}
|
|
exports.navigateInWindow = navigateInWindow;
|
|
function findChild(c, selector) {
|
|
var self = $(ReactDOM.findDOMNode(c));
|
|
if (!selector)
|
|
return self;
|
|
return self.find(selector);
|
|
}
|
|
exports.findChild = findChild;
|
|
function parseQueryString(qs) {
|
|
var r = {};
|
|
qs.replace(/\+/g, " ").replace(/([^&=]+)=?([^&]*)/g, function (f, k, v) {
|
|
r[decodeURIComponent(k)] = decodeURIComponent(v);
|
|
return "";
|
|
});
|
|
return r;
|
|
}
|
|
exports.parseQueryString = parseQueryString;
|
|
var lastTime = {};
|
|
function htmlmsg(kind, msg) {
|
|
var now = Date.now();
|
|
var prev = lastTime[kind] || 0;
|
|
if (now - prev < 100)
|
|
$('#' + kind + 'msg').text(msg);
|
|
else {
|
|
lastTime[kind] = now;
|
|
$('#' + kind + 'msg').finish().text(msg).fadeIn('fast').delay(3000).fadeOut('slow');
|
|
}
|
|
}
|
|
function errorNotification(msg) {
|
|
pxt.tickEvent("notification.error", { message: msg });
|
|
htmlmsg("err", msg);
|
|
}
|
|
exports.errorNotification = errorNotification;
|
|
function warningNotification(msg) {
|
|
pxt.log("warning: " + msg);
|
|
htmlmsg("warn", msg);
|
|
}
|
|
exports.warningNotification = warningNotification;
|
|
function infoNotification(msg) {
|
|
pxt.debug(msg);
|
|
htmlmsg("info", msg);
|
|
}
|
|
exports.infoNotification = infoNotification;
|
|
function handleNetworkError(e, ignoredCodes) {
|
|
var statusCode = parseInt(e.statusCode);
|
|
if (e.isOffline || statusCode === 0) {
|
|
warningNotification(lf("Network request failed; you appear to be offline"));
|
|
}
|
|
else if (!isNaN(statusCode) && statusCode !== 200) {
|
|
if (ignoredCodes && ignoredCodes.indexOf(statusCode) !== -1) {
|
|
return e;
|
|
}
|
|
warningNotification(lf("Network request failed"));
|
|
}
|
|
throw e;
|
|
}
|
|
exports.handleNetworkError = handleNetworkError;
|
|
function dialogAsync(options) {
|
|
var buttons = options.buttons ? options.buttons.filter(function (b) { return !!b; }) : undefined;
|
|
var logos = (options.logos || [])
|
|
.filter(function (logo) { return !!logo; })
|
|
.map(function (logo) { return ("<img class=\"ui logo\" src=\"" + Util.toDataUri(logo) + "\" />"); })
|
|
.join(' ');
|
|
var html = "\n <div class=\"ui " + (options.size || "small") + " modal\">\n <div class=\"header\">\n " + Util.htmlEscape(options.header) + "\n </div>\n <div class=\"content\">\n " + (options.body ? "<p>" + Util.htmlEscape(options.body) + "</p>" : "") + "\n " + (options.htmlBody || "") + "\n " + (options.copyable ? "<div class=\"ui fluid action input\">\n <input class=\"linkinput\" readonly spellcheck=\"false\" type=\"text\" value=\"" + Util.htmlEscape(options.copyable) + "\">\n <button class=\"ui teal right labeled icon button copybtn\" data-content=\"" + lf("Copied!") + "\">\n " + lf("Copy") + "\n <i class=\"copy icon\"></i>\n </button>\n </div>" : "") + "\n </div>";
|
|
html += "<div class=\"actions\">";
|
|
html += logos;
|
|
if (!options.hideCancel) {
|
|
buttons.push({
|
|
label: options.disagreeLbl || lf("Cancel"),
|
|
class: options.disagreeClass || "cancel",
|
|
icon: options.disagreeIcon || "cancel"
|
|
});
|
|
}
|
|
var btnno = 0;
|
|
for (var _i = 0, buttons_1 = buttons; _i < buttons_1.length; _i++) {
|
|
var b = buttons_1[_i];
|
|
html += "\n <" + (b.url ? "a" : "button") + " class=\"ui right labeled icon button approve " + (b.class || "positive") + "\" data-btnid=\"" + btnno++ + "\" " + (b.url ? "href=\"" + b.url + "\"" : "") + " " + (b.fileName ? "download=\"" + Util.htmlEscape(b.fileName) + "\"" : '') + " target=\"_blank\">\n " + Util.htmlEscape(b.label) + "\n <i class=\"" + (b.icon || "checkmark") + " icon\"></i>\n </" + (b.url ? "a" : "button") + ">";
|
|
}
|
|
html += "</div>";
|
|
html += "</div>";
|
|
var modal = $(html);
|
|
if (options.copyable)
|
|
enableCopyable(modal);
|
|
var done = false;
|
|
$('#root').append(modal);
|
|
if (options.onLoaded)
|
|
options.onLoaded(modal);
|
|
modal.find('img').on('load', function () {
|
|
modal.modal('refresh');
|
|
});
|
|
modal.find(".ui.accordion").accordion();
|
|
return new Promise(function (resolve, reject) {
|
|
var mo;
|
|
var timer = options.timeout ? setTimeout(function () {
|
|
timer = 0;
|
|
mo.modal("hide");
|
|
}, options.timeout) : 0;
|
|
var onfinish = function (elt) {
|
|
if (!done) {
|
|
done = true;
|
|
if (timer)
|
|
clearTimeout(timer);
|
|
var id = elt.attr("data-btnid");
|
|
if (id) {
|
|
var btn = buttons[+id];
|
|
if (btn.onclick)
|
|
return resolve(btn.onclick());
|
|
}
|
|
return resolve();
|
|
}
|
|
};
|
|
mo = modal.modal({
|
|
observeChanges: true,
|
|
closeable: !options.hideCancel,
|
|
context: "#root",
|
|
onHidden: function () {
|
|
modal.remove();
|
|
mo.remove();
|
|
},
|
|
onApprove: onfinish,
|
|
onDeny: onfinish,
|
|
onHide: function () {
|
|
if (!done) {
|
|
done = true;
|
|
if (timer)
|
|
clearTimeout(timer);
|
|
resolve();
|
|
}
|
|
},
|
|
});
|
|
mo.modal("show");
|
|
});
|
|
}
|
|
exports.dialogAsync = dialogAsync;
|
|
function hideDialog() {
|
|
$('.modal').modal("hide");
|
|
}
|
|
exports.hideDialog = hideDialog;
|
|
function confirmAsync(options) {
|
|
if (!options.buttons)
|
|
options.buttons = [];
|
|
var result = 0;
|
|
if (!options.hideAgree) {
|
|
options.buttons.push({
|
|
label: options.agreeLbl || lf("Go ahead!"),
|
|
class: options.agreeClass,
|
|
icon: options.agreeIcon,
|
|
onclick: function () {
|
|
result = 1;
|
|
}
|
|
});
|
|
}
|
|
if (options.deleteLbl) {
|
|
options.buttons.push({
|
|
label: options.deleteLbl,
|
|
class: "delete red",
|
|
icon: "trash",
|
|
onclick: function () {
|
|
result = 2;
|
|
}
|
|
});
|
|
}
|
|
return dialogAsync(options)
|
|
.then(function () { return result; });
|
|
}
|
|
exports.confirmAsync = confirmAsync;
|
|
function confirmDelete(what, cb) {
|
|
confirmAsync({
|
|
header: lf("Would you like to delete '{0}'?", what),
|
|
body: lf("It will be deleted for good. No undo."),
|
|
agreeLbl: lf("Delete"),
|
|
agreeClass: "red",
|
|
agreeIcon: "trash",
|
|
}).then(function (res) {
|
|
if (res) {
|
|
cb().done();
|
|
}
|
|
}).done();
|
|
}
|
|
exports.confirmDelete = confirmDelete;
|
|
function promptAsync(options) {
|
|
if (!options.buttons)
|
|
options.buttons = [];
|
|
var result = options.defaultValue;
|
|
if (!options.hideAgree) {
|
|
options.buttons.push({
|
|
label: options.agreeLbl || lf("Go ahead!"),
|
|
class: options.agreeClass,
|
|
icon: options.agreeIcon,
|
|
onclick: function () {
|
|
var dialogInput = document.getElementById('promptDialogInput');
|
|
result = dialogInput.value;
|
|
}
|
|
});
|
|
}
|
|
options.htmlBody = "<div class=\"ui fluid icon input\">\n <input type=\"text\" id=\"promptDialogInput\" placeholder=\"" + options.defaultValue + "\">\n </div>";
|
|
options.onLoaded = function () {
|
|
var dialogInput = document.getElementById('promptDialogInput');
|
|
if (dialogInput) {
|
|
dialogInput.focus();
|
|
}
|
|
};
|
|
return dialogAsync(options)
|
|
.then(function () { return result; });
|
|
}
|
|
exports.promptAsync = promptAsync;
|
|
function shareLinkAsync(options) {
|
|
var html = "\n <div class=\"ui small modal\">\n <div class=\"header\">\n " + Util.htmlEscape(options.header) + "\n </div>\n <div class=\"content\">\n <p>" + Util.htmlEscape(options.body || "") + "</p>\n <div class=\"ui fluid action input\">\n <input class=\"linkinput\" readonly spellcheck=\"false\" type=\"text\" value=\"" + Util.htmlEscape(options.link) + "\">\n <button class=\"ui teal right labeled icon button copybtn\" data-content=\"" + lf("Copied!") + "\">\n " + lf("Copy") + "\n <i class=\"copy icon\"></i>\n </button>\n </div>\n </div>\n <div class=\"actions\">\n <div class=\"ui approve right labeled icon button " + (options.okClass || "teal") + "\">\n " + Util.htmlEscape(options.okLabel || lf("Ok")) + "\n <i class=\"" + (options.okIcon || "checkmark") + " icon\"></i>\n </div>\n </div>\n </div>\n ";
|
|
var modal = $(html);
|
|
enableCopyable(modal);
|
|
var done = false;
|
|
$('body.main').append(modal);
|
|
return new Promise(function (resolve, reject) {
|
|
return modal.modal({
|
|
onHidden: function () {
|
|
modal.remove();
|
|
},
|
|
onHide: function () {
|
|
if (!done) {
|
|
done = true;
|
|
resolve(false);
|
|
}
|
|
},
|
|
}).modal("show");
|
|
});
|
|
}
|
|
exports.shareLinkAsync = shareLinkAsync;
|
|
function enableCopyable(modal) {
|
|
var btn = modal.find('.copybtn');
|
|
btn.click(function () {
|
|
try {
|
|
var inp = modal.find('.linkinput')[0];
|
|
inp.focus();
|
|
inp.setSelectionRange(0, inp.value.length);
|
|
document.execCommand("copy");
|
|
btn.popup({
|
|
on: "manual",
|
|
inline: true
|
|
});
|
|
btn.popup("show");
|
|
}
|
|
catch (e) {
|
|
}
|
|
});
|
|
}
|
|
function scrollIntoView(item, margin) {
|
|
if (margin === void 0) { margin = 0; }
|
|
if (!item.length)
|
|
return;
|
|
var parent = item.offsetParent();
|
|
var itemTop = Math.max(item.position().top - margin, 0);
|
|
var itemBottom = item.position().top + item.outerHeight(true) + margin;
|
|
var selfTop = $(parent).scrollTop();
|
|
var selfH = $(parent).height();
|
|
var newTop = selfTop;
|
|
if (itemTop < selfTop) {
|
|
newTop = itemTop;
|
|
}
|
|
else if (itemBottom > selfTop + selfH) {
|
|
newTop = itemBottom - selfH;
|
|
}
|
|
if (newTop != selfTop) {
|
|
parent.scrollTop(newTop);
|
|
}
|
|
}
|
|
exports.scrollIntoView = scrollIntoView;
|
|
// for JavaScript console
|
|
function apiAsync(path, data) {
|
|
return (data ?
|
|
Cloud.privatePostAsync(path, data) :
|
|
Cloud.privateGetAsync(path))
|
|
.then(function (resp) {
|
|
console.log("*");
|
|
console.log("*******", path, "--->");
|
|
console.log("*");
|
|
console.log(resp);
|
|
console.log("*");
|
|
return resp;
|
|
}, function (err) {
|
|
console.log(err.message);
|
|
});
|
|
}
|
|
exports.apiAsync = apiAsync;
|
|
|
|
},{"react-dom":135}],11:[function(require,module,exports){
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var core = require("./core");
|
|
var gallery = require("./gallery");
|
|
var Cloud = pxt.Cloud;
|
|
var Util = pxt.Util;
|
|
var virtualApis = {};
|
|
var targetConfig = undefined;
|
|
mountVirtualApi("cloud", {
|
|
getAsync: function (p) { return Cloud.privateGetAsync(stripProtocol(p)).catch(core.handleNetworkError); },
|
|
expirationTime: function (p) { return 60 * 1000; },
|
|
isOffline: function () { return !Cloud.isOnline(); },
|
|
});
|
|
mountVirtualApi("cloud-search", {
|
|
getAsync: function (p) { return Cloud.privateGetAsync(stripProtocol(p)).catch(function (e) { return core.handleNetworkError(e, [404]); }); },
|
|
expirationTime: function (p) { return 60 * 1000; },
|
|
isOffline: function () { return !Cloud.isOnline(); },
|
|
});
|
|
mountVirtualApi("gallery", {
|
|
getAsync: function (p) { return gallery.loadGalleryAsync(stripProtocol(p)).catch(core.handleNetworkError); },
|
|
expirationTime: function (p) { return 3600 * 1000; },
|
|
isOffline: function () { return !Cloud.isOnline(); }
|
|
});
|
|
mountVirtualApi("td-cloud", {
|
|
getAsync: function (p) {
|
|
return Util.httpGetJsonAsync("https://www.touchdevelop.com/api/" + stripProtocol(p))
|
|
.catch(core.handleNetworkError);
|
|
},
|
|
expirationTime: function (p) { return 60 * 1000; },
|
|
});
|
|
mountVirtualApi("gh-search", {
|
|
getAsync: function (query) { return pxt.targetConfigAsync()
|
|
.then(function (config) { return pxt.github.searchAsync(stripProtocol(query), config ? config.packages : undefined); })
|
|
.catch(core.handleNetworkError); },
|
|
expirationTime: function (p) { return 60 * 1000; },
|
|
isOffline: function () { return !Cloud.isOnline(); },
|
|
});
|
|
mountVirtualApi("gh-pkgcfg", {
|
|
getAsync: function (query) {
|
|
return pxt.github.pkgConfigAsync(stripProtocol(query)).catch(core.handleNetworkError);
|
|
},
|
|
expirationTime: function (p) { return 60 * 1000; },
|
|
isOffline: function () { return !Cloud.isOnline(); },
|
|
});
|
|
var cachedData = {};
|
|
function subscribe(component, path) {
|
|
var e = lookup(path);
|
|
var lst = e.components;
|
|
if (lst.indexOf(component) < 0) {
|
|
lst.push(component);
|
|
component.subscriptions.push(e);
|
|
}
|
|
}
|
|
function unsubscribe(component) {
|
|
var lst = component.subscriptions;
|
|
if (lst.length == 0)
|
|
return;
|
|
component.subscriptions = [];
|
|
lst.forEach(function (ce) {
|
|
var idx = ce.components.indexOf(component);
|
|
if (idx >= 0)
|
|
ce.components.splice(idx, 1);
|
|
});
|
|
}
|
|
function expired(ce) {
|
|
if (!ce.api.expirationTime)
|
|
return ce.data != null;
|
|
return ce.data == null || (Date.now() - ce.lastRefresh) > ce.api.expirationTime(ce.path);
|
|
}
|
|
function shouldCache(ce) {
|
|
if (!ce.data)
|
|
return false;
|
|
return /^cloud:(me\/settings|ptr-pkg-)/.test(ce.path);
|
|
}
|
|
function clearCache() {
|
|
cachedData = {};
|
|
saveCache();
|
|
}
|
|
exports.clearCache = clearCache;
|
|
function loadCache() {
|
|
JSON.parse(pxt.storage.getLocal("apiCache2") || "[]").forEach(function (e) {
|
|
var ce = lookup(e.path);
|
|
ce.data = e.data;
|
|
});
|
|
}
|
|
function saveCache() {
|
|
var obj = Util.values(cachedData).filter(function (e) { return shouldCache(e); }).map(function (e) {
|
|
return {
|
|
path: e.path,
|
|
data: e.data
|
|
};
|
|
});
|
|
pxt.storage.setLocal("apiCache2", JSON.stringify(obj));
|
|
}
|
|
function matches(ce, prefix) {
|
|
return ce.path.slice(0, prefix.length) == prefix;
|
|
}
|
|
function notify(ce) {
|
|
if (shouldCache(ce))
|
|
saveCache();
|
|
var lst = ce.callbackOnce;
|
|
if (lst.length > 0) {
|
|
ce.callbackOnce = [];
|
|
Util.nextTick(function () { return lst.forEach(function (f) { return f(); }); });
|
|
}
|
|
if (ce.components.length > 0)
|
|
Util.nextTick(function () { return ce.components.forEach(function (c) { return c.forceUpdate(); }); });
|
|
}
|
|
function getVirtualApi(path) {
|
|
var m = /^([\w\-]+):/.exec(path);
|
|
if (!m || !virtualApis[m[1]])
|
|
Util.oops("bad data protocol: " + path);
|
|
return virtualApis[m[1]];
|
|
}
|
|
function queue(ce) {
|
|
if (ce.queued)
|
|
return;
|
|
if (ce.api.isOffline && ce.api.isOffline())
|
|
return;
|
|
ce.queued = true;
|
|
var final = function (res) {
|
|
ce.data = res;
|
|
ce.lastRefresh = Date.now();
|
|
ce.queued = false;
|
|
notify(ce);
|
|
};
|
|
if (ce.api.isSync)
|
|
final(ce.api.getSync(ce.path));
|
|
else
|
|
ce.api.getAsync(ce.path).done(final);
|
|
}
|
|
function lookup(path) {
|
|
if (!cachedData.hasOwnProperty(path))
|
|
cachedData[path] = {
|
|
path: path,
|
|
data: null,
|
|
lastRefresh: 0,
|
|
queued: false,
|
|
callbackOnce: [],
|
|
components: [],
|
|
api: getVirtualApi(path)
|
|
};
|
|
return cachedData[path];
|
|
}
|
|
function getCached(component, path) {
|
|
subscribe(component, path);
|
|
var r = lookup(path);
|
|
if (r.api.isSync)
|
|
return r.api.getSync(r.path);
|
|
if (expired(r))
|
|
queue(r);
|
|
return r.data;
|
|
}
|
|
function mountVirtualApi(protocol, handler) {
|
|
Util.assert(!virtualApis[protocol]);
|
|
Util.assert(!!handler.getSync || !!handler.getAsync);
|
|
Util.assert(!!handler.getSync != !!handler.getAsync);
|
|
handler.isSync = !!handler.getSync;
|
|
virtualApis[protocol] = handler;
|
|
}
|
|
exports.mountVirtualApi = mountVirtualApi;
|
|
function stripProtocol(path) {
|
|
var m = /^([\w\-]+):(.*)/.exec(path);
|
|
if (m)
|
|
return m[2];
|
|
else
|
|
Util.oops("protocol missing in: " + path);
|
|
return path;
|
|
}
|
|
exports.stripProtocol = stripProtocol;
|
|
function invalidate(prefix) {
|
|
Util.values(cachedData).forEach(function (ce) {
|
|
if (matches(ce, prefix)) {
|
|
ce.lastRefresh = 0;
|
|
if (ce.components.length > 0)
|
|
queue(lookup(ce.path));
|
|
}
|
|
});
|
|
}
|
|
exports.invalidate = invalidate;
|
|
function getAsync(path) {
|
|
var ce = lookup(path);
|
|
if (ce.api.isSync)
|
|
return Promise.resolve(ce.api.getSync(ce.path));
|
|
if (!Cloud.isOnline() || !expired(ce))
|
|
return Promise.resolve(ce.data);
|
|
return new Promise(function (resolve, reject) {
|
|
ce.callbackOnce.push(function () {
|
|
resolve(ce.data);
|
|
});
|
|
queue(ce);
|
|
});
|
|
}
|
|
exports.getAsync = getAsync;
|
|
var Component = (function (_super) {
|
|
__extends(Component, _super);
|
|
function Component(props) {
|
|
_super.call(this, props);
|
|
this.subscriptions = [];
|
|
this.renderCoreOk = false;
|
|
this.state = {};
|
|
}
|
|
Component.prototype.getData = function (path) {
|
|
if (!this.renderCoreOk)
|
|
Util.oops("Override renderCore() not render()");
|
|
return getCached(this, path);
|
|
};
|
|
Component.prototype.componentWillUnmount = function () {
|
|
unsubscribe(this);
|
|
};
|
|
Component.prototype.child = function (selector) {
|
|
return core.findChild(this, selector);
|
|
};
|
|
Component.prototype.renderCore = function () {
|
|
return null;
|
|
};
|
|
Component.prototype.render = function () {
|
|
unsubscribe(this);
|
|
this.renderCoreOk = true;
|
|
return this.renderCore();
|
|
};
|
|
return Component;
|
|
}(React.Component));
|
|
exports.Component = Component;
|
|
loadCache();
|
|
|
|
},{"./core":10,"./gallery":18,"react":264}],12:[function(require,module,exports){
|
|
"use strict";
|
|
var Promise = require("bluebird");
|
|
window.Promise = Promise;
|
|
var PouchDB = require("pouchdb");
|
|
require('pouchdb/extras/memory');
|
|
Promise.config({
|
|
// Enables all warnings except forgotten return statements.
|
|
warnings: {
|
|
wForgottenReturn: false
|
|
}
|
|
});
|
|
var _db = undefined;
|
|
var inMemory = false;
|
|
function memoryDb() {
|
|
pxt.debug('db: in memory...');
|
|
inMemory = true;
|
|
_db = new PouchDB("pxt-" + pxt.storage.storageId(), {
|
|
adapter: 'memory'
|
|
});
|
|
return Promise.resolve(_db);
|
|
}
|
|
function getDbAsync() {
|
|
if (_db)
|
|
return Promise.resolve(_db);
|
|
if (pxt.shell.isSandboxMode() || pxt.shell.isReadOnly())
|
|
return memoryDb();
|
|
var temp = new PouchDB("pxt-" + pxt.storage.storageId(), { revs_limit: 2 });
|
|
return temp.get('pouchdbsupportabletest')
|
|
.catch(function (error) {
|
|
if (error && error.error && error.name == 'indexed_db_went_bad') {
|
|
return memoryDb();
|
|
}
|
|
else {
|
|
_db = temp;
|
|
return Promise.resolve(_db);
|
|
}
|
|
});
|
|
}
|
|
exports.getDbAsync = getDbAsync;
|
|
function destroyAsync() {
|
|
return !_db ? Promise.resolve() : _db.destroy();
|
|
}
|
|
exports.destroyAsync = destroyAsync;
|
|
var Table = (function () {
|
|
function Table(name) {
|
|
this.name = name;
|
|
}
|
|
Table.prototype.getAsync = function (id) {
|
|
var _this = this;
|
|
return getDbAsync().then(function (db) { return db.get(_this.name + "--" + id); }).then(function (v) {
|
|
v.id = id;
|
|
return v;
|
|
});
|
|
};
|
|
Table.prototype.getAllAsync = function () {
|
|
var _this = this;
|
|
return getDbAsync().then(function (db) { return db.allDocs({
|
|
include_docs: true,
|
|
startkey: _this.name + "--",
|
|
endkey: _this.name + "--\uffff"
|
|
}); }).then(function (resp) { return resp.rows.map(function (e) { return e.doc; }); });
|
|
};
|
|
Table.prototype.deleteAsync = function (obj) {
|
|
return getDbAsync().then(function (db) { return db.remove(obj); });
|
|
};
|
|
Table.prototype.forceSetAsync = function (obj) {
|
|
var _this = this;
|
|
return this.getAsync(obj.id)
|
|
.then(function (o) {
|
|
obj._rev = o._rev;
|
|
return _this.setAsync(obj);
|
|
}, function (e) { return _this.setAsync(obj); });
|
|
};
|
|
Table.prototype.setAsync = function (obj) {
|
|
if (obj.id && !obj._id)
|
|
obj._id = this.name + "--" + obj.id;
|
|
return getDbAsync().then(function (db) { return db.put(obj); }).then(function (resp) { return resp.rev; });
|
|
};
|
|
return Table;
|
|
}());
|
|
exports.Table = Table;
|
|
|
|
},{"bluebird":40,"pouchdb":120,"pouchdb/extras/memory":118}],13:[function(require,module,exports){
|
|
"use strict";
|
|
function setupDragAndDrop(r, filter, dragged) {
|
|
var dragAndDrop = document && document.createElement && 'draggable' in document.createElement('span');
|
|
r.addEventListener('paste', function (e) {
|
|
if (e.clipboardData) {
|
|
// has file?
|
|
var files = Util.toArray(e.clipboardData.files).filter(filter);
|
|
if (files.length > 0) {
|
|
e.stopPropagation(); // Stops some browsers from redirecting.
|
|
e.preventDefault();
|
|
dragged(files);
|
|
}
|
|
else if (e.clipboardData.items && e.clipboardData.items.length > 0) {
|
|
var f = e.clipboardData.items[0].getAsFile();
|
|
if (f) {
|
|
e.stopPropagation(); // Stops some browsers from redirecting.
|
|
e.preventDefault();
|
|
dragged([f]);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
r.addEventListener('dragover', function (e) {
|
|
var types = e.dataTransfer.types;
|
|
var found = false;
|
|
for (var i = 0; i < types.length; ++i)
|
|
if (types[i] == "Files")
|
|
found = true;
|
|
if (found) {
|
|
if (e.preventDefault)
|
|
e.preventDefault(); // Necessary. Allows us to drop.
|
|
e.dataTransfer.dropEffect = 'copy'; // See the section on the DataTransfer object.
|
|
return false;
|
|
}
|
|
return true;
|
|
}, false);
|
|
r.addEventListener('drop', function (e) {
|
|
var files = Util.toArray(e.dataTransfer.files);
|
|
if (files.length > 0) {
|
|
e.stopPropagation(); // Stops some browsers from redirecting.
|
|
e.preventDefault();
|
|
dragged(files);
|
|
}
|
|
return false;
|
|
}, false);
|
|
r.addEventListener('dragend', function (e) {
|
|
return false;
|
|
}, false);
|
|
}
|
|
exports.setupDragAndDrop = setupDragAndDrop;
|
|
|
|
},{}],14:[function(require,module,exports){
|
|
/// <reference path="../../typings/globals/react/index.d.ts" />
|
|
/// <reference path="../../typings/globals/react-dom/index.d.ts" />
|
|
/// <reference path="../../built/pxtlib.d.ts" />
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var data = require("./data");
|
|
var sui = require("./sui");
|
|
var EditorToolbar = (function (_super) {
|
|
__extends(EditorToolbar, _super);
|
|
function EditorToolbar(props) {
|
|
_super.call(this, props);
|
|
}
|
|
EditorToolbar.prototype.saveProjectName = function (name, view) {
|
|
pxt.tickEvent("editortools.projectrename", { view: view });
|
|
this.props.parent.updateHeaderName(name);
|
|
};
|
|
EditorToolbar.prototype.compile = function (view) {
|
|
pxt.tickEvent("editortools.download", { view: view, collapsed: this.getCollapsedState() });
|
|
this.props.parent.compile();
|
|
};
|
|
EditorToolbar.prototype.saveFile = function (view) {
|
|
pxt.tickEvent("editortools.save", { view: view, collapsed: this.getCollapsedState() });
|
|
this.props.parent.saveAndCompile();
|
|
};
|
|
EditorToolbar.prototype.undo = function (view) {
|
|
pxt.tickEvent("editortools.undo", { view: view, collapsed: this.getCollapsedState() });
|
|
this.props.parent.editor.undo();
|
|
};
|
|
EditorToolbar.prototype.redo = function (view) {
|
|
pxt.tickEvent("editortools.redo", { view: view, collapsed: this.getCollapsedState() });
|
|
this.props.parent.editor.redo();
|
|
};
|
|
EditorToolbar.prototype.zoomIn = function (view) {
|
|
pxt.tickEvent("editortools.zoomIn", { view: view, collapsed: this.getCollapsedState() });
|
|
this.props.parent.editor.zoomIn();
|
|
};
|
|
EditorToolbar.prototype.zoomOut = function (view) {
|
|
pxt.tickEvent("editortools.zoomOut", { view: view, collapsed: this.getCollapsedState() });
|
|
this.props.parent.editor.zoomOut();
|
|
};
|
|
EditorToolbar.prototype.startStopSimulator = function (view) {
|
|
pxt.tickEvent("editortools.startStopSimulator", { view: view, collapsed: this.getCollapsedState(), headless: this.getHeadlessState() });
|
|
this.props.parent.startStopSimulator();
|
|
};
|
|
EditorToolbar.prototype.restartSimulator = function (view) {
|
|
pxt.tickEvent("editortools.restart", { view: view, collapsed: this.getCollapsedState(), headless: this.getHeadlessState() });
|
|
this.props.parent.restartSimulator();
|
|
};
|
|
EditorToolbar.prototype.toggleCollapse = function (view) {
|
|
pxt.tickEvent("editortools.toggleCollapse", { view: view, collapsedTo: '' + !this.props.parent.state.collapseEditorTools });
|
|
this.props.parent.toggleSimulatorCollapse();
|
|
};
|
|
EditorToolbar.prototype.getCollapsedState = function () {
|
|
return '' + this.props.parent.state.collapseEditorTools;
|
|
};
|
|
EditorToolbar.prototype.getHeadlessState = function () {
|
|
return pxt.appTarget.simulator.headless ? "true" : "false";
|
|
};
|
|
EditorToolbar.prototype.render = function () {
|
|
var _this = this;
|
|
var state = this.props.parent.state;
|
|
var sandbox = pxt.shell.isSandboxMode();
|
|
var readOnly = pxt.shell.isReadOnly();
|
|
var hideEditorFloats = state.hideEditorFloats;
|
|
var collapsed = state.hideEditorFloats || state.collapseEditorTools;
|
|
var isEditor = this.props.parent.isBlocksEditor() || this.props.parent.isTextEditor();
|
|
if (!isEditor)
|
|
return React.createElement("div", null);
|
|
var targetTheme = pxt.appTarget.appTheme;
|
|
var compile = pxt.appTarget.compile;
|
|
var compileBtn = compile.hasHex;
|
|
var simOpts = pxt.appTarget.simulator;
|
|
var make = !sandbox && state.showParts && simOpts && (simOpts.instructions || (simOpts.parts && pxt.options.debug));
|
|
var compileTooltip = lf("Download your code to the {0}", targetTheme.boardName);
|
|
var compileLoading = !!state.compiling;
|
|
var runTooltip = state.running ? lf("Stop the simulator") : lf("Start the simulator");
|
|
var makeTooltip = lf("Open assembly instructions");
|
|
var restartTooltip = lf("Restart the simulator");
|
|
var collapseTooltip = collapsed ? lf("Show the simulator") : lf("Hide the simulator");
|
|
var headless = pxt.appTarget.simulator.headless;
|
|
var hasUndo = this.props.parent.editor.hasUndo();
|
|
var hasRedo = this.props.parent.editor.hasRedo();
|
|
var run = true;
|
|
var restart = run && !simOpts.hideRestart;
|
|
return React.createElement("div", {className: "ui equal width grid right aligned padded"}, React.createElement("div", {className: "column mobile only"}, collapsed ?
|
|
React.createElement("div", {className: "ui equal width grid"}, React.createElement("div", {className: "left aligned column"}, React.createElement("div", {className: "ui icon small buttons"}, React.createElement(sui.Button, {icon: "" + (collapsed ? 'toggle up' : 'toggle down'), class: "collapse-button " + (hideEditorFloats ? 'disabled' : ''), title: collapseTooltip, onClick: function () { return _this.toggleCollapse('mobile'); }}), headless && run ? React.createElement(sui.Button, {class: "", key: 'runmenubtn', icon: state.running ? "stop" : "play", title: runTooltip, onClick: function () { return _this.startStopSimulator('mobile'); }}) : undefined, headless && restart ? React.createElement(sui.Button, {key: 'restartbtn', class: "restart-button", icon: "refresh", title: restartTooltip, onClick: function () { return _this.restartSimulator('mobile'); }}) : undefined, compileBtn ? React.createElement(sui.Button, {class: "primary download-button download-button-full " + (compileLoading ? 'loading' : ''), icon: "download", title: compileTooltip, onClick: function () { return _this.compile('mobile'); }}) : undefined)), readOnly ? undefined :
|
|
React.createElement("div", {className: "right aligned column"}, React.createElement("div", {className: "ui icon small buttons"}, React.createElement(sui.Button, {icon: 'save', class: "editortools-btn save-editortools-btn", title: lf("Save"), onClick: function () { return _this.saveFile('mobile'); }}), React.createElement(sui.Button, {icon: 'undo', class: "editortools-btn undo-editortools-btn} " + (!hasUndo ? 'disabled' : ''), title: lf("Undo"), onClick: function () { return _this.undo('mobile'); }}))), React.createElement("div", {className: "right aligned column"}, React.createElement("div", {className: "ui icon small buttons"}, React.createElement(sui.Button, {icon: 'zoom', class: "editortools-btn zoomin-editortools-btn", title: lf("Zoom In"), onClick: function () { return _this.zoomIn('mobile'); }}), React.createElement(sui.Button, {icon: 'zoom out', class: "editortools-btn zoomout-editortools-btn", title: lf("Zoom Out"), onClick: function () { return _this.zoomOut('mobile'); }})))) :
|
|
React.createElement("div", {className: "ui equal width grid"}, React.createElement("div", {className: "left aligned two wide column"}, React.createElement("div", {className: "ui vertical icon small buttons"}, run ? React.createElement(sui.Button, {class: "", key: 'runmenubtn', icon: state.running ? "stop" : "play", title: runTooltip, onClick: function () { return _this.startStopSimulator('mobile'); }}) : undefined, restart ? React.createElement(sui.Button, {key: 'restartbtn', class: "restart-button", icon: "refresh", title: restartTooltip, onClick: function () { return _this.restartSimulator('mobile'); }}) : undefined), React.createElement("div", {className: "row", style: { paddingTop: "1rem" }}, React.createElement("div", {className: "ui vertical icon small buttons"}, React.createElement(sui.Button, {icon: "" + (collapsed ? 'toggle up' : 'toggle down'), class: "collapse-button", title: collapseTooltip, onClick: function () { return _this.toggleCollapse('mobile'); }})))), React.createElement("div", {className: "three wide column"}), React.createElement("div", {className: "ui grid column"}, readOnly ? undefined :
|
|
React.createElement("div", {className: "row"}, React.createElement("div", {className: "column"}, React.createElement("div", {className: "ui icon large buttons"}, React.createElement(sui.Button, {icon: 'undo', class: "editortools-btn undo-editortools-btn} " + (!hasUndo ? 'disabled' : ''), title: lf("Undo"), onClick: function () { return _this.undo('mobile'); }})))), React.createElement("div", {className: "row", style: readOnly ? undefined : { paddingTop: 0 }}, React.createElement("div", {className: "column"}, React.createElement("div", {className: "ui icon large buttons"}, compileBtn ? React.createElement(sui.Button, {class: "primary download-button download-button-full " + (compileLoading ? 'loading' : ''), icon: "download", title: compileTooltip, onClick: function () { return _this.compile('mobile'); }}) : undefined)))))), React.createElement("div", {className: "column tablet only"}, collapsed ?
|
|
React.createElement("div", {className: "ui grid seven column"}, headless ?
|
|
React.createElement("div", {className: "left aligned six wide column"}, React.createElement("div", {className: "ui icon large buttons"}, React.createElement(sui.Button, {icon: "" + (collapsed ? 'toggle up' : 'toggle down'), class: "large collapse-button " + (hideEditorFloats ? 'disabled' : ''), title: collapseTooltip, onClick: function () { return _this.toggleCollapse('tablet'); }}), run ? React.createElement(sui.Button, {role: "menuitem", class: "large", key: 'runmenubtn', icon: state.running ? "stop" : "play", title: runTooltip, onClick: function () { return _this.startStopSimulator('tablet'); }}) : undefined, restart ? React.createElement(sui.Button, {key: 'restartbtn', class: "large restart-button", icon: "refresh", title: restartTooltip, onClick: function () { return _this.restartSimulator('tablet'); }}) : undefined, compileBtn ? React.createElement(sui.Button, {class: "primary large download-button download-button-full " + (compileLoading ? 'loading' : ''), icon: "download", title: compileTooltip, onClick: function () { return _this.compile('tablet'); }}) : undefined)) :
|
|
React.createElement("div", {className: "left aligned six wide column"}, React.createElement(sui.Button, {icon: "" + (collapsed ? 'toggle up' : 'toggle down'), class: "large collapse-button " + (hideEditorFloats ? 'disabled' : ''), title: collapseTooltip, onClick: function () { return _this.toggleCollapse('tablet'); }}), compileBtn ? React.createElement(sui.Button, {class: "primary large download-button download-button-full " + (compileLoading ? 'loading' : ''), icon: "download", text: lf("Download"), title: compileTooltip, onClick: function () { return _this.compile('tablet'); }}) : undefined), readOnly ? undefined :
|
|
React.createElement("div", {className: "column four wide"}, React.createElement(sui.Button, {icon: 'save', class: "large editortools-btn save-editortools-btn", title: lf("Save"), onClick: function () { return _this.saveFile('tablet'); }})), React.createElement("div", {className: "column six wide right aligned"}, readOnly ? undefined :
|
|
React.createElement("div", {className: "ui icon large buttons"}, React.createElement(sui.Button, {icon: 'undo', class: "editortools-btn undo-editortools-btn} " + (!hasUndo ? 'disabled' : ''), title: lf("Undo"), onClick: function () { return _this.undo('tablet'); }}), React.createElement(sui.Button, {icon: 'repeat', class: "editortools-btn redo-editortools-btn} " + (!hasRedo ? 'disabled' : ''), title: lf("Redo"), onClick: function () { return _this.redo('tablet'); }})), React.createElement("div", {className: "ui icon large buttons"}, React.createElement(sui.Button, {icon: 'zoom', class: "editortools-btn zoomin-editortools-btn", title: lf("Zoom In"), onClick: function () { return _this.zoomIn('tablet'); }}), React.createElement(sui.Button, {icon: 'zoom out', class: "editortools-btn zoomout-editortools-btn", title: lf("Zoom Out"), onClick: function () { return _this.zoomOut('tablet'); }}))))
|
|
: React.createElement("div", {className: "ui grid"}, React.createElement("div", {className: "left aligned two wide column"}, React.createElement("div", {className: "ui vertical icon small buttons"}, run ? React.createElement(sui.Button, {role: "menuitem", class: "", key: 'runmenubtn', icon: state.running ? "stop" : "play", title: runTooltip, onClick: function () { return _this.startStopSimulator('tablet'); }}) : undefined, restart ? React.createElement(sui.Button, {key: 'restartbtn', class: "restart-button", icon: "refresh", title: restartTooltip, onClick: function () { return _this.restartSimulator('tablet'); }}) : undefined), React.createElement("div", {className: "row", style: { paddingTop: "1rem" }}, React.createElement("div", {className: "ui vertical icon small buttons"}, React.createElement(sui.Button, {icon: "" + (collapsed ? 'toggle up' : 'toggle down'), class: "collapse-button", title: collapseTooltip, onClick: function () { return _this.toggleCollapse('tablet'); }})))), React.createElement("div", {className: "three wide column"}), React.createElement("div", {className: "five wide column"}, React.createElement("div", {className: "ui grid right aligned"}, compileBtn ? React.createElement("div", {className: "row"}, React.createElement("div", {className: "column"}, React.createElement(sui.Button, {role: "menuitem", class: "primary large fluid download-button download-button-full " + (compileLoading ? 'loading' : ''), icon: "download", text: lf("Download"), title: compileTooltip, onClick: function () { return _this.compile('tablet'); }}))) : undefined, readOnly ? undefined :
|
|
React.createElement("div", {className: "row", style: compileBtn ? { paddingTop: 0 } : {}}, React.createElement("div", {className: "column"}, React.createElement("div", {className: "ui item large right labeled fluid input projectname-input projectname-tablet", title: lf("Pick a name for your project")}, React.createElement("input", {id: "fileNameInput", type: "text", placeholder: lf("Pick a name..."), value: state.projectName || '', onChange: function (e) { return _this.saveProjectName(e.target.value, 'tablet'); }}), React.createElement(sui.Button, {icon: 'save', class: "large right attached editortools-btn save-editortools-btn", title: lf("Save"), onClick: function () { return _this.saveFile('tablet'); }})))))), React.createElement("div", {className: "six wide column right aligned"}, readOnly ? undefined :
|
|
React.createElement("div", {className: "ui icon large buttons"}, React.createElement(sui.Button, {icon: 'undo', class: "editortools-btn undo-editortools-btn} " + (!hasUndo ? 'disabled' : ''), title: lf("Undo"), onClick: function () { return _this.undo(); }}), React.createElement(sui.Button, {icon: 'repeat', class: "editortools-btn redo-editortools-btn} " + (!hasRedo ? 'disabled' : ''), title: lf("Redo"), onClick: function () { return _this.redo(); }})), React.createElement("div", {className: "ui icon large buttons"}, React.createElement(sui.Button, {icon: 'zoom', class: "editortools-btn zoomin-editortools-btn", title: lf("Zoom In"), onClick: function () { return _this.zoomIn(); }}), React.createElement(sui.Button, {icon: 'zoom out', class: "editortools-btn zoomout-editortools-btn", title: lf("Zoom Out"), onClick: function () { return _this.zoomOut(); }}))))), React.createElement("div", {className: "column computer only"}, React.createElement("div", {className: "ui grid equal width"}, React.createElement("div", {id: "downloadArea", className: "ui column items"}, headless && collapsed ?
|
|
React.createElement("div", {className: "ui item"}, React.createElement("div", {className: "ui icon large buttons"}, React.createElement(sui.Button, {icon: "" + (state.collapseEditorTools ? 'toggle right' : 'toggle left'), class: "large collapse-button", title: collapseTooltip, onClick: function () { return _this.toggleCollapse('computer'); }}), run ? React.createElement(sui.Button, {role: "menuitem", class: "large", key: 'runmenubtn', icon: state.running ? "stop" : "play", title: runTooltip, onClick: function () { return _this.startStopSimulator('tablet'); }}) : undefined, restart ? React.createElement(sui.Button, {key: 'restartbtn', class: "large restart-button", icon: "refresh", title: restartTooltip, onClick: function () { return _this.restartSimulator('tablet'); }}) : undefined, compileBtn ? React.createElement(sui.Button, {icon: 'icon download', class: "primary large download-button " + (compileLoading ? 'loading' : ''), title: compileTooltip, onClick: function () { return _this.compile('computer'); }}) : undefined)) :
|
|
React.createElement("div", {className: "ui item"}, React.createElement(sui.Button, {icon: "" + (state.collapseEditorTools ? 'toggle right' : 'toggle left'), class: "large collapse-button", title: collapseTooltip, onClick: function () { return _this.toggleCollapse('computer'); }}), compileBtn ? React.createElement(sui.Button, {icon: 'icon download', class: "primary huge fluid download-button " + (compileLoading ? 'loading' : ''), text: lf("Download"), title: compileTooltip, onClick: function () { return _this.compile('computer'); }}) : undefined)), readOnly ? undefined :
|
|
React.createElement("div", {className: "column left aligned"}, React.createElement("div", {className: "ui large right labeled input projectname-input projectname-computer", title: lf("Pick a name for your project")}, React.createElement("input", {id: "fileNameInput", type: "text", placeholder: lf("Pick a name..."), value: state.projectName || '', onChange: function (e) { return _this.saveProjectName(e.target.value, 'computer'); }}), React.createElement(sui.Button, {icon: 'save', class: "small right attached editortools-btn save-editortools-btn", title: lf("Save"), onClick: function () { return _this.saveFile('computer'); }}))), React.createElement("div", {className: "column right aligned"}, readOnly ? undefined :
|
|
React.createElement("div", {className: "ui icon small buttons"}, React.createElement(sui.Button, {icon: 'undo', class: "editortools-btn undo-editortools-btn} " + (!hasUndo ? 'disabled' : ''), title: lf("Undo"), onClick: function () { return _this.undo('computer'); }}), React.createElement(sui.Button, {icon: 'repeat', class: "editortools-btn redo-editortools-btn} " + (!hasRedo ? 'disabled' : ''), title: lf("Redo"), onClick: function () { return _this.redo('computer'); }})), React.createElement("div", {className: "ui icon small buttons"}, React.createElement(sui.Button, {icon: 'zoom', class: "editortools-btn zoomin-editortools-btn", title: lf("Zoom In"), onClick: function () { return _this.zoomIn('computer'); }}), React.createElement(sui.Button, {icon: 'zoom out', class: "editortools-btn zoomout-editortools-btn", title: lf("Zoom Out"), onClick: function () { return _this.zoomOut('computer'); }}))))));
|
|
};
|
|
return EditorToolbar;
|
|
}(data.Component));
|
|
exports.EditorToolbar = EditorToolbar;
|
|
|
|
},{"./data":11,"./sui":32,"react":264}],15:[function(require,module,exports){
|
|
"use strict";
|
|
var core = require("./core");
|
|
var Cloud = pxt.Cloud;
|
|
var UpdateEventType;
|
|
(function (UpdateEventType) {
|
|
UpdateEventType[UpdateEventType["Critical"] = 1] = "Critical";
|
|
UpdateEventType[UpdateEventType["Notification"] = 2] = "Notification";
|
|
UpdateEventType[UpdateEventType["Prompt"] = 3] = "Prompt";
|
|
})(UpdateEventType || (UpdateEventType = {}));
|
|
var electronSocket = null;
|
|
exports.isElectron = /[?&]electron=1/.test(window.location.href);
|
|
function init() {
|
|
if (!exports.isElectron || !Cloud.isLocalHost() || !Cloud.localToken) {
|
|
return;
|
|
}
|
|
function onCriticalUpdate(args) {
|
|
var isUrl = /^https:\/\//.test(args.targetVersion);
|
|
var body = lf("To continue using {0}, you must install an update.", args.appName || lf("this application"));
|
|
var agreeLbl = lf("Update");
|
|
if (isUrl) {
|
|
body = lf("To continue using {0}, you must install an update from the website.", args.appName || lf("this application"));
|
|
agreeLbl = lf("Go to website");
|
|
}
|
|
core.confirmAsync({
|
|
header: lf("Critical update required"),
|
|
body: body,
|
|
agreeLbl: agreeLbl,
|
|
disagreeLbl: lf("Quit"),
|
|
disagreeClass: "red",
|
|
size: "medium"
|
|
}).then(function (b) {
|
|
if (!b) {
|
|
pxt.tickEvent("update.refusedCritical");
|
|
sendMessage("quit");
|
|
}
|
|
else {
|
|
pxt.tickEvent("update.acceptedCritical");
|
|
core.showLoading(lf("Downloading update..."));
|
|
sendMessage("update", {
|
|
targetVersion: args.targetVersion,
|
|
type: args.type
|
|
});
|
|
}
|
|
});
|
|
}
|
|
function onUpdateAvailable(args) {
|
|
var isUrl = /^https:\/\//.test(args.targetVersion);
|
|
var header = lf("Version {0} available", args.targetVersion);
|
|
var body = lf("A new version of {0} is ready to download and install. The app will restart during the update. Update now?", args.appName || lf("this application"));
|
|
var agreeLbl = lf("Update");
|
|
if (isUrl) {
|
|
header = lf("Update available from website");
|
|
body = lf("A new version of {0} is available from the website.", args.appName || lf("this application"));
|
|
agreeLbl = lf("Go to website");
|
|
}
|
|
if (args.type === UpdateEventType.Notification) {
|
|
core.infoNotification(lf("A new version is available. Select 'Check for updates...' in the menu.", args.targetVersion));
|
|
}
|
|
else if (args.type === UpdateEventType.Prompt) {
|
|
core.confirmAsync({
|
|
header: header,
|
|
body: body,
|
|
agreeLbl: agreeLbl,
|
|
disagreeLbl: lf("Not now"),
|
|
size: "medium"
|
|
}).then(function (b) {
|
|
if (!b) {
|
|
if (args.isInitialCheck) {
|
|
pxt.tickEvent("update.refusedInitial");
|
|
}
|
|
else {
|
|
pxt.tickEvent("update.refused");
|
|
}
|
|
}
|
|
else {
|
|
if (args.isInitialCheck) {
|
|
pxt.tickEvent("update.acceptedInitial");
|
|
}
|
|
else {
|
|
pxt.tickEvent("update.accepted");
|
|
}
|
|
if (!isUrl) {
|
|
core.showLoading(lf("Downloading update..."));
|
|
}
|
|
sendMessage("update", {
|
|
targetVersion: args.targetVersion,
|
|
type: args.type
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
function onUpdateNotAvailable() {
|
|
core.confirmAsync({
|
|
body: lf("You are using the latest version available."),
|
|
header: lf("Good to go!"),
|
|
agreeLbl: lf("Ok"),
|
|
hideCancel: true
|
|
});
|
|
}
|
|
function onUpdateCheckError() {
|
|
displayUpdateError(lf("Unable to check for updates"), lf("Ok"));
|
|
}
|
|
function onUpdateDownloadError(args) {
|
|
var isCritical = args && args.type === UpdateEventType.Critical;
|
|
core.hideLoading();
|
|
displayUpdateError(lf("There was an error downloading the update"), isCritical ? lf("Quit") : lf("Ok"))
|
|
.finally(function () {
|
|
if (isCritical) {
|
|
sendMessage("quit");
|
|
}
|
|
});
|
|
}
|
|
function displayUpdateError(header, btnLabel) {
|
|
return core.confirmAsync({
|
|
header: header,
|
|
body: lf("Please ensure you are connected to the Internet and try again later."),
|
|
agreeClass: "red",
|
|
agreeIcon: "cancel",
|
|
agreeLbl: btnLabel,
|
|
hideCancel: true
|
|
});
|
|
}
|
|
pxt.log('initializing electron socket');
|
|
electronSocket = new WebSocket("ws://localhost:" + pxt.options.wsPort + "/" + Cloud.localToken + "/electron");
|
|
electronSocket.onopen = function (ev) {
|
|
pxt.log('electron: socket opened');
|
|
sendMessage("ready");
|
|
};
|
|
electronSocket.onclose = function (ev) {
|
|
pxt.log('electron: socket closed');
|
|
electronSocket = null;
|
|
};
|
|
electronSocket.onmessage = function (ev) {
|
|
try {
|
|
var msg = JSON.parse(ev.data);
|
|
switch (msg.type) {
|
|
case "critical-update":
|
|
onCriticalUpdate(msg.args);
|
|
break;
|
|
case "update-available":
|
|
onUpdateAvailable(msg.args);
|
|
break;
|
|
case "update-not-available":
|
|
onUpdateNotAvailable();
|
|
break;
|
|
case "update-check-error":
|
|
onUpdateCheckError();
|
|
break;
|
|
case "update-download-error":
|
|
onUpdateDownloadError(msg.args);
|
|
break;
|
|
case "telemetry":
|
|
var telemetryInfo = msg.args;
|
|
if (!telemetryInfo) {
|
|
pxt.debug('invalid telemetry message: ' + ev.data);
|
|
return;
|
|
}
|
|
pxt.tickEvent(telemetryInfo.event, telemetryInfo.data);
|
|
default:
|
|
pxt.debug('unknown electron message: ' + ev.data);
|
|
break;
|
|
}
|
|
}
|
|
catch (e) {
|
|
pxt.debug('unknown electron message: ' + ev.data);
|
|
}
|
|
};
|
|
}
|
|
exports.init = init;
|
|
function sendMessage(type, args) {
|
|
if (!electronSocket) {
|
|
return;
|
|
}
|
|
var message = {
|
|
type: type,
|
|
args: args
|
|
};
|
|
// Sending messages to the web socket sometimes hangs the app briefly; use setTimeout to smoothen the UI animations a bit
|
|
setTimeout(function () {
|
|
electronSocket.send(JSON.stringify(message));
|
|
}, 150);
|
|
}
|
|
exports.sendMessage = sendMessage;
|
|
function checkForUpdate() {
|
|
pxt.tickEvent("menu.electronupdate");
|
|
sendMessage("check-for-update");
|
|
}
|
|
exports.checkForUpdate = checkForUpdate;
|
|
|
|
},{"./core":10}],16:[function(require,module,exports){
|
|
/// <reference path="../../typings/globals/react/index.d.ts" />
|
|
/// <reference path="../../typings/globals/react-dom/index.d.ts" />
|
|
/// <reference path="../../built/pxtlib.d.ts" />
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var data = require("./data");
|
|
var sui = require("./sui");
|
|
var pkg = require("./package");
|
|
var core = require("./core");
|
|
var FileList = (function (_super) {
|
|
__extends(FileList, _super);
|
|
function FileList(props) {
|
|
_super.call(this, props);
|
|
this.state = {
|
|
expands: {}
|
|
};
|
|
}
|
|
FileList.prototype.removePkg = function (e, p) {
|
|
var _this = this;
|
|
e.stopPropagation();
|
|
core.confirmAsync({
|
|
header: lf("Remove {0} package", p.getPkgId()),
|
|
body: lf("You are about to remove a package from your project. Are you sure?"),
|
|
agreeClass: "red",
|
|
agreeIcon: "trash",
|
|
agreeLbl: lf("Remove it"),
|
|
}).done(function (res) {
|
|
if (res) {
|
|
pkg.mainEditorPkg().removeDepAsync(p.getPkgId())
|
|
.then(function () { return _this.props.parent.reloadHeaderAsync(); })
|
|
.done();
|
|
}
|
|
});
|
|
};
|
|
FileList.prototype.removeFile = function (e, f) {
|
|
e.stopPropagation();
|
|
this.props.parent.removeFile(f);
|
|
};
|
|
FileList.prototype.updatePkg = function (e, p) {
|
|
var _this = this;
|
|
e.stopPropagation();
|
|
pkg.mainEditorPkg().updateDepAsync(p.getPkgId())
|
|
.then(function () { return _this.props.parent.reloadHeaderAsync(); })
|
|
.done();
|
|
};
|
|
FileList.prototype.filesOf = function (pkg) {
|
|
var _this = this;
|
|
var deleteFiles = pkg.getPkgId() == "this";
|
|
var parent = this.props.parent;
|
|
return pkg.sortedFiles().map(function (file) {
|
|
var meta = _this.getData("open-meta:" + file.getName());
|
|
return (React.createElement("a", {key: file.getName(), onClick: function () { return parent.setSideFile(file); }, className: (parent.state.currFile == file ? "active " : "") + (pkg.isTopLevel() ? "" : "nested ") + "item"}, file.name, " ", meta.isSaved ? "" : "*", /\.ts$/.test(file.name) ? React.createElement("i", {className: "align left icon"}) : /\.blocks$/.test(file.name) ? React.createElement("i", {className: "puzzle icon"}) : undefined, meta.isReadonly ? React.createElement("i", {className: "lock icon"}) : null, !meta.numErrors ? null : React.createElement("span", {className: 'ui label red'}, meta.numErrors), deleteFiles && /\.blocks$/i.test(file.getName()) ? React.createElement(sui.Button, {class: "primary label", icon: "trash", onClick: function (e) { return _this.removeFile(e, file); }}) : ''));
|
|
});
|
|
};
|
|
FileList.prototype.packageOf = function (p) {
|
|
var _this = this;
|
|
var expands = this.state.expands;
|
|
var del = p.getPkgId() != pxt.appTarget.id && p.getPkgId() != "built";
|
|
var upd = p.getKsPkg() && p.getKsPkg().verProtocol() == "github";
|
|
return [React.createElement("div", {key: "hd-" + p.getPkgId(), className: "header link item", onClick: function () { return _this.togglePkg(p); }}, React.createElement("i", {className: "chevron " + (expands[p.getPkgId()] ? "down" : "right") + " icon"}), upd ? React.createElement(sui.Button, {class: "primary label", icon: "refresh", onClick: function (e) { return _this.updatePkg(e, p); }}) : '', del ? React.createElement(sui.Button, {class: "primary label", icon: "trash", onClick: function (e) { return _this.removePkg(e, p); }}) : '', p.getPkgId())
|
|
].concat(expands[p.getPkgId()] ? this.filesOf(p) : []);
|
|
};
|
|
FileList.prototype.togglePkg = function (p) {
|
|
var expands = this.state.expands;
|
|
expands[p.getPkgId()] = !expands[p.getPkgId()];
|
|
this.forceUpdate();
|
|
};
|
|
FileList.prototype.filesWithHeader = function (p) {
|
|
return p.isTopLevel() ? this.filesOf(p) : this.packageOf(p);
|
|
};
|
|
FileList.prototype.toggleVisibility = function () {
|
|
this.props.parent.setState({ showFiles: !this.props.parent.state.showFiles });
|
|
};
|
|
FileList.prototype.renderCore = function () {
|
|
var _this = this;
|
|
var show = !!this.props.parent.state.showFiles;
|
|
var targetTheme = pxt.appTarget.appTheme;
|
|
return React.createElement("div", {className: "ui tiny vertical " + (targetTheme.invertedMenu ? "inverted" : '') + " menu filemenu landscape only"}, React.createElement("div", {key: "projectheader", className: "link item", onClick: function () { return _this.toggleVisibility(); }}, lf("Explorer"), React.createElement("i", {className: "chevron " + (show ? "down" : "right") + " icon"})), show ? Util.concat(pkg.allEditorPkgs().map(function (p) { return _this.filesWithHeader(p); })) : undefined);
|
|
};
|
|
return FileList;
|
|
}(data.Component));
|
|
exports.FileList = FileList;
|
|
|
|
},{"./core":10,"./data":11,"./package":24,"./sui":32,"react":264}],17:[function(require,module,exports){
|
|
"use strict";
|
|
var db = require("./db");
|
|
var core = require("./core");
|
|
var data = require("./data");
|
|
var U = pxt.Util;
|
|
var Cloud = pxt.Cloud;
|
|
var lf = U.lf;
|
|
var allScripts = [];
|
|
var currentTarget;
|
|
function lookup(id) {
|
|
return allScripts.filter(function (x) { return x.id == id; })[0];
|
|
}
|
|
function getHeaders() {
|
|
return allScripts.map(function (e) { return e.header; });
|
|
}
|
|
function getHeader(id) {
|
|
var e = lookup(id);
|
|
if (e && !e.header.isDeleted)
|
|
return e.header;
|
|
return null;
|
|
}
|
|
function apiAsync(path, data) {
|
|
return U.requestAsync({
|
|
url: "/api/" + path,
|
|
headers: { "Authorization": Cloud.localToken },
|
|
method: data ? "POST" : "GET",
|
|
data: data || undefined
|
|
}).then(function (r) { return r.json; }).catch(core.handleNetworkError);
|
|
}
|
|
function mergeFsPkg(pkg) {
|
|
var e = lookup(pkg.path);
|
|
if (!e) {
|
|
e = {
|
|
id: pkg.path,
|
|
header: null,
|
|
text: null,
|
|
fsText: null
|
|
};
|
|
allScripts.push(e);
|
|
}
|
|
var time = pkg.files.map(function (f) { return f.mtime; });
|
|
time.sort(function (a, b) { return b - a; });
|
|
var modTime = Math.round(time[0] / 1000) || U.nowSeconds();
|
|
var hd = {
|
|
target: currentTarget,
|
|
name: pkg.config.name,
|
|
meta: {},
|
|
editor: pxt.JAVASCRIPT_PROJECT_NAME,
|
|
pubId: pkg.config.installedVersion,
|
|
pubCurrent: false,
|
|
_rev: null,
|
|
id: pkg.path,
|
|
recentUse: modTime,
|
|
modificationTime: modTime,
|
|
blobId: null,
|
|
blobCurrent: false,
|
|
isDeleted: false,
|
|
icon: pkg.icon
|
|
};
|
|
if (!e.header) {
|
|
e.header = hd;
|
|
}
|
|
else {
|
|
var eh = e.header;
|
|
eh.name = hd.name;
|
|
eh.pubId = hd.pubId;
|
|
eh.modificationTime = hd.modificationTime;
|
|
eh.isDeleted = hd.isDeleted;
|
|
eh.icon = hd.icon;
|
|
}
|
|
}
|
|
function initAsync(target) {
|
|
allScripts = [];
|
|
currentTarget = target;
|
|
// TODO check that target is correct.
|
|
return syncAsync();
|
|
}
|
|
function fetchTextAsync(e) {
|
|
return apiAsync("pkg/" + e.id)
|
|
.then(function (resp) {
|
|
if (!e.text) {
|
|
// otherwise we were beaten to it
|
|
e.text = {};
|
|
e.mtime = 0;
|
|
for (var _i = 0, _a = resp.files; _i < _a.length; _i++) {
|
|
var f = _a[_i];
|
|
e.text[f.name] = f.content;
|
|
e.mtime = Math.max(e.mtime, f.mtime);
|
|
}
|
|
e.fsText = U.flatClone(e.text);
|
|
}
|
|
return e.text;
|
|
});
|
|
}
|
|
var headerQ = new U.PromiseQueue();
|
|
function getTextAsync(id) {
|
|
var e = lookup(id);
|
|
if (!e)
|
|
return Promise.resolve(null);
|
|
if (e.text)
|
|
return Promise.resolve(e.text);
|
|
return headerQ.enqueue(id, function () { return fetchTextAsync(e); });
|
|
}
|
|
function saveCoreAsync(h, text) {
|
|
if (h.temporary)
|
|
return Promise.resolve();
|
|
var e = lookup(h.id);
|
|
U.assert(e.header === h);
|
|
if (!text)
|
|
return Promise.resolve();
|
|
h.saveId = null;
|
|
e.textNeedsSave = true;
|
|
e.text = text;
|
|
return headerQ.enqueue(h.id, function () {
|
|
U.assert(!!e.fsText);
|
|
var pkg = {
|
|
files: [],
|
|
config: null,
|
|
path: h.id,
|
|
};
|
|
for (var _i = 0, _a = Object.keys(e.text); _i < _a.length; _i++) {
|
|
var fn = _a[_i];
|
|
if (e.text[fn] !== e.fsText[fn])
|
|
pkg.files.push({
|
|
name: fn,
|
|
mtime: null,
|
|
content: e.text[fn],
|
|
prevContent: e.fsText[fn]
|
|
});
|
|
}
|
|
var savedText = U.flatClone(e.text);
|
|
if (pkg.files.length == 0)
|
|
return Promise.resolve();
|
|
return apiAsync("pkg/" + h.id, pkg)
|
|
.then(function (pkg) {
|
|
e.fsText = savedText;
|
|
mergeFsPkg(pkg);
|
|
data.invalidate("header:" + h.id);
|
|
data.invalidate("header:*");
|
|
if (text) {
|
|
data.invalidate("text:" + h.id);
|
|
h.saveId = null;
|
|
}
|
|
})
|
|
.catch(function (e) { return core.errorNotification(lf("Save failed!")); });
|
|
});
|
|
}
|
|
function saveAsync(h, text) {
|
|
return saveCoreAsync(h, text);
|
|
}
|
|
function installAsync(h0, text) {
|
|
var h = h0;
|
|
var path = h.name.replace(/[^a-zA-Z0-9]+/g, " ").trim().replace(/ /g, "-");
|
|
if (lookup(path)) {
|
|
var n = 2;
|
|
while (lookup(path + "-" + n))
|
|
n++;
|
|
path += "-" + n;
|
|
h.name += " " + n;
|
|
}
|
|
h.id = path;
|
|
h.recentUse = U.nowSeconds();
|
|
h.modificationTime = h.recentUse;
|
|
h.target = currentTarget;
|
|
var e = {
|
|
id: h.id,
|
|
header: h,
|
|
text: text,
|
|
fsText: {}
|
|
};
|
|
allScripts.push(e);
|
|
return saveCoreAsync(h, text)
|
|
.then(function () { return h; });
|
|
}
|
|
function saveToCloudAsync(h) {
|
|
return Promise.resolve();
|
|
}
|
|
function syncAsync() {
|
|
return apiAsync("list").then(function (h) {
|
|
h.pkgs.forEach(mergeFsPkg);
|
|
})
|
|
.then(function () {
|
|
data.invalidate("header:");
|
|
data.invalidate("text:");
|
|
});
|
|
}
|
|
function saveScreenshotAsync(h, screenshot, icon) {
|
|
return apiAsync("screenshot/" + h.id, { screenshot: screenshot, icon: icon });
|
|
}
|
|
function resetAsync() {
|
|
return db.destroyAsync()
|
|
.then(function () {
|
|
allScripts = [];
|
|
pxt.storage.clearLocal();
|
|
data.clearCache();
|
|
});
|
|
}
|
|
exports.provider = {
|
|
getHeaders: getHeaders,
|
|
getHeader: getHeader,
|
|
getTextAsync: getTextAsync,
|
|
initAsync: initAsync,
|
|
saveAsync: saveAsync,
|
|
installAsync: installAsync,
|
|
saveToCloudAsync: saveToCloudAsync,
|
|
syncAsync: syncAsync,
|
|
resetAsync: resetAsync,
|
|
saveScreenshotAsync: saveScreenshotAsync
|
|
};
|
|
|
|
},{"./core":10,"./data":11,"./db":12}],18:[function(require,module,exports){
|
|
"use strict";
|
|
function parseExampleMarkdown(name, md) {
|
|
if (!md)
|
|
return undefined;
|
|
var m = /```blocks\s*((.|\s)+?)\s*```/i.exec(md);
|
|
if (!m)
|
|
return undefined;
|
|
return {
|
|
name: name,
|
|
filesOverride: {
|
|
"main.blocks": "",
|
|
"main.ts": m[1]
|
|
}
|
|
};
|
|
}
|
|
function parseGalleryMardown(md) {
|
|
if (!md)
|
|
return [];
|
|
// second level titles are categories
|
|
// ## foo bar
|
|
// fenced code ```cards are sections of cards
|
|
var galleries = [];
|
|
var incard = false;
|
|
var name = undefined;
|
|
var cards = "";
|
|
md.split(/\r?\n/).forEach(function (line) {
|
|
// new category
|
|
if (/^##/.test(line)) {
|
|
name = line.substr(2).trim();
|
|
}
|
|
else if (/^```codecard$/.test(line)) {
|
|
incard = true;
|
|
}
|
|
else if (/^```$/.test(line)) {
|
|
incard = false;
|
|
if (name && cards) {
|
|
try {
|
|
var cardsJSON = JSON.parse(cards);
|
|
if (cardsJSON && cardsJSON.length > 0)
|
|
galleries.push({ name: name, cards: cardsJSON });
|
|
}
|
|
catch (e) {
|
|
pxt.log('invalid card format in gallery');
|
|
}
|
|
}
|
|
cards = "";
|
|
name = undefined;
|
|
}
|
|
else if (incard)
|
|
cards += line + '\n';
|
|
});
|
|
return galleries;
|
|
}
|
|
function loadGalleryAsync(name) {
|
|
return pxt.Cloud.downloadMarkdownAsync(name, pxt.Util.userLanguage(), pxt.Util.localizeLive)
|
|
.then(function (md) { return parseGalleryMardown(md); });
|
|
}
|
|
exports.loadGalleryAsync = loadGalleryAsync;
|
|
function loadExampleAsync(name, path) {
|
|
return pxt.Cloud.downloadMarkdownAsync(path, pxt.Util.userLanguage(), pxt.Util.localizeLive)
|
|
.then(function (md) { return parseExampleMarkdown(name, md); });
|
|
}
|
|
exports.loadExampleAsync = loadExampleAsync;
|
|
|
|
},{}],19:[function(require,module,exports){
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var workeriface = require("./workeriface");
|
|
var Cloud = pxt.Cloud;
|
|
var U = pxt.Util;
|
|
var iface;
|
|
var devPath;
|
|
var HIDError = (function (_super) {
|
|
__extends(HIDError, _super);
|
|
function HIDError(msg) {
|
|
_super.call(this, msg);
|
|
this.message = msg;
|
|
}
|
|
return HIDError;
|
|
}(Error));
|
|
exports.HIDError = HIDError;
|
|
var bridgeByPath = {};
|
|
function onOOB(v) {
|
|
var b = U.lookup(bridgeByPath, v.result.path);
|
|
if (b) {
|
|
b.onOOB(v);
|
|
}
|
|
else {
|
|
console.error("Dropping data for " + v.result.path);
|
|
}
|
|
}
|
|
function init() {
|
|
if (!iface) {
|
|
if (!Cloud.isLocalHost() || !Cloud.localToken)
|
|
return;
|
|
pxt.debug('initializing hid pipe');
|
|
iface = workeriface.makeWebSocket("ws://localhost:" + pxt.options.wsPort + "/" + Cloud.localToken + "/hid", onOOB);
|
|
}
|
|
}
|
|
function shouldUse() {
|
|
return pxt.appTarget.serial && pxt.appTarget.serial.useHF2 && Cloud.isLocalHost() && !!Cloud.localToken;
|
|
}
|
|
exports.shouldUse = shouldUse;
|
|
function mkBridgeAsync() {
|
|
init();
|
|
return iface.opAsync("list", {})
|
|
.then(function (devs) {
|
|
var d0 = devs.devices.filter(function (d) { return (d.release & 0xff00) == 0x4200; })[0];
|
|
if (d0)
|
|
return new BridgeIO(d0);
|
|
else
|
|
throw new Error("No device connected");
|
|
})
|
|
.then(function (b) { return b.initAsync().then(function () { return b; }); });
|
|
}
|
|
exports.mkBridgeAsync = mkBridgeAsync;
|
|
var BridgeIO = (function () {
|
|
function BridgeIO(dev) {
|
|
this.dev = dev;
|
|
this.onData = function (v) { };
|
|
this.onError = function (e) { };
|
|
this.onSerial = function (v, isErr) { };
|
|
}
|
|
BridgeIO.prototype.onOOB = function (v) {
|
|
if (v.op == "serial") {
|
|
this.onSerial(U.fromHex(v.result.data), v.result.isError);
|
|
}
|
|
};
|
|
BridgeIO.prototype.talksAsync = function (cmds) {
|
|
return iface.opAsync("talk", {
|
|
path: this.dev.path,
|
|
cmds: cmds.map(function (c) { return ({ cmd: c.cmd, data: c.data ? U.toHex(c.data) : "" }); })
|
|
})
|
|
.then(function (resp) {
|
|
return resp.map(function (v) { return U.fromHex(v.data); });
|
|
});
|
|
};
|
|
BridgeIO.prototype.error = function (msg) {
|
|
throw new HIDError(U.lf("USB/HID error on device {0} ({1})", this.dev.product, msg));
|
|
};
|
|
BridgeIO.prototype.reconnectAsync = function () {
|
|
return this.initAsync();
|
|
};
|
|
BridgeIO.prototype.sendPacketAsync = function (pkt) {
|
|
throw new Error("should use talksAsync()!");
|
|
};
|
|
BridgeIO.prototype.sendSerialAsync = function (buf, useStdErr) {
|
|
return iface.opAsync("sendserial", {
|
|
path: this.dev.path,
|
|
data: U.toHex(buf),
|
|
isError: useStdErr
|
|
});
|
|
};
|
|
BridgeIO.prototype.initAsync = function () {
|
|
bridgeByPath[this.dev.path] = this;
|
|
return iface.opAsync("init", {
|
|
path: this.dev.path
|
|
});
|
|
};
|
|
return BridgeIO;
|
|
}());
|
|
function hf2Async() {
|
|
return mkBridgeAsync()
|
|
.then(function (h) {
|
|
var w = new pxt.HF2.Wrapper(h);
|
|
return w.reconnectAsync(true)
|
|
.then(function () { return w; });
|
|
});
|
|
}
|
|
var initPromise;
|
|
function initAsync() {
|
|
if (!initPromise)
|
|
initPromise = hf2Async()
|
|
.catch(function (err) {
|
|
initPromise = null;
|
|
return Promise.reject(err);
|
|
});
|
|
return initPromise;
|
|
}
|
|
exports.initAsync = initAsync;
|
|
|
|
},{"./workeriface":36}],20:[function(require,module,exports){
|
|
"use strict";
|
|
var compiler = require("./compiler");
|
|
var workeriface = require("./workeriface");
|
|
var Cloud = pxt.Cloud;
|
|
var U = pxt.Util;
|
|
var iface;
|
|
var isHalted = false;
|
|
var lastCompileResult;
|
|
var haltCheckRunning = false;
|
|
var onHalted = Promise.resolve();
|
|
var haltHandler;
|
|
var cachedStateInfo;
|
|
var nextBreakpoints = [];
|
|
var currBreakpoint;
|
|
var lastDebugStatus;
|
|
var callInfos;
|
|
function init() {
|
|
if (!iface) {
|
|
if (!Cloud.isLocalHost() || !Cloud.localToken)
|
|
return;
|
|
pxt.debug('initializing debug pipe');
|
|
iface = workeriface.makeWebSocket("ws://localhost:" + pxt.options.wsPort + "/" + Cloud.localToken + "/debug");
|
|
}
|
|
}
|
|
function readMemAsync(addr, numwords) {
|
|
return workerOpAsync("mem", { addr: addr, words: numwords })
|
|
.then(function (resp) { return resp.data; });
|
|
}
|
|
exports.readMemAsync = readMemAsync;
|
|
function writeMemAsync(addr, words) {
|
|
return workerOpAsync("wrmem", { addr: addr, words: words })
|
|
.then(function () { });
|
|
}
|
|
exports.writeMemAsync = writeMemAsync;
|
|
var asm = "";
|
|
function callAndPush(prc) {
|
|
var idx = asm.length;
|
|
asm += "\n ldr r4, .proc" + idx + "\n blx r4\n push {r0}\n b .next" + idx + "\n .balign 4\n.proc" + idx + ":\n .word " + prc + "|1\n.next" + idx + ":\n";
|
|
}
|
|
var stateProcs = [
|
|
"pxt::getNumGlobals/numGlobals",
|
|
"pxtrt::getGlobalsPtr/globalsPtr",
|
|
];
|
|
function callForStateAsync(st) {
|
|
if (cachedStateInfo)
|
|
return Promise.resolve(cachedStateInfo);
|
|
asm = "";
|
|
for (var _i = 0, stateProcs_1 = stateProcs; _i < stateProcs_1.length; _i++) {
|
|
var p = stateProcs_1[_i];
|
|
callAndPush(p.replace(/\/.*/, ""));
|
|
}
|
|
asm += "\n bkpt 42\n @nostackcheck\n";
|
|
return compiler.assembleAsync(asm)
|
|
.then(function (res) { return workerOpAsync("exec", { code: res.words, args: [] }); })
|
|
.then(function () { return snapshotAsync(); })
|
|
.then(function (st) {
|
|
var fields = stateProcs.map(function (s) { return s.replace(/.*\//, ""); });
|
|
fields.reverse();
|
|
var r = {};
|
|
fields.forEach(function (f, i) {
|
|
r[f] = st.stack[i];
|
|
});
|
|
cachedStateInfo = r;
|
|
})
|
|
.then(function () { return restoreAsync(st); })
|
|
.then(function () { return cachedStateInfo; });
|
|
}
|
|
function clearAsync() {
|
|
isHalted = false;
|
|
lastCompileResult = null;
|
|
cachedStateInfo = null;
|
|
lastDebugStatus = null;
|
|
return Promise.resolve();
|
|
}
|
|
function coreHalted() {
|
|
return getHwStateAsync()
|
|
.then(function (st) {
|
|
nextBreakpoints = [];
|
|
var globals = {};
|
|
st.globals.slice(1).forEach(function (v, i) {
|
|
var loc = lastCompileResult.procDebugInfo[0].locals[i];
|
|
if (loc)
|
|
globals[loc.name] = v;
|
|
else
|
|
globals["?" + i] = v;
|
|
});
|
|
var pc = st.machineState.registers[15];
|
|
var final = function () { return Promise.resolve(); };
|
|
var stepInBkp = lastCompileResult.procDebugInfo.filter(function (p) { return p.bkptLoc == pc; })[0];
|
|
if (stepInBkp) {
|
|
pc = stepInBkp.codeStartLoc;
|
|
st.machineState.registers[15] = pc;
|
|
final = function () { return restoreAsync(st.machineState); };
|
|
}
|
|
var bb = lastCompileResult.breakpoints;
|
|
var brkMatch = bb[0];
|
|
var bestDelta = Infinity;
|
|
for (var _i = 0, bb_1 = bb; _i < bb_1.length; _i++) {
|
|
var b = bb_1[_i];
|
|
var delta = pc - b.binAddr;
|
|
if (delta >= 0 && delta < bestDelta) {
|
|
bestDelta = delta;
|
|
brkMatch = b;
|
|
}
|
|
}
|
|
currBreakpoint = brkMatch;
|
|
var msg = {
|
|
type: 'debugger',
|
|
subtype: 'breakpoint',
|
|
breakpointId: brkMatch.id,
|
|
globals: globals,
|
|
stackframes: []
|
|
};
|
|
exports.postMessage(msg);
|
|
return final();
|
|
})
|
|
.then(haltHandler);
|
|
}
|
|
function haltCheckAsync() {
|
|
if (isHalted)
|
|
return Promise.delay(100).then(haltCheckAsync);
|
|
return workerOpAsync("status")
|
|
.then(function (res) {
|
|
if (res.isHalted) {
|
|
isHalted = true;
|
|
coreHalted();
|
|
}
|
|
return Promise.delay(300);
|
|
})
|
|
.then(haltCheckAsync);
|
|
}
|
|
function clearHalted() {
|
|
isHalted = false;
|
|
onHalted = new Promise(function (resolve, reject) {
|
|
haltHandler = resolve;
|
|
});
|
|
if (!haltCheckRunning) {
|
|
haltCheckRunning = true;
|
|
haltCheckAsync();
|
|
}
|
|
}
|
|
function writeDebugStatusAsync(v) {
|
|
if (v === lastDebugStatus)
|
|
return Promise.resolve();
|
|
lastDebugStatus = v;
|
|
return writeMemAsync(cachedStateInfo.globalsPtr, [v]);
|
|
}
|
|
function setBreakpointsAsync(addrs) {
|
|
return workerOpAsync("breakpoints", { addrs: addrs });
|
|
}
|
|
function startDebugAsync() {
|
|
return clearAsync()
|
|
.then(function () { return compiler.compileAsync({ native: true }); })
|
|
.then(function (res) {
|
|
lastCompileResult = res;
|
|
callInfos = {};
|
|
var procLookup = [];
|
|
for (var _i = 0, _a = res.procDebugInfo; _i < _a.length; _i++) {
|
|
var pdi = _a[_i];
|
|
procLookup[pdi.idx] = pdi;
|
|
}
|
|
for (var _b = 0, _c = res.procDebugInfo; _b < _c.length; _b++) {
|
|
var pdi = _c[_b];
|
|
for (var _d = 0, _e = pdi.calls; _d < _e.length; _d++) {
|
|
var ci = _e[_d];
|
|
callInfos[ci.addr + ""] = {
|
|
from: pdi,
|
|
to: procLookup[ci.procIndex],
|
|
stack: ci.stack
|
|
};
|
|
}
|
|
}
|
|
var bb = lastCompileResult.breakpoints;
|
|
var entry = bb[1];
|
|
for (var _f = 0, bb_2 = bb; _f < bb_2.length; _f++) {
|
|
var b = bb_2[_f];
|
|
if (b.binAddr && b.binAddr < entry.binAddr)
|
|
entry = b;
|
|
}
|
|
return setBreakpointsAsync([entry.binAddr]);
|
|
})
|
|
.then(function () { return workerOpAsync("reset"); })
|
|
.then(clearHalted)
|
|
.then(waitForHaltAsync)
|
|
.then(function (res) { return writeDebugStatusAsync(1).then(function () { return res; }); });
|
|
}
|
|
exports.startDebugAsync = startDebugAsync;
|
|
function handleMessage(msg) {
|
|
console.log("HWDBGMSG", msg);
|
|
if (msg.type != "debugger")
|
|
return;
|
|
var stepInto = false;
|
|
switch (msg.subtype) {
|
|
case 'stepinto':
|
|
stepInto = true;
|
|
case 'stepover':
|
|
nextBreakpoints = currBreakpoint.successors.map(function (id) { return lastCompileResult.breakpoints[id].binAddr; });
|
|
resumeAsync(stepInto);
|
|
break;
|
|
}
|
|
}
|
|
exports.handleMessage = handleMessage;
|
|
function snapshotAsync() {
|
|
return workerOpAsync("snapshot")
|
|
.then(function (r) { return r.state; });
|
|
}
|
|
exports.snapshotAsync = snapshotAsync;
|
|
function restoreAsync(st) {
|
|
return workerOpAsync("restore", { state: st })
|
|
.then(function () { });
|
|
}
|
|
exports.restoreAsync = restoreAsync;
|
|
function resumeAsync(into) {
|
|
if (into === void 0) { into = false; }
|
|
return Promise.resolve()
|
|
.then(function () { return writeDebugStatusAsync(into ? 3 : 1); })
|
|
.then(function () { return setBreakpointsAsync(nextBreakpoints); })
|
|
.then(function () { return workerOpAsync("resume"); })
|
|
.then(clearHalted);
|
|
}
|
|
exports.resumeAsync = resumeAsync;
|
|
function waitForHaltAsync() {
|
|
U.assert(haltCheckRunning);
|
|
return onHalted;
|
|
}
|
|
exports.waitForHaltAsync = waitForHaltAsync;
|
|
function getHwStateAsync() {
|
|
var res = {
|
|
machineState: null,
|
|
globals: []
|
|
};
|
|
return snapshotAsync()
|
|
.then(function (v) {
|
|
res.machineState = v;
|
|
return callForStateAsync(v);
|
|
})
|
|
.then(function (info) { return readMemAsync(info.globalsPtr, info.numGlobals); })
|
|
.then(function (g) {
|
|
res.globals = g;
|
|
return res;
|
|
});
|
|
}
|
|
exports.getHwStateAsync = getHwStateAsync;
|
|
var devPath;
|
|
function workerOpAsync(op, arg) {
|
|
if (arg === void 0) { arg = {}; }
|
|
init();
|
|
if (!devPath)
|
|
devPath = iface.opAsync("list", {})
|
|
.then(function (devs) {
|
|
var d0 = devs.devices[0];
|
|
if (d0)
|
|
return d0.path;
|
|
else
|
|
throw new Error("No device connected");
|
|
});
|
|
return devPath
|
|
.then(function (path) {
|
|
arg["path"] = path;
|
|
return iface.opAsync(op, arg);
|
|
});
|
|
}
|
|
exports.workerOpAsync = workerOpAsync;
|
|
function flashDeviceAsync(startAddr, words) {
|
|
var cfg = {
|
|
flashWords: words,
|
|
flashCode: [],
|
|
bufferAddr: 0x20000400,
|
|
numBuffers: 2,
|
|
flashAddr: startAddr
|
|
};
|
|
return compiler.assembleAsync(nrfFlashAsm)
|
|
.then(function (res) { cfg.flashCode = res.words; })
|
|
.then(function (res) { return workerOpAsync("wrpages", cfg); });
|
|
}
|
|
exports.flashDeviceAsync = flashDeviceAsync;
|
|
/*
|
|
#define PAGE_SIZE 0x400
|
|
#define SIZE_IN_WORDS (PAGE_SIZE/4)
|
|
|
|
void setConfig(uint32_t v) {
|
|
NRF_NVMC->CONFIG = v;
|
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
|
|
}
|
|
|
|
void overwriteFlashPage(uint32_t* to, uint32_t* from)
|
|
{
|
|
// Turn on flash erase enable and wait until the NVMC is ready:
|
|
setConfig(NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos);
|
|
|
|
// Erase page:
|
|
NRF_NVMC->ERASEPAGE = (uint32_t)to;
|
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
|
|
|
|
// Turn off flash erase enable and wait until the NVMC is ready:
|
|
setConfig(NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
|
|
|
|
// Turn on flash write enable and wait until the NVMC is ready:
|
|
setConfig(NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
|
|
|
|
for(int i = 0; i <= (SIZE_IN_WORDS - 1); i++) {
|
|
*(to + i) = *(from + i);
|
|
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
|
|
}
|
|
|
|
// Turn off flash write enable and wait until the NVMC is ready:
|
|
setConfig(NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
|
|
}
|
|
*/
|
|
var nrfFlashAsm = "\noverwriteFlashPage:\n cpsid i\n push {r4, r5, r6, lr}\n movs r5, r0\n movs r0, #2\n movs r6, r1\n bl .setConfig\n movs r3, #161 ; 0xa1\n movs r2, #128 ; 0x80\n ldr r4, .NRF_NVMC\n lsls r3, r3, #3\n str r5, [r4, r3]\n lsls r2, r2, #3\n.overLoop:\n ldr r3, [r4, r2]\n cmp r3, #0\n beq .overLoop\n movs r0, #0\n bl .setConfig\n movs r0, #1\n bl .setConfig\n movs r2, #128\n lsls r2, r2, #3\n movs r3, #0\n movs r1, r2\n.overOuterLoop:\n ldr r0, [r6, r3]\n str r0, [r5, r3]\n.overLoop2:\n ldr r0, [r4, r2]\n cmp r0, #0\n beq .overLoop2\n adds r3, #4\n cmp r3, r1\n bne .overOuterLoop\n movs r0, #0\n bl .setConfig\n pop {r4, r5, r6, pc}\n\n.setConfig:\n movs r1, #128\n ldr r3, .NRF_NVMC\n ldr r2, .v504\n lsls r1, r1, #3\n str r0, [r3, r2]\n.cfgLoop:\n ldr r2, [r3, r1]\n cmp r2, #0\n beq .cfgLoop\n bx lr\n\n\n .balign 4\n.NRF_NVMC: .word 0x4001e000\n.v504: .word 0x504\n";
|
|
|
|
},{"./compiler":8,"./workeriface":36}],21:[function(require,module,exports){
|
|
/// <reference path="../../built/pxtsim.d.ts" />
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var ReactDOM = require("react-dom");
|
|
var core = require("./core");
|
|
var STREAM_INTERVAL = 30000;
|
|
var LogView = (function (_super) {
|
|
__extends(LogView, _super);
|
|
function LogView(props) {
|
|
var _this = this;
|
|
_super.call(this, props);
|
|
this.lastStreamUploadTime = 0;
|
|
this.streamUploadTimeout = 0;
|
|
this.view = new pxsim.logs.LogViewElement({
|
|
maxEntries: 80,
|
|
maxAccValues: 500,
|
|
onClick: function (es) { return _this.onClick(es); },
|
|
onTrendChartChanged: function () { return _this.setState({ trends: _this.view.hasTrends() }); }
|
|
});
|
|
this.state = {};
|
|
}
|
|
LogView.prototype.componentDidMount = function () {
|
|
var node = ReactDOM.findDOMNode(this);
|
|
node.appendChild(this.view.element);
|
|
};
|
|
LogView.prototype.clear = function () {
|
|
this.view.clear();
|
|
};
|
|
LogView.prototype.render = function () {
|
|
return React.createElement("div", null);
|
|
};
|
|
LogView.prototype.componentDidUpdate = function () {
|
|
var streams = pxt.appTarget.simulator && !!pxt.appTarget.simulator.streams;
|
|
if (streams && this.state.stream)
|
|
this.view.setLabel(lf("streaming to cloud"), "green cloudflash");
|
|
else if (streams && this.state.trends)
|
|
this.view.setLabel(lf("streaming off"), "gray");
|
|
else
|
|
this.view.setLabel(undefined);
|
|
if (this.state.stream)
|
|
this.scheduleStreamData();
|
|
};
|
|
LogView.prototype.cancelStreamData = function () {
|
|
if (this.streamUploadTimeout) {
|
|
clearTimeout(this.streamUploadTimeout);
|
|
this.streamUploadTimeout = 0;
|
|
}
|
|
};
|
|
LogView.prototype.scheduleStreamData = function () {
|
|
var _this = this;
|
|
this.cancelStreamData();
|
|
var towait = Math.max(100, STREAM_INTERVAL - (Util.now() - this.lastStreamUploadTime));
|
|
this.streamUploadTimeout = setTimeout(function () { return _this.streamData(); }, towait);
|
|
};
|
|
LogView.prototype.streamData = function () {
|
|
var _this = this;
|
|
var stream = this.state.stream;
|
|
if (!stream) {
|
|
if (this.streamUploadTimeout) {
|
|
clearTimeout(this.streamUploadTimeout);
|
|
this.streamUploadTimeout = 0;
|
|
}
|
|
return;
|
|
}
|
|
if (!pxt.Cloud.isOnline()) {
|
|
this.scheduleStreamData();
|
|
return;
|
|
}
|
|
pxt.debug('streaming payload...');
|
|
var data = this.view.streamPayload(this.lastStreamUploadTime);
|
|
this.lastStreamUploadTime = Util.now();
|
|
if (!data) {
|
|
this.scheduleStreamData();
|
|
return;
|
|
}
|
|
pxt.streams.postPayloadAsync(stream, data)
|
|
.catch(function (e) {
|
|
core.warningNotification(lf("Oops, we could not upload your data..."));
|
|
_this.scheduleStreamData();
|
|
}).done(function () { return _this.scheduleStreamData(); });
|
|
};
|
|
LogView.prototype.setStream = function (stream) {
|
|
this.setState({ stream: stream });
|
|
};
|
|
LogView.prototype.onClick = function (entries) {
|
|
this.showStreamDialog(entries);
|
|
};
|
|
LogView.prototype.showStreamDialog = function (entries) {
|
|
var _this = this;
|
|
var targetTheme = pxt.appTarget.appTheme;
|
|
var streaming = pxt.appTarget.simulator && !!pxt.appTarget.simulator.streams;
|
|
var rootUrl = targetTheme.embedUrl;
|
|
if (!rootUrl) {
|
|
pxt.commands.browserDownloadAsync(pxsim.logs.entriesToCSV(entries), "data.csv", 'text/csv');
|
|
return;
|
|
}
|
|
if (!/\/$/.test(rootUrl))
|
|
rootUrl += '/';
|
|
var streamUrl = this.state.stream ? rootUrl + this.state.stream.id : undefined;
|
|
core.confirmAsync({
|
|
logos: streaming ? ["https://az851932.vo.msecnd.net/pub/hjlxsmaf"] : undefined,
|
|
header: pxt.appTarget.title + ' - ' + lf("Analyze Data"),
|
|
hideAgree: true,
|
|
disagreeLbl: lf("Close"),
|
|
onLoaded: function (_) {
|
|
_.find('#datasavelocalfile').click(function () {
|
|
_.modal('hide');
|
|
pxt.commands.browserDownloadAsync(pxsim.logs.entriesToCSV(entries), "data.csv", 'text/csv');
|
|
}),
|
|
_.find('#datastreamstart').click(function () {
|
|
_.modal('hide');
|
|
core.showLoading(lf("creating stream in Microsoft Azure..."));
|
|
pxt.streams.createStreamAsync(pxt.appTarget.id)
|
|
.then(function (stream) {
|
|
core.hideLoading();
|
|
_this.setStream(stream);
|
|
}).catch(function (e) {
|
|
pxt.reportException(e, {});
|
|
core.hideLoading();
|
|
core.warningNotification(lf("Oops, we could not create the stream. Please try again later."));
|
|
}).done();
|
|
});
|
|
_.find('#datastreamstop').click(function () {
|
|
_.modal('hide');
|
|
_this.setStream(null);
|
|
});
|
|
},
|
|
htmlBody: "\n<div class=\"ui cards\">\n <div class=\"ui card\">\n <div class=\"content\">\n <div class=\"header\">" + lf("Local File") + "</div>\n <div class=\"description\">\n " + lf("Save the data to your 'Downloads' folder.") + "\n </div>\n </div>\n <div id=\"datasavelocalfile\" class=\"ui bottom attached button\">\n <i class=\"download icon\"></i>\n " + lf("Download data") + "\n </div> \n </div>\n " + (streaming ?
|
|
"<div id=\"datastreamcard\" class=\"ui card\">\n <div class=\"content\">\n <div class=\"header\">" + lf("Stream to Cloud") + "</div>\n <div class=\"description\">\n " + (streamUrl ? lf("We are uploading your data to Microsoft Azure every minute.")
|
|
: lf("Upload your data to Microsoft Azure to analyze it.")) + "\n </div>\n </div>\n " + (streamUrl ?
|
|
"<div id=\"datastream\" class=\"ui bottom attached two buttons\">\n <a target=\"_blank\" href=\"" + streamUrl + "\" class=\"ui green button\">Open</a>\n <div id=\"datastreamstop\" class=\"ui button\">Stop</div>\n </div>" :
|
|
"<div id=\"datastreamstart\" class=\"ui bottom attached green button\">\n <i class=\"play icon\"></i>\n " + lf("Start") + "\n </div>") + "\n </div>" : "") + "\n</div>"
|
|
}).done();
|
|
};
|
|
return LogView;
|
|
}(React.Component));
|
|
exports.LogView = LogView;
|
|
|
|
},{"./core":10,"react":264,"react-dom":135}],22:[function(require,module,exports){
|
|
"use strict";
|
|
var U = pxt.Util;
|
|
var projects = {};
|
|
var target = "";
|
|
function getHeaders() {
|
|
return Util.values(projects).map(function (p) { return p.header; });
|
|
}
|
|
function getHeader(id) {
|
|
var p = projects[id];
|
|
return p ? p.header : undefined;
|
|
}
|
|
function getTextAsync(id) {
|
|
var p = projects[id];
|
|
return Promise.resolve(p ? p.text : undefined);
|
|
}
|
|
function initAsync(trg) {
|
|
target = target;
|
|
return Promise.resolve();
|
|
}
|
|
function saveAsync(h, text) {
|
|
projects[h.id] = {
|
|
header: h,
|
|
text: text
|
|
};
|
|
return Promise.resolve();
|
|
}
|
|
function installAsync(h0, text) {
|
|
var h = h0;
|
|
h.id = U.guidGen();
|
|
h.recentUse = U.nowSeconds();
|
|
h.modificationTime = h.recentUse;
|
|
h.target = pxt.appTarget.id;
|
|
return saveAsync(h, text).then(function () { return h; });
|
|
}
|
|
function saveToCloudAsync(h) {
|
|
return Promise.resolve();
|
|
}
|
|
function syncAsync() {
|
|
return Promise.resolve();
|
|
}
|
|
function resetAsync() {
|
|
projects = {};
|
|
target = "";
|
|
return Promise.resolve();
|
|
}
|
|
exports.provider = {
|
|
getHeaders: getHeaders,
|
|
getHeader: getHeader,
|
|
getTextAsync: getTextAsync,
|
|
initAsync: initAsync,
|
|
saveAsync: saveAsync,
|
|
installAsync: installAsync,
|
|
saveToCloudAsync: saveToCloudAsync,
|
|
syncAsync: syncAsync,
|
|
resetAsync: resetAsync
|
|
};
|
|
|
|
},{}],23:[function(require,module,exports){
|
|
/// <reference path="../../localtypings/monaco.d.ts" />
|
|
/// <reference path="../../built/pxteditor.d.ts" />
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var pkg = require("./package");
|
|
var core = require("./core");
|
|
var srceditor = require("./srceditor");
|
|
var compiler = require("./compiler");
|
|
var Util = pxt.Util;
|
|
var lf = Util.lf;
|
|
var MIN_EDITOR_FONT_SIZE = 10;
|
|
var MAX_EDITOR_FONT_SIZE = 40;
|
|
var FileType;
|
|
(function (FileType) {
|
|
FileType[FileType["Unknown"] = 0] = "Unknown";
|
|
FileType[FileType["TypeScript"] = 1] = "TypeScript";
|
|
FileType[FileType["Markdown"] = 2] = "Markdown";
|
|
})(FileType || (FileType = {}));
|
|
var Editor = (function (_super) {
|
|
__extends(Editor, _super);
|
|
function Editor() {
|
|
_super.apply(this, arguments);
|
|
this.fileType = FileType.Unknown;
|
|
this.highlightDecorations = [];
|
|
}
|
|
Editor.prototype.hasBlocks = function () {
|
|
if (!this.currFile)
|
|
return true;
|
|
var blockFile = this.currFile.getVirtualFileName();
|
|
return (blockFile && pkg.mainEditorPkg().files[blockFile] != null);
|
|
};
|
|
Editor.prototype.openBlocks = function () {
|
|
var _this = this;
|
|
pxt.tickEvent("typescript.showBlocks");
|
|
var header = this.parent.state.header;
|
|
if (header) {
|
|
header.editor = pxt.BLOCKS_PROJECT_NAME;
|
|
header.pubCurrent = false;
|
|
}
|
|
var promise = Promise.resolve().then(function () {
|
|
if (!_this.hasBlocks())
|
|
return;
|
|
var blockFile = _this.currFile.getVirtualFileName();
|
|
if (!blockFile) {
|
|
var mainPkg_1 = pkg.mainEditorPkg();
|
|
if (!mainPkg_1 || !mainPkg_1.files["main.blocks"]) {
|
|
if (mainPkg_1) {
|
|
_this.parent.setFile(mainPkg_1.files["main.ts"]);
|
|
}
|
|
return;
|
|
}
|
|
_this.currFile = mainPkg_1.files["main.ts"];
|
|
blockFile = _this.currFile.getVirtualFileName();
|
|
}
|
|
var failedAsync = function (file) {
|
|
core.cancelAsyncLoading();
|
|
_this.forceDiagnosticsUpdate();
|
|
return _this.showConversionFailedDialog(file);
|
|
};
|
|
// might be undefined
|
|
var mainPkg = pkg.mainEditorPkg();
|
|
var xml;
|
|
// it's a bit for a wild round trip:
|
|
// 1) convert blocks to js to see if any changes happened, otherwise, just reload blocks
|
|
// 2) decompile js -> blocks then take the decompiled blocks -> js
|
|
// 3) check that decompiled js == current js % white space
|
|
var blocksInfo;
|
|
return _this.parent.saveFileAsync()
|
|
.then(function () { return compiler.getBlocksAsync(); })
|
|
.then(function (bi) {
|
|
blocksInfo = bi;
|
|
pxt.blocks.initBlocks(blocksInfo);
|
|
var oldWorkspace = pxt.blocks.loadWorkspaceXml(mainPkg.files[blockFile].content);
|
|
if (oldWorkspace) {
|
|
var oldJs = pxt.blocks.compile(oldWorkspace, blocksInfo).source;
|
|
if (pxtc.format(oldJs, 0).formatted == pxtc.format(_this.editor.getValue(), 0).formatted) {
|
|
pxt.debug('js not changed, skipping decompile');
|
|
pxt.tickEvent("typescript.noChanges");
|
|
return _this.parent.setFile(mainPkg.files[blockFile]);
|
|
}
|
|
}
|
|
return compiler.decompileAsync(_this.currFile.name, blocksInfo, oldWorkspace, blockFile)
|
|
.then(function (resp) {
|
|
if (!resp.success) {
|
|
_this.currFile.diagnostics = resp.diagnostics;
|
|
return failedAsync(blockFile);
|
|
}
|
|
xml = resp.outfiles[blockFile];
|
|
Util.assert(!!xml);
|
|
return mainPkg.setContentAsync(blockFile, xml)
|
|
.then(function () { return _this.parent.setFile(mainPkg.files[blockFile]); });
|
|
});
|
|
}).catch(function (e) {
|
|
pxt.reportException(e);
|
|
core.errorNotification(lf("Oops, something went wrong trying to convert your code."));
|
|
});
|
|
});
|
|
core.showLoadingAsync(lf("switching to blocks..."), promise).done();
|
|
};
|
|
Editor.prototype.showConversionFailedDialog = function (blockFile) {
|
|
var _this = this;
|
|
var bf = pkg.mainEditorPkg().files[blockFile];
|
|
return core.confirmAsync({
|
|
header: lf("Oops, there is a problem converting your code."),
|
|
body: lf("We are unable to convert your JavaScript code back to blocks. You can keep working in JavaScript or discard your changes and go back to the previous Blocks version."),
|
|
agreeLbl: lf("Discard and go to Blocks"),
|
|
agreeClass: "cancel",
|
|
agreeIcon: "cancel",
|
|
disagreeLbl: lf("Stay in JavaScript"),
|
|
disagreeClass: "positive",
|
|
disagreeIcon: "checkmark",
|
|
size: "medium",
|
|
hideCancel: !bf
|
|
}).then(function (b) {
|
|
// discard
|
|
if (!b) {
|
|
pxt.tickEvent("typescript.keepText");
|
|
}
|
|
else {
|
|
pxt.tickEvent("typescript.discardText");
|
|
_this.overrideFile(_this.parent.saveBlocksToTypeScript());
|
|
_this.parent.setFile(bf);
|
|
}
|
|
});
|
|
};
|
|
Editor.prototype.decompileAsync = function (blockFile) {
|
|
return compiler.decompileAsync(blockFile)
|
|
.then(function (resp) { return resp.success; });
|
|
};
|
|
Editor.prototype.display = function () {
|
|
return (React.createElement("div", {className: 'full-abs', id: "monacoEditorArea"}, React.createElement("div", {id: 'monacoEditorToolbox', className: 'injectionDiv'}), React.createElement("div", {id: 'monacoEditorInner'})));
|
|
};
|
|
Editor.prototype.defineEditorTheme = function () {
|
|
var inverted = pxt.appTarget.appTheme.invertedMonaco;
|
|
var invertedColorluminosityMultipler = 0.6;
|
|
var fnDict = this.definitions;
|
|
var rules = [];
|
|
Object.keys(fnDict).forEach(function (ns) {
|
|
var element = fnDict[ns];
|
|
if (element.metaData && element.metaData.color && element.fns) {
|
|
var hexcolor_1 = pxt.blocks.convertColour(element.metaData.color);
|
|
hexcolor_1 = (inverted ? Blockly.PXTUtils.fadeColour(hexcolor_1, invertedColorluminosityMultipler, true) : hexcolor_1).replace('#', '');
|
|
Object.keys(element.fns).forEach(function (fn) {
|
|
rules.push({ token: "identifier.ts " + fn, foreground: hexcolor_1 });
|
|
});
|
|
rules.push({ token: "identifier.ts " + ns, foreground: hexcolor_1 });
|
|
}
|
|
});
|
|
rules.push({ token: "identifier.ts if", foreground: '5B80A5', });
|
|
rules.push({ token: "identifier.ts else", foreground: '5B80A5', });
|
|
rules.push({ token: "identifier.ts while", foreground: '5BA55B', });
|
|
rules.push({ token: "identifier.ts for", foreground: '5BA55B', });
|
|
monaco.editor.defineTheme('pxtTheme', {
|
|
base: inverted ? 'vs-dark' : 'vs',
|
|
inherit: true,
|
|
rules: rules
|
|
});
|
|
this.editor.updateOptions({ theme: 'pxtTheme' });
|
|
};
|
|
Editor.prototype.beforeCompile = function () {
|
|
if (this.editor)
|
|
this.formatCode();
|
|
};
|
|
Editor.prototype.formatCode = function (isAutomatic) {
|
|
if (isAutomatic === void 0) { isAutomatic = false; }
|
|
Util.assert(this.editor != undefined); // Guarded
|
|
if (this.fileType != FileType.TypeScript)
|
|
return;
|
|
function spliceStr(big, idx, deleteCount, injection) {
|
|
if (injection === void 0) { injection = ""; }
|
|
return big.slice(0, idx) + injection + big.slice(idx + deleteCount);
|
|
}
|
|
var position = this.editor.getPosition();
|
|
var data = this.textAndPosition(position);
|
|
var cursorOverride = this.editor.getModel().getOffsetAt(position);
|
|
if (cursorOverride >= 0) {
|
|
isAutomatic = false;
|
|
data.charNo = cursorOverride;
|
|
}
|
|
var tmp = pxtc.format(data.programText, data.charNo);
|
|
if (isAutomatic && tmp.formatted == data.programText)
|
|
return;
|
|
var formatted = tmp.formatted;
|
|
var line = 1;
|
|
var col = 0;
|
|
//console.log(data.charNo, tmp.pos)
|
|
for (var i = 0; i < formatted.length; ++i) {
|
|
var c = formatted.charCodeAt(i);
|
|
col++;
|
|
if (i >= tmp.pos)
|
|
break;
|
|
if (c == 10) {
|
|
line++;
|
|
col = 0;
|
|
}
|
|
}
|
|
this.editor.setValue(formatted);
|
|
this.editor.setScrollPosition(line);
|
|
this.editor.setPosition(position);
|
|
return formatted;
|
|
};
|
|
Editor.prototype.textAndPosition = function (pos) {
|
|
var programText = this.editor.getValue();
|
|
var lines = pos.lineNumber;
|
|
var chars = pos.column;
|
|
var charNo = 0;
|
|
for (; charNo < programText.length; ++charNo) {
|
|
if (lines == 0) {
|
|
if (chars-- == 0)
|
|
break;
|
|
}
|
|
else if (programText[charNo] == '\n')
|
|
lines--;
|
|
}
|
|
return { programText: programText, charNo: charNo };
|
|
};
|
|
Editor.prototype.isIncomplete = function () {
|
|
return this.editor && this.editor._view ?
|
|
this.editor._view.contentWidgets._widgets["editor.widget.suggestWidget"].isVisible :
|
|
false;
|
|
};
|
|
Editor.prototype.resize = function (e) {
|
|
var monacoArea = document.getElementById('monacoEditorArea');
|
|
var monacoToolbox = document.getElementById('monacoEditorToolbox');
|
|
if (monacoArea && monacoToolbox && this.editor)
|
|
this.editor.layout({ width: monacoArea.offsetWidth - monacoToolbox.offsetWidth - 1, height: monacoArea.offsetHeight });
|
|
};
|
|
Editor.prototype.prepare = function () {
|
|
this.isReady = true;
|
|
};
|
|
Editor.prototype.loadMonacoAsync = function () {
|
|
var _this = this;
|
|
if (this.editor || this.loadingMonaco)
|
|
return Promise.resolve();
|
|
this.loadingMonaco = true;
|
|
this.extraLibs = Object.create(null);
|
|
var editorArea = document.getElementById("monacoEditorArea");
|
|
var editorElement = document.getElementById("monacoEditorInner");
|
|
return pxt.vs.initMonacoAsync(editorElement).then(function (editor) {
|
|
_this.editor = editor;
|
|
_this.loadingMonaco = false;
|
|
_this.editor.updateOptions({ fontSize: _this.parent.settings.editorFontSize });
|
|
_this.editor.getActions().filter(function (action) { return action.id == "editor.action.formatDocument"; })[0]
|
|
.run = function () { return Promise.resolve(_this.beforeCompile()); };
|
|
_this.editor.addAction({
|
|
id: "save",
|
|
label: lf("Save"),
|
|
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S],
|
|
keybindingContext: "!editorReadonly",
|
|
precondition: "!editorReadonly",
|
|
contextMenuGroupId: "0_pxtnavigation",
|
|
contextMenuOrder: 0.2,
|
|
run: function () { return Promise.resolve(_this.parent.typecheckNow()); }
|
|
});
|
|
_this.editor.addAction({
|
|
id: "runSimulator",
|
|
label: lf("Run Simulator"),
|
|
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
|
|
keybindingContext: "!editorReadonly",
|
|
precondition: "!editorReadonly",
|
|
contextMenuGroupId: "0_pxtnavigation",
|
|
contextMenuOrder: 0.21,
|
|
run: function () { return Promise.resolve(_this.parent.runSimulator()); }
|
|
});
|
|
if (pxt.appTarget.compile && pxt.appTarget.compile.hasHex) {
|
|
_this.editor.addAction({
|
|
id: "compileHex",
|
|
label: lf("Download"),
|
|
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyMod.Alt | monaco.KeyCode.Enter],
|
|
keybindingContext: "!editorReadonly",
|
|
precondition: "!editorReadonly",
|
|
contextMenuGroupId: "0_pxtnavigation",
|
|
contextMenuOrder: 0.22,
|
|
run: function () { return Promise.resolve(_this.parent.compile()); }
|
|
});
|
|
}
|
|
_this.editor.addAction({
|
|
id: "zoomIn",
|
|
label: lf("Zoom In"),
|
|
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.NUMPAD_ADD, monaco.KeyMod.CtrlCmd | monaco.KeyCode.US_EQUAL],
|
|
run: function () { return Promise.resolve(_this.zoomIn()); }
|
|
});
|
|
_this.editor.addAction({
|
|
id: "zoomOut",
|
|
label: lf("Zoom Out"),
|
|
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.NUMPAD_SUBTRACT, monaco.KeyMod.CtrlCmd | monaco.KeyCode.US_MINUS],
|
|
run: function () { return Promise.resolve(_this.zoomOut()); }
|
|
});
|
|
_this.editor.onDidBlurEditorText(function () {
|
|
if (_this.isIncomplete()) {
|
|
monaco.languages.typescript.typescriptDefaults._diagnosticsOptions = ({ noSyntaxValidation: true, noSemanticValidation: true });
|
|
}
|
|
else {
|
|
monaco.languages.typescript.typescriptDefaults._diagnosticsOptions = ({ noSyntaxValidation: false, noSemanticValidation: false });
|
|
}
|
|
});
|
|
if (pxt.appTarget.appTheme.hasReferenceDocs) {
|
|
var referenceContextKey_1 = _this.editor.createContextKey("editorHasReference", false);
|
|
_this.editor.addAction({
|
|
id: "reference",
|
|
label: lf("Help"),
|
|
keybindingContext: "!editorReadonly && editorHasReference",
|
|
precondition: "!editorReadonly && editorHasReference",
|
|
contextMenuGroupId: "navigation",
|
|
contextMenuOrder: 0.1,
|
|
run: function () { return Promise.resolve(_this.loadReference()); }
|
|
});
|
|
_this.editor.onDidChangeCursorPosition(function (e) {
|
|
var word = _this.editor.getModel().getWordUntilPosition(e.position);
|
|
if (word && word.word != "") {
|
|
referenceContextKey_1.set(true);
|
|
}
|
|
else {
|
|
referenceContextKey_1.reset();
|
|
}
|
|
});
|
|
}
|
|
_this.editor.onDidLayoutChange(function (e) {
|
|
// Update editor font size in settings after a ctrl+scroll zoom
|
|
var currentFont = _this.editor.getConfiguration().fontInfo.fontSize;
|
|
if (_this.parent.settings.editorFontSize != currentFont) {
|
|
_this.parent.settings.editorFontSize = currentFont;
|
|
_this.forceDiagnosticsUpdate();
|
|
}
|
|
// Update widgets
|
|
var toolbox = document.getElementById('monacoEditorToolbox');
|
|
toolbox.style.height = _this.editor.getLayoutInfo().contentHeight + "px";
|
|
var flyout = document.getElementById('monacoFlyoutWidget');
|
|
flyout.style.height = _this.editor.getLayoutInfo().contentHeight + "px";
|
|
});
|
|
var monacoEditorInner = document.getElementById('monacoEditorInner');
|
|
monacoEditorInner.ondragenter = (function (ev) {
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
});
|
|
monacoEditorInner.ondragover = (function (ev) {
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
var mouseTarget = _this.editor.getTargetAtClientPoint(ev.clientX, ev.clientY);
|
|
var position = mouseTarget.position;
|
|
if (position && _this.editor.getPosition() != position)
|
|
_this.editor.setPosition(position);
|
|
_this.editor.focus();
|
|
});
|
|
monacoEditorInner.ondrop = (function (ev) {
|
|
var insertText = ev.dataTransfer.getData('text'); // IE11 only support "text"
|
|
if (!insertText)
|
|
return;
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
var mouseTarget = _this.editor.getTargetAtClientPoint(ev.clientX, ev.clientY);
|
|
var position = mouseTarget.position;
|
|
var model = _this.editor.getModel();
|
|
var currPos = _this.editor.getPosition();
|
|
var cursor = model.getOffsetAt(currPos);
|
|
if (!position)
|
|
position = currPos;
|
|
insertText = (currPos.column > 1) ? '\n' + insertText :
|
|
model.getWordUntilPosition(currPos) != undefined && model.getWordUntilPosition(currPos).word != '' ?
|
|
insertText + '\n' : insertText;
|
|
if (insertText.indexOf('{{}}') > -1) {
|
|
cursor += (insertText.indexOf('{{}}'));
|
|
insertText = insertText.replace('{{}}', '');
|
|
}
|
|
else
|
|
cursor += (insertText.length);
|
|
_this.editor.pushUndoStop();
|
|
_this.editor.executeEdits("", [
|
|
{
|
|
identifier: { major: 0, minor: 0 },
|
|
range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column),
|
|
text: insertText,
|
|
forceMoveMarkers: false
|
|
}
|
|
]);
|
|
_this.editor.pushUndoStop();
|
|
var endPos = model.getPositionAt(cursor);
|
|
_this.editor.setPosition(endPos);
|
|
_this.editor.focus();
|
|
});
|
|
_this.editor.onDidFocusEditorText(function () {
|
|
_this.resetFlyout(true);
|
|
});
|
|
_this.editorViewZones = [];
|
|
_this.setupToolbox(editorArea);
|
|
});
|
|
};
|
|
Editor.prototype.undo = function () {
|
|
if (!this.editor)
|
|
return;
|
|
this.editor.trigger('keyboard', monaco.editor.Handler.Undo, null);
|
|
};
|
|
Editor.prototype.redo = function () {
|
|
if (!this.editor)
|
|
return;
|
|
this.editor.trigger('keyboard', monaco.editor.Handler.Redo, null);
|
|
};
|
|
Editor.prototype.zoomIn = function () {
|
|
if (!this.editor)
|
|
return;
|
|
if (this.parent.settings.editorFontSize >= MAX_EDITOR_FONT_SIZE)
|
|
return;
|
|
var currentFont = this.editor.getConfiguration().fontInfo.fontSize;
|
|
this.parent.settings.editorFontSize = currentFont + 1;
|
|
this.editor.updateOptions({ fontSize: this.parent.settings.editorFontSize });
|
|
this.forceDiagnosticsUpdate();
|
|
};
|
|
Editor.prototype.zoomOut = function () {
|
|
if (!this.editor)
|
|
return;
|
|
if (this.parent.settings.editorFontSize <= MIN_EDITOR_FONT_SIZE)
|
|
return;
|
|
var currentFont = this.editor.getConfiguration().fontInfo.fontSize;
|
|
this.parent.settings.editorFontSize = currentFont - 1;
|
|
this.editor.updateOptions({ fontSize: this.parent.settings.editorFontSize });
|
|
this.forceDiagnosticsUpdate();
|
|
};
|
|
Editor.prototype.loadReference = function () {
|
|
Util.assert(this.editor != undefined); // Guarded
|
|
var currentPosition = this.editor.getPosition();
|
|
var wordInfo = this.editor.getModel().getWordAtPosition(currentPosition);
|
|
if (!wordInfo)
|
|
return;
|
|
var prevWordInfo = this.editor.getModel().getWordUntilPosition(new monaco.Position(currentPosition.lineNumber, wordInfo.startColumn - 1));
|
|
if (prevWordInfo && wordInfo) {
|
|
var namespaceName = prevWordInfo.word.replace(/([A-Z]+)/g, "-$1");
|
|
var methodName = wordInfo.word.replace(/([A-Z]+)/g, "-$1");
|
|
this.parent.setSideDoc("/reference/" + namespaceName + "/" + methodName);
|
|
}
|
|
else if (wordInfo) {
|
|
var methodName = wordInfo.word.replace(/([A-Z]+)/g, "-$1");
|
|
this.parent.setSideDoc("/reference/" + methodName);
|
|
}
|
|
};
|
|
Editor.prototype.setupToolbox = function (editorElement) {
|
|
// Monaco flyout widget
|
|
var flyoutWidget = {
|
|
getId: function () {
|
|
return 'pxt.flyout.widget';
|
|
},
|
|
getDomNode: function () {
|
|
if (!this.domNode) {
|
|
this.domNode = document.createElement('div');
|
|
this.domNode.id = 'monacoFlyoutWidget';
|
|
this.domNode.style.top = "0";
|
|
this.domNode.className = 'monacoFlyout';
|
|
// Hide by default
|
|
this.domNode.style.display = 'none';
|
|
this.domNode.innerText = 'Flyout';
|
|
}
|
|
return this.domNode;
|
|
},
|
|
getPosition: function () {
|
|
return null;
|
|
}
|
|
};
|
|
this.editor.addOverlayWidget(flyoutWidget);
|
|
};
|
|
Editor.prototype.resetFlyout = function (clear) {
|
|
// Hide the flyout
|
|
var flyout = document.getElementById('monacoFlyoutWidget');
|
|
flyout.innerHTML = '';
|
|
flyout.style.display = 'none';
|
|
// Hide the currnet toolbox category
|
|
if (this.selectedCategoryRow) {
|
|
this.selectedCategoryRow.style.background = "" + this.selectedCategoryBackgroundColor;
|
|
this.selectedCategoryRow.style.color = "" + this.selectedCategoryColor;
|
|
this.selectedCategoryRow.className = 'blocklyTreeRow';
|
|
}
|
|
if (clear) {
|
|
this.selectedCategoryRow = null;
|
|
}
|
|
};
|
|
Editor.prototype.updateToolbox = function () {
|
|
var _this = this;
|
|
var appTheme = pxt.appTarget.appTheme;
|
|
if (!appTheme.monacoToolbox || pxt.shell.isReadOnly())
|
|
return;
|
|
// Toolbox div
|
|
var toolbox = document.getElementById('monacoEditorToolbox');
|
|
// Move the monaco editor to make room for the toolbox div
|
|
this.editor.getLayoutInfo().glyphMarginLeft = 200;
|
|
this.editor.layout();
|
|
var monacoEditor = this;
|
|
// clear the toolbox
|
|
toolbox.innerHTML = '';
|
|
// Add an overlay widget for the toolbox
|
|
toolbox.style.height = monacoEditor.editor.getLayoutInfo().contentHeight + "px";
|
|
var root = document.createElement('div');
|
|
root.className = 'blocklyTreeRoot';
|
|
toolbox.appendChild(root);
|
|
var group = document.createElement('div');
|
|
group.setAttribute('role', 'group');
|
|
root.appendChild(group);
|
|
var fnDef = this.definitions;
|
|
Object.keys(fnDef).sort(function (f1, f2) {
|
|
// sort by fn weight
|
|
var fn1 = fnDef[f1];
|
|
var fn2 = fnDef[f2];
|
|
var w2 = (fn2.metaData ? fn2.metaData.weight || 50 : 50)
|
|
+ (fn2.metaData && fn2.metaData.advanced ? 0 : 1000);
|
|
+(fn2.metaData && fn2.metaData.blockId ? 10000 : 0);
|
|
var w1 = (fn1.metaData ? fn1.metaData.weight || 50 : 50)
|
|
+ (fn1.metaData && fn1.metaData.advanced ? 0 : 1000);
|
|
+(fn1.metaData && fn1.metaData.blockId ? 10000 : 0);
|
|
return w2 - w1;
|
|
}).filter(function (ns) { return fnDef[ns].metaData != null && fnDef[ns].metaData.color != null; }).forEach(function (ns) {
|
|
var metaElement = fnDef[ns];
|
|
var fnElement = fnDef[ns];
|
|
monacoEditor.addToolboxCategory(group, ns, metaElement.metaData.color, metaElement.metaData.icon, true, fnElement.fns);
|
|
});
|
|
Editor.addBuiltinCategories(group, monacoEditor);
|
|
// Add the toolbox buttons
|
|
if (pxt.appTarget.cloud && pxt.appTarget.cloud.packages) {
|
|
this.addToolboxCategory(group, "", "#717171", "addpackage", false, null, function () {
|
|
_this.resetFlyout();
|
|
_this.parent.addPackage();
|
|
}, lf("{id:category}Add Package"));
|
|
}
|
|
// Inject toolbox icon css
|
|
pxt.blocks.injectToolboxIconCss();
|
|
};
|
|
Editor.addBuiltinCategories = function (group, monacoEditor) {
|
|
monacoEditor.addToolboxCategory(group, "", pxt.blocks.blockColors["logic"].toString(), "logic", false, {
|
|
"if": {
|
|
sig: "",
|
|
snippet: "if (true) {\n\n}",
|
|
comment: lf("Runs code if the condition is true"),
|
|
metaData: {
|
|
callingConvention: ts.pxtc.ir.CallingConvention.Plain,
|
|
paramDefl: {}
|
|
}
|
|
}, "if ": {
|
|
sig: "",
|
|
snippet: "if (true) {\n\n} else {\n\n}",
|
|
comment: lf("Runs code if the condition is true; else run other code"),
|
|
metaData: {
|
|
callingConvention: ts.pxtc.ir.CallingConvention.Plain,
|
|
paramDefl: {}
|
|
}
|
|
}, "switch": {
|
|
sig: "",
|
|
snippet: "switch(item) {\n case 0:\n break;\n case 1:\n break;\n}",
|
|
comment: lf("Runs different code based on a value"),
|
|
metaData: {
|
|
callingConvention: ts.pxtc.ir.CallingConvention.Plain,
|
|
paramDefl: {}
|
|
}
|
|
}
|
|
}, null, lf("{id:category}Logic"));
|
|
monacoEditor.addToolboxCategory(group, "", pxt.blocks.blockColors["loops"].toString(), "loops", false, {
|
|
"while": {
|
|
sig: "while(...)",
|
|
snippet: "while(true) {\n\n}",
|
|
comment: lf("Repeat code while condition is true"),
|
|
metaData: {
|
|
callingConvention: ts.pxtc.ir.CallingConvention.Plain,
|
|
paramDefl: {}
|
|
}
|
|
},
|
|
"for": {
|
|
sig: "",
|
|
snippet: "for(let i = 0; i < 5; i++) {\n\n}",
|
|
comment: lf("Repeat code a number of times in a loop"),
|
|
metaData: {
|
|
callingConvention: ts.pxtc.ir.CallingConvention.Plain,
|
|
paramDefl: {}
|
|
}
|
|
}
|
|
}, null, lf("{id:category}Loops"));
|
|
};
|
|
Editor.prototype.addToolboxCategory = function (group, ns, metaColor, icon, injectIconClass, fns, onClick, category) {
|
|
if (injectIconClass === void 0) { injectIconClass = true; }
|
|
var appTheme = pxt.appTarget.appTheme;
|
|
var monacoEditor = this;
|
|
// Create a tree item
|
|
var treeitem = document.createElement('div');
|
|
var treerow = document.createElement('div');
|
|
treeitem.setAttribute('role', 'treeitem');
|
|
var color = pxt.blocks.convertColour(metaColor);
|
|
treeitem.onclick = function (ev) {
|
|
pxt.tickEvent("monaco.toolbox.click");
|
|
var monacoFlyout = document.getElementById('monacoFlyoutWidget');
|
|
monacoEditor.resetFlyout(false);
|
|
// Hide the toolbox if the current category is clicked twice
|
|
if (monacoEditor.selectedCategoryRow == treerow) {
|
|
monacoEditor.selectedCategoryRow = null;
|
|
monacoFlyout.style.display = 'none';
|
|
treerow.className = 'blocklyTreeRow';
|
|
return;
|
|
}
|
|
else {
|
|
// Selected category
|
|
treerow.style.background = appTheme.invertedToolbox ?
|
|
"" + Blockly.PXTUtils.fadeColour(color, Blockly.Options.invertedMultiplier, false) :
|
|
"" + color;
|
|
treerow.style.color = '#fff';
|
|
treerow.className += ' blocklyTreeSelected';
|
|
monacoEditor.selectedCategoryRow = treerow;
|
|
if (appTheme.invertedToolbox) {
|
|
// Inverted toolbox
|
|
monacoEditor.selectedCategoryColor = '#fff';
|
|
monacoEditor.selectedCategoryBackgroundColor = color;
|
|
}
|
|
else {
|
|
monacoEditor.selectedCategoryColor = color;
|
|
monacoEditor.selectedCategoryBackgroundColor = 'none';
|
|
}
|
|
}
|
|
monacoFlyout.style.left = monacoEditor.editor.getLayoutInfo().lineNumbersLeft + "px";
|
|
monacoFlyout.style.height = monacoEditor.editor.getLayoutInfo().contentHeight + "px";
|
|
monacoFlyout.style.display = 'block';
|
|
monacoFlyout.className = 'monacoFlyout';
|
|
monacoFlyout.style.transform = 'none';
|
|
if (onClick) {
|
|
// No flyout
|
|
onClick();
|
|
}
|
|
else {
|
|
// Create a flyout and add the category methods in there
|
|
Object.keys(fns).sort(function (f1, f2) {
|
|
// sort by fn weight
|
|
var fn1 = fns[f1];
|
|
var fn2 = fns[f2];
|
|
var w2 = (fn2.metaData ? fn2.metaData.weight || 50 : 50)
|
|
+ (fn2.metaData && fn2.metaData.advanced ? 0 : 1000);
|
|
+(fn2.metaData && fn2.metaData.blockId ? 10000 : 0);
|
|
var w1 = (fn1.metaData ? fn1.metaData.weight || 50 : 50)
|
|
+ (fn1.metaData && fn1.metaData.advanced ? 0 : 1000);
|
|
+(fn1.metaData && fn1.metaData.blockId ? 10000 : 0);
|
|
return w2 - w1;
|
|
}).forEach(function (fn) {
|
|
var monacoBlock = document.createElement('div');
|
|
monacoBlock.className = 'monacoDraggableBlock';
|
|
monacoBlock.style.fontSize = monacoEditor.parent.settings.editorFontSize + "px";
|
|
monacoBlock.style.backgroundColor = "" + color;
|
|
monacoBlock.style.borderColor = "" + color;
|
|
monacoBlock.draggable = true;
|
|
var elem = fns[fn];
|
|
var snippet = elem.snippet;
|
|
var comment = elem.comment;
|
|
var metaData = elem.metaData;
|
|
var methodToken = document.createElement('span');
|
|
methodToken.innerText = fn;
|
|
var sigToken = document.createElement('span');
|
|
sigToken.className = 'sig';
|
|
// completion is a bit busted but looks better
|
|
sigToken.innerText = snippet
|
|
.replace(/^[^(]*\(/, '(')
|
|
.replace(/^\s*\{\{\}\}\n/gm, '')
|
|
.replace(/\{\n\}/g, '{}')
|
|
.replace(/(?:\{\{)|(?:\}\})/g, '');
|
|
monacoBlock.title = comment;
|
|
monacoBlock.onclick = function (ev2) {
|
|
pxt.tickEvent("monaco.toolbox.itemclick");
|
|
monacoEditor.resetFlyout(true);
|
|
var model = monacoEditor.editor.getModel();
|
|
var currPos = monacoEditor.editor.getPosition();
|
|
var cursor = model.getOffsetAt(currPos);
|
|
var insertText = ns ? ns + "." + snippet : snippet;
|
|
insertText = (currPos.column > 1) ? '\n' + insertText :
|
|
model.getWordUntilPosition(currPos) != undefined && model.getWordUntilPosition(currPos).word != '' ?
|
|
insertText + '\n' : insertText;
|
|
if (insertText.indexOf('{{}}') > -1) {
|
|
cursor += (insertText.indexOf('{{}}'));
|
|
insertText = insertText.replace('{{}}', '');
|
|
}
|
|
else
|
|
cursor += (insertText.length);
|
|
insertText = insertText.replace(/(?:\{\{)|(?:\}\})/g, '');
|
|
monacoEditor.editor.pushUndoStop();
|
|
monacoEditor.editor.executeEdits("", [
|
|
{
|
|
identifier: { major: 0, minor: 0 },
|
|
range: new monaco.Range(currPos.lineNumber, currPos.column, currPos.lineNumber, currPos.column),
|
|
text: insertText,
|
|
forceMoveMarkers: false
|
|
}
|
|
]);
|
|
monacoEditor.editor.pushUndoStop();
|
|
var endPos = model.getPositionAt(cursor);
|
|
monacoEditor.editor.setPosition(endPos);
|
|
monacoEditor.editor.focus();
|
|
//monacoEditor.editor.setSelection(new monaco.Range(currPos.lineNumber, currPos.column, endPos.lineNumber, endPos.column));
|
|
};
|
|
monacoBlock.ondragstart = function (ev2) {
|
|
pxt.tickEvent("monaco.toolbox.itemdrag");
|
|
var clone = monacoBlock.cloneNode(true);
|
|
setTimeout(function () {
|
|
monacoFlyout.style.transform = "translateX(-9999px)";
|
|
});
|
|
var insertText = ns ? ns + "." + snippet : snippet;
|
|
ev2.dataTransfer.setData('text', insertText); // IE11 only supports text
|
|
};
|
|
monacoBlock.ondragend = function (ev2) {
|
|
monacoFlyout.style.transform = "none";
|
|
monacoEditor.resetFlyout(true);
|
|
};
|
|
monacoBlock.appendChild(methodToken);
|
|
monacoBlock.appendChild(sigToken);
|
|
monacoFlyout.appendChild(monacoBlock);
|
|
});
|
|
}
|
|
};
|
|
group.appendChild(treeitem);
|
|
treerow.className = 'blocklyTreeRow';
|
|
treeitem.appendChild(treerow);
|
|
var iconBlank = document.createElement('span');
|
|
var iconNone = document.createElement('span');
|
|
var label = document.createElement('span');
|
|
var iconClass = ("blocklyTreeIcon" + (icon ? (ns || icon).toLowerCase() : 'Default')).replace(/\s/g, '');
|
|
iconBlank.className = 'blocklyTreeIcon';
|
|
iconBlank.setAttribute('role', 'presentation');
|
|
iconNone.className = "blocklyTreeIcon " + iconClass;
|
|
iconNone.setAttribute('role', 'presentation');
|
|
iconNone.style.display = 'inline-block';
|
|
label.className = 'blocklyTreeLabel';
|
|
treerow.appendChild(iconBlank);
|
|
treerow.appendChild(iconNone);
|
|
treerow.appendChild(label);
|
|
if (appTheme.coloredToolbox) {
|
|
// Colored toolbox
|
|
treerow.style.color = "" + color;
|
|
treerow.style.borderLeft = "8px solid " + color;
|
|
}
|
|
else if (appTheme.invertedToolbox) {
|
|
// Inverted toolbox
|
|
treerow.style.color = '#fff';
|
|
treerow.style.background = (color || '#ddd');
|
|
treerow.onmouseenter = function () {
|
|
if (treerow != monacoEditor.selectedCategoryRow) {
|
|
treerow.style.background = Blockly.PXTUtils.fadeColour(color || '#ddd', Blockly.Options.invertedMultiplier, false);
|
|
}
|
|
};
|
|
treerow.onmouseleave = function () {
|
|
if (treerow != monacoEditor.selectedCategoryRow) {
|
|
treerow.style.background = (color || '#ddd');
|
|
}
|
|
};
|
|
}
|
|
else {
|
|
// Standard toolbox
|
|
treerow.style.borderLeft = "8px solid " + color;
|
|
}
|
|
if (icon && injectIconClass) {
|
|
pxt.blocks.appendToolboxIconCss(iconClass, icon);
|
|
}
|
|
treerow.style.paddingLeft = '0px';
|
|
label.innerText = "" + Util.capitalize(category || ns);
|
|
};
|
|
Editor.prototype.getId = function () {
|
|
return "monacoEditor";
|
|
};
|
|
Editor.prototype.getViewState = function () {
|
|
return this.editor ? this.editor.getPosition() : {};
|
|
};
|
|
Editor.prototype.getCurrentSource = function () {
|
|
return this.editor ? this.editor.getValue() : this.currSource;
|
|
};
|
|
Editor.prototype.acceptsFile = function (file) {
|
|
return true;
|
|
};
|
|
Editor.prototype.setValue = function (v) {
|
|
this.editor.setValue(v);
|
|
};
|
|
Editor.prototype.overrideFile = function (content) {
|
|
Util.assert(this.editor != undefined); // Guarded
|
|
this.editor.setValue(content);
|
|
};
|
|
Editor.prototype.loadFileAsync = function (file) {
|
|
var _this = this;
|
|
var mode = "text";
|
|
this.currSource = file.content;
|
|
var loading = document.createElement("div");
|
|
loading.className = "ui inverted loading dimmer active";
|
|
var editorArea = document.getElementById("monacoEditorArea");
|
|
var editorDiv = document.getElementById("monacoEditorInner");
|
|
editorArea.insertBefore(loading, editorDiv);
|
|
return this.loadMonacoAsync()
|
|
.then(function () {
|
|
if (!_this.editor)
|
|
return;
|
|
var toolbox = document.getElementById('monacoEditorToolbox');
|
|
var ext = file.getExtension();
|
|
var modeMap = {
|
|
"cpp": "cpp",
|
|
"h": "cpp",
|
|
"json": "json",
|
|
"md": "text",
|
|
"ts": "typescript",
|
|
"js": "javascript",
|
|
"svg": "xml",
|
|
"blocks": "xml",
|
|
"asm": "asm"
|
|
};
|
|
if (modeMap.hasOwnProperty(ext))
|
|
mode = modeMap[ext];
|
|
var readOnly = file.isReadonly() || pxt.shell.isReadOnly();
|
|
_this.editor.updateOptions({ readOnly: readOnly });
|
|
var proto = "pkg:" + file.getName();
|
|
var model = monaco.editor.getModels().filter(function (model) { return model.uri.toString() == proto; })[0];
|
|
if (!model)
|
|
model = monaco.editor.createModel(pkg.mainPkg.readFile(file.getName()), mode, monaco.Uri.parse(proto));
|
|
if (model)
|
|
_this.editor.setModel(model);
|
|
if (mode == "typescript") {
|
|
toolbox.innerHTML = '';
|
|
_this.beginLoadToolbox(file);
|
|
}
|
|
// Set the current file
|
|
_this.currFile = file;
|
|
_this.setValue(file.content);
|
|
_this.setDiagnostics(file, _this.snapshotState());
|
|
_this.fileType = mode == "typescript" ? FileType.TypeScript : ext == "md" ? FileType.Markdown : FileType.Unknown;
|
|
if (_this.fileType == FileType.Markdown)
|
|
_this.parent.setSideMarkdown(file.content);
|
|
_this.currFile.setForceChangeCallback(function (from, to) {
|
|
if (from != to) {
|
|
pxt.debug("File changed (from " + from + ", to " + to + "). Reloading editor");
|
|
_this.loadFileAsync(_this.currFile);
|
|
}
|
|
});
|
|
if (!file.isReadonly()) {
|
|
model.onDidChangeContent(function (e) {
|
|
// Remove any Highlighted lines
|
|
if (_this.highlightDecorations)
|
|
_this.editor.deltaDecorations(_this.highlightDecorations, []);
|
|
// Remove any current error shown, as a change has been made.
|
|
var viewZones = _this.editorViewZones || [];
|
|
_this.editor.changeViewZones(function (changeAccessor) {
|
|
viewZones.forEach(function (id) {
|
|
changeAccessor.removeZone(id);
|
|
});
|
|
});
|
|
_this.editorViewZones = [];
|
|
if (!e.isRedoing && !e.isUndoing && !_this.editor.getValue()) {
|
|
_this.editor.setValue(" ");
|
|
}
|
|
_this.updateDiagnostics();
|
|
_this.changeCallback();
|
|
});
|
|
}
|
|
if (mode == "typescript" && !file.isReadonly()) {
|
|
toolbox.className = 'monacoToolboxDiv';
|
|
}
|
|
else {
|
|
toolbox.className = 'monacoToolboxDiv hide';
|
|
}
|
|
_this.resize();
|
|
_this.resetFlyout(true);
|
|
}).finally(function () {
|
|
editorArea.removeChild(loading);
|
|
});
|
|
};
|
|
Editor.prototype.beginLoadToolbox = function (file) {
|
|
var _this = this;
|
|
pxt.vs.syncModels(pkg.mainPkg, this.extraLibs, file.getName(), file.isReadonly())
|
|
.then(function (definitions) {
|
|
_this.definitions = definitions;
|
|
_this.defineEditorTheme();
|
|
_this.updateToolbox();
|
|
_this.resize();
|
|
});
|
|
};
|
|
Editor.prototype.unloadFileAsync = function () {
|
|
if (this.currFile && this.currFile.getName() == "this/" + pxt.CONFIG_NAME) {
|
|
// Reload the header if a change was made to the config file: pxt.json
|
|
return this.parent.reloadHeaderAsync();
|
|
}
|
|
return Promise.resolve();
|
|
};
|
|
Editor.prototype.snapshotState = function () {
|
|
return this.editor && this.editor.getModel() ? this.editor.getModel().getLinesContent() : null;
|
|
};
|
|
Editor.prototype.setViewState = function (pos) {
|
|
if (!this.editor)
|
|
return;
|
|
if (!pos || Object.keys(pos).length === 0)
|
|
return;
|
|
this.editor.setPosition(pos);
|
|
this.editor.setScrollPosition(pos);
|
|
};
|
|
Editor.prototype.setDiagnostics = function (file, snapshot) {
|
|
Util.assert(this.editor != undefined); // Guarded
|
|
Util.assert(this.currFile == file);
|
|
this.diagSnapshot = snapshot;
|
|
this.forceDiagnosticsUpdate();
|
|
};
|
|
Editor.prototype.updateDiagnostics = function () {
|
|
if (this.needsDiagUpdate())
|
|
this.forceDiagnosticsUpdate();
|
|
};
|
|
Editor.prototype.needsDiagUpdate = function () {
|
|
if (!this.annotationLines)
|
|
return false;
|
|
var lines = this.editor.getModel().getLinesContent();
|
|
for (var _i = 0, _a = this.annotationLines; _i < _a.length; _i++) {
|
|
var line = _a[_i];
|
|
if (this.diagSnapshot[line] !== lines[line])
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
Editor.prototype.forceDiagnosticsUpdate = function () {
|
|
if (this.fileType != FileType.TypeScript)
|
|
return;
|
|
var file = this.currFile;
|
|
var lines = this.editor.getModel().getLinesContent();
|
|
var fontSize = this.parent.settings.editorFontSize - 3;
|
|
var lineHeight = this.editor.getConfiguration().lineHeight;
|
|
var borderSize = lineHeight / 10;
|
|
var viewZones = this.editorViewZones || [];
|
|
this.annotationLines = [];
|
|
this.editor.changeViewZones(function (changeAccessor) {
|
|
viewZones.forEach(function (id) {
|
|
changeAccessor.removeZone(id);
|
|
});
|
|
});
|
|
this.editorViewZones = [];
|
|
this.errorLines = [];
|
|
if (file && file.diagnostics) {
|
|
var _loop_1 = function(d) {
|
|
if (this_1.errorLines.filter(function (lineNumber) { return lineNumber == d.line; }).length > 0 || this_1.errorLines.length > 0)
|
|
return "continue";
|
|
var viewZoneId = null;
|
|
this_1.editor.changeViewZones(function (changeAccessor) {
|
|
var wrapper = document.createElement('div');
|
|
wrapper.className = "zone-widget error-view-zone";
|
|
var container = document.createElement('div');
|
|
container.className = "zone-widget-container marker-widget";
|
|
container.setAttribute('role', 'tooltip');
|
|
container.style.setProperty("border", "solid " + borderSize + "px rgb(255, 90, 90)");
|
|
container.style.setProperty("border", "solid " + borderSize + "px rgb(255, 90, 90)");
|
|
container.style.setProperty("top", "" + lineHeight / 4);
|
|
var domNode = document.createElement('div');
|
|
domNode.className = "block descriptioncontainer";
|
|
domNode.style.setProperty("font-size", fontSize.toString() + "px");
|
|
domNode.style.setProperty("line-height", lineHeight.toString() + "px");
|
|
domNode.innerText = ts.flattenDiagnosticMessageText(d.messageText, "\n");
|
|
container.appendChild(domNode);
|
|
wrapper.appendChild(container);
|
|
viewZoneId = changeAccessor.addZone({
|
|
afterLineNumber: d.line + 1,
|
|
heightInLines: 1,
|
|
domNode: wrapper
|
|
});
|
|
});
|
|
this_1.editorViewZones.push(viewZoneId);
|
|
this_1.errorLines.push(d.line);
|
|
if (lines[d.line] === this_1.diagSnapshot[d.line]) {
|
|
this_1.annotationLines.push(d.line);
|
|
}
|
|
};
|
|
var this_1 = this;
|
|
for (var _i = 0, _a = file.diagnostics; _i < _a.length; _i++) {
|
|
var d = _a[_i];
|
|
var state_1 = _loop_1(d);
|
|
if (state_1 === "continue") continue;
|
|
}
|
|
}
|
|
};
|
|
Editor.prototype.highlightStatement = function (brk) {
|
|
if (!brk || !this.currFile || this.currFile.name != brk.fileName || !this.editor)
|
|
return;
|
|
var position = this.editor.getModel().getPositionAt(brk.start);
|
|
if (!position)
|
|
return;
|
|
this.highlightDecorations = this.editor.deltaDecorations(this.highlightDecorations, [
|
|
{
|
|
range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column + brk.length),
|
|
options: { inlineClassName: 'highlight-statement' }
|
|
},
|
|
]);
|
|
};
|
|
return Editor;
|
|
}(srceditor.Editor));
|
|
exports.Editor = Editor;
|
|
|
|
},{"./compiler":8,"./core":10,"./package":24,"./srceditor":31,"react":264}],24:[function(require,module,exports){
|
|
"use strict";
|
|
var workspace = require("./workspace");
|
|
var data = require("./data");
|
|
var core = require("./core");
|
|
var db = require("./db");
|
|
var Util = pxt.Util;
|
|
var lf = Util.lf;
|
|
var hostCache = new db.Table("hostcache");
|
|
var extWeight = {
|
|
"ts": 10,
|
|
"blocks": 20,
|
|
"json": 30,
|
|
"md": 40,
|
|
};
|
|
function setupAppTarget(trgbundle) {
|
|
//if (!trgbundle.appTheme) trgbundle.appTheme = {};
|
|
pxt.setAppTarget(trgbundle);
|
|
}
|
|
exports.setupAppTarget = setupAppTarget;
|
|
var File = (function () {
|
|
function File(epkg, name, content) {
|
|
this.epkg = epkg;
|
|
this.name = name;
|
|
this.content = content;
|
|
this.inSyncWithEditor = true;
|
|
this.inSyncWithDisk = true;
|
|
}
|
|
File.prototype.isReadonly = function () {
|
|
return !this.epkg.header;
|
|
};
|
|
File.prototype.getName = function () {
|
|
return this.epkg.getPkgId() + "/" + this.name;
|
|
};
|
|
File.prototype.getTypeScriptName = function () {
|
|
if (this.epkg.isTopLevel())
|
|
return this.name;
|
|
else
|
|
return "pxt_modules/" + this.epkg.getPkgId() + "/" + this.name;
|
|
};
|
|
File.prototype.getExtension = function () {
|
|
var m = /\.([^\.]+)$/.exec(this.name);
|
|
if (m)
|
|
return m[1];
|
|
return "";
|
|
};
|
|
File.prototype.getVirtualFileName = function () {
|
|
if (File.blocksFileNameRx.test(this.name))
|
|
return this.name.replace(File.blocksFileNameRx, '.ts');
|
|
if (File.tsFileNameRx.test(this.name))
|
|
return this.name.replace(File.tsFileNameRx, '.blocks');
|
|
return undefined;
|
|
};
|
|
File.prototype.weight = function () {
|
|
if (/^main\./.test(this.name))
|
|
return 5;
|
|
if (extWeight.hasOwnProperty(this.getExtension()))
|
|
return extWeight[this.getExtension()];
|
|
return 60;
|
|
};
|
|
File.prototype.markDirty = function () {
|
|
this.inSyncWithEditor = false;
|
|
this.updateStatus();
|
|
};
|
|
File.prototype.updateStatus = function () {
|
|
data.invalidate("open-meta:" + this.getName());
|
|
};
|
|
File.prototype.setContentAsync = function (newContent, force) {
|
|
var _this = this;
|
|
Util.assert(newContent !== undefined);
|
|
this.inSyncWithEditor = true;
|
|
if (newContent != this.content) {
|
|
var prevContent_1 = this.content;
|
|
this.inSyncWithDisk = false;
|
|
this.content = newContent;
|
|
this.updateStatus();
|
|
return this.epkg.saveFilesAsync()
|
|
.then(function () {
|
|
if (_this.content == newContent) {
|
|
_this.inSyncWithDisk = true;
|
|
_this.updateStatus();
|
|
}
|
|
if (force && _this.forceChangeCallback)
|
|
_this.forceChangeCallback(prevContent_1, newContent);
|
|
});
|
|
}
|
|
else {
|
|
this.updateStatus();
|
|
return Promise.resolve();
|
|
}
|
|
};
|
|
File.prototype.setForceChangeCallback = function (callback) {
|
|
this.forceChangeCallback = callback;
|
|
};
|
|
File.tsFileNameRx = /\.ts$/;
|
|
File.blocksFileNameRx = /\.blocks$/;
|
|
return File;
|
|
}());
|
|
exports.File = File;
|
|
var EditorPackage = (function () {
|
|
function EditorPackage(ksPkg, topPkg) {
|
|
this.ksPkg = ksPkg;
|
|
this.topPkg = topPkg;
|
|
this.files = {};
|
|
this.onupdate = function () { };
|
|
this.saveScheduled = false;
|
|
this.savingNow = 0;
|
|
if (ksPkg && ksPkg.verProtocol() == "workspace")
|
|
this.header = workspace.getHeader(ksPkg.verArgument());
|
|
}
|
|
EditorPackage.prototype.getTopHeader = function () {
|
|
return this.topPkg.header;
|
|
};
|
|
EditorPackage.prototype.makeTopLevel = function () {
|
|
this.topPkg = this;
|
|
this.outputPkg = new EditorPackage(null, this);
|
|
this.outputPkg.id = "built";
|
|
};
|
|
EditorPackage.prototype.updateConfigAsync = function (update) {
|
|
var cfgFile = this.files[pxt.CONFIG_NAME];
|
|
if (cfgFile) {
|
|
try {
|
|
var cfg = JSON.parse(cfgFile.content);
|
|
update(cfg);
|
|
return cfgFile.setContentAsync(JSON.stringify(cfg, null, 4) + "\n");
|
|
}
|
|
catch (e) { }
|
|
}
|
|
return null;
|
|
};
|
|
EditorPackage.prototype.updateDepAsync = function (pkgid) {
|
|
var _this = this;
|
|
var p = this.ksPkg.resolveDep(pkgid);
|
|
if (!p || p.verProtocol() != "github")
|
|
return Promise.resolve();
|
|
var parsed = pxt.github.parseRepoId(p.verArgument());
|
|
return pxt.packagesConfigAsync()
|
|
.then(function (config) { return pxt.github.latestVersionAsync(parsed.fullName, config); })
|
|
.then(function (tag) { parsed.tag = tag; })
|
|
.then(function () { return pxt.github.pkgConfigAsync(parsed.fullName, parsed.tag); })
|
|
.catch(core.handleNetworkError)
|
|
.then(function (cfg) { return _this.addDepAsync(cfg.name, pxt.github.stringifyRepo(parsed)); });
|
|
};
|
|
EditorPackage.prototype.removeDepAsync = function (pkgid) {
|
|
var _this = this;
|
|
return this.updateConfigAsync(function (cfg) { return delete cfg.dependencies[pkgid]; })
|
|
.then(function () { return _this.saveFilesAsync(true); });
|
|
};
|
|
EditorPackage.prototype.addDepAsync = function (pkgid, pkgversion) {
|
|
var _this = this;
|
|
return this.updateConfigAsync(function (cfg) { return cfg.dependencies[pkgid] = pkgversion; })
|
|
.then(function () { return _this.saveFilesAsync(true); });
|
|
};
|
|
EditorPackage.prototype.getKsPkg = function () {
|
|
return this.ksPkg;
|
|
};
|
|
EditorPackage.prototype.getPkgId = function () {
|
|
return this.ksPkg ? this.ksPkg.id : this.id;
|
|
};
|
|
EditorPackage.prototype.isTopLevel = function () {
|
|
return this.ksPkg && this.ksPkg.level == 0;
|
|
};
|
|
EditorPackage.prototype.setFile = function (n, v) {
|
|
var f = new File(this, n, v);
|
|
this.files[n] = f;
|
|
data.invalidate("open-meta:");
|
|
return f;
|
|
};
|
|
EditorPackage.prototype.removeFileAsync = function (n) {
|
|
delete this.files[n];
|
|
data.invalidate("open-meta:");
|
|
return this.updateConfigAsync(function (cfg) { return cfg.files = cfg.files.filter(function (f) { return f != n; }); });
|
|
};
|
|
EditorPackage.prototype.setContentAsync = function (n, v) {
|
|
var f = this.files[n];
|
|
if (!f)
|
|
f = this.setFile(n, v);
|
|
return f.setContentAsync(v);
|
|
};
|
|
EditorPackage.prototype.setFiles = function (files) {
|
|
var _this = this;
|
|
this.files = Util.mapMap(files, function (k, v) { return new File(_this, k, v); });
|
|
data.invalidate("open-meta:");
|
|
};
|
|
EditorPackage.prototype.updateStatus = function () {
|
|
data.invalidate("pkg-status:" + this.header.id);
|
|
};
|
|
EditorPackage.prototype.savePkgAsync = function () {
|
|
var _this = this;
|
|
if (this.header.blobCurrent)
|
|
return Promise.resolve();
|
|
this.savingNow++;
|
|
this.updateStatus();
|
|
return workspace.saveToCloudAsync(this.header)
|
|
.then(function () {
|
|
_this.savingNow--;
|
|
_this.updateStatus();
|
|
if (!_this.header.blobCurrent)
|
|
_this.scheduleSave();
|
|
});
|
|
};
|
|
EditorPackage.prototype.scheduleSave = function () {
|
|
var _this = this;
|
|
if (this.saveScheduled)
|
|
return;
|
|
this.saveScheduled = true;
|
|
setTimeout(function () {
|
|
_this.saveScheduled = false;
|
|
_this.savePkgAsync().done();
|
|
}, 5000);
|
|
};
|
|
EditorPackage.prototype.getAllFiles = function () {
|
|
return Util.mapMap(this.files, function (k, f) { return f.content; });
|
|
};
|
|
EditorPackage.prototype.saveFilesAsync = function (immediate) {
|
|
var _this = this;
|
|
if (!this.header)
|
|
return Promise.resolve();
|
|
var cfgFile = this.files[pxt.CONFIG_NAME];
|
|
if (cfgFile) {
|
|
try {
|
|
var cfg = JSON.parse(cfgFile.content);
|
|
this.header.name = cfg.name;
|
|
}
|
|
catch (e) {
|
|
}
|
|
}
|
|
return workspace.saveAsync(this.header, this.getAllFiles())
|
|
.then(function () { return immediate ? _this.savePkgAsync() : _this.scheduleSave(); });
|
|
};
|
|
EditorPackage.prototype.sortedFiles = function () {
|
|
var lst = Util.values(this.files);
|
|
lst.sort(function (a, b) { return a.weight() - b.weight() || Util.strcmp(a.name, b.name); });
|
|
return lst;
|
|
};
|
|
EditorPackage.prototype.forEachFile = function (cb) {
|
|
this.pkgAndDeps().forEach(function (p) {
|
|
Util.values(p.files).forEach(cb);
|
|
});
|
|
};
|
|
EditorPackage.prototype.getMainFile = function () {
|
|
return this.sortedFiles()[0];
|
|
};
|
|
EditorPackage.prototype.pkgAndDeps = function () {
|
|
if (this.topPkg != this)
|
|
return this.topPkg.pkgAndDeps();
|
|
return Util.values(this.ksPkg.deps).map(getEditorPkg).concat([this.outputPkg]);
|
|
};
|
|
EditorPackage.prototype.filterFiles = function (cond) {
|
|
return Util.concat(this.pkgAndDeps().map(function (e) { return Util.values(e.files).filter(cond); }));
|
|
};
|
|
EditorPackage.prototype.lookupFile = function (name) {
|
|
return this.filterFiles(function (f) { return f.getName() == name; })[0];
|
|
};
|
|
return EditorPackage;
|
|
}());
|
|
exports.EditorPackage = EditorPackage;
|
|
var Host = (function () {
|
|
function Host() {
|
|
}
|
|
Host.prototype.readFile = function (module, filename) {
|
|
var epkg = getEditorPkg(module);
|
|
var file = epkg.files[filename];
|
|
return file ? file.content : null;
|
|
};
|
|
Host.prototype.writeFile = function (module, filename, contents, force) {
|
|
if (filename == pxt.CONFIG_NAME || force) {
|
|
// only write config writes
|
|
var epkg = getEditorPkg(module);
|
|
var file = epkg.files[filename];
|
|
file.setContentAsync(contents, force).done();
|
|
return;
|
|
}
|
|
throw Util.oops("trying to write " + module + " / " + filename);
|
|
};
|
|
Host.prototype.getHexInfoAsync = function (extInfo) {
|
|
return pxt.hex.getHexInfoAsync(this, extInfo).catch(core.handleNetworkError);
|
|
};
|
|
Host.prototype.cacheStoreAsync = function (id, val) {
|
|
return hostCache.forceSetAsync({
|
|
id: id,
|
|
val: val
|
|
}).then(function () { }, function (e) {
|
|
pxt.tickEvent('cache.store.failed', { error: e.name });
|
|
pxt.log("cache store failed for " + id + ": " + e.name);
|
|
});
|
|
};
|
|
Host.prototype.cacheGetAsync = function (id) {
|
|
return hostCache.getAsync(id)
|
|
.then(function (v) { return v.val; }, function (e) { return null; });
|
|
};
|
|
Host.prototype.downloadPackageAsync = function (pkg) {
|
|
var proto = pkg.verProtocol();
|
|
var epkg = getEditorPkg(pkg);
|
|
if (proto == "pub") {
|
|
// make sure it sits in cache
|
|
return workspace.getPublishedScriptAsync(pkg.verArgument())
|
|
.then(function (files) { return epkg.setFiles(files); });
|
|
}
|
|
else if (proto == "github") {
|
|
return workspace.getPublishedScriptAsync(pkg.version())
|
|
.then(function (files) { return epkg.setFiles(files); });
|
|
}
|
|
else if (proto == "workspace") {
|
|
return workspace.getTextAsync(pkg.verArgument())
|
|
.then(function (scr) { return epkg.setFiles(scr); });
|
|
}
|
|
else if (proto == "file") {
|
|
var arg = pkg.verArgument();
|
|
if (arg[0] == ".")
|
|
arg = resolvePath(pkg.parent.verArgument() + "/" + arg);
|
|
return workspace.getTextAsync(arg)
|
|
.then(function (scr) { return epkg.setFiles(scr); });
|
|
}
|
|
else if (proto == "embed") {
|
|
epkg.setFiles(pxt.getEmbeddedScript(pkg.verArgument()));
|
|
return Promise.resolve();
|
|
}
|
|
else {
|
|
return Promise.reject("Cannot download " + pkg.version() + "; unknown protocol");
|
|
}
|
|
};
|
|
return Host;
|
|
}());
|
|
function resolvePath(p) {
|
|
return p.replace(/\/+/g, "/").replace(/[^\/]+\/\.\.\//g, "").replace(/\/\.\//g, "/");
|
|
}
|
|
var theHost = new Host();
|
|
exports.mainPkg = new pxt.MainPackage(theHost);
|
|
function getEditorPkg(p) {
|
|
var r = p._editorPkg;
|
|
if (r)
|
|
return r;
|
|
var top = null;
|
|
if (p != exports.mainPkg)
|
|
top = getEditorPkg(exports.mainPkg);
|
|
var newOne = new EditorPackage(p, top);
|
|
if (p == exports.mainPkg)
|
|
newOne.makeTopLevel();
|
|
p._editorPkg = newOne;
|
|
return newOne;
|
|
}
|
|
exports.getEditorPkg = getEditorPkg;
|
|
function mainEditorPkg() {
|
|
return getEditorPkg(exports.mainPkg);
|
|
}
|
|
exports.mainEditorPkg = mainEditorPkg;
|
|
function genFileName(extension) {
|
|
var sanitizedName = mainEditorPkg().header.name.replace(/[\\\/.?*^:<>|"\x00-\x1F ]/g, "-");
|
|
var fn = (pxt.appTarget.nickname || pxt.appTarget.id) + "-" + sanitizedName + extension;
|
|
return fn;
|
|
}
|
|
exports.genFileName = genFileName;
|
|
function allEditorPkgs() {
|
|
return getEditorPkg(exports.mainPkg).pkgAndDeps();
|
|
}
|
|
exports.allEditorPkgs = allEditorPkgs;
|
|
function notifySyncDone(updated) {
|
|
var newOnes = Util.values(exports.mainPkg.deps).filter(function (d) { return d.verProtocol() == "workspace" && updated.hasOwnProperty(d.verArgument()); });
|
|
if (newOnes.length > 0) {
|
|
getEditorPkg(exports.mainPkg).onupdate();
|
|
}
|
|
}
|
|
exports.notifySyncDone = notifySyncDone;
|
|
function loadPkgAsync(id) {
|
|
exports.mainPkg = new pxt.MainPackage(theHost);
|
|
exports.mainPkg._verspec = "workspace:" + id;
|
|
return theHost.downloadPackageAsync(exports.mainPkg)
|
|
.catch(core.handleNetworkError)
|
|
.then(function () { return theHost.readFile(exports.mainPkg, pxt.CONFIG_NAME); })
|
|
.then(function (str) {
|
|
if (!str)
|
|
return Promise.resolve();
|
|
return exports.mainPkg.installAllAsync()
|
|
.catch(function (e) {
|
|
core.errorNotification(lf("Cannot load package: {0}", e.message));
|
|
});
|
|
});
|
|
}
|
|
exports.loadPkgAsync = loadPkgAsync;
|
|
/*
|
|
open-meta:<pkgName>/<filename> - readonly/saved/unsaved + number of errors
|
|
*/
|
|
data.mountVirtualApi("open-meta", {
|
|
getSync: function (p) {
|
|
p = data.stripProtocol(p);
|
|
var f = getEditorPkg(exports.mainPkg).lookupFile(p);
|
|
if (!f)
|
|
return {};
|
|
var fs = {
|
|
isReadonly: f.isReadonly(),
|
|
isSaved: f.inSyncWithEditor && f.inSyncWithDisk,
|
|
numErrors: f.numDiagnosticsOverride
|
|
};
|
|
if (fs.numErrors == null)
|
|
fs.numErrors = f.diagnostics ? f.diagnostics.length : 0;
|
|
return fs;
|
|
},
|
|
});
|
|
// pkg-status:<guid>
|
|
data.mountVirtualApi("pkg-status", {
|
|
getSync: function (p) {
|
|
p = data.stripProtocol(p);
|
|
var ep = allEditorPkgs().filter(function (pkg) { return pkg.header && pkg.header.id == p; })[0];
|
|
if (ep)
|
|
return ep.savingNow ? "saving" : "";
|
|
return "";
|
|
},
|
|
});
|
|
|
|
},{"./core":10,"./data":11,"./db":12,"./workspace":37}],25:[function(require,module,exports){
|
|
/// <reference path="../../typings/globals/react/index.d.ts" />
|
|
/// <reference path="../../typings/globals/react-dom/index.d.ts" />
|
|
/// <reference path="../../built/pxtlib.d.ts" />
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var ReactDOM = require("react-dom");
|
|
var workspace = require("./workspace");
|
|
var data = require("./data");
|
|
var sui = require("./sui");
|
|
var pkg = require("./package");
|
|
var core = require("./core");
|
|
var codecard = require("./codecard");
|
|
var gallery = require("./gallery");
|
|
var ProjectsTab;
|
|
(function (ProjectsTab) {
|
|
ProjectsTab[ProjectsTab["MyStuff"] = 0] = "MyStuff";
|
|
ProjectsTab[ProjectsTab["Make"] = 1] = "Make";
|
|
ProjectsTab[ProjectsTab["Code"] = 2] = "Code";
|
|
})(ProjectsTab || (ProjectsTab = {}));
|
|
var Projects = (function (_super) {
|
|
__extends(Projects, _super);
|
|
function Projects(props) {
|
|
_super.call(this, props);
|
|
this.prevGhData = [];
|
|
this.prevUrlData = [];
|
|
this.prevMakes = [];
|
|
this.prevCodes = [];
|
|
this.state = {
|
|
visible: false,
|
|
tab: ProjectsTab.MyStuff
|
|
};
|
|
}
|
|
Projects.prototype.hide = function () {
|
|
this.setState({ visible: false });
|
|
};
|
|
Projects.prototype.showOpenProject = function () {
|
|
this.setState({ visible: true, tab: ProjectsTab.MyStuff });
|
|
};
|
|
Projects.prototype.fetchMakes = function () {
|
|
if (this.state.tab != ProjectsTab.Make)
|
|
return [];
|
|
var res = this.getData("gallery:" + encodeURIComponent(pxt.appTarget.appTheme.projectGallery));
|
|
if (res)
|
|
this.prevMakes = Util.concat(res.map(function (g) { return g.cards; }));
|
|
return this.prevMakes;
|
|
};
|
|
Projects.prototype.fetchCodes = function () {
|
|
if (this.state.tab != ProjectsTab.Code)
|
|
return [];
|
|
var res = this.getData("gallery:" + encodeURIComponent(pxt.appTarget.appTheme.exampleGallery));
|
|
if (res)
|
|
this.prevCodes = Util.concat(res.map(function (g) { return g.cards; }));
|
|
return this.prevCodes;
|
|
};
|
|
Projects.prototype.fetchUrlData = function () {
|
|
var scriptid = pxt.Cloud.parseScriptId(this.state.searchFor);
|
|
if (scriptid) {
|
|
var res = this.getData("cloud-search:" + scriptid);
|
|
if (res) {
|
|
if (res.statusCode !== 404) {
|
|
if (!this.prevUrlData)
|
|
this.prevUrlData = [res];
|
|
else
|
|
this.prevUrlData.push(res);
|
|
}
|
|
}
|
|
}
|
|
return this.prevUrlData;
|
|
};
|
|
Projects.prototype.fetchLocalData = function () {
|
|
var _this = this;
|
|
if (this.state.tab != ProjectsTab.MyStuff)
|
|
return [];
|
|
var headers = this.getData("header:*");
|
|
if (this.state.searchFor)
|
|
headers = headers.filter(function (hdr) { return hdr.name.toLowerCase().indexOf(_this.state.searchFor.toLowerCase()) > -1; });
|
|
return headers;
|
|
};
|
|
Projects.prototype.shouldComponentUpdate = function (nextProps, nextState, nextContext) {
|
|
return this.state.visible != nextState.visible
|
|
|| this.state.tab != nextState.tab
|
|
|| this.state.searchFor != nextState.searchFor;
|
|
};
|
|
Projects.prototype.numDaysOld = function (d1) {
|
|
var diff = Math.abs((Date.now() / 1000) - d1);
|
|
return Math.floor(diff / (60 * 60 * 24));
|
|
};
|
|
Projects.prototype.renderCore = function () {
|
|
var _this = this;
|
|
var _a = this.state, visible = _a.visible, tab = _a.tab;
|
|
var tabNames = [
|
|
lf("My Stuff"),
|
|
lf("Make"),
|
|
lf("Code")
|
|
];
|
|
var headers = this.fetchLocalData();
|
|
var urldata = this.fetchUrlData();
|
|
var makes = this.fetchMakes();
|
|
var codes = this.fetchCodes();
|
|
var chgHeader = function (hdr) {
|
|
pxt.tickEvent("projects.header");
|
|
_this.hide();
|
|
_this.props.parent.loadHeaderAsync(hdr);
|
|
};
|
|
var chgMake = function (scr) {
|
|
pxt.tickEvent("projects.gallery", { name: scr.name });
|
|
_this.hide();
|
|
_this.props.parent.newEmptyProject(scr.name.toLowerCase(), scr.url);
|
|
};
|
|
var chgCode = function (scr) {
|
|
pxt.tickEvent("projects.example", { name: scr.name });
|
|
_this.hide();
|
|
core.showLoading(lf("Loading..."));
|
|
gallery.loadExampleAsync(scr.name.toLowerCase(), scr.url)
|
|
.done(function (opts) {
|
|
core.hideLoading();
|
|
if (opts)
|
|
_this.props.parent.newProject(opts);
|
|
});
|
|
};
|
|
var upd = function (v) {
|
|
var str = ReactDOM.findDOMNode(_this.refs["searchInput"]).value;
|
|
_this.setState({ searchFor: str });
|
|
};
|
|
var kupd = function (ev) {
|
|
if (ev.keyCode == 13)
|
|
upd(ev);
|
|
};
|
|
var installScript = function (scr) {
|
|
_this.hide();
|
|
core.showLoading(lf("loading project..."));
|
|
workspace.installByIdAsync(scr.id)
|
|
.then(function (r) { return _this.props.parent.loadHeaderAsync(r); })
|
|
.done(function () { return core.hideLoading(); });
|
|
};
|
|
var importHex = function () {
|
|
pxt.tickEvent("projects.import");
|
|
_this.hide();
|
|
_this.props.parent.importFileDialog();
|
|
};
|
|
var importUrl = function () {
|
|
pxt.tickEvent("projects.importurl");
|
|
_this.hide();
|
|
_this.props.parent.importUrlDialog();
|
|
};
|
|
var newProject = function () {
|
|
pxt.tickEvent("projects.new");
|
|
_this.hide();
|
|
_this.props.parent.newProject();
|
|
};
|
|
var saveProject = function () {
|
|
pxt.tickEvent("projects.save");
|
|
_this.hide();
|
|
_this.props.parent.saveAndCompile();
|
|
};
|
|
var renameProject = function () {
|
|
pxt.tickEvent("projects.rename");
|
|
_this.hide();
|
|
_this.props.parent.setFile(pkg.mainEditorPkg().files[pxt.CONFIG_NAME]);
|
|
};
|
|
var isEmpty = function () {
|
|
if (_this.state.searchFor) {
|
|
if (headers.length > 0
|
|
|| urldata.length > 0)
|
|
return false;
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
var targetTheme = pxt.appTarget.appTheme;
|
|
var tabs = [ProjectsTab.MyStuff];
|
|
if (pxt.appTarget.appTheme.projectGallery)
|
|
tabs.push(ProjectsTab.Make);
|
|
if (pxt.appTarget.appTheme.exampleGallery)
|
|
tabs.push(ProjectsTab.Code);
|
|
var headersToday = headers.filter(function (h) { var days = _this.numDaysOld(h.modificationTime); return days == 0; });
|
|
var headersYesterday = headers.filter(function (h) { var days = _this.numDaysOld(h.modificationTime); return days == 1; });
|
|
var headersThisWeek = headers.filter(function (h) { var days = _this.numDaysOld(h.modificationTime); return days > 1 && days <= 7; });
|
|
var headersLastWeek = headers.filter(function (h) { var days = _this.numDaysOld(h.modificationTime); return days > 7 && days <= 14; });
|
|
var headersThisMonth = headers.filter(function (h) { var days = _this.numDaysOld(h.modificationTime); return days > 14 && days <= 30; });
|
|
var headersOlder = headers.filter(function (h) { var days = _this.numDaysOld(h.modificationTime); return days > 30; });
|
|
var headersGrouped = [
|
|
{ name: lf("Today"), headers: headersToday },
|
|
{ name: lf("Yesterday"), headers: headersYesterday },
|
|
{ name: lf("This Week"), headers: headersThisWeek },
|
|
{ name: lf("Last Week"), headers: headersLastWeek },
|
|
{ name: lf("This Month"), headers: headersThisMonth },
|
|
{ name: lf("Older"), headers: headersOlder },
|
|
];
|
|
var isLoading = (tab == ProjectsTab.Make && makes.length == 0) ||
|
|
(tab == ProjectsTab.Code && codes.length == 0);
|
|
var tabClasses = sui.cx([
|
|
isLoading ? 'loading' : '',
|
|
'ui segment bottom attached tab active tabsegment'
|
|
]);
|
|
return (React.createElement(sui.Modal, {open: visible, className: "projectsdialog", size: "fullscreen", closeIcon: true, onClose: function () { return _this.setState({ visible: false }); }, dimmer: true, closeOnDimmerClick: true, closeOnDocumentClick: true}, React.createElement(sui.Segment, {inverted: targetTheme.invertedMenu, attached: "top"}, React.createElement(sui.Menu, {inverted: targetTheme.invertedMenu, secondary: true}, tabs.map(function (t) {
|
|
return React.createElement(sui.MenuItem, {key: "tab" + t, active: tab == t, name: tabNames[t], onClick: function () { return _this.setState({ tab: t }); }});
|
|
}), React.createElement("div", {className: "right menu"}, React.createElement(sui.Button, {icon: 'close', class: "clear " + (targetTheme.invertedMenu ? 'inverted' : ''), onClick: function () { return _this.setState({ visible: false }); }})))), tab == ProjectsTab.MyStuff ? React.createElement("div", {className: tabClasses}, React.createElement("div", {className: "group"}, React.createElement("div", {className: "ui cards"}, React.createElement(codecard.CodeCardView, {key: 'newproject', icon: "file outline", iconColor: "primary", name: lf("New Project..."), description: lf("Creates a new empty project"), onClick: function () { return newProject(); }}), pxt.appTarget.compile ?
|
|
React.createElement(codecard.CodeCardView, {key: 'import', icon: "upload", iconColor: "secondary", name: lf("Import File..."), description: lf("Open files from your computer"), onClick: function () { return importHex(); }}) : undefined, pxt.appTarget.cloud && pxt.appTarget.cloud.sharing && pxt.appTarget.cloud.publishing && pxt.appTarget.cloud.importing ?
|
|
React.createElement(codecard.CodeCardView, {key: 'importurl', icon: "upload", iconColor: "secondary", name: lf("Import URL..."), description: lf("Open a shared project URL"), onClick: function () { return importUrl(); }}) : undefined)), headersGrouped.filter(function (g) { return g.headers.length != 0; }).map(function (headerGroup) {
|
|
return React.createElement("div", {key: 'localgroup' + headerGroup.name, className: "group"}, React.createElement("h3", {className: "ui dividing header disabled"}, headerGroup.name), React.createElement("div", {className: "ui cards"}, headerGroup.headers.map(function (scr) {
|
|
return React.createElement(codecard.CodeCardView, {key: 'local' + scr.id, name: scr.name, time: scr.recentUse, imageUrl: scr.icon, url: scr.pubId && scr.pubCurrent ? "/" + scr.pubId : "", onClick: function () { return chgHeader(scr); }});
|
|
})));
|
|
}), React.createElement("div", {className: "group"}, React.createElement("div", {className: "ui cards"}, urldata.map(function (scr) {
|
|
return React.createElement(codecard.CodeCardView, {name: scr.name, time: scr.time, header: '/' + scr.id, description: scr.description, key: 'cloud' + scr.id, onClick: function () { return installScript(scr); }, url: '/' + scr.id, color: "blue"});
|
|
})))) : undefined, tab == ProjectsTab.Make ? React.createElement("div", {className: tabClasses}, React.createElement("div", {className: "ui cards"}, makes.map(function (scr) { return React.createElement(codecard.CodeCardView, {key: 'make' + scr.name, name: scr.name, description: scr.description, url: scr.url, imageUrl: scr.imageUrl, onClick: function () { return chgMake(scr); }}); }))) : undefined, tab == ProjectsTab.Code ? React.createElement("div", {className: tabClasses}, React.createElement("div", {className: "ui cards"}, codes.map(function (scr) { return React.createElement(codecard.CodeCardView, {key: 'code' + scr.name, name: scr.name, description: scr.description, url: scr.url, imageUrl: scr.imageUrl, onClick: function () { return chgCode(scr); }}); }))) : undefined, isEmpty() ?
|
|
React.createElement("div", {className: "ui items"}, React.createElement("div", {className: "ui item"}, lf("We couldn't find any projects matching '{0}'", this.state.searchFor)))
|
|
: undefined));
|
|
};
|
|
return Projects;
|
|
}(data.Component));
|
|
exports.Projects = Projects;
|
|
|
|
},{"./codecard":7,"./core":10,"./data":11,"./gallery":18,"./package":24,"./sui":32,"./workspace":37,"react":264,"react-dom":135}],26:[function(require,module,exports){
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var pkg = require("./package");
|
|
var srceditor = require("./srceditor");
|
|
var sui = require("./sui");
|
|
var Util = pxt.Util;
|
|
var lf = Util.lf;
|
|
var Editor = (function (_super) {
|
|
__extends(Editor, _super);
|
|
function Editor() {
|
|
_super.apply(this, arguments);
|
|
this.config = {};
|
|
this.changeMade = false;
|
|
}
|
|
Editor.prototype.prepare = function () {
|
|
this.isReady = true;
|
|
};
|
|
Editor.prototype.getId = function () {
|
|
return "pxtJsonEditor";
|
|
};
|
|
Editor.prototype.display = function () {
|
|
var _this = this;
|
|
var c = this.config;
|
|
var save = function () {
|
|
_this.isSaving = true;
|
|
var f = pkg.mainEditorPkg().lookupFile("this/" + pxt.CONFIG_NAME);
|
|
f.setContentAsync(JSON.stringify(_this.config, null, 4) + "\n").then(function () {
|
|
pkg.mainPkg.config.name = c.name;
|
|
_this.parent.setState({ projectName: c.name });
|
|
_this.parent.forceUpdate();
|
|
Util.nextTick(_this.changeCallback);
|
|
_this.isSaving = false;
|
|
_this.changeMade = true;
|
|
});
|
|
};
|
|
var setFileName = function (v) {
|
|
c.name = v;
|
|
_this.parent.forceUpdate();
|
|
};
|
|
var deleteProject = function () {
|
|
_this.parent.removeProject();
|
|
};
|
|
var initCard = function () {
|
|
if (!c.card)
|
|
c.card = {};
|
|
};
|
|
var card = c.card || {};
|
|
var userConfigs = [];
|
|
pkg.allEditorPkgs().map(function (ep) { return ep.getKsPkg(); })
|
|
.filter(function (dep) { return !!dep && dep.isLoaded && !!dep.config && !!dep.config.yotta && !!dep.config.yotta.userConfigs; })
|
|
.forEach(function (dep) { return userConfigs = userConfigs.concat(dep.config.yotta.userConfigs); });
|
|
var isUserConfigActive = function (uc) {
|
|
var cfg = Util.jsonFlatten(_this.config.yotta ? _this.config.yotta.config : {});
|
|
var ucfg = Util.jsonFlatten(uc.config);
|
|
return !Object.keys(ucfg).some(function (k) { return ucfg[k] === null ? !!cfg[k] : cfg[k] !== ucfg[k]; });
|
|
};
|
|
var applyUserConfig = function (uc) {
|
|
var cfg = Util.jsonFlatten(_this.config.yotta ? _this.config.yotta.config : {});
|
|
var ucfg = Util.jsonFlatten(uc.config);
|
|
if (isUserConfigActive(uc)) {
|
|
Object.keys(ucfg).forEach(function (k) { return delete cfg[k]; });
|
|
}
|
|
else {
|
|
Object.keys(ucfg).forEach(function (k) { return cfg[k] = ucfg[k]; });
|
|
}
|
|
// update cfg
|
|
if (Object.keys(cfg).length) {
|
|
if (!_this.config.yotta)
|
|
_this.config.yotta = {};
|
|
Object.keys(cfg).filter(function (k) { return cfg[k] === null; }).forEach(function (k) { return delete cfg[k]; });
|
|
_this.config.yotta.config = Util.jsonUnFlatten(cfg);
|
|
}
|
|
else {
|
|
if (_this.config.yotta) {
|
|
delete _this.config.yotta.config;
|
|
if (!Object.keys(_this.config.yotta).length)
|
|
delete _this.config.yotta;
|
|
}
|
|
}
|
|
// trigger update
|
|
save();
|
|
};
|
|
return (React.createElement("div", {className: "ui content"}, React.createElement("div", {className: "ui segment form text", style: { backgroundColor: "white" }}, React.createElement(sui.Input, {label: lf("Name"), value: c.name, onChange: setFileName}), userConfigs.map(function (uc) {
|
|
return React.createElement(sui.Checkbox, {key: "userconfig-" + uc.description, inputLabel: uc.description, checked: isUserConfigActive(uc), onChange: function () { return applyUserConfig(uc); }});
|
|
}), React.createElement(sui.Field, null, React.createElement(sui.Button, {text: lf("Save"), class: "green " + (this.isSaving ? 'disabled' : ''), onClick: function () { return save(); }}), React.createElement(sui.Button, {text: lf("Edit Settings As text"), onClick: function () { return _this.editSettingsText(); }})))));
|
|
};
|
|
Editor.prototype.editSettingsText = function () {
|
|
this.changeMade = false;
|
|
this.parent.editText();
|
|
};
|
|
Editor.prototype.getCurrentSource = function () {
|
|
return JSON.stringify(this.config, null, 4) + "\n";
|
|
};
|
|
Editor.prototype.acceptsFile = function (file) {
|
|
if (file.name != pxt.CONFIG_NAME)
|
|
return false;
|
|
if (file.isReadonly()) {
|
|
// TODO add read-only support
|
|
return false;
|
|
}
|
|
try {
|
|
var cfg = JSON.parse(file.content);
|
|
// TODO validate?
|
|
return true;
|
|
}
|
|
catch (e) {
|
|
return false;
|
|
}
|
|
};
|
|
Editor.prototype.loadFileAsync = function (file) {
|
|
this.config = JSON.parse(file.content);
|
|
this.setDiagnostics(file, this.snapshotState());
|
|
this.changeMade = false;
|
|
return Promise.resolve();
|
|
};
|
|
Editor.prototype.unloadFileAsync = function () {
|
|
if (this.changeMade) {
|
|
return this.parent.reloadHeaderAsync();
|
|
}
|
|
return Promise.resolve();
|
|
};
|
|
return Editor;
|
|
}(srceditor.Editor));
|
|
exports.Editor = Editor;
|
|
|
|
},{"./package":24,"./srceditor":31,"./sui":32,"react":264}],27:[function(require,module,exports){
|
|
"use strict";
|
|
var workspace = require("./workspace");
|
|
var data = require("./data");
|
|
function loadImageAsync(data) {
|
|
var img = document.createElement("img");
|
|
return new Promise(function (resolve, reject) {
|
|
img.onload = function () { return resolve(img); };
|
|
img.onerror = function () { return resolve(undefined); };
|
|
img.src = data;
|
|
});
|
|
}
|
|
function renderIcon(img) {
|
|
var icon = null;
|
|
if (img && img.width > 0 && img.height > 0) {
|
|
var cvs = document.createElement("canvas");
|
|
cvs.width = 305;
|
|
cvs.height = 200;
|
|
var ox = 0;
|
|
var oy = 0;
|
|
var iw = 0;
|
|
var ih = 0;
|
|
if (img.height > img.width) {
|
|
ox = 0;
|
|
iw = img.width;
|
|
ih = iw / cvs.width * cvs.height;
|
|
oy = (img.height - ih) / 2;
|
|
}
|
|
else {
|
|
oy = 0;
|
|
ih = img.height;
|
|
iw = ih / cvs.height * cvs.width;
|
|
ox = (img.width - iw) / 2;
|
|
}
|
|
var ctx = cvs.getContext("2d");
|
|
ctx.drawImage(img, ox, oy, iw, ih, 0, 0, cvs.width, cvs.height);
|
|
icon = cvs.toDataURL('image/jpeg', 85);
|
|
}
|
|
return icon;
|
|
}
|
|
function saveAsync(header, screenshot) {
|
|
return loadImageAsync(screenshot)
|
|
.then(function (img) {
|
|
var icon = renderIcon(img);
|
|
return workspace.saveScreenshotAsync(header, screenshot, icon)
|
|
.then(function () {
|
|
data.invalidate("header:" + header.id);
|
|
data.invalidate("header:*");
|
|
});
|
|
});
|
|
}
|
|
exports.saveAsync = saveAsync;
|
|
|
|
},{"./data":11,"./workspace":37}],28:[function(require,module,exports){
|
|
/// <reference path="../../typings/globals/react/index.d.ts" />
|
|
/// <reference path="../../typings/globals/react-dom/index.d.ts" />
|
|
/// <reference path="../../built/pxtlib.d.ts" />
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var ReactDOM = require("react-dom");
|
|
var data = require("./data");
|
|
var sui = require("./sui");
|
|
var pkg = require("./package");
|
|
var core = require("./core");
|
|
var codecard = require("./codecard");
|
|
var ScriptSearch = (function (_super) {
|
|
__extends(ScriptSearch, _super);
|
|
function ScriptSearch(props) {
|
|
_super.call(this, props);
|
|
this.prevGhData = [];
|
|
this.prevUrlData = [];
|
|
this.prevGalleries = [];
|
|
this.state = {
|
|
searchFor: '',
|
|
visible: false
|
|
};
|
|
}
|
|
ScriptSearch.prototype.hide = function () {
|
|
this.setState({ visible: false });
|
|
};
|
|
ScriptSearch.prototype.showAddPackages = function () {
|
|
this.setState({ visible: true, searchFor: '' });
|
|
};
|
|
ScriptSearch.prototype.fetchGhData = function () {
|
|
var cloud = pxt.appTarget.cloud || {};
|
|
if (!cloud.packages)
|
|
return [];
|
|
var searchFor = cloud.githubPackages ? this.state.searchFor : undefined;
|
|
var res = searchFor || cloud.preferredPackages
|
|
? this.getData("gh-search:" + (searchFor || cloud.preferredPackages.join('|')))
|
|
: null;
|
|
if (res)
|
|
this.prevGhData = res;
|
|
return this.prevGhData || [];
|
|
};
|
|
ScriptSearch.prototype.fetchBundled = function () {
|
|
if (!!this.state.searchFor)
|
|
return [];
|
|
var bundled = pxt.appTarget.bundledpkgs;
|
|
return Object.keys(bundled).filter(function (k) { return !/prj$/.test(k); })
|
|
.map(function (k) { return JSON.parse(bundled[k]["pxt.json"]); });
|
|
};
|
|
ScriptSearch.prototype.shouldComponentUpdate = function (nextProps, nextState, nextContext) {
|
|
return this.state.visible != nextState.visible
|
|
|| this.state.searchFor != nextState.searchFor;
|
|
};
|
|
ScriptSearch.prototype.renderCore = function () {
|
|
var _this = this;
|
|
var bundles = this.fetchBundled();
|
|
var ghdata = this.fetchGhData();
|
|
var chgHeader = function (hdr) {
|
|
pxt.tickEvent("projects.header");
|
|
_this.hide();
|
|
_this.props.parent.loadHeaderAsync(hdr);
|
|
};
|
|
var chgBundle = function (scr) {
|
|
pxt.tickEvent("packages.bundled", { name: scr.name });
|
|
_this.hide();
|
|
addDepIfNoConflict(scr, "*")
|
|
.done();
|
|
};
|
|
var chgGallery = function (scr) {
|
|
pxt.tickEvent("projects.gallery", { name: scr.name });
|
|
_this.hide();
|
|
_this.props.parent.newEmptyProject(scr.name.toLowerCase(), scr.url);
|
|
};
|
|
var upd = function (v) {
|
|
var str = ReactDOM.findDOMNode(_this.refs["searchInput"]).value;
|
|
_this.setState({ searchFor: str });
|
|
};
|
|
var kupd = function (ev) {
|
|
if (ev.keyCode == 13)
|
|
upd(ev);
|
|
};
|
|
var installGh = function (scr) {
|
|
pxt.tickEvent("packages.github");
|
|
_this.hide();
|
|
var p = pkg.mainEditorPkg();
|
|
core.showLoading(lf("downloading package..."));
|
|
pxt.packagesConfigAsync()
|
|
.then(function (config) { return pxt.github.latestVersionAsync(scr.fullName, config); })
|
|
.then(function (tag) { return pxt.github.pkgConfigAsync(scr.fullName, tag)
|
|
.then(function (cfg) { return addDepIfNoConflict(cfg, "github:" + scr.fullName + "#" + tag); }); })
|
|
.catch(core.handleNetworkError)
|
|
.finally(function () { return core.hideLoading(); });
|
|
};
|
|
var addDepIfNoConflict = function (config, version) {
|
|
return pkg.mainPkg.findConflictsAsync(config, version)
|
|
.then(function (conflicts) {
|
|
var inUse = conflicts.filter(function (c) { return pkg.mainPkg.isPackageInUse(c.pkg0.id); });
|
|
var addDependencyPromise = Promise.resolve(true);
|
|
if (inUse.length) {
|
|
addDependencyPromise = addDependencyPromise
|
|
.then(function () { return core.confirmAsync({
|
|
header: lf("Cannot add {0} package", config.name),
|
|
hideCancel: true,
|
|
agreeLbl: lf("Ok"),
|
|
body: lf("Remove all the blocks from the {0} package and try again.", inUse[0].pkg0.id)
|
|
}); })
|
|
.then(function () {
|
|
return false;
|
|
});
|
|
}
|
|
else if (conflicts.length) {
|
|
var body_1 = conflicts.length === 1 ?
|
|
// Single conflict: "Package a is..."
|
|
lf("Package {0} is incompatible with {1}. Remove {0} and add {1}?", conflicts[0].pkg0.id, config.name) :
|
|
// 2 conflicts: "Packages A and B are..."; 3+ conflicts: "Packages A, B, C and D are..."
|
|
lf("Packages {0} and {1} are incompatible with {2}. Remove them and add {2}?", conflicts.slice(0, -1).map(function (c) { return c.pkg0.id; }).join(","), conflicts.slice(-1)[0].pkg0.id, config.name);
|
|
addDependencyPromise = addDependencyPromise
|
|
.then(function () { return core.confirmAsync({
|
|
header: lf("Some packages will be removed"),
|
|
agreeLbl: lf("Remove package(s) and add {0}", config.name),
|
|
agreeClass: "pink",
|
|
body: body_1
|
|
}); })
|
|
.then(function (buttonPressed) {
|
|
if (buttonPressed !== 0) {
|
|
var p_1 = pkg.mainEditorPkg();
|
|
return Promise.all(conflicts.map(function (c) {
|
|
return p_1.removeDepAsync(c.pkg0.id);
|
|
}))
|
|
.then(function () { return true; });
|
|
}
|
|
return Promise.resolve(false);
|
|
});
|
|
}
|
|
return addDependencyPromise
|
|
.then(function (shouldAdd) {
|
|
if (shouldAdd) {
|
|
var p = pkg.mainEditorPkg();
|
|
return p.addDepAsync(config.name, version)
|
|
.then(function () { return _this.props.parent.reloadHeaderAsync(); });
|
|
}
|
|
return Promise.resolve();
|
|
});
|
|
});
|
|
};
|
|
var isEmpty = function () {
|
|
if (_this.state.searchFor) {
|
|
if (bundles.length > 0
|
|
|| ghdata.length > 0)
|
|
return false;
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
var headerText = lf("Add Package...");
|
|
return (React.createElement(sui.Modal, {open: this.state.visible, dimmer: true, header: headerText, className: "searchdialog", size: "large", onClose: function () { return _this.setState({ visible: false }); }, closeIcon: true, closeOnDimmerClick: true}, React.createElement("div", {className: "ui vertical segment"}, React.createElement("div", {className: "ui search"}, React.createElement("div", {className: "ui fluid action input", role: "search"}, React.createElement("input", {ref: "searchInput", type: "text", placeholder: lf("Search..."), onKeyUp: kupd}), React.createElement("button", {title: lf("Search"), className: "ui right icon button", onClick: upd}, React.createElement("i", {className: "search icon"})))), React.createElement("div", {className: "ui cards"}, bundles.map(function (scr) {
|
|
return React.createElement(codecard.CodeCardView, {key: 'bundled' + scr.name, name: scr.name, description: scr.description, url: "/" + scr.installedVersion, onClick: function () { return chgBundle(scr); }});
|
|
}), ghdata.filter(function (repo) { return repo.status == pxt.github.GitRepoStatus.Approved; }).map(function (scr) {
|
|
return React.createElement(codecard.CodeCardView, {name: scr.name.replace(/^pxt-/, ""), header: scr.fullName, description: scr.description, key: 'gh' + scr.fullName, onClick: function () { return installGh(scr); }, url: 'github:' + scr.fullName, color: "blue"});
|
|
}), ghdata.filter(function (repo) { return repo.status != pxt.github.GitRepoStatus.Approved; }).map(function (scr) {
|
|
return React.createElement(codecard.CodeCardView, {name: scr.name.replace(/^pxt-/, ""), header: scr.fullName, description: scr.description, key: 'gh' + scr.fullName, onClick: function () { return installGh(scr); }, url: 'github:' + scr.fullName, color: "red"});
|
|
})), isEmpty() ?
|
|
React.createElement("div", {className: "ui items"}, React.createElement("div", {className: "ui item"}, lf("We couldn't find any packages matching '{0}'", this.state.searchFor)))
|
|
: undefined)));
|
|
};
|
|
return ScriptSearch;
|
|
}(data.Component));
|
|
exports.ScriptSearch = ScriptSearch;
|
|
|
|
},{"./codecard":7,"./core":10,"./data":11,"./package":24,"./sui":32,"react":264,"react-dom":135}],29:[function(require,module,exports){
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var data = require("./data");
|
|
var sui = require("./sui");
|
|
var pkg = require("./package");
|
|
(function (ShareMode) {
|
|
ShareMode[ShareMode["Screenshot"] = 0] = "Screenshot";
|
|
ShareMode[ShareMode["Url"] = 1] = "Url";
|
|
ShareMode[ShareMode["Editor"] = 2] = "Editor";
|
|
ShareMode[ShareMode["Simulator"] = 3] = "Simulator";
|
|
ShareMode[ShareMode["Cli"] = 4] = "Cli";
|
|
})(exports.ShareMode || (exports.ShareMode = {}));
|
|
var ShareMode = exports.ShareMode;
|
|
var ShareEditor = (function (_super) {
|
|
__extends(ShareEditor, _super);
|
|
function ShareEditor(props) {
|
|
_super.call(this, props);
|
|
this.state = {
|
|
currentPubId: undefined,
|
|
pubCurrent: false,
|
|
visible: false,
|
|
advancedMenu: false
|
|
};
|
|
}
|
|
ShareEditor.prototype.hide = function () {
|
|
this.setState({ visible: false });
|
|
};
|
|
ShareEditor.prototype.show = function (header) {
|
|
this.setState({ visible: true, mode: ShareMode.Screenshot, pubCurrent: header.pubCurrent });
|
|
};
|
|
ShareEditor.prototype.shouldComponentUpdate = function (nextProps, nextState, nextContext) {
|
|
return this.state.visible != nextState.visible
|
|
|| this.state.advancedMenu != nextState.advancedMenu
|
|
|| this.state.mode != nextState.mode
|
|
|| this.state.pubCurrent != nextState.pubCurrent
|
|
|| this.state.screenshotId != nextState.screenshotId
|
|
|| this.state.currentPubId != nextState.currentPubId;
|
|
};
|
|
ShareEditor.prototype.renderCore = function () {
|
|
var _this = this;
|
|
var visible = this.state.visible;
|
|
var cloud = pxt.appTarget.cloud || {};
|
|
var embedding = !!cloud.embedding;
|
|
var header = this.props.parent.state.header;
|
|
var advancedMenu = !!this.state.advancedMenu;
|
|
var ready = false;
|
|
var mode = this.state.mode;
|
|
var url = '';
|
|
var embed = '';
|
|
var help = lf("Copy this HTML to your website or blog.");
|
|
if (header) {
|
|
var rootUrl = pxt.appTarget.appTheme.embedUrl;
|
|
if (!/\/$/.test(rootUrl))
|
|
rootUrl += '/';
|
|
var isBlocks = this.props.parent.getPreferredEditor() == pxt.BLOCKS_PROJECT_NAME;
|
|
var pubCurrent = header ? header.pubCurrent : false;
|
|
var currentPubId_1 = (header ? header.pubId : undefined) || this.state.currentPubId;
|
|
ready = (!!currentPubId_1 && header.pubCurrent);
|
|
if (ready) {
|
|
url = "" + rootUrl + header.pubId;
|
|
var editUrl = rootUrl + "#pub:" + currentPubId_1;
|
|
switch (mode) {
|
|
case ShareMode.Cli:
|
|
embed = "pxt target " + pxt.appTarget.id + "\npxt extract " + url;
|
|
help = lf("Run this command from a shell.");
|
|
break;
|
|
case ShareMode.Editor:
|
|
embed = pxt.docs.embedUrl(rootUrl, "pub", header.pubId);
|
|
break;
|
|
case ShareMode.Simulator:
|
|
var padding = '81.97%';
|
|
// TODO: parts aspect ratio
|
|
if (pxt.appTarget.simulator)
|
|
padding = (100 / pxt.appTarget.simulator.aspectRatio).toPrecision(4) + '%';
|
|
var runUrl = rootUrl + (pxt.webConfig.runUrl || "--run").replace(/^\//, '');
|
|
embed = pxt.docs.runUrl(runUrl, padding, header.pubId);
|
|
break;
|
|
case ShareMode.Url:
|
|
embed = editUrl;
|
|
break;
|
|
default:
|
|
if (isBlocks) {
|
|
// Render screenshot
|
|
if (this.state.screenshotId == currentPubId_1) {
|
|
if (this.state.screenshotUri)
|
|
embed = "<a href=\"" + editUrl + "\"><img src=\"" + this.state.screenshotUri + "\" /></a>";
|
|
else
|
|
embed = lf("Ooops, no screenshot available.");
|
|
}
|
|
else {
|
|
pxt.debug("rendering share-editor screenshot png");
|
|
embed = lf("rendering...");
|
|
pxt.blocks.layout.toPngAsync(this.props.parent.editor.editor)
|
|
.done(function (uri) { return _this.setState({ screenshotId: currentPubId_1, screenshotUri: uri }); });
|
|
}
|
|
}
|
|
else {
|
|
// Render javascript code
|
|
pxt.debug("rendering share-editor javascript markdown");
|
|
embed = lf("rendering...");
|
|
var main = pkg.getEditorPkg(pkg.mainPkg);
|
|
var file = main.getMainFile();
|
|
if (pkg.File.blocksFileNameRx.test(file.getName()) && file.getVirtualFileName())
|
|
file = main.lookupFile("this/" + file.getVirtualFileName()) || file;
|
|
if (pkg.File.tsFileNameRx.test(file.getName())) {
|
|
var fileContents = file.content;
|
|
var mdContent = pxt.docs.renderMarkdown("@body@", "```javascript\n" + fileContents + "\n```");
|
|
embed = "<a style=\"text-decoration: none;\" href=\"" + editUrl + "\">" + mdContent + "</a>";
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
var publish = function () {
|
|
pxt.tickEvent("menu.embed.publish");
|
|
_this.props.parent.anonymousPublishAsync().done(function () {
|
|
_this.setState({ pubCurrent: true });
|
|
});
|
|
_this.forceUpdate();
|
|
};
|
|
var formats = [{ mode: ShareMode.Screenshot, label: lf("Screenshot") },
|
|
{ mode: ShareMode.Editor, label: lf("Editor") },
|
|
{ mode: ShareMode.Simulator, label: lf("Simulator") },
|
|
{ mode: ShareMode.Cli, label: lf("Command line") }
|
|
];
|
|
var action = !ready ? lf("Publish project") : undefined;
|
|
var actionLoading = this.props.parent.state.publishing;
|
|
return (React.createElement(sui.Modal, {open: this.state.visible, className: "sharedialog", header: lf("Share Project"), size: "small", onClose: function () { return _this.setState({ visible: false }); }, dimmer: true, action: action, actionClick: publish, actionLoading: actionLoading, closeIcon: true, closeOnDimmerClick: true, closeOnDocumentClick: true}, React.createElement("div", {className: "ui form"}, action ?
|
|
React.createElement("p", null, lf("You need to publish your project to share it or embed it in other web pages.") +
|
|
lf("You acknowledge having consent to publish this project."))
|
|
: undefined, url && ready ? React.createElement("div", null, React.createElement("p", null, lf("Your project is ready! Use the address below to share your projects.")), React.createElement(sui.Input, {class: "mini", readOnly: true, lines: 1, value: url, copy: true}))
|
|
: undefined, ready ? React.createElement("div", null, React.createElement("div", {className: "ui divider"}), React.createElement(sui.Button, {class: "labeled", icon: "chevron " + (advancedMenu ? "down" : "right"), text: lf("Embed"), onClick: function () { return _this.setState({ advancedMenu: !advancedMenu }); }}), advancedMenu ?
|
|
React.createElement(sui.Menu, {pointing: true, secondary: true}, formats.map(function (f) {
|
|
return React.createElement(sui.MenuItem, {key: "tab" + f.label, active: mode == f.mode, name: f.label, onClick: function () { return _this.setState({ mode: f.mode }); }});
|
|
})) : undefined, advancedMenu ?
|
|
React.createElement(sui.Field, null, React.createElement(sui.Input, {class: "mini", readOnly: true, lines: 4, value: embed, copy: ready, disabled: !ready})) : null) : undefined)));
|
|
};
|
|
return ShareEditor;
|
|
}(data.Component));
|
|
exports.ShareEditor = ShareEditor;
|
|
|
|
},{"./data":11,"./package":24,"./sui":32,"react":264}],30:[function(require,module,exports){
|
|
/// <reference path="../../built/pxtsim.d.ts" />
|
|
/// <reference path="../../localtypings/pxtparts.d.ts" />
|
|
"use strict";
|
|
var core = require("./core");
|
|
var U = pxt.U;
|
|
var nextFrameId = 0;
|
|
var themes = ["blue", "red", "green", "yellow"];
|
|
var config;
|
|
var lastCompileResult;
|
|
var $debugger;
|
|
function init(root, cfg) {
|
|
$(root).html("\n <div id=\"simulators\" class='simulator'>\n </div>\n <div id=\"debugger\" class=\"ui item landscape only\">\n </div>\n ");
|
|
$debugger = $('#debugger');
|
|
var options = {
|
|
revealElement: function (el) {
|
|
$(el).transition({
|
|
animation: pxt.appTarget.appTheme.simAnimationEnter || 'fly right in',
|
|
duration: '0.5s',
|
|
});
|
|
},
|
|
removeElement: function (el, completeHandler) {
|
|
if (pxt.appTarget.simulator.headless) {
|
|
$(el).addClass('simHeadless');
|
|
completeHandler();
|
|
}
|
|
else {
|
|
$(el).transition({
|
|
animation: pxt.appTarget.appTheme.simAnimationExit || 'fly right out',
|
|
duration: '0.5s',
|
|
onComplete: function () {
|
|
if (completeHandler)
|
|
completeHandler();
|
|
$(el).remove();
|
|
}
|
|
}).error(function () {
|
|
// Problem with animation, still complete
|
|
if (completeHandler)
|
|
completeHandler();
|
|
$(el).remove();
|
|
});
|
|
}
|
|
},
|
|
unhideElement: function (el) {
|
|
$(el).removeClass("simHeadless");
|
|
},
|
|
onDebuggerBreakpoint: function (brk) {
|
|
updateDebuggerButtons(brk);
|
|
var brkInfo = lastCompileResult.breakpoints[brk.breakpointId];
|
|
config.highlightStatement(brkInfo);
|
|
if (brk.exceptionMessage) {
|
|
core.errorNotification(lf("Program Error: {0}", brk.exceptionMessage));
|
|
}
|
|
},
|
|
onDebuggerWarning: function (wrn) {
|
|
for (var _i = 0, _a = wrn.breakpointIds; _i < _a.length; _i++) {
|
|
var id = _a[_i];
|
|
var brkInfo = lastCompileResult.breakpoints[id];
|
|
if (brkInfo) {
|
|
if (!U.startsWith("pxt_modules/", brkInfo.fileName)) {
|
|
config.highlightStatement(brkInfo);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
onDebuggerResume: function () {
|
|
config.highlightStatement(null);
|
|
updateDebuggerButtons();
|
|
},
|
|
onStateChanged: function (state) {
|
|
updateDebuggerButtons();
|
|
},
|
|
onSimulatorCommand: function (msg) {
|
|
switch (msg.command) {
|
|
case "restart":
|
|
cfg.restartSimulator();
|
|
break;
|
|
case "modal":
|
|
stop();
|
|
core.confirmAsync({
|
|
header: msg.header,
|
|
body: msg.body,
|
|
size: "large",
|
|
copyable: msg.copyable,
|
|
hideAgree: true,
|
|
disagreeLbl: lf("Close")
|
|
}).done();
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
exports.driver = new pxsim.SimulatorDriver($('#simulators')[0], options);
|
|
config = cfg;
|
|
updateDebuggerButtons();
|
|
}
|
|
exports.init = init;
|
|
function setState(editor) {
|
|
if (config.editor != editor) {
|
|
config.editor = editor;
|
|
config.highlightStatement(null);
|
|
updateDebuggerButtons();
|
|
}
|
|
}
|
|
exports.setState = setState;
|
|
function makeDirty() {
|
|
pxsim.U.addClass(exports.driver.container, "sepia");
|
|
}
|
|
exports.makeDirty = makeDirty;
|
|
function isDirty() {
|
|
return /sepia/.test(exports.driver.container.className);
|
|
}
|
|
exports.isDirty = isDirty;
|
|
function run(pkg, debug, res, mute) {
|
|
pxsim.U.removeClass(exports.driver.container, "sepia");
|
|
var js = res.outfiles[pxtc.BINARY_JS];
|
|
var boardDefinition = pxt.appTarget.simulator.boardDefinition;
|
|
var parts = pxtc.computeUsedParts(res, true);
|
|
var fnArgs = res.usedArguments;
|
|
lastCompileResult = res;
|
|
var opts = {
|
|
boardDefinition: boardDefinition,
|
|
mute: mute,
|
|
parts: parts,
|
|
debug: debug,
|
|
fnArgs: fnArgs,
|
|
aspectRatio: parts.length ? pxt.appTarget.simulator.partsAspectRatio : pxt.appTarget.simulator.aspectRatio,
|
|
partDefinitions: pkg.computePartDefinitions(parts)
|
|
};
|
|
exports.driver.run(js, opts);
|
|
}
|
|
exports.run = run;
|
|
function mute(mute) {
|
|
exports.driver.mute(mute);
|
|
$debugger.empty();
|
|
}
|
|
exports.mute = mute;
|
|
function stop(unload) {
|
|
if (!exports.driver)
|
|
return;
|
|
pxsim.U.removeClass(exports.driver.container, "sepia");
|
|
exports.driver.stop(unload);
|
|
$debugger.empty();
|
|
}
|
|
exports.stop = stop;
|
|
function hide(completeHandler) {
|
|
if (!pxt.appTarget.simulator.headless) {
|
|
pxsim.U.addClass(exports.driver.container, "sepia");
|
|
}
|
|
exports.driver.hide(completeHandler);
|
|
$debugger.empty();
|
|
}
|
|
exports.hide = hide;
|
|
function unhide() {
|
|
exports.driver.unhide();
|
|
}
|
|
exports.unhide = unhide;
|
|
function proxy(message) {
|
|
if (!exports.driver)
|
|
return;
|
|
exports.driver.postMessage(message);
|
|
$debugger.empty();
|
|
}
|
|
exports.proxy = proxy;
|
|
function updateDebuggerButtons(brk) {
|
|
if (brk === void 0) { brk = null; }
|
|
function btn(icon, name, label, click) {
|
|
var b = $("<button class=\"ui mini button teal\" title=\"" + Util.htmlEscape(label) + "\"></button>");
|
|
if (icon)
|
|
b.addClass("icon").append("<i class=\"" + icon + " icon\"></i>");
|
|
if (name)
|
|
b.append(Util.htmlEscape(name));
|
|
return b.click(click);
|
|
}
|
|
$debugger.empty();
|
|
if (!exports.driver.runOptions.debug)
|
|
return;
|
|
var advanced = config.editor == 'tsprj';
|
|
if (exports.driver.state == pxsim.SimulatorState.Paused) {
|
|
var $resume = btn("play", lf("Resume"), lf("Resume execution"), function () { return exports.driver.resume(pxsim.SimulatorDebuggerCommand.Resume); });
|
|
var $stepOver = btn("xicon stepover", lf("Step over"), lf("Step over next function call"), function () { return exports.driver.resume(pxsim.SimulatorDebuggerCommand.StepOver); });
|
|
var $stepInto = btn("xicon stepinto", lf("Step into"), lf("Step into next function call"), function () { return exports.driver.resume(pxsim.SimulatorDebuggerCommand.StepInto); });
|
|
$debugger.append($resume).append($stepOver);
|
|
if (advanced)
|
|
$debugger.append($stepInto);
|
|
}
|
|
else if (exports.driver.state == pxsim.SimulatorState.Running) {
|
|
var $pause = btn("pause", lf("Pause"), lf("Pause execution on the next instruction"), function () { return exports.driver.resume(pxsim.SimulatorDebuggerCommand.Pause); });
|
|
$debugger.append($pause);
|
|
}
|
|
if (!brk || !advanced)
|
|
return;
|
|
function vars(hd, frame) {
|
|
var frameView = $("<div><h4>" + U.htmlEscape(hd) + "</h4></div>");
|
|
for (var _i = 0, _a = Object.keys(frame); _i < _a.length; _i++) {
|
|
var k = _a[_i];
|
|
var v = frame[k];
|
|
var sv = "";
|
|
switch (typeof (v)) {
|
|
case "number":
|
|
sv = v + "";
|
|
break;
|
|
case "string":
|
|
sv = JSON.stringify(v);
|
|
break;
|
|
case "object":
|
|
if (v == null)
|
|
sv = "null";
|
|
else if (v.id !== undefined)
|
|
sv = "(object)";
|
|
else if (v.text)
|
|
sv = v.text;
|
|
else
|
|
sv = "(unknown)";
|
|
break;
|
|
default: U.oops();
|
|
}
|
|
var n = k.replace(/___\d+$/, "");
|
|
frameView.append("<div>" + U.htmlEscape(n) + ": " + U.htmlEscape(sv) + "</div>");
|
|
}
|
|
return frameView;
|
|
}
|
|
var dbgView = $("<div class=\"ui segment debuggerview\"></div>");
|
|
dbgView.append(vars(U.lf("globals"), brk.globals));
|
|
brk.stackframes.forEach(function (sf) {
|
|
var info = sf.funcInfo;
|
|
dbgView.append(vars(info.functionName, sf.locals));
|
|
});
|
|
$('#debugger').append(dbgView);
|
|
}
|
|
|
|
},{"./core":10}],31:[function(require,module,exports){
|
|
"use strict";
|
|
var React = require("react");
|
|
var Editor = (function () {
|
|
function Editor(parent) {
|
|
this.parent = parent;
|
|
this.isVisible = false;
|
|
this.changeCallback = function () { };
|
|
this.isReady = false;
|
|
}
|
|
Editor.prototype.setVisible = function (v) {
|
|
this.isVisible = v;
|
|
};
|
|
/*******************************
|
|
Methods called before loadFile
|
|
this.editor may be undefined
|
|
Always check that this.editor exists
|
|
*******************************/
|
|
Editor.prototype.acceptsFile = function (file) {
|
|
return false;
|
|
};
|
|
Editor.prototype.getViewState = function () {
|
|
return {};
|
|
};
|
|
Editor.prototype.getCurrentSource = function () {
|
|
return this.currSource;
|
|
};
|
|
Editor.prototype.getId = function () {
|
|
return "editor";
|
|
};
|
|
Editor.prototype.displayOuter = function () {
|
|
return (React.createElement("div", {className: 'full-abs', key: this.getId(), id: this.getId(), style: { display: this.isVisible ? "block" : "none" }}, this.display()));
|
|
};
|
|
Editor.prototype.display = function () {
|
|
return null;
|
|
};
|
|
Editor.prototype.beforeCompile = function () { };
|
|
Editor.prototype.prepare = function () {
|
|
this.isReady = true;
|
|
};
|
|
Editor.prototype.resize = function (e) { };
|
|
Editor.prototype.snapshotState = function () {
|
|
return null;
|
|
};
|
|
Editor.prototype.unloadFileAsync = function () { return Promise.resolve(); };
|
|
Editor.prototype.isIncomplete = function () {
|
|
return false;
|
|
};
|
|
Editor.prototype.hasUndo = function () { return true; };
|
|
Editor.prototype.hasRedo = function () { return true; };
|
|
Editor.prototype.undo = function () { };
|
|
Editor.prototype.redo = function () { };
|
|
Editor.prototype.zoomIn = function () { };
|
|
Editor.prototype.zoomOut = function () { };
|
|
/*******************************
|
|
loadFile
|
|
*******************************/
|
|
Editor.prototype.loadFileAsync = function (file) {
|
|
this.currSource = file.content;
|
|
this.setDiagnostics(file, this.snapshotState());
|
|
return Promise.resolve();
|
|
};
|
|
/*******************************
|
|
Methods called after loadFile
|
|
this.editor != undefined
|
|
*******************************/
|
|
Editor.prototype.domUpdate = function () { };
|
|
Editor.prototype.setDiagnostics = function (file, snapshot) { };
|
|
Editor.prototype.setViewState = function (view) { };
|
|
Editor.prototype.saveToTypeScript = function () {
|
|
return null;
|
|
};
|
|
Editor.prototype.highlightStatement = function (brk) { };
|
|
Editor.prototype.filterToolbox = function (blockSubset, showCategories, showToolboxButtons) {
|
|
if (showCategories === void 0) { showCategories = true; }
|
|
if (showToolboxButtons === void 0) { showToolboxButtons = true; }
|
|
return null;
|
|
};
|
|
return Editor;
|
|
}());
|
|
exports.Editor = Editor;
|
|
|
|
},{"react":264}],32:[function(require,module,exports){
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var ReactDOM = require("react-dom");
|
|
var data = require("./data");
|
|
function cx(classes) {
|
|
return classes.filter(function (c) { return c && c.length !== 0 && c.trim() != ''; }).join(' ');
|
|
}
|
|
exports.cx = cx;
|
|
function genericClassName(cls, props, ignoreIcon) {
|
|
if (ignoreIcon === void 0) { ignoreIcon = false; }
|
|
return cls + " " + (ignoreIcon ? '' : props.icon && props.text ? 'icon-and-text' : props.icon ? 'icon' : "") + " " + (props.class || "");
|
|
}
|
|
function genericContent(props) {
|
|
return [
|
|
props.icon ? (React.createElement("i", {key: 'iconkey', className: props.icon + " icon " + (props.text ? " icon-and-text " : "") + (props.iconClass ? " " + props.iconClass : '')})) : null,
|
|
props.text ? (React.createElement("span", {key: 'textkey', className: 'ui text' + (props.textClass ? ' ' + props.textClass : '')}, props.text)) : null,
|
|
];
|
|
}
|
|
var UiElement = (function (_super) {
|
|
__extends(UiElement, _super);
|
|
function UiElement() {
|
|
_super.apply(this, arguments);
|
|
}
|
|
UiElement.prototype.popup = function () {
|
|
if (this.props.popup) {
|
|
var ch_1 = this.child("");
|
|
ch_1.popup({
|
|
content: this.props.popup
|
|
});
|
|
if (!ch_1.data("hasPopupHide")) {
|
|
ch_1.data("hasPopupHide", "yes");
|
|
ch_1.on("click", function () {
|
|
ch_1.popup("hide");
|
|
});
|
|
}
|
|
}
|
|
};
|
|
UiElement.prototype.componentDidMount = function () {
|
|
this.popup();
|
|
};
|
|
UiElement.prototype.componentDidUpdate = function () {
|
|
this.popup();
|
|
};
|
|
return UiElement;
|
|
}(data.Component));
|
|
exports.UiElement = UiElement;
|
|
var DropdownMenuItem = (function (_super) {
|
|
__extends(DropdownMenuItem, _super);
|
|
function DropdownMenuItem() {
|
|
_super.apply(this, arguments);
|
|
}
|
|
DropdownMenuItem.prototype.componentDidMount = function () {
|
|
var _this = this;
|
|
this.popup();
|
|
this.child("").dropdown({
|
|
action: "hide",
|
|
fullTextSearch: true,
|
|
onChange: function (v) {
|
|
if (_this.props.onChange && v != _this.props.value) {
|
|
_this.props.onChange(v);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
DropdownMenuItem.prototype.componentDidUpdate = function () {
|
|
this.child("").dropdown("refresh");
|
|
this.popup();
|
|
};
|
|
DropdownMenuItem.prototype.renderCore = function () {
|
|
return (React.createElement("div", {className: genericClassName("ui dropdown item", this.props), role: this.props.role, title: this.props.title}, genericContent(this.props), React.createElement("div", {className: "menu"}, this.props.children)));
|
|
};
|
|
return DropdownMenuItem;
|
|
}(UiElement));
|
|
exports.DropdownMenuItem = DropdownMenuItem;
|
|
var Item = (function (_super) {
|
|
__extends(Item, _super);
|
|
function Item() {
|
|
_super.apply(this, arguments);
|
|
}
|
|
Item.prototype.renderCore = function () {
|
|
return (React.createElement("div", {className: genericClassName("ui item link", this.props, true) + (" " + (this.props.active ? 'active' : '')), role: this.props.role, title: this.props.title, key: this.props.value, "data-value": this.props.value, onClick: this.props.onClick}, genericContent(this.props), this.props.children));
|
|
};
|
|
return Item;
|
|
}(data.Component));
|
|
exports.Item = Item;
|
|
var Button = (function (_super) {
|
|
__extends(Button, _super);
|
|
function Button() {
|
|
_super.apply(this, arguments);
|
|
}
|
|
Button.prototype.renderCore = function () {
|
|
return (React.createElement("button", {className: genericClassName("ui button", this.props) + " " + (this.props.disabled ? "disabled" : ""), role: this.props.role, title: this.props.title, onClick: this.props.onClick}, genericContent(this.props), this.props.children));
|
|
};
|
|
return Button;
|
|
}(UiElement));
|
|
exports.Button = Button;
|
|
var Popup = (function (_super) {
|
|
__extends(Popup, _super);
|
|
function Popup() {
|
|
_super.apply(this, arguments);
|
|
}
|
|
Popup.prototype.componentDidMount = function () {
|
|
this.child(".popup-button").popup({
|
|
position: "bottom right",
|
|
on: "click",
|
|
hoverable: true,
|
|
delay: {
|
|
show: 50,
|
|
hide: 1000
|
|
}
|
|
});
|
|
};
|
|
Popup.prototype.componentDidUpdate = function () {
|
|
this.child(".popup-button").popup('refresh');
|
|
};
|
|
Popup.prototype.renderCore = function () {
|
|
return (React.createElement("div", {role: this.props.role}, React.createElement("div", {className: genericClassName("ui button popup-button", this.props)}, genericContent(this.props)), React.createElement("div", {className: "ui popup transition hidden"}, this.props.children)));
|
|
};
|
|
return Popup;
|
|
}(data.Component));
|
|
exports.Popup = Popup;
|
|
var Field = (function (_super) {
|
|
__extends(Field, _super);
|
|
function Field() {
|
|
_super.apply(this, arguments);
|
|
}
|
|
Field.prototype.renderCore = function () {
|
|
return (React.createElement("div", {className: "field"}, this.props.label ? React.createElement("label", null, this.props.label) : null, this.props.children));
|
|
};
|
|
return Field;
|
|
}(data.Component));
|
|
exports.Field = Field;
|
|
var Input = (function (_super) {
|
|
__extends(Input, _super);
|
|
function Input() {
|
|
_super.apply(this, arguments);
|
|
}
|
|
Input.prototype.copy = function () {
|
|
var p = this.props;
|
|
var el = ReactDOM.findDOMNode(this);
|
|
if (!p.lines || p.lines == 1) {
|
|
var inp = el.getElementsByTagName("input")[0];
|
|
inp.select();
|
|
}
|
|
else {
|
|
var inp = el.getElementsByTagName("textarea")[0];
|
|
inp.select();
|
|
}
|
|
try {
|
|
var success = document.execCommand("copy");
|
|
pxt.debug('copy: ' + success);
|
|
}
|
|
catch (e) {
|
|
}
|
|
};
|
|
Input.prototype.renderCore = function () {
|
|
var _this = this;
|
|
var p = this.props;
|
|
var copyBtn = p.copy && document.queryCommandSupported('copy')
|
|
? React.createElement(Button, {class: "ui right labeled primary icon button", text: lf("Copy"), icon: "copy", onClick: function () { return _this.copy(); }})
|
|
: null;
|
|
return (React.createElement(Field, {label: p.label}, React.createElement("div", {className: "ui input" + (p.inputLabel ? " labelled" : "") + (p.copy ? " action fluid" : "") + (p.disabled ? " disabled" : "")}, p.inputLabel ? (React.createElement("div", {className: "ui label"}, p.inputLabel)) : "", !p.lines || p.lines == 1 ? React.createElement("input", {className: p.class || "", type: p.type || "text", placeholder: p.placeholder, value: p.value, readOnly: !!p.readOnly, onChange: function (v) { return p.onChange(v.target.value); }})
|
|
: React.createElement("textarea", {className: "ui input " + (p.class || "") + (p.inputLabel ? " labelled" : ""), rows: p.lines, placeholder: p.placeholder, value: p.value, readOnly: !!p.readOnly, onChange: function (v) { return p.onChange(v.target.value); }}), copyBtn)));
|
|
};
|
|
return Input;
|
|
}(data.Component));
|
|
exports.Input = Input;
|
|
var Checkbox = (function (_super) {
|
|
__extends(Checkbox, _super);
|
|
function Checkbox() {
|
|
_super.apply(this, arguments);
|
|
}
|
|
Checkbox.prototype.renderCore = function () {
|
|
var p = this.props;
|
|
return React.createElement(Field, {label: p.label}, React.createElement("div", {className: "ui toggle checkbox"}, React.createElement("input", {type: "checkbox", checked: p.checked, onChange: function (v) { return p.onChange(v.target.value); }}), p.inputLabel ? React.createElement("label", null, p.inputLabel) : undefined));
|
|
};
|
|
return Checkbox;
|
|
}(data.Component));
|
|
exports.Checkbox = Checkbox;
|
|
var Segment = (function (_super) {
|
|
__extends(Segment, _super);
|
|
function Segment() {
|
|
_super.apply(this, arguments);
|
|
}
|
|
Segment.prototype.renderCore = function () {
|
|
var _a = this.props, attached = _a.attached, basic = _a.basic, children = _a.children, circular = _a.circular, className = _a.className, clearing = _a.clearing, color = _a.color, compact = _a.compact, disabled = _a.disabled, floated = _a.floated, inverted = _a.inverted, loading = _a.loading, padded = _a.padded, piled = _a.piled, raised = _a.raised, secondary = _a.secondary, size = _a.size, stacked = _a.stacked, tertiary = _a.tertiary, textAlign = _a.textAlign, vertical = _a.vertical;
|
|
var classes = cx([
|
|
'ui',
|
|
color,
|
|
size,
|
|
basic ? 'basic' : '',
|
|
circular ? 'circular' : '',
|
|
clearing ? 'clearing' : '',
|
|
compact ? 'compact' : '',
|
|
disabled ? 'disabled' : '',
|
|
inverted ? 'inverted' : '',
|
|
loading ? 'loading' : '',
|
|
piled ? 'piled' : '',
|
|
raised ? 'raised' : '',
|
|
secondary ? 'secondary' : '',
|
|
stacked ? 'stacked' : '',
|
|
tertiary ? 'tertiary' : '',
|
|
vertical ? 'vertical' : '',
|
|
attached ? attached + " attached" : '',
|
|
padded ? padded + " padded" : '',
|
|
textAlign ? (textAlign == 'justified' ? 'justified' : textAlign + " aligned") : '',
|
|
floated ? floated + " floated" : '',
|
|
'segment',
|
|
className,
|
|
]);
|
|
return (React.createElement("div", {className: classes}, children));
|
|
};
|
|
return Segment;
|
|
}(data.Component));
|
|
exports.Segment = Segment;
|
|
var MenuItem = (function (_super) {
|
|
__extends(MenuItem, _super);
|
|
function MenuItem(props) {
|
|
var _this = this;
|
|
_super.call(this, props);
|
|
this.handleClick = function (e) {
|
|
var onClick = _this.props.onClick;
|
|
if (onClick)
|
|
onClick(e, _this.props);
|
|
};
|
|
}
|
|
MenuItem.prototype.renderCore = function () {
|
|
var _a = this.props, active = _a.active, children = _a.children, className = _a.className, color = _a.color, content = _a.content, fitted = _a.fitted, header = _a.header, icon = _a.icon, link = _a.link, name = _a.name, onClick = _a.onClick, position = _a.position;
|
|
var classes = cx([
|
|
color,
|
|
position,
|
|
active ? 'active' : '',
|
|
icon === true || icon && !(name || content) ? 'icon' : '',
|
|
header ? 'header' : '',
|
|
link || onClick ? 'link' : '',
|
|
fitted ? (fitted == true ? "" + fitted : "fitted " + fitted) : '',
|
|
'item',
|
|
className
|
|
]);
|
|
if (children) {
|
|
return React.createElement("div", {className: classes, onClick: this.handleClick}, children);
|
|
}
|
|
return (React.createElement("div", {className: classes, onClick: this.handleClick}, icon ? React.createElement("i", {className: "icon " + icon}) : undefined, content || name));
|
|
};
|
|
return MenuItem;
|
|
}(data.Component));
|
|
exports.MenuItem = MenuItem;
|
|
var Menu = (function (_super) {
|
|
__extends(Menu, _super);
|
|
function Menu(props) {
|
|
_super.call(this, props);
|
|
}
|
|
Menu.prototype.renderCore = function () {
|
|
var _a = this.props, attached = _a.attached, borderless = _a.borderless, children = _a.children, className = _a.className, color = _a.color, compact = _a.compact, fixed = _a.fixed, floated = _a.floated, fluid = _a.fluid, icon = _a.icon, inverted = _a.inverted, pagination = _a.pagination, pointing = _a.pointing, secondary = _a.secondary, size = _a.size, stackable = _a.stackable, tabular = _a.tabular, text = _a.text, vertical = _a.vertical;
|
|
var classes = cx([
|
|
'ui',
|
|
color,
|
|
size,
|
|
borderless ? 'borderless' : '',
|
|
compact ? 'compact' : '',
|
|
fluid ? 'fluid' : '',
|
|
inverted ? 'inverted' : '',
|
|
pagination ? 'pagination' : '',
|
|
pointing ? 'pointing' : '',
|
|
secondary ? 'secondary' : '',
|
|
stackable ? 'stackable' : '',
|
|
text ? 'text' : '',
|
|
vertical ? 'vertical' : '',
|
|
attached ? (attached == true ? 'attached' : attached + " attached ") : '',
|
|
floated ? (floated == true ? 'floated' : floated + " floated") : '',
|
|
icon ? (icon == true ? 'icon' : icon + " icon") : '',
|
|
tabular ? (tabular == true ? 'tabular' : tabular + " tabular") : '',
|
|
fixed ? "tabular " + tabular : '',
|
|
className,
|
|
'menu'
|
|
]);
|
|
return (React.createElement("div", {className: classes}, children));
|
|
};
|
|
return Menu;
|
|
}(data.Component));
|
|
exports.Menu = Menu;
|
|
var Modal = (function (_super) {
|
|
__extends(Modal, _super);
|
|
function Modal(props) {
|
|
var _this = this;
|
|
_super.call(this, props);
|
|
this.getMountNode = function () { return _this.props.mountNode || document.body; };
|
|
this.handleClose = function (e) {
|
|
var onClose = _this.props.onClose;
|
|
if (onClose)
|
|
onClose(e, _this.props);
|
|
if (_this.state.open != false)
|
|
_this.setState({ open: false });
|
|
};
|
|
this.handleOpen = function (e) {
|
|
var onOpen = _this.props.onOpen;
|
|
if (onOpen)
|
|
onOpen(e, _this.props);
|
|
if (_this.state.open != true)
|
|
_this.setState({ open: true });
|
|
};
|
|
this.setPosition = function () {
|
|
if (_this.ref) {
|
|
var mountNode = _this.getMountNode();
|
|
var height = _this.ref.getBoundingClientRect().height;
|
|
var marginTop = -Math.round(height / 2);
|
|
var scrolling = height >= window.innerHeight;
|
|
var newState = {};
|
|
if (_this.state.marginTop !== marginTop) {
|
|
newState.marginTop = marginTop;
|
|
}
|
|
if (_this.state.scrolling !== scrolling) {
|
|
newState.scrolling = scrolling;
|
|
if (scrolling) {
|
|
mountNode.classList.add('scrolling');
|
|
}
|
|
else {
|
|
mountNode.classList.remove('scrolling');
|
|
}
|
|
}
|
|
if (Object.keys(newState).length > 0)
|
|
_this.setState(newState);
|
|
}
|
|
_this.animationId = requestAnimationFrame(_this.setPosition);
|
|
};
|
|
this.handlePortalMount = function () {
|
|
var dimmer = _this.props.dimmer;
|
|
var mountNode = _this.getMountNode();
|
|
if (dimmer) {
|
|
mountNode.classList.add('dimmable', 'dimmed');
|
|
if (dimmer === 'blurring' && !pxt.options.light) {
|
|
mountNode.classList.add('blurring');
|
|
}
|
|
}
|
|
_this.setPosition();
|
|
};
|
|
this.handleRef = function (c) { return (_this.ref = c); };
|
|
this.handlePortalUnmount = function () {
|
|
var mountNode = _this.getMountNode();
|
|
mountNode.classList.remove('blurring', 'dimmable', 'dimmed', 'scrollable');
|
|
if (_this.animationId)
|
|
cancelAnimationFrame(_this.animationId);
|
|
};
|
|
this.id = Util.guidGen();
|
|
this.state = {
|
|
open: this.props.open
|
|
};
|
|
}
|
|
Modal.prototype.componentWillUnmount = function () {
|
|
this.handlePortalUnmount();
|
|
};
|
|
Modal.prototype.componentWillMount = function () {
|
|
var open = this.props.open;
|
|
this.state = { open: open };
|
|
};
|
|
Modal.prototype.componentWillReceiveProps = function (nextProps) {
|
|
var newState = {};
|
|
if (nextProps.open != undefined) {
|
|
newState.open = nextProps.open;
|
|
}
|
|
if (Object.keys(newState).length > 0)
|
|
this.setState(newState);
|
|
};
|
|
Modal.prototype.renderCore = function () {
|
|
var _this = this;
|
|
var open = this.state.open;
|
|
var _a = this.props, basic = _a.basic, children = _a.children, className = _a.className, closeIcon = _a.closeIcon, closeOnDimmerClick = _a.closeOnDimmerClick, closeOnDocumentClick = _a.closeOnDocumentClick, dimmer = _a.dimmer, dimmerClassName = _a.dimmerClassName, size = _a.size;
|
|
var _b = this.state, marginTop = _b.marginTop, scrolling = _b.scrolling;
|
|
var classes = cx([
|
|
'ui',
|
|
size,
|
|
basic ? 'basic' : '',
|
|
scrolling ? 'scrolling' : '',
|
|
'modal transition visible active',
|
|
className,
|
|
]);
|
|
var closeIconName = closeIcon === true ? 'close' : closeIcon;
|
|
var modalJSX = (React.createElement("div", {className: classes, style: { marginTop: marginTop }, ref: this.handleRef, role: "dialog", "aria-labelledby": this.id + 'title', "aria-describedby": this.id + 'desc'}, this.props.header ? React.createElement("div", {id: this.id + 'title', className: "header " + (this.props.headerClass || "")}, this.props.header, this.props.closeIcon ? React.createElement(Button, {icon: closeIconName, class: "clear right floated", onClick: function () { return _this.handleClose(null); }}) : undefined, this.props.helpUrl ?
|
|
React.createElement("a", {className: "ui button icon-and-text right floated labeled", href: this.props.helpUrl, target: "_docs"}, React.createElement("i", {className: "help icon"}), lf("Help"))
|
|
: undefined) : undefined, React.createElement("div", {id: this.id + 'desc', className: "content"}, children), this.props.action && this.props.actionClick ?
|
|
React.createElement("div", {className: "actions"}, React.createElement(Button, {text: this.props.action, class: "approve primary " + (this.props.actionLoading ? "loading" : ""), onClick: function () {
|
|
_this.props.actionClick();
|
|
}})) : undefined));
|
|
var dimmerClasses = !dimmer
|
|
? null
|
|
: cx([
|
|
'ui',
|
|
dimmer === 'inverted' ? 'inverted' : '',
|
|
pxt.options.light ? '' : "transition",
|
|
'page modals dimmer visible active',
|
|
dimmerClassName
|
|
]);
|
|
var blurring = dimmer === 'blurring';
|
|
return (React.createElement(Portal, {closeOnRootNodeClick: closeOnDimmerClick, closeOnDocumentClick: closeOnDocumentClick, className: dimmerClasses, mountNode: this.getMountNode(), onMount: this.handlePortalMount, onUnmount: this.handlePortalUnmount, onClose: this.handleClose, onOpen: this.handleOpen, open: open}, modalJSX));
|
|
};
|
|
return Modal;
|
|
}(data.Component));
|
|
exports.Modal = Modal;
|
|
var Portal = (function (_super) {
|
|
__extends(Portal, _super);
|
|
function Portal(props) {
|
|
var _this = this;
|
|
_super.call(this, props);
|
|
this.handleDocumentClick = function (e) {
|
|
var _a = _this.props, closeOnDocumentClick = _a.closeOnDocumentClick, closeOnRootNodeClick = _a.closeOnRootNodeClick;
|
|
if (!_this.rootNode || !_this.portalNode || _this.portalNode.contains(e.target))
|
|
return;
|
|
var didClickInRootNode = _this.rootNode.contains(e.target);
|
|
if (closeOnDocumentClick && !didClickInRootNode || closeOnRootNodeClick && didClickInRootNode) {
|
|
_this.close(e);
|
|
}
|
|
};
|
|
this.close = function (e) {
|
|
var onClose = _this.props.onClose;
|
|
if (onClose)
|
|
onClose(e);
|
|
if (_this.state.open != false)
|
|
_this.setState({ open: false });
|
|
};
|
|
this.open = function (e) {
|
|
var onOpen = _this.props.onOpen;
|
|
if (onOpen)
|
|
onOpen(e);
|
|
if (_this.state.open != true)
|
|
_this.setState({ open: true });
|
|
};
|
|
this.mountPortal = function () {
|
|
if (_this.rootNode)
|
|
return;
|
|
var _a = _this.props.mountNode, mountNode = _a === void 0 ? document.body : _a;
|
|
_this.rootNode = document.createElement('div');
|
|
mountNode.appendChild(_this.rootNode);
|
|
document.addEventListener('click', _this.handleDocumentClick);
|
|
var onMount = _this.props.onMount;
|
|
if (onMount)
|
|
onMount();
|
|
};
|
|
this.unmountPortal = function () {
|
|
if (!_this.rootNode)
|
|
return;
|
|
ReactDOM.unmountComponentAtNode(_this.rootNode);
|
|
_this.rootNode.parentNode.removeChild(_this.rootNode);
|
|
_this.rootNode = null;
|
|
_this.portalNode = null;
|
|
document.removeEventListener('click', _this.handleDocumentClick);
|
|
var onUnmount = _this.props.onUnmount;
|
|
if (onUnmount)
|
|
onUnmount();
|
|
};
|
|
}
|
|
Portal.prototype.componentDidMount = function () {
|
|
if (this.state.open) {
|
|
this.renderPortal();
|
|
}
|
|
};
|
|
Portal.prototype.componentDidUpdate = function (prevProps, prevState) {
|
|
if (this.state.open) {
|
|
this.renderPortal();
|
|
}
|
|
if (prevState.open && !this.state.open) {
|
|
this.unmountPortal();
|
|
}
|
|
};
|
|
Portal.prototype.componentWillUnmount = function () {
|
|
this.unmountPortal();
|
|
};
|
|
Portal.prototype.componentWillMount = function () {
|
|
var open = this.props.open;
|
|
this.state = { open: open };
|
|
};
|
|
Portal.prototype.componentWillReceiveProps = function (nextProps) {
|
|
var newState = {};
|
|
if (nextProps.open != undefined) {
|
|
newState.open = nextProps.open;
|
|
}
|
|
if (Object.keys(newState).length > 0)
|
|
this.setState(newState);
|
|
};
|
|
Portal.prototype.renderPortal = function () {
|
|
var _a = this.props, children = _a.children, className = _a.className, open = _a.open;
|
|
this.mountPortal();
|
|
this.rootNode.className = className || '';
|
|
ReactDOM.unstable_renderSubtreeIntoContainer(this, React.Children.only(children), this.rootNode);
|
|
this.portalNode = this.rootNode.firstElementChild;
|
|
};
|
|
Portal.prototype.renderCore = function () {
|
|
return React.createElement("div", null);
|
|
};
|
|
return Portal;
|
|
}(data.Component));
|
|
exports.Portal = Portal;
|
|
|
|
},{"./data":11,"react":264,"react-dom":135}],33:[function(require,module,exports){
|
|
"use strict";
|
|
var workeriface = require("./workeriface");
|
|
var pkg = require("./package");
|
|
var iface;
|
|
function td2tsAsync(td) {
|
|
if (!iface)
|
|
iface = workeriface.makeWebWorker(pxt.webConfig.tdworkerjs);
|
|
return pkg.mainPkg.getCompileOptionsAsync()
|
|
.then(function (opts) {
|
|
opts.ast = true;
|
|
var res = pxtc.compile(opts);
|
|
var apiinfo = pxtc.getApiInfo(res.ast, true);
|
|
var arg = {
|
|
text: td,
|
|
useExtensions: true,
|
|
apiInfo: apiinfo
|
|
};
|
|
return iface.opAsync("td2ts", arg);
|
|
})
|
|
.then(function (resp) {
|
|
pxt.debug(resp);
|
|
return resp.text;
|
|
});
|
|
}
|
|
exports.td2tsAsync = td2tsAsync;
|
|
|
|
},{"./package":24,"./workeriface":36}],34:[function(require,module,exports){
|
|
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.default = new DOMParser().parseFromString("<xml id=\"blocklyToolboxDefinition\" style=\"display: none\">\n <category name=\"Loops\" nameid=\"loops\" colour=\"#107c10\" category=\"50\" iconclass=\"blocklyTreeIconloops\">\n <block type=\"controls_repeat_ext\">\n <value name=\"TIMES\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">4</field>\n </shadow>\n </value>\n </block>\n <block type=\"device_while\">\n <value name=\"COND\">\n <shadow type=\"logic_boolean\"></shadow>\n </value>\n </block>\n <block type=\"controls_simple_for\">\n <value name=\"TO\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">4</field>\n </shadow>\n </value>\n </block>\n </category>\n <category name=\"Logic\" nameid=\"logic\" colour=\"#006970\" category=\"49\" iconclass=\"blocklyTreeIconlogic\">\n <block type=\"controls_if\" gap=\"8\">\n <value name=\"IF0\">\n <shadow type=\"logic_boolean\">\n <field name=\"BOOL\">TRUE</field>\n </shadow>\n </value>\n </block>\n <block type=\"controls_if\" gap=\"8\">\n <mutation else=\"1\"></mutation>\n <value name=\"IF0\">\n <shadow type=\"logic_boolean\">\n <field name=\"BOOL\">TRUE</field>\n </shadow>\n </value>\n </block>\n <block type=\"logic_compare\" gap=\"8\">\n <value name=\"A\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <value name=\"B\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n </block>\n <block type=\"logic_compare\">\n <field name=\"OP\">LT</field>\n <value name=\"A\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <value name=\"B\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n </block>\n <block type=\"logic_operation\" gap=\"8\"></block>\n <block type=\"logic_operation\" gap=\"8\">\n <field name=\"OP\">OR</field>\n </block>\n <block type=\"logic_negate\"></block>\n <block type=\"logic_boolean\" gap=\"8\"></block>\n <block type=\"logic_boolean\">\n <field name=\"BOOL\">FALSE</field>\n </block>\n </category>\n <category name=\"Variables\" nameid=\"variables\" colour=\"#A80000\" custom=\"VARIABLE\" category=\"48\" iconclass=\"blocklyTreeIconvariables\">\n </category>\n <category name=\"Math\" nameid=\"math\" colour=\"#712672\" category=\"47\" iconclass=\"blocklyTreeIconmath\" expandedclass=\"blocklyTreeIconmath\">\n <block type=\"math_arithmetic\" gap=\"8\">\n <value name=\"A\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <value name=\"B\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n </block>\n <block type=\"math_arithmetic\" gap=\"8\">\n <value name=\"A\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <value name=\"B\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <field name=\"OP\">MINUS</field>\n </block>\n <block type=\"math_arithmetic\" gap=\"8\">\n <value name=\"A\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <value name=\"B\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <field name=\"OP\">MULTIPLY</field>\n </block>\n <block type=\"math_arithmetic\" gap=\"8\">\n <value name=\"A\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <value name=\"B\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <field name=\"OP\">DIVIDE</field>\n </block>\n <block type=\"math_number\" gap=\"8\">\n <field name=\"NUM\">0</field>\n </block>\n <block type=\"device_random\" gap=\"8\">\n <value name=\"limit\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">4</field>\n </shadow>\n </value>\n </block>\n <category colour=\"#712672\" name=\"More\" nameid=\"more\" iconclass=\"blocklyTreeIconmore\" expandedclass=\"blocklyTreeIconmore\">\n <block type=\"math_modulo\">\n <value name=\"DIVIDEND\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <value name=\"DIVISOR\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">1</field>\n </shadow>\n </value>\n </block>\n <block type=\"math_op2\" gap=\"8\">\n <value name=\"x\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <value name=\"y\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n </block>\n <block type=\"math_op2\" gap=\"8\">\n <value name=\"x\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <value name=\"y\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n <field name=\"op\">max</field>\n </block>\n <block type=\"math_op3\">\n <value name=\"x\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n </block>\n </category>\n </category>\n <category name=\"Advanced\" nameid=\"advanced\" colour=\"#3c3c3c\" weight=\"1\" iconclass=\"blocklyTreeIconadvanced\" expandedclass=\"blocklyTreeIconadvanced\">\n <category colour=\"#D83B01\" name=\"Lists\" nameid=\"lists\" category=\"45\" iconclass=\"blocklyTreeIconlists\">\n <block type=\"lists_create_with\">\n <mutation items=\"1\"></mutation>\n <value name=\"ADD0\">\n <shadow type=\"math_number\">\n <field name=\"NUM\">0</field>\n </shadow>\n </value>\n </block>\n <block type=\"lists_length\"></block>\n </category>\n <category colour=\"#996600\" name=\"Text\" nameid=\"text\" category=\"46\" iconclass=\"blocklyTreeIcontext\">\n <block type=\"text\"></block>\n <block type=\"text_length\">\n <value name=\"VALUE\">\n <shadow type=\"text\">\n <field name=\"TEXT\">abc</field>\n </shadow>\n </value>\n </block>\n <block type=\"text_join\">\n <mutation items=\"2\"></mutation>\n <value name=\"ADD0\">\n <shadow type=\"text\">\n <field name=\"TEXT\"></field>\n </shadow>\n </value>\n <value name=\"ADD1\">\n <shadow type=\"text\">\n <field name=\"TEXT\"></field>\n </shadow>\n </value>\n </block>\n </category>\n </category>\n </xml>", "text/xml");
|
|
|
|
},{}],35:[function(require,module,exports){
|
|
/// <reference path="../../typings/globals/react/index.d.ts" />
|
|
/// <reference path="../../typings/globals/react-dom/index.d.ts" />
|
|
/// <reference path="../../built/pxtlib.d.ts" />
|
|
"use strict";
|
|
var __extends = (this && this.__extends) || function (d, b) {
|
|
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
var React = require("react");
|
|
var data = require("./data");
|
|
var sui = require("./sui");
|
|
var TutorialMenuItem = (function (_super) {
|
|
__extends(TutorialMenuItem, _super);
|
|
function TutorialMenuItem(props) {
|
|
_super.call(this, props);
|
|
}
|
|
TutorialMenuItem.prototype.openTutorialStep = function (step) {
|
|
pxt.tickEvent("tutorial.step", { tutorial: this.props.parent.state.tutorial, step: step });
|
|
this.props.parent.setState({ tutorialStep: step, tutorialReady: false });
|
|
this.props.parent.setTutorialStep(step);
|
|
};
|
|
TutorialMenuItem.prototype.render = function () {
|
|
var _this = this;
|
|
var state = this.props.parent.state;
|
|
var tutorialReady = state.tutorialReady;
|
|
var targetTheme = pxt.appTarget.appTheme;
|
|
var tutorialSteps = state.tutorialSteps;
|
|
var currentStep = state.tutorialStep;
|
|
var tutorialName = state.tutorialName;
|
|
return React.createElement("div", {className: "ui item"}, React.createElement("div", {className: "ui item tutorial-menuitem"}, tutorialSteps.map(function (step, index) {
|
|
return React.createElement(sui.Button, {key: 'tutorialStep' + index, class: "icon circular " + (currentStep == index ? 'red selected' : 'inverted') + " " + (!tutorialReady ? 'disabled' : ''), text: " " + (index + 1) + " ", onClick: function () { return _this.openTutorialStep(index); }});
|
|
})));
|
|
};
|
|
return TutorialMenuItem;
|
|
}(data.Component));
|
|
exports.TutorialMenuItem = TutorialMenuItem;
|
|
var TutorialContent = (function (_super) {
|
|
__extends(TutorialContent, _super);
|
|
function TutorialContent(props) {
|
|
_super.call(this, props);
|
|
}
|
|
TutorialContent.notify = function (message) {
|
|
var tc = document.getElementById("tutorialcontent");
|
|
if (tc && tc.contentWindow)
|
|
tc.contentWindow.postMessage(message, "*");
|
|
};
|
|
TutorialContent.prototype.setPath = function (path) {
|
|
var docsUrl = pxt.webConfig.docsUrl || '/--docs';
|
|
var mode = this.props.parent.isBlocksEditor() ? "blocks" : "js";
|
|
var url = docsUrl + "#tutorial:" + path + ":" + mode + ":" + pxt.Util.localeInfo();
|
|
this.setUrl(url);
|
|
};
|
|
TutorialContent.prototype.setUrl = function (url) {
|
|
var el = document.getElementById("tutorialcontent");
|
|
if (el)
|
|
el.src = url;
|
|
else
|
|
this.props.parent.setState({ tutorialUrl: url });
|
|
};
|
|
TutorialContent.refresh = function () {
|
|
var el = document.getElementById("tutorialcontent");
|
|
if (el && el.contentWindow) {
|
|
el.parentElement.style.height = "";
|
|
el.parentElement.style.height = el.contentWindow.document.body.scrollHeight + "px";
|
|
}
|
|
};
|
|
TutorialContent.prototype.renderCore = function () {
|
|
var state = this.props.parent.state;
|
|
var docsUrl = state.tutorialUrl;
|
|
if (!docsUrl)
|
|
return null;
|
|
return React.createElement("iframe", {id: "tutorialcontent", onLoad: function () { return TutorialContent.refresh(); }, src: docsUrl, role: "complementary", sandbox: "allow-scripts allow-same-origin allow-popups"});
|
|
};
|
|
return TutorialContent;
|
|
}(data.Component));
|
|
exports.TutorialContent = TutorialContent;
|
|
var TutorialCard = (function (_super) {
|
|
__extends(TutorialCard, _super);
|
|
function TutorialCard(props) {
|
|
_super.call(this, props);
|
|
}
|
|
TutorialCard.prototype.previousTutorialStep = function () {
|
|
var currentStep = this.props.parent.state.tutorialStep;
|
|
var previousStep = currentStep - 1;
|
|
pxt.tickEvent("tutorial.previous", { tutorial: this.props.parent.state.tutorial, step: previousStep });
|
|
this.props.parent.setState({ tutorialStep: previousStep, tutorialReady: false });
|
|
this.props.parent.setTutorialStep(previousStep);
|
|
};
|
|
TutorialCard.prototype.nextTutorialStep = function () {
|
|
var currentStep = this.props.parent.state.tutorialStep;
|
|
var nextStep = currentStep + 1;
|
|
pxt.tickEvent("tutorial.next", { tutorial: this.props.parent.state.tutorial, step: nextStep });
|
|
this.props.parent.setState({ tutorialStep: nextStep, tutorialReady: false });
|
|
this.props.parent.setTutorialStep(nextStep);
|
|
};
|
|
TutorialCard.prototype.finishTutorial = function () {
|
|
this.props.parent.exitTutorial();
|
|
};
|
|
TutorialCard.prototype.setPath = function (path) {
|
|
var tc = this.refs["tutorialcontent"];
|
|
if (!tc)
|
|
return;
|
|
tc.setPath(path);
|
|
};
|
|
TutorialCard.prototype.render = function () {
|
|
var _this = this;
|
|
var state = this.props.parent.state;
|
|
var tutorialReady = state.tutorialReady;
|
|
var currentStep = state.tutorialStep;
|
|
var cardLocation = state.tutorialCardLocation || 'bottom';
|
|
var maxSteps = state.tutorialSteps.length;
|
|
var hasPrevious = currentStep != 0;
|
|
var hasNext = currentStep != maxSteps - 1;
|
|
var hasFinish = currentStep == maxSteps - 1;
|
|
return React.createElement("div", {id: "tutorialcard", className: "ui " + (pxt.options.light ? "" : "transition fly in") + " " + cardLocation + " visible active"}, React.createElement("div", {className: "ui raised fluid card"}, React.createElement("div", {className: "ui"}, React.createElement(TutorialContent, {ref: "tutorialcontent", parent: this.props.parent})), React.createElement("div", {className: "extra content"}, React.createElement("div", {className: "ui two buttons"}, hasPrevious ? React.createElement(sui.Button, {icon: "left chevron", class: "ui icon red button " + (!tutorialReady ? 'disabled' : ''), text: lf("Back"), onClick: function () { return _this.previousTutorialStep(); }}) : undefined, hasNext ? React.createElement(sui.Button, {icon: "right chevron", class: "ui icon green button " + (!tutorialReady ? 'disabled' : ''), text: lf("Next"), onClick: function () { return _this.nextTutorialStep(); }}) : undefined, hasFinish ? React.createElement(sui.Button, {icon: "left checkmark", class: "ui icon orange button " + (!tutorialReady ? 'disabled' : ''), text: lf("Finish"), onClick: function () { return _this.finishTutorial(); }}) : undefined))));
|
|
};
|
|
return TutorialCard;
|
|
}(data.Component));
|
|
exports.TutorialCard = TutorialCard;
|
|
|
|
},{"./data":11,"./sui":32,"react":264}],36:[function(require,module,exports){
|
|
"use strict";
|
|
var U = pxt.Util;
|
|
function wrap(send) {
|
|
var pendingMsgs = {};
|
|
var msgId = 0;
|
|
var q = new U.PromiseQueue();
|
|
var initPromise = new Promise(function (resolve, reject) {
|
|
pendingMsgs["ready"] = resolve;
|
|
});
|
|
q.enqueue("main", function () { return initPromise; });
|
|
var recvHandler = function (data) {
|
|
if (pendingMsgs.hasOwnProperty(data.id)) {
|
|
var cb = pendingMsgs[data.id];
|
|
delete pendingMsgs[data.id];
|
|
cb(data.result);
|
|
}
|
|
};
|
|
function opAsync(op, arg) {
|
|
return q.enqueue("main", function () { return new Promise(function (resolve, reject) {
|
|
var id = "" + msgId++;
|
|
pendingMsgs[id] = function (v) {
|
|
if (!v) {
|
|
//pxt.reportError("worker", "no response")
|
|
reject(new Error("no response"));
|
|
}
|
|
else if (v.errorMessage) {
|
|
//pxt.reportError("worker", v.errorMessage)
|
|
reject(new Error(v.errorMessage));
|
|
}
|
|
else {
|
|
resolve(v);
|
|
}
|
|
};
|
|
send({ id: id, op: op, arg: arg });
|
|
}); });
|
|
}
|
|
return { opAsync: opAsync, recvHandler: recvHandler };
|
|
}
|
|
exports.wrap = wrap;
|
|
function makeWebWorker(workerFile) {
|
|
var worker = new Worker(workerFile);
|
|
var iface = wrap(function (v) { return worker.postMessage(v); });
|
|
worker.onmessage = function (ev) {
|
|
iface.recvHandler(ev.data);
|
|
};
|
|
return iface;
|
|
}
|
|
exports.makeWebWorker = makeWebWorker;
|
|
function makeWebSocket(url, onOOB) {
|
|
if (onOOB === void 0) { onOOB = null; }
|
|
var ws = new WebSocket(url);
|
|
var sendq = [];
|
|
var iface = wrap(function (v) {
|
|
var s = JSON.stringify(v);
|
|
if (sendq)
|
|
sendq.push(s);
|
|
else
|
|
ws.send(s);
|
|
});
|
|
ws.onmessage = function (ev) {
|
|
var js = JSON.parse(ev.data);
|
|
if (onOOB && js.id == null) {
|
|
onOOB(js);
|
|
}
|
|
else {
|
|
iface.recvHandler(js);
|
|
}
|
|
};
|
|
ws.onopen = function (ev) {
|
|
pxt.debug('socket opened');
|
|
for (var _i = 0, sendq_1 = sendq; _i < sendq_1.length; _i++) {
|
|
var m = sendq_1[_i];
|
|
ws.send(m);
|
|
}
|
|
sendq = null;
|
|
};
|
|
ws.onclose = function (ev) {
|
|
pxt.debug('socket closed');
|
|
};
|
|
return iface;
|
|
}
|
|
exports.makeWebSocket = makeWebSocket;
|
|
|
|
},{}],37:[function(require,module,exports){
|
|
/// <reference path="../../built/pxtlib.d.ts" />
|
|
/// <reference path="../../built/pxteditor.d.ts" />
|
|
"use strict";
|
|
var db = require("./db");
|
|
var core = require("./core");
|
|
var data = require("./data");
|
|
var cloudworkspace = require("./cloudworkspace");
|
|
var fileworkspace = require("./fileworkspace");
|
|
var memoryworkspace = require("./memoryworkspace");
|
|
var scripts = new db.Table("script");
|
|
var U = pxt.Util;
|
|
var Cloud = pxt.Cloud;
|
|
var lf = U.lf;
|
|
var impl;
|
|
function setupWorkspace(id) {
|
|
U.assert(!impl, "workspace set twice");
|
|
pxt.debug("workspace: " + id);
|
|
switch (id) {
|
|
case "fs":
|
|
case "file":
|
|
impl = fileworkspace.provider;
|
|
break;
|
|
case "mem":
|
|
case "memory":
|
|
impl = memoryworkspace.provider;
|
|
break;
|
|
case "cloud":
|
|
default:
|
|
impl = cloudworkspace.provider;
|
|
break;
|
|
}
|
|
}
|
|
exports.setupWorkspace = setupWorkspace;
|
|
function getHeaders(withDeleted) {
|
|
if (withDeleted === void 0) { withDeleted = false; }
|
|
checkSession();
|
|
var r = impl.getHeaders();
|
|
if (!withDeleted)
|
|
r = r.filter(function (r) { return !r.isDeleted; });
|
|
r.sort(function (a, b) { return b.recentUse - a.recentUse; });
|
|
return r;
|
|
}
|
|
exports.getHeaders = getHeaders;
|
|
function getHeader(id) {
|
|
checkSession();
|
|
var hd = impl.getHeader(id);
|
|
if (hd && !hd.isDeleted)
|
|
return hd;
|
|
return null;
|
|
}
|
|
exports.getHeader = getHeader;
|
|
var sessionID;
|
|
function isSessionOutdated() {
|
|
return pxt.storage.getLocal('pxt_workspace_session_id') != sessionID;
|
|
}
|
|
exports.isSessionOutdated = isSessionOutdated;
|
|
function checkSession() {
|
|
if (isSessionOutdated()) {
|
|
Util.assert(false, "trying to access outdated session");
|
|
}
|
|
}
|
|
function initAsync() {
|
|
if (!impl)
|
|
impl = cloudworkspace.provider;
|
|
// generate new workspace session id to avoid races with other tabs
|
|
sessionID = Util.guidGen();
|
|
pxt.storage.setLocal('pxt_workspace_session_id', sessionID);
|
|
pxt.debug("workspace session: " + sessionID);
|
|
return impl.initAsync(pxt.appTarget.id);
|
|
}
|
|
exports.initAsync = initAsync;
|
|
function getTextAsync(id) {
|
|
checkSession();
|
|
return impl.getTextAsync(id);
|
|
}
|
|
exports.getTextAsync = getTextAsync;
|
|
// https://github.com/Microsoft/pxt-backend/blob/master/docs/sharing.md#anonymous-publishing
|
|
function anonymousPublishAsync(h, text, meta) {
|
|
var saveId = {};
|
|
h.saveId = saveId;
|
|
var stext = JSON.stringify(text, null, 2) + "\n";
|
|
var scrReq = {
|
|
name: h.name,
|
|
target: h.target,
|
|
description: meta.description,
|
|
editor: h.editor,
|
|
text: text,
|
|
meta: {
|
|
versions: pxt.appTarget.versions,
|
|
blocksHeight: meta.blocksHeight,
|
|
blocksWidth: meta.blocksWidth
|
|
}
|
|
};
|
|
pxt.debug("publishing script; " + stext.length + " bytes");
|
|
return Cloud.privatePostAsync("scripts", scrReq)
|
|
.then(function (inf) {
|
|
h.pubId = inf.id;
|
|
h.pubCurrent = h.saveId === saveId;
|
|
h.meta = inf.meta;
|
|
pxt.debug("published; id /" + inf.id);
|
|
return saveAsync(h)
|
|
.then(function () { return inf; });
|
|
});
|
|
}
|
|
exports.anonymousPublishAsync = anonymousPublishAsync;
|
|
function saveAsync(h, text) {
|
|
checkSession();
|
|
if (text || h.isDeleted) {
|
|
h.pubCurrent = false;
|
|
h.blobCurrent = false;
|
|
h.modificationTime = U.nowSeconds();
|
|
}
|
|
h.recentUse = U.nowSeconds();
|
|
return impl.saveAsync(h, text);
|
|
}
|
|
exports.saveAsync = saveAsync;
|
|
function installAsync(h0, text) {
|
|
checkSession();
|
|
return impl.installAsync(h0, text);
|
|
}
|
|
exports.installAsync = installAsync;
|
|
function saveScreenshotAsync(h, data, icon) {
|
|
checkSession();
|
|
return impl.saveScreenshotAsync
|
|
? impl.saveScreenshotAsync(h, data, icon)
|
|
: Promise.resolve();
|
|
}
|
|
exports.saveScreenshotAsync = saveScreenshotAsync;
|
|
function fixupFileNames(txt) {
|
|
if (!txt)
|
|
return txt;
|
|
for (var oldName in ["kind.json", "yelm.json"]) {
|
|
if (!txt[pxt.CONFIG_NAME] && txt[oldName]) {
|
|
txt[pxt.CONFIG_NAME] = txt[oldName];
|
|
delete txt[oldName];
|
|
}
|
|
}
|
|
return txt;
|
|
}
|
|
exports.fixupFileNames = fixupFileNames;
|
|
var scriptDlQ = new U.PromiseQueue();
|
|
//let scriptCache:any = {}
|
|
function getPublishedScriptAsync(id) {
|
|
checkSession();
|
|
//if (scriptCache.hasOwnProperty(id)) return Promise.resolve(scriptCache[id])
|
|
if (pxt.github.isGithubId(id))
|
|
id = pxt.github.noramlizeRepoId(id);
|
|
var eid = encodeURIComponent(id);
|
|
return pxt.packagesConfigAsync()
|
|
.then(function (config) { return scriptDlQ.enqueue(id, function () { return scripts.getAsync(eid)
|
|
.then(function (v) { return v.files; }, function (e) {
|
|
return (pxt.github.isGithubId(id) ?
|
|
pxt.github.downloadPackageAsync(id, config).then(function (v) { return v.files; }) :
|
|
Cloud.downloadScriptFilesAsync(id))
|
|
.catch(core.handleNetworkError)
|
|
.then(function (files) { return scripts.setAsync({ id: eid, files: files })
|
|
.then(function () {
|
|
//return (scriptCache[id] = files)
|
|
return files;
|
|
}); });
|
|
})
|
|
.then(fixupFileNames); }); });
|
|
}
|
|
exports.getPublishedScriptAsync = getPublishedScriptAsync;
|
|
function installByIdAsync(id) {
|
|
return Cloud.privateGetAsync(id)
|
|
.then(function (scr) {
|
|
return getPublishedScriptAsync(scr.id)
|
|
.then(function (files) { return installAsync({
|
|
name: scr.name,
|
|
pubId: id,
|
|
pubCurrent: true,
|
|
meta: scr.meta,
|
|
editor: scr.editor,
|
|
target: scr.target,
|
|
}, files); });
|
|
});
|
|
}
|
|
exports.installByIdAsync = installByIdAsync;
|
|
function saveToCloudAsync(h) {
|
|
checkSession();
|
|
return impl.saveToCloudAsync(h);
|
|
}
|
|
exports.saveToCloudAsync = saveToCloudAsync;
|
|
function syncAsync() {
|
|
checkSession();
|
|
return impl.syncAsync();
|
|
}
|
|
exports.syncAsync = syncAsync;
|
|
function resetAsync() {
|
|
checkSession();
|
|
return impl.resetAsync();
|
|
}
|
|
exports.resetAsync = resetAsync;
|
|
/*
|
|
header:<guid> - one header
|
|
header:* - all headers
|
|
*/
|
|
data.mountVirtualApi("header", {
|
|
getSync: function (p) {
|
|
p = data.stripProtocol(p);
|
|
if (p == "*")
|
|
return getHeaders();
|
|
return getHeader(p);
|
|
},
|
|
});
|
|
/*
|
|
text:<guid> - all files
|
|
text:<guid>/<filename> - one file
|
|
*/
|
|
data.mountVirtualApi("text", {
|
|
getAsync: function (p) {
|
|
var m = /^[\w\-]+:([^\/]+)(\/(.*))?/.exec(p);
|
|
return getTextAsync(m[1])
|
|
.then(function (files) {
|
|
if (m[3])
|
|
return files[m[3]];
|
|
else
|
|
return files;
|
|
});
|
|
},
|
|
});
|
|
|
|
},{"./cloudworkspace":5,"./core":10,"./data":11,"./db":12,"./fileworkspace":17,"./memoryworkspace":22}],38:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
module.exports = argsArray;
|
|
|
|
function argsArray(fun) {
|
|
return function () {
|
|
var len = arguments.length;
|
|
if (len) {
|
|
var args = [];
|
|
var i = -1;
|
|
while (++i < len) {
|
|
args[i] = arguments[i];
|
|
}
|
|
return fun.call(this, args);
|
|
} else {
|
|
return fun.call(this, []);
|
|
}
|
|
};
|
|
}
|
|
},{}],39:[function(require,module,exports){
|
|
'use strict'
|
|
|
|
exports.byteLength = byteLength
|
|
exports.toByteArray = toByteArray
|
|
exports.fromByteArray = fromByteArray
|
|
|
|
var lookup = []
|
|
var revLookup = []
|
|
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
|
|
|
|
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
|
for (var i = 0, len = code.length; i < len; ++i) {
|
|
lookup[i] = code[i]
|
|
revLookup[code.charCodeAt(i)] = i
|
|
}
|
|
|
|
revLookup['-'.charCodeAt(0)] = 62
|
|
revLookup['_'.charCodeAt(0)] = 63
|
|
|
|
function placeHoldersCount (b64) {
|
|
var len = b64.length
|
|
if (len % 4 > 0) {
|
|
throw new Error('Invalid string. Length must be a multiple of 4')
|
|
}
|
|
|
|
// the number of equal signs (place holders)
|
|
// if there are two placeholders, than the two characters before it
|
|
// represent one byte
|
|
// if there is only one, then the three characters before it represent 2 bytes
|
|
// this is just a cheap hack to not do indexOf twice
|
|
return b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0
|
|
}
|
|
|
|
function byteLength (b64) {
|
|
// base64 is 4/3 + up to two characters of the original data
|
|
return b64.length * 3 / 4 - placeHoldersCount(b64)
|
|
}
|
|
|
|
function toByteArray (b64) {
|
|
var i, j, l, tmp, placeHolders, arr
|
|
var len = b64.length
|
|
placeHolders = placeHoldersCount(b64)
|
|
|
|
arr = new Arr(len * 3 / 4 - placeHolders)
|
|
|
|
// if there are placeholders, only get up to the last complete 4 chars
|
|
l = placeHolders > 0 ? len - 4 : len
|
|
|
|
var L = 0
|
|
|
|
for (i = 0, j = 0; i < l; i += 4, j += 3) {
|
|
tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)]
|
|
arr[L++] = (tmp >> 16) & 0xFF
|
|
arr[L++] = (tmp >> 8) & 0xFF
|
|
arr[L++] = tmp & 0xFF
|
|
}
|
|
|
|
if (placeHolders === 2) {
|
|
tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4)
|
|
arr[L++] = tmp & 0xFF
|
|
} else if (placeHolders === 1) {
|
|
tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2)
|
|
arr[L++] = (tmp >> 8) & 0xFF
|
|
arr[L++] = tmp & 0xFF
|
|
}
|
|
|
|
return arr
|
|
}
|
|
|
|
function tripletToBase64 (num) {
|
|
return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
|
|
}
|
|
|
|
function encodeChunk (uint8, start, end) {
|
|
var tmp
|
|
var output = []
|
|
for (var i = start; i < end; i += 3) {
|
|
tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
|
|
output.push(tripletToBase64(tmp))
|
|
}
|
|
return output.join('')
|
|
}
|
|
|
|
function fromByteArray (uint8) {
|
|
var tmp
|
|
var len = uint8.length
|
|
var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
|
|
var output = ''
|
|
var parts = []
|
|
var maxChunkLength = 16383 // must be multiple of 3
|
|
|
|
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
|
parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
|
|
}
|
|
|
|
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
if (extraBytes === 1) {
|
|
tmp = uint8[len - 1]
|
|
output += lookup[tmp >> 2]
|
|
output += lookup[(tmp << 4) & 0x3F]
|
|
output += '=='
|
|
} else if (extraBytes === 2) {
|
|
tmp = (uint8[len - 2] << 8) + (uint8[len - 1])
|
|
output += lookup[tmp >> 10]
|
|
output += lookup[(tmp >> 4) & 0x3F]
|
|
output += lookup[(tmp << 2) & 0x3F]
|
|
output += '='
|
|
}
|
|
|
|
parts.push(output)
|
|
|
|
return parts.join('')
|
|
}
|
|
|
|
},{}],40:[function(require,module,exports){
|
|
(function (process,global){
|
|
/* @preserve
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2013-2015 Petka Antonov
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
/**
|
|
* bluebird build version 3.4.7
|
|
* Features enabled: core, race, call_get, generators, map, nodeify, promisify, props, reduce, settle, some, using, timers, filter, any, each
|
|
*/
|
|
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Promise=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof _dereq_=="function"&&_dereq_;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof _dereq_=="function"&&_dereq_;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise) {
|
|
var SomePromiseArray = Promise._SomePromiseArray;
|
|
function any(promises) {
|
|
var ret = new SomePromiseArray(promises);
|
|
var promise = ret.promise();
|
|
ret.setHowMany(1);
|
|
ret.setUnwrap();
|
|
ret.init();
|
|
return promise;
|
|
}
|
|
|
|
Promise.any = function (promises) {
|
|
return any(promises);
|
|
};
|
|
|
|
Promise.prototype.any = function () {
|
|
return any(this);
|
|
};
|
|
|
|
};
|
|
|
|
},{}],2:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
var firstLineError;
|
|
try {throw new Error(); } catch (e) {firstLineError = e;}
|
|
var schedule = _dereq_("./schedule");
|
|
var Queue = _dereq_("./queue");
|
|
var util = _dereq_("./util");
|
|
|
|
function Async() {
|
|
this._customScheduler = false;
|
|
this._isTickUsed = false;
|
|
this._lateQueue = new Queue(16);
|
|
this._normalQueue = new Queue(16);
|
|
this._haveDrainedQueues = false;
|
|
this._trampolineEnabled = true;
|
|
var self = this;
|
|
this.drainQueues = function () {
|
|
self._drainQueues();
|
|
};
|
|
this._schedule = schedule;
|
|
}
|
|
|
|
Async.prototype.setScheduler = function(fn) {
|
|
var prev = this._schedule;
|
|
this._schedule = fn;
|
|
this._customScheduler = true;
|
|
return prev;
|
|
};
|
|
|
|
Async.prototype.hasCustomScheduler = function() {
|
|
return this._customScheduler;
|
|
};
|
|
|
|
Async.prototype.enableTrampoline = function() {
|
|
this._trampolineEnabled = true;
|
|
};
|
|
|
|
Async.prototype.disableTrampolineIfNecessary = function() {
|
|
if (util.hasDevTools) {
|
|
this._trampolineEnabled = false;
|
|
}
|
|
};
|
|
|
|
Async.prototype.haveItemsQueued = function () {
|
|
return this._isTickUsed || this._haveDrainedQueues;
|
|
};
|
|
|
|
|
|
Async.prototype.fatalError = function(e, isNode) {
|
|
if (isNode) {
|
|
process.stderr.write("Fatal " + (e instanceof Error ? e.stack : e) +
|
|
"\n");
|
|
process.exit(2);
|
|
} else {
|
|
this.throwLater(e);
|
|
}
|
|
};
|
|
|
|
Async.prototype.throwLater = function(fn, arg) {
|
|
if (arguments.length === 1) {
|
|
arg = fn;
|
|
fn = function () { throw arg; };
|
|
}
|
|
if (typeof setTimeout !== "undefined") {
|
|
setTimeout(function() {
|
|
fn(arg);
|
|
}, 0);
|
|
} else try {
|
|
this._schedule(function() {
|
|
fn(arg);
|
|
});
|
|
} catch (e) {
|
|
throw new Error("No async scheduler available\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
}
|
|
};
|
|
|
|
function AsyncInvokeLater(fn, receiver, arg) {
|
|
this._lateQueue.push(fn, receiver, arg);
|
|
this._queueTick();
|
|
}
|
|
|
|
function AsyncInvoke(fn, receiver, arg) {
|
|
this._normalQueue.push(fn, receiver, arg);
|
|
this._queueTick();
|
|
}
|
|
|
|
function AsyncSettlePromises(promise) {
|
|
this._normalQueue._pushOne(promise);
|
|
this._queueTick();
|
|
}
|
|
|
|
if (!util.hasDevTools) {
|
|
Async.prototype.invokeLater = AsyncInvokeLater;
|
|
Async.prototype.invoke = AsyncInvoke;
|
|
Async.prototype.settlePromises = AsyncSettlePromises;
|
|
} else {
|
|
Async.prototype.invokeLater = function (fn, receiver, arg) {
|
|
if (this._trampolineEnabled) {
|
|
AsyncInvokeLater.call(this, fn, receiver, arg);
|
|
} else {
|
|
this._schedule(function() {
|
|
setTimeout(function() {
|
|
fn.call(receiver, arg);
|
|
}, 100);
|
|
});
|
|
}
|
|
};
|
|
|
|
Async.prototype.invoke = function (fn, receiver, arg) {
|
|
if (this._trampolineEnabled) {
|
|
AsyncInvoke.call(this, fn, receiver, arg);
|
|
} else {
|
|
this._schedule(function() {
|
|
fn.call(receiver, arg);
|
|
});
|
|
}
|
|
};
|
|
|
|
Async.prototype.settlePromises = function(promise) {
|
|
if (this._trampolineEnabled) {
|
|
AsyncSettlePromises.call(this, promise);
|
|
} else {
|
|
this._schedule(function() {
|
|
promise._settlePromises();
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
Async.prototype._drainQueue = function(queue) {
|
|
while (queue.length() > 0) {
|
|
var fn = queue.shift();
|
|
if (typeof fn !== "function") {
|
|
fn._settlePromises();
|
|
continue;
|
|
}
|
|
var receiver = queue.shift();
|
|
var arg = queue.shift();
|
|
fn.call(receiver, arg);
|
|
}
|
|
};
|
|
|
|
Async.prototype._drainQueues = function () {
|
|
this._drainQueue(this._normalQueue);
|
|
this._reset();
|
|
this._haveDrainedQueues = true;
|
|
this._drainQueue(this._lateQueue);
|
|
};
|
|
|
|
Async.prototype._queueTick = function () {
|
|
if (!this._isTickUsed) {
|
|
this._isTickUsed = true;
|
|
this._schedule(this.drainQueues);
|
|
}
|
|
};
|
|
|
|
Async.prototype._reset = function () {
|
|
this._isTickUsed = false;
|
|
};
|
|
|
|
module.exports = Async;
|
|
module.exports.firstLineError = firstLineError;
|
|
|
|
},{"./queue":26,"./schedule":29,"./util":36}],3:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise, INTERNAL, tryConvertToPromise, debug) {
|
|
var calledBind = false;
|
|
var rejectThis = function(_, e) {
|
|
this._reject(e);
|
|
};
|
|
|
|
var targetRejected = function(e, context) {
|
|
context.promiseRejectionQueued = true;
|
|
context.bindingPromise._then(rejectThis, rejectThis, null, this, e);
|
|
};
|
|
|
|
var bindingResolved = function(thisArg, context) {
|
|
if (((this._bitField & 50397184) === 0)) {
|
|
this._resolveCallback(context.target);
|
|
}
|
|
};
|
|
|
|
var bindingRejected = function(e, context) {
|
|
if (!context.promiseRejectionQueued) this._reject(e);
|
|
};
|
|
|
|
Promise.prototype.bind = function (thisArg) {
|
|
if (!calledBind) {
|
|
calledBind = true;
|
|
Promise.prototype._propagateFrom = debug.propagateFromFunction();
|
|
Promise.prototype._boundValue = debug.boundValueFunction();
|
|
}
|
|
var maybePromise = tryConvertToPromise(thisArg);
|
|
var ret = new Promise(INTERNAL);
|
|
ret._propagateFrom(this, 1);
|
|
var target = this._target();
|
|
ret._setBoundTo(maybePromise);
|
|
if (maybePromise instanceof Promise) {
|
|
var context = {
|
|
promiseRejectionQueued: false,
|
|
promise: ret,
|
|
target: target,
|
|
bindingPromise: maybePromise
|
|
};
|
|
target._then(INTERNAL, targetRejected, undefined, ret, context);
|
|
maybePromise._then(
|
|
bindingResolved, bindingRejected, undefined, ret, context);
|
|
ret._setOnCancel(maybePromise);
|
|
} else {
|
|
ret._resolveCallback(target);
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
Promise.prototype._setBoundTo = function (obj) {
|
|
if (obj !== undefined) {
|
|
this._bitField = this._bitField | 2097152;
|
|
this._boundTo = obj;
|
|
} else {
|
|
this._bitField = this._bitField & (~2097152);
|
|
}
|
|
};
|
|
|
|
Promise.prototype._isBound = function () {
|
|
return (this._bitField & 2097152) === 2097152;
|
|
};
|
|
|
|
Promise.bind = function (thisArg, value) {
|
|
return Promise.resolve(value).bind(thisArg);
|
|
};
|
|
};
|
|
|
|
},{}],4:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
var old;
|
|
if (typeof Promise !== "undefined") old = Promise;
|
|
function noConflict() {
|
|
try { if (Promise === bluebird) Promise = old; }
|
|
catch (e) {}
|
|
return bluebird;
|
|
}
|
|
var bluebird = _dereq_("./promise")();
|
|
bluebird.noConflict = noConflict;
|
|
module.exports = bluebird;
|
|
|
|
},{"./promise":22}],5:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
var cr = Object.create;
|
|
if (cr) {
|
|
var callerCache = cr(null);
|
|
var getterCache = cr(null);
|
|
callerCache[" size"] = getterCache[" size"] = 0;
|
|
}
|
|
|
|
module.exports = function(Promise) {
|
|
var util = _dereq_("./util");
|
|
var canEvaluate = util.canEvaluate;
|
|
var isIdentifier = util.isIdentifier;
|
|
|
|
var getMethodCaller;
|
|
var getGetter;
|
|
if (!true) {
|
|
var makeMethodCaller = function (methodName) {
|
|
return new Function("ensureMethod", " \n\
|
|
return function(obj) { \n\
|
|
'use strict' \n\
|
|
var len = this.length; \n\
|
|
ensureMethod(obj, 'methodName'); \n\
|
|
switch(len) { \n\
|
|
case 1: return obj.methodName(this[0]); \n\
|
|
case 2: return obj.methodName(this[0], this[1]); \n\
|
|
case 3: return obj.methodName(this[0], this[1], this[2]); \n\
|
|
case 0: return obj.methodName(); \n\
|
|
default: \n\
|
|
return obj.methodName.apply(obj, this); \n\
|
|
} \n\
|
|
}; \n\
|
|
".replace(/methodName/g, methodName))(ensureMethod);
|
|
};
|
|
|
|
var makeGetter = function (propertyName) {
|
|
return new Function("obj", " \n\
|
|
'use strict'; \n\
|
|
return obj.propertyName; \n\
|
|
".replace("propertyName", propertyName));
|
|
};
|
|
|
|
var getCompiled = function(name, compiler, cache) {
|
|
var ret = cache[name];
|
|
if (typeof ret !== "function") {
|
|
if (!isIdentifier(name)) {
|
|
return null;
|
|
}
|
|
ret = compiler(name);
|
|
cache[name] = ret;
|
|
cache[" size"]++;
|
|
if (cache[" size"] > 512) {
|
|
var keys = Object.keys(cache);
|
|
for (var i = 0; i < 256; ++i) delete cache[keys[i]];
|
|
cache[" size"] = keys.length - 256;
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
getMethodCaller = function(name) {
|
|
return getCompiled(name, makeMethodCaller, callerCache);
|
|
};
|
|
|
|
getGetter = function(name) {
|
|
return getCompiled(name, makeGetter, getterCache);
|
|
};
|
|
}
|
|
|
|
function ensureMethod(obj, methodName) {
|
|
var fn;
|
|
if (obj != null) fn = obj[methodName];
|
|
if (typeof fn !== "function") {
|
|
var message = "Object " + util.classString(obj) + " has no method '" +
|
|
util.toString(methodName) + "'";
|
|
throw new Promise.TypeError(message);
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
function caller(obj) {
|
|
var methodName = this.pop();
|
|
var fn = ensureMethod(obj, methodName);
|
|
return fn.apply(obj, this);
|
|
}
|
|
Promise.prototype.call = function (methodName) {
|
|
var args = [].slice.call(arguments, 1);;
|
|
if (!true) {
|
|
if (canEvaluate) {
|
|
var maybeCaller = getMethodCaller(methodName);
|
|
if (maybeCaller !== null) {
|
|
return this._then(
|
|
maybeCaller, undefined, undefined, args, undefined);
|
|
}
|
|
}
|
|
}
|
|
args.push(methodName);
|
|
return this._then(caller, undefined, undefined, args, undefined);
|
|
};
|
|
|
|
function namedGetter(obj) {
|
|
return obj[this];
|
|
}
|
|
function indexedGetter(obj) {
|
|
var index = +this;
|
|
if (index < 0) index = Math.max(0, index + obj.length);
|
|
return obj[index];
|
|
}
|
|
Promise.prototype.get = function (propertyName) {
|
|
var isIndex = (typeof propertyName === "number");
|
|
var getter;
|
|
if (!isIndex) {
|
|
if (canEvaluate) {
|
|
var maybeGetter = getGetter(propertyName);
|
|
getter = maybeGetter !== null ? maybeGetter : namedGetter;
|
|
} else {
|
|
getter = namedGetter;
|
|
}
|
|
} else {
|
|
getter = indexedGetter;
|
|
}
|
|
return this._then(getter, undefined, undefined, propertyName, undefined);
|
|
};
|
|
};
|
|
|
|
},{"./util":36}],6:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise, PromiseArray, apiRejection, debug) {
|
|
var util = _dereq_("./util");
|
|
var tryCatch = util.tryCatch;
|
|
var errorObj = util.errorObj;
|
|
var async = Promise._async;
|
|
|
|
Promise.prototype["break"] = Promise.prototype.cancel = function() {
|
|
if (!debug.cancellation()) return this._warn("cancellation is disabled");
|
|
|
|
var promise = this;
|
|
var child = promise;
|
|
while (promise._isCancellable()) {
|
|
if (!promise._cancelBy(child)) {
|
|
if (child._isFollowing()) {
|
|
child._followee().cancel();
|
|
} else {
|
|
child._cancelBranched();
|
|
}
|
|
break;
|
|
}
|
|
|
|
var parent = promise._cancellationParent;
|
|
if (parent == null || !parent._isCancellable()) {
|
|
if (promise._isFollowing()) {
|
|
promise._followee().cancel();
|
|
} else {
|
|
promise._cancelBranched();
|
|
}
|
|
break;
|
|
} else {
|
|
if (promise._isFollowing()) promise._followee().cancel();
|
|
promise._setWillBeCancelled();
|
|
child = promise;
|
|
promise = parent;
|
|
}
|
|
}
|
|
};
|
|
|
|
Promise.prototype._branchHasCancelled = function() {
|
|
this._branchesRemainingToCancel--;
|
|
};
|
|
|
|
Promise.prototype._enoughBranchesHaveCancelled = function() {
|
|
return this._branchesRemainingToCancel === undefined ||
|
|
this._branchesRemainingToCancel <= 0;
|
|
};
|
|
|
|
Promise.prototype._cancelBy = function(canceller) {
|
|
if (canceller === this) {
|
|
this._branchesRemainingToCancel = 0;
|
|
this._invokeOnCancel();
|
|
return true;
|
|
} else {
|
|
this._branchHasCancelled();
|
|
if (this._enoughBranchesHaveCancelled()) {
|
|
this._invokeOnCancel();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
Promise.prototype._cancelBranched = function() {
|
|
if (this._enoughBranchesHaveCancelled()) {
|
|
this._cancel();
|
|
}
|
|
};
|
|
|
|
Promise.prototype._cancel = function() {
|
|
if (!this._isCancellable()) return;
|
|
this._setCancelled();
|
|
async.invoke(this._cancelPromises, this, undefined);
|
|
};
|
|
|
|
Promise.prototype._cancelPromises = function() {
|
|
if (this._length() > 0) this._settlePromises();
|
|
};
|
|
|
|
Promise.prototype._unsetOnCancel = function() {
|
|
this._onCancelField = undefined;
|
|
};
|
|
|
|
Promise.prototype._isCancellable = function() {
|
|
return this.isPending() && !this._isCancelled();
|
|
};
|
|
|
|
Promise.prototype.isCancellable = function() {
|
|
return this.isPending() && !this.isCancelled();
|
|
};
|
|
|
|
Promise.prototype._doInvokeOnCancel = function(onCancelCallback, internalOnly) {
|
|
if (util.isArray(onCancelCallback)) {
|
|
for (var i = 0; i < onCancelCallback.length; ++i) {
|
|
this._doInvokeOnCancel(onCancelCallback[i], internalOnly);
|
|
}
|
|
} else if (onCancelCallback !== undefined) {
|
|
if (typeof onCancelCallback === "function") {
|
|
if (!internalOnly) {
|
|
var e = tryCatch(onCancelCallback).call(this._boundValue());
|
|
if (e === errorObj) {
|
|
this._attachExtraTrace(e.e);
|
|
async.throwLater(e.e);
|
|
}
|
|
}
|
|
} else {
|
|
onCancelCallback._resultCancelled(this);
|
|
}
|
|
}
|
|
};
|
|
|
|
Promise.prototype._invokeOnCancel = function() {
|
|
var onCancelCallback = this._onCancel();
|
|
this._unsetOnCancel();
|
|
async.invoke(this._doInvokeOnCancel, this, onCancelCallback);
|
|
};
|
|
|
|
Promise.prototype._invokeInternalOnCancel = function() {
|
|
if (this._isCancellable()) {
|
|
this._doInvokeOnCancel(this._onCancel(), true);
|
|
this._unsetOnCancel();
|
|
}
|
|
};
|
|
|
|
Promise.prototype._resultCancelled = function() {
|
|
this.cancel();
|
|
};
|
|
|
|
};
|
|
|
|
},{"./util":36}],7:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(NEXT_FILTER) {
|
|
var util = _dereq_("./util");
|
|
var getKeys = _dereq_("./es5").keys;
|
|
var tryCatch = util.tryCatch;
|
|
var errorObj = util.errorObj;
|
|
|
|
function catchFilter(instances, cb, promise) {
|
|
return function(e) {
|
|
var boundTo = promise._boundValue();
|
|
predicateLoop: for (var i = 0; i < instances.length; ++i) {
|
|
var item = instances[i];
|
|
|
|
if (item === Error ||
|
|
(item != null && item.prototype instanceof Error)) {
|
|
if (e instanceof item) {
|
|
return tryCatch(cb).call(boundTo, e);
|
|
}
|
|
} else if (typeof item === "function") {
|
|
var matchesPredicate = tryCatch(item).call(boundTo, e);
|
|
if (matchesPredicate === errorObj) {
|
|
return matchesPredicate;
|
|
} else if (matchesPredicate) {
|
|
return tryCatch(cb).call(boundTo, e);
|
|
}
|
|
} else if (util.isObject(e)) {
|
|
var keys = getKeys(item);
|
|
for (var j = 0; j < keys.length; ++j) {
|
|
var key = keys[j];
|
|
if (item[key] != e[key]) {
|
|
continue predicateLoop;
|
|
}
|
|
}
|
|
return tryCatch(cb).call(boundTo, e);
|
|
}
|
|
}
|
|
return NEXT_FILTER;
|
|
};
|
|
}
|
|
|
|
return catchFilter;
|
|
};
|
|
|
|
},{"./es5":13,"./util":36}],8:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise) {
|
|
var longStackTraces = false;
|
|
var contextStack = [];
|
|
|
|
Promise.prototype._promiseCreated = function() {};
|
|
Promise.prototype._pushContext = function() {};
|
|
Promise.prototype._popContext = function() {return null;};
|
|
Promise._peekContext = Promise.prototype._peekContext = function() {};
|
|
|
|
function Context() {
|
|
this._trace = new Context.CapturedTrace(peekContext());
|
|
}
|
|
Context.prototype._pushContext = function () {
|
|
if (this._trace !== undefined) {
|
|
this._trace._promiseCreated = null;
|
|
contextStack.push(this._trace);
|
|
}
|
|
};
|
|
|
|
Context.prototype._popContext = function () {
|
|
if (this._trace !== undefined) {
|
|
var trace = contextStack.pop();
|
|
var ret = trace._promiseCreated;
|
|
trace._promiseCreated = null;
|
|
return ret;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
function createContext() {
|
|
if (longStackTraces) return new Context();
|
|
}
|
|
|
|
function peekContext() {
|
|
var lastIndex = contextStack.length - 1;
|
|
if (lastIndex >= 0) {
|
|
return contextStack[lastIndex];
|
|
}
|
|
return undefined;
|
|
}
|
|
Context.CapturedTrace = null;
|
|
Context.create = createContext;
|
|
Context.deactivateLongStackTraces = function() {};
|
|
Context.activateLongStackTraces = function() {
|
|
var Promise_pushContext = Promise.prototype._pushContext;
|
|
var Promise_popContext = Promise.prototype._popContext;
|
|
var Promise_PeekContext = Promise._peekContext;
|
|
var Promise_peekContext = Promise.prototype._peekContext;
|
|
var Promise_promiseCreated = Promise.prototype._promiseCreated;
|
|
Context.deactivateLongStackTraces = function() {
|
|
Promise.prototype._pushContext = Promise_pushContext;
|
|
Promise.prototype._popContext = Promise_popContext;
|
|
Promise._peekContext = Promise_PeekContext;
|
|
Promise.prototype._peekContext = Promise_peekContext;
|
|
Promise.prototype._promiseCreated = Promise_promiseCreated;
|
|
longStackTraces = false;
|
|
};
|
|
longStackTraces = true;
|
|
Promise.prototype._pushContext = Context.prototype._pushContext;
|
|
Promise.prototype._popContext = Context.prototype._popContext;
|
|
Promise._peekContext = Promise.prototype._peekContext = peekContext;
|
|
Promise.prototype._promiseCreated = function() {
|
|
var ctx = this._peekContext();
|
|
if (ctx && ctx._promiseCreated == null) ctx._promiseCreated = this;
|
|
};
|
|
};
|
|
return Context;
|
|
};
|
|
|
|
},{}],9:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise, Context) {
|
|
var getDomain = Promise._getDomain;
|
|
var async = Promise._async;
|
|
var Warning = _dereq_("./errors").Warning;
|
|
var util = _dereq_("./util");
|
|
var canAttachTrace = util.canAttachTrace;
|
|
var unhandledRejectionHandled;
|
|
var possiblyUnhandledRejection;
|
|
var bluebirdFramePattern =
|
|
/[\\\/]bluebird[\\\/]js[\\\/](release|debug|instrumented)/;
|
|
var nodeFramePattern = /\((?:timers\.js):\d+:\d+\)/;
|
|
var parseLinePattern = /[\/<\(](.+?):(\d+):(\d+)\)?\s*$/;
|
|
var stackFramePattern = null;
|
|
var formatStack = null;
|
|
var indentStackFrames = false;
|
|
var printWarning;
|
|
var debugging = !!(util.env("BLUEBIRD_DEBUG") != 0 &&
|
|
(true ||
|
|
util.env("BLUEBIRD_DEBUG") ||
|
|
util.env("NODE_ENV") === "development"));
|
|
|
|
var warnings = !!(util.env("BLUEBIRD_WARNINGS") != 0 &&
|
|
(debugging || util.env("BLUEBIRD_WARNINGS")));
|
|
|
|
var longStackTraces = !!(util.env("BLUEBIRD_LONG_STACK_TRACES") != 0 &&
|
|
(debugging || util.env("BLUEBIRD_LONG_STACK_TRACES")));
|
|
|
|
var wForgottenReturn = util.env("BLUEBIRD_W_FORGOTTEN_RETURN") != 0 &&
|
|
(warnings || !!util.env("BLUEBIRD_W_FORGOTTEN_RETURN"));
|
|
|
|
Promise.prototype.suppressUnhandledRejections = function() {
|
|
var target = this._target();
|
|
target._bitField = ((target._bitField & (~1048576)) |
|
|
524288);
|
|
};
|
|
|
|
Promise.prototype._ensurePossibleRejectionHandled = function () {
|
|
if ((this._bitField & 524288) !== 0) return;
|
|
this._setRejectionIsUnhandled();
|
|
async.invokeLater(this._notifyUnhandledRejection, this, undefined);
|
|
};
|
|
|
|
Promise.prototype._notifyUnhandledRejectionIsHandled = function () {
|
|
fireRejectionEvent("rejectionHandled",
|
|
unhandledRejectionHandled, undefined, this);
|
|
};
|
|
|
|
Promise.prototype._setReturnedNonUndefined = function() {
|
|
this._bitField = this._bitField | 268435456;
|
|
};
|
|
|
|
Promise.prototype._returnedNonUndefined = function() {
|
|
return (this._bitField & 268435456) !== 0;
|
|
};
|
|
|
|
Promise.prototype._notifyUnhandledRejection = function () {
|
|
if (this._isRejectionUnhandled()) {
|
|
var reason = this._settledValue();
|
|
this._setUnhandledRejectionIsNotified();
|
|
fireRejectionEvent("unhandledRejection",
|
|
possiblyUnhandledRejection, reason, this);
|
|
}
|
|
};
|
|
|
|
Promise.prototype._setUnhandledRejectionIsNotified = function () {
|
|
this._bitField = this._bitField | 262144;
|
|
};
|
|
|
|
Promise.prototype._unsetUnhandledRejectionIsNotified = function () {
|
|
this._bitField = this._bitField & (~262144);
|
|
};
|
|
|
|
Promise.prototype._isUnhandledRejectionNotified = function () {
|
|
return (this._bitField & 262144) > 0;
|
|
};
|
|
|
|
Promise.prototype._setRejectionIsUnhandled = function () {
|
|
this._bitField = this._bitField | 1048576;
|
|
};
|
|
|
|
Promise.prototype._unsetRejectionIsUnhandled = function () {
|
|
this._bitField = this._bitField & (~1048576);
|
|
if (this._isUnhandledRejectionNotified()) {
|
|
this._unsetUnhandledRejectionIsNotified();
|
|
this._notifyUnhandledRejectionIsHandled();
|
|
}
|
|
};
|
|
|
|
Promise.prototype._isRejectionUnhandled = function () {
|
|
return (this._bitField & 1048576) > 0;
|
|
};
|
|
|
|
Promise.prototype._warn = function(message, shouldUseOwnTrace, promise) {
|
|
return warn(message, shouldUseOwnTrace, promise || this);
|
|
};
|
|
|
|
Promise.onPossiblyUnhandledRejection = function (fn) {
|
|
var domain = getDomain();
|
|
possiblyUnhandledRejection =
|
|
typeof fn === "function" ? (domain === null ?
|
|
fn : util.domainBind(domain, fn))
|
|
: undefined;
|
|
};
|
|
|
|
Promise.onUnhandledRejectionHandled = function (fn) {
|
|
var domain = getDomain();
|
|
unhandledRejectionHandled =
|
|
typeof fn === "function" ? (domain === null ?
|
|
fn : util.domainBind(domain, fn))
|
|
: undefined;
|
|
};
|
|
|
|
var disableLongStackTraces = function() {};
|
|
Promise.longStackTraces = function () {
|
|
if (async.haveItemsQueued() && !config.longStackTraces) {
|
|
throw new Error("cannot enable long stack traces after promises have been created\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
}
|
|
if (!config.longStackTraces && longStackTracesIsSupported()) {
|
|
var Promise_captureStackTrace = Promise.prototype._captureStackTrace;
|
|
var Promise_attachExtraTrace = Promise.prototype._attachExtraTrace;
|
|
config.longStackTraces = true;
|
|
disableLongStackTraces = function() {
|
|
if (async.haveItemsQueued() && !config.longStackTraces) {
|
|
throw new Error("cannot enable long stack traces after promises have been created\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
}
|
|
Promise.prototype._captureStackTrace = Promise_captureStackTrace;
|
|
Promise.prototype._attachExtraTrace = Promise_attachExtraTrace;
|
|
Context.deactivateLongStackTraces();
|
|
async.enableTrampoline();
|
|
config.longStackTraces = false;
|
|
};
|
|
Promise.prototype._captureStackTrace = longStackTracesCaptureStackTrace;
|
|
Promise.prototype._attachExtraTrace = longStackTracesAttachExtraTrace;
|
|
Context.activateLongStackTraces();
|
|
async.disableTrampolineIfNecessary();
|
|
}
|
|
};
|
|
|
|
Promise.hasLongStackTraces = function () {
|
|
return config.longStackTraces && longStackTracesIsSupported();
|
|
};
|
|
|
|
var fireDomEvent = (function() {
|
|
try {
|
|
if (typeof CustomEvent === "function") {
|
|
var event = new CustomEvent("CustomEvent");
|
|
util.global.dispatchEvent(event);
|
|
return function(name, event) {
|
|
var domEvent = new CustomEvent(name.toLowerCase(), {
|
|
detail: event,
|
|
cancelable: true
|
|
});
|
|
return !util.global.dispatchEvent(domEvent);
|
|
};
|
|
} else if (typeof Event === "function") {
|
|
var event = new Event("CustomEvent");
|
|
util.global.dispatchEvent(event);
|
|
return function(name, event) {
|
|
var domEvent = new Event(name.toLowerCase(), {
|
|
cancelable: true
|
|
});
|
|
domEvent.detail = event;
|
|
return !util.global.dispatchEvent(domEvent);
|
|
};
|
|
} else {
|
|
var event = document.createEvent("CustomEvent");
|
|
event.initCustomEvent("testingtheevent", false, true, {});
|
|
util.global.dispatchEvent(event);
|
|
return function(name, event) {
|
|
var domEvent = document.createEvent("CustomEvent");
|
|
domEvent.initCustomEvent(name.toLowerCase(), false, true,
|
|
event);
|
|
return !util.global.dispatchEvent(domEvent);
|
|
};
|
|
}
|
|
} catch (e) {}
|
|
return function() {
|
|
return false;
|
|
};
|
|
})();
|
|
|
|
var fireGlobalEvent = (function() {
|
|
if (util.isNode) {
|
|
return function() {
|
|
return process.emit.apply(process, arguments);
|
|
};
|
|
} else {
|
|
if (!util.global) {
|
|
return function() {
|
|
return false;
|
|
};
|
|
}
|
|
return function(name) {
|
|
var methodName = "on" + name.toLowerCase();
|
|
var method = util.global[methodName];
|
|
if (!method) return false;
|
|
method.apply(util.global, [].slice.call(arguments, 1));
|
|
return true;
|
|
};
|
|
}
|
|
})();
|
|
|
|
function generatePromiseLifecycleEventObject(name, promise) {
|
|
return {promise: promise};
|
|
}
|
|
|
|
var eventToObjectGenerator = {
|
|
promiseCreated: generatePromiseLifecycleEventObject,
|
|
promiseFulfilled: generatePromiseLifecycleEventObject,
|
|
promiseRejected: generatePromiseLifecycleEventObject,
|
|
promiseResolved: generatePromiseLifecycleEventObject,
|
|
promiseCancelled: generatePromiseLifecycleEventObject,
|
|
promiseChained: function(name, promise, child) {
|
|
return {promise: promise, child: child};
|
|
},
|
|
warning: function(name, warning) {
|
|
return {warning: warning};
|
|
},
|
|
unhandledRejection: function (name, reason, promise) {
|
|
return {reason: reason, promise: promise};
|
|
},
|
|
rejectionHandled: generatePromiseLifecycleEventObject
|
|
};
|
|
|
|
var activeFireEvent = function (name) {
|
|
var globalEventFired = false;
|
|
try {
|
|
globalEventFired = fireGlobalEvent.apply(null, arguments);
|
|
} catch (e) {
|
|
async.throwLater(e);
|
|
globalEventFired = true;
|
|
}
|
|
|
|
var domEventFired = false;
|
|
try {
|
|
domEventFired = fireDomEvent(name,
|
|
eventToObjectGenerator[name].apply(null, arguments));
|
|
} catch (e) {
|
|
async.throwLater(e);
|
|
domEventFired = true;
|
|
}
|
|
|
|
return domEventFired || globalEventFired;
|
|
};
|
|
|
|
Promise.config = function(opts) {
|
|
opts = Object(opts);
|
|
if ("longStackTraces" in opts) {
|
|
if (opts.longStackTraces) {
|
|
Promise.longStackTraces();
|
|
} else if (!opts.longStackTraces && Promise.hasLongStackTraces()) {
|
|
disableLongStackTraces();
|
|
}
|
|
}
|
|
if ("warnings" in opts) {
|
|
var warningsOption = opts.warnings;
|
|
config.warnings = !!warningsOption;
|
|
wForgottenReturn = config.warnings;
|
|
|
|
if (util.isObject(warningsOption)) {
|
|
if ("wForgottenReturn" in warningsOption) {
|
|
wForgottenReturn = !!warningsOption.wForgottenReturn;
|
|
}
|
|
}
|
|
}
|
|
if ("cancellation" in opts && opts.cancellation && !config.cancellation) {
|
|
if (async.haveItemsQueued()) {
|
|
throw new Error(
|
|
"cannot enable cancellation after promises are in use");
|
|
}
|
|
Promise.prototype._clearCancellationData =
|
|
cancellationClearCancellationData;
|
|
Promise.prototype._propagateFrom = cancellationPropagateFrom;
|
|
Promise.prototype._onCancel = cancellationOnCancel;
|
|
Promise.prototype._setOnCancel = cancellationSetOnCancel;
|
|
Promise.prototype._attachCancellationCallback =
|
|
cancellationAttachCancellationCallback;
|
|
Promise.prototype._execute = cancellationExecute;
|
|
propagateFromFunction = cancellationPropagateFrom;
|
|
config.cancellation = true;
|
|
}
|
|
if ("monitoring" in opts) {
|
|
if (opts.monitoring && !config.monitoring) {
|
|
config.monitoring = true;
|
|
Promise.prototype._fireEvent = activeFireEvent;
|
|
} else if (!opts.monitoring && config.monitoring) {
|
|
config.monitoring = false;
|
|
Promise.prototype._fireEvent = defaultFireEvent;
|
|
}
|
|
}
|
|
return Promise;
|
|
};
|
|
|
|
function defaultFireEvent() { return false; }
|
|
|
|
Promise.prototype._fireEvent = defaultFireEvent;
|
|
Promise.prototype._execute = function(executor, resolve, reject) {
|
|
try {
|
|
executor(resolve, reject);
|
|
} catch (e) {
|
|
return e;
|
|
}
|
|
};
|
|
Promise.prototype._onCancel = function () {};
|
|
Promise.prototype._setOnCancel = function (handler) { ; };
|
|
Promise.prototype._attachCancellationCallback = function(onCancel) {
|
|
;
|
|
};
|
|
Promise.prototype._captureStackTrace = function () {};
|
|
Promise.prototype._attachExtraTrace = function () {};
|
|
Promise.prototype._clearCancellationData = function() {};
|
|
Promise.prototype._propagateFrom = function (parent, flags) {
|
|
;
|
|
;
|
|
};
|
|
|
|
function cancellationExecute(executor, resolve, reject) {
|
|
var promise = this;
|
|
try {
|
|
executor(resolve, reject, function(onCancel) {
|
|
if (typeof onCancel !== "function") {
|
|
throw new TypeError("onCancel must be a function, got: " +
|
|
util.toString(onCancel));
|
|
}
|
|
promise._attachCancellationCallback(onCancel);
|
|
});
|
|
} catch (e) {
|
|
return e;
|
|
}
|
|
}
|
|
|
|
function cancellationAttachCancellationCallback(onCancel) {
|
|
if (!this._isCancellable()) return this;
|
|
|
|
var previousOnCancel = this._onCancel();
|
|
if (previousOnCancel !== undefined) {
|
|
if (util.isArray(previousOnCancel)) {
|
|
previousOnCancel.push(onCancel);
|
|
} else {
|
|
this._setOnCancel([previousOnCancel, onCancel]);
|
|
}
|
|
} else {
|
|
this._setOnCancel(onCancel);
|
|
}
|
|
}
|
|
|
|
function cancellationOnCancel() {
|
|
return this._onCancelField;
|
|
}
|
|
|
|
function cancellationSetOnCancel(onCancel) {
|
|
this._onCancelField = onCancel;
|
|
}
|
|
|
|
function cancellationClearCancellationData() {
|
|
this._cancellationParent = undefined;
|
|
this._onCancelField = undefined;
|
|
}
|
|
|
|
function cancellationPropagateFrom(parent, flags) {
|
|
if ((flags & 1) !== 0) {
|
|
this._cancellationParent = parent;
|
|
var branchesRemainingToCancel = parent._branchesRemainingToCancel;
|
|
if (branchesRemainingToCancel === undefined) {
|
|
branchesRemainingToCancel = 0;
|
|
}
|
|
parent._branchesRemainingToCancel = branchesRemainingToCancel + 1;
|
|
}
|
|
if ((flags & 2) !== 0 && parent._isBound()) {
|
|
this._setBoundTo(parent._boundTo);
|
|
}
|
|
}
|
|
|
|
function bindingPropagateFrom(parent, flags) {
|
|
if ((flags & 2) !== 0 && parent._isBound()) {
|
|
this._setBoundTo(parent._boundTo);
|
|
}
|
|
}
|
|
var propagateFromFunction = bindingPropagateFrom;
|
|
|
|
function boundValueFunction() {
|
|
var ret = this._boundTo;
|
|
if (ret !== undefined) {
|
|
if (ret instanceof Promise) {
|
|
if (ret.isFulfilled()) {
|
|
return ret.value();
|
|
} else {
|
|
return undefined;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
function longStackTracesCaptureStackTrace() {
|
|
this._trace = new CapturedTrace(this._peekContext());
|
|
}
|
|
|
|
function longStackTracesAttachExtraTrace(error, ignoreSelf) {
|
|
if (canAttachTrace(error)) {
|
|
var trace = this._trace;
|
|
if (trace !== undefined) {
|
|
if (ignoreSelf) trace = trace._parent;
|
|
}
|
|
if (trace !== undefined) {
|
|
trace.attachExtraTrace(error);
|
|
} else if (!error.__stackCleaned__) {
|
|
var parsed = parseStackAndMessage(error);
|
|
util.notEnumerableProp(error, "stack",
|
|
parsed.message + "\n" + parsed.stack.join("\n"));
|
|
util.notEnumerableProp(error, "__stackCleaned__", true);
|
|
}
|
|
}
|
|
}
|
|
|
|
function checkForgottenReturns(returnValue, promiseCreated, name, promise,
|
|
parent) {
|
|
if (returnValue === undefined && promiseCreated !== null &&
|
|
wForgottenReturn) {
|
|
if (parent !== undefined && parent._returnedNonUndefined()) return;
|
|
if ((promise._bitField & 65535) === 0) return;
|
|
|
|
if (name) name = name + " ";
|
|
var handlerLine = "";
|
|
var creatorLine = "";
|
|
if (promiseCreated._trace) {
|
|
var traceLines = promiseCreated._trace.stack.split("\n");
|
|
var stack = cleanStack(traceLines);
|
|
for (var i = stack.length - 1; i >= 0; --i) {
|
|
var line = stack[i];
|
|
if (!nodeFramePattern.test(line)) {
|
|
var lineMatches = line.match(parseLinePattern);
|
|
if (lineMatches) {
|
|
handlerLine = "at " + lineMatches[1] +
|
|
":" + lineMatches[2] + ":" + lineMatches[3] + " ";
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (stack.length > 0) {
|
|
var firstUserLine = stack[0];
|
|
for (var i = 0; i < traceLines.length; ++i) {
|
|
|
|
if (traceLines[i] === firstUserLine) {
|
|
if (i > 0) {
|
|
creatorLine = "\n" + traceLines[i - 1];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
var msg = "a promise was created in a " + name +
|
|
"handler " + handlerLine + "but was not returned from it, " +
|
|
"see http://goo.gl/rRqMUw" +
|
|
creatorLine;
|
|
promise._warn(msg, true, promiseCreated);
|
|
}
|
|
}
|
|
|
|
function deprecated(name, replacement) {
|
|
var message = name +
|
|
" is deprecated and will be removed in a future version.";
|
|
if (replacement) message += " Use " + replacement + " instead.";
|
|
return warn(message);
|
|
}
|
|
|
|
function warn(message, shouldUseOwnTrace, promise) {
|
|
if (!config.warnings) return;
|
|
var warning = new Warning(message);
|
|
var ctx;
|
|
if (shouldUseOwnTrace) {
|
|
promise._attachExtraTrace(warning);
|
|
} else if (config.longStackTraces && (ctx = Promise._peekContext())) {
|
|
ctx.attachExtraTrace(warning);
|
|
} else {
|
|
var parsed = parseStackAndMessage(warning);
|
|
warning.stack = parsed.message + "\n" + parsed.stack.join("\n");
|
|
}
|
|
|
|
if (!activeFireEvent("warning", warning)) {
|
|
formatAndLogError(warning, "", true);
|
|
}
|
|
}
|
|
|
|
function reconstructStack(message, stacks) {
|
|
for (var i = 0; i < stacks.length - 1; ++i) {
|
|
stacks[i].push("From previous event:");
|
|
stacks[i] = stacks[i].join("\n");
|
|
}
|
|
if (i < stacks.length) {
|
|
stacks[i] = stacks[i].join("\n");
|
|
}
|
|
return message + "\n" + stacks.join("\n");
|
|
}
|
|
|
|
function removeDuplicateOrEmptyJumps(stacks) {
|
|
for (var i = 0; i < stacks.length; ++i) {
|
|
if (stacks[i].length === 0 ||
|
|
((i + 1 < stacks.length) && stacks[i][0] === stacks[i+1][0])) {
|
|
stacks.splice(i, 1);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
function removeCommonRoots(stacks) {
|
|
var current = stacks[0];
|
|
for (var i = 1; i < stacks.length; ++i) {
|
|
var prev = stacks[i];
|
|
var currentLastIndex = current.length - 1;
|
|
var currentLastLine = current[currentLastIndex];
|
|
var commonRootMeetPoint = -1;
|
|
|
|
for (var j = prev.length - 1; j >= 0; --j) {
|
|
if (prev[j] === currentLastLine) {
|
|
commonRootMeetPoint = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (var j = commonRootMeetPoint; j >= 0; --j) {
|
|
var line = prev[j];
|
|
if (current[currentLastIndex] === line) {
|
|
current.pop();
|
|
currentLastIndex--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
current = prev;
|
|
}
|
|
}
|
|
|
|
function cleanStack(stack) {
|
|
var ret = [];
|
|
for (var i = 0; i < stack.length; ++i) {
|
|
var line = stack[i];
|
|
var isTraceLine = " (No stack trace)" === line ||
|
|
stackFramePattern.test(line);
|
|
var isInternalFrame = isTraceLine && shouldIgnore(line);
|
|
if (isTraceLine && !isInternalFrame) {
|
|
if (indentStackFrames && line.charAt(0) !== " ") {
|
|
line = " " + line;
|
|
}
|
|
ret.push(line);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
function stackFramesAsArray(error) {
|
|
var stack = error.stack.replace(/\s+$/g, "").split("\n");
|
|
for (var i = 0; i < stack.length; ++i) {
|
|
var line = stack[i];
|
|
if (" (No stack trace)" === line || stackFramePattern.test(line)) {
|
|
break;
|
|
}
|
|
}
|
|
if (i > 0 && error.name != "SyntaxError") {
|
|
stack = stack.slice(i);
|
|
}
|
|
return stack;
|
|
}
|
|
|
|
function parseStackAndMessage(error) {
|
|
var stack = error.stack;
|
|
var message = error.toString();
|
|
stack = typeof stack === "string" && stack.length > 0
|
|
? stackFramesAsArray(error) : [" (No stack trace)"];
|
|
return {
|
|
message: message,
|
|
stack: error.name == "SyntaxError" ? stack : cleanStack(stack)
|
|
};
|
|
}
|
|
|
|
function formatAndLogError(error, title, isSoft) {
|
|
if (typeof console !== "undefined") {
|
|
var message;
|
|
if (util.isObject(error)) {
|
|
var stack = error.stack;
|
|
message = title + formatStack(stack, error);
|
|
} else {
|
|
message = title + String(error);
|
|
}
|
|
if (typeof printWarning === "function") {
|
|
printWarning(message, isSoft);
|
|
} else if (typeof console.log === "function" ||
|
|
typeof console.log === "object") {
|
|
console.log(message);
|
|
}
|
|
}
|
|
}
|
|
|
|
function fireRejectionEvent(name, localHandler, reason, promise) {
|
|
var localEventFired = false;
|
|
try {
|
|
if (typeof localHandler === "function") {
|
|
localEventFired = true;
|
|
if (name === "rejectionHandled") {
|
|
localHandler(promise);
|
|
} else {
|
|
localHandler(reason, promise);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
async.throwLater(e);
|
|
}
|
|
|
|
if (name === "unhandledRejection") {
|
|
if (!activeFireEvent(name, reason, promise) && !localEventFired) {
|
|
formatAndLogError(reason, "Unhandled rejection ");
|
|
}
|
|
} else {
|
|
activeFireEvent(name, promise);
|
|
}
|
|
}
|
|
|
|
function formatNonError(obj) {
|
|
var str;
|
|
if (typeof obj === "function") {
|
|
str = "[function " +
|
|
(obj.name || "anonymous") +
|
|
"]";
|
|
} else {
|
|
str = obj && typeof obj.toString === "function"
|
|
? obj.toString() : util.toString(obj);
|
|
var ruselessToString = /\[object [a-zA-Z0-9$_]+\]/;
|
|
if (ruselessToString.test(str)) {
|
|
try {
|
|
var newStr = JSON.stringify(obj);
|
|
str = newStr;
|
|
}
|
|
catch(e) {
|
|
|
|
}
|
|
}
|
|
if (str.length === 0) {
|
|
str = "(empty array)";
|
|
}
|
|
}
|
|
return ("(<" + snip(str) + ">, no stack trace)");
|
|
}
|
|
|
|
function snip(str) {
|
|
var maxChars = 41;
|
|
if (str.length < maxChars) {
|
|
return str;
|
|
}
|
|
return str.substr(0, maxChars - 3) + "...";
|
|
}
|
|
|
|
function longStackTracesIsSupported() {
|
|
return typeof captureStackTrace === "function";
|
|
}
|
|
|
|
var shouldIgnore = function() { return false; };
|
|
var parseLineInfoRegex = /[\/<\(]([^:\/]+):(\d+):(?:\d+)\)?\s*$/;
|
|
function parseLineInfo(line) {
|
|
var matches = line.match(parseLineInfoRegex);
|
|
if (matches) {
|
|
return {
|
|
fileName: matches[1],
|
|
line: parseInt(matches[2], 10)
|
|
};
|
|
}
|
|
}
|
|
|
|
function setBounds(firstLineError, lastLineError) {
|
|
if (!longStackTracesIsSupported()) return;
|
|
var firstStackLines = firstLineError.stack.split("\n");
|
|
var lastStackLines = lastLineError.stack.split("\n");
|
|
var firstIndex = -1;
|
|
var lastIndex = -1;
|
|
var firstFileName;
|
|
var lastFileName;
|
|
for (var i = 0; i < firstStackLines.length; ++i) {
|
|
var result = parseLineInfo(firstStackLines[i]);
|
|
if (result) {
|
|
firstFileName = result.fileName;
|
|
firstIndex = result.line;
|
|
break;
|
|
}
|
|
}
|
|
for (var i = 0; i < lastStackLines.length; ++i) {
|
|
var result = parseLineInfo(lastStackLines[i]);
|
|
if (result) {
|
|
lastFileName = result.fileName;
|
|
lastIndex = result.line;
|
|
break;
|
|
}
|
|
}
|
|
if (firstIndex < 0 || lastIndex < 0 || !firstFileName || !lastFileName ||
|
|
firstFileName !== lastFileName || firstIndex >= lastIndex) {
|
|
return;
|
|
}
|
|
|
|
shouldIgnore = function(line) {
|
|
if (bluebirdFramePattern.test(line)) return true;
|
|
var info = parseLineInfo(line);
|
|
if (info) {
|
|
if (info.fileName === firstFileName &&
|
|
(firstIndex <= info.line && info.line <= lastIndex)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
}
|
|
|
|
function CapturedTrace(parent) {
|
|
this._parent = parent;
|
|
this._promisesCreated = 0;
|
|
var length = this._length = 1 + (parent === undefined ? 0 : parent._length);
|
|
captureStackTrace(this, CapturedTrace);
|
|
if (length > 32) this.uncycle();
|
|
}
|
|
util.inherits(CapturedTrace, Error);
|
|
Context.CapturedTrace = CapturedTrace;
|
|
|
|
CapturedTrace.prototype.uncycle = function() {
|
|
var length = this._length;
|
|
if (length < 2) return;
|
|
var nodes = [];
|
|
var stackToIndex = {};
|
|
|
|
for (var i = 0, node = this; node !== undefined; ++i) {
|
|
nodes.push(node);
|
|
node = node._parent;
|
|
}
|
|
length = this._length = i;
|
|
for (var i = length - 1; i >= 0; --i) {
|
|
var stack = nodes[i].stack;
|
|
if (stackToIndex[stack] === undefined) {
|
|
stackToIndex[stack] = i;
|
|
}
|
|
}
|
|
for (var i = 0; i < length; ++i) {
|
|
var currentStack = nodes[i].stack;
|
|
var index = stackToIndex[currentStack];
|
|
if (index !== undefined && index !== i) {
|
|
if (index > 0) {
|
|
nodes[index - 1]._parent = undefined;
|
|
nodes[index - 1]._length = 1;
|
|
}
|
|
nodes[i]._parent = undefined;
|
|
nodes[i]._length = 1;
|
|
var cycleEdgeNode = i > 0 ? nodes[i - 1] : this;
|
|
|
|
if (index < length - 1) {
|
|
cycleEdgeNode._parent = nodes[index + 1];
|
|
cycleEdgeNode._parent.uncycle();
|
|
cycleEdgeNode._length =
|
|
cycleEdgeNode._parent._length + 1;
|
|
} else {
|
|
cycleEdgeNode._parent = undefined;
|
|
cycleEdgeNode._length = 1;
|
|
}
|
|
var currentChildLength = cycleEdgeNode._length + 1;
|
|
for (var j = i - 2; j >= 0; --j) {
|
|
nodes[j]._length = currentChildLength;
|
|
currentChildLength++;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
CapturedTrace.prototype.attachExtraTrace = function(error) {
|
|
if (error.__stackCleaned__) return;
|
|
this.uncycle();
|
|
var parsed = parseStackAndMessage(error);
|
|
var message = parsed.message;
|
|
var stacks = [parsed.stack];
|
|
|
|
var trace = this;
|
|
while (trace !== undefined) {
|
|
stacks.push(cleanStack(trace.stack.split("\n")));
|
|
trace = trace._parent;
|
|
}
|
|
removeCommonRoots(stacks);
|
|
removeDuplicateOrEmptyJumps(stacks);
|
|
util.notEnumerableProp(error, "stack", reconstructStack(message, stacks));
|
|
util.notEnumerableProp(error, "__stackCleaned__", true);
|
|
};
|
|
|
|
var captureStackTrace = (function stackDetection() {
|
|
var v8stackFramePattern = /^\s*at\s*/;
|
|
var v8stackFormatter = function(stack, error) {
|
|
if (typeof stack === "string") return stack;
|
|
|
|
if (error.name !== undefined &&
|
|
error.message !== undefined) {
|
|
return error.toString();
|
|
}
|
|
return formatNonError(error);
|
|
};
|
|
|
|
if (typeof Error.stackTraceLimit === "number" &&
|
|
typeof Error.captureStackTrace === "function") {
|
|
Error.stackTraceLimit += 6;
|
|
stackFramePattern = v8stackFramePattern;
|
|
formatStack = v8stackFormatter;
|
|
var captureStackTrace = Error.captureStackTrace;
|
|
|
|
shouldIgnore = function(line) {
|
|
return bluebirdFramePattern.test(line);
|
|
};
|
|
return function(receiver, ignoreUntil) {
|
|
Error.stackTraceLimit += 6;
|
|
captureStackTrace(receiver, ignoreUntil);
|
|
Error.stackTraceLimit -= 6;
|
|
};
|
|
}
|
|
var err = new Error();
|
|
|
|
if (typeof err.stack === "string" &&
|
|
err.stack.split("\n")[0].indexOf("stackDetection@") >= 0) {
|
|
stackFramePattern = /@/;
|
|
formatStack = v8stackFormatter;
|
|
indentStackFrames = true;
|
|
return function captureStackTrace(o) {
|
|
o.stack = new Error().stack;
|
|
};
|
|
}
|
|
|
|
var hasStackAfterThrow;
|
|
try { throw new Error(); }
|
|
catch(e) {
|
|
hasStackAfterThrow = ("stack" in e);
|
|
}
|
|
if (!("stack" in err) && hasStackAfterThrow &&
|
|
typeof Error.stackTraceLimit === "number") {
|
|
stackFramePattern = v8stackFramePattern;
|
|
formatStack = v8stackFormatter;
|
|
return function captureStackTrace(o) {
|
|
Error.stackTraceLimit += 6;
|
|
try { throw new Error(); }
|
|
catch(e) { o.stack = e.stack; }
|
|
Error.stackTraceLimit -= 6;
|
|
};
|
|
}
|
|
|
|
formatStack = function(stack, error) {
|
|
if (typeof stack === "string") return stack;
|
|
|
|
if ((typeof error === "object" ||
|
|
typeof error === "function") &&
|
|
error.name !== undefined &&
|
|
error.message !== undefined) {
|
|
return error.toString();
|
|
}
|
|
return formatNonError(error);
|
|
};
|
|
|
|
return null;
|
|
|
|
})([]);
|
|
|
|
if (typeof console !== "undefined" && typeof console.warn !== "undefined") {
|
|
printWarning = function (message) {
|
|
console.warn(message);
|
|
};
|
|
if (util.isNode && process.stderr.isTTY) {
|
|
printWarning = function(message, isSoft) {
|
|
var color = isSoft ? "\u001b[33m" : "\u001b[31m";
|
|
console.warn(color + message + "\u001b[0m\n");
|
|
};
|
|
} else if (!util.isNode && typeof (new Error().stack) === "string") {
|
|
printWarning = function(message, isSoft) {
|
|
console.warn("%c" + message,
|
|
isSoft ? "color: darkorange" : "color: red");
|
|
};
|
|
}
|
|
}
|
|
|
|
var config = {
|
|
warnings: warnings,
|
|
longStackTraces: false,
|
|
cancellation: false,
|
|
monitoring: false
|
|
};
|
|
|
|
if (longStackTraces) Promise.longStackTraces();
|
|
|
|
return {
|
|
longStackTraces: function() {
|
|
return config.longStackTraces;
|
|
},
|
|
warnings: function() {
|
|
return config.warnings;
|
|
},
|
|
cancellation: function() {
|
|
return config.cancellation;
|
|
},
|
|
monitoring: function() {
|
|
return config.monitoring;
|
|
},
|
|
propagateFromFunction: function() {
|
|
return propagateFromFunction;
|
|
},
|
|
boundValueFunction: function() {
|
|
return boundValueFunction;
|
|
},
|
|
checkForgottenReturns: checkForgottenReturns,
|
|
setBounds: setBounds,
|
|
warn: warn,
|
|
deprecated: deprecated,
|
|
CapturedTrace: CapturedTrace,
|
|
fireDomEvent: fireDomEvent,
|
|
fireGlobalEvent: fireGlobalEvent
|
|
};
|
|
};
|
|
|
|
},{"./errors":12,"./util":36}],10:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise) {
|
|
function returner() {
|
|
return this.value;
|
|
}
|
|
function thrower() {
|
|
throw this.reason;
|
|
}
|
|
|
|
Promise.prototype["return"] =
|
|
Promise.prototype.thenReturn = function (value) {
|
|
if (value instanceof Promise) value.suppressUnhandledRejections();
|
|
return this._then(
|
|
returner, undefined, undefined, {value: value}, undefined);
|
|
};
|
|
|
|
Promise.prototype["throw"] =
|
|
Promise.prototype.thenThrow = function (reason) {
|
|
return this._then(
|
|
thrower, undefined, undefined, {reason: reason}, undefined);
|
|
};
|
|
|
|
Promise.prototype.catchThrow = function (reason) {
|
|
if (arguments.length <= 1) {
|
|
return this._then(
|
|
undefined, thrower, undefined, {reason: reason}, undefined);
|
|
} else {
|
|
var _reason = arguments[1];
|
|
var handler = function() {throw _reason;};
|
|
return this.caught(reason, handler);
|
|
}
|
|
};
|
|
|
|
Promise.prototype.catchReturn = function (value) {
|
|
if (arguments.length <= 1) {
|
|
if (value instanceof Promise) value.suppressUnhandledRejections();
|
|
return this._then(
|
|
undefined, returner, undefined, {value: value}, undefined);
|
|
} else {
|
|
var _value = arguments[1];
|
|
if (_value instanceof Promise) _value.suppressUnhandledRejections();
|
|
var handler = function() {return _value;};
|
|
return this.caught(value, handler);
|
|
}
|
|
};
|
|
};
|
|
|
|
},{}],11:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise, INTERNAL) {
|
|
var PromiseReduce = Promise.reduce;
|
|
var PromiseAll = Promise.all;
|
|
|
|
function promiseAllThis() {
|
|
return PromiseAll(this);
|
|
}
|
|
|
|
function PromiseMapSeries(promises, fn) {
|
|
return PromiseReduce(promises, fn, INTERNAL, INTERNAL);
|
|
}
|
|
|
|
Promise.prototype.each = function (fn) {
|
|
return PromiseReduce(this, fn, INTERNAL, 0)
|
|
._then(promiseAllThis, undefined, undefined, this, undefined);
|
|
};
|
|
|
|
Promise.prototype.mapSeries = function (fn) {
|
|
return PromiseReduce(this, fn, INTERNAL, INTERNAL);
|
|
};
|
|
|
|
Promise.each = function (promises, fn) {
|
|
return PromiseReduce(promises, fn, INTERNAL, 0)
|
|
._then(promiseAllThis, undefined, undefined, promises, undefined);
|
|
};
|
|
|
|
Promise.mapSeries = PromiseMapSeries;
|
|
};
|
|
|
|
|
|
},{}],12:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
var es5 = _dereq_("./es5");
|
|
var Objectfreeze = es5.freeze;
|
|
var util = _dereq_("./util");
|
|
var inherits = util.inherits;
|
|
var notEnumerableProp = util.notEnumerableProp;
|
|
|
|
function subError(nameProperty, defaultMessage) {
|
|
function SubError(message) {
|
|
if (!(this instanceof SubError)) return new SubError(message);
|
|
notEnumerableProp(this, "message",
|
|
typeof message === "string" ? message : defaultMessage);
|
|
notEnumerableProp(this, "name", nameProperty);
|
|
if (Error.captureStackTrace) {
|
|
Error.captureStackTrace(this, this.constructor);
|
|
} else {
|
|
Error.call(this);
|
|
}
|
|
}
|
|
inherits(SubError, Error);
|
|
return SubError;
|
|
}
|
|
|
|
var _TypeError, _RangeError;
|
|
var Warning = subError("Warning", "warning");
|
|
var CancellationError = subError("CancellationError", "cancellation error");
|
|
var TimeoutError = subError("TimeoutError", "timeout error");
|
|
var AggregateError = subError("AggregateError", "aggregate error");
|
|
try {
|
|
_TypeError = TypeError;
|
|
_RangeError = RangeError;
|
|
} catch(e) {
|
|
_TypeError = subError("TypeError", "type error");
|
|
_RangeError = subError("RangeError", "range error");
|
|
}
|
|
|
|
var methods = ("join pop push shift unshift slice filter forEach some " +
|
|
"every map indexOf lastIndexOf reduce reduceRight sort reverse").split(" ");
|
|
|
|
for (var i = 0; i < methods.length; ++i) {
|
|
if (typeof Array.prototype[methods[i]] === "function") {
|
|
AggregateError.prototype[methods[i]] = Array.prototype[methods[i]];
|
|
}
|
|
}
|
|
|
|
es5.defineProperty(AggregateError.prototype, "length", {
|
|
value: 0,
|
|
configurable: false,
|
|
writable: true,
|
|
enumerable: true
|
|
});
|
|
AggregateError.prototype["isOperational"] = true;
|
|
var level = 0;
|
|
AggregateError.prototype.toString = function() {
|
|
var indent = Array(level * 4 + 1).join(" ");
|
|
var ret = "\n" + indent + "AggregateError of:" + "\n";
|
|
level++;
|
|
indent = Array(level * 4 + 1).join(" ");
|
|
for (var i = 0; i < this.length; ++i) {
|
|
var str = this[i] === this ? "[Circular AggregateError]" : this[i] + "";
|
|
var lines = str.split("\n");
|
|
for (var j = 0; j < lines.length; ++j) {
|
|
lines[j] = indent + lines[j];
|
|
}
|
|
str = lines.join("\n");
|
|
ret += str + "\n";
|
|
}
|
|
level--;
|
|
return ret;
|
|
};
|
|
|
|
function OperationalError(message) {
|
|
if (!(this instanceof OperationalError))
|
|
return new OperationalError(message);
|
|
notEnumerableProp(this, "name", "OperationalError");
|
|
notEnumerableProp(this, "message", message);
|
|
this.cause = message;
|
|
this["isOperational"] = true;
|
|
|
|
if (message instanceof Error) {
|
|
notEnumerableProp(this, "message", message.message);
|
|
notEnumerableProp(this, "stack", message.stack);
|
|
} else if (Error.captureStackTrace) {
|
|
Error.captureStackTrace(this, this.constructor);
|
|
}
|
|
|
|
}
|
|
inherits(OperationalError, Error);
|
|
|
|
var errorTypes = Error["__BluebirdErrorTypes__"];
|
|
if (!errorTypes) {
|
|
errorTypes = Objectfreeze({
|
|
CancellationError: CancellationError,
|
|
TimeoutError: TimeoutError,
|
|
OperationalError: OperationalError,
|
|
RejectionError: OperationalError,
|
|
AggregateError: AggregateError
|
|
});
|
|
es5.defineProperty(Error, "__BluebirdErrorTypes__", {
|
|
value: errorTypes,
|
|
writable: false,
|
|
enumerable: false,
|
|
configurable: false
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
Error: Error,
|
|
TypeError: _TypeError,
|
|
RangeError: _RangeError,
|
|
CancellationError: errorTypes.CancellationError,
|
|
OperationalError: errorTypes.OperationalError,
|
|
TimeoutError: errorTypes.TimeoutError,
|
|
AggregateError: errorTypes.AggregateError,
|
|
Warning: Warning
|
|
};
|
|
|
|
},{"./es5":13,"./util":36}],13:[function(_dereq_,module,exports){
|
|
var isES5 = (function(){
|
|
"use strict";
|
|
return this === undefined;
|
|
})();
|
|
|
|
if (isES5) {
|
|
module.exports = {
|
|
freeze: Object.freeze,
|
|
defineProperty: Object.defineProperty,
|
|
getDescriptor: Object.getOwnPropertyDescriptor,
|
|
keys: Object.keys,
|
|
names: Object.getOwnPropertyNames,
|
|
getPrototypeOf: Object.getPrototypeOf,
|
|
isArray: Array.isArray,
|
|
isES5: isES5,
|
|
propertyIsWritable: function(obj, prop) {
|
|
var descriptor = Object.getOwnPropertyDescriptor(obj, prop);
|
|
return !!(!descriptor || descriptor.writable || descriptor.set);
|
|
}
|
|
};
|
|
} else {
|
|
var has = {}.hasOwnProperty;
|
|
var str = {}.toString;
|
|
var proto = {}.constructor.prototype;
|
|
|
|
var ObjectKeys = function (o) {
|
|
var ret = [];
|
|
for (var key in o) {
|
|
if (has.call(o, key)) {
|
|
ret.push(key);
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
var ObjectGetDescriptor = function(o, key) {
|
|
return {value: o[key]};
|
|
};
|
|
|
|
var ObjectDefineProperty = function (o, key, desc) {
|
|
o[key] = desc.value;
|
|
return o;
|
|
};
|
|
|
|
var ObjectFreeze = function (obj) {
|
|
return obj;
|
|
};
|
|
|
|
var ObjectGetPrototypeOf = function (obj) {
|
|
try {
|
|
return Object(obj).constructor.prototype;
|
|
}
|
|
catch (e) {
|
|
return proto;
|
|
}
|
|
};
|
|
|
|
var ArrayIsArray = function (obj) {
|
|
try {
|
|
return str.call(obj) === "[object Array]";
|
|
}
|
|
catch(e) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
isArray: ArrayIsArray,
|
|
keys: ObjectKeys,
|
|
names: ObjectKeys,
|
|
defineProperty: ObjectDefineProperty,
|
|
getDescriptor: ObjectGetDescriptor,
|
|
freeze: ObjectFreeze,
|
|
getPrototypeOf: ObjectGetPrototypeOf,
|
|
isES5: isES5,
|
|
propertyIsWritable: function() {
|
|
return true;
|
|
}
|
|
};
|
|
}
|
|
|
|
},{}],14:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise, INTERNAL) {
|
|
var PromiseMap = Promise.map;
|
|
|
|
Promise.prototype.filter = function (fn, options) {
|
|
return PromiseMap(this, fn, options, INTERNAL);
|
|
};
|
|
|
|
Promise.filter = function (promises, fn, options) {
|
|
return PromiseMap(promises, fn, options, INTERNAL);
|
|
};
|
|
};
|
|
|
|
},{}],15:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise, tryConvertToPromise) {
|
|
var util = _dereq_("./util");
|
|
var CancellationError = Promise.CancellationError;
|
|
var errorObj = util.errorObj;
|
|
|
|
function PassThroughHandlerContext(promise, type, handler) {
|
|
this.promise = promise;
|
|
this.type = type;
|
|
this.handler = handler;
|
|
this.called = false;
|
|
this.cancelPromise = null;
|
|
}
|
|
|
|
PassThroughHandlerContext.prototype.isFinallyHandler = function() {
|
|
return this.type === 0;
|
|
};
|
|
|
|
function FinallyHandlerCancelReaction(finallyHandler) {
|
|
this.finallyHandler = finallyHandler;
|
|
}
|
|
|
|
FinallyHandlerCancelReaction.prototype._resultCancelled = function() {
|
|
checkCancel(this.finallyHandler);
|
|
};
|
|
|
|
function checkCancel(ctx, reason) {
|
|
if (ctx.cancelPromise != null) {
|
|
if (arguments.length > 1) {
|
|
ctx.cancelPromise._reject(reason);
|
|
} else {
|
|
ctx.cancelPromise._cancel();
|
|
}
|
|
ctx.cancelPromise = null;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function succeed() {
|
|
return finallyHandler.call(this, this.promise._target()._settledValue());
|
|
}
|
|
function fail(reason) {
|
|
if (checkCancel(this, reason)) return;
|
|
errorObj.e = reason;
|
|
return errorObj;
|
|
}
|
|
function finallyHandler(reasonOrValue) {
|
|
var promise = this.promise;
|
|
var handler = this.handler;
|
|
|
|
if (!this.called) {
|
|
this.called = true;
|
|
var ret = this.isFinallyHandler()
|
|
? handler.call(promise._boundValue())
|
|
: handler.call(promise._boundValue(), reasonOrValue);
|
|
if (ret !== undefined) {
|
|
promise._setReturnedNonUndefined();
|
|
var maybePromise = tryConvertToPromise(ret, promise);
|
|
if (maybePromise instanceof Promise) {
|
|
if (this.cancelPromise != null) {
|
|
if (maybePromise._isCancelled()) {
|
|
var reason =
|
|
new CancellationError("late cancellation observer");
|
|
promise._attachExtraTrace(reason);
|
|
errorObj.e = reason;
|
|
return errorObj;
|
|
} else if (maybePromise.isPending()) {
|
|
maybePromise._attachCancellationCallback(
|
|
new FinallyHandlerCancelReaction(this));
|
|
}
|
|
}
|
|
return maybePromise._then(
|
|
succeed, fail, undefined, this, undefined);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (promise.isRejected()) {
|
|
checkCancel(this);
|
|
errorObj.e = reasonOrValue;
|
|
return errorObj;
|
|
} else {
|
|
checkCancel(this);
|
|
return reasonOrValue;
|
|
}
|
|
}
|
|
|
|
Promise.prototype._passThrough = function(handler, type, success, fail) {
|
|
if (typeof handler !== "function") return this.then();
|
|
return this._then(success,
|
|
fail,
|
|
undefined,
|
|
new PassThroughHandlerContext(this, type, handler),
|
|
undefined);
|
|
};
|
|
|
|
Promise.prototype.lastly =
|
|
Promise.prototype["finally"] = function (handler) {
|
|
return this._passThrough(handler,
|
|
0,
|
|
finallyHandler,
|
|
finallyHandler);
|
|
};
|
|
|
|
Promise.prototype.tap = function (handler) {
|
|
return this._passThrough(handler, 1, finallyHandler);
|
|
};
|
|
|
|
return PassThroughHandlerContext;
|
|
};
|
|
|
|
},{"./util":36}],16:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise,
|
|
apiRejection,
|
|
INTERNAL,
|
|
tryConvertToPromise,
|
|
Proxyable,
|
|
debug) {
|
|
var errors = _dereq_("./errors");
|
|
var TypeError = errors.TypeError;
|
|
var util = _dereq_("./util");
|
|
var errorObj = util.errorObj;
|
|
var tryCatch = util.tryCatch;
|
|
var yieldHandlers = [];
|
|
|
|
function promiseFromYieldHandler(value, yieldHandlers, traceParent) {
|
|
for (var i = 0; i < yieldHandlers.length; ++i) {
|
|
traceParent._pushContext();
|
|
var result = tryCatch(yieldHandlers[i])(value);
|
|
traceParent._popContext();
|
|
if (result === errorObj) {
|
|
traceParent._pushContext();
|
|
var ret = Promise.reject(errorObj.e);
|
|
traceParent._popContext();
|
|
return ret;
|
|
}
|
|
var maybePromise = tryConvertToPromise(result, traceParent);
|
|
if (maybePromise instanceof Promise) return maybePromise;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function PromiseSpawn(generatorFunction, receiver, yieldHandler, stack) {
|
|
if (debug.cancellation()) {
|
|
var internal = new Promise(INTERNAL);
|
|
var _finallyPromise = this._finallyPromise = new Promise(INTERNAL);
|
|
this._promise = internal.lastly(function() {
|
|
return _finallyPromise;
|
|
});
|
|
internal._captureStackTrace();
|
|
internal._setOnCancel(this);
|
|
} else {
|
|
var promise = this._promise = new Promise(INTERNAL);
|
|
promise._captureStackTrace();
|
|
}
|
|
this._stack = stack;
|
|
this._generatorFunction = generatorFunction;
|
|
this._receiver = receiver;
|
|
this._generator = undefined;
|
|
this._yieldHandlers = typeof yieldHandler === "function"
|
|
? [yieldHandler].concat(yieldHandlers)
|
|
: yieldHandlers;
|
|
this._yieldedPromise = null;
|
|
this._cancellationPhase = false;
|
|
}
|
|
util.inherits(PromiseSpawn, Proxyable);
|
|
|
|
PromiseSpawn.prototype._isResolved = function() {
|
|
return this._promise === null;
|
|
};
|
|
|
|
PromiseSpawn.prototype._cleanup = function() {
|
|
this._promise = this._generator = null;
|
|
if (debug.cancellation() && this._finallyPromise !== null) {
|
|
this._finallyPromise._fulfill();
|
|
this._finallyPromise = null;
|
|
}
|
|
};
|
|
|
|
PromiseSpawn.prototype._promiseCancelled = function() {
|
|
if (this._isResolved()) return;
|
|
var implementsReturn = typeof this._generator["return"] !== "undefined";
|
|
|
|
var result;
|
|
if (!implementsReturn) {
|
|
var reason = new Promise.CancellationError(
|
|
"generator .return() sentinel");
|
|
Promise.coroutine.returnSentinel = reason;
|
|
this._promise._attachExtraTrace(reason);
|
|
this._promise._pushContext();
|
|
result = tryCatch(this._generator["throw"]).call(this._generator,
|
|
reason);
|
|
this._promise._popContext();
|
|
} else {
|
|
this._promise._pushContext();
|
|
result = tryCatch(this._generator["return"]).call(this._generator,
|
|
undefined);
|
|
this._promise._popContext();
|
|
}
|
|
this._cancellationPhase = true;
|
|
this._yieldedPromise = null;
|
|
this._continue(result);
|
|
};
|
|
|
|
PromiseSpawn.prototype._promiseFulfilled = function(value) {
|
|
this._yieldedPromise = null;
|
|
this._promise._pushContext();
|
|
var result = tryCatch(this._generator.next).call(this._generator, value);
|
|
this._promise._popContext();
|
|
this._continue(result);
|
|
};
|
|
|
|
PromiseSpawn.prototype._promiseRejected = function(reason) {
|
|
this._yieldedPromise = null;
|
|
this._promise._attachExtraTrace(reason);
|
|
this._promise._pushContext();
|
|
var result = tryCatch(this._generator["throw"])
|
|
.call(this._generator, reason);
|
|
this._promise._popContext();
|
|
this._continue(result);
|
|
};
|
|
|
|
PromiseSpawn.prototype._resultCancelled = function() {
|
|
if (this._yieldedPromise instanceof Promise) {
|
|
var promise = this._yieldedPromise;
|
|
this._yieldedPromise = null;
|
|
promise.cancel();
|
|
}
|
|
};
|
|
|
|
PromiseSpawn.prototype.promise = function () {
|
|
return this._promise;
|
|
};
|
|
|
|
PromiseSpawn.prototype._run = function () {
|
|
this._generator = this._generatorFunction.call(this._receiver);
|
|
this._receiver =
|
|
this._generatorFunction = undefined;
|
|
this._promiseFulfilled(undefined);
|
|
};
|
|
|
|
PromiseSpawn.prototype._continue = function (result) {
|
|
var promise = this._promise;
|
|
if (result === errorObj) {
|
|
this._cleanup();
|
|
if (this._cancellationPhase) {
|
|
return promise.cancel();
|
|
} else {
|
|
return promise._rejectCallback(result.e, false);
|
|
}
|
|
}
|
|
|
|
var value = result.value;
|
|
if (result.done === true) {
|
|
this._cleanup();
|
|
if (this._cancellationPhase) {
|
|
return promise.cancel();
|
|
} else {
|
|
return promise._resolveCallback(value);
|
|
}
|
|
} else {
|
|
var maybePromise = tryConvertToPromise(value, this._promise);
|
|
if (!(maybePromise instanceof Promise)) {
|
|
maybePromise =
|
|
promiseFromYieldHandler(maybePromise,
|
|
this._yieldHandlers,
|
|
this._promise);
|
|
if (maybePromise === null) {
|
|
this._promiseRejected(
|
|
new TypeError(
|
|
"A value %s was yielded that could not be treated as a promise\u000a\u000a See http://goo.gl/MqrFmX\u000a\u000a".replace("%s", value) +
|
|
"From coroutine:\u000a" +
|
|
this._stack.split("\n").slice(1, -7).join("\n")
|
|
)
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
maybePromise = maybePromise._target();
|
|
var bitField = maybePromise._bitField;
|
|
;
|
|
if (((bitField & 50397184) === 0)) {
|
|
this._yieldedPromise = maybePromise;
|
|
maybePromise._proxy(this, null);
|
|
} else if (((bitField & 33554432) !== 0)) {
|
|
Promise._async.invoke(
|
|
this._promiseFulfilled, this, maybePromise._value()
|
|
);
|
|
} else if (((bitField & 16777216) !== 0)) {
|
|
Promise._async.invoke(
|
|
this._promiseRejected, this, maybePromise._reason()
|
|
);
|
|
} else {
|
|
this._promiseCancelled();
|
|
}
|
|
}
|
|
};
|
|
|
|
Promise.coroutine = function (generatorFunction, options) {
|
|
if (typeof generatorFunction !== "function") {
|
|
throw new TypeError("generatorFunction must be a function\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
}
|
|
var yieldHandler = Object(options).yieldHandler;
|
|
var PromiseSpawn$ = PromiseSpawn;
|
|
var stack = new Error().stack;
|
|
return function () {
|
|
var generator = generatorFunction.apply(this, arguments);
|
|
var spawn = new PromiseSpawn$(undefined, undefined, yieldHandler,
|
|
stack);
|
|
var ret = spawn.promise();
|
|
spawn._generator = generator;
|
|
spawn._promiseFulfilled(undefined);
|
|
return ret;
|
|
};
|
|
};
|
|
|
|
Promise.coroutine.addYieldHandler = function(fn) {
|
|
if (typeof fn !== "function") {
|
|
throw new TypeError("expecting a function but got " + util.classString(fn));
|
|
}
|
|
yieldHandlers.push(fn);
|
|
};
|
|
|
|
Promise.spawn = function (generatorFunction) {
|
|
debug.deprecated("Promise.spawn()", "Promise.coroutine()");
|
|
if (typeof generatorFunction !== "function") {
|
|
return apiRejection("generatorFunction must be a function\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
}
|
|
var spawn = new PromiseSpawn(generatorFunction, this);
|
|
var ret = spawn.promise();
|
|
spawn._run(Promise.spawn);
|
|
return ret;
|
|
};
|
|
};
|
|
|
|
},{"./errors":12,"./util":36}],17:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports =
|
|
function(Promise, PromiseArray, tryConvertToPromise, INTERNAL, async,
|
|
getDomain) {
|
|
var util = _dereq_("./util");
|
|
var canEvaluate = util.canEvaluate;
|
|
var tryCatch = util.tryCatch;
|
|
var errorObj = util.errorObj;
|
|
var reject;
|
|
|
|
if (!true) {
|
|
if (canEvaluate) {
|
|
var thenCallback = function(i) {
|
|
return new Function("value", "holder", " \n\
|
|
'use strict'; \n\
|
|
holder.pIndex = value; \n\
|
|
holder.checkFulfillment(this); \n\
|
|
".replace(/Index/g, i));
|
|
};
|
|
|
|
var promiseSetter = function(i) {
|
|
return new Function("promise", "holder", " \n\
|
|
'use strict'; \n\
|
|
holder.pIndex = promise; \n\
|
|
".replace(/Index/g, i));
|
|
};
|
|
|
|
var generateHolderClass = function(total) {
|
|
var props = new Array(total);
|
|
for (var i = 0; i < props.length; ++i) {
|
|
props[i] = "this.p" + (i+1);
|
|
}
|
|
var assignment = props.join(" = ") + " = null;";
|
|
var cancellationCode= "var promise;\n" + props.map(function(prop) {
|
|
return " \n\
|
|
promise = " + prop + "; \n\
|
|
if (promise instanceof Promise) { \n\
|
|
promise.cancel(); \n\
|
|
} \n\
|
|
";
|
|
}).join("\n");
|
|
var passedArguments = props.join(", ");
|
|
var name = "Holder$" + total;
|
|
|
|
|
|
var code = "return function(tryCatch, errorObj, Promise, async) { \n\
|
|
'use strict'; \n\
|
|
function [TheName](fn) { \n\
|
|
[TheProperties] \n\
|
|
this.fn = fn; \n\
|
|
this.asyncNeeded = true; \n\
|
|
this.now = 0; \n\
|
|
} \n\
|
|
\n\
|
|
[TheName].prototype._callFunction = function(promise) { \n\
|
|
promise._pushContext(); \n\
|
|
var ret = tryCatch(this.fn)([ThePassedArguments]); \n\
|
|
promise._popContext(); \n\
|
|
if (ret === errorObj) { \n\
|
|
promise._rejectCallback(ret.e, false); \n\
|
|
} else { \n\
|
|
promise._resolveCallback(ret); \n\
|
|
} \n\
|
|
}; \n\
|
|
\n\
|
|
[TheName].prototype.checkFulfillment = function(promise) { \n\
|
|
var now = ++this.now; \n\
|
|
if (now === [TheTotal]) { \n\
|
|
if (this.asyncNeeded) { \n\
|
|
async.invoke(this._callFunction, this, promise); \n\
|
|
} else { \n\
|
|
this._callFunction(promise); \n\
|
|
} \n\
|
|
\n\
|
|
} \n\
|
|
}; \n\
|
|
\n\
|
|
[TheName].prototype._resultCancelled = function() { \n\
|
|
[CancellationCode] \n\
|
|
}; \n\
|
|
\n\
|
|
return [TheName]; \n\
|
|
}(tryCatch, errorObj, Promise, async); \n\
|
|
";
|
|
|
|
code = code.replace(/\[TheName\]/g, name)
|
|
.replace(/\[TheTotal\]/g, total)
|
|
.replace(/\[ThePassedArguments\]/g, passedArguments)
|
|
.replace(/\[TheProperties\]/g, assignment)
|
|
.replace(/\[CancellationCode\]/g, cancellationCode);
|
|
|
|
return new Function("tryCatch", "errorObj", "Promise", "async", code)
|
|
(tryCatch, errorObj, Promise, async);
|
|
};
|
|
|
|
var holderClasses = [];
|
|
var thenCallbacks = [];
|
|
var promiseSetters = [];
|
|
|
|
for (var i = 0; i < 8; ++i) {
|
|
holderClasses.push(generateHolderClass(i + 1));
|
|
thenCallbacks.push(thenCallback(i + 1));
|
|
promiseSetters.push(promiseSetter(i + 1));
|
|
}
|
|
|
|
reject = function (reason) {
|
|
this._reject(reason);
|
|
};
|
|
}}
|
|
|
|
Promise.join = function () {
|
|
var last = arguments.length - 1;
|
|
var fn;
|
|
if (last > 0 && typeof arguments[last] === "function") {
|
|
fn = arguments[last];
|
|
if (!true) {
|
|
if (last <= 8 && canEvaluate) {
|
|
var ret = new Promise(INTERNAL);
|
|
ret._captureStackTrace();
|
|
var HolderClass = holderClasses[last - 1];
|
|
var holder = new HolderClass(fn);
|
|
var callbacks = thenCallbacks;
|
|
|
|
for (var i = 0; i < last; ++i) {
|
|
var maybePromise = tryConvertToPromise(arguments[i], ret);
|
|
if (maybePromise instanceof Promise) {
|
|
maybePromise = maybePromise._target();
|
|
var bitField = maybePromise._bitField;
|
|
;
|
|
if (((bitField & 50397184) === 0)) {
|
|
maybePromise._then(callbacks[i], reject,
|
|
undefined, ret, holder);
|
|
promiseSetters[i](maybePromise, holder);
|
|
holder.asyncNeeded = false;
|
|
} else if (((bitField & 33554432) !== 0)) {
|
|
callbacks[i].call(ret,
|
|
maybePromise._value(), holder);
|
|
} else if (((bitField & 16777216) !== 0)) {
|
|
ret._reject(maybePromise._reason());
|
|
} else {
|
|
ret._cancel();
|
|
}
|
|
} else {
|
|
callbacks[i].call(ret, maybePromise, holder);
|
|
}
|
|
}
|
|
|
|
if (!ret._isFateSealed()) {
|
|
if (holder.asyncNeeded) {
|
|
var domain = getDomain();
|
|
if (domain !== null) {
|
|
holder.fn = util.domainBind(domain, holder.fn);
|
|
}
|
|
}
|
|
ret._setAsyncGuaranteed();
|
|
ret._setOnCancel(holder);
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
var args = [].slice.call(arguments);;
|
|
if (fn) args.pop();
|
|
var ret = new PromiseArray(args).promise();
|
|
return fn !== undefined ? ret.spread(fn) : ret;
|
|
};
|
|
|
|
};
|
|
|
|
},{"./util":36}],18:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise,
|
|
PromiseArray,
|
|
apiRejection,
|
|
tryConvertToPromise,
|
|
INTERNAL,
|
|
debug) {
|
|
var getDomain = Promise._getDomain;
|
|
var util = _dereq_("./util");
|
|
var tryCatch = util.tryCatch;
|
|
var errorObj = util.errorObj;
|
|
var async = Promise._async;
|
|
|
|
function MappingPromiseArray(promises, fn, limit, _filter) {
|
|
this.constructor$(promises);
|
|
this._promise._captureStackTrace();
|
|
var domain = getDomain();
|
|
this._callback = domain === null ? fn : util.domainBind(domain, fn);
|
|
this._preservedValues = _filter === INTERNAL
|
|
? new Array(this.length())
|
|
: null;
|
|
this._limit = limit;
|
|
this._inFlight = 0;
|
|
this._queue = [];
|
|
async.invoke(this._asyncInit, this, undefined);
|
|
}
|
|
util.inherits(MappingPromiseArray, PromiseArray);
|
|
|
|
MappingPromiseArray.prototype._asyncInit = function() {
|
|
this._init$(undefined, -2);
|
|
};
|
|
|
|
MappingPromiseArray.prototype._init = function () {};
|
|
|
|
MappingPromiseArray.prototype._promiseFulfilled = function (value, index) {
|
|
var values = this._values;
|
|
var length = this.length();
|
|
var preservedValues = this._preservedValues;
|
|
var limit = this._limit;
|
|
|
|
if (index < 0) {
|
|
index = (index * -1) - 1;
|
|
values[index] = value;
|
|
if (limit >= 1) {
|
|
this._inFlight--;
|
|
this._drainQueue();
|
|
if (this._isResolved()) return true;
|
|
}
|
|
} else {
|
|
if (limit >= 1 && this._inFlight >= limit) {
|
|
values[index] = value;
|
|
this._queue.push(index);
|
|
return false;
|
|
}
|
|
if (preservedValues !== null) preservedValues[index] = value;
|
|
|
|
var promise = this._promise;
|
|
var callback = this._callback;
|
|
var receiver = promise._boundValue();
|
|
promise._pushContext();
|
|
var ret = tryCatch(callback).call(receiver, value, index, length);
|
|
var promiseCreated = promise._popContext();
|
|
debug.checkForgottenReturns(
|
|
ret,
|
|
promiseCreated,
|
|
preservedValues !== null ? "Promise.filter" : "Promise.map",
|
|
promise
|
|
);
|
|
if (ret === errorObj) {
|
|
this._reject(ret.e);
|
|
return true;
|
|
}
|
|
|
|
var maybePromise = tryConvertToPromise(ret, this._promise);
|
|
if (maybePromise instanceof Promise) {
|
|
maybePromise = maybePromise._target();
|
|
var bitField = maybePromise._bitField;
|
|
;
|
|
if (((bitField & 50397184) === 0)) {
|
|
if (limit >= 1) this._inFlight++;
|
|
values[index] = maybePromise;
|
|
maybePromise._proxy(this, (index + 1) * -1);
|
|
return false;
|
|
} else if (((bitField & 33554432) !== 0)) {
|
|
ret = maybePromise._value();
|
|
} else if (((bitField & 16777216) !== 0)) {
|
|
this._reject(maybePromise._reason());
|
|
return true;
|
|
} else {
|
|
this._cancel();
|
|
return true;
|
|
}
|
|
}
|
|
values[index] = ret;
|
|
}
|
|
var totalResolved = ++this._totalResolved;
|
|
if (totalResolved >= length) {
|
|
if (preservedValues !== null) {
|
|
this._filter(values, preservedValues);
|
|
} else {
|
|
this._resolve(values);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
MappingPromiseArray.prototype._drainQueue = function () {
|
|
var queue = this._queue;
|
|
var limit = this._limit;
|
|
var values = this._values;
|
|
while (queue.length > 0 && this._inFlight < limit) {
|
|
if (this._isResolved()) return;
|
|
var index = queue.pop();
|
|
this._promiseFulfilled(values[index], index);
|
|
}
|
|
};
|
|
|
|
MappingPromiseArray.prototype._filter = function (booleans, values) {
|
|
var len = values.length;
|
|
var ret = new Array(len);
|
|
var j = 0;
|
|
for (var i = 0; i < len; ++i) {
|
|
if (booleans[i]) ret[j++] = values[i];
|
|
}
|
|
ret.length = j;
|
|
this._resolve(ret);
|
|
};
|
|
|
|
MappingPromiseArray.prototype.preservedValues = function () {
|
|
return this._preservedValues;
|
|
};
|
|
|
|
function map(promises, fn, options, _filter) {
|
|
if (typeof fn !== "function") {
|
|
return apiRejection("expecting a function but got " + util.classString(fn));
|
|
}
|
|
|
|
var limit = 0;
|
|
if (options !== undefined) {
|
|
if (typeof options === "object" && options !== null) {
|
|
if (typeof options.concurrency !== "number") {
|
|
return Promise.reject(
|
|
new TypeError("'concurrency' must be a number but it is " +
|
|
util.classString(options.concurrency)));
|
|
}
|
|
limit = options.concurrency;
|
|
} else {
|
|
return Promise.reject(new TypeError(
|
|
"options argument must be an object but it is " +
|
|
util.classString(options)));
|
|
}
|
|
}
|
|
limit = typeof limit === "number" &&
|
|
isFinite(limit) && limit >= 1 ? limit : 0;
|
|
return new MappingPromiseArray(promises, fn, limit, _filter).promise();
|
|
}
|
|
|
|
Promise.prototype.map = function (fn, options) {
|
|
return map(this, fn, options, null);
|
|
};
|
|
|
|
Promise.map = function (promises, fn, options, _filter) {
|
|
return map(promises, fn, options, _filter);
|
|
};
|
|
|
|
|
|
};
|
|
|
|
},{"./util":36}],19:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports =
|
|
function(Promise, INTERNAL, tryConvertToPromise, apiRejection, debug) {
|
|
var util = _dereq_("./util");
|
|
var tryCatch = util.tryCatch;
|
|
|
|
Promise.method = function (fn) {
|
|
if (typeof fn !== "function") {
|
|
throw new Promise.TypeError("expecting a function but got " + util.classString(fn));
|
|
}
|
|
return function () {
|
|
var ret = new Promise(INTERNAL);
|
|
ret._captureStackTrace();
|
|
ret._pushContext();
|
|
var value = tryCatch(fn).apply(this, arguments);
|
|
var promiseCreated = ret._popContext();
|
|
debug.checkForgottenReturns(
|
|
value, promiseCreated, "Promise.method", ret);
|
|
ret._resolveFromSyncValue(value);
|
|
return ret;
|
|
};
|
|
};
|
|
|
|
Promise.attempt = Promise["try"] = function (fn) {
|
|
if (typeof fn !== "function") {
|
|
return apiRejection("expecting a function but got " + util.classString(fn));
|
|
}
|
|
var ret = new Promise(INTERNAL);
|
|
ret._captureStackTrace();
|
|
ret._pushContext();
|
|
var value;
|
|
if (arguments.length > 1) {
|
|
debug.deprecated("calling Promise.try with more than 1 argument");
|
|
var arg = arguments[1];
|
|
var ctx = arguments[2];
|
|
value = util.isArray(arg) ? tryCatch(fn).apply(ctx, arg)
|
|
: tryCatch(fn).call(ctx, arg);
|
|
} else {
|
|
value = tryCatch(fn)();
|
|
}
|
|
var promiseCreated = ret._popContext();
|
|
debug.checkForgottenReturns(
|
|
value, promiseCreated, "Promise.try", ret);
|
|
ret._resolveFromSyncValue(value);
|
|
return ret;
|
|
};
|
|
|
|
Promise.prototype._resolveFromSyncValue = function (value) {
|
|
if (value === util.errorObj) {
|
|
this._rejectCallback(value.e, false);
|
|
} else {
|
|
this._resolveCallback(value, true);
|
|
}
|
|
};
|
|
};
|
|
|
|
},{"./util":36}],20:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
var util = _dereq_("./util");
|
|
var maybeWrapAsError = util.maybeWrapAsError;
|
|
var errors = _dereq_("./errors");
|
|
var OperationalError = errors.OperationalError;
|
|
var es5 = _dereq_("./es5");
|
|
|
|
function isUntypedError(obj) {
|
|
return obj instanceof Error &&
|
|
es5.getPrototypeOf(obj) === Error.prototype;
|
|
}
|
|
|
|
var rErrorKey = /^(?:name|message|stack|cause)$/;
|
|
function wrapAsOperationalError(obj) {
|
|
var ret;
|
|
if (isUntypedError(obj)) {
|
|
ret = new OperationalError(obj);
|
|
ret.name = obj.name;
|
|
ret.message = obj.message;
|
|
ret.stack = obj.stack;
|
|
var keys = es5.keys(obj);
|
|
for (var i = 0; i < keys.length; ++i) {
|
|
var key = keys[i];
|
|
if (!rErrorKey.test(key)) {
|
|
ret[key] = obj[key];
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
util.markAsOriginatingFromRejection(obj);
|
|
return obj;
|
|
}
|
|
|
|
function nodebackForPromise(promise, multiArgs) {
|
|
return function(err, value) {
|
|
if (promise === null) return;
|
|
if (err) {
|
|
var wrapped = wrapAsOperationalError(maybeWrapAsError(err));
|
|
promise._attachExtraTrace(wrapped);
|
|
promise._reject(wrapped);
|
|
} else if (!multiArgs) {
|
|
promise._fulfill(value);
|
|
} else {
|
|
var args = [].slice.call(arguments, 1);;
|
|
promise._fulfill(args);
|
|
}
|
|
promise = null;
|
|
};
|
|
}
|
|
|
|
module.exports = nodebackForPromise;
|
|
|
|
},{"./errors":12,"./es5":13,"./util":36}],21:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise) {
|
|
var util = _dereq_("./util");
|
|
var async = Promise._async;
|
|
var tryCatch = util.tryCatch;
|
|
var errorObj = util.errorObj;
|
|
|
|
function spreadAdapter(val, nodeback) {
|
|
var promise = this;
|
|
if (!util.isArray(val)) return successAdapter.call(promise, val, nodeback);
|
|
var ret =
|
|
tryCatch(nodeback).apply(promise._boundValue(), [null].concat(val));
|
|
if (ret === errorObj) {
|
|
async.throwLater(ret.e);
|
|
}
|
|
}
|
|
|
|
function successAdapter(val, nodeback) {
|
|
var promise = this;
|
|
var receiver = promise._boundValue();
|
|
var ret = val === undefined
|
|
? tryCatch(nodeback).call(receiver, null)
|
|
: tryCatch(nodeback).call(receiver, null, val);
|
|
if (ret === errorObj) {
|
|
async.throwLater(ret.e);
|
|
}
|
|
}
|
|
function errorAdapter(reason, nodeback) {
|
|
var promise = this;
|
|
if (!reason) {
|
|
var newReason = new Error(reason + "");
|
|
newReason.cause = reason;
|
|
reason = newReason;
|
|
}
|
|
var ret = tryCatch(nodeback).call(promise._boundValue(), reason);
|
|
if (ret === errorObj) {
|
|
async.throwLater(ret.e);
|
|
}
|
|
}
|
|
|
|
Promise.prototype.asCallback = Promise.prototype.nodeify = function (nodeback,
|
|
options) {
|
|
if (typeof nodeback == "function") {
|
|
var adapter = successAdapter;
|
|
if (options !== undefined && Object(options).spread) {
|
|
adapter = spreadAdapter;
|
|
}
|
|
this._then(
|
|
adapter,
|
|
errorAdapter,
|
|
undefined,
|
|
this,
|
|
nodeback
|
|
);
|
|
}
|
|
return this;
|
|
};
|
|
};
|
|
|
|
},{"./util":36}],22:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function() {
|
|
var makeSelfResolutionError = function () {
|
|
return new TypeError("circular promise resolution chain\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
};
|
|
var reflectHandler = function() {
|
|
return new Promise.PromiseInspection(this._target());
|
|
};
|
|
var apiRejection = function(msg) {
|
|
return Promise.reject(new TypeError(msg));
|
|
};
|
|
function Proxyable() {}
|
|
var UNDEFINED_BINDING = {};
|
|
var util = _dereq_("./util");
|
|
|
|
var getDomain;
|
|
if (util.isNode) {
|
|
getDomain = function() {
|
|
var ret = process.domain;
|
|
if (ret === undefined) ret = null;
|
|
return ret;
|
|
};
|
|
} else {
|
|
getDomain = function() {
|
|
return null;
|
|
};
|
|
}
|
|
util.notEnumerableProp(Promise, "_getDomain", getDomain);
|
|
|
|
var es5 = _dereq_("./es5");
|
|
var Async = _dereq_("./async");
|
|
var async = new Async();
|
|
es5.defineProperty(Promise, "_async", {value: async});
|
|
var errors = _dereq_("./errors");
|
|
var TypeError = Promise.TypeError = errors.TypeError;
|
|
Promise.RangeError = errors.RangeError;
|
|
var CancellationError = Promise.CancellationError = errors.CancellationError;
|
|
Promise.TimeoutError = errors.TimeoutError;
|
|
Promise.OperationalError = errors.OperationalError;
|
|
Promise.RejectionError = errors.OperationalError;
|
|
Promise.AggregateError = errors.AggregateError;
|
|
var INTERNAL = function(){};
|
|
var APPLY = {};
|
|
var NEXT_FILTER = {};
|
|
var tryConvertToPromise = _dereq_("./thenables")(Promise, INTERNAL);
|
|
var PromiseArray =
|
|
_dereq_("./promise_array")(Promise, INTERNAL,
|
|
tryConvertToPromise, apiRejection, Proxyable);
|
|
var Context = _dereq_("./context")(Promise);
|
|
/*jshint unused:false*/
|
|
var createContext = Context.create;
|
|
var debug = _dereq_("./debuggability")(Promise, Context);
|
|
var CapturedTrace = debug.CapturedTrace;
|
|
var PassThroughHandlerContext =
|
|
_dereq_("./finally")(Promise, tryConvertToPromise);
|
|
var catchFilter = _dereq_("./catch_filter")(NEXT_FILTER);
|
|
var nodebackForPromise = _dereq_("./nodeback");
|
|
var errorObj = util.errorObj;
|
|
var tryCatch = util.tryCatch;
|
|
function check(self, executor) {
|
|
if (typeof executor !== "function") {
|
|
throw new TypeError("expecting a function but got " + util.classString(executor));
|
|
}
|
|
if (self.constructor !== Promise) {
|
|
throw new TypeError("the promise constructor cannot be invoked directly\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
}
|
|
}
|
|
|
|
function Promise(executor) {
|
|
this._bitField = 0;
|
|
this._fulfillmentHandler0 = undefined;
|
|
this._rejectionHandler0 = undefined;
|
|
this._promise0 = undefined;
|
|
this._receiver0 = undefined;
|
|
if (executor !== INTERNAL) {
|
|
check(this, executor);
|
|
this._resolveFromExecutor(executor);
|
|
}
|
|
this._promiseCreated();
|
|
this._fireEvent("promiseCreated", this);
|
|
}
|
|
|
|
Promise.prototype.toString = function () {
|
|
return "[object Promise]";
|
|
};
|
|
|
|
Promise.prototype.caught = Promise.prototype["catch"] = function (fn) {
|
|
var len = arguments.length;
|
|
if (len > 1) {
|
|
var catchInstances = new Array(len - 1),
|
|
j = 0, i;
|
|
for (i = 0; i < len - 1; ++i) {
|
|
var item = arguments[i];
|
|
if (util.isObject(item)) {
|
|
catchInstances[j++] = item;
|
|
} else {
|
|
return apiRejection("expecting an object but got " +
|
|
"A catch statement predicate " + util.classString(item));
|
|
}
|
|
}
|
|
catchInstances.length = j;
|
|
fn = arguments[i];
|
|
return this.then(undefined, catchFilter(catchInstances, fn, this));
|
|
}
|
|
return this.then(undefined, fn);
|
|
};
|
|
|
|
Promise.prototype.reflect = function () {
|
|
return this._then(reflectHandler,
|
|
reflectHandler, undefined, this, undefined);
|
|
};
|
|
|
|
Promise.prototype.then = function (didFulfill, didReject) {
|
|
if (debug.warnings() && arguments.length > 0 &&
|
|
typeof didFulfill !== "function" &&
|
|
typeof didReject !== "function") {
|
|
var msg = ".then() only accepts functions but was passed: " +
|
|
util.classString(didFulfill);
|
|
if (arguments.length > 1) {
|
|
msg += ", " + util.classString(didReject);
|
|
}
|
|
this._warn(msg);
|
|
}
|
|
return this._then(didFulfill, didReject, undefined, undefined, undefined);
|
|
};
|
|
|
|
Promise.prototype.done = function (didFulfill, didReject) {
|
|
var promise =
|
|
this._then(didFulfill, didReject, undefined, undefined, undefined);
|
|
promise._setIsFinal();
|
|
};
|
|
|
|
Promise.prototype.spread = function (fn) {
|
|
if (typeof fn !== "function") {
|
|
return apiRejection("expecting a function but got " + util.classString(fn));
|
|
}
|
|
return this.all()._then(fn, undefined, undefined, APPLY, undefined);
|
|
};
|
|
|
|
Promise.prototype.toJSON = function () {
|
|
var ret = {
|
|
isFulfilled: false,
|
|
isRejected: false,
|
|
fulfillmentValue: undefined,
|
|
rejectionReason: undefined
|
|
};
|
|
if (this.isFulfilled()) {
|
|
ret.fulfillmentValue = this.value();
|
|
ret.isFulfilled = true;
|
|
} else if (this.isRejected()) {
|
|
ret.rejectionReason = this.reason();
|
|
ret.isRejected = true;
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
Promise.prototype.all = function () {
|
|
if (arguments.length > 0) {
|
|
this._warn(".all() was passed arguments but it does not take any");
|
|
}
|
|
return new PromiseArray(this).promise();
|
|
};
|
|
|
|
Promise.prototype.error = function (fn) {
|
|
return this.caught(util.originatesFromRejection, fn);
|
|
};
|
|
|
|
Promise.getNewLibraryCopy = module.exports;
|
|
|
|
Promise.is = function (val) {
|
|
return val instanceof Promise;
|
|
};
|
|
|
|
Promise.fromNode = Promise.fromCallback = function(fn) {
|
|
var ret = new Promise(INTERNAL);
|
|
ret._captureStackTrace();
|
|
var multiArgs = arguments.length > 1 ? !!Object(arguments[1]).multiArgs
|
|
: false;
|
|
var result = tryCatch(fn)(nodebackForPromise(ret, multiArgs));
|
|
if (result === errorObj) {
|
|
ret._rejectCallback(result.e, true);
|
|
}
|
|
if (!ret._isFateSealed()) ret._setAsyncGuaranteed();
|
|
return ret;
|
|
};
|
|
|
|
Promise.all = function (promises) {
|
|
return new PromiseArray(promises).promise();
|
|
};
|
|
|
|
Promise.cast = function (obj) {
|
|
var ret = tryConvertToPromise(obj);
|
|
if (!(ret instanceof Promise)) {
|
|
ret = new Promise(INTERNAL);
|
|
ret._captureStackTrace();
|
|
ret._setFulfilled();
|
|
ret._rejectionHandler0 = obj;
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
Promise.resolve = Promise.fulfilled = Promise.cast;
|
|
|
|
Promise.reject = Promise.rejected = function (reason) {
|
|
var ret = new Promise(INTERNAL);
|
|
ret._captureStackTrace();
|
|
ret._rejectCallback(reason, true);
|
|
return ret;
|
|
};
|
|
|
|
Promise.setScheduler = function(fn) {
|
|
if (typeof fn !== "function") {
|
|
throw new TypeError("expecting a function but got " + util.classString(fn));
|
|
}
|
|
return async.setScheduler(fn);
|
|
};
|
|
|
|
Promise.prototype._then = function (
|
|
didFulfill,
|
|
didReject,
|
|
_, receiver,
|
|
internalData
|
|
) {
|
|
var haveInternalData = internalData !== undefined;
|
|
var promise = haveInternalData ? internalData : new Promise(INTERNAL);
|
|
var target = this._target();
|
|
var bitField = target._bitField;
|
|
|
|
if (!haveInternalData) {
|
|
promise._propagateFrom(this, 3);
|
|
promise._captureStackTrace();
|
|
if (receiver === undefined &&
|
|
((this._bitField & 2097152) !== 0)) {
|
|
if (!((bitField & 50397184) === 0)) {
|
|
receiver = this._boundValue();
|
|
} else {
|
|
receiver = target === this ? undefined : this._boundTo;
|
|
}
|
|
}
|
|
this._fireEvent("promiseChained", this, promise);
|
|
}
|
|
|
|
var domain = getDomain();
|
|
if (!((bitField & 50397184) === 0)) {
|
|
var handler, value, settler = target._settlePromiseCtx;
|
|
if (((bitField & 33554432) !== 0)) {
|
|
value = target._rejectionHandler0;
|
|
handler = didFulfill;
|
|
} else if (((bitField & 16777216) !== 0)) {
|
|
value = target._fulfillmentHandler0;
|
|
handler = didReject;
|
|
target._unsetRejectionIsUnhandled();
|
|
} else {
|
|
settler = target._settlePromiseLateCancellationObserver;
|
|
value = new CancellationError("late cancellation observer");
|
|
target._attachExtraTrace(value);
|
|
handler = didReject;
|
|
}
|
|
|
|
async.invoke(settler, target, {
|
|
handler: domain === null ? handler
|
|
: (typeof handler === "function" &&
|
|
util.domainBind(domain, handler)),
|
|
promise: promise,
|
|
receiver: receiver,
|
|
value: value
|
|
});
|
|
} else {
|
|
target._addCallbacks(didFulfill, didReject, promise, receiver, domain);
|
|
}
|
|
|
|
return promise;
|
|
};
|
|
|
|
Promise.prototype._length = function () {
|
|
return this._bitField & 65535;
|
|
};
|
|
|
|
Promise.prototype._isFateSealed = function () {
|
|
return (this._bitField & 117506048) !== 0;
|
|
};
|
|
|
|
Promise.prototype._isFollowing = function () {
|
|
return (this._bitField & 67108864) === 67108864;
|
|
};
|
|
|
|
Promise.prototype._setLength = function (len) {
|
|
this._bitField = (this._bitField & -65536) |
|
|
(len & 65535);
|
|
};
|
|
|
|
Promise.prototype._setFulfilled = function () {
|
|
this._bitField = this._bitField | 33554432;
|
|
this._fireEvent("promiseFulfilled", this);
|
|
};
|
|
|
|
Promise.prototype._setRejected = function () {
|
|
this._bitField = this._bitField | 16777216;
|
|
this._fireEvent("promiseRejected", this);
|
|
};
|
|
|
|
Promise.prototype._setFollowing = function () {
|
|
this._bitField = this._bitField | 67108864;
|
|
this._fireEvent("promiseResolved", this);
|
|
};
|
|
|
|
Promise.prototype._setIsFinal = function () {
|
|
this._bitField = this._bitField | 4194304;
|
|
};
|
|
|
|
Promise.prototype._isFinal = function () {
|
|
return (this._bitField & 4194304) > 0;
|
|
};
|
|
|
|
Promise.prototype._unsetCancelled = function() {
|
|
this._bitField = this._bitField & (~65536);
|
|
};
|
|
|
|
Promise.prototype._setCancelled = function() {
|
|
this._bitField = this._bitField | 65536;
|
|
this._fireEvent("promiseCancelled", this);
|
|
};
|
|
|
|
Promise.prototype._setWillBeCancelled = function() {
|
|
this._bitField = this._bitField | 8388608;
|
|
};
|
|
|
|
Promise.prototype._setAsyncGuaranteed = function() {
|
|
if (async.hasCustomScheduler()) return;
|
|
this._bitField = this._bitField | 134217728;
|
|
};
|
|
|
|
Promise.prototype._receiverAt = function (index) {
|
|
var ret = index === 0 ? this._receiver0 : this[
|
|
index * 4 - 4 + 3];
|
|
if (ret === UNDEFINED_BINDING) {
|
|
return undefined;
|
|
} else if (ret === undefined && this._isBound()) {
|
|
return this._boundValue();
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
Promise.prototype._promiseAt = function (index) {
|
|
return this[
|
|
index * 4 - 4 + 2];
|
|
};
|
|
|
|
Promise.prototype._fulfillmentHandlerAt = function (index) {
|
|
return this[
|
|
index * 4 - 4 + 0];
|
|
};
|
|
|
|
Promise.prototype._rejectionHandlerAt = function (index) {
|
|
return this[
|
|
index * 4 - 4 + 1];
|
|
};
|
|
|
|
Promise.prototype._boundValue = function() {};
|
|
|
|
Promise.prototype._migrateCallback0 = function (follower) {
|
|
var bitField = follower._bitField;
|
|
var fulfill = follower._fulfillmentHandler0;
|
|
var reject = follower._rejectionHandler0;
|
|
var promise = follower._promise0;
|
|
var receiver = follower._receiverAt(0);
|
|
if (receiver === undefined) receiver = UNDEFINED_BINDING;
|
|
this._addCallbacks(fulfill, reject, promise, receiver, null);
|
|
};
|
|
|
|
Promise.prototype._migrateCallbackAt = function (follower, index) {
|
|
var fulfill = follower._fulfillmentHandlerAt(index);
|
|
var reject = follower._rejectionHandlerAt(index);
|
|
var promise = follower._promiseAt(index);
|
|
var receiver = follower._receiverAt(index);
|
|
if (receiver === undefined) receiver = UNDEFINED_BINDING;
|
|
this._addCallbacks(fulfill, reject, promise, receiver, null);
|
|
};
|
|
|
|
Promise.prototype._addCallbacks = function (
|
|
fulfill,
|
|
reject,
|
|
promise,
|
|
receiver,
|
|
domain
|
|
) {
|
|
var index = this._length();
|
|
|
|
if (index >= 65535 - 4) {
|
|
index = 0;
|
|
this._setLength(0);
|
|
}
|
|
|
|
if (index === 0) {
|
|
this._promise0 = promise;
|
|
this._receiver0 = receiver;
|
|
if (typeof fulfill === "function") {
|
|
this._fulfillmentHandler0 =
|
|
domain === null ? fulfill : util.domainBind(domain, fulfill);
|
|
}
|
|
if (typeof reject === "function") {
|
|
this._rejectionHandler0 =
|
|
domain === null ? reject : util.domainBind(domain, reject);
|
|
}
|
|
} else {
|
|
var base = index * 4 - 4;
|
|
this[base + 2] = promise;
|
|
this[base + 3] = receiver;
|
|
if (typeof fulfill === "function") {
|
|
this[base + 0] =
|
|
domain === null ? fulfill : util.domainBind(domain, fulfill);
|
|
}
|
|
if (typeof reject === "function") {
|
|
this[base + 1] =
|
|
domain === null ? reject : util.domainBind(domain, reject);
|
|
}
|
|
}
|
|
this._setLength(index + 1);
|
|
return index;
|
|
};
|
|
|
|
Promise.prototype._proxy = function (proxyable, arg) {
|
|
this._addCallbacks(undefined, undefined, arg, proxyable, null);
|
|
};
|
|
|
|
Promise.prototype._resolveCallback = function(value, shouldBind) {
|
|
if (((this._bitField & 117506048) !== 0)) return;
|
|
if (value === this)
|
|
return this._rejectCallback(makeSelfResolutionError(), false);
|
|
var maybePromise = tryConvertToPromise(value, this);
|
|
if (!(maybePromise instanceof Promise)) return this._fulfill(value);
|
|
|
|
if (shouldBind) this._propagateFrom(maybePromise, 2);
|
|
|
|
var promise = maybePromise._target();
|
|
|
|
if (promise === this) {
|
|
this._reject(makeSelfResolutionError());
|
|
return;
|
|
}
|
|
|
|
var bitField = promise._bitField;
|
|
if (((bitField & 50397184) === 0)) {
|
|
var len = this._length();
|
|
if (len > 0) promise._migrateCallback0(this);
|
|
for (var i = 1; i < len; ++i) {
|
|
promise._migrateCallbackAt(this, i);
|
|
}
|
|
this._setFollowing();
|
|
this._setLength(0);
|
|
this._setFollowee(promise);
|
|
} else if (((bitField & 33554432) !== 0)) {
|
|
this._fulfill(promise._value());
|
|
} else if (((bitField & 16777216) !== 0)) {
|
|
this._reject(promise._reason());
|
|
} else {
|
|
var reason = new CancellationError("late cancellation observer");
|
|
promise._attachExtraTrace(reason);
|
|
this._reject(reason);
|
|
}
|
|
};
|
|
|
|
Promise.prototype._rejectCallback =
|
|
function(reason, synchronous, ignoreNonErrorWarnings) {
|
|
var trace = util.ensureErrorObject(reason);
|
|
var hasStack = trace === reason;
|
|
if (!hasStack && !ignoreNonErrorWarnings && debug.warnings()) {
|
|
var message = "a promise was rejected with a non-error: " +
|
|
util.classString(reason);
|
|
this._warn(message, true);
|
|
}
|
|
this._attachExtraTrace(trace, synchronous ? hasStack : false);
|
|
this._reject(reason);
|
|
};
|
|
|
|
Promise.prototype._resolveFromExecutor = function (executor) {
|
|
var promise = this;
|
|
this._captureStackTrace();
|
|
this._pushContext();
|
|
var synchronous = true;
|
|
var r = this._execute(executor, function(value) {
|
|
promise._resolveCallback(value);
|
|
}, function (reason) {
|
|
promise._rejectCallback(reason, synchronous);
|
|
});
|
|
synchronous = false;
|
|
this._popContext();
|
|
|
|
if (r !== undefined) {
|
|
promise._rejectCallback(r, true);
|
|
}
|
|
};
|
|
|
|
Promise.prototype._settlePromiseFromHandler = function (
|
|
handler, receiver, value, promise
|
|
) {
|
|
var bitField = promise._bitField;
|
|
if (((bitField & 65536) !== 0)) return;
|
|
promise._pushContext();
|
|
var x;
|
|
if (receiver === APPLY) {
|
|
if (!value || typeof value.length !== "number") {
|
|
x = errorObj;
|
|
x.e = new TypeError("cannot .spread() a non-array: " +
|
|
util.classString(value));
|
|
} else {
|
|
x = tryCatch(handler).apply(this._boundValue(), value);
|
|
}
|
|
} else {
|
|
x = tryCatch(handler).call(receiver, value);
|
|
}
|
|
var promiseCreated = promise._popContext();
|
|
bitField = promise._bitField;
|
|
if (((bitField & 65536) !== 0)) return;
|
|
|
|
if (x === NEXT_FILTER) {
|
|
promise._reject(value);
|
|
} else if (x === errorObj) {
|
|
promise._rejectCallback(x.e, false);
|
|
} else {
|
|
debug.checkForgottenReturns(x, promiseCreated, "", promise, this);
|
|
promise._resolveCallback(x);
|
|
}
|
|
};
|
|
|
|
Promise.prototype._target = function() {
|
|
var ret = this;
|
|
while (ret._isFollowing()) ret = ret._followee();
|
|
return ret;
|
|
};
|
|
|
|
Promise.prototype._followee = function() {
|
|
return this._rejectionHandler0;
|
|
};
|
|
|
|
Promise.prototype._setFollowee = function(promise) {
|
|
this._rejectionHandler0 = promise;
|
|
};
|
|
|
|
Promise.prototype._settlePromise = function(promise, handler, receiver, value) {
|
|
var isPromise = promise instanceof Promise;
|
|
var bitField = this._bitField;
|
|
var asyncGuaranteed = ((bitField & 134217728) !== 0);
|
|
if (((bitField & 65536) !== 0)) {
|
|
if (isPromise) promise._invokeInternalOnCancel();
|
|
|
|
if (receiver instanceof PassThroughHandlerContext &&
|
|
receiver.isFinallyHandler()) {
|
|
receiver.cancelPromise = promise;
|
|
if (tryCatch(handler).call(receiver, value) === errorObj) {
|
|
promise._reject(errorObj.e);
|
|
}
|
|
} else if (handler === reflectHandler) {
|
|
promise._fulfill(reflectHandler.call(receiver));
|
|
} else if (receiver instanceof Proxyable) {
|
|
receiver._promiseCancelled(promise);
|
|
} else if (isPromise || promise instanceof PromiseArray) {
|
|
promise._cancel();
|
|
} else {
|
|
receiver.cancel();
|
|
}
|
|
} else if (typeof handler === "function") {
|
|
if (!isPromise) {
|
|
handler.call(receiver, value, promise);
|
|
} else {
|
|
if (asyncGuaranteed) promise._setAsyncGuaranteed();
|
|
this._settlePromiseFromHandler(handler, receiver, value, promise);
|
|
}
|
|
} else if (receiver instanceof Proxyable) {
|
|
if (!receiver._isResolved()) {
|
|
if (((bitField & 33554432) !== 0)) {
|
|
receiver._promiseFulfilled(value, promise);
|
|
} else {
|
|
receiver._promiseRejected(value, promise);
|
|
}
|
|
}
|
|
} else if (isPromise) {
|
|
if (asyncGuaranteed) promise._setAsyncGuaranteed();
|
|
if (((bitField & 33554432) !== 0)) {
|
|
promise._fulfill(value);
|
|
} else {
|
|
promise._reject(value);
|
|
}
|
|
}
|
|
};
|
|
|
|
Promise.prototype._settlePromiseLateCancellationObserver = function(ctx) {
|
|
var handler = ctx.handler;
|
|
var promise = ctx.promise;
|
|
var receiver = ctx.receiver;
|
|
var value = ctx.value;
|
|
if (typeof handler === "function") {
|
|
if (!(promise instanceof Promise)) {
|
|
handler.call(receiver, value, promise);
|
|
} else {
|
|
this._settlePromiseFromHandler(handler, receiver, value, promise);
|
|
}
|
|
} else if (promise instanceof Promise) {
|
|
promise._reject(value);
|
|
}
|
|
};
|
|
|
|
Promise.prototype._settlePromiseCtx = function(ctx) {
|
|
this._settlePromise(ctx.promise, ctx.handler, ctx.receiver, ctx.value);
|
|
};
|
|
|
|
Promise.prototype._settlePromise0 = function(handler, value, bitField) {
|
|
var promise = this._promise0;
|
|
var receiver = this._receiverAt(0);
|
|
this._promise0 = undefined;
|
|
this._receiver0 = undefined;
|
|
this._settlePromise(promise, handler, receiver, value);
|
|
};
|
|
|
|
Promise.prototype._clearCallbackDataAtIndex = function(index) {
|
|
var base = index * 4 - 4;
|
|
this[base + 2] =
|
|
this[base + 3] =
|
|
this[base + 0] =
|
|
this[base + 1] = undefined;
|
|
};
|
|
|
|
Promise.prototype._fulfill = function (value) {
|
|
var bitField = this._bitField;
|
|
if (((bitField & 117506048) >>> 16)) return;
|
|
if (value === this) {
|
|
var err = makeSelfResolutionError();
|
|
this._attachExtraTrace(err);
|
|
return this._reject(err);
|
|
}
|
|
this._setFulfilled();
|
|
this._rejectionHandler0 = value;
|
|
|
|
if ((bitField & 65535) > 0) {
|
|
if (((bitField & 134217728) !== 0)) {
|
|
this._settlePromises();
|
|
} else {
|
|
async.settlePromises(this);
|
|
}
|
|
}
|
|
};
|
|
|
|
Promise.prototype._reject = function (reason) {
|
|
var bitField = this._bitField;
|
|
if (((bitField & 117506048) >>> 16)) return;
|
|
this._setRejected();
|
|
this._fulfillmentHandler0 = reason;
|
|
|
|
if (this._isFinal()) {
|
|
return async.fatalError(reason, util.isNode);
|
|
}
|
|
|
|
if ((bitField & 65535) > 0) {
|
|
async.settlePromises(this);
|
|
} else {
|
|
this._ensurePossibleRejectionHandled();
|
|
}
|
|
};
|
|
|
|
Promise.prototype._fulfillPromises = function (len, value) {
|
|
for (var i = 1; i < len; i++) {
|
|
var handler = this._fulfillmentHandlerAt(i);
|
|
var promise = this._promiseAt(i);
|
|
var receiver = this._receiverAt(i);
|
|
this._clearCallbackDataAtIndex(i);
|
|
this._settlePromise(promise, handler, receiver, value);
|
|
}
|
|
};
|
|
|
|
Promise.prototype._rejectPromises = function (len, reason) {
|
|
for (var i = 1; i < len; i++) {
|
|
var handler = this._rejectionHandlerAt(i);
|
|
var promise = this._promiseAt(i);
|
|
var receiver = this._receiverAt(i);
|
|
this._clearCallbackDataAtIndex(i);
|
|
this._settlePromise(promise, handler, receiver, reason);
|
|
}
|
|
};
|
|
|
|
Promise.prototype._settlePromises = function () {
|
|
var bitField = this._bitField;
|
|
var len = (bitField & 65535);
|
|
|
|
if (len > 0) {
|
|
if (((bitField & 16842752) !== 0)) {
|
|
var reason = this._fulfillmentHandler0;
|
|
this._settlePromise0(this._rejectionHandler0, reason, bitField);
|
|
this._rejectPromises(len, reason);
|
|
} else {
|
|
var value = this._rejectionHandler0;
|
|
this._settlePromise0(this._fulfillmentHandler0, value, bitField);
|
|
this._fulfillPromises(len, value);
|
|
}
|
|
this._setLength(0);
|
|
}
|
|
this._clearCancellationData();
|
|
};
|
|
|
|
Promise.prototype._settledValue = function() {
|
|
var bitField = this._bitField;
|
|
if (((bitField & 33554432) !== 0)) {
|
|
return this._rejectionHandler0;
|
|
} else if (((bitField & 16777216) !== 0)) {
|
|
return this._fulfillmentHandler0;
|
|
}
|
|
};
|
|
|
|
function deferResolve(v) {this.promise._resolveCallback(v);}
|
|
function deferReject(v) {this.promise._rejectCallback(v, false);}
|
|
|
|
Promise.defer = Promise.pending = function() {
|
|
debug.deprecated("Promise.defer", "new Promise");
|
|
var promise = new Promise(INTERNAL);
|
|
return {
|
|
promise: promise,
|
|
resolve: deferResolve,
|
|
reject: deferReject
|
|
};
|
|
};
|
|
|
|
util.notEnumerableProp(Promise,
|
|
"_makeSelfResolutionError",
|
|
makeSelfResolutionError);
|
|
|
|
_dereq_("./method")(Promise, INTERNAL, tryConvertToPromise, apiRejection,
|
|
debug);
|
|
_dereq_("./bind")(Promise, INTERNAL, tryConvertToPromise, debug);
|
|
_dereq_("./cancel")(Promise, PromiseArray, apiRejection, debug);
|
|
_dereq_("./direct_resolve")(Promise);
|
|
_dereq_("./synchronous_inspection")(Promise);
|
|
_dereq_("./join")(
|
|
Promise, PromiseArray, tryConvertToPromise, INTERNAL, async, getDomain);
|
|
Promise.Promise = Promise;
|
|
Promise.version = "3.4.7";
|
|
_dereq_('./map.js')(Promise, PromiseArray, apiRejection, tryConvertToPromise, INTERNAL, debug);
|
|
_dereq_('./call_get.js')(Promise);
|
|
_dereq_('./using.js')(Promise, apiRejection, tryConvertToPromise, createContext, INTERNAL, debug);
|
|
_dereq_('./timers.js')(Promise, INTERNAL, debug);
|
|
_dereq_('./generators.js')(Promise, apiRejection, INTERNAL, tryConvertToPromise, Proxyable, debug);
|
|
_dereq_('./nodeify.js')(Promise);
|
|
_dereq_('./promisify.js')(Promise, INTERNAL);
|
|
_dereq_('./props.js')(Promise, PromiseArray, tryConvertToPromise, apiRejection);
|
|
_dereq_('./race.js')(Promise, INTERNAL, tryConvertToPromise, apiRejection);
|
|
_dereq_('./reduce.js')(Promise, PromiseArray, apiRejection, tryConvertToPromise, INTERNAL, debug);
|
|
_dereq_('./settle.js')(Promise, PromiseArray, debug);
|
|
_dereq_('./some.js')(Promise, PromiseArray, apiRejection);
|
|
_dereq_('./filter.js')(Promise, INTERNAL);
|
|
_dereq_('./each.js')(Promise, INTERNAL);
|
|
_dereq_('./any.js')(Promise);
|
|
|
|
util.toFastProperties(Promise);
|
|
util.toFastProperties(Promise.prototype);
|
|
function fillTypes(value) {
|
|
var p = new Promise(INTERNAL);
|
|
p._fulfillmentHandler0 = value;
|
|
p._rejectionHandler0 = value;
|
|
p._promise0 = value;
|
|
p._receiver0 = value;
|
|
}
|
|
// Complete slack tracking, opt out of field-type tracking and
|
|
// stabilize map
|
|
fillTypes({a: 1});
|
|
fillTypes({b: 2});
|
|
fillTypes({c: 3});
|
|
fillTypes(1);
|
|
fillTypes(function(){});
|
|
fillTypes(undefined);
|
|
fillTypes(false);
|
|
fillTypes(new Promise(INTERNAL));
|
|
debug.setBounds(Async.firstLineError, util.lastLineError);
|
|
return Promise;
|
|
|
|
};
|
|
|
|
},{"./any.js":1,"./async":2,"./bind":3,"./call_get.js":5,"./cancel":6,"./catch_filter":7,"./context":8,"./debuggability":9,"./direct_resolve":10,"./each.js":11,"./errors":12,"./es5":13,"./filter.js":14,"./finally":15,"./generators.js":16,"./join":17,"./map.js":18,"./method":19,"./nodeback":20,"./nodeify.js":21,"./promise_array":23,"./promisify.js":24,"./props.js":25,"./race.js":27,"./reduce.js":28,"./settle.js":30,"./some.js":31,"./synchronous_inspection":32,"./thenables":33,"./timers.js":34,"./using.js":35,"./util":36}],23:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise, INTERNAL, tryConvertToPromise,
|
|
apiRejection, Proxyable) {
|
|
var util = _dereq_("./util");
|
|
var isArray = util.isArray;
|
|
|
|
function toResolutionValue(val) {
|
|
switch(val) {
|
|
case -2: return [];
|
|
case -3: return {};
|
|
}
|
|
}
|
|
|
|
function PromiseArray(values) {
|
|
var promise = this._promise = new Promise(INTERNAL);
|
|
if (values instanceof Promise) {
|
|
promise._propagateFrom(values, 3);
|
|
}
|
|
promise._setOnCancel(this);
|
|
this._values = values;
|
|
this._length = 0;
|
|
this._totalResolved = 0;
|
|
this._init(undefined, -2);
|
|
}
|
|
util.inherits(PromiseArray, Proxyable);
|
|
|
|
PromiseArray.prototype.length = function () {
|
|
return this._length;
|
|
};
|
|
|
|
PromiseArray.prototype.promise = function () {
|
|
return this._promise;
|
|
};
|
|
|
|
PromiseArray.prototype._init = function init(_, resolveValueIfEmpty) {
|
|
var values = tryConvertToPromise(this._values, this._promise);
|
|
if (values instanceof Promise) {
|
|
values = values._target();
|
|
var bitField = values._bitField;
|
|
;
|
|
this._values = values;
|
|
|
|
if (((bitField & 50397184) === 0)) {
|
|
this._promise._setAsyncGuaranteed();
|
|
return values._then(
|
|
init,
|
|
this._reject,
|
|
undefined,
|
|
this,
|
|
resolveValueIfEmpty
|
|
);
|
|
} else if (((bitField & 33554432) !== 0)) {
|
|
values = values._value();
|
|
} else if (((bitField & 16777216) !== 0)) {
|
|
return this._reject(values._reason());
|
|
} else {
|
|
return this._cancel();
|
|
}
|
|
}
|
|
values = util.asArray(values);
|
|
if (values === null) {
|
|
var err = apiRejection(
|
|
"expecting an array or an iterable object but got " + util.classString(values)).reason();
|
|
this._promise._rejectCallback(err, false);
|
|
return;
|
|
}
|
|
|
|
if (values.length === 0) {
|
|
if (resolveValueIfEmpty === -5) {
|
|
this._resolveEmptyArray();
|
|
}
|
|
else {
|
|
this._resolve(toResolutionValue(resolveValueIfEmpty));
|
|
}
|
|
return;
|
|
}
|
|
this._iterate(values);
|
|
};
|
|
|
|
PromiseArray.prototype._iterate = function(values) {
|
|
var len = this.getActualLength(values.length);
|
|
this._length = len;
|
|
this._values = this.shouldCopyValues() ? new Array(len) : this._values;
|
|
var result = this._promise;
|
|
var isResolved = false;
|
|
var bitField = null;
|
|
for (var i = 0; i < len; ++i) {
|
|
var maybePromise = tryConvertToPromise(values[i], result);
|
|
|
|
if (maybePromise instanceof Promise) {
|
|
maybePromise = maybePromise._target();
|
|
bitField = maybePromise._bitField;
|
|
} else {
|
|
bitField = null;
|
|
}
|
|
|
|
if (isResolved) {
|
|
if (bitField !== null) {
|
|
maybePromise.suppressUnhandledRejections();
|
|
}
|
|
} else if (bitField !== null) {
|
|
if (((bitField & 50397184) === 0)) {
|
|
maybePromise._proxy(this, i);
|
|
this._values[i] = maybePromise;
|
|
} else if (((bitField & 33554432) !== 0)) {
|
|
isResolved = this._promiseFulfilled(maybePromise._value(), i);
|
|
} else if (((bitField & 16777216) !== 0)) {
|
|
isResolved = this._promiseRejected(maybePromise._reason(), i);
|
|
} else {
|
|
isResolved = this._promiseCancelled(i);
|
|
}
|
|
} else {
|
|
isResolved = this._promiseFulfilled(maybePromise, i);
|
|
}
|
|
}
|
|
if (!isResolved) result._setAsyncGuaranteed();
|
|
};
|
|
|
|
PromiseArray.prototype._isResolved = function () {
|
|
return this._values === null;
|
|
};
|
|
|
|
PromiseArray.prototype._resolve = function (value) {
|
|
this._values = null;
|
|
this._promise._fulfill(value);
|
|
};
|
|
|
|
PromiseArray.prototype._cancel = function() {
|
|
if (this._isResolved() || !this._promise._isCancellable()) return;
|
|
this._values = null;
|
|
this._promise._cancel();
|
|
};
|
|
|
|
PromiseArray.prototype._reject = function (reason) {
|
|
this._values = null;
|
|
this._promise._rejectCallback(reason, false);
|
|
};
|
|
|
|
PromiseArray.prototype._promiseFulfilled = function (value, index) {
|
|
this._values[index] = value;
|
|
var totalResolved = ++this._totalResolved;
|
|
if (totalResolved >= this._length) {
|
|
this._resolve(this._values);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
PromiseArray.prototype._promiseCancelled = function() {
|
|
this._cancel();
|
|
return true;
|
|
};
|
|
|
|
PromiseArray.prototype._promiseRejected = function (reason) {
|
|
this._totalResolved++;
|
|
this._reject(reason);
|
|
return true;
|
|
};
|
|
|
|
PromiseArray.prototype._resultCancelled = function() {
|
|
if (this._isResolved()) return;
|
|
var values = this._values;
|
|
this._cancel();
|
|
if (values instanceof Promise) {
|
|
values.cancel();
|
|
} else {
|
|
for (var i = 0; i < values.length; ++i) {
|
|
if (values[i] instanceof Promise) {
|
|
values[i].cancel();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
PromiseArray.prototype.shouldCopyValues = function () {
|
|
return true;
|
|
};
|
|
|
|
PromiseArray.prototype.getActualLength = function (len) {
|
|
return len;
|
|
};
|
|
|
|
return PromiseArray;
|
|
};
|
|
|
|
},{"./util":36}],24:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise, INTERNAL) {
|
|
var THIS = {};
|
|
var util = _dereq_("./util");
|
|
var nodebackForPromise = _dereq_("./nodeback");
|
|
var withAppended = util.withAppended;
|
|
var maybeWrapAsError = util.maybeWrapAsError;
|
|
var canEvaluate = util.canEvaluate;
|
|
var TypeError = _dereq_("./errors").TypeError;
|
|
var defaultSuffix = "Async";
|
|
var defaultPromisified = {__isPromisified__: true};
|
|
var noCopyProps = [
|
|
"arity", "length",
|
|
"name",
|
|
"arguments",
|
|
"caller",
|
|
"callee",
|
|
"prototype",
|
|
"__isPromisified__"
|
|
];
|
|
var noCopyPropsPattern = new RegExp("^(?:" + noCopyProps.join("|") + ")$");
|
|
|
|
var defaultFilter = function(name) {
|
|
return util.isIdentifier(name) &&
|
|
name.charAt(0) !== "_" &&
|
|
name !== "constructor";
|
|
};
|
|
|
|
function propsFilter(key) {
|
|
return !noCopyPropsPattern.test(key);
|
|
}
|
|
|
|
function isPromisified(fn) {
|
|
try {
|
|
return fn.__isPromisified__ === true;
|
|
}
|
|
catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function hasPromisified(obj, key, suffix) {
|
|
var val = util.getDataPropertyOrDefault(obj, key + suffix,
|
|
defaultPromisified);
|
|
return val ? isPromisified(val) : false;
|
|
}
|
|
function checkValid(ret, suffix, suffixRegexp) {
|
|
for (var i = 0; i < ret.length; i += 2) {
|
|
var key = ret[i];
|
|
if (suffixRegexp.test(key)) {
|
|
var keyWithoutAsyncSuffix = key.replace(suffixRegexp, "");
|
|
for (var j = 0; j < ret.length; j += 2) {
|
|
if (ret[j] === keyWithoutAsyncSuffix) {
|
|
throw new TypeError("Cannot promisify an API that has normal methods with '%s'-suffix\u000a\u000a See http://goo.gl/MqrFmX\u000a"
|
|
.replace("%s", suffix));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function promisifiableMethods(obj, suffix, suffixRegexp, filter) {
|
|
var keys = util.inheritedDataKeys(obj);
|
|
var ret = [];
|
|
for (var i = 0; i < keys.length; ++i) {
|
|
var key = keys[i];
|
|
var value = obj[key];
|
|
var passesDefaultFilter = filter === defaultFilter
|
|
? true : defaultFilter(key, value, obj);
|
|
if (typeof value === "function" &&
|
|
!isPromisified(value) &&
|
|
!hasPromisified(obj, key, suffix) &&
|
|
filter(key, value, obj, passesDefaultFilter)) {
|
|
ret.push(key, value);
|
|
}
|
|
}
|
|
checkValid(ret, suffix, suffixRegexp);
|
|
return ret;
|
|
}
|
|
|
|
var escapeIdentRegex = function(str) {
|
|
return str.replace(/([$])/, "\\$");
|
|
};
|
|
|
|
var makeNodePromisifiedEval;
|
|
if (!true) {
|
|
var switchCaseArgumentOrder = function(likelyArgumentCount) {
|
|
var ret = [likelyArgumentCount];
|
|
var min = Math.max(0, likelyArgumentCount - 1 - 3);
|
|
for(var i = likelyArgumentCount - 1; i >= min; --i) {
|
|
ret.push(i);
|
|
}
|
|
for(var i = likelyArgumentCount + 1; i <= 3; ++i) {
|
|
ret.push(i);
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
var argumentSequence = function(argumentCount) {
|
|
return util.filledRange(argumentCount, "_arg", "");
|
|
};
|
|
|
|
var parameterDeclaration = function(parameterCount) {
|
|
return util.filledRange(
|
|
Math.max(parameterCount, 3), "_arg", "");
|
|
};
|
|
|
|
var parameterCount = function(fn) {
|
|
if (typeof fn.length === "number") {
|
|
return Math.max(Math.min(fn.length, 1023 + 1), 0);
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
makeNodePromisifiedEval =
|
|
function(callback, receiver, originalName, fn, _, multiArgs) {
|
|
var newParameterCount = Math.max(0, parameterCount(fn) - 1);
|
|
var argumentOrder = switchCaseArgumentOrder(newParameterCount);
|
|
var shouldProxyThis = typeof callback === "string" || receiver === THIS;
|
|
|
|
function generateCallForArgumentCount(count) {
|
|
var args = argumentSequence(count).join(", ");
|
|
var comma = count > 0 ? ", " : "";
|
|
var ret;
|
|
if (shouldProxyThis) {
|
|
ret = "ret = callback.call(this, {{args}}, nodeback); break;\n";
|
|
} else {
|
|
ret = receiver === undefined
|
|
? "ret = callback({{args}}, nodeback); break;\n"
|
|
: "ret = callback.call(receiver, {{args}}, nodeback); break;\n";
|
|
}
|
|
return ret.replace("{{args}}", args).replace(", ", comma);
|
|
}
|
|
|
|
function generateArgumentSwitchCase() {
|
|
var ret = "";
|
|
for (var i = 0; i < argumentOrder.length; ++i) {
|
|
ret += "case " + argumentOrder[i] +":" +
|
|
generateCallForArgumentCount(argumentOrder[i]);
|
|
}
|
|
|
|
ret += " \n\
|
|
default: \n\
|
|
var args = new Array(len + 1); \n\
|
|
var i = 0; \n\
|
|
for (var i = 0; i < len; ++i) { \n\
|
|
args[i] = arguments[i]; \n\
|
|
} \n\
|
|
args[i] = nodeback; \n\
|
|
[CodeForCall] \n\
|
|
break; \n\
|
|
".replace("[CodeForCall]", (shouldProxyThis
|
|
? "ret = callback.apply(this, args);\n"
|
|
: "ret = callback.apply(receiver, args);\n"));
|
|
return ret;
|
|
}
|
|
|
|
var getFunctionCode = typeof callback === "string"
|
|
? ("this != null ? this['"+callback+"'] : fn")
|
|
: "fn";
|
|
var body = "'use strict'; \n\
|
|
var ret = function (Parameters) { \n\
|
|
'use strict'; \n\
|
|
var len = arguments.length; \n\
|
|
var promise = new Promise(INTERNAL); \n\
|
|
promise._captureStackTrace(); \n\
|
|
var nodeback = nodebackForPromise(promise, " + multiArgs + "); \n\
|
|
var ret; \n\
|
|
var callback = tryCatch([GetFunctionCode]); \n\
|
|
switch(len) { \n\
|
|
[CodeForSwitchCase] \n\
|
|
} \n\
|
|
if (ret === errorObj) { \n\
|
|
promise._rejectCallback(maybeWrapAsError(ret.e), true, true);\n\
|
|
} \n\
|
|
if (!promise._isFateSealed()) promise._setAsyncGuaranteed(); \n\
|
|
return promise; \n\
|
|
}; \n\
|
|
notEnumerableProp(ret, '__isPromisified__', true); \n\
|
|
return ret; \n\
|
|
".replace("[CodeForSwitchCase]", generateArgumentSwitchCase())
|
|
.replace("[GetFunctionCode]", getFunctionCode);
|
|
body = body.replace("Parameters", parameterDeclaration(newParameterCount));
|
|
return new Function("Promise",
|
|
"fn",
|
|
"receiver",
|
|
"withAppended",
|
|
"maybeWrapAsError",
|
|
"nodebackForPromise",
|
|
"tryCatch",
|
|
"errorObj",
|
|
"notEnumerableProp",
|
|
"INTERNAL",
|
|
body)(
|
|
Promise,
|
|
fn,
|
|
receiver,
|
|
withAppended,
|
|
maybeWrapAsError,
|
|
nodebackForPromise,
|
|
util.tryCatch,
|
|
util.errorObj,
|
|
util.notEnumerableProp,
|
|
INTERNAL);
|
|
};
|
|
}
|
|
|
|
function makeNodePromisifiedClosure(callback, receiver, _, fn, __, multiArgs) {
|
|
var defaultThis = (function() {return this;})();
|
|
var method = callback;
|
|
if (typeof method === "string") {
|
|
callback = fn;
|
|
}
|
|
function promisified() {
|
|
var _receiver = receiver;
|
|
if (receiver === THIS) _receiver = this;
|
|
var promise = new Promise(INTERNAL);
|
|
promise._captureStackTrace();
|
|
var cb = typeof method === "string" && this !== defaultThis
|
|
? this[method] : callback;
|
|
var fn = nodebackForPromise(promise, multiArgs);
|
|
try {
|
|
cb.apply(_receiver, withAppended(arguments, fn));
|
|
} catch(e) {
|
|
promise._rejectCallback(maybeWrapAsError(e), true, true);
|
|
}
|
|
if (!promise._isFateSealed()) promise._setAsyncGuaranteed();
|
|
return promise;
|
|
}
|
|
util.notEnumerableProp(promisified, "__isPromisified__", true);
|
|
return promisified;
|
|
}
|
|
|
|
var makeNodePromisified = canEvaluate
|
|
? makeNodePromisifiedEval
|
|
: makeNodePromisifiedClosure;
|
|
|
|
function promisifyAll(obj, suffix, filter, promisifier, multiArgs) {
|
|
var suffixRegexp = new RegExp(escapeIdentRegex(suffix) + "$");
|
|
var methods =
|
|
promisifiableMethods(obj, suffix, suffixRegexp, filter);
|
|
|
|
for (var i = 0, len = methods.length; i < len; i+= 2) {
|
|
var key = methods[i];
|
|
var fn = methods[i+1];
|
|
var promisifiedKey = key + suffix;
|
|
if (promisifier === makeNodePromisified) {
|
|
obj[promisifiedKey] =
|
|
makeNodePromisified(key, THIS, key, fn, suffix, multiArgs);
|
|
} else {
|
|
var promisified = promisifier(fn, function() {
|
|
return makeNodePromisified(key, THIS, key,
|
|
fn, suffix, multiArgs);
|
|
});
|
|
util.notEnumerableProp(promisified, "__isPromisified__", true);
|
|
obj[promisifiedKey] = promisified;
|
|
}
|
|
}
|
|
util.toFastProperties(obj);
|
|
return obj;
|
|
}
|
|
|
|
function promisify(callback, receiver, multiArgs) {
|
|
return makeNodePromisified(callback, receiver, undefined,
|
|
callback, null, multiArgs);
|
|
}
|
|
|
|
Promise.promisify = function (fn, options) {
|
|
if (typeof fn !== "function") {
|
|
throw new TypeError("expecting a function but got " + util.classString(fn));
|
|
}
|
|
if (isPromisified(fn)) {
|
|
return fn;
|
|
}
|
|
options = Object(options);
|
|
var receiver = options.context === undefined ? THIS : options.context;
|
|
var multiArgs = !!options.multiArgs;
|
|
var ret = promisify(fn, receiver, multiArgs);
|
|
util.copyDescriptors(fn, ret, propsFilter);
|
|
return ret;
|
|
};
|
|
|
|
Promise.promisifyAll = function (target, options) {
|
|
if (typeof target !== "function" && typeof target !== "object") {
|
|
throw new TypeError("the target of promisifyAll must be an object or a function\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
}
|
|
options = Object(options);
|
|
var multiArgs = !!options.multiArgs;
|
|
var suffix = options.suffix;
|
|
if (typeof suffix !== "string") suffix = defaultSuffix;
|
|
var filter = options.filter;
|
|
if (typeof filter !== "function") filter = defaultFilter;
|
|
var promisifier = options.promisifier;
|
|
if (typeof promisifier !== "function") promisifier = makeNodePromisified;
|
|
|
|
if (!util.isIdentifier(suffix)) {
|
|
throw new RangeError("suffix must be a valid identifier\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
}
|
|
|
|
var keys = util.inheritedDataKeys(target);
|
|
for (var i = 0; i < keys.length; ++i) {
|
|
var value = target[keys[i]];
|
|
if (keys[i] !== "constructor" &&
|
|
util.isClass(value)) {
|
|
promisifyAll(value.prototype, suffix, filter, promisifier,
|
|
multiArgs);
|
|
promisifyAll(value, suffix, filter, promisifier, multiArgs);
|
|
}
|
|
}
|
|
|
|
return promisifyAll(target, suffix, filter, promisifier, multiArgs);
|
|
};
|
|
};
|
|
|
|
|
|
},{"./errors":12,"./nodeback":20,"./util":36}],25:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(
|
|
Promise, PromiseArray, tryConvertToPromise, apiRejection) {
|
|
var util = _dereq_("./util");
|
|
var isObject = util.isObject;
|
|
var es5 = _dereq_("./es5");
|
|
var Es6Map;
|
|
if (typeof Map === "function") Es6Map = Map;
|
|
|
|
var mapToEntries = (function() {
|
|
var index = 0;
|
|
var size = 0;
|
|
|
|
function extractEntry(value, key) {
|
|
this[index] = value;
|
|
this[index + size] = key;
|
|
index++;
|
|
}
|
|
|
|
return function mapToEntries(map) {
|
|
size = map.size;
|
|
index = 0;
|
|
var ret = new Array(map.size * 2);
|
|
map.forEach(extractEntry, ret);
|
|
return ret;
|
|
};
|
|
})();
|
|
|
|
var entriesToMap = function(entries) {
|
|
var ret = new Es6Map();
|
|
var length = entries.length / 2 | 0;
|
|
for (var i = 0; i < length; ++i) {
|
|
var key = entries[length + i];
|
|
var value = entries[i];
|
|
ret.set(key, value);
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
function PropertiesPromiseArray(obj) {
|
|
var isMap = false;
|
|
var entries;
|
|
if (Es6Map !== undefined && obj instanceof Es6Map) {
|
|
entries = mapToEntries(obj);
|
|
isMap = true;
|
|
} else {
|
|
var keys = es5.keys(obj);
|
|
var len = keys.length;
|
|
entries = new Array(len * 2);
|
|
for (var i = 0; i < len; ++i) {
|
|
var key = keys[i];
|
|
entries[i] = obj[key];
|
|
entries[i + len] = key;
|
|
}
|
|
}
|
|
this.constructor$(entries);
|
|
this._isMap = isMap;
|
|
this._init$(undefined, -3);
|
|
}
|
|
util.inherits(PropertiesPromiseArray, PromiseArray);
|
|
|
|
PropertiesPromiseArray.prototype._init = function () {};
|
|
|
|
PropertiesPromiseArray.prototype._promiseFulfilled = function (value, index) {
|
|
this._values[index] = value;
|
|
var totalResolved = ++this._totalResolved;
|
|
if (totalResolved >= this._length) {
|
|
var val;
|
|
if (this._isMap) {
|
|
val = entriesToMap(this._values);
|
|
} else {
|
|
val = {};
|
|
var keyOffset = this.length();
|
|
for (var i = 0, len = this.length(); i < len; ++i) {
|
|
val[this._values[i + keyOffset]] = this._values[i];
|
|
}
|
|
}
|
|
this._resolve(val);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
PropertiesPromiseArray.prototype.shouldCopyValues = function () {
|
|
return false;
|
|
};
|
|
|
|
PropertiesPromiseArray.prototype.getActualLength = function (len) {
|
|
return len >> 1;
|
|
};
|
|
|
|
function props(promises) {
|
|
var ret;
|
|
var castValue = tryConvertToPromise(promises);
|
|
|
|
if (!isObject(castValue)) {
|
|
return apiRejection("cannot await properties of a non-object\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
} else if (castValue instanceof Promise) {
|
|
ret = castValue._then(
|
|
Promise.props, undefined, undefined, undefined, undefined);
|
|
} else {
|
|
ret = new PropertiesPromiseArray(castValue).promise();
|
|
}
|
|
|
|
if (castValue instanceof Promise) {
|
|
ret._propagateFrom(castValue, 2);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Promise.prototype.props = function () {
|
|
return props(this);
|
|
};
|
|
|
|
Promise.props = function (promises) {
|
|
return props(promises);
|
|
};
|
|
};
|
|
|
|
},{"./es5":13,"./util":36}],26:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
function arrayMove(src, srcIndex, dst, dstIndex, len) {
|
|
for (var j = 0; j < len; ++j) {
|
|
dst[j + dstIndex] = src[j + srcIndex];
|
|
src[j + srcIndex] = void 0;
|
|
}
|
|
}
|
|
|
|
function Queue(capacity) {
|
|
this._capacity = capacity;
|
|
this._length = 0;
|
|
this._front = 0;
|
|
}
|
|
|
|
Queue.prototype._willBeOverCapacity = function (size) {
|
|
return this._capacity < size;
|
|
};
|
|
|
|
Queue.prototype._pushOne = function (arg) {
|
|
var length = this.length();
|
|
this._checkCapacity(length + 1);
|
|
var i = (this._front + length) & (this._capacity - 1);
|
|
this[i] = arg;
|
|
this._length = length + 1;
|
|
};
|
|
|
|
Queue.prototype.push = function (fn, receiver, arg) {
|
|
var length = this.length() + 3;
|
|
if (this._willBeOverCapacity(length)) {
|
|
this._pushOne(fn);
|
|
this._pushOne(receiver);
|
|
this._pushOne(arg);
|
|
return;
|
|
}
|
|
var j = this._front + length - 3;
|
|
this._checkCapacity(length);
|
|
var wrapMask = this._capacity - 1;
|
|
this[(j + 0) & wrapMask] = fn;
|
|
this[(j + 1) & wrapMask] = receiver;
|
|
this[(j + 2) & wrapMask] = arg;
|
|
this._length = length;
|
|
};
|
|
|
|
Queue.prototype.shift = function () {
|
|
var front = this._front,
|
|
ret = this[front];
|
|
|
|
this[front] = undefined;
|
|
this._front = (front + 1) & (this._capacity - 1);
|
|
this._length--;
|
|
return ret;
|
|
};
|
|
|
|
Queue.prototype.length = function () {
|
|
return this._length;
|
|
};
|
|
|
|
Queue.prototype._checkCapacity = function (size) {
|
|
if (this._capacity < size) {
|
|
this._resizeTo(this._capacity << 1);
|
|
}
|
|
};
|
|
|
|
Queue.prototype._resizeTo = function (capacity) {
|
|
var oldCapacity = this._capacity;
|
|
this._capacity = capacity;
|
|
var front = this._front;
|
|
var length = this._length;
|
|
var moveItemsCount = (front + length) & (oldCapacity - 1);
|
|
arrayMove(this, 0, this, oldCapacity, moveItemsCount);
|
|
};
|
|
|
|
module.exports = Queue;
|
|
|
|
},{}],27:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(
|
|
Promise, INTERNAL, tryConvertToPromise, apiRejection) {
|
|
var util = _dereq_("./util");
|
|
|
|
var raceLater = function (promise) {
|
|
return promise.then(function(array) {
|
|
return race(array, promise);
|
|
});
|
|
};
|
|
|
|
function race(promises, parent) {
|
|
var maybePromise = tryConvertToPromise(promises);
|
|
|
|
if (maybePromise instanceof Promise) {
|
|
return raceLater(maybePromise);
|
|
} else {
|
|
promises = util.asArray(promises);
|
|
if (promises === null)
|
|
return apiRejection("expecting an array or an iterable object but got " + util.classString(promises));
|
|
}
|
|
|
|
var ret = new Promise(INTERNAL);
|
|
if (parent !== undefined) {
|
|
ret._propagateFrom(parent, 3);
|
|
}
|
|
var fulfill = ret._fulfill;
|
|
var reject = ret._reject;
|
|
for (var i = 0, len = promises.length; i < len; ++i) {
|
|
var val = promises[i];
|
|
|
|
if (val === undefined && !(i in promises)) {
|
|
continue;
|
|
}
|
|
|
|
Promise.cast(val)._then(fulfill, reject, undefined, ret, null);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Promise.race = function (promises) {
|
|
return race(promises, undefined);
|
|
};
|
|
|
|
Promise.prototype.race = function () {
|
|
return race(this, undefined);
|
|
};
|
|
|
|
};
|
|
|
|
},{"./util":36}],28:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise,
|
|
PromiseArray,
|
|
apiRejection,
|
|
tryConvertToPromise,
|
|
INTERNAL,
|
|
debug) {
|
|
var getDomain = Promise._getDomain;
|
|
var util = _dereq_("./util");
|
|
var tryCatch = util.tryCatch;
|
|
|
|
function ReductionPromiseArray(promises, fn, initialValue, _each) {
|
|
this.constructor$(promises);
|
|
var domain = getDomain();
|
|
this._fn = domain === null ? fn : util.domainBind(domain, fn);
|
|
if (initialValue !== undefined) {
|
|
initialValue = Promise.resolve(initialValue);
|
|
initialValue._attachCancellationCallback(this);
|
|
}
|
|
this._initialValue = initialValue;
|
|
this._currentCancellable = null;
|
|
if(_each === INTERNAL) {
|
|
this._eachValues = Array(this._length);
|
|
} else if (_each === 0) {
|
|
this._eachValues = null;
|
|
} else {
|
|
this._eachValues = undefined;
|
|
}
|
|
this._promise._captureStackTrace();
|
|
this._init$(undefined, -5);
|
|
}
|
|
util.inherits(ReductionPromiseArray, PromiseArray);
|
|
|
|
ReductionPromiseArray.prototype._gotAccum = function(accum) {
|
|
if (this._eachValues !== undefined &&
|
|
this._eachValues !== null &&
|
|
accum !== INTERNAL) {
|
|
this._eachValues.push(accum);
|
|
}
|
|
};
|
|
|
|
ReductionPromiseArray.prototype._eachComplete = function(value) {
|
|
if (this._eachValues !== null) {
|
|
this._eachValues.push(value);
|
|
}
|
|
return this._eachValues;
|
|
};
|
|
|
|
ReductionPromiseArray.prototype._init = function() {};
|
|
|
|
ReductionPromiseArray.prototype._resolveEmptyArray = function() {
|
|
this._resolve(this._eachValues !== undefined ? this._eachValues
|
|
: this._initialValue);
|
|
};
|
|
|
|
ReductionPromiseArray.prototype.shouldCopyValues = function () {
|
|
return false;
|
|
};
|
|
|
|
ReductionPromiseArray.prototype._resolve = function(value) {
|
|
this._promise._resolveCallback(value);
|
|
this._values = null;
|
|
};
|
|
|
|
ReductionPromiseArray.prototype._resultCancelled = function(sender) {
|
|
if (sender === this._initialValue) return this._cancel();
|
|
if (this._isResolved()) return;
|
|
this._resultCancelled$();
|
|
if (this._currentCancellable instanceof Promise) {
|
|
this._currentCancellable.cancel();
|
|
}
|
|
if (this._initialValue instanceof Promise) {
|
|
this._initialValue.cancel();
|
|
}
|
|
};
|
|
|
|
ReductionPromiseArray.prototype._iterate = function (values) {
|
|
this._values = values;
|
|
var value;
|
|
var i;
|
|
var length = values.length;
|
|
if (this._initialValue !== undefined) {
|
|
value = this._initialValue;
|
|
i = 0;
|
|
} else {
|
|
value = Promise.resolve(values[0]);
|
|
i = 1;
|
|
}
|
|
|
|
this._currentCancellable = value;
|
|
|
|
if (!value.isRejected()) {
|
|
for (; i < length; ++i) {
|
|
var ctx = {
|
|
accum: null,
|
|
value: values[i],
|
|
index: i,
|
|
length: length,
|
|
array: this
|
|
};
|
|
value = value._then(gotAccum, undefined, undefined, ctx, undefined);
|
|
}
|
|
}
|
|
|
|
if (this._eachValues !== undefined) {
|
|
value = value
|
|
._then(this._eachComplete, undefined, undefined, this, undefined);
|
|
}
|
|
value._then(completed, completed, undefined, value, this);
|
|
};
|
|
|
|
Promise.prototype.reduce = function (fn, initialValue) {
|
|
return reduce(this, fn, initialValue, null);
|
|
};
|
|
|
|
Promise.reduce = function (promises, fn, initialValue, _each) {
|
|
return reduce(promises, fn, initialValue, _each);
|
|
};
|
|
|
|
function completed(valueOrReason, array) {
|
|
if (this.isFulfilled()) {
|
|
array._resolve(valueOrReason);
|
|
} else {
|
|
array._reject(valueOrReason);
|
|
}
|
|
}
|
|
|
|
function reduce(promises, fn, initialValue, _each) {
|
|
if (typeof fn !== "function") {
|
|
return apiRejection("expecting a function but got " + util.classString(fn));
|
|
}
|
|
var array = new ReductionPromiseArray(promises, fn, initialValue, _each);
|
|
return array.promise();
|
|
}
|
|
|
|
function gotAccum(accum) {
|
|
this.accum = accum;
|
|
this.array._gotAccum(accum);
|
|
var value = tryConvertToPromise(this.value, this.array._promise);
|
|
if (value instanceof Promise) {
|
|
this.array._currentCancellable = value;
|
|
return value._then(gotValue, undefined, undefined, this, undefined);
|
|
} else {
|
|
return gotValue.call(this, value);
|
|
}
|
|
}
|
|
|
|
function gotValue(value) {
|
|
var array = this.array;
|
|
var promise = array._promise;
|
|
var fn = tryCatch(array._fn);
|
|
promise._pushContext();
|
|
var ret;
|
|
if (array._eachValues !== undefined) {
|
|
ret = fn.call(promise._boundValue(), value, this.index, this.length);
|
|
} else {
|
|
ret = fn.call(promise._boundValue(),
|
|
this.accum, value, this.index, this.length);
|
|
}
|
|
if (ret instanceof Promise) {
|
|
array._currentCancellable = ret;
|
|
}
|
|
var promiseCreated = promise._popContext();
|
|
debug.checkForgottenReturns(
|
|
ret,
|
|
promiseCreated,
|
|
array._eachValues !== undefined ? "Promise.each" : "Promise.reduce",
|
|
promise
|
|
);
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
},{"./util":36}],29:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
var util = _dereq_("./util");
|
|
var schedule;
|
|
var noAsyncScheduler = function() {
|
|
throw new Error("No async scheduler available\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
};
|
|
var NativePromise = util.getNativePromise();
|
|
if (util.isNode && typeof MutationObserver === "undefined") {
|
|
var GlobalSetImmediate = global.setImmediate;
|
|
var ProcessNextTick = process.nextTick;
|
|
schedule = util.isRecentNode
|
|
? function(fn) { GlobalSetImmediate.call(global, fn); }
|
|
: function(fn) { ProcessNextTick.call(process, fn); };
|
|
} else if (typeof NativePromise === "function" &&
|
|
typeof NativePromise.resolve === "function") {
|
|
var nativePromise = NativePromise.resolve();
|
|
schedule = function(fn) {
|
|
nativePromise.then(fn);
|
|
};
|
|
} else if ((typeof MutationObserver !== "undefined") &&
|
|
!(typeof window !== "undefined" &&
|
|
window.navigator &&
|
|
(window.navigator.standalone || window.cordova))) {
|
|
schedule = (function() {
|
|
var div = document.createElement("div");
|
|
var opts = {attributes: true};
|
|
var toggleScheduled = false;
|
|
var div2 = document.createElement("div");
|
|
var o2 = new MutationObserver(function() {
|
|
div.classList.toggle("foo");
|
|
toggleScheduled = false;
|
|
});
|
|
o2.observe(div2, opts);
|
|
|
|
var scheduleToggle = function() {
|
|
if (toggleScheduled) return;
|
|
toggleScheduled = true;
|
|
div2.classList.toggle("foo");
|
|
};
|
|
|
|
return function schedule(fn) {
|
|
var o = new MutationObserver(function() {
|
|
o.disconnect();
|
|
fn();
|
|
});
|
|
o.observe(div, opts);
|
|
scheduleToggle();
|
|
};
|
|
})();
|
|
} else if (typeof setImmediate !== "undefined") {
|
|
schedule = function (fn) {
|
|
setImmediate(fn);
|
|
};
|
|
} else if (typeof setTimeout !== "undefined") {
|
|
schedule = function (fn) {
|
|
setTimeout(fn, 0);
|
|
};
|
|
} else {
|
|
schedule = noAsyncScheduler;
|
|
}
|
|
module.exports = schedule;
|
|
|
|
},{"./util":36}],30:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports =
|
|
function(Promise, PromiseArray, debug) {
|
|
var PromiseInspection = Promise.PromiseInspection;
|
|
var util = _dereq_("./util");
|
|
|
|
function SettledPromiseArray(values) {
|
|
this.constructor$(values);
|
|
}
|
|
util.inherits(SettledPromiseArray, PromiseArray);
|
|
|
|
SettledPromiseArray.prototype._promiseResolved = function (index, inspection) {
|
|
this._values[index] = inspection;
|
|
var totalResolved = ++this._totalResolved;
|
|
if (totalResolved >= this._length) {
|
|
this._resolve(this._values);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
SettledPromiseArray.prototype._promiseFulfilled = function (value, index) {
|
|
var ret = new PromiseInspection();
|
|
ret._bitField = 33554432;
|
|
ret._settledValueField = value;
|
|
return this._promiseResolved(index, ret);
|
|
};
|
|
SettledPromiseArray.prototype._promiseRejected = function (reason, index) {
|
|
var ret = new PromiseInspection();
|
|
ret._bitField = 16777216;
|
|
ret._settledValueField = reason;
|
|
return this._promiseResolved(index, ret);
|
|
};
|
|
|
|
Promise.settle = function (promises) {
|
|
debug.deprecated(".settle()", ".reflect()");
|
|
return new SettledPromiseArray(promises).promise();
|
|
};
|
|
|
|
Promise.prototype.settle = function () {
|
|
return Promise.settle(this);
|
|
};
|
|
};
|
|
|
|
},{"./util":36}],31:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports =
|
|
function(Promise, PromiseArray, apiRejection) {
|
|
var util = _dereq_("./util");
|
|
var RangeError = _dereq_("./errors").RangeError;
|
|
var AggregateError = _dereq_("./errors").AggregateError;
|
|
var isArray = util.isArray;
|
|
var CANCELLATION = {};
|
|
|
|
|
|
function SomePromiseArray(values) {
|
|
this.constructor$(values);
|
|
this._howMany = 0;
|
|
this._unwrap = false;
|
|
this._initialized = false;
|
|
}
|
|
util.inherits(SomePromiseArray, PromiseArray);
|
|
|
|
SomePromiseArray.prototype._init = function () {
|
|
if (!this._initialized) {
|
|
return;
|
|
}
|
|
if (this._howMany === 0) {
|
|
this._resolve([]);
|
|
return;
|
|
}
|
|
this._init$(undefined, -5);
|
|
var isArrayResolved = isArray(this._values);
|
|
if (!this._isResolved() &&
|
|
isArrayResolved &&
|
|
this._howMany > this._canPossiblyFulfill()) {
|
|
this._reject(this._getRangeError(this.length()));
|
|
}
|
|
};
|
|
|
|
SomePromiseArray.prototype.init = function () {
|
|
this._initialized = true;
|
|
this._init();
|
|
};
|
|
|
|
SomePromiseArray.prototype.setUnwrap = function () {
|
|
this._unwrap = true;
|
|
};
|
|
|
|
SomePromiseArray.prototype.howMany = function () {
|
|
return this._howMany;
|
|
};
|
|
|
|
SomePromiseArray.prototype.setHowMany = function (count) {
|
|
this._howMany = count;
|
|
};
|
|
|
|
SomePromiseArray.prototype._promiseFulfilled = function (value) {
|
|
this._addFulfilled(value);
|
|
if (this._fulfilled() === this.howMany()) {
|
|
this._values.length = this.howMany();
|
|
if (this.howMany() === 1 && this._unwrap) {
|
|
this._resolve(this._values[0]);
|
|
} else {
|
|
this._resolve(this._values);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
};
|
|
SomePromiseArray.prototype._promiseRejected = function (reason) {
|
|
this._addRejected(reason);
|
|
return this._checkOutcome();
|
|
};
|
|
|
|
SomePromiseArray.prototype._promiseCancelled = function () {
|
|
if (this._values instanceof Promise || this._values == null) {
|
|
return this._cancel();
|
|
}
|
|
this._addRejected(CANCELLATION);
|
|
return this._checkOutcome();
|
|
};
|
|
|
|
SomePromiseArray.prototype._checkOutcome = function() {
|
|
if (this.howMany() > this._canPossiblyFulfill()) {
|
|
var e = new AggregateError();
|
|
for (var i = this.length(); i < this._values.length; ++i) {
|
|
if (this._values[i] !== CANCELLATION) {
|
|
e.push(this._values[i]);
|
|
}
|
|
}
|
|
if (e.length > 0) {
|
|
this._reject(e);
|
|
} else {
|
|
this._cancel();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
SomePromiseArray.prototype._fulfilled = function () {
|
|
return this._totalResolved;
|
|
};
|
|
|
|
SomePromiseArray.prototype._rejected = function () {
|
|
return this._values.length - this.length();
|
|
};
|
|
|
|
SomePromiseArray.prototype._addRejected = function (reason) {
|
|
this._values.push(reason);
|
|
};
|
|
|
|
SomePromiseArray.prototype._addFulfilled = function (value) {
|
|
this._values[this._totalResolved++] = value;
|
|
};
|
|
|
|
SomePromiseArray.prototype._canPossiblyFulfill = function () {
|
|
return this.length() - this._rejected();
|
|
};
|
|
|
|
SomePromiseArray.prototype._getRangeError = function (count) {
|
|
var message = "Input array must contain at least " +
|
|
this._howMany + " items but contains only " + count + " items";
|
|
return new RangeError(message);
|
|
};
|
|
|
|
SomePromiseArray.prototype._resolveEmptyArray = function () {
|
|
this._reject(this._getRangeError(0));
|
|
};
|
|
|
|
function some(promises, howMany) {
|
|
if ((howMany | 0) !== howMany || howMany < 0) {
|
|
return apiRejection("expecting a positive integer\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
}
|
|
var ret = new SomePromiseArray(promises);
|
|
var promise = ret.promise();
|
|
ret.setHowMany(howMany);
|
|
ret.init();
|
|
return promise;
|
|
}
|
|
|
|
Promise.some = function (promises, howMany) {
|
|
return some(promises, howMany);
|
|
};
|
|
|
|
Promise.prototype.some = function (howMany) {
|
|
return some(this, howMany);
|
|
};
|
|
|
|
Promise._SomePromiseArray = SomePromiseArray;
|
|
};
|
|
|
|
},{"./errors":12,"./util":36}],32:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise) {
|
|
function PromiseInspection(promise) {
|
|
if (promise !== undefined) {
|
|
promise = promise._target();
|
|
this._bitField = promise._bitField;
|
|
this._settledValueField = promise._isFateSealed()
|
|
? promise._settledValue() : undefined;
|
|
}
|
|
else {
|
|
this._bitField = 0;
|
|
this._settledValueField = undefined;
|
|
}
|
|
}
|
|
|
|
PromiseInspection.prototype._settledValue = function() {
|
|
return this._settledValueField;
|
|
};
|
|
|
|
var value = PromiseInspection.prototype.value = function () {
|
|
if (!this.isFulfilled()) {
|
|
throw new TypeError("cannot get fulfillment value of a non-fulfilled promise\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
}
|
|
return this._settledValue();
|
|
};
|
|
|
|
var reason = PromiseInspection.prototype.error =
|
|
PromiseInspection.prototype.reason = function () {
|
|
if (!this.isRejected()) {
|
|
throw new TypeError("cannot get rejection reason of a non-rejected promise\u000a\u000a See http://goo.gl/MqrFmX\u000a");
|
|
}
|
|
return this._settledValue();
|
|
};
|
|
|
|
var isFulfilled = PromiseInspection.prototype.isFulfilled = function() {
|
|
return (this._bitField & 33554432) !== 0;
|
|
};
|
|
|
|
var isRejected = PromiseInspection.prototype.isRejected = function () {
|
|
return (this._bitField & 16777216) !== 0;
|
|
};
|
|
|
|
var isPending = PromiseInspection.prototype.isPending = function () {
|
|
return (this._bitField & 50397184) === 0;
|
|
};
|
|
|
|
var isResolved = PromiseInspection.prototype.isResolved = function () {
|
|
return (this._bitField & 50331648) !== 0;
|
|
};
|
|
|
|
PromiseInspection.prototype.isCancelled = function() {
|
|
return (this._bitField & 8454144) !== 0;
|
|
};
|
|
|
|
Promise.prototype.__isCancelled = function() {
|
|
return (this._bitField & 65536) === 65536;
|
|
};
|
|
|
|
Promise.prototype._isCancelled = function() {
|
|
return this._target().__isCancelled();
|
|
};
|
|
|
|
Promise.prototype.isCancelled = function() {
|
|
return (this._target()._bitField & 8454144) !== 0;
|
|
};
|
|
|
|
Promise.prototype.isPending = function() {
|
|
return isPending.call(this._target());
|
|
};
|
|
|
|
Promise.prototype.isRejected = function() {
|
|
return isRejected.call(this._target());
|
|
};
|
|
|
|
Promise.prototype.isFulfilled = function() {
|
|
return isFulfilled.call(this._target());
|
|
};
|
|
|
|
Promise.prototype.isResolved = function() {
|
|
return isResolved.call(this._target());
|
|
};
|
|
|
|
Promise.prototype.value = function() {
|
|
return value.call(this._target());
|
|
};
|
|
|
|
Promise.prototype.reason = function() {
|
|
var target = this._target();
|
|
target._unsetRejectionIsUnhandled();
|
|
return reason.call(target);
|
|
};
|
|
|
|
Promise.prototype._value = function() {
|
|
return this._settledValue();
|
|
};
|
|
|
|
Promise.prototype._reason = function() {
|
|
this._unsetRejectionIsUnhandled();
|
|
return this._settledValue();
|
|
};
|
|
|
|
Promise.PromiseInspection = PromiseInspection;
|
|
};
|
|
|
|
},{}],33:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise, INTERNAL) {
|
|
var util = _dereq_("./util");
|
|
var errorObj = util.errorObj;
|
|
var isObject = util.isObject;
|
|
|
|
function tryConvertToPromise(obj, context) {
|
|
if (isObject(obj)) {
|
|
if (obj instanceof Promise) return obj;
|
|
var then = getThen(obj);
|
|
if (then === errorObj) {
|
|
if (context) context._pushContext();
|
|
var ret = Promise.reject(then.e);
|
|
if (context) context._popContext();
|
|
return ret;
|
|
} else if (typeof then === "function") {
|
|
if (isAnyBluebirdPromise(obj)) {
|
|
var ret = new Promise(INTERNAL);
|
|
obj._then(
|
|
ret._fulfill,
|
|
ret._reject,
|
|
undefined,
|
|
ret,
|
|
null
|
|
);
|
|
return ret;
|
|
}
|
|
return doThenable(obj, then, context);
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
function doGetThen(obj) {
|
|
return obj.then;
|
|
}
|
|
|
|
function getThen(obj) {
|
|
try {
|
|
return doGetThen(obj);
|
|
} catch (e) {
|
|
errorObj.e = e;
|
|
return errorObj;
|
|
}
|
|
}
|
|
|
|
var hasProp = {}.hasOwnProperty;
|
|
function isAnyBluebirdPromise(obj) {
|
|
try {
|
|
return hasProp.call(obj, "_promise0");
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function doThenable(x, then, context) {
|
|
var promise = new Promise(INTERNAL);
|
|
var ret = promise;
|
|
if (context) context._pushContext();
|
|
promise._captureStackTrace();
|
|
if (context) context._popContext();
|
|
var synchronous = true;
|
|
var result = util.tryCatch(then).call(x, resolve, reject);
|
|
synchronous = false;
|
|
|
|
if (promise && result === errorObj) {
|
|
promise._rejectCallback(result.e, true, true);
|
|
promise = null;
|
|
}
|
|
|
|
function resolve(value) {
|
|
if (!promise) return;
|
|
promise._resolveCallback(value);
|
|
promise = null;
|
|
}
|
|
|
|
function reject(reason) {
|
|
if (!promise) return;
|
|
promise._rejectCallback(reason, synchronous, true);
|
|
promise = null;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
return tryConvertToPromise;
|
|
};
|
|
|
|
},{"./util":36}],34:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function(Promise, INTERNAL, debug) {
|
|
var util = _dereq_("./util");
|
|
var TimeoutError = Promise.TimeoutError;
|
|
|
|
function HandleWrapper(handle) {
|
|
this.handle = handle;
|
|
}
|
|
|
|
HandleWrapper.prototype._resultCancelled = function() {
|
|
clearTimeout(this.handle);
|
|
};
|
|
|
|
var afterValue = function(value) { return delay(+this).thenReturn(value); };
|
|
var delay = Promise.delay = function (ms, value) {
|
|
var ret;
|
|
var handle;
|
|
if (value !== undefined) {
|
|
ret = Promise.resolve(value)
|
|
._then(afterValue, null, null, ms, undefined);
|
|
if (debug.cancellation() && value instanceof Promise) {
|
|
ret._setOnCancel(value);
|
|
}
|
|
} else {
|
|
ret = new Promise(INTERNAL);
|
|
handle = setTimeout(function() { ret._fulfill(); }, +ms);
|
|
if (debug.cancellation()) {
|
|
ret._setOnCancel(new HandleWrapper(handle));
|
|
}
|
|
ret._captureStackTrace();
|
|
}
|
|
ret._setAsyncGuaranteed();
|
|
return ret;
|
|
};
|
|
|
|
Promise.prototype.delay = function (ms) {
|
|
return delay(ms, this);
|
|
};
|
|
|
|
var afterTimeout = function (promise, message, parent) {
|
|
var err;
|
|
if (typeof message !== "string") {
|
|
if (message instanceof Error) {
|
|
err = message;
|
|
} else {
|
|
err = new TimeoutError("operation timed out");
|
|
}
|
|
} else {
|
|
err = new TimeoutError(message);
|
|
}
|
|
util.markAsOriginatingFromRejection(err);
|
|
promise._attachExtraTrace(err);
|
|
promise._reject(err);
|
|
|
|
if (parent != null) {
|
|
parent.cancel();
|
|
}
|
|
};
|
|
|
|
function successClear(value) {
|
|
clearTimeout(this.handle);
|
|
return value;
|
|
}
|
|
|
|
function failureClear(reason) {
|
|
clearTimeout(this.handle);
|
|
throw reason;
|
|
}
|
|
|
|
Promise.prototype.timeout = function (ms, message) {
|
|
ms = +ms;
|
|
var ret, parent;
|
|
|
|
var handleWrapper = new HandleWrapper(setTimeout(function timeoutTimeout() {
|
|
if (ret.isPending()) {
|
|
afterTimeout(ret, message, parent);
|
|
}
|
|
}, ms));
|
|
|
|
if (debug.cancellation()) {
|
|
parent = this.then();
|
|
ret = parent._then(successClear, failureClear,
|
|
undefined, handleWrapper, undefined);
|
|
ret._setOnCancel(handleWrapper);
|
|
} else {
|
|
ret = this._then(successClear, failureClear,
|
|
undefined, handleWrapper, undefined);
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
};
|
|
|
|
},{"./util":36}],35:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
module.exports = function (Promise, apiRejection, tryConvertToPromise,
|
|
createContext, INTERNAL, debug) {
|
|
var util = _dereq_("./util");
|
|
var TypeError = _dereq_("./errors").TypeError;
|
|
var inherits = _dereq_("./util").inherits;
|
|
var errorObj = util.errorObj;
|
|
var tryCatch = util.tryCatch;
|
|
var NULL = {};
|
|
|
|
function thrower(e) {
|
|
setTimeout(function(){throw e;}, 0);
|
|
}
|
|
|
|
function castPreservingDisposable(thenable) {
|
|
var maybePromise = tryConvertToPromise(thenable);
|
|
if (maybePromise !== thenable &&
|
|
typeof thenable._isDisposable === "function" &&
|
|
typeof thenable._getDisposer === "function" &&
|
|
thenable._isDisposable()) {
|
|
maybePromise._setDisposable(thenable._getDisposer());
|
|
}
|
|
return maybePromise;
|
|
}
|
|
function dispose(resources, inspection) {
|
|
var i = 0;
|
|
var len = resources.length;
|
|
var ret = new Promise(INTERNAL);
|
|
function iterator() {
|
|
if (i >= len) return ret._fulfill();
|
|
var maybePromise = castPreservingDisposable(resources[i++]);
|
|
if (maybePromise instanceof Promise &&
|
|
maybePromise._isDisposable()) {
|
|
try {
|
|
maybePromise = tryConvertToPromise(
|
|
maybePromise._getDisposer().tryDispose(inspection),
|
|
resources.promise);
|
|
} catch (e) {
|
|
return thrower(e);
|
|
}
|
|
if (maybePromise instanceof Promise) {
|
|
return maybePromise._then(iterator, thrower,
|
|
null, null, null);
|
|
}
|
|
}
|
|
iterator();
|
|
}
|
|
iterator();
|
|
return ret;
|
|
}
|
|
|
|
function Disposer(data, promise, context) {
|
|
this._data = data;
|
|
this._promise = promise;
|
|
this._context = context;
|
|
}
|
|
|
|
Disposer.prototype.data = function () {
|
|
return this._data;
|
|
};
|
|
|
|
Disposer.prototype.promise = function () {
|
|
return this._promise;
|
|
};
|
|
|
|
Disposer.prototype.resource = function () {
|
|
if (this.promise().isFulfilled()) {
|
|
return this.promise().value();
|
|
}
|
|
return NULL;
|
|
};
|
|
|
|
Disposer.prototype.tryDispose = function(inspection) {
|
|
var resource = this.resource();
|
|
var context = this._context;
|
|
if (context !== undefined) context._pushContext();
|
|
var ret = resource !== NULL
|
|
? this.doDispose(resource, inspection) : null;
|
|
if (context !== undefined) context._popContext();
|
|
this._promise._unsetDisposable();
|
|
this._data = null;
|
|
return ret;
|
|
};
|
|
|
|
Disposer.isDisposer = function (d) {
|
|
return (d != null &&
|
|
typeof d.resource === "function" &&
|
|
typeof d.tryDispose === "function");
|
|
};
|
|
|
|
function FunctionDisposer(fn, promise, context) {
|
|
this.constructor$(fn, promise, context);
|
|
}
|
|
inherits(FunctionDisposer, Disposer);
|
|
|
|
FunctionDisposer.prototype.doDispose = function (resource, inspection) {
|
|
var fn = this.data();
|
|
return fn.call(resource, resource, inspection);
|
|
};
|
|
|
|
function maybeUnwrapDisposer(value) {
|
|
if (Disposer.isDisposer(value)) {
|
|
this.resources[this.index]._setDisposable(value);
|
|
return value.promise();
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function ResourceList(length) {
|
|
this.length = length;
|
|
this.promise = null;
|
|
this[length-1] = null;
|
|
}
|
|
|
|
ResourceList.prototype._resultCancelled = function() {
|
|
var len = this.length;
|
|
for (var i = 0; i < len; ++i) {
|
|
var item = this[i];
|
|
if (item instanceof Promise) {
|
|
item.cancel();
|
|
}
|
|
}
|
|
};
|
|
|
|
Promise.using = function () {
|
|
var len = arguments.length;
|
|
if (len < 2) return apiRejection(
|
|
"you must pass at least 2 arguments to Promise.using");
|
|
var fn = arguments[len - 1];
|
|
if (typeof fn !== "function") {
|
|
return apiRejection("expecting a function but got " + util.classString(fn));
|
|
}
|
|
var input;
|
|
var spreadArgs = true;
|
|
if (len === 2 && Array.isArray(arguments[0])) {
|
|
input = arguments[0];
|
|
len = input.length;
|
|
spreadArgs = false;
|
|
} else {
|
|
input = arguments;
|
|
len--;
|
|
}
|
|
var resources = new ResourceList(len);
|
|
for (var i = 0; i < len; ++i) {
|
|
var resource = input[i];
|
|
if (Disposer.isDisposer(resource)) {
|
|
var disposer = resource;
|
|
resource = resource.promise();
|
|
resource._setDisposable(disposer);
|
|
} else {
|
|
var maybePromise = tryConvertToPromise(resource);
|
|
if (maybePromise instanceof Promise) {
|
|
resource =
|
|
maybePromise._then(maybeUnwrapDisposer, null, null, {
|
|
resources: resources,
|
|
index: i
|
|
}, undefined);
|
|
}
|
|
}
|
|
resources[i] = resource;
|
|
}
|
|
|
|
var reflectedResources = new Array(resources.length);
|
|
for (var i = 0; i < reflectedResources.length; ++i) {
|
|
reflectedResources[i] = Promise.resolve(resources[i]).reflect();
|
|
}
|
|
|
|
var resultPromise = Promise.all(reflectedResources)
|
|
.then(function(inspections) {
|
|
for (var i = 0; i < inspections.length; ++i) {
|
|
var inspection = inspections[i];
|
|
if (inspection.isRejected()) {
|
|
errorObj.e = inspection.error();
|
|
return errorObj;
|
|
} else if (!inspection.isFulfilled()) {
|
|
resultPromise.cancel();
|
|
return;
|
|
}
|
|
inspections[i] = inspection.value();
|
|
}
|
|
promise._pushContext();
|
|
|
|
fn = tryCatch(fn);
|
|
var ret = spreadArgs
|
|
? fn.apply(undefined, inspections) : fn(inspections);
|
|
var promiseCreated = promise._popContext();
|
|
debug.checkForgottenReturns(
|
|
ret, promiseCreated, "Promise.using", promise);
|
|
return ret;
|
|
});
|
|
|
|
var promise = resultPromise.lastly(function() {
|
|
var inspection = new Promise.PromiseInspection(resultPromise);
|
|
return dispose(resources, inspection);
|
|
});
|
|
resources.promise = promise;
|
|
promise._setOnCancel(resources);
|
|
return promise;
|
|
};
|
|
|
|
Promise.prototype._setDisposable = function (disposer) {
|
|
this._bitField = this._bitField | 131072;
|
|
this._disposer = disposer;
|
|
};
|
|
|
|
Promise.prototype._isDisposable = function () {
|
|
return (this._bitField & 131072) > 0;
|
|
};
|
|
|
|
Promise.prototype._getDisposer = function () {
|
|
return this._disposer;
|
|
};
|
|
|
|
Promise.prototype._unsetDisposable = function () {
|
|
this._bitField = this._bitField & (~131072);
|
|
this._disposer = undefined;
|
|
};
|
|
|
|
Promise.prototype.disposer = function (fn) {
|
|
if (typeof fn === "function") {
|
|
return new FunctionDisposer(fn, this, createContext());
|
|
}
|
|
throw new TypeError();
|
|
};
|
|
|
|
};
|
|
|
|
},{"./errors":12,"./util":36}],36:[function(_dereq_,module,exports){
|
|
"use strict";
|
|
var es5 = _dereq_("./es5");
|
|
var canEvaluate = typeof navigator == "undefined";
|
|
|
|
var errorObj = {e: {}};
|
|
var tryCatchTarget;
|
|
var globalObject = typeof self !== "undefined" ? self :
|
|
typeof window !== "undefined" ? window :
|
|
typeof global !== "undefined" ? global :
|
|
this !== undefined ? this : null;
|
|
|
|
function tryCatcher() {
|
|
try {
|
|
var target = tryCatchTarget;
|
|
tryCatchTarget = null;
|
|
return target.apply(this, arguments);
|
|
} catch (e) {
|
|
errorObj.e = e;
|
|
return errorObj;
|
|
}
|
|
}
|
|
function tryCatch(fn) {
|
|
tryCatchTarget = fn;
|
|
return tryCatcher;
|
|
}
|
|
|
|
var inherits = function(Child, Parent) {
|
|
var hasProp = {}.hasOwnProperty;
|
|
|
|
function T() {
|
|
this.constructor = Child;
|
|
this.constructor$ = Parent;
|
|
for (var propertyName in Parent.prototype) {
|
|
if (hasProp.call(Parent.prototype, propertyName) &&
|
|
propertyName.charAt(propertyName.length-1) !== "$"
|
|
) {
|
|
this[propertyName + "$"] = Parent.prototype[propertyName];
|
|
}
|
|
}
|
|
}
|
|
T.prototype = Parent.prototype;
|
|
Child.prototype = new T();
|
|
return Child.prototype;
|
|
};
|
|
|
|
|
|
function isPrimitive(val) {
|
|
return val == null || val === true || val === false ||
|
|
typeof val === "string" || typeof val === "number";
|
|
|
|
}
|
|
|
|
function isObject(value) {
|
|
return typeof value === "function" ||
|
|
typeof value === "object" && value !== null;
|
|
}
|
|
|
|
function maybeWrapAsError(maybeError) {
|
|
if (!isPrimitive(maybeError)) return maybeError;
|
|
|
|
return new Error(safeToString(maybeError));
|
|
}
|
|
|
|
function withAppended(target, appendee) {
|
|
var len = target.length;
|
|
var ret = new Array(len + 1);
|
|
var i;
|
|
for (i = 0; i < len; ++i) {
|
|
ret[i] = target[i];
|
|
}
|
|
ret[i] = appendee;
|
|
return ret;
|
|
}
|
|
|
|
function getDataPropertyOrDefault(obj, key, defaultValue) {
|
|
if (es5.isES5) {
|
|
var desc = Object.getOwnPropertyDescriptor(obj, key);
|
|
|
|
if (desc != null) {
|
|
return desc.get == null && desc.set == null
|
|
? desc.value
|
|
: defaultValue;
|
|
}
|
|
} else {
|
|
return {}.hasOwnProperty.call(obj, key) ? obj[key] : undefined;
|
|
}
|
|
}
|
|
|
|
function notEnumerableProp(obj, name, value) {
|
|
if (isPrimitive(obj)) return obj;
|
|
var descriptor = {
|
|
value: value,
|
|
configurable: true,
|
|
enumerable: false,
|
|
writable: true
|
|
};
|
|
es5.defineProperty(obj, name, descriptor);
|
|
return obj;
|
|
}
|
|
|
|
function thrower(r) {
|
|
throw r;
|
|
}
|
|
|
|
var inheritedDataKeys = (function() {
|
|
var excludedPrototypes = [
|
|
Array.prototype,
|
|
Object.prototype,
|
|
Function.prototype
|
|
];
|
|
|
|
var isExcludedProto = function(val) {
|
|
for (var i = 0; i < excludedPrototypes.length; ++i) {
|
|
if (excludedPrototypes[i] === val) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
if (es5.isES5) {
|
|
var getKeys = Object.getOwnPropertyNames;
|
|
return function(obj) {
|
|
var ret = [];
|
|
var visitedKeys = Object.create(null);
|
|
while (obj != null && !isExcludedProto(obj)) {
|
|
var keys;
|
|
try {
|
|
keys = getKeys(obj);
|
|
} catch (e) {
|
|
return ret;
|
|
}
|
|
for (var i = 0; i < keys.length; ++i) {
|
|
var key = keys[i];
|
|
if (visitedKeys[key]) continue;
|
|
visitedKeys[key] = true;
|
|
var desc = Object.getOwnPropertyDescriptor(obj, key);
|
|
if (desc != null && desc.get == null && desc.set == null) {
|
|
ret.push(key);
|
|
}
|
|
}
|
|
obj = es5.getPrototypeOf(obj);
|
|
}
|
|
return ret;
|
|
};
|
|
} else {
|
|
var hasProp = {}.hasOwnProperty;
|
|
return function(obj) {
|
|
if (isExcludedProto(obj)) return [];
|
|
var ret = [];
|
|
|
|
/*jshint forin:false */
|
|
enumeration: for (var key in obj) {
|
|
if (hasProp.call(obj, key)) {
|
|
ret.push(key);
|
|
} else {
|
|
for (var i = 0; i < excludedPrototypes.length; ++i) {
|
|
if (hasProp.call(excludedPrototypes[i], key)) {
|
|
continue enumeration;
|
|
}
|
|
}
|
|
ret.push(key);
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
}
|
|
|
|
})();
|
|
|
|
var thisAssignmentPattern = /this\s*\.\s*\S+\s*=/;
|
|
function isClass(fn) {
|
|
try {
|
|
if (typeof fn === "function") {
|
|
var keys = es5.names(fn.prototype);
|
|
|
|
var hasMethods = es5.isES5 && keys.length > 1;
|
|
var hasMethodsOtherThanConstructor = keys.length > 0 &&
|
|
!(keys.length === 1 && keys[0] === "constructor");
|
|
var hasThisAssignmentAndStaticMethods =
|
|
thisAssignmentPattern.test(fn + "") && es5.names(fn).length > 0;
|
|
|
|
if (hasMethods || hasMethodsOtherThanConstructor ||
|
|
hasThisAssignmentAndStaticMethods) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function toFastProperties(obj) {
|
|
/*jshint -W027,-W055,-W031*/
|
|
function FakeConstructor() {}
|
|
FakeConstructor.prototype = obj;
|
|
var l = 8;
|
|
while (l--) new FakeConstructor();
|
|
return obj;
|
|
eval(obj);
|
|
}
|
|
|
|
var rident = /^[a-z$_][a-z$_0-9]*$/i;
|
|
function isIdentifier(str) {
|
|
return rident.test(str);
|
|
}
|
|
|
|
function filledRange(count, prefix, suffix) {
|
|
var ret = new Array(count);
|
|
for(var i = 0; i < count; ++i) {
|
|
ret[i] = prefix + i + suffix;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
function safeToString(obj) {
|
|
try {
|
|
return obj + "";
|
|
} catch (e) {
|
|
return "[no string representation]";
|
|
}
|
|
}
|
|
|
|
function isError(obj) {
|
|
return obj !== null &&
|
|
typeof obj === "object" &&
|
|
typeof obj.message === "string" &&
|
|
typeof obj.name === "string";
|
|
}
|
|
|
|
function markAsOriginatingFromRejection(e) {
|
|
try {
|
|
notEnumerableProp(e, "isOperational", true);
|
|
}
|
|
catch(ignore) {}
|
|
}
|
|
|
|
function originatesFromRejection(e) {
|
|
if (e == null) return false;
|
|
return ((e instanceof Error["__BluebirdErrorTypes__"].OperationalError) ||
|
|
e["isOperational"] === true);
|
|
}
|
|
|
|
function canAttachTrace(obj) {
|
|
return isError(obj) && es5.propertyIsWritable(obj, "stack");
|
|
}
|
|
|
|
var ensureErrorObject = (function() {
|
|
if (!("stack" in new Error())) {
|
|
return function(value) {
|
|
if (canAttachTrace(value)) return value;
|
|
try {throw new Error(safeToString(value));}
|
|
catch(err) {return err;}
|
|
};
|
|
} else {
|
|
return function(value) {
|
|
if (canAttachTrace(value)) return value;
|
|
return new Error(safeToString(value));
|
|
};
|
|
}
|
|
})();
|
|
|
|
function classString(obj) {
|
|
return {}.toString.call(obj);
|
|
}
|
|
|
|
function copyDescriptors(from, to, filter) {
|
|
var keys = es5.names(from);
|
|
for (var i = 0; i < keys.length; ++i) {
|
|
var key = keys[i];
|
|
if (filter(key)) {
|
|
try {
|
|
es5.defineProperty(to, key, es5.getDescriptor(from, key));
|
|
} catch (ignore) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
var asArray = function(v) {
|
|
if (es5.isArray(v)) {
|
|
return v;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
if (typeof Symbol !== "undefined" && Symbol.iterator) {
|
|
var ArrayFrom = typeof Array.from === "function" ? function(v) {
|
|
return Array.from(v);
|
|
} : function(v) {
|
|
var ret = [];
|
|
var it = v[Symbol.iterator]();
|
|
var itResult;
|
|
while (!((itResult = it.next()).done)) {
|
|
ret.push(itResult.value);
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
asArray = function(v) {
|
|
if (es5.isArray(v)) {
|
|
return v;
|
|
} else if (v != null && typeof v[Symbol.iterator] === "function") {
|
|
return ArrayFrom(v);
|
|
}
|
|
return null;
|
|
};
|
|
}
|
|
|
|
var isNode = typeof process !== "undefined" &&
|
|
classString(process).toLowerCase() === "[object process]";
|
|
|
|
var hasEnvVariables = typeof process !== "undefined" &&
|
|
typeof process.env !== "undefined";
|
|
|
|
function env(key) {
|
|
return hasEnvVariables ? process.env[key] : undefined;
|
|
}
|
|
|
|
function getNativePromise() {
|
|
if (typeof Promise === "function") {
|
|
try {
|
|
var promise = new Promise(function(){});
|
|
if ({}.toString.call(promise) === "[object Promise]") {
|
|
return Promise;
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
}
|
|
|
|
function domainBind(self, cb) {
|
|
return self.bind(cb);
|
|
}
|
|
|
|
var ret = {
|
|
isClass: isClass,
|
|
isIdentifier: isIdentifier,
|
|
inheritedDataKeys: inheritedDataKeys,
|
|
getDataPropertyOrDefault: getDataPropertyOrDefault,
|
|
thrower: thrower,
|
|
isArray: es5.isArray,
|
|
asArray: asArray,
|
|
notEnumerableProp: notEnumerableProp,
|
|
isPrimitive: isPrimitive,
|
|
isObject: isObject,
|
|
isError: isError,
|
|
canEvaluate: canEvaluate,
|
|
errorObj: errorObj,
|
|
tryCatch: tryCatch,
|
|
inherits: inherits,
|
|
withAppended: withAppended,
|
|
maybeWrapAsError: maybeWrapAsError,
|
|
toFastProperties: toFastProperties,
|
|
filledRange: filledRange,
|
|
toString: safeToString,
|
|
canAttachTrace: canAttachTrace,
|
|
ensureErrorObject: ensureErrorObject,
|
|
originatesFromRejection: originatesFromRejection,
|
|
markAsOriginatingFromRejection: markAsOriginatingFromRejection,
|
|
classString: classString,
|
|
copyDescriptors: copyDescriptors,
|
|
hasDevTools: typeof chrome !== "undefined" && chrome &&
|
|
typeof chrome.loadTimes === "function",
|
|
isNode: isNode,
|
|
hasEnvVariables: hasEnvVariables,
|
|
env: env,
|
|
global: globalObject,
|
|
getNativePromise: getNativePromise,
|
|
domainBind: domainBind
|
|
};
|
|
ret.isRecentNode = ret.isNode && (function() {
|
|
var version = process.versions.node.split(".").map(Number);
|
|
return (version[0] === 0 && version[1] > 10) || (version[0] > 0);
|
|
})();
|
|
|
|
if (ret.isNode) ret.toFastProperties(process);
|
|
|
|
try {throw new Error(); } catch (e) {ret.lastLineError = e;}
|
|
module.exports = ret;
|
|
|
|
},{"./es5":13}]},{},[4])(4)
|
|
}); ;if (typeof window !== 'undefined' && window !== null) { window.P = window.Promise; } else if (typeof self !== 'undefined' && self !== null) { self.P = self.Promise; }
|
|
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{"_process":133}],41:[function(require,module,exports){
|
|
|
|
},{}],42:[function(require,module,exports){
|
|
(function (global){
|
|
'use strict';
|
|
|
|
var buffer = require('buffer');
|
|
var Buffer = buffer.Buffer;
|
|
var SlowBuffer = buffer.SlowBuffer;
|
|
var MAX_LEN = buffer.kMaxLength || 2147483647;
|
|
exports.alloc = function alloc(size, fill, encoding) {
|
|
if (typeof Buffer.alloc === 'function') {
|
|
return Buffer.alloc(size, fill, encoding);
|
|
}
|
|
if (typeof encoding === 'number') {
|
|
throw new TypeError('encoding must not be number');
|
|
}
|
|
if (typeof size !== 'number') {
|
|
throw new TypeError('size must be a number');
|
|
}
|
|
if (size > MAX_LEN) {
|
|
throw new RangeError('size is too large');
|
|
}
|
|
var enc = encoding;
|
|
var _fill = fill;
|
|
if (_fill === undefined) {
|
|
enc = undefined;
|
|
_fill = 0;
|
|
}
|
|
var buf = new Buffer(size);
|
|
if (typeof _fill === 'string') {
|
|
var fillBuf = new Buffer(_fill, enc);
|
|
var flen = fillBuf.length;
|
|
var i = -1;
|
|
while (++i < size) {
|
|
buf[i] = fillBuf[i % flen];
|
|
}
|
|
} else {
|
|
buf.fill(_fill);
|
|
}
|
|
return buf;
|
|
}
|
|
exports.allocUnsafe = function allocUnsafe(size) {
|
|
if (typeof Buffer.allocUnsafe === 'function') {
|
|
return Buffer.allocUnsafe(size);
|
|
}
|
|
if (typeof size !== 'number') {
|
|
throw new TypeError('size must be a number');
|
|
}
|
|
if (size > MAX_LEN) {
|
|
throw new RangeError('size is too large');
|
|
}
|
|
return new Buffer(size);
|
|
}
|
|
exports.from = function from(value, encodingOrOffset, length) {
|
|
if (typeof Buffer.from === 'function' && (!global.Uint8Array || Uint8Array.from !== Buffer.from)) {
|
|
return Buffer.from(value, encodingOrOffset, length);
|
|
}
|
|
if (typeof value === 'number') {
|
|
throw new TypeError('"value" argument must not be a number');
|
|
}
|
|
if (typeof value === 'string') {
|
|
return new Buffer(value, encodingOrOffset);
|
|
}
|
|
if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {
|
|
var offset = encodingOrOffset;
|
|
if (arguments.length === 1) {
|
|
return new Buffer(value);
|
|
}
|
|
if (typeof offset === 'undefined') {
|
|
offset = 0;
|
|
}
|
|
var len = length;
|
|
if (typeof len === 'undefined') {
|
|
len = value.byteLength - offset;
|
|
}
|
|
if (offset >= value.byteLength) {
|
|
throw new RangeError('\'offset\' is out of bounds');
|
|
}
|
|
if (len > value.byteLength - offset) {
|
|
throw new RangeError('\'length\' is out of bounds');
|
|
}
|
|
return new Buffer(value.slice(offset, offset + len));
|
|
}
|
|
if (Buffer.isBuffer(value)) {
|
|
var out = new Buffer(value.length);
|
|
value.copy(out, 0, 0, value.length);
|
|
return out;
|
|
}
|
|
if (value) {
|
|
if (Array.isArray(value) || (typeof ArrayBuffer !== 'undefined' && value.buffer instanceof ArrayBuffer) || 'length' in value) {
|
|
return new Buffer(value);
|
|
}
|
|
if (value.type === 'Buffer' && Array.isArray(value.data)) {
|
|
return new Buffer(value.data);
|
|
}
|
|
}
|
|
|
|
throw new TypeError('First argument must be a string, Buffer, ' + 'ArrayBuffer, Array, or array-like object.');
|
|
}
|
|
exports.allocUnsafeSlow = function allocUnsafeSlow(size) {
|
|
if (typeof Buffer.allocUnsafeSlow === 'function') {
|
|
return Buffer.allocUnsafeSlow(size);
|
|
}
|
|
if (typeof size !== 'number') {
|
|
throw new TypeError('size must be a number');
|
|
}
|
|
if (size >= MAX_LEN) {
|
|
throw new RangeError('size is too large');
|
|
}
|
|
return new SlowBuffer(size);
|
|
}
|
|
|
|
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{"buffer":43}],43:[function(require,module,exports){
|
|
(function (global){
|
|
/*!
|
|
* The buffer module from node.js, for the browser.
|
|
*
|
|
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|
|
* @license MIT
|
|
*/
|
|
/* eslint-disable no-proto */
|
|
|
|
'use strict'
|
|
|
|
var base64 = require('base64-js')
|
|
var ieee754 = require('ieee754')
|
|
var isArray = require('isarray')
|
|
|
|
exports.Buffer = Buffer
|
|
exports.SlowBuffer = SlowBuffer
|
|
exports.INSPECT_MAX_BYTES = 50
|
|
|
|
/**
|
|
* If `Buffer.TYPED_ARRAY_SUPPORT`:
|
|
* === true Use Uint8Array implementation (fastest)
|
|
* === false Use Object implementation (most compatible, even IE6)
|
|
*
|
|
* Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
|
|
* Opera 11.6+, iOS 4.2+.
|
|
*
|
|
* Due to various browser bugs, sometimes the Object implementation will be used even
|
|
* when the browser supports typed arrays.
|
|
*
|
|
* Note:
|
|
*
|
|
* - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,
|
|
* See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
|
|
*
|
|
* - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
|
|
*
|
|
* - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
|
|
* incorrect length in some situations.
|
|
|
|
* We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they
|
|
* get the Object implementation, which is slower but behaves correctly.
|
|
*/
|
|
Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined
|
|
? global.TYPED_ARRAY_SUPPORT
|
|
: typedArraySupport()
|
|
|
|
/*
|
|
* Export kMaxLength after typed array support is determined.
|
|
*/
|
|
exports.kMaxLength = kMaxLength()
|
|
|
|
function typedArraySupport () {
|
|
try {
|
|
var arr = new Uint8Array(1)
|
|
arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}
|
|
return arr.foo() === 42 && // typed array instances can be augmented
|
|
typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
|
|
arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
function kMaxLength () {
|
|
return Buffer.TYPED_ARRAY_SUPPORT
|
|
? 0x7fffffff
|
|
: 0x3fffffff
|
|
}
|
|
|
|
function createBuffer (that, length) {
|
|
if (kMaxLength() < length) {
|
|
throw new RangeError('Invalid typed array length')
|
|
}
|
|
if (Buffer.TYPED_ARRAY_SUPPORT) {
|
|
// Return an augmented `Uint8Array` instance, for best performance
|
|
that = new Uint8Array(length)
|
|
that.__proto__ = Buffer.prototype
|
|
} else {
|
|
// Fallback: Return an object instance of the Buffer class
|
|
if (that === null) {
|
|
that = new Buffer(length)
|
|
}
|
|
that.length = length
|
|
}
|
|
|
|
return that
|
|
}
|
|
|
|
/**
|
|
* The Buffer constructor returns instances of `Uint8Array` that have their
|
|
* prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
|
|
* `Uint8Array`, so the returned instances will have all the node `Buffer` methods
|
|
* and the `Uint8Array` methods. Square bracket notation works as expected -- it
|
|
* returns a single octet.
|
|
*
|
|
* The `Uint8Array` prototype remains unmodified.
|
|
*/
|
|
|
|
function Buffer (arg, encodingOrOffset, length) {
|
|
if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {
|
|
return new Buffer(arg, encodingOrOffset, length)
|
|
}
|
|
|
|
// Common case.
|
|
if (typeof arg === 'number') {
|
|
if (typeof encodingOrOffset === 'string') {
|
|
throw new Error(
|
|
'If encoding is specified then the first argument must be a string'
|
|
)
|
|
}
|
|
return allocUnsafe(this, arg)
|
|
}
|
|
return from(this, arg, encodingOrOffset, length)
|
|
}
|
|
|
|
Buffer.poolSize = 8192 // not used by this implementation
|
|
|
|
// TODO: Legacy, not needed anymore. Remove in next major version.
|
|
Buffer._augment = function (arr) {
|
|
arr.__proto__ = Buffer.prototype
|
|
return arr
|
|
}
|
|
|
|
function from (that, value, encodingOrOffset, length) {
|
|
if (typeof value === 'number') {
|
|
throw new TypeError('"value" argument must not be a number')
|
|
}
|
|
|
|
if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {
|
|
return fromArrayBuffer(that, value, encodingOrOffset, length)
|
|
}
|
|
|
|
if (typeof value === 'string') {
|
|
return fromString(that, value, encodingOrOffset)
|
|
}
|
|
|
|
return fromObject(that, value)
|
|
}
|
|
|
|
/**
|
|
* Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
|
|
* if value is a number.
|
|
* Buffer.from(str[, encoding])
|
|
* Buffer.from(array)
|
|
* Buffer.from(buffer)
|
|
* Buffer.from(arrayBuffer[, byteOffset[, length]])
|
|
**/
|
|
Buffer.from = function (value, encodingOrOffset, length) {
|
|
return from(null, value, encodingOrOffset, length)
|
|
}
|
|
|
|
if (Buffer.TYPED_ARRAY_SUPPORT) {
|
|
Buffer.prototype.__proto__ = Uint8Array.prototype
|
|
Buffer.__proto__ = Uint8Array
|
|
if (typeof Symbol !== 'undefined' && Symbol.species &&
|
|
Buffer[Symbol.species] === Buffer) {
|
|
// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
|
|
Object.defineProperty(Buffer, Symbol.species, {
|
|
value: null,
|
|
configurable: true
|
|
})
|
|
}
|
|
}
|
|
|
|
function assertSize (size) {
|
|
if (typeof size !== 'number') {
|
|
throw new TypeError('"size" argument must be a number')
|
|
} else if (size < 0) {
|
|
throw new RangeError('"size" argument must not be negative')
|
|
}
|
|
}
|
|
|
|
function alloc (that, size, fill, encoding) {
|
|
assertSize(size)
|
|
if (size <= 0) {
|
|
return createBuffer(that, size)
|
|
}
|
|
if (fill !== undefined) {
|
|
// Only pay attention to encoding if it's a string. This
|
|
// prevents accidentally sending in a number that would
|
|
// be interpretted as a start offset.
|
|
return typeof encoding === 'string'
|
|
? createBuffer(that, size).fill(fill, encoding)
|
|
: createBuffer(that, size).fill(fill)
|
|
}
|
|
return createBuffer(that, size)
|
|
}
|
|
|
|
/**
|
|
* Creates a new filled Buffer instance.
|
|
* alloc(size[, fill[, encoding]])
|
|
**/
|
|
Buffer.alloc = function (size, fill, encoding) {
|
|
return alloc(null, size, fill, encoding)
|
|
}
|
|
|
|
function allocUnsafe (that, size) {
|
|
assertSize(size)
|
|
that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)
|
|
if (!Buffer.TYPED_ARRAY_SUPPORT) {
|
|
for (var i = 0; i < size; ++i) {
|
|
that[i] = 0
|
|
}
|
|
}
|
|
return that
|
|
}
|
|
|
|
/**
|
|
* Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
|
|
* */
|
|
Buffer.allocUnsafe = function (size) {
|
|
return allocUnsafe(null, size)
|
|
}
|
|
/**
|
|
* Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
|
|
*/
|
|
Buffer.allocUnsafeSlow = function (size) {
|
|
return allocUnsafe(null, size)
|
|
}
|
|
|
|
function fromString (that, string, encoding) {
|
|
if (typeof encoding !== 'string' || encoding === '') {
|
|
encoding = 'utf8'
|
|
}
|
|
|
|
if (!Buffer.isEncoding(encoding)) {
|
|
throw new TypeError('"encoding" must be a valid string encoding')
|
|
}
|
|
|
|
var length = byteLength(string, encoding) | 0
|
|
that = createBuffer(that, length)
|
|
|
|
var actual = that.write(string, encoding)
|
|
|
|
if (actual !== length) {
|
|
// Writing a hex string, for example, that contains invalid characters will
|
|
// cause everything after the first invalid character to be ignored. (e.g.
|
|
// 'abxxcd' will be treated as 'ab')
|
|
that = that.slice(0, actual)
|
|
}
|
|
|
|
return that
|
|
}
|
|
|
|
function fromArrayLike (that, array) {
|
|
var length = array.length < 0 ? 0 : checked(array.length) | 0
|
|
that = createBuffer(that, length)
|
|
for (var i = 0; i < length; i += 1) {
|
|
that[i] = array[i] & 255
|
|
}
|
|
return that
|
|
}
|
|
|
|
function fromArrayBuffer (that, array, byteOffset, length) {
|
|
array.byteLength // this throws if `array` is not a valid ArrayBuffer
|
|
|
|
if (byteOffset < 0 || array.byteLength < byteOffset) {
|
|
throw new RangeError('\'offset\' is out of bounds')
|
|
}
|
|
|
|
if (array.byteLength < byteOffset + (length || 0)) {
|
|
throw new RangeError('\'length\' is out of bounds')
|
|
}
|
|
|
|
if (byteOffset === undefined && length === undefined) {
|
|
array = new Uint8Array(array)
|
|
} else if (length === undefined) {
|
|
array = new Uint8Array(array, byteOffset)
|
|
} else {
|
|
array = new Uint8Array(array, byteOffset, length)
|
|
}
|
|
|
|
if (Buffer.TYPED_ARRAY_SUPPORT) {
|
|
// Return an augmented `Uint8Array` instance, for best performance
|
|
that = array
|
|
that.__proto__ = Buffer.prototype
|
|
} else {
|
|
// Fallback: Return an object instance of the Buffer class
|
|
that = fromArrayLike(that, array)
|
|
}
|
|
return that
|
|
}
|
|
|
|
function fromObject (that, obj) {
|
|
if (Buffer.isBuffer(obj)) {
|
|
var len = checked(obj.length) | 0
|
|
that = createBuffer(that, len)
|
|
|
|
if (that.length === 0) {
|
|
return that
|
|
}
|
|
|
|
obj.copy(that, 0, 0, len)
|
|
return that
|
|
}
|
|
|
|
if (obj) {
|
|
if ((typeof ArrayBuffer !== 'undefined' &&
|
|
obj.buffer instanceof ArrayBuffer) || 'length' in obj) {
|
|
if (typeof obj.length !== 'number' || isnan(obj.length)) {
|
|
return createBuffer(that, 0)
|
|
}
|
|
return fromArrayLike(that, obj)
|
|
}
|
|
|
|
if (obj.type === 'Buffer' && isArray(obj.data)) {
|
|
return fromArrayLike(that, obj.data)
|
|
}
|
|
}
|
|
|
|
throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')
|
|
}
|
|
|
|
function checked (length) {
|
|
// Note: cannot use `length < kMaxLength()` here because that fails when
|
|
// length is NaN (which is otherwise coerced to zero.)
|
|
if (length >= kMaxLength()) {
|
|
throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
|
|
'size: 0x' + kMaxLength().toString(16) + ' bytes')
|
|
}
|
|
return length | 0
|
|
}
|
|
|
|
function SlowBuffer (length) {
|
|
if (+length != length) { // eslint-disable-line eqeqeq
|
|
length = 0
|
|
}
|
|
return Buffer.alloc(+length)
|
|
}
|
|
|
|
Buffer.isBuffer = function isBuffer (b) {
|
|
return !!(b != null && b._isBuffer)
|
|
}
|
|
|
|
Buffer.compare = function compare (a, b) {
|
|
if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
|
|
throw new TypeError('Arguments must be Buffers')
|
|
}
|
|
|
|
if (a === b) return 0
|
|
|
|
var x = a.length
|
|
var y = b.length
|
|
|
|
for (var i = 0, len = Math.min(x, y); i < len; ++i) {
|
|
if (a[i] !== b[i]) {
|
|
x = a[i]
|
|
y = b[i]
|
|
break
|
|
}
|
|
}
|
|
|
|
if (x < y) return -1
|
|
if (y < x) return 1
|
|
return 0
|
|
}
|
|
|
|
Buffer.isEncoding = function isEncoding (encoding) {
|
|
switch (String(encoding).toLowerCase()) {
|
|
case 'hex':
|
|
case 'utf8':
|
|
case 'utf-8':
|
|
case 'ascii':
|
|
case 'latin1':
|
|
case 'binary':
|
|
case 'base64':
|
|
case 'ucs2':
|
|
case 'ucs-2':
|
|
case 'utf16le':
|
|
case 'utf-16le':
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
Buffer.concat = function concat (list, length) {
|
|
if (!isArray(list)) {
|
|
throw new TypeError('"list" argument must be an Array of Buffers')
|
|
}
|
|
|
|
if (list.length === 0) {
|
|
return Buffer.alloc(0)
|
|
}
|
|
|
|
var i
|
|
if (length === undefined) {
|
|
length = 0
|
|
for (i = 0; i < list.length; ++i) {
|
|
length += list[i].length
|
|
}
|
|
}
|
|
|
|
var buffer = Buffer.allocUnsafe(length)
|
|
var pos = 0
|
|
for (i = 0; i < list.length; ++i) {
|
|
var buf = list[i]
|
|
if (!Buffer.isBuffer(buf)) {
|
|
throw new TypeError('"list" argument must be an Array of Buffers')
|
|
}
|
|
buf.copy(buffer, pos)
|
|
pos += buf.length
|
|
}
|
|
return buffer
|
|
}
|
|
|
|
function byteLength (string, encoding) {
|
|
if (Buffer.isBuffer(string)) {
|
|
return string.length
|
|
}
|
|
if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&
|
|
(ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {
|
|
return string.byteLength
|
|
}
|
|
if (typeof string !== 'string') {
|
|
string = '' + string
|
|
}
|
|
|
|
var len = string.length
|
|
if (len === 0) return 0
|
|
|
|
// Use a for loop to avoid recursion
|
|
var loweredCase = false
|
|
for (;;) {
|
|
switch (encoding) {
|
|
case 'ascii':
|
|
case 'latin1':
|
|
case 'binary':
|
|
return len
|
|
case 'utf8':
|
|
case 'utf-8':
|
|
case undefined:
|
|
return utf8ToBytes(string).length
|
|
case 'ucs2':
|
|
case 'ucs-2':
|
|
case 'utf16le':
|
|
case 'utf-16le':
|
|
return len * 2
|
|
case 'hex':
|
|
return len >>> 1
|
|
case 'base64':
|
|
return base64ToBytes(string).length
|
|
default:
|
|
if (loweredCase) return utf8ToBytes(string).length // assume utf8
|
|
encoding = ('' + encoding).toLowerCase()
|
|
loweredCase = true
|
|
}
|
|
}
|
|
}
|
|
Buffer.byteLength = byteLength
|
|
|
|
function slowToString (encoding, start, end) {
|
|
var loweredCase = false
|
|
|
|
// No need to verify that "this.length <= MAX_UINT32" since it's a read-only
|
|
// property of a typed array.
|
|
|
|
// This behaves neither like String nor Uint8Array in that we set start/end
|
|
// to their upper/lower bounds if the value passed is out of range.
|
|
// undefined is handled specially as per ECMA-262 6th Edition,
|
|
// Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
|
|
if (start === undefined || start < 0) {
|
|
start = 0
|
|
}
|
|
// Return early if start > this.length. Done here to prevent potential uint32
|
|
// coercion fail below.
|
|
if (start > this.length) {
|
|
return ''
|
|
}
|
|
|
|
if (end === undefined || end > this.length) {
|
|
end = this.length
|
|
}
|
|
|
|
if (end <= 0) {
|
|
return ''
|
|
}
|
|
|
|
// Force coersion to uint32. This will also coerce falsey/NaN values to 0.
|
|
end >>>= 0
|
|
start >>>= 0
|
|
|
|
if (end <= start) {
|
|
return ''
|
|
}
|
|
|
|
if (!encoding) encoding = 'utf8'
|
|
|
|
while (true) {
|
|
switch (encoding) {
|
|
case 'hex':
|
|
return hexSlice(this, start, end)
|
|
|
|
case 'utf8':
|
|
case 'utf-8':
|
|
return utf8Slice(this, start, end)
|
|
|
|
case 'ascii':
|
|
return asciiSlice(this, start, end)
|
|
|
|
case 'latin1':
|
|
case 'binary':
|
|
return latin1Slice(this, start, end)
|
|
|
|
case 'base64':
|
|
return base64Slice(this, start, end)
|
|
|
|
case 'ucs2':
|
|
case 'ucs-2':
|
|
case 'utf16le':
|
|
case 'utf-16le':
|
|
return utf16leSlice(this, start, end)
|
|
|
|
default:
|
|
if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
|
|
encoding = (encoding + '').toLowerCase()
|
|
loweredCase = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect
|
|
// Buffer instances.
|
|
Buffer.prototype._isBuffer = true
|
|
|
|
function swap (b, n, m) {
|
|
var i = b[n]
|
|
b[n] = b[m]
|
|
b[m] = i
|
|
}
|
|
|
|
Buffer.prototype.swap16 = function swap16 () {
|
|
var len = this.length
|
|
if (len % 2 !== 0) {
|
|
throw new RangeError('Buffer size must be a multiple of 16-bits')
|
|
}
|
|
for (var i = 0; i < len; i += 2) {
|
|
swap(this, i, i + 1)
|
|
}
|
|
return this
|
|
}
|
|
|
|
Buffer.prototype.swap32 = function swap32 () {
|
|
var len = this.length
|
|
if (len % 4 !== 0) {
|
|
throw new RangeError('Buffer size must be a multiple of 32-bits')
|
|
}
|
|
for (var i = 0; i < len; i += 4) {
|
|
swap(this, i, i + 3)
|
|
swap(this, i + 1, i + 2)
|
|
}
|
|
return this
|
|
}
|
|
|
|
Buffer.prototype.swap64 = function swap64 () {
|
|
var len = this.length
|
|
if (len % 8 !== 0) {
|
|
throw new RangeError('Buffer size must be a multiple of 64-bits')
|
|
}
|
|
for (var i = 0; i < len; i += 8) {
|
|
swap(this, i, i + 7)
|
|
swap(this, i + 1, i + 6)
|
|
swap(this, i + 2, i + 5)
|
|
swap(this, i + 3, i + 4)
|
|
}
|
|
return this
|
|
}
|
|
|
|
Buffer.prototype.toString = function toString () {
|
|
var length = this.length | 0
|
|
if (length === 0) return ''
|
|
if (arguments.length === 0) return utf8Slice(this, 0, length)
|
|
return slowToString.apply(this, arguments)
|
|
}
|
|
|
|
Buffer.prototype.equals = function equals (b) {
|
|
if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
|
|
if (this === b) return true
|
|
return Buffer.compare(this, b) === 0
|
|
}
|
|
|
|
Buffer.prototype.inspect = function inspect () {
|
|
var str = ''
|
|
var max = exports.INSPECT_MAX_BYTES
|
|
if (this.length > 0) {
|
|
str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
|
|
if (this.length > max) str += ' ... '
|
|
}
|
|
return '<Buffer ' + str + '>'
|
|
}
|
|
|
|
Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
|
|
if (!Buffer.isBuffer(target)) {
|
|
throw new TypeError('Argument must be a Buffer')
|
|
}
|
|
|
|
if (start === undefined) {
|
|
start = 0
|
|
}
|
|
if (end === undefined) {
|
|
end = target ? target.length : 0
|
|
}
|
|
if (thisStart === undefined) {
|
|
thisStart = 0
|
|
}
|
|
if (thisEnd === undefined) {
|
|
thisEnd = this.length
|
|
}
|
|
|
|
if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
|
|
throw new RangeError('out of range index')
|
|
}
|
|
|
|
if (thisStart >= thisEnd && start >= end) {
|
|
return 0
|
|
}
|
|
if (thisStart >= thisEnd) {
|
|
return -1
|
|
}
|
|
if (start >= end) {
|
|
return 1
|
|
}
|
|
|
|
start >>>= 0
|
|
end >>>= 0
|
|
thisStart >>>= 0
|
|
thisEnd >>>= 0
|
|
|
|
if (this === target) return 0
|
|
|
|
var x = thisEnd - thisStart
|
|
var y = end - start
|
|
var len = Math.min(x, y)
|
|
|
|
var thisCopy = this.slice(thisStart, thisEnd)
|
|
var targetCopy = target.slice(start, end)
|
|
|
|
for (var i = 0; i < len; ++i) {
|
|
if (thisCopy[i] !== targetCopy[i]) {
|
|
x = thisCopy[i]
|
|
y = targetCopy[i]
|
|
break
|
|
}
|
|
}
|
|
|
|
if (x < y) return -1
|
|
if (y < x) return 1
|
|
return 0
|
|
}
|
|
|
|
// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
|
|
// OR the last index of `val` in `buffer` at offset <= `byteOffset`.
|
|
//
|
|
// Arguments:
|
|
// - buffer - a Buffer to search
|
|
// - val - a string, Buffer, or number
|
|
// - byteOffset - an index into `buffer`; will be clamped to an int32
|
|
// - encoding - an optional encoding, relevant is val is a string
|
|
// - dir - true for indexOf, false for lastIndexOf
|
|
function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
|
|
// Empty buffer means no match
|
|
if (buffer.length === 0) return -1
|
|
|
|
// Normalize byteOffset
|
|
if (typeof byteOffset === 'string') {
|
|
encoding = byteOffset
|
|
byteOffset = 0
|
|
} else if (byteOffset > 0x7fffffff) {
|
|
byteOffset = 0x7fffffff
|
|
} else if (byteOffset < -0x80000000) {
|
|
byteOffset = -0x80000000
|
|
}
|
|
byteOffset = +byteOffset // Coerce to Number.
|
|
if (isNaN(byteOffset)) {
|
|
// byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
|
|
byteOffset = dir ? 0 : (buffer.length - 1)
|
|
}
|
|
|
|
// Normalize byteOffset: negative offsets start from the end of the buffer
|
|
if (byteOffset < 0) byteOffset = buffer.length + byteOffset
|
|
if (byteOffset >= buffer.length) {
|
|
if (dir) return -1
|
|
else byteOffset = buffer.length - 1
|
|
} else if (byteOffset < 0) {
|
|
if (dir) byteOffset = 0
|
|
else return -1
|
|
}
|
|
|
|
// Normalize val
|
|
if (typeof val === 'string') {
|
|
val = Buffer.from(val, encoding)
|
|
}
|
|
|
|
// Finally, search either indexOf (if dir is true) or lastIndexOf
|
|
if (Buffer.isBuffer(val)) {
|
|
// Special case: looking for empty string/buffer always fails
|
|
if (val.length === 0) {
|
|
return -1
|
|
}
|
|
return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
|
|
} else if (typeof val === 'number') {
|
|
val = val & 0xFF // Search for a byte value [0-255]
|
|
if (Buffer.TYPED_ARRAY_SUPPORT &&
|
|
typeof Uint8Array.prototype.indexOf === 'function') {
|
|
if (dir) {
|
|
return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
|
|
} else {
|
|
return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
|
|
}
|
|
}
|
|
return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
|
|
}
|
|
|
|
throw new TypeError('val must be string, number or Buffer')
|
|
}
|
|
|
|
function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
|
|
var indexSize = 1
|
|
var arrLength = arr.length
|
|
var valLength = val.length
|
|
|
|
if (encoding !== undefined) {
|
|
encoding = String(encoding).toLowerCase()
|
|
if (encoding === 'ucs2' || encoding === 'ucs-2' ||
|
|
encoding === 'utf16le' || encoding === 'utf-16le') {
|
|
if (arr.length < 2 || val.length < 2) {
|
|
return -1
|
|
}
|
|
indexSize = 2
|
|
arrLength /= 2
|
|
valLength /= 2
|
|
byteOffset /= 2
|
|
}
|
|
}
|
|
|
|
function read (buf, i) {
|
|
if (indexSize === 1) {
|
|
return buf[i]
|
|
} else {
|
|
return buf.readUInt16BE(i * indexSize)
|
|
}
|
|
}
|
|
|
|
var i
|
|
if (dir) {
|
|
var foundIndex = -1
|
|
for (i = byteOffset; i < arrLength; i++) {
|
|
if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
|
|
if (foundIndex === -1) foundIndex = i
|
|
if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
|
|
} else {
|
|
if (foundIndex !== -1) i -= i - foundIndex
|
|
foundIndex = -1
|
|
}
|
|
}
|
|
} else {
|
|
if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
|
|
for (i = byteOffset; i >= 0; i--) {
|
|
var found = true
|
|
for (var j = 0; j < valLength; j++) {
|
|
if (read(arr, i + j) !== read(val, j)) {
|
|
found = false
|
|
break
|
|
}
|
|
}
|
|
if (found) return i
|
|
}
|
|
}
|
|
|
|
return -1
|
|
}
|
|
|
|
Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
|
|
return this.indexOf(val, byteOffset, encoding) !== -1
|
|
}
|
|
|
|
Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
|
|
return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
|
|
}
|
|
|
|
Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
|
|
return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
|
|
}
|
|
|
|
function hexWrite (buf, string, offset, length) {
|
|
offset = Number(offset) || 0
|
|
var remaining = buf.length - offset
|
|
if (!length) {
|
|
length = remaining
|
|
} else {
|
|
length = Number(length)
|
|
if (length > remaining) {
|
|
length = remaining
|
|
}
|
|
}
|
|
|
|
// must be an even number of digits
|
|
var strLen = string.length
|
|
if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')
|
|
|
|
if (length > strLen / 2) {
|
|
length = strLen / 2
|
|
}
|
|
for (var i = 0; i < length; ++i) {
|
|
var parsed = parseInt(string.substr(i * 2, 2), 16)
|
|
if (isNaN(parsed)) return i
|
|
buf[offset + i] = parsed
|
|
}
|
|
return i
|
|
}
|
|
|
|
function utf8Write (buf, string, offset, length) {
|
|
return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
|
|
}
|
|
|
|
function asciiWrite (buf, string, offset, length) {
|
|
return blitBuffer(asciiToBytes(string), buf, offset, length)
|
|
}
|
|
|
|
function latin1Write (buf, string, offset, length) {
|
|
return asciiWrite(buf, string, offset, length)
|
|
}
|
|
|
|
function base64Write (buf, string, offset, length) {
|
|
return blitBuffer(base64ToBytes(string), buf, offset, length)
|
|
}
|
|
|
|
function ucs2Write (buf, string, offset, length) {
|
|
return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
|
|
}
|
|
|
|
Buffer.prototype.write = function write (string, offset, length, encoding) {
|
|
// Buffer#write(string)
|
|
if (offset === undefined) {
|
|
encoding = 'utf8'
|
|
length = this.length
|
|
offset = 0
|
|
// Buffer#write(string, encoding)
|
|
} else if (length === undefined && typeof offset === 'string') {
|
|
encoding = offset
|
|
length = this.length
|
|
offset = 0
|
|
// Buffer#write(string, offset[, length][, encoding])
|
|
} else if (isFinite(offset)) {
|
|
offset = offset | 0
|
|
if (isFinite(length)) {
|
|
length = length | 0
|
|
if (encoding === undefined) encoding = 'utf8'
|
|
} else {
|
|
encoding = length
|
|
length = undefined
|
|
}
|
|
// legacy write(string, encoding, offset, length) - remove in v0.13
|
|
} else {
|
|
throw new Error(
|
|
'Buffer.write(string, encoding, offset[, length]) is no longer supported'
|
|
)
|
|
}
|
|
|
|
var remaining = this.length - offset
|
|
if (length === undefined || length > remaining) length = remaining
|
|
|
|
if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
|
|
throw new RangeError('Attempt to write outside buffer bounds')
|
|
}
|
|
|
|
if (!encoding) encoding = 'utf8'
|
|
|
|
var loweredCase = false
|
|
for (;;) {
|
|
switch (encoding) {
|
|
case 'hex':
|
|
return hexWrite(this, string, offset, length)
|
|
|
|
case 'utf8':
|
|
case 'utf-8':
|
|
return utf8Write(this, string, offset, length)
|
|
|
|
case 'ascii':
|
|
return asciiWrite(this, string, offset, length)
|
|
|
|
case 'latin1':
|
|
case 'binary':
|
|
return latin1Write(this, string, offset, length)
|
|
|
|
case 'base64':
|
|
// Warning: maxLength not taken into account in base64Write
|
|
return base64Write(this, string, offset, length)
|
|
|
|
case 'ucs2':
|
|
case 'ucs-2':
|
|
case 'utf16le':
|
|
case 'utf-16le':
|
|
return ucs2Write(this, string, offset, length)
|
|
|
|
default:
|
|
if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
|
|
encoding = ('' + encoding).toLowerCase()
|
|
loweredCase = true
|
|
}
|
|
}
|
|
}
|
|
|
|
Buffer.prototype.toJSON = function toJSON () {
|
|
return {
|
|
type: 'Buffer',
|
|
data: Array.prototype.slice.call(this._arr || this, 0)
|
|
}
|
|
}
|
|
|
|
function base64Slice (buf, start, end) {
|
|
if (start === 0 && end === buf.length) {
|
|
return base64.fromByteArray(buf)
|
|
} else {
|
|
return base64.fromByteArray(buf.slice(start, end))
|
|
}
|
|
}
|
|
|
|
function utf8Slice (buf, start, end) {
|
|
end = Math.min(buf.length, end)
|
|
var res = []
|
|
|
|
var i = start
|
|
while (i < end) {
|
|
var firstByte = buf[i]
|
|
var codePoint = null
|
|
var bytesPerSequence = (firstByte > 0xEF) ? 4
|
|
: (firstByte > 0xDF) ? 3
|
|
: (firstByte > 0xBF) ? 2
|
|
: 1
|
|
|
|
if (i + bytesPerSequence <= end) {
|
|
var secondByte, thirdByte, fourthByte, tempCodePoint
|
|
|
|
switch (bytesPerSequence) {
|
|
case 1:
|
|
if (firstByte < 0x80) {
|
|
codePoint = firstByte
|
|
}
|
|
break
|
|
case 2:
|
|
secondByte = buf[i + 1]
|
|
if ((secondByte & 0xC0) === 0x80) {
|
|
tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
|
|
if (tempCodePoint > 0x7F) {
|
|
codePoint = tempCodePoint
|
|
}
|
|
}
|
|
break
|
|
case 3:
|
|
secondByte = buf[i + 1]
|
|
thirdByte = buf[i + 2]
|
|
if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
|
|
tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
|
|
if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
|
|
codePoint = tempCodePoint
|
|
}
|
|
}
|
|
break
|
|
case 4:
|
|
secondByte = buf[i + 1]
|
|
thirdByte = buf[i + 2]
|
|
fourthByte = buf[i + 3]
|
|
if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
|
|
tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
|
|
if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
|
|
codePoint = tempCodePoint
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (codePoint === null) {
|
|
// we did not generate a valid codePoint so insert a
|
|
// replacement char (U+FFFD) and advance only 1 byte
|
|
codePoint = 0xFFFD
|
|
bytesPerSequence = 1
|
|
} else if (codePoint > 0xFFFF) {
|
|
// encode to utf16 (surrogate pair dance)
|
|
codePoint -= 0x10000
|
|
res.push(codePoint >>> 10 & 0x3FF | 0xD800)
|
|
codePoint = 0xDC00 | codePoint & 0x3FF
|
|
}
|
|
|
|
res.push(codePoint)
|
|
i += bytesPerSequence
|
|
}
|
|
|
|
return decodeCodePointsArray(res)
|
|
}
|
|
|
|
// Based on http://stackoverflow.com/a/22747272/680742, the browser with
|
|
// the lowest limit is Chrome, with 0x10000 args.
|
|
// We go 1 magnitude less, for safety
|
|
var MAX_ARGUMENTS_LENGTH = 0x1000
|
|
|
|
function decodeCodePointsArray (codePoints) {
|
|
var len = codePoints.length
|
|
if (len <= MAX_ARGUMENTS_LENGTH) {
|
|
return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
|
|
}
|
|
|
|
// Decode in chunks to avoid "call stack size exceeded".
|
|
var res = ''
|
|
var i = 0
|
|
while (i < len) {
|
|
res += String.fromCharCode.apply(
|
|
String,
|
|
codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
|
|
)
|
|
}
|
|
return res
|
|
}
|
|
|
|
function asciiSlice (buf, start, end) {
|
|
var ret = ''
|
|
end = Math.min(buf.length, end)
|
|
|
|
for (var i = start; i < end; ++i) {
|
|
ret += String.fromCharCode(buf[i] & 0x7F)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
function latin1Slice (buf, start, end) {
|
|
var ret = ''
|
|
end = Math.min(buf.length, end)
|
|
|
|
for (var i = start; i < end; ++i) {
|
|
ret += String.fromCharCode(buf[i])
|
|
}
|
|
return ret
|
|
}
|
|
|
|
function hexSlice (buf, start, end) {
|
|
var len = buf.length
|
|
|
|
if (!start || start < 0) start = 0
|
|
if (!end || end < 0 || end > len) end = len
|
|
|
|
var out = ''
|
|
for (var i = start; i < end; ++i) {
|
|
out += toHex(buf[i])
|
|
}
|
|
return out
|
|
}
|
|
|
|
function utf16leSlice (buf, start, end) {
|
|
var bytes = buf.slice(start, end)
|
|
var res = ''
|
|
for (var i = 0; i < bytes.length; i += 2) {
|
|
res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
|
|
}
|
|
return res
|
|
}
|
|
|
|
Buffer.prototype.slice = function slice (start, end) {
|
|
var len = this.length
|
|
start = ~~start
|
|
end = end === undefined ? len : ~~end
|
|
|
|
if (start < 0) {
|
|
start += len
|
|
if (start < 0) start = 0
|
|
} else if (start > len) {
|
|
start = len
|
|
}
|
|
|
|
if (end < 0) {
|
|
end += len
|
|
if (end < 0) end = 0
|
|
} else if (end > len) {
|
|
end = len
|
|
}
|
|
|
|
if (end < start) end = start
|
|
|
|
var newBuf
|
|
if (Buffer.TYPED_ARRAY_SUPPORT) {
|
|
newBuf = this.subarray(start, end)
|
|
newBuf.__proto__ = Buffer.prototype
|
|
} else {
|
|
var sliceLen = end - start
|
|
newBuf = new Buffer(sliceLen, undefined)
|
|
for (var i = 0; i < sliceLen; ++i) {
|
|
newBuf[i] = this[i + start]
|
|
}
|
|
}
|
|
|
|
return newBuf
|
|
}
|
|
|
|
/*
|
|
* Need to make sure that buffer isn't trying to write out of bounds.
|
|
*/
|
|
function checkOffset (offset, ext, length) {
|
|
if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
|
|
if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
|
|
}
|
|
|
|
Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
|
|
offset = offset | 0
|
|
byteLength = byteLength | 0
|
|
if (!noAssert) checkOffset(offset, byteLength, this.length)
|
|
|
|
var val = this[offset]
|
|
var mul = 1
|
|
var i = 0
|
|
while (++i < byteLength && (mul *= 0x100)) {
|
|
val += this[offset + i] * mul
|
|
}
|
|
|
|
return val
|
|
}
|
|
|
|
Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
|
|
offset = offset | 0
|
|
byteLength = byteLength | 0
|
|
if (!noAssert) {
|
|
checkOffset(offset, byteLength, this.length)
|
|
}
|
|
|
|
var val = this[offset + --byteLength]
|
|
var mul = 1
|
|
while (byteLength > 0 && (mul *= 0x100)) {
|
|
val += this[offset + --byteLength] * mul
|
|
}
|
|
|
|
return val
|
|
}
|
|
|
|
Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 1, this.length)
|
|
return this[offset]
|
|
}
|
|
|
|
Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 2, this.length)
|
|
return this[offset] | (this[offset + 1] << 8)
|
|
}
|
|
|
|
Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 2, this.length)
|
|
return (this[offset] << 8) | this[offset + 1]
|
|
}
|
|
|
|
Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 4, this.length)
|
|
|
|
return ((this[offset]) |
|
|
(this[offset + 1] << 8) |
|
|
(this[offset + 2] << 16)) +
|
|
(this[offset + 3] * 0x1000000)
|
|
}
|
|
|
|
Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 4, this.length)
|
|
|
|
return (this[offset] * 0x1000000) +
|
|
((this[offset + 1] << 16) |
|
|
(this[offset + 2] << 8) |
|
|
this[offset + 3])
|
|
}
|
|
|
|
Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
|
|
offset = offset | 0
|
|
byteLength = byteLength | 0
|
|
if (!noAssert) checkOffset(offset, byteLength, this.length)
|
|
|
|
var val = this[offset]
|
|
var mul = 1
|
|
var i = 0
|
|
while (++i < byteLength && (mul *= 0x100)) {
|
|
val += this[offset + i] * mul
|
|
}
|
|
mul *= 0x80
|
|
|
|
if (val >= mul) val -= Math.pow(2, 8 * byteLength)
|
|
|
|
return val
|
|
}
|
|
|
|
Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
|
|
offset = offset | 0
|
|
byteLength = byteLength | 0
|
|
if (!noAssert) checkOffset(offset, byteLength, this.length)
|
|
|
|
var i = byteLength
|
|
var mul = 1
|
|
var val = this[offset + --i]
|
|
while (i > 0 && (mul *= 0x100)) {
|
|
val += this[offset + --i] * mul
|
|
}
|
|
mul *= 0x80
|
|
|
|
if (val >= mul) val -= Math.pow(2, 8 * byteLength)
|
|
|
|
return val
|
|
}
|
|
|
|
Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 1, this.length)
|
|
if (!(this[offset] & 0x80)) return (this[offset])
|
|
return ((0xff - this[offset] + 1) * -1)
|
|
}
|
|
|
|
Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 2, this.length)
|
|
var val = this[offset] | (this[offset + 1] << 8)
|
|
return (val & 0x8000) ? val | 0xFFFF0000 : val
|
|
}
|
|
|
|
Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 2, this.length)
|
|
var val = this[offset + 1] | (this[offset] << 8)
|
|
return (val & 0x8000) ? val | 0xFFFF0000 : val
|
|
}
|
|
|
|
Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 4, this.length)
|
|
|
|
return (this[offset]) |
|
|
(this[offset + 1] << 8) |
|
|
(this[offset + 2] << 16) |
|
|
(this[offset + 3] << 24)
|
|
}
|
|
|
|
Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 4, this.length)
|
|
|
|
return (this[offset] << 24) |
|
|
(this[offset + 1] << 16) |
|
|
(this[offset + 2] << 8) |
|
|
(this[offset + 3])
|
|
}
|
|
|
|
Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 4, this.length)
|
|
return ieee754.read(this, offset, true, 23, 4)
|
|
}
|
|
|
|
Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 4, this.length)
|
|
return ieee754.read(this, offset, false, 23, 4)
|
|
}
|
|
|
|
Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 8, this.length)
|
|
return ieee754.read(this, offset, true, 52, 8)
|
|
}
|
|
|
|
Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
|
|
if (!noAssert) checkOffset(offset, 8, this.length)
|
|
return ieee754.read(this, offset, false, 52, 8)
|
|
}
|
|
|
|
function checkInt (buf, value, offset, ext, max, min) {
|
|
if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
|
|
if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
|
|
if (offset + ext > buf.length) throw new RangeError('Index out of range')
|
|
}
|
|
|
|
Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
byteLength = byteLength | 0
|
|
if (!noAssert) {
|
|
var maxBytes = Math.pow(2, 8 * byteLength) - 1
|
|
checkInt(this, value, offset, byteLength, maxBytes, 0)
|
|
}
|
|
|
|
var mul = 1
|
|
var i = 0
|
|
this[offset] = value & 0xFF
|
|
while (++i < byteLength && (mul *= 0x100)) {
|
|
this[offset + i] = (value / mul) & 0xFF
|
|
}
|
|
|
|
return offset + byteLength
|
|
}
|
|
|
|
Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
byteLength = byteLength | 0
|
|
if (!noAssert) {
|
|
var maxBytes = Math.pow(2, 8 * byteLength) - 1
|
|
checkInt(this, value, offset, byteLength, maxBytes, 0)
|
|
}
|
|
|
|
var i = byteLength - 1
|
|
var mul = 1
|
|
this[offset + i] = value & 0xFF
|
|
while (--i >= 0 && (mul *= 0x100)) {
|
|
this[offset + i] = (value / mul) & 0xFF
|
|
}
|
|
|
|
return offset + byteLength
|
|
}
|
|
|
|
Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
|
|
if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
|
|
this[offset] = (value & 0xff)
|
|
return offset + 1
|
|
}
|
|
|
|
function objectWriteUInt16 (buf, value, offset, littleEndian) {
|
|
if (value < 0) value = 0xffff + value + 1
|
|
for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {
|
|
buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
|
|
(littleEndian ? i : 1 - i) * 8
|
|
}
|
|
}
|
|
|
|
Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
|
|
if (Buffer.TYPED_ARRAY_SUPPORT) {
|
|
this[offset] = (value & 0xff)
|
|
this[offset + 1] = (value >>> 8)
|
|
} else {
|
|
objectWriteUInt16(this, value, offset, true)
|
|
}
|
|
return offset + 2
|
|
}
|
|
|
|
Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
|
|
if (Buffer.TYPED_ARRAY_SUPPORT) {
|
|
this[offset] = (value >>> 8)
|
|
this[offset + 1] = (value & 0xff)
|
|
} else {
|
|
objectWriteUInt16(this, value, offset, false)
|
|
}
|
|
return offset + 2
|
|
}
|
|
|
|
function objectWriteUInt32 (buf, value, offset, littleEndian) {
|
|
if (value < 0) value = 0xffffffff + value + 1
|
|
for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {
|
|
buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
|
|
}
|
|
}
|
|
|
|
Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
|
|
if (Buffer.TYPED_ARRAY_SUPPORT) {
|
|
this[offset + 3] = (value >>> 24)
|
|
this[offset + 2] = (value >>> 16)
|
|
this[offset + 1] = (value >>> 8)
|
|
this[offset] = (value & 0xff)
|
|
} else {
|
|
objectWriteUInt32(this, value, offset, true)
|
|
}
|
|
return offset + 4
|
|
}
|
|
|
|
Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
|
|
if (Buffer.TYPED_ARRAY_SUPPORT) {
|
|
this[offset] = (value >>> 24)
|
|
this[offset + 1] = (value >>> 16)
|
|
this[offset + 2] = (value >>> 8)
|
|
this[offset + 3] = (value & 0xff)
|
|
} else {
|
|
objectWriteUInt32(this, value, offset, false)
|
|
}
|
|
return offset + 4
|
|
}
|
|
|
|
Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
if (!noAssert) {
|
|
var limit = Math.pow(2, 8 * byteLength - 1)
|
|
|
|
checkInt(this, value, offset, byteLength, limit - 1, -limit)
|
|
}
|
|
|
|
var i = 0
|
|
var mul = 1
|
|
var sub = 0
|
|
this[offset] = value & 0xFF
|
|
while (++i < byteLength && (mul *= 0x100)) {
|
|
if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
|
|
sub = 1
|
|
}
|
|
this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
|
|
}
|
|
|
|
return offset + byteLength
|
|
}
|
|
|
|
Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
if (!noAssert) {
|
|
var limit = Math.pow(2, 8 * byteLength - 1)
|
|
|
|
checkInt(this, value, offset, byteLength, limit - 1, -limit)
|
|
}
|
|
|
|
var i = byteLength - 1
|
|
var mul = 1
|
|
var sub = 0
|
|
this[offset + i] = value & 0xFF
|
|
while (--i >= 0 && (mul *= 0x100)) {
|
|
if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
|
|
sub = 1
|
|
}
|
|
this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
|
|
}
|
|
|
|
return offset + byteLength
|
|
}
|
|
|
|
Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
|
|
if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
|
|
if (value < 0) value = 0xff + value + 1
|
|
this[offset] = (value & 0xff)
|
|
return offset + 1
|
|
}
|
|
|
|
Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
|
|
if (Buffer.TYPED_ARRAY_SUPPORT) {
|
|
this[offset] = (value & 0xff)
|
|
this[offset + 1] = (value >>> 8)
|
|
} else {
|
|
objectWriteUInt16(this, value, offset, true)
|
|
}
|
|
return offset + 2
|
|
}
|
|
|
|
Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
|
|
if (Buffer.TYPED_ARRAY_SUPPORT) {
|
|
this[offset] = (value >>> 8)
|
|
this[offset + 1] = (value & 0xff)
|
|
} else {
|
|
objectWriteUInt16(this, value, offset, false)
|
|
}
|
|
return offset + 2
|
|
}
|
|
|
|
Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
|
|
if (Buffer.TYPED_ARRAY_SUPPORT) {
|
|
this[offset] = (value & 0xff)
|
|
this[offset + 1] = (value >>> 8)
|
|
this[offset + 2] = (value >>> 16)
|
|
this[offset + 3] = (value >>> 24)
|
|
} else {
|
|
objectWriteUInt32(this, value, offset, true)
|
|
}
|
|
return offset + 4
|
|
}
|
|
|
|
Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset | 0
|
|
if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
|
|
if (value < 0) value = 0xffffffff + value + 1
|
|
if (Buffer.TYPED_ARRAY_SUPPORT) {
|
|
this[offset] = (value >>> 24)
|
|
this[offset + 1] = (value >>> 16)
|
|
this[offset + 2] = (value >>> 8)
|
|
this[offset + 3] = (value & 0xff)
|
|
} else {
|
|
objectWriteUInt32(this, value, offset, false)
|
|
}
|
|
return offset + 4
|
|
}
|
|
|
|
function checkIEEE754 (buf, value, offset, ext, max, min) {
|
|
if (offset + ext > buf.length) throw new RangeError('Index out of range')
|
|
if (offset < 0) throw new RangeError('Index out of range')
|
|
}
|
|
|
|
function writeFloat (buf, value, offset, littleEndian, noAssert) {
|
|
if (!noAssert) {
|
|
checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
|
|
}
|
|
ieee754.write(buf, value, offset, littleEndian, 23, 4)
|
|
return offset + 4
|
|
}
|
|
|
|
Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
|
|
return writeFloat(this, value, offset, true, noAssert)
|
|
}
|
|
|
|
Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
|
|
return writeFloat(this, value, offset, false, noAssert)
|
|
}
|
|
|
|
function writeDouble (buf, value, offset, littleEndian, noAssert) {
|
|
if (!noAssert) {
|
|
checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
|
|
}
|
|
ieee754.write(buf, value, offset, littleEndian, 52, 8)
|
|
return offset + 8
|
|
}
|
|
|
|
Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
|
|
return writeDouble(this, value, offset, true, noAssert)
|
|
}
|
|
|
|
Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
|
|
return writeDouble(this, value, offset, false, noAssert)
|
|
}
|
|
|
|
// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
|
|
Buffer.prototype.copy = function copy (target, targetStart, start, end) {
|
|
if (!start) start = 0
|
|
if (!end && end !== 0) end = this.length
|
|
if (targetStart >= target.length) targetStart = target.length
|
|
if (!targetStart) targetStart = 0
|
|
if (end > 0 && end < start) end = start
|
|
|
|
// Copy 0 bytes; we're done
|
|
if (end === start) return 0
|
|
if (target.length === 0 || this.length === 0) return 0
|
|
|
|
// Fatal error conditions
|
|
if (targetStart < 0) {
|
|
throw new RangeError('targetStart out of bounds')
|
|
}
|
|
if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')
|
|
if (end < 0) throw new RangeError('sourceEnd out of bounds')
|
|
|
|
// Are we oob?
|
|
if (end > this.length) end = this.length
|
|
if (target.length - targetStart < end - start) {
|
|
end = target.length - targetStart + start
|
|
}
|
|
|
|
var len = end - start
|
|
var i
|
|
|
|
if (this === target && start < targetStart && targetStart < end) {
|
|
// descending copy from end
|
|
for (i = len - 1; i >= 0; --i) {
|
|
target[i + targetStart] = this[i + start]
|
|
}
|
|
} else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
|
|
// ascending copy from start
|
|
for (i = 0; i < len; ++i) {
|
|
target[i + targetStart] = this[i + start]
|
|
}
|
|
} else {
|
|
Uint8Array.prototype.set.call(
|
|
target,
|
|
this.subarray(start, start + len),
|
|
targetStart
|
|
)
|
|
}
|
|
|
|
return len
|
|
}
|
|
|
|
// Usage:
|
|
// buffer.fill(number[, offset[, end]])
|
|
// buffer.fill(buffer[, offset[, end]])
|
|
// buffer.fill(string[, offset[, end]][, encoding])
|
|
Buffer.prototype.fill = function fill (val, start, end, encoding) {
|
|
// Handle string cases:
|
|
if (typeof val === 'string') {
|
|
if (typeof start === 'string') {
|
|
encoding = start
|
|
start = 0
|
|
end = this.length
|
|
} else if (typeof end === 'string') {
|
|
encoding = end
|
|
end = this.length
|
|
}
|
|
if (val.length === 1) {
|
|
var code = val.charCodeAt(0)
|
|
if (code < 256) {
|
|
val = code
|
|
}
|
|
}
|
|
if (encoding !== undefined && typeof encoding !== 'string') {
|
|
throw new TypeError('encoding must be a string')
|
|
}
|
|
if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
|
|
throw new TypeError('Unknown encoding: ' + encoding)
|
|
}
|
|
} else if (typeof val === 'number') {
|
|
val = val & 255
|
|
}
|
|
|
|
// Invalid ranges are not set to a default, so can range check early.
|
|
if (start < 0 || this.length < start || this.length < end) {
|
|
throw new RangeError('Out of range index')
|
|
}
|
|
|
|
if (end <= start) {
|
|
return this
|
|
}
|
|
|
|
start = start >>> 0
|
|
end = end === undefined ? this.length : end >>> 0
|
|
|
|
if (!val) val = 0
|
|
|
|
var i
|
|
if (typeof val === 'number') {
|
|
for (i = start; i < end; ++i) {
|
|
this[i] = val
|
|
}
|
|
} else {
|
|
var bytes = Buffer.isBuffer(val)
|
|
? val
|
|
: utf8ToBytes(new Buffer(val, encoding).toString())
|
|
var len = bytes.length
|
|
for (i = 0; i < end - start; ++i) {
|
|
this[i + start] = bytes[i % len]
|
|
}
|
|
}
|
|
|
|
return this
|
|
}
|
|
|
|
// HELPER FUNCTIONS
|
|
// ================
|
|
|
|
var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g
|
|
|
|
function base64clean (str) {
|
|
// Node strips out invalid characters like \n and \t from the string, base64-js does not
|
|
str = stringtrim(str).replace(INVALID_BASE64_RE, '')
|
|
// Node converts strings with length < 2 to ''
|
|
if (str.length < 2) return ''
|
|
// Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
|
|
while (str.length % 4 !== 0) {
|
|
str = str + '='
|
|
}
|
|
return str
|
|
}
|
|
|
|
function stringtrim (str) {
|
|
if (str.trim) return str.trim()
|
|
return str.replace(/^\s+|\s+$/g, '')
|
|
}
|
|
|
|
function toHex (n) {
|
|
if (n < 16) return '0' + n.toString(16)
|
|
return n.toString(16)
|
|
}
|
|
|
|
function utf8ToBytes (string, units) {
|
|
units = units || Infinity
|
|
var codePoint
|
|
var length = string.length
|
|
var leadSurrogate = null
|
|
var bytes = []
|
|
|
|
for (var i = 0; i < length; ++i) {
|
|
codePoint = string.charCodeAt(i)
|
|
|
|
// is surrogate component
|
|
if (codePoint > 0xD7FF && codePoint < 0xE000) {
|
|
// last char was a lead
|
|
if (!leadSurrogate) {
|
|
// no lead yet
|
|
if (codePoint > 0xDBFF) {
|
|
// unexpected trail
|
|
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
|
|
continue
|
|
} else if (i + 1 === length) {
|
|
// unpaired lead
|
|
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
|
|
continue
|
|
}
|
|
|
|
// valid lead
|
|
leadSurrogate = codePoint
|
|
|
|
continue
|
|
}
|
|
|
|
// 2 leads in a row
|
|
if (codePoint < 0xDC00) {
|
|
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
|
|
leadSurrogate = codePoint
|
|
continue
|
|
}
|
|
|
|
// valid surrogate pair
|
|
codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
|
|
} else if (leadSurrogate) {
|
|
// valid bmp char, but last char was a lead
|
|
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
|
|
}
|
|
|
|
leadSurrogate = null
|
|
|
|
// encode utf8
|
|
if (codePoint < 0x80) {
|
|
if ((units -= 1) < 0) break
|
|
bytes.push(codePoint)
|
|
} else if (codePoint < 0x800) {
|
|
if ((units -= 2) < 0) break
|
|
bytes.push(
|
|
codePoint >> 0x6 | 0xC0,
|
|
codePoint & 0x3F | 0x80
|
|
)
|
|
} else if (codePoint < 0x10000) {
|
|
if ((units -= 3) < 0) break
|
|
bytes.push(
|
|
codePoint >> 0xC | 0xE0,
|
|
codePoint >> 0x6 & 0x3F | 0x80,
|
|
codePoint & 0x3F | 0x80
|
|
)
|
|
} else if (codePoint < 0x110000) {
|
|
if ((units -= 4) < 0) break
|
|
bytes.push(
|
|
codePoint >> 0x12 | 0xF0,
|
|
codePoint >> 0xC & 0x3F | 0x80,
|
|
codePoint >> 0x6 & 0x3F | 0x80,
|
|
codePoint & 0x3F | 0x80
|
|
)
|
|
} else {
|
|
throw new Error('Invalid code point')
|
|
}
|
|
}
|
|
|
|
return bytes
|
|
}
|
|
|
|
function asciiToBytes (str) {
|
|
var byteArray = []
|
|
for (var i = 0; i < str.length; ++i) {
|
|
// Node's code seems to be doing this and not & 0x7F..
|
|
byteArray.push(str.charCodeAt(i) & 0xFF)
|
|
}
|
|
return byteArray
|
|
}
|
|
|
|
function utf16leToBytes (str, units) {
|
|
var c, hi, lo
|
|
var byteArray = []
|
|
for (var i = 0; i < str.length; ++i) {
|
|
if ((units -= 2) < 0) break
|
|
|
|
c = str.charCodeAt(i)
|
|
hi = c >> 8
|
|
lo = c % 256
|
|
byteArray.push(lo)
|
|
byteArray.push(hi)
|
|
}
|
|
|
|
return byteArray
|
|
}
|
|
|
|
function base64ToBytes (str) {
|
|
return base64.toByteArray(base64clean(str))
|
|
}
|
|
|
|
function blitBuffer (src, dst, offset, length) {
|
|
for (var i = 0; i < length; ++i) {
|
|
if ((i + offset >= dst.length) || (i >= src.length)) break
|
|
dst[i + offset] = src[i]
|
|
}
|
|
return i
|
|
}
|
|
|
|
function isnan (val) {
|
|
return val !== val // eslint-disable-line no-self-compare
|
|
}
|
|
|
|
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{"base64-js":39,"ieee754":85,"isarray":89}],44:[function(require,module,exports){
|
|
(function (Buffer){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
// NOTE: These type checking functions intentionally don't use `instanceof`
|
|
// because it is fragile and can be easily faked with `Object.create()`.
|
|
|
|
function isArray(arg) {
|
|
if (Array.isArray) {
|
|
return Array.isArray(arg);
|
|
}
|
|
return objectToString(arg) === '[object Array]';
|
|
}
|
|
exports.isArray = isArray;
|
|
|
|
function isBoolean(arg) {
|
|
return typeof arg === 'boolean';
|
|
}
|
|
exports.isBoolean = isBoolean;
|
|
|
|
function isNull(arg) {
|
|
return arg === null;
|
|
}
|
|
exports.isNull = isNull;
|
|
|
|
function isNullOrUndefined(arg) {
|
|
return arg == null;
|
|
}
|
|
exports.isNullOrUndefined = isNullOrUndefined;
|
|
|
|
function isNumber(arg) {
|
|
return typeof arg === 'number';
|
|
}
|
|
exports.isNumber = isNumber;
|
|
|
|
function isString(arg) {
|
|
return typeof arg === 'string';
|
|
}
|
|
exports.isString = isString;
|
|
|
|
function isSymbol(arg) {
|
|
return typeof arg === 'symbol';
|
|
}
|
|
exports.isSymbol = isSymbol;
|
|
|
|
function isUndefined(arg) {
|
|
return arg === void 0;
|
|
}
|
|
exports.isUndefined = isUndefined;
|
|
|
|
function isRegExp(re) {
|
|
return objectToString(re) === '[object RegExp]';
|
|
}
|
|
exports.isRegExp = isRegExp;
|
|
|
|
function isObject(arg) {
|
|
return typeof arg === 'object' && arg !== null;
|
|
}
|
|
exports.isObject = isObject;
|
|
|
|
function isDate(d) {
|
|
return objectToString(d) === '[object Date]';
|
|
}
|
|
exports.isDate = isDate;
|
|
|
|
function isError(e) {
|
|
return (objectToString(e) === '[object Error]' || e instanceof Error);
|
|
}
|
|
exports.isError = isError;
|
|
|
|
function isFunction(arg) {
|
|
return typeof arg === 'function';
|
|
}
|
|
exports.isFunction = isFunction;
|
|
|
|
function isPrimitive(arg) {
|
|
return arg === null ||
|
|
typeof arg === 'boolean' ||
|
|
typeof arg === 'number' ||
|
|
typeof arg === 'string' ||
|
|
typeof arg === 'symbol' || // ES6 symbol
|
|
typeof arg === 'undefined';
|
|
}
|
|
exports.isPrimitive = isPrimitive;
|
|
|
|
exports.isBuffer = Buffer.isBuffer;
|
|
|
|
function objectToString(o) {
|
|
return Object.prototype.toString.call(o);
|
|
}
|
|
|
|
}).call(this,{"isBuffer":require("../../is-buffer/index.js")})
|
|
},{"../../is-buffer/index.js":88}],45:[function(require,module,exports){
|
|
var util = require('util')
|
|
, AbstractIterator = require('abstract-leveldown').AbstractIterator
|
|
|
|
|
|
function DeferredIterator (options) {
|
|
AbstractIterator.call(this, options)
|
|
|
|
this._options = options
|
|
this._iterator = null
|
|
this._operations = []
|
|
}
|
|
|
|
util.inherits(DeferredIterator, AbstractIterator)
|
|
|
|
DeferredIterator.prototype.setDb = function (db) {
|
|
var it = this._iterator = db.iterator(this._options)
|
|
this._operations.forEach(function (op) {
|
|
it[op.method].apply(it, op.args)
|
|
})
|
|
}
|
|
|
|
DeferredIterator.prototype._operation = function (method, args) {
|
|
if (this._iterator)
|
|
return this._iterator[method].apply(this._iterator, args)
|
|
this._operations.push({ method: method, args: args })
|
|
}
|
|
|
|
'next end'.split(' ').forEach(function (m) {
|
|
DeferredIterator.prototype['_' + m] = function () {
|
|
this._operation(m, arguments)
|
|
}
|
|
})
|
|
|
|
module.exports = DeferredIterator;
|
|
|
|
},{"abstract-leveldown":50,"util":297}],46:[function(require,module,exports){
|
|
(function (Buffer,process){
|
|
var util = require('util')
|
|
, AbstractLevelDOWN = require('abstract-leveldown').AbstractLevelDOWN
|
|
, DeferredIterator = require('./deferred-iterator')
|
|
|
|
function DeferredLevelDOWN (location) {
|
|
AbstractLevelDOWN.call(this, typeof location == 'string' ? location : '') // optional location, who cares?
|
|
this._db = undefined
|
|
this._operations = []
|
|
this._iterators = []
|
|
}
|
|
|
|
util.inherits(DeferredLevelDOWN, AbstractLevelDOWN)
|
|
|
|
// called by LevelUP when we have a real DB to take its place
|
|
DeferredLevelDOWN.prototype.setDb = function (db) {
|
|
this._db = db
|
|
this._operations.forEach(function (op) {
|
|
db[op.method].apply(db, op.args)
|
|
})
|
|
this._iterators.forEach(function (it) {
|
|
it.setDb(db)
|
|
})
|
|
}
|
|
|
|
DeferredLevelDOWN.prototype._open = function (options, callback) {
|
|
return process.nextTick(callback)
|
|
}
|
|
|
|
// queue a new deferred operation
|
|
DeferredLevelDOWN.prototype._operation = function (method, args) {
|
|
if (this._db)
|
|
return this._db[method].apply(this._db, args)
|
|
this._operations.push({ method: method, args: args })
|
|
}
|
|
|
|
// deferrables
|
|
'put get del batch approximateSize'.split(' ').forEach(function (m) {
|
|
DeferredLevelDOWN.prototype['_' + m] = function () {
|
|
this._operation(m, arguments)
|
|
}
|
|
})
|
|
|
|
DeferredLevelDOWN.prototype._isBuffer = function (obj) {
|
|
return Buffer.isBuffer(obj)
|
|
}
|
|
|
|
DeferredLevelDOWN.prototype._iterator = function (options) {
|
|
if (this._db)
|
|
return this._db.iterator.apply(this._db, arguments)
|
|
var it = new DeferredIterator(options)
|
|
this._iterators.push(it)
|
|
return it
|
|
}
|
|
|
|
module.exports = DeferredLevelDOWN
|
|
module.exports.DeferredIterator = DeferredIterator
|
|
|
|
}).call(this,{"isBuffer":require("../is-buffer/index.js")},require('_process'))
|
|
},{"../is-buffer/index.js":88,"./deferred-iterator":45,"_process":133,"abstract-leveldown":50,"util":297}],47:[function(require,module,exports){
|
|
(function (process){
|
|
/* Copyright (c) 2013 Rod Vagg, MIT License */
|
|
|
|
function AbstractChainedBatch (db) {
|
|
this._db = db
|
|
this._operations = []
|
|
this._written = false
|
|
}
|
|
|
|
AbstractChainedBatch.prototype._checkWritten = function () {
|
|
if (this._written)
|
|
throw new Error('write() already called on this batch')
|
|
}
|
|
|
|
AbstractChainedBatch.prototype.put = function (key, value) {
|
|
this._checkWritten()
|
|
|
|
var err = this._db._checkKey(key, 'key', this._db._isBuffer)
|
|
if (err)
|
|
throw err
|
|
|
|
if (!this._db._isBuffer(key)) key = String(key)
|
|
if (!this._db._isBuffer(value)) value = String(value)
|
|
|
|
if (typeof this._put == 'function' )
|
|
this._put(key, value)
|
|
else
|
|
this._operations.push({ type: 'put', key: key, value: value })
|
|
|
|
return this
|
|
}
|
|
|
|
AbstractChainedBatch.prototype.del = function (key) {
|
|
this._checkWritten()
|
|
|
|
var err = this._db._checkKey(key, 'key', this._db._isBuffer)
|
|
if (err) throw err
|
|
|
|
if (!this._db._isBuffer(key)) key = String(key)
|
|
|
|
if (typeof this._del == 'function' )
|
|
this._del(key)
|
|
else
|
|
this._operations.push({ type: 'del', key: key })
|
|
|
|
return this
|
|
}
|
|
|
|
AbstractChainedBatch.prototype.clear = function () {
|
|
this._checkWritten()
|
|
|
|
this._operations = []
|
|
|
|
if (typeof this._clear == 'function' )
|
|
this._clear()
|
|
|
|
return this
|
|
}
|
|
|
|
AbstractChainedBatch.prototype.write = function (options, callback) {
|
|
this._checkWritten()
|
|
|
|
if (typeof options == 'function')
|
|
callback = options
|
|
if (typeof callback != 'function')
|
|
throw new Error('write() requires a callback argument')
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
|
|
this._written = true
|
|
|
|
if (typeof this._write == 'function' )
|
|
return this._write(callback)
|
|
|
|
if (typeof this._db._batch == 'function')
|
|
return this._db._batch(this._operations, options, callback)
|
|
|
|
process.nextTick(callback)
|
|
}
|
|
|
|
module.exports = AbstractChainedBatch
|
|
}).call(this,require('_process'))
|
|
},{"_process":133}],48:[function(require,module,exports){
|
|
(function (process){
|
|
/* Copyright (c) 2013 Rod Vagg, MIT License */
|
|
|
|
function AbstractIterator (db) {
|
|
this.db = db
|
|
this._ended = false
|
|
this._nexting = false
|
|
}
|
|
|
|
AbstractIterator.prototype.next = function (callback) {
|
|
var self = this
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('next() requires a callback argument')
|
|
|
|
if (self._ended)
|
|
return callback(new Error('cannot call next() after end()'))
|
|
if (self._nexting)
|
|
return callback(new Error('cannot call next() before previous next() has completed'))
|
|
|
|
self._nexting = true
|
|
if (typeof self._next == 'function') {
|
|
return self._next(function () {
|
|
self._nexting = false
|
|
callback.apply(null, arguments)
|
|
})
|
|
}
|
|
|
|
process.nextTick(function () {
|
|
self._nexting = false
|
|
callback()
|
|
})
|
|
}
|
|
|
|
AbstractIterator.prototype.end = function (callback) {
|
|
if (typeof callback != 'function')
|
|
throw new Error('end() requires a callback argument')
|
|
|
|
if (this._ended)
|
|
return callback(new Error('end() already called on iterator'))
|
|
|
|
this._ended = true
|
|
|
|
if (typeof this._end == 'function')
|
|
return this._end(callback)
|
|
|
|
process.nextTick(callback)
|
|
}
|
|
|
|
module.exports = AbstractIterator
|
|
|
|
}).call(this,require('_process'))
|
|
},{"_process":133}],49:[function(require,module,exports){
|
|
(function (Buffer,process){
|
|
/* Copyright (c) 2013 Rod Vagg, MIT License */
|
|
|
|
var xtend = require('xtend')
|
|
, AbstractIterator = require('./abstract-iterator')
|
|
, AbstractChainedBatch = require('./abstract-chained-batch')
|
|
|
|
function AbstractLevelDOWN (location) {
|
|
if (!arguments.length || location === undefined)
|
|
throw new Error('constructor requires at least a location argument')
|
|
|
|
if (typeof location != 'string')
|
|
throw new Error('constructor requires a location string argument')
|
|
|
|
this.location = location
|
|
this.status = 'new'
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.open = function (options, callback) {
|
|
var self = this
|
|
, oldStatus = this.status
|
|
|
|
if (typeof options == 'function')
|
|
callback = options
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('open() requires a callback argument')
|
|
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
|
|
options.createIfMissing = options.createIfMissing != false
|
|
options.errorIfExists = !!options.errorIfExists
|
|
|
|
if (typeof this._open == 'function') {
|
|
this.status = 'opening'
|
|
this._open(options, function (err) {
|
|
if (err) {
|
|
self.status = oldStatus
|
|
return callback(err)
|
|
}
|
|
self.status = 'open'
|
|
callback()
|
|
})
|
|
} else {
|
|
this.status = 'open'
|
|
process.nextTick(callback)
|
|
}
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.close = function (callback) {
|
|
var self = this
|
|
, oldStatus = this.status
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('close() requires a callback argument')
|
|
|
|
if (typeof this._close == 'function') {
|
|
this.status = 'closing'
|
|
this._close(function (err) {
|
|
if (err) {
|
|
self.status = oldStatus
|
|
return callback(err)
|
|
}
|
|
self.status = 'closed'
|
|
callback()
|
|
})
|
|
} else {
|
|
this.status = 'closed'
|
|
process.nextTick(callback)
|
|
}
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.get = function (key, options, callback) {
|
|
var err
|
|
|
|
if (typeof options == 'function')
|
|
callback = options
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('get() requires a callback argument')
|
|
|
|
if (err = this._checkKey(key, 'key', this._isBuffer))
|
|
return callback(err)
|
|
|
|
if (!this._isBuffer(key))
|
|
key = String(key)
|
|
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
|
|
options.asBuffer = options.asBuffer != false
|
|
|
|
if (typeof this._get == 'function')
|
|
return this._get(key, options, callback)
|
|
|
|
process.nextTick(function () { callback(new Error('NotFound')) })
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.put = function (key, value, options, callback) {
|
|
var err
|
|
|
|
if (typeof options == 'function')
|
|
callback = options
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('put() requires a callback argument')
|
|
|
|
if (err = this._checkKey(key, 'key', this._isBuffer))
|
|
return callback(err)
|
|
|
|
if (!this._isBuffer(key))
|
|
key = String(key)
|
|
|
|
// coerce value to string in node, don't touch it in browser
|
|
// (indexeddb can store any JS type)
|
|
if (value != null && !this._isBuffer(value) && !process.browser)
|
|
value = String(value)
|
|
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
|
|
if (typeof this._put == 'function')
|
|
return this._put(key, value, options, callback)
|
|
|
|
process.nextTick(callback)
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.del = function (key, options, callback) {
|
|
var err
|
|
|
|
if (typeof options == 'function')
|
|
callback = options
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('del() requires a callback argument')
|
|
|
|
if (err = this._checkKey(key, 'key', this._isBuffer))
|
|
return callback(err)
|
|
|
|
if (!this._isBuffer(key))
|
|
key = String(key)
|
|
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
|
|
if (typeof this._del == 'function')
|
|
return this._del(key, options, callback)
|
|
|
|
process.nextTick(callback)
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.batch = function (array, options, callback) {
|
|
if (!arguments.length)
|
|
return this._chainedBatch()
|
|
|
|
if (typeof options == 'function')
|
|
callback = options
|
|
|
|
if (typeof array == 'function')
|
|
callback = array
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('batch(array) requires a callback argument')
|
|
|
|
if (!Array.isArray(array))
|
|
return callback(new Error('batch(array) requires an array argument'))
|
|
|
|
if (!options || typeof options != 'object')
|
|
options = {}
|
|
|
|
var i = 0
|
|
, l = array.length
|
|
, e
|
|
, err
|
|
|
|
for (; i < l; i++) {
|
|
e = array[i]
|
|
if (typeof e != 'object')
|
|
continue
|
|
|
|
if (err = this._checkKey(e.type, 'type', this._isBuffer))
|
|
return callback(err)
|
|
|
|
if (err = this._checkKey(e.key, 'key', this._isBuffer))
|
|
return callback(err)
|
|
}
|
|
|
|
if (typeof this._batch == 'function')
|
|
return this._batch(array, options, callback)
|
|
|
|
process.nextTick(callback)
|
|
}
|
|
|
|
//TODO: remove from here, not a necessary primitive
|
|
AbstractLevelDOWN.prototype.approximateSize = function (start, end, callback) {
|
|
if ( start == null
|
|
|| end == null
|
|
|| typeof start == 'function'
|
|
|| typeof end == 'function') {
|
|
throw new Error('approximateSize() requires valid `start`, `end` and `callback` arguments')
|
|
}
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('approximateSize() requires a callback argument')
|
|
|
|
if (!this._isBuffer(start))
|
|
start = String(start)
|
|
|
|
if (!this._isBuffer(end))
|
|
end = String(end)
|
|
|
|
if (typeof this._approximateSize == 'function')
|
|
return this._approximateSize(start, end, callback)
|
|
|
|
process.nextTick(function () {
|
|
callback(null, 0)
|
|
})
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype._setupIteratorOptions = function (options) {
|
|
var self = this
|
|
|
|
options = xtend(options)
|
|
|
|
;[ 'start', 'end', 'gt', 'gte', 'lt', 'lte' ].forEach(function (o) {
|
|
if (options[o] && self._isBuffer(options[o]) && options[o].length === 0)
|
|
delete options[o]
|
|
})
|
|
|
|
options.reverse = !!options.reverse
|
|
options.keys = options.keys != false
|
|
options.values = options.values != false
|
|
options.limit = 'limit' in options ? options.limit : -1
|
|
options.keyAsBuffer = options.keyAsBuffer != false
|
|
options.valueAsBuffer = options.valueAsBuffer != false
|
|
|
|
return options
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.iterator = function (options) {
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
|
|
options = this._setupIteratorOptions(options)
|
|
|
|
if (typeof this._iterator == 'function')
|
|
return this._iterator(options)
|
|
|
|
return new AbstractIterator(this)
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype._chainedBatch = function () {
|
|
return new AbstractChainedBatch(this)
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype._isBuffer = function (obj) {
|
|
return Buffer.isBuffer(obj)
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype._checkKey = function (obj, type) {
|
|
|
|
if (obj === null || obj === undefined)
|
|
return new Error(type + ' cannot be `null` or `undefined`')
|
|
|
|
if (this._isBuffer(obj)) {
|
|
if (obj.length === 0)
|
|
return new Error(type + ' cannot be an empty Buffer')
|
|
} else if (String(obj) === '')
|
|
return new Error(type + ' cannot be an empty String')
|
|
}
|
|
|
|
module.exports = AbstractLevelDOWN
|
|
|
|
}).call(this,{"isBuffer":require("../../../is-buffer/index.js")},require('_process'))
|
|
},{"../../../is-buffer/index.js":88,"./abstract-chained-batch":47,"./abstract-iterator":48,"_process":133,"xtend":299}],50:[function(require,module,exports){
|
|
exports.AbstractLevelDOWN = require('./abstract-leveldown')
|
|
exports.AbstractIterator = require('./abstract-iterator')
|
|
exports.AbstractChainedBatch = require('./abstract-chained-batch')
|
|
exports.isLevelDOWN = require('./is-leveldown')
|
|
|
|
},{"./abstract-chained-batch":47,"./abstract-iterator":48,"./abstract-leveldown":49,"./is-leveldown":51}],51:[function(require,module,exports){
|
|
var AbstractLevelDOWN = require('./abstract-leveldown')
|
|
|
|
function isLevelDOWN (db) {
|
|
if (!db || typeof db !== 'object')
|
|
return false
|
|
return Object.keys(AbstractLevelDOWN.prototype).filter(function (name) {
|
|
// TODO remove approximateSize check when method is gone
|
|
return name[0] != '_' && name != 'approximateSize'
|
|
}).every(function (name) {
|
|
return typeof db[name] == 'function'
|
|
})
|
|
}
|
|
|
|
module.exports = isLevelDOWN
|
|
|
|
},{"./abstract-leveldown":49}],52:[function(require,module,exports){
|
|
/**
|
|
* Copyright (c) 2013 Petka Antonov
|
|
*
|
|
* 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:</p>
|
|
*
|
|
* 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.
|
|
*/
|
|
"use strict";
|
|
function Deque(capacity) {
|
|
this._capacity = getCapacity(capacity);
|
|
this._length = 0;
|
|
this._front = 0;
|
|
this._makeCapacity();
|
|
if (isArray(capacity)) {
|
|
var len = capacity.length;
|
|
for (var i = 0; i < len; ++i) {
|
|
this[i] = capacity[i];
|
|
}
|
|
this._length = len;
|
|
}
|
|
}
|
|
|
|
Deque.prototype.toArray = function Deque$toArray() {
|
|
var len = this._length;
|
|
var ret = new Array(len);
|
|
var front = this._front;
|
|
var capacity = this._capacity;
|
|
for (var j = 0; j < len; ++j) {
|
|
ret[j] = this[(front + j) & (capacity - 1)];
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
Deque.prototype.push = function Deque$push(item) {
|
|
var argsLength = arguments.length;
|
|
var length = this._length;
|
|
if (argsLength > 1) {
|
|
var capacity = this._capacity;
|
|
if (length + argsLength > capacity) {
|
|
for (var i = 0; i < argsLength; ++i) {
|
|
this._checkCapacity(length + 1);
|
|
var j = (this._front + length) & (this._capacity - 1);
|
|
this[j] = arguments[i];
|
|
length++;
|
|
this._length = length;
|
|
}
|
|
return length;
|
|
}
|
|
else {
|
|
var j = this._front;
|
|
for (var i = 0; i < argsLength; ++i) {
|
|
this[(j + length) & (capacity - 1)] = arguments[i];
|
|
j++;
|
|
}
|
|
this._length = length + argsLength;
|
|
return length + argsLength;
|
|
}
|
|
|
|
}
|
|
|
|
if (argsLength === 0) return length;
|
|
|
|
this._checkCapacity(length + 1);
|
|
var i = (this._front + length) & (this._capacity - 1);
|
|
this[i] = item;
|
|
this._length = length + 1;
|
|
return length + 1;
|
|
};
|
|
|
|
Deque.prototype.pop = function Deque$pop() {
|
|
var length = this._length;
|
|
if (length === 0) {
|
|
return void 0;
|
|
}
|
|
var i = (this._front + length - 1) & (this._capacity - 1);
|
|
var ret = this[i];
|
|
this[i] = void 0;
|
|
this._length = length - 1;
|
|
return ret;
|
|
};
|
|
|
|
Deque.prototype.shift = function Deque$shift() {
|
|
var length = this._length;
|
|
if (length === 0) {
|
|
return void 0;
|
|
}
|
|
var front = this._front;
|
|
var ret = this[front];
|
|
this[front] = void 0;
|
|
this._front = (front + 1) & (this._capacity - 1);
|
|
this._length = length - 1;
|
|
return ret;
|
|
};
|
|
|
|
Deque.prototype.unshift = function Deque$unshift(item) {
|
|
var length = this._length;
|
|
var argsLength = arguments.length;
|
|
|
|
|
|
if (argsLength > 1) {
|
|
var capacity = this._capacity;
|
|
if (length + argsLength > capacity) {
|
|
for (var i = argsLength - 1; i >= 0; i--) {
|
|
this._checkCapacity(length + 1);
|
|
var capacity = this._capacity;
|
|
var j = (((( this._front - 1 ) &
|
|
( capacity - 1) ) ^ capacity ) - capacity );
|
|
this[j] = arguments[i];
|
|
length++;
|
|
this._length = length;
|
|
this._front = j;
|
|
}
|
|
return length;
|
|
}
|
|
else {
|
|
var front = this._front;
|
|
for (var i = argsLength - 1; i >= 0; i--) {
|
|
var j = (((( front - 1 ) &
|
|
( capacity - 1) ) ^ capacity ) - capacity );
|
|
this[j] = arguments[i];
|
|
front = j;
|
|
}
|
|
this._front = front;
|
|
this._length = length + argsLength;
|
|
return length + argsLength;
|
|
}
|
|
}
|
|
|
|
if (argsLength === 0) return length;
|
|
|
|
this._checkCapacity(length + 1);
|
|
var capacity = this._capacity;
|
|
var i = (((( this._front - 1 ) &
|
|
( capacity - 1) ) ^ capacity ) - capacity );
|
|
this[i] = item;
|
|
this._length = length + 1;
|
|
this._front = i;
|
|
return length + 1;
|
|
};
|
|
|
|
Deque.prototype.peekBack = function Deque$peekBack() {
|
|
var length = this._length;
|
|
if (length === 0) {
|
|
return void 0;
|
|
}
|
|
var index = (this._front + length - 1) & (this._capacity - 1);
|
|
return this[index];
|
|
};
|
|
|
|
Deque.prototype.peekFront = function Deque$peekFront() {
|
|
if (this._length === 0) {
|
|
return void 0;
|
|
}
|
|
return this[this._front];
|
|
};
|
|
|
|
Deque.prototype.get = function Deque$get(index) {
|
|
var i = index;
|
|
if ((i !== (i | 0))) {
|
|
return void 0;
|
|
}
|
|
var len = this._length;
|
|
if (i < 0) {
|
|
i = i + len;
|
|
}
|
|
if (i < 0 || i >= len) {
|
|
return void 0;
|
|
}
|
|
return this[(this._front + i) & (this._capacity - 1)];
|
|
};
|
|
|
|
Deque.prototype.isEmpty = function Deque$isEmpty() {
|
|
return this._length === 0;
|
|
};
|
|
|
|
Deque.prototype.clear = function Deque$clear() {
|
|
this._length = 0;
|
|
this._front = 0;
|
|
this._makeCapacity();
|
|
};
|
|
|
|
Deque.prototype.toString = function Deque$toString() {
|
|
return this.toArray().toString();
|
|
};
|
|
|
|
Deque.prototype.valueOf = Deque.prototype.toString;
|
|
Deque.prototype.removeFront = Deque.prototype.shift;
|
|
Deque.prototype.removeBack = Deque.prototype.pop;
|
|
Deque.prototype.insertFront = Deque.prototype.unshift;
|
|
Deque.prototype.insertBack = Deque.prototype.push;
|
|
Deque.prototype.enqueue = Deque.prototype.push;
|
|
Deque.prototype.dequeue = Deque.prototype.shift;
|
|
Deque.prototype.toJSON = Deque.prototype.toArray;
|
|
|
|
Object.defineProperty(Deque.prototype, "length", {
|
|
get: function() {
|
|
return this._length;
|
|
},
|
|
set: function() {
|
|
throw new RangeError("");
|
|
}
|
|
});
|
|
|
|
Deque.prototype._makeCapacity = function Deque$_makeCapacity() {
|
|
var len = this._capacity;
|
|
for (var i = 0; i < len; ++i) {
|
|
this[i] = void 0;
|
|
}
|
|
};
|
|
|
|
Deque.prototype._checkCapacity = function Deque$_checkCapacity(size) {
|
|
if (this._capacity < size) {
|
|
this._resizeTo(getCapacity(this._capacity * 1.5 + 16));
|
|
}
|
|
};
|
|
|
|
Deque.prototype._resizeTo = function Deque$_resizeTo(capacity) {
|
|
var oldFront = this._front;
|
|
var oldCapacity = this._capacity;
|
|
var oldDeque = new Array(oldCapacity);
|
|
var length = this._length;
|
|
|
|
arrayCopy(this, 0, oldDeque, 0, oldCapacity);
|
|
this._capacity = capacity;
|
|
this._makeCapacity();
|
|
this._front = 0;
|
|
if (oldFront + length <= oldCapacity) {
|
|
arrayCopy(oldDeque, oldFront, this, 0, length);
|
|
} else { var lengthBeforeWrapping =
|
|
length - ((oldFront + length) & (oldCapacity - 1));
|
|
|
|
arrayCopy(oldDeque, oldFront, this, 0, lengthBeforeWrapping);
|
|
arrayCopy(oldDeque, 0, this, lengthBeforeWrapping,
|
|
length - lengthBeforeWrapping);
|
|
}
|
|
};
|
|
|
|
|
|
var isArray = Array.isArray;
|
|
|
|
function arrayCopy(src, srcIndex, dst, dstIndex, len) {
|
|
for (var j = 0; j < len; ++j) {
|
|
dst[j + dstIndex] = src[j + srcIndex];
|
|
}
|
|
}
|
|
|
|
function pow2AtLeast(n) {
|
|
n = n >>> 0;
|
|
n = n - 1;
|
|
n = n | (n >> 1);
|
|
n = n | (n >> 2);
|
|
n = n | (n >> 4);
|
|
n = n | (n >> 8);
|
|
n = n | (n >> 16);
|
|
return n + 1;
|
|
}
|
|
|
|
function getCapacity(capacity) {
|
|
if (typeof capacity !== "number") {
|
|
if (isArray(capacity)) {
|
|
capacity = capacity.length;
|
|
}
|
|
else {
|
|
return 16;
|
|
}
|
|
}
|
|
return pow2AtLeast(
|
|
Math.min(
|
|
Math.max(16, capacity), 1073741824)
|
|
);
|
|
}
|
|
|
|
module.exports = Deque;
|
|
|
|
},{}],53:[function(require,module,exports){
|
|
var prr = require('prr')
|
|
|
|
function init (type, message, cause) {
|
|
prr(this, {
|
|
type : type
|
|
, name : type
|
|
// can be passed just a 'cause'
|
|
, cause : typeof message != 'string' ? message : cause
|
|
, message : !!message && typeof message != 'string' ? message.message : message
|
|
|
|
}, 'ewr')
|
|
}
|
|
|
|
// generic prototype, not intended to be actually used - helpful for `instanceof`
|
|
function CustomError (message, cause) {
|
|
Error.call(this)
|
|
if (Error.captureStackTrace)
|
|
Error.captureStackTrace(this, arguments.callee)
|
|
init.call(this, 'CustomError', message, cause)
|
|
}
|
|
|
|
CustomError.prototype = new Error()
|
|
|
|
function createError (errno, type, proto) {
|
|
var err = function (message, cause) {
|
|
init.call(this, type, message, cause)
|
|
//TODO: the specificity here is stupid, errno should be available everywhere
|
|
if (type == 'FilesystemError') {
|
|
this.code = this.cause.code
|
|
this.path = this.cause.path
|
|
this.errno = this.cause.errno
|
|
this.message =
|
|
(errno.errno[this.cause.errno]
|
|
? errno.errno[this.cause.errno].description
|
|
: this.cause.message)
|
|
+ (this.cause.path ? ' [' + this.cause.path + ']' : '')
|
|
}
|
|
Error.call(this)
|
|
if (Error.captureStackTrace)
|
|
Error.captureStackTrace(this, arguments.callee)
|
|
}
|
|
err.prototype = !!proto ? new proto() : new CustomError()
|
|
return err
|
|
}
|
|
|
|
module.exports = function (errno) {
|
|
var ce = function (type, proto) {
|
|
return createError(errno, type, proto)
|
|
}
|
|
return {
|
|
CustomError : CustomError
|
|
, FilesystemError : ce('FilesystemError')
|
|
, createError : ce
|
|
}
|
|
}
|
|
|
|
},{"prr":134}],54:[function(require,module,exports){
|
|
var all = module.exports.all = [
|
|
{
|
|
errno: -2,
|
|
code: 'ENOENT',
|
|
description: 'no such file or directory'
|
|
},
|
|
{
|
|
errno: -1,
|
|
code: 'UNKNOWN',
|
|
description: 'unknown error'
|
|
},
|
|
{
|
|
errno: 0,
|
|
code: 'OK',
|
|
description: 'success'
|
|
},
|
|
{
|
|
errno: 1,
|
|
code: 'EOF',
|
|
description: 'end of file'
|
|
},
|
|
{
|
|
errno: 2,
|
|
code: 'EADDRINFO',
|
|
description: 'getaddrinfo error'
|
|
},
|
|
{
|
|
errno: 3,
|
|
code: 'EACCES',
|
|
description: 'permission denied'
|
|
},
|
|
{
|
|
errno: 4,
|
|
code: 'EAGAIN',
|
|
description: 'resource temporarily unavailable'
|
|
},
|
|
{
|
|
errno: 5,
|
|
code: 'EADDRINUSE',
|
|
description: 'address already in use'
|
|
},
|
|
{
|
|
errno: 6,
|
|
code: 'EADDRNOTAVAIL',
|
|
description: 'address not available'
|
|
},
|
|
{
|
|
errno: 7,
|
|
code: 'EAFNOSUPPORT',
|
|
description: 'address family not supported'
|
|
},
|
|
{
|
|
errno: 8,
|
|
code: 'EALREADY',
|
|
description: 'connection already in progress'
|
|
},
|
|
{
|
|
errno: 9,
|
|
code: 'EBADF',
|
|
description: 'bad file descriptor'
|
|
},
|
|
{
|
|
errno: 10,
|
|
code: 'EBUSY',
|
|
description: 'resource busy or locked'
|
|
},
|
|
{
|
|
errno: 11,
|
|
code: 'ECONNABORTED',
|
|
description: 'software caused connection abort'
|
|
},
|
|
{
|
|
errno: 12,
|
|
code: 'ECONNREFUSED',
|
|
description: 'connection refused'
|
|
},
|
|
{
|
|
errno: 13,
|
|
code: 'ECONNRESET',
|
|
description: 'connection reset by peer'
|
|
},
|
|
{
|
|
errno: 14,
|
|
code: 'EDESTADDRREQ',
|
|
description: 'destination address required'
|
|
},
|
|
{
|
|
errno: 15,
|
|
code: 'EFAULT',
|
|
description: 'bad address in system call argument'
|
|
},
|
|
{
|
|
errno: 16,
|
|
code: 'EHOSTUNREACH',
|
|
description: 'host is unreachable'
|
|
},
|
|
{
|
|
errno: 17,
|
|
code: 'EINTR',
|
|
description: 'interrupted system call'
|
|
},
|
|
{
|
|
errno: 18,
|
|
code: 'EINVAL',
|
|
description: 'invalid argument'
|
|
},
|
|
{
|
|
errno: 19,
|
|
code: 'EISCONN',
|
|
description: 'socket is already connected'
|
|
},
|
|
{
|
|
errno: 20,
|
|
code: 'EMFILE',
|
|
description: 'too many open files'
|
|
},
|
|
{
|
|
errno: 21,
|
|
code: 'EMSGSIZE',
|
|
description: 'message too long'
|
|
},
|
|
{
|
|
errno: 22,
|
|
code: 'ENETDOWN',
|
|
description: 'network is down'
|
|
},
|
|
{
|
|
errno: 23,
|
|
code: 'ENETUNREACH',
|
|
description: 'network is unreachable'
|
|
},
|
|
{
|
|
errno: 24,
|
|
code: 'ENFILE',
|
|
description: 'file table overflow'
|
|
},
|
|
{
|
|
errno: 25,
|
|
code: 'ENOBUFS',
|
|
description: 'no buffer space available'
|
|
},
|
|
{
|
|
errno: 26,
|
|
code: 'ENOMEM',
|
|
description: 'not enough memory'
|
|
},
|
|
{
|
|
errno: 27,
|
|
code: 'ENOTDIR',
|
|
description: 'not a directory'
|
|
},
|
|
{
|
|
errno: 28,
|
|
code: 'EISDIR',
|
|
description: 'illegal operation on a directory'
|
|
},
|
|
{
|
|
errno: 29,
|
|
code: 'ENONET',
|
|
description: 'machine is not on the network'
|
|
},
|
|
{
|
|
errno: 31,
|
|
code: 'ENOTCONN',
|
|
description: 'socket is not connected'
|
|
},
|
|
{
|
|
errno: 32,
|
|
code: 'ENOTSOCK',
|
|
description: 'socket operation on non-socket'
|
|
},
|
|
{
|
|
errno: 33,
|
|
code: 'ENOTSUP',
|
|
description: 'operation not supported on socket'
|
|
},
|
|
{
|
|
errno: 34,
|
|
code: 'ENOENT',
|
|
description: 'no such file or directory'
|
|
},
|
|
{
|
|
errno: 35,
|
|
code: 'ENOSYS',
|
|
description: 'function not implemented'
|
|
},
|
|
{
|
|
errno: 36,
|
|
code: 'EPIPE',
|
|
description: 'broken pipe'
|
|
},
|
|
{
|
|
errno: 37,
|
|
code: 'EPROTO',
|
|
description: 'protocol error'
|
|
},
|
|
{
|
|
errno: 38,
|
|
code: 'EPROTONOSUPPORT',
|
|
description: 'protocol not supported'
|
|
},
|
|
{
|
|
errno: 39,
|
|
code: 'EPROTOTYPE',
|
|
description: 'protocol wrong type for socket'
|
|
},
|
|
{
|
|
errno: 40,
|
|
code: 'ETIMEDOUT',
|
|
description: 'connection timed out'
|
|
},
|
|
{
|
|
errno: 41,
|
|
code: 'ECHARSET',
|
|
description: 'invalid Unicode character'
|
|
},
|
|
{
|
|
errno: 42,
|
|
code: 'EAIFAMNOSUPPORT',
|
|
description: 'address family for hostname not supported'
|
|
},
|
|
{
|
|
errno: 44,
|
|
code: 'EAISERVICE',
|
|
description: 'servname not supported for ai_socktype'
|
|
},
|
|
{
|
|
errno: 45,
|
|
code: 'EAISOCKTYPE',
|
|
description: 'ai_socktype not supported'
|
|
},
|
|
{
|
|
errno: 46,
|
|
code: 'ESHUTDOWN',
|
|
description: 'cannot send after transport endpoint shutdown'
|
|
},
|
|
{
|
|
errno: 47,
|
|
code: 'EEXIST',
|
|
description: 'file already exists'
|
|
},
|
|
{
|
|
errno: 48,
|
|
code: 'ESRCH',
|
|
description: 'no such process'
|
|
},
|
|
{
|
|
errno: 49,
|
|
code: 'ENAMETOOLONG',
|
|
description: 'name too long'
|
|
},
|
|
{
|
|
errno: 50,
|
|
code: 'EPERM',
|
|
description: 'operation not permitted'
|
|
},
|
|
{
|
|
errno: 51,
|
|
code: 'ELOOP',
|
|
description: 'too many symbolic links encountered'
|
|
},
|
|
{
|
|
errno: 52,
|
|
code: 'EXDEV',
|
|
description: 'cross-device link not permitted'
|
|
},
|
|
{
|
|
errno: 53,
|
|
code: 'ENOTEMPTY',
|
|
description: 'directory not empty'
|
|
},
|
|
{
|
|
errno: 54,
|
|
code: 'ENOSPC',
|
|
description: 'no space left on device'
|
|
},
|
|
{
|
|
errno: 55,
|
|
code: 'EIO',
|
|
description: 'i/o error'
|
|
},
|
|
{
|
|
errno: 56,
|
|
code: 'EROFS',
|
|
description: 'read-only file system'
|
|
},
|
|
{
|
|
errno: 57,
|
|
code: 'ENODEV',
|
|
description: 'no such device'
|
|
},
|
|
{
|
|
errno: 58,
|
|
code: 'ESPIPE',
|
|
description: 'invalid seek'
|
|
},
|
|
{
|
|
errno: 59,
|
|
code: 'ECANCELED',
|
|
description: 'operation canceled'
|
|
}
|
|
]
|
|
|
|
module.exports.errno = {}
|
|
module.exports.code = {}
|
|
|
|
all.forEach(function (error) {
|
|
module.exports.errno[error.errno] = error
|
|
module.exports.code[error.code] = error
|
|
})
|
|
|
|
module.exports.custom = require('./custom')(module.exports)
|
|
module.exports.create = module.exports.custom.createError
|
|
|
|
},{"./custom":53}],55:[function(require,module,exports){
|
|
(function (root, factory) {
|
|
/* istanbul ignore next */
|
|
if (typeof define === 'function' && define.amd) {
|
|
define([], factory)
|
|
} else if (typeof exports === 'object') {
|
|
module.exports = factory()
|
|
} else {
|
|
root.PromisePool = factory()
|
|
// Legacy API
|
|
root.promisePool = root.PromisePool
|
|
}
|
|
})(this, function () {
|
|
'use strict'
|
|
|
|
var EventTarget = function () {
|
|
this._listeners = {}
|
|
}
|
|
|
|
EventTarget.prototype.addEventListener = function (type, listener) {
|
|
this._listeners[type] = this._listeners[type] || []
|
|
if (this._listeners[type].indexOf(listener) < 0) {
|
|
this._listeners[type].push(listener)
|
|
}
|
|
}
|
|
|
|
EventTarget.prototype.removeEventListener = function (type, listener) {
|
|
if (this._listeners[type]) {
|
|
var p = this._listeners[type].indexOf(listener)
|
|
if (p >= 0) {
|
|
this._listeners[type].splice(p, 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
EventTarget.prototype.dispatchEvent = function (evt) {
|
|
if (this._listeners[evt.type] && this._listeners[evt.type].length) {
|
|
var listeners = this._listeners[evt.type].slice()
|
|
for (var i = 0, l = listeners.length; i < l; ++i) {
|
|
listeners[i].call(this, evt)
|
|
}
|
|
}
|
|
}
|
|
|
|
var isGenerator = function (func) {
|
|
return (typeof func.constructor === 'function' &&
|
|
func.constructor.name === 'GeneratorFunction')
|
|
}
|
|
|
|
var functionToIterator = function (func) {
|
|
return {
|
|
next: function () {
|
|
var promise = func()
|
|
return promise ? {value: promise} : {done: true}
|
|
}
|
|
}
|
|
}
|
|
|
|
var promiseToIterator = function (promise) {
|
|
var called = false
|
|
return {
|
|
next: function () {
|
|
if (called) {
|
|
return {done: true}
|
|
}
|
|
called = true
|
|
return {value: promise}
|
|
}
|
|
}
|
|
}
|
|
|
|
var toIterator = function (obj, Promise) {
|
|
var type = typeof obj
|
|
if (type === 'object') {
|
|
if (typeof obj.next === 'function') {
|
|
return obj
|
|
}
|
|
/* istanbul ignore else */
|
|
if (typeof obj.then === 'function') {
|
|
return promiseToIterator(obj)
|
|
}
|
|
}
|
|
if (type === 'function') {
|
|
return isGenerator(obj) ? obj() : functionToIterator(obj)
|
|
}
|
|
return promiseToIterator(Promise.resolve(obj))
|
|
}
|
|
|
|
var PromisePoolEvent = function (target, type, data) {
|
|
this.target = target
|
|
this.type = type
|
|
this.data = data
|
|
}
|
|
|
|
var PromisePool = function (source, concurrency, options) {
|
|
EventTarget.call(this)
|
|
if (typeof concurrency !== 'number' ||
|
|
Math.floor(concurrency) !== concurrency ||
|
|
concurrency < 1) {
|
|
throw new Error('Invalid concurrency')
|
|
}
|
|
this._concurrency = concurrency
|
|
this._options = options || {}
|
|
this._options.promise = this._options.promise || Promise
|
|
this._iterator = toIterator(source, this._options.promise)
|
|
this._done = false
|
|
this._size = 0
|
|
this._promise = null
|
|
this._callbacks = null
|
|
}
|
|
PromisePool.prototype = new EventTarget()
|
|
PromisePool.prototype.constructor = PromisePool
|
|
|
|
PromisePool.prototype.concurrency = function (value) {
|
|
if (typeof value !== 'undefined') {
|
|
this._concurrency = value
|
|
if (this.active()) {
|
|
this._proceed()
|
|
}
|
|
}
|
|
return this._concurrency
|
|
}
|
|
|
|
PromisePool.prototype.size = function () {
|
|
return this._size
|
|
}
|
|
|
|
PromisePool.prototype.active = function () {
|
|
return !!this._promise
|
|
}
|
|
|
|
PromisePool.prototype.promise = function () {
|
|
return this._promise
|
|
}
|
|
|
|
PromisePool.prototype.start = function () {
|
|
var that = this
|
|
var Promise = this._options.promise
|
|
this._promise = new Promise(function (resolve, reject) {
|
|
that._callbacks = {
|
|
reject: reject,
|
|
resolve: resolve
|
|
}
|
|
that._proceed()
|
|
})
|
|
return this._promise
|
|
}
|
|
|
|
PromisePool.prototype._fireEvent = function (type, data) {
|
|
this.dispatchEvent(new PromisePoolEvent(this, type, data))
|
|
}
|
|
|
|
PromisePool.prototype._settle = function (error) {
|
|
if (error) {
|
|
this._callbacks.reject(error)
|
|
} else {
|
|
this._callbacks.resolve()
|
|
}
|
|
this._promise = null
|
|
this._callbacks = null
|
|
}
|
|
|
|
PromisePool.prototype._onPooledPromiseFulfilled = function (promise, result) {
|
|
this._size--
|
|
if (this.active()) {
|
|
this._fireEvent('fulfilled', {
|
|
promise: promise,
|
|
result: result
|
|
})
|
|
this._proceed()
|
|
}
|
|
}
|
|
|
|
PromisePool.prototype._onPooledPromiseRejected = function (promise, error) {
|
|
this._size--
|
|
if (this.active()) {
|
|
this._fireEvent('rejected', {
|
|
promise: promise,
|
|
error: error
|
|
})
|
|
this._settle(error || new Error('Unknown error'))
|
|
}
|
|
}
|
|
|
|
PromisePool.prototype._trackPromise = function (promise) {
|
|
var that = this
|
|
promise
|
|
.then(function (result) {
|
|
that._onPooledPromiseFulfilled(promise, result)
|
|
}, function (error) {
|
|
that._onPooledPromiseRejected(promise, error)
|
|
})['catch'](function (err) {
|
|
that._settle(new Error('Promise processing failed: ' + err))
|
|
})
|
|
}
|
|
|
|
PromisePool.prototype._proceed = function () {
|
|
if (!this._done) {
|
|
var result = null
|
|
while (this._size < this._concurrency &&
|
|
!(result = this._iterator.next()).done) {
|
|
this._size++
|
|
this._trackPromise(result.value)
|
|
}
|
|
this._done = (result === null || !!result.done)
|
|
}
|
|
if (this._done && this._size === 0) {
|
|
this._settle()
|
|
}
|
|
}
|
|
|
|
PromisePool.PromisePoolEvent = PromisePoolEvent
|
|
// Legacy API
|
|
PromisePool.PromisePool = PromisePool
|
|
|
|
return PromisePool
|
|
})
|
|
|
|
},{}],56:[function(require,module,exports){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
function EventEmitter() {
|
|
this._events = this._events || {};
|
|
this._maxListeners = this._maxListeners || undefined;
|
|
}
|
|
module.exports = EventEmitter;
|
|
|
|
// Backwards-compat with node 0.10.x
|
|
EventEmitter.EventEmitter = EventEmitter;
|
|
|
|
EventEmitter.prototype._events = undefined;
|
|
EventEmitter.prototype._maxListeners = undefined;
|
|
|
|
// By default EventEmitters will print a warning if more than 10 listeners are
|
|
// added to it. This is a useful default which helps finding memory leaks.
|
|
EventEmitter.defaultMaxListeners = 10;
|
|
|
|
// Obviously not all Emitters should be limited to 10. This function allows
|
|
// that to be increased. Set to zero for unlimited.
|
|
EventEmitter.prototype.setMaxListeners = function(n) {
|
|
if (!isNumber(n) || n < 0 || isNaN(n))
|
|
throw TypeError('n must be a positive number');
|
|
this._maxListeners = n;
|
|
return this;
|
|
};
|
|
|
|
EventEmitter.prototype.emit = function(type) {
|
|
var er, handler, len, args, i, listeners;
|
|
|
|
if (!this._events)
|
|
this._events = {};
|
|
|
|
// If there is no 'error' event listener then throw.
|
|
if (type === 'error') {
|
|
if (!this._events.error ||
|
|
(isObject(this._events.error) && !this._events.error.length)) {
|
|
er = arguments[1];
|
|
if (er instanceof Error) {
|
|
throw er; // Unhandled 'error' event
|
|
} else {
|
|
// At least give some kind of context to the user
|
|
var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
|
|
err.context = er;
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
|
|
handler = this._events[type];
|
|
|
|
if (isUndefined(handler))
|
|
return false;
|
|
|
|
if (isFunction(handler)) {
|
|
switch (arguments.length) {
|
|
// fast cases
|
|
case 1:
|
|
handler.call(this);
|
|
break;
|
|
case 2:
|
|
handler.call(this, arguments[1]);
|
|
break;
|
|
case 3:
|
|
handler.call(this, arguments[1], arguments[2]);
|
|
break;
|
|
// slower
|
|
default:
|
|
args = Array.prototype.slice.call(arguments, 1);
|
|
handler.apply(this, args);
|
|
}
|
|
} else if (isObject(handler)) {
|
|
args = Array.prototype.slice.call(arguments, 1);
|
|
listeners = handler.slice();
|
|
len = listeners.length;
|
|
for (i = 0; i < len; i++)
|
|
listeners[i].apply(this, args);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
EventEmitter.prototype.addListener = function(type, listener) {
|
|
var m;
|
|
|
|
if (!isFunction(listener))
|
|
throw TypeError('listener must be a function');
|
|
|
|
if (!this._events)
|
|
this._events = {};
|
|
|
|
// To avoid recursion in the case that type === "newListener"! Before
|
|
// adding it to the listeners, first emit "newListener".
|
|
if (this._events.newListener)
|
|
this.emit('newListener', type,
|
|
isFunction(listener.listener) ?
|
|
listener.listener : listener);
|
|
|
|
if (!this._events[type])
|
|
// Optimize the case of one listener. Don't need the extra array object.
|
|
this._events[type] = listener;
|
|
else if (isObject(this._events[type]))
|
|
// If we've already got an array, just append.
|
|
this._events[type].push(listener);
|
|
else
|
|
// Adding the second element, need to change to array.
|
|
this._events[type] = [this._events[type], listener];
|
|
|
|
// Check for listener leak
|
|
if (isObject(this._events[type]) && !this._events[type].warned) {
|
|
if (!isUndefined(this._maxListeners)) {
|
|
m = this._maxListeners;
|
|
} else {
|
|
m = EventEmitter.defaultMaxListeners;
|
|
}
|
|
|
|
if (m && m > 0 && this._events[type].length > m) {
|
|
this._events[type].warned = true;
|
|
console.error('(node) warning: possible EventEmitter memory ' +
|
|
'leak detected. %d listeners added. ' +
|
|
'Use emitter.setMaxListeners() to increase limit.',
|
|
this._events[type].length);
|
|
if (typeof console.trace === 'function') {
|
|
// not supported in IE 10
|
|
console.trace();
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
|
|
|
EventEmitter.prototype.once = function(type, listener) {
|
|
if (!isFunction(listener))
|
|
throw TypeError('listener must be a function');
|
|
|
|
var fired = false;
|
|
|
|
function g() {
|
|
this.removeListener(type, g);
|
|
|
|
if (!fired) {
|
|
fired = true;
|
|
listener.apply(this, arguments);
|
|
}
|
|
}
|
|
|
|
g.listener = listener;
|
|
this.on(type, g);
|
|
|
|
return this;
|
|
};
|
|
|
|
// emits a 'removeListener' event iff the listener was removed
|
|
EventEmitter.prototype.removeListener = function(type, listener) {
|
|
var list, position, length, i;
|
|
|
|
if (!isFunction(listener))
|
|
throw TypeError('listener must be a function');
|
|
|
|
if (!this._events || !this._events[type])
|
|
return this;
|
|
|
|
list = this._events[type];
|
|
length = list.length;
|
|
position = -1;
|
|
|
|
if (list === listener ||
|
|
(isFunction(list.listener) && list.listener === listener)) {
|
|
delete this._events[type];
|
|
if (this._events.removeListener)
|
|
this.emit('removeListener', type, listener);
|
|
|
|
} else if (isObject(list)) {
|
|
for (i = length; i-- > 0;) {
|
|
if (list[i] === listener ||
|
|
(list[i].listener && list[i].listener === listener)) {
|
|
position = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (position < 0)
|
|
return this;
|
|
|
|
if (list.length === 1) {
|
|
list.length = 0;
|
|
delete this._events[type];
|
|
} else {
|
|
list.splice(position, 1);
|
|
}
|
|
|
|
if (this._events.removeListener)
|
|
this.emit('removeListener', type, listener);
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
EventEmitter.prototype.removeAllListeners = function(type) {
|
|
var key, listeners;
|
|
|
|
if (!this._events)
|
|
return this;
|
|
|
|
// not listening for removeListener, no need to emit
|
|
if (!this._events.removeListener) {
|
|
if (arguments.length === 0)
|
|
this._events = {};
|
|
else if (this._events[type])
|
|
delete this._events[type];
|
|
return this;
|
|
}
|
|
|
|
// emit removeListener for all listeners on all events
|
|
if (arguments.length === 0) {
|
|
for (key in this._events) {
|
|
if (key === 'removeListener') continue;
|
|
this.removeAllListeners(key);
|
|
}
|
|
this.removeAllListeners('removeListener');
|
|
this._events = {};
|
|
return this;
|
|
}
|
|
|
|
listeners = this._events[type];
|
|
|
|
if (isFunction(listeners)) {
|
|
this.removeListener(type, listeners);
|
|
} else if (listeners) {
|
|
// LIFO order
|
|
while (listeners.length)
|
|
this.removeListener(type, listeners[listeners.length - 1]);
|
|
}
|
|
delete this._events[type];
|
|
|
|
return this;
|
|
};
|
|
|
|
EventEmitter.prototype.listeners = function(type) {
|
|
var ret;
|
|
if (!this._events || !this._events[type])
|
|
ret = [];
|
|
else if (isFunction(this._events[type]))
|
|
ret = [this._events[type]];
|
|
else
|
|
ret = this._events[type].slice();
|
|
return ret;
|
|
};
|
|
|
|
EventEmitter.prototype.listenerCount = function(type) {
|
|
if (this._events) {
|
|
var evlistener = this._events[type];
|
|
|
|
if (isFunction(evlistener))
|
|
return 1;
|
|
else if (evlistener)
|
|
return evlistener.length;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
EventEmitter.listenerCount = function(emitter, type) {
|
|
return emitter.listenerCount(type);
|
|
};
|
|
|
|
function isFunction(arg) {
|
|
return typeof arg === 'function';
|
|
}
|
|
|
|
function isNumber(arg) {
|
|
return typeof arg === 'number';
|
|
}
|
|
|
|
function isObject(arg) {
|
|
return typeof arg === 'object' && arg !== null;
|
|
}
|
|
|
|
function isUndefined(arg) {
|
|
return arg === void 0;
|
|
}
|
|
|
|
},{}],57:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
*
|
|
* 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,
|
|
* 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.
|
|
*
|
|
* @providesModule EventListener
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var emptyFunction = require('./emptyFunction');
|
|
|
|
/**
|
|
* Upstream version of event listener. Does not take into account specific
|
|
* nature of platform.
|
|
*/
|
|
var EventListener = {
|
|
/**
|
|
* Listen to DOM events during the bubble phase.
|
|
*
|
|
* @param {DOMEventTarget} target DOM element to register listener on.
|
|
* @param {string} eventType Event type, e.g. 'click' or 'mouseover'.
|
|
* @param {function} callback Callback function.
|
|
* @return {object} Object with a `remove` method.
|
|
*/
|
|
listen: function (target, eventType, callback) {
|
|
if (target.addEventListener) {
|
|
target.addEventListener(eventType, callback, false);
|
|
return {
|
|
remove: function () {
|
|
target.removeEventListener(eventType, callback, false);
|
|
}
|
|
};
|
|
} else if (target.attachEvent) {
|
|
target.attachEvent('on' + eventType, callback);
|
|
return {
|
|
remove: function () {
|
|
target.detachEvent('on' + eventType, callback);
|
|
}
|
|
};
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Listen to DOM events during the capture phase.
|
|
*
|
|
* @param {DOMEventTarget} target DOM element to register listener on.
|
|
* @param {string} eventType Event type, e.g. 'click' or 'mouseover'.
|
|
* @param {function} callback Callback function.
|
|
* @return {object} Object with a `remove` method.
|
|
*/
|
|
capture: function (target, eventType, callback) {
|
|
if (target.addEventListener) {
|
|
target.addEventListener(eventType, callback, true);
|
|
return {
|
|
remove: function () {
|
|
target.removeEventListener(eventType, callback, true);
|
|
}
|
|
};
|
|
} else {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
console.error('Attempted to listen to events during the capture phase on a ' + 'browser that does not support the capture phase. Your application ' + 'will not receive some events.');
|
|
}
|
|
return {
|
|
remove: emptyFunction
|
|
};
|
|
}
|
|
},
|
|
|
|
registerDefault: function () {}
|
|
};
|
|
|
|
module.exports = EventListener;
|
|
}).call(this,require('_process'))
|
|
},{"./emptyFunction":64,"_process":133}],58:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ExecutionEnvironment
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
|
|
|
|
/**
|
|
* Simple, lightweight module assisting with the detection and context of
|
|
* Worker. Helps avoid circular dependencies and allows code to reason about
|
|
* whether or not they are in a Worker, even if they never include the main
|
|
* `ReactWorker` dependency.
|
|
*/
|
|
var ExecutionEnvironment = {
|
|
|
|
canUseDOM: canUseDOM,
|
|
|
|
canUseWorkers: typeof Worker !== 'undefined',
|
|
|
|
canUseEventListeners: canUseDOM && !!(window.addEventListener || window.attachEvent),
|
|
|
|
canUseViewport: canUseDOM && !!window.screen,
|
|
|
|
isInWorker: !canUseDOM // For now, this is true - might change in the future.
|
|
|
|
};
|
|
|
|
module.exports = ExecutionEnvironment;
|
|
},{}],59:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule camelize
|
|
* @typechecks
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
var _hyphenPattern = /-(.)/g;
|
|
|
|
/**
|
|
* Camelcases a hyphenated string, for example:
|
|
*
|
|
* > camelize('background-color')
|
|
* < "backgroundColor"
|
|
*
|
|
* @param {string} string
|
|
* @return {string}
|
|
*/
|
|
function camelize(string) {
|
|
return string.replace(_hyphenPattern, function (_, character) {
|
|
return character.toUpperCase();
|
|
});
|
|
}
|
|
|
|
module.exports = camelize;
|
|
},{}],60:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule camelizeStyleName
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var camelize = require('./camelize');
|
|
|
|
var msPattern = /^-ms-/;
|
|
|
|
/**
|
|
* Camelcases a hyphenated CSS property name, for example:
|
|
*
|
|
* > camelizeStyleName('background-color')
|
|
* < "backgroundColor"
|
|
* > camelizeStyleName('-moz-transition')
|
|
* < "MozTransition"
|
|
* > camelizeStyleName('-ms-transition')
|
|
* < "msTransition"
|
|
*
|
|
* As Andi Smith suggests
|
|
* (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix
|
|
* is converted to lowercase `ms`.
|
|
*
|
|
* @param {string} string
|
|
* @return {string}
|
|
*/
|
|
function camelizeStyleName(string) {
|
|
return camelize(string.replace(msPattern, 'ms-'));
|
|
}
|
|
|
|
module.exports = camelizeStyleName;
|
|
},{"./camelize":59}],61:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule containsNode
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var isTextNode = require('./isTextNode');
|
|
|
|
/*eslint-disable no-bitwise */
|
|
|
|
/**
|
|
* Checks if a given DOM node contains or is another DOM node.
|
|
*
|
|
* @param {?DOMNode} outerNode Outer DOM node.
|
|
* @param {?DOMNode} innerNode Inner DOM node.
|
|
* @return {boolean} True if `outerNode` contains or is `innerNode`.
|
|
*/
|
|
function containsNode(_x, _x2) {
|
|
var _again = true;
|
|
|
|
_function: while (_again) {
|
|
var outerNode = _x,
|
|
innerNode = _x2;
|
|
_again = false;
|
|
|
|
if (!outerNode || !innerNode) {
|
|
return false;
|
|
} else if (outerNode === innerNode) {
|
|
return true;
|
|
} else if (isTextNode(outerNode)) {
|
|
return false;
|
|
} else if (isTextNode(innerNode)) {
|
|
_x = outerNode;
|
|
_x2 = innerNode.parentNode;
|
|
_again = true;
|
|
continue _function;
|
|
} else if (outerNode.contains) {
|
|
return outerNode.contains(innerNode);
|
|
} else if (outerNode.compareDocumentPosition) {
|
|
return !!(outerNode.compareDocumentPosition(innerNode) & 16);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = containsNode;
|
|
},{"./isTextNode":74}],62:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule createArrayFromMixed
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var toArray = require('./toArray');
|
|
|
|
/**
|
|
* Perform a heuristic test to determine if an object is "array-like".
|
|
*
|
|
* A monk asked Joshu, a Zen master, "Has a dog Buddha nature?"
|
|
* Joshu replied: "Mu."
|
|
*
|
|
* This function determines if its argument has "array nature": it returns
|
|
* true if the argument is an actual array, an `arguments' object, or an
|
|
* HTMLCollection (e.g. node.childNodes or node.getElementsByTagName()).
|
|
*
|
|
* It will return false for other array-like objects like Filelist.
|
|
*
|
|
* @param {*} obj
|
|
* @return {boolean}
|
|
*/
|
|
function hasArrayNature(obj) {
|
|
return(
|
|
// not null/false
|
|
!!obj && (
|
|
// arrays are objects, NodeLists are functions in Safari
|
|
typeof obj == 'object' || typeof obj == 'function') &&
|
|
// quacks like an array
|
|
'length' in obj &&
|
|
// not window
|
|
!('setInterval' in obj) &&
|
|
// no DOM node should be considered an array-like
|
|
// a 'select' element has 'length' and 'item' properties on IE8
|
|
typeof obj.nodeType != 'number' && (
|
|
// a real array
|
|
Array.isArray(obj) ||
|
|
// arguments
|
|
'callee' in obj ||
|
|
// HTMLCollection/NodeList
|
|
'item' in obj)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Ensure that the argument is an array by wrapping it in an array if it is not.
|
|
* Creates a copy of the argument if it is already an array.
|
|
*
|
|
* This is mostly useful idiomatically:
|
|
*
|
|
* var createArrayFromMixed = require('createArrayFromMixed');
|
|
*
|
|
* function takesOneOrMoreThings(things) {
|
|
* things = createArrayFromMixed(things);
|
|
* ...
|
|
* }
|
|
*
|
|
* This allows you to treat `things' as an array, but accept scalars in the API.
|
|
*
|
|
* If you need to convert an array-like object, like `arguments`, into an array
|
|
* use toArray instead.
|
|
*
|
|
* @param {*} obj
|
|
* @return {array}
|
|
*/
|
|
function createArrayFromMixed(obj) {
|
|
if (!hasArrayNature(obj)) {
|
|
return [obj];
|
|
} else if (Array.isArray(obj)) {
|
|
return obj.slice();
|
|
} else {
|
|
return toArray(obj);
|
|
}
|
|
}
|
|
|
|
module.exports = createArrayFromMixed;
|
|
},{"./toArray":82}],63:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule createNodesFromMarkup
|
|
* @typechecks
|
|
*/
|
|
|
|
/*eslint-disable fb-www/unsafe-html*/
|
|
|
|
'use strict';
|
|
|
|
var ExecutionEnvironment = require('./ExecutionEnvironment');
|
|
|
|
var createArrayFromMixed = require('./createArrayFromMixed');
|
|
var getMarkupWrap = require('./getMarkupWrap');
|
|
var invariant = require('./invariant');
|
|
|
|
/**
|
|
* Dummy container used to render all markup.
|
|
*/
|
|
var dummyNode = ExecutionEnvironment.canUseDOM ? document.createElement('div') : null;
|
|
|
|
/**
|
|
* Pattern used by `getNodeName`.
|
|
*/
|
|
var nodeNamePattern = /^\s*<(\w+)/;
|
|
|
|
/**
|
|
* Extracts the `nodeName` of the first element in a string of markup.
|
|
*
|
|
* @param {string} markup String of markup.
|
|
* @return {?string} Node name of the supplied markup.
|
|
*/
|
|
function getNodeName(markup) {
|
|
var nodeNameMatch = markup.match(nodeNamePattern);
|
|
return nodeNameMatch && nodeNameMatch[1].toLowerCase();
|
|
}
|
|
|
|
/**
|
|
* Creates an array containing the nodes rendered from the supplied markup. The
|
|
* optionally supplied `handleScript` function will be invoked once for each
|
|
* <script> element that is rendered. If no `handleScript` function is supplied,
|
|
* an exception is thrown if any <script> elements are rendered.
|
|
*
|
|
* @param {string} markup A string of valid HTML markup.
|
|
* @param {?function} handleScript Invoked once for each rendered <script>.
|
|
* @return {array<DOMElement|DOMTextNode>} An array of rendered nodes.
|
|
*/
|
|
function createNodesFromMarkup(markup, handleScript) {
|
|
var node = dummyNode;
|
|
!!!dummyNode ? process.env.NODE_ENV !== 'production' ? invariant(false, 'createNodesFromMarkup dummy not initialized') : invariant(false) : undefined;
|
|
var nodeName = getNodeName(markup);
|
|
|
|
var wrap = nodeName && getMarkupWrap(nodeName);
|
|
if (wrap) {
|
|
node.innerHTML = wrap[1] + markup + wrap[2];
|
|
|
|
var wrapDepth = wrap[0];
|
|
while (wrapDepth--) {
|
|
node = node.lastChild;
|
|
}
|
|
} else {
|
|
node.innerHTML = markup;
|
|
}
|
|
|
|
var scripts = node.getElementsByTagName('script');
|
|
if (scripts.length) {
|
|
!handleScript ? process.env.NODE_ENV !== 'production' ? invariant(false, 'createNodesFromMarkup(...): Unexpected <script> element rendered.') : invariant(false) : undefined;
|
|
createArrayFromMixed(scripts).forEach(handleScript);
|
|
}
|
|
|
|
var nodes = createArrayFromMixed(node.childNodes);
|
|
while (node.lastChild) {
|
|
node.removeChild(node.lastChild);
|
|
}
|
|
return nodes;
|
|
}
|
|
|
|
module.exports = createNodesFromMarkup;
|
|
}).call(this,require('_process'))
|
|
},{"./ExecutionEnvironment":58,"./createArrayFromMixed":62,"./getMarkupWrap":68,"./invariant":72,"_process":133}],64:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule emptyFunction
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
function makeEmptyFunction(arg) {
|
|
return function () {
|
|
return arg;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* This function accepts and discards inputs; it has no side effects. This is
|
|
* primarily useful idiomatically for overridable function endpoints which
|
|
* always need to be callable, since JS lacks a null-call idiom ala Cocoa.
|
|
*/
|
|
function emptyFunction() {}
|
|
|
|
emptyFunction.thatReturns = makeEmptyFunction;
|
|
emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
|
|
emptyFunction.thatReturnsTrue = makeEmptyFunction(true);
|
|
emptyFunction.thatReturnsNull = makeEmptyFunction(null);
|
|
emptyFunction.thatReturnsThis = function () {
|
|
return this;
|
|
};
|
|
emptyFunction.thatReturnsArgument = function (arg) {
|
|
return arg;
|
|
};
|
|
|
|
module.exports = emptyFunction;
|
|
},{}],65:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule emptyObject
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var emptyObject = {};
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
Object.freeze(emptyObject);
|
|
}
|
|
|
|
module.exports = emptyObject;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133}],66:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule focusNode
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* @param {DOMElement} node input/textarea to focus
|
|
*/
|
|
function focusNode(node) {
|
|
// IE8 can throw "Can't move focus to the control because it is invisible,
|
|
// not enabled, or of a type that does not accept the focus." for all kinds of
|
|
// reasons that are too expensive and fragile to test.
|
|
try {
|
|
node.focus();
|
|
} catch (e) {}
|
|
}
|
|
|
|
module.exports = focusNode;
|
|
},{}],67:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule getActiveElement
|
|
* @typechecks
|
|
*/
|
|
|
|
/* eslint-disable fb-www/typeof-undefined */
|
|
|
|
/**
|
|
* Same as document.activeElement but wraps in a try-catch block. In IE it is
|
|
* not safe to call document.activeElement if there is nothing focused.
|
|
*
|
|
* The activeElement will be null only if the document or document body is not
|
|
* yet defined.
|
|
*/
|
|
'use strict';
|
|
|
|
function getActiveElement() /*?DOMElement*/{
|
|
if (typeof document === 'undefined') {
|
|
return null;
|
|
}
|
|
try {
|
|
return document.activeElement || document.body;
|
|
} catch (e) {
|
|
return document.body;
|
|
}
|
|
}
|
|
|
|
module.exports = getActiveElement;
|
|
},{}],68:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule getMarkupWrap
|
|
*/
|
|
|
|
/*eslint-disable fb-www/unsafe-html */
|
|
|
|
'use strict';
|
|
|
|
var ExecutionEnvironment = require('./ExecutionEnvironment');
|
|
|
|
var invariant = require('./invariant');
|
|
|
|
/**
|
|
* Dummy container used to detect which wraps are necessary.
|
|
*/
|
|
var dummyNode = ExecutionEnvironment.canUseDOM ? document.createElement('div') : null;
|
|
|
|
/**
|
|
* Some browsers cannot use `innerHTML` to render certain elements standalone,
|
|
* so we wrap them, render the wrapped nodes, then extract the desired node.
|
|
*
|
|
* In IE8, certain elements cannot render alone, so wrap all elements ('*').
|
|
*/
|
|
|
|
var shouldWrap = {};
|
|
|
|
var selectWrap = [1, '<select multiple="true">', '</select>'];
|
|
var tableWrap = [1, '<table>', '</table>'];
|
|
var trWrap = [3, '<table><tbody><tr>', '</tr></tbody></table>'];
|
|
|
|
var svgWrap = [1, '<svg xmlns="http://www.w3.org/2000/svg">', '</svg>'];
|
|
|
|
var markupWrap = {
|
|
'*': [1, '?<div>', '</div>'],
|
|
|
|
'area': [1, '<map>', '</map>'],
|
|
'col': [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
|
|
'legend': [1, '<fieldset>', '</fieldset>'],
|
|
'param': [1, '<object>', '</object>'],
|
|
'tr': [2, '<table><tbody>', '</tbody></table>'],
|
|
|
|
'optgroup': selectWrap,
|
|
'option': selectWrap,
|
|
|
|
'caption': tableWrap,
|
|
'colgroup': tableWrap,
|
|
'tbody': tableWrap,
|
|
'tfoot': tableWrap,
|
|
'thead': tableWrap,
|
|
|
|
'td': trWrap,
|
|
'th': trWrap
|
|
};
|
|
|
|
// Initialize the SVG elements since we know they'll always need to be wrapped
|
|
// consistently. If they are created inside a <div> they will be initialized in
|
|
// the wrong namespace (and will not display).
|
|
var svgElements = ['circle', 'clipPath', 'defs', 'ellipse', 'g', 'image', 'line', 'linearGradient', 'mask', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'text', 'tspan'];
|
|
svgElements.forEach(function (nodeName) {
|
|
markupWrap[nodeName] = svgWrap;
|
|
shouldWrap[nodeName] = true;
|
|
});
|
|
|
|
/**
|
|
* Gets the markup wrap configuration for the supplied `nodeName`.
|
|
*
|
|
* NOTE: This lazily detects which wraps are necessary for the current browser.
|
|
*
|
|
* @param {string} nodeName Lowercase `nodeName`.
|
|
* @return {?array} Markup wrap configuration, if applicable.
|
|
*/
|
|
function getMarkupWrap(nodeName) {
|
|
!!!dummyNode ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Markup wrapping node not initialized') : invariant(false) : undefined;
|
|
if (!markupWrap.hasOwnProperty(nodeName)) {
|
|
nodeName = '*';
|
|
}
|
|
if (!shouldWrap.hasOwnProperty(nodeName)) {
|
|
if (nodeName === '*') {
|
|
dummyNode.innerHTML = '<link />';
|
|
} else {
|
|
dummyNode.innerHTML = '<' + nodeName + '></' + nodeName + '>';
|
|
}
|
|
shouldWrap[nodeName] = !dummyNode.firstChild;
|
|
}
|
|
return shouldWrap[nodeName] ? markupWrap[nodeName] : null;
|
|
}
|
|
|
|
module.exports = getMarkupWrap;
|
|
}).call(this,require('_process'))
|
|
},{"./ExecutionEnvironment":58,"./invariant":72,"_process":133}],69:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule getUnboundedScrollPosition
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Gets the scroll position of the supplied element or window.
|
|
*
|
|
* The return values are unbounded, unlike `getScrollPosition`. This means they
|
|
* may be negative or exceed the element boundaries (which is possible using
|
|
* inertial scrolling).
|
|
*
|
|
* @param {DOMWindow|DOMElement} scrollable
|
|
* @return {object} Map with `x` and `y` keys.
|
|
*/
|
|
function getUnboundedScrollPosition(scrollable) {
|
|
if (scrollable === window) {
|
|
return {
|
|
x: window.pageXOffset || document.documentElement.scrollLeft,
|
|
y: window.pageYOffset || document.documentElement.scrollTop
|
|
};
|
|
}
|
|
return {
|
|
x: scrollable.scrollLeft,
|
|
y: scrollable.scrollTop
|
|
};
|
|
}
|
|
|
|
module.exports = getUnboundedScrollPosition;
|
|
},{}],70:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule hyphenate
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var _uppercasePattern = /([A-Z])/g;
|
|
|
|
/**
|
|
* Hyphenates a camelcased string, for example:
|
|
*
|
|
* > hyphenate('backgroundColor')
|
|
* < "background-color"
|
|
*
|
|
* For CSS style names, use `hyphenateStyleName` instead which works properly
|
|
* with all vendor prefixes, including `ms`.
|
|
*
|
|
* @param {string} string
|
|
* @return {string}
|
|
*/
|
|
function hyphenate(string) {
|
|
return string.replace(_uppercasePattern, '-$1').toLowerCase();
|
|
}
|
|
|
|
module.exports = hyphenate;
|
|
},{}],71:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule hyphenateStyleName
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var hyphenate = require('./hyphenate');
|
|
|
|
var msPattern = /^ms-/;
|
|
|
|
/**
|
|
* Hyphenates a camelcased CSS property name, for example:
|
|
*
|
|
* > hyphenateStyleName('backgroundColor')
|
|
* < "background-color"
|
|
* > hyphenateStyleName('MozTransition')
|
|
* < "-moz-transition"
|
|
* > hyphenateStyleName('msTransition')
|
|
* < "-ms-transition"
|
|
*
|
|
* As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix
|
|
* is converted to `-ms-`.
|
|
*
|
|
* @param {string} string
|
|
* @return {string}
|
|
*/
|
|
function hyphenateStyleName(string) {
|
|
return hyphenate(string).replace(msPattern, '-ms-');
|
|
}
|
|
|
|
module.exports = hyphenateStyleName;
|
|
},{"./hyphenate":70}],72:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule invariant
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Use invariant() to assert state which your program assumes to be true.
|
|
*
|
|
* Provide sprintf-style format (only %s is supported) and arguments
|
|
* to provide information about what broke and what you were
|
|
* expecting.
|
|
*
|
|
* The invariant message will be stripped in production, but the invariant
|
|
* will remain to ensure logic does not differ in production.
|
|
*/
|
|
|
|
function invariant(condition, format, a, b, c, d, e, f) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
if (format === undefined) {
|
|
throw new Error('invariant requires an error message argument');
|
|
}
|
|
}
|
|
|
|
if (!condition) {
|
|
var error;
|
|
if (format === undefined) {
|
|
error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
|
|
} else {
|
|
var args = [a, b, c, d, e, f];
|
|
var argIndex = 0;
|
|
error = new Error(format.replace(/%s/g, function () {
|
|
return args[argIndex++];
|
|
}));
|
|
error.name = 'Invariant Violation';
|
|
}
|
|
|
|
error.framesToPop = 1; // we don't care about invariant's own frame
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
module.exports = invariant;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133}],73:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule isNode
|
|
* @typechecks
|
|
*/
|
|
|
|
/**
|
|
* @param {*} object The object to check.
|
|
* @return {boolean} Whether or not the object is a DOM node.
|
|
*/
|
|
'use strict';
|
|
|
|
function isNode(object) {
|
|
return !!(object && (typeof Node === 'function' ? object instanceof Node : typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'));
|
|
}
|
|
|
|
module.exports = isNode;
|
|
},{}],74:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule isTextNode
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var isNode = require('./isNode');
|
|
|
|
/**
|
|
* @param {*} object The object to check.
|
|
* @return {boolean} Whether or not the object is a DOM text node.
|
|
*/
|
|
function isTextNode(object) {
|
|
return isNode(object) && object.nodeType == 3;
|
|
}
|
|
|
|
module.exports = isTextNode;
|
|
},{"./isNode":73}],75:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule keyMirror
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var invariant = require('./invariant');
|
|
|
|
/**
|
|
* Constructs an enumeration with keys equal to their value.
|
|
*
|
|
* For example:
|
|
*
|
|
* var COLORS = keyMirror({blue: null, red: null});
|
|
* var myColor = COLORS.blue;
|
|
* var isColorValid = !!COLORS[myColor];
|
|
*
|
|
* The last line could not be performed if the values of the generated enum were
|
|
* not equal to their keys.
|
|
*
|
|
* Input: {key1: val1, key2: val2}
|
|
* Output: {key1: key1, key2: key2}
|
|
*
|
|
* @param {object} obj
|
|
* @return {object}
|
|
*/
|
|
var keyMirror = function (obj) {
|
|
var ret = {};
|
|
var key;
|
|
!(obj instanceof Object && !Array.isArray(obj)) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'keyMirror(...): Argument must be an object.') : invariant(false) : undefined;
|
|
for (key in obj) {
|
|
if (!obj.hasOwnProperty(key)) {
|
|
continue;
|
|
}
|
|
ret[key] = key;
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
module.exports = keyMirror;
|
|
}).call(this,require('_process'))
|
|
},{"./invariant":72,"_process":133}],76:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule keyOf
|
|
*/
|
|
|
|
/**
|
|
* Allows extraction of a minified key. Let's the build system minify keys
|
|
* without losing the ability to dynamically use key strings as values
|
|
* themselves. Pass in an object with a single key/val pair and it will return
|
|
* you the string key of that single record. Suppose you want to grab the
|
|
* value for a key 'className' inside of an object. Key/val minification may
|
|
* have aliased that key to be 'xa12'. keyOf({className: null}) will return
|
|
* 'xa12' in that case. Resolve keys you want to use once at startup time, then
|
|
* reuse those resolutions.
|
|
*/
|
|
"use strict";
|
|
|
|
var keyOf = function (oneKeyObj) {
|
|
var key;
|
|
for (key in oneKeyObj) {
|
|
if (!oneKeyObj.hasOwnProperty(key)) {
|
|
continue;
|
|
}
|
|
return key;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
module.exports = keyOf;
|
|
},{}],77:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule mapObject
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
|
|
/**
|
|
* Executes the provided `callback` once for each enumerable own property in the
|
|
* object and constructs a new object from the results. The `callback` is
|
|
* invoked with three arguments:
|
|
*
|
|
* - the property value
|
|
* - the property name
|
|
* - the object being traversed
|
|
*
|
|
* Properties that are added after the call to `mapObject` will not be visited
|
|
* by `callback`. If the values of existing properties are changed, the value
|
|
* passed to `callback` will be the value at the time `mapObject` visits them.
|
|
* Properties that are deleted before being visited are not visited.
|
|
*
|
|
* @grep function objectMap()
|
|
* @grep function objMap()
|
|
*
|
|
* @param {?object} object
|
|
* @param {function} callback
|
|
* @param {*} context
|
|
* @return {?object}
|
|
*/
|
|
function mapObject(object, callback, context) {
|
|
if (!object) {
|
|
return null;
|
|
}
|
|
var result = {};
|
|
for (var name in object) {
|
|
if (hasOwnProperty.call(object, name)) {
|
|
result[name] = callback.call(context, object[name], name, object);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
module.exports = mapObject;
|
|
},{}],78:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule memoizeStringOnly
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Memoizes the return value of a function that accepts one string argument.
|
|
*
|
|
* @param {function} callback
|
|
* @return {function}
|
|
*/
|
|
function memoizeStringOnly(callback) {
|
|
var cache = {};
|
|
return function (string) {
|
|
if (!cache.hasOwnProperty(string)) {
|
|
cache[string] = callback.call(this, string);
|
|
}
|
|
return cache[string];
|
|
};
|
|
}
|
|
|
|
module.exports = memoizeStringOnly;
|
|
},{}],79:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule performance
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ExecutionEnvironment = require('./ExecutionEnvironment');
|
|
|
|
var performance;
|
|
|
|
if (ExecutionEnvironment.canUseDOM) {
|
|
performance = window.performance || window.msPerformance || window.webkitPerformance;
|
|
}
|
|
|
|
module.exports = performance || {};
|
|
},{"./ExecutionEnvironment":58}],80:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule performanceNow
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var performance = require('./performance');
|
|
|
|
var performanceNow;
|
|
|
|
/**
|
|
* Detect if we can use `window.performance.now()` and gracefully fallback to
|
|
* `Date.now()` if it doesn't exist. We need to support Firefox < 15 for now
|
|
* because of Facebook's testing infrastructure.
|
|
*/
|
|
if (performance.now) {
|
|
performanceNow = function () {
|
|
return performance.now();
|
|
};
|
|
} else {
|
|
performanceNow = function () {
|
|
return Date.now();
|
|
};
|
|
}
|
|
|
|
module.exports = performanceNow;
|
|
},{"./performance":79}],81:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule shallowEqual
|
|
* @typechecks
|
|
*
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
|
|
/**
|
|
* Performs equality by iterating through keys on an object and returning false
|
|
* when any key has values which are not strictly equal between the arguments.
|
|
* Returns true when the values of all keys are strictly equal.
|
|
*/
|
|
function shallowEqual(objA, objB) {
|
|
if (objA === objB) {
|
|
return true;
|
|
}
|
|
|
|
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
|
|
return false;
|
|
}
|
|
|
|
var keysA = Object.keys(objA);
|
|
var keysB = Object.keys(objB);
|
|
|
|
if (keysA.length !== keysB.length) {
|
|
return false;
|
|
}
|
|
|
|
// Test for A's keys different from B.
|
|
var bHasOwnProperty = hasOwnProperty.bind(objB);
|
|
for (var i = 0; i < keysA.length; i++) {
|
|
if (!bHasOwnProperty(keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
module.exports = shallowEqual;
|
|
},{}],82:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule toArray
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var invariant = require('./invariant');
|
|
|
|
/**
|
|
* Convert array-like objects to arrays.
|
|
*
|
|
* This API assumes the caller knows the contents of the data type. For less
|
|
* well defined inputs use createArrayFromMixed.
|
|
*
|
|
* @param {object|function|filelist} obj
|
|
* @return {array}
|
|
*/
|
|
function toArray(obj) {
|
|
var length = obj.length;
|
|
|
|
// Some browse builtin objects can report typeof 'function' (e.g. NodeList in
|
|
// old versions of Safari).
|
|
!(!Array.isArray(obj) && (typeof obj === 'object' || typeof obj === 'function')) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'toArray: Array-like object expected') : invariant(false) : undefined;
|
|
|
|
!(typeof length === 'number') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'toArray: Object needs a length property') : invariant(false) : undefined;
|
|
|
|
!(length === 0 || length - 1 in obj) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'toArray: Object should have keys for indices') : invariant(false) : undefined;
|
|
|
|
// Old IE doesn't give collections access to hasOwnProperty. Assume inputs
|
|
// without method will throw during the slice call and skip straight to the
|
|
// fallback.
|
|
if (obj.hasOwnProperty) {
|
|
try {
|
|
return Array.prototype.slice.call(obj);
|
|
} catch (e) {
|
|
// IE < 9 does not support Array#slice on collections objects
|
|
}
|
|
}
|
|
|
|
// Fall back to copying key by key. This assumes all keys have a value,
|
|
// so will not preserve sparsely populated inputs.
|
|
var ret = Array(length);
|
|
for (var ii = 0; ii < length; ii++) {
|
|
ret[ii] = obj[ii];
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
module.exports = toArray;
|
|
}).call(this,require('_process'))
|
|
},{"./invariant":72,"_process":133}],83:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2014-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule warning
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var emptyFunction = require('./emptyFunction');
|
|
|
|
/**
|
|
* Similar to invariant but only logs a warning if the condition is not met.
|
|
* This can be used to log issues in development environments in critical
|
|
* paths. Removing the logging code for production environments will keep the
|
|
* same logic and follow the same code paths.
|
|
*/
|
|
|
|
var warning = emptyFunction;
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
warning = function (condition, format) {
|
|
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
|
|
args[_key - 2] = arguments[_key];
|
|
}
|
|
|
|
if (format === undefined) {
|
|
throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument');
|
|
}
|
|
|
|
if (format.indexOf('Failed Composite propType: ') === 0) {
|
|
return; // Ignore CompositeComponent proptype check.
|
|
}
|
|
|
|
if (!condition) {
|
|
var argIndex = 0;
|
|
var message = 'Warning: ' + format.replace(/%s/g, function () {
|
|
return args[argIndex++];
|
|
});
|
|
if (typeof console !== 'undefined') {
|
|
console.error(message);
|
|
}
|
|
try {
|
|
// --- Welcome to debugging React ---
|
|
// This error was thrown as a convenience so that you can use this stack
|
|
// to find the callsite that caused this warning to fire.
|
|
throw new Error(message);
|
|
} catch (x) {}
|
|
}
|
|
};
|
|
}
|
|
|
|
module.exports = warning;
|
|
}).call(this,require('_process'))
|
|
},{"./emptyFunction":64,"_process":133}],84:[function(require,module,exports){
|
|
"use strict"
|
|
|
|
module.exports = createRBTree
|
|
|
|
var RED = 0
|
|
var BLACK = 1
|
|
|
|
function RBNode(color, key, value, left, right, count) {
|
|
this._color = color
|
|
this.key = key
|
|
this.value = value
|
|
this.left = left
|
|
this.right = right
|
|
this._count = count
|
|
}
|
|
|
|
function cloneNode(node) {
|
|
return new RBNode(node._color, node.key, node.value, node.left, node.right, node._count)
|
|
}
|
|
|
|
function repaint(color, node) {
|
|
return new RBNode(color, node.key, node.value, node.left, node.right, node._count)
|
|
}
|
|
|
|
function recount(node) {
|
|
node._count = 1 + (node.left ? node.left._count : 0) + (node.right ? node.right._count : 0)
|
|
}
|
|
|
|
function RedBlackTree(compare, root) {
|
|
this._compare = compare
|
|
this.root = root
|
|
}
|
|
|
|
var proto = RedBlackTree.prototype
|
|
|
|
Object.defineProperty(proto, "keys", {
|
|
get: function() {
|
|
var result = []
|
|
this.forEach(function(k,v) {
|
|
result.push(k)
|
|
})
|
|
return result
|
|
}
|
|
})
|
|
|
|
Object.defineProperty(proto, "values", {
|
|
get: function() {
|
|
var result = []
|
|
this.forEach(function(k,v) {
|
|
result.push(v)
|
|
})
|
|
return result
|
|
}
|
|
})
|
|
|
|
//Returns the number of nodes in the tree
|
|
Object.defineProperty(proto, "length", {
|
|
get: function() {
|
|
if(this.root) {
|
|
return this.root._count
|
|
}
|
|
return 0
|
|
}
|
|
})
|
|
|
|
//Insert a new item into the tree
|
|
proto.insert = function(key, value) {
|
|
var cmp = this._compare
|
|
//Find point to insert new node at
|
|
var n = this.root
|
|
var n_stack = []
|
|
var d_stack = []
|
|
while(n) {
|
|
var d = cmp(key, n.key)
|
|
n_stack.push(n)
|
|
d_stack.push(d)
|
|
if(d <= 0) {
|
|
n = n.left
|
|
} else {
|
|
n = n.right
|
|
}
|
|
}
|
|
//Rebuild path to leaf node
|
|
n_stack.push(new RBNode(RED, key, value, null, null, 1))
|
|
for(var s=n_stack.length-2; s>=0; --s) {
|
|
var n = n_stack[s]
|
|
if(d_stack[s] <= 0) {
|
|
n_stack[s] = new RBNode(n._color, n.key, n.value, n_stack[s+1], n.right, n._count+1)
|
|
} else {
|
|
n_stack[s] = new RBNode(n._color, n.key, n.value, n.left, n_stack[s+1], n._count+1)
|
|
}
|
|
}
|
|
//Rebalance tree using rotations
|
|
//console.log("start insert", key, d_stack)
|
|
for(var s=n_stack.length-1; s>1; --s) {
|
|
var p = n_stack[s-1]
|
|
var n = n_stack[s]
|
|
if(p._color === BLACK || n._color === BLACK) {
|
|
break
|
|
}
|
|
var pp = n_stack[s-2]
|
|
if(pp.left === p) {
|
|
if(p.left === n) {
|
|
var y = pp.right
|
|
if(y && y._color === RED) {
|
|
//console.log("LLr")
|
|
p._color = BLACK
|
|
pp.right = repaint(BLACK, y)
|
|
pp._color = RED
|
|
s -= 1
|
|
} else {
|
|
//console.log("LLb")
|
|
pp._color = RED
|
|
pp.left = p.right
|
|
p._color = BLACK
|
|
p.right = pp
|
|
n_stack[s-2] = p
|
|
n_stack[s-1] = n
|
|
recount(pp)
|
|
recount(p)
|
|
if(s >= 3) {
|
|
var ppp = n_stack[s-3]
|
|
if(ppp.left === pp) {
|
|
ppp.left = p
|
|
} else {
|
|
ppp.right = p
|
|
}
|
|
}
|
|
break
|
|
}
|
|
} else {
|
|
var y = pp.right
|
|
if(y && y._color === RED) {
|
|
//console.log("LRr")
|
|
p._color = BLACK
|
|
pp.right = repaint(BLACK, y)
|
|
pp._color = RED
|
|
s -= 1
|
|
} else {
|
|
//console.log("LRb")
|
|
p.right = n.left
|
|
pp._color = RED
|
|
pp.left = n.right
|
|
n._color = BLACK
|
|
n.left = p
|
|
n.right = pp
|
|
n_stack[s-2] = n
|
|
n_stack[s-1] = p
|
|
recount(pp)
|
|
recount(p)
|
|
recount(n)
|
|
if(s >= 3) {
|
|
var ppp = n_stack[s-3]
|
|
if(ppp.left === pp) {
|
|
ppp.left = n
|
|
} else {
|
|
ppp.right = n
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
} else {
|
|
if(p.right === n) {
|
|
var y = pp.left
|
|
if(y && y._color === RED) {
|
|
//console.log("RRr", y.key)
|
|
p._color = BLACK
|
|
pp.left = repaint(BLACK, y)
|
|
pp._color = RED
|
|
s -= 1
|
|
} else {
|
|
//console.log("RRb")
|
|
pp._color = RED
|
|
pp.right = p.left
|
|
p._color = BLACK
|
|
p.left = pp
|
|
n_stack[s-2] = p
|
|
n_stack[s-1] = n
|
|
recount(pp)
|
|
recount(p)
|
|
if(s >= 3) {
|
|
var ppp = n_stack[s-3]
|
|
if(ppp.right === pp) {
|
|
ppp.right = p
|
|
} else {
|
|
ppp.left = p
|
|
}
|
|
}
|
|
break
|
|
}
|
|
} else {
|
|
var y = pp.left
|
|
if(y && y._color === RED) {
|
|
//console.log("RLr")
|
|
p._color = BLACK
|
|
pp.left = repaint(BLACK, y)
|
|
pp._color = RED
|
|
s -= 1
|
|
} else {
|
|
//console.log("RLb")
|
|
p.left = n.right
|
|
pp._color = RED
|
|
pp.right = n.left
|
|
n._color = BLACK
|
|
n.right = p
|
|
n.left = pp
|
|
n_stack[s-2] = n
|
|
n_stack[s-1] = p
|
|
recount(pp)
|
|
recount(p)
|
|
recount(n)
|
|
if(s >= 3) {
|
|
var ppp = n_stack[s-3]
|
|
if(ppp.right === pp) {
|
|
ppp.right = n
|
|
} else {
|
|
ppp.left = n
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//Return new tree
|
|
n_stack[0]._color = BLACK
|
|
return new RedBlackTree(cmp, n_stack[0])
|
|
}
|
|
|
|
|
|
//Visit all nodes inorder
|
|
function doVisitFull(visit, node) {
|
|
if(node.left) {
|
|
var v = doVisitFull(visit, node.left)
|
|
if(v) { return v }
|
|
}
|
|
var v = visit(node.key, node.value)
|
|
if(v) { return v }
|
|
if(node.right) {
|
|
return doVisitFull(visit, node.right)
|
|
}
|
|
}
|
|
|
|
//Visit half nodes in order
|
|
function doVisitHalf(lo, compare, visit, node) {
|
|
var l = compare(lo, node.key)
|
|
if(l <= 0) {
|
|
if(node.left) {
|
|
var v = doVisitHalf(lo, compare, visit, node.left)
|
|
if(v) { return v }
|
|
}
|
|
var v = visit(node.key, node.value)
|
|
if(v) { return v }
|
|
}
|
|
if(node.right) {
|
|
return doVisitHalf(lo, compare, visit, node.right)
|
|
}
|
|
}
|
|
|
|
//Visit all nodes within a range
|
|
function doVisit(lo, hi, compare, visit, node) {
|
|
var l = compare(lo, node.key)
|
|
var h = compare(hi, node.key)
|
|
var v
|
|
if(l <= 0) {
|
|
if(node.left) {
|
|
v = doVisit(lo, hi, compare, visit, node.left)
|
|
if(v) { return v }
|
|
}
|
|
if(h > 0) {
|
|
v = visit(node.key, node.value)
|
|
if(v) { return v }
|
|
}
|
|
}
|
|
if(h > 0 && node.right) {
|
|
return doVisit(lo, hi, compare, visit, node.right)
|
|
}
|
|
}
|
|
|
|
|
|
proto.forEach = function rbTreeForEach(visit, lo, hi) {
|
|
if(!this.root) {
|
|
return
|
|
}
|
|
switch(arguments.length) {
|
|
case 1:
|
|
return doVisitFull(visit, this.root)
|
|
break
|
|
|
|
case 2:
|
|
return doVisitHalf(lo, this._compare, visit, this.root)
|
|
break
|
|
|
|
case 3:
|
|
if(this._compare(lo, hi) >= 0) {
|
|
return
|
|
}
|
|
return doVisit(lo, hi, this._compare, visit, this.root)
|
|
break
|
|
}
|
|
}
|
|
|
|
//First item in list
|
|
Object.defineProperty(proto, "begin", {
|
|
get: function() {
|
|
var stack = []
|
|
var n = this.root
|
|
while(n) {
|
|
stack.push(n)
|
|
n = n.left
|
|
}
|
|
return new RedBlackTreeIterator(this, stack)
|
|
}
|
|
})
|
|
|
|
//Last item in list
|
|
Object.defineProperty(proto, "end", {
|
|
get: function() {
|
|
var stack = []
|
|
var n = this.root
|
|
while(n) {
|
|
stack.push(n)
|
|
n = n.right
|
|
}
|
|
return new RedBlackTreeIterator(this, stack)
|
|
}
|
|
})
|
|
|
|
//Find the ith item in the tree
|
|
proto.at = function(idx) {
|
|
if(idx < 0) {
|
|
return new RedBlackTreeIterator(this, [])
|
|
}
|
|
var n = this.root
|
|
var stack = []
|
|
while(true) {
|
|
stack.push(n)
|
|
if(n.left) {
|
|
if(idx < n.left._count) {
|
|
n = n.left
|
|
continue
|
|
}
|
|
idx -= n.left._count
|
|
}
|
|
if(!idx) {
|
|
return new RedBlackTreeIterator(this, stack)
|
|
}
|
|
idx -= 1
|
|
if(n.right) {
|
|
if(idx >= n.right._count) {
|
|
break
|
|
}
|
|
n = n.right
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
return new RedBlackTreeIterator(this, [])
|
|
}
|
|
|
|
proto.ge = function(key) {
|
|
var cmp = this._compare
|
|
var n = this.root
|
|
var stack = []
|
|
var last_ptr = 0
|
|
while(n) {
|
|
var d = cmp(key, n.key)
|
|
stack.push(n)
|
|
if(d <= 0) {
|
|
last_ptr = stack.length
|
|
}
|
|
if(d <= 0) {
|
|
n = n.left
|
|
} else {
|
|
n = n.right
|
|
}
|
|
}
|
|
stack.length = last_ptr
|
|
return new RedBlackTreeIterator(this, stack)
|
|
}
|
|
|
|
proto.gt = function(key) {
|
|
var cmp = this._compare
|
|
var n = this.root
|
|
var stack = []
|
|
var last_ptr = 0
|
|
while(n) {
|
|
var d = cmp(key, n.key)
|
|
stack.push(n)
|
|
if(d < 0) {
|
|
last_ptr = stack.length
|
|
}
|
|
if(d < 0) {
|
|
n = n.left
|
|
} else {
|
|
n = n.right
|
|
}
|
|
}
|
|
stack.length = last_ptr
|
|
return new RedBlackTreeIterator(this, stack)
|
|
}
|
|
|
|
proto.lt = function(key) {
|
|
var cmp = this._compare
|
|
var n = this.root
|
|
var stack = []
|
|
var last_ptr = 0
|
|
while(n) {
|
|
var d = cmp(key, n.key)
|
|
stack.push(n)
|
|
if(d > 0) {
|
|
last_ptr = stack.length
|
|
}
|
|
if(d <= 0) {
|
|
n = n.left
|
|
} else {
|
|
n = n.right
|
|
}
|
|
}
|
|
stack.length = last_ptr
|
|
return new RedBlackTreeIterator(this, stack)
|
|
}
|
|
|
|
proto.le = function(key) {
|
|
var cmp = this._compare
|
|
var n = this.root
|
|
var stack = []
|
|
var last_ptr = 0
|
|
while(n) {
|
|
var d = cmp(key, n.key)
|
|
stack.push(n)
|
|
if(d >= 0) {
|
|
last_ptr = stack.length
|
|
}
|
|
if(d < 0) {
|
|
n = n.left
|
|
} else {
|
|
n = n.right
|
|
}
|
|
}
|
|
stack.length = last_ptr
|
|
return new RedBlackTreeIterator(this, stack)
|
|
}
|
|
|
|
//Finds the item with key if it exists
|
|
proto.find = function(key) {
|
|
var cmp = this._compare
|
|
var n = this.root
|
|
var stack = []
|
|
while(n) {
|
|
var d = cmp(key, n.key)
|
|
stack.push(n)
|
|
if(d === 0) {
|
|
return new RedBlackTreeIterator(this, stack)
|
|
}
|
|
if(d <= 0) {
|
|
n = n.left
|
|
} else {
|
|
n = n.right
|
|
}
|
|
}
|
|
return new RedBlackTreeIterator(this, [])
|
|
}
|
|
|
|
//Removes item with key from tree
|
|
proto.remove = function(key) {
|
|
var iter = this.find(key)
|
|
if(iter) {
|
|
return iter.remove()
|
|
}
|
|
return this
|
|
}
|
|
|
|
//Returns the item at `key`
|
|
proto.get = function(key) {
|
|
var cmp = this._compare
|
|
var n = this.root
|
|
while(n) {
|
|
var d = cmp(key, n.key)
|
|
if(d === 0) {
|
|
return n.value
|
|
}
|
|
if(d <= 0) {
|
|
n = n.left
|
|
} else {
|
|
n = n.right
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
//Iterator for red black tree
|
|
function RedBlackTreeIterator(tree, stack) {
|
|
this.tree = tree
|
|
this._stack = stack
|
|
}
|
|
|
|
var iproto = RedBlackTreeIterator.prototype
|
|
|
|
//Test if iterator is valid
|
|
Object.defineProperty(iproto, "valid", {
|
|
get: function() {
|
|
return this._stack.length > 0
|
|
}
|
|
})
|
|
|
|
//Node of the iterator
|
|
Object.defineProperty(iproto, "node", {
|
|
get: function() {
|
|
if(this._stack.length > 0) {
|
|
return this._stack[this._stack.length-1]
|
|
}
|
|
return null
|
|
},
|
|
enumerable: true
|
|
})
|
|
|
|
//Makes a copy of an iterator
|
|
iproto.clone = function() {
|
|
return new RedBlackTreeIterator(this.tree, this._stack.slice())
|
|
}
|
|
|
|
//Swaps two nodes
|
|
function swapNode(n, v) {
|
|
n.key = v.key
|
|
n.value = v.value
|
|
n.left = v.left
|
|
n.right = v.right
|
|
n._color = v._color
|
|
n._count = v._count
|
|
}
|
|
|
|
//Fix up a double black node in a tree
|
|
function fixDoubleBlack(stack) {
|
|
var n, p, s, z
|
|
for(var i=stack.length-1; i>=0; --i) {
|
|
n = stack[i]
|
|
if(i === 0) {
|
|
n._color = BLACK
|
|
return
|
|
}
|
|
//console.log("visit node:", n.key, i, stack[i].key, stack[i-1].key)
|
|
p = stack[i-1]
|
|
if(p.left === n) {
|
|
//console.log("left child")
|
|
s = p.right
|
|
if(s.right && s.right._color === RED) {
|
|
//console.log("case 1: right sibling child red")
|
|
s = p.right = cloneNode(s)
|
|
z = s.right = cloneNode(s.right)
|
|
p.right = s.left
|
|
s.left = p
|
|
s.right = z
|
|
s._color = p._color
|
|
n._color = BLACK
|
|
p._color = BLACK
|
|
z._color = BLACK
|
|
recount(p)
|
|
recount(s)
|
|
if(i > 1) {
|
|
var pp = stack[i-2]
|
|
if(pp.left === p) {
|
|
pp.left = s
|
|
} else {
|
|
pp.right = s
|
|
}
|
|
}
|
|
stack[i-1] = s
|
|
return
|
|
} else if(s.left && s.left._color === RED) {
|
|
//console.log("case 1: left sibling child red")
|
|
s = p.right = cloneNode(s)
|
|
z = s.left = cloneNode(s.left)
|
|
p.right = z.left
|
|
s.left = z.right
|
|
z.left = p
|
|
z.right = s
|
|
z._color = p._color
|
|
p._color = BLACK
|
|
s._color = BLACK
|
|
n._color = BLACK
|
|
recount(p)
|
|
recount(s)
|
|
recount(z)
|
|
if(i > 1) {
|
|
var pp = stack[i-2]
|
|
if(pp.left === p) {
|
|
pp.left = z
|
|
} else {
|
|
pp.right = z
|
|
}
|
|
}
|
|
stack[i-1] = z
|
|
return
|
|
}
|
|
if(s._color === BLACK) {
|
|
if(p._color === RED) {
|
|
//console.log("case 2: black sibling, red parent", p.right.value)
|
|
p._color = BLACK
|
|
p.right = repaint(RED, s)
|
|
return
|
|
} else {
|
|
//console.log("case 2: black sibling, black parent", p.right.value)
|
|
p.right = repaint(RED, s)
|
|
continue
|
|
}
|
|
} else {
|
|
//console.log("case 3: red sibling")
|
|
s = cloneNode(s)
|
|
p.right = s.left
|
|
s.left = p
|
|
s._color = p._color
|
|
p._color = RED
|
|
recount(p)
|
|
recount(s)
|
|
if(i > 1) {
|
|
var pp = stack[i-2]
|
|
if(pp.left === p) {
|
|
pp.left = s
|
|
} else {
|
|
pp.right = s
|
|
}
|
|
}
|
|
stack[i-1] = s
|
|
stack[i] = p
|
|
if(i+1 < stack.length) {
|
|
stack[i+1] = n
|
|
} else {
|
|
stack.push(n)
|
|
}
|
|
i = i+2
|
|
}
|
|
} else {
|
|
//console.log("right child")
|
|
s = p.left
|
|
if(s.left && s.left._color === RED) {
|
|
//console.log("case 1: left sibling child red", p.value, p._color)
|
|
s = p.left = cloneNode(s)
|
|
z = s.left = cloneNode(s.left)
|
|
p.left = s.right
|
|
s.right = p
|
|
s.left = z
|
|
s._color = p._color
|
|
n._color = BLACK
|
|
p._color = BLACK
|
|
z._color = BLACK
|
|
recount(p)
|
|
recount(s)
|
|
if(i > 1) {
|
|
var pp = stack[i-2]
|
|
if(pp.right === p) {
|
|
pp.right = s
|
|
} else {
|
|
pp.left = s
|
|
}
|
|
}
|
|
stack[i-1] = s
|
|
return
|
|
} else if(s.right && s.right._color === RED) {
|
|
//console.log("case 1: right sibling child red")
|
|
s = p.left = cloneNode(s)
|
|
z = s.right = cloneNode(s.right)
|
|
p.left = z.right
|
|
s.right = z.left
|
|
z.right = p
|
|
z.left = s
|
|
z._color = p._color
|
|
p._color = BLACK
|
|
s._color = BLACK
|
|
n._color = BLACK
|
|
recount(p)
|
|
recount(s)
|
|
recount(z)
|
|
if(i > 1) {
|
|
var pp = stack[i-2]
|
|
if(pp.right === p) {
|
|
pp.right = z
|
|
} else {
|
|
pp.left = z
|
|
}
|
|
}
|
|
stack[i-1] = z
|
|
return
|
|
}
|
|
if(s._color === BLACK) {
|
|
if(p._color === RED) {
|
|
//console.log("case 2: black sibling, red parent")
|
|
p._color = BLACK
|
|
p.left = repaint(RED, s)
|
|
return
|
|
} else {
|
|
//console.log("case 2: black sibling, black parent")
|
|
p.left = repaint(RED, s)
|
|
continue
|
|
}
|
|
} else {
|
|
//console.log("case 3: red sibling")
|
|
s = cloneNode(s)
|
|
p.left = s.right
|
|
s.right = p
|
|
s._color = p._color
|
|
p._color = RED
|
|
recount(p)
|
|
recount(s)
|
|
if(i > 1) {
|
|
var pp = stack[i-2]
|
|
if(pp.right === p) {
|
|
pp.right = s
|
|
} else {
|
|
pp.left = s
|
|
}
|
|
}
|
|
stack[i-1] = s
|
|
stack[i] = p
|
|
if(i+1 < stack.length) {
|
|
stack[i+1] = n
|
|
} else {
|
|
stack.push(n)
|
|
}
|
|
i = i+2
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Removes item at iterator from tree
|
|
iproto.remove = function() {
|
|
var stack = this._stack
|
|
if(stack.length === 0) {
|
|
return this.tree
|
|
}
|
|
//First copy path to node
|
|
var cstack = new Array(stack.length)
|
|
var n = stack[stack.length-1]
|
|
cstack[cstack.length-1] = new RBNode(n._color, n.key, n.value, n.left, n.right, n._count)
|
|
for(var i=stack.length-2; i>=0; --i) {
|
|
var n = stack[i]
|
|
if(n.left === stack[i+1]) {
|
|
cstack[i] = new RBNode(n._color, n.key, n.value, cstack[i+1], n.right, n._count)
|
|
} else {
|
|
cstack[i] = new RBNode(n._color, n.key, n.value, n.left, cstack[i+1], n._count)
|
|
}
|
|
}
|
|
|
|
//Get node
|
|
n = cstack[cstack.length-1]
|
|
//console.log("start remove: ", n.value)
|
|
|
|
//If not leaf, then swap with previous node
|
|
if(n.left && n.right) {
|
|
//console.log("moving to leaf")
|
|
|
|
//First walk to previous leaf
|
|
var split = cstack.length
|
|
n = n.left
|
|
while(n.right) {
|
|
cstack.push(n)
|
|
n = n.right
|
|
}
|
|
//Copy path to leaf
|
|
var v = cstack[split-1]
|
|
cstack.push(new RBNode(n._color, v.key, v.value, n.left, n.right, n._count))
|
|
cstack[split-1].key = n.key
|
|
cstack[split-1].value = n.value
|
|
|
|
//Fix up stack
|
|
for(var i=cstack.length-2; i>=split; --i) {
|
|
n = cstack[i]
|
|
cstack[i] = new RBNode(n._color, n.key, n.value, n.left, cstack[i+1], n._count)
|
|
}
|
|
cstack[split-1].left = cstack[split]
|
|
}
|
|
//console.log("stack=", cstack.map(function(v) { return v.value }))
|
|
|
|
//Remove leaf node
|
|
n = cstack[cstack.length-1]
|
|
if(n._color === RED) {
|
|
//Easy case: removing red leaf
|
|
//console.log("RED leaf")
|
|
var p = cstack[cstack.length-2]
|
|
if(p.left === n) {
|
|
p.left = null
|
|
} else if(p.right === n) {
|
|
p.right = null
|
|
}
|
|
cstack.pop()
|
|
for(var i=0; i<cstack.length; ++i) {
|
|
cstack[i]._count--
|
|
}
|
|
return new RedBlackTree(this.tree._compare, cstack[0])
|
|
} else {
|
|
if(n.left || n.right) {
|
|
//Second easy case: Single child black parent
|
|
//console.log("BLACK single child")
|
|
if(n.left) {
|
|
swapNode(n, n.left)
|
|
} else if(n.right) {
|
|
swapNode(n, n.right)
|
|
}
|
|
//Child must be red, so repaint it black to balance color
|
|
n._color = BLACK
|
|
for(var i=0; i<cstack.length-1; ++i) {
|
|
cstack[i]._count--
|
|
}
|
|
return new RedBlackTree(this.tree._compare, cstack[0])
|
|
} else if(cstack.length === 1) {
|
|
//Third easy case: root
|
|
//console.log("ROOT")
|
|
return new RedBlackTree(this.tree._compare, null)
|
|
} else {
|
|
//Hard case: Repaint n, and then do some nasty stuff
|
|
//console.log("BLACK leaf no children")
|
|
for(var i=0; i<cstack.length; ++i) {
|
|
cstack[i]._count--
|
|
}
|
|
var parent = cstack[cstack.length-2]
|
|
fixDoubleBlack(cstack)
|
|
//Fix up links
|
|
if(parent.left === n) {
|
|
parent.left = null
|
|
} else {
|
|
parent.right = null
|
|
}
|
|
}
|
|
}
|
|
return new RedBlackTree(this.tree._compare, cstack[0])
|
|
}
|
|
|
|
//Returns key
|
|
Object.defineProperty(iproto, "key", {
|
|
get: function() {
|
|
if(this._stack.length > 0) {
|
|
return this._stack[this._stack.length-1].key
|
|
}
|
|
return
|
|
},
|
|
enumerable: true
|
|
})
|
|
|
|
//Returns value
|
|
Object.defineProperty(iproto, "value", {
|
|
get: function() {
|
|
if(this._stack.length > 0) {
|
|
return this._stack[this._stack.length-1].value
|
|
}
|
|
return
|
|
},
|
|
enumerable: true
|
|
})
|
|
|
|
|
|
//Returns the position of this iterator in the sorted list
|
|
Object.defineProperty(iproto, "index", {
|
|
get: function() {
|
|
var idx = 0
|
|
var stack = this._stack
|
|
if(stack.length === 0) {
|
|
var r = this.tree.root
|
|
if(r) {
|
|
return r._count
|
|
}
|
|
return 0
|
|
} else if(stack[stack.length-1].left) {
|
|
idx = stack[stack.length-1].left._count
|
|
}
|
|
for(var s=stack.length-2; s>=0; --s) {
|
|
if(stack[s+1] === stack[s].right) {
|
|
++idx
|
|
if(stack[s].left) {
|
|
idx += stack[s].left._count
|
|
}
|
|
}
|
|
}
|
|
return idx
|
|
},
|
|
enumerable: true
|
|
})
|
|
|
|
//Advances iterator to next element in list
|
|
iproto.next = function() {
|
|
var stack = this._stack
|
|
if(stack.length === 0) {
|
|
return
|
|
}
|
|
var n = stack[stack.length-1]
|
|
if(n.right) {
|
|
n = n.right
|
|
while(n) {
|
|
stack.push(n)
|
|
n = n.left
|
|
}
|
|
} else {
|
|
stack.pop()
|
|
while(stack.length > 0 && stack[stack.length-1].right === n) {
|
|
n = stack[stack.length-1]
|
|
stack.pop()
|
|
}
|
|
}
|
|
}
|
|
|
|
//Checks if iterator is at end of tree
|
|
Object.defineProperty(iproto, "hasNext", {
|
|
get: function() {
|
|
var stack = this._stack
|
|
if(stack.length === 0) {
|
|
return false
|
|
}
|
|
if(stack[stack.length-1].right) {
|
|
return true
|
|
}
|
|
for(var s=stack.length-1; s>0; --s) {
|
|
if(stack[s-1].left === stack[s]) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
})
|
|
|
|
//Update value
|
|
iproto.update = function(value) {
|
|
var stack = this._stack
|
|
if(stack.length === 0) {
|
|
throw new Error("Can't update empty node!")
|
|
}
|
|
var cstack = new Array(stack.length)
|
|
var n = stack[stack.length-1]
|
|
cstack[cstack.length-1] = new RBNode(n._color, n.key, value, n.left, n.right, n._count)
|
|
for(var i=stack.length-2; i>=0; --i) {
|
|
n = stack[i]
|
|
if(n.left === stack[i+1]) {
|
|
cstack[i] = new RBNode(n._color, n.key, n.value, cstack[i+1], n.right, n._count)
|
|
} else {
|
|
cstack[i] = new RBNode(n._color, n.key, n.value, n.left, cstack[i+1], n._count)
|
|
}
|
|
}
|
|
return new RedBlackTree(this.tree._compare, cstack[0])
|
|
}
|
|
|
|
//Moves iterator backward one element
|
|
iproto.prev = function() {
|
|
var stack = this._stack
|
|
if(stack.length === 0) {
|
|
return
|
|
}
|
|
var n = stack[stack.length-1]
|
|
if(n.left) {
|
|
n = n.left
|
|
while(n) {
|
|
stack.push(n)
|
|
n = n.right
|
|
}
|
|
} else {
|
|
stack.pop()
|
|
while(stack.length > 0 && stack[stack.length-1].left === n) {
|
|
n = stack[stack.length-1]
|
|
stack.pop()
|
|
}
|
|
}
|
|
}
|
|
|
|
//Checks if iterator is at start of tree
|
|
Object.defineProperty(iproto, "hasPrev", {
|
|
get: function() {
|
|
var stack = this._stack
|
|
if(stack.length === 0) {
|
|
return false
|
|
}
|
|
if(stack[stack.length-1].left) {
|
|
return true
|
|
}
|
|
for(var s=stack.length-1; s>0; --s) {
|
|
if(stack[s-1].right === stack[s]) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
})
|
|
|
|
//Default comparison function
|
|
function defaultCompare(a, b) {
|
|
if(a < b) {
|
|
return -1
|
|
}
|
|
if(a > b) {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
//Build a tree
|
|
function createRBTree(compare) {
|
|
return new RedBlackTree(compare || defaultCompare, null)
|
|
}
|
|
},{}],85:[function(require,module,exports){
|
|
exports.read = function (buffer, offset, isLE, mLen, nBytes) {
|
|
var e, m
|
|
var eLen = nBytes * 8 - mLen - 1
|
|
var eMax = (1 << eLen) - 1
|
|
var eBias = eMax >> 1
|
|
var nBits = -7
|
|
var i = isLE ? (nBytes - 1) : 0
|
|
var d = isLE ? -1 : 1
|
|
var s = buffer[offset + i]
|
|
|
|
i += d
|
|
|
|
e = s & ((1 << (-nBits)) - 1)
|
|
s >>= (-nBits)
|
|
nBits += eLen
|
|
for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
|
|
|
|
m = e & ((1 << (-nBits)) - 1)
|
|
e >>= (-nBits)
|
|
nBits += mLen
|
|
for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
|
|
|
|
if (e === 0) {
|
|
e = 1 - eBias
|
|
} else if (e === eMax) {
|
|
return m ? NaN : ((s ? -1 : 1) * Infinity)
|
|
} else {
|
|
m = m + Math.pow(2, mLen)
|
|
e = e - eBias
|
|
}
|
|
return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
|
|
}
|
|
|
|
exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
|
|
var e, m, c
|
|
var eLen = nBytes * 8 - mLen - 1
|
|
var eMax = (1 << eLen) - 1
|
|
var eBias = eMax >> 1
|
|
var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
|
|
var i = isLE ? 0 : (nBytes - 1)
|
|
var d = isLE ? 1 : -1
|
|
var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
|
|
|
|
value = Math.abs(value)
|
|
|
|
if (isNaN(value) || value === Infinity) {
|
|
m = isNaN(value) ? 1 : 0
|
|
e = eMax
|
|
} else {
|
|
e = Math.floor(Math.log(value) / Math.LN2)
|
|
if (value * (c = Math.pow(2, -e)) < 1) {
|
|
e--
|
|
c *= 2
|
|
}
|
|
if (e + eBias >= 1) {
|
|
value += rt / c
|
|
} else {
|
|
value += rt * Math.pow(2, 1 - eBias)
|
|
}
|
|
if (value * c >= 2) {
|
|
e++
|
|
c /= 2
|
|
}
|
|
|
|
if (e + eBias >= eMax) {
|
|
m = 0
|
|
e = eMax
|
|
} else if (e + eBias >= 1) {
|
|
m = (value * c - 1) * Math.pow(2, mLen)
|
|
e = e + eBias
|
|
} else {
|
|
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
|
|
e = 0
|
|
}
|
|
}
|
|
|
|
for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
|
|
|
|
e = (e << mLen) | m
|
|
eLen += mLen
|
|
for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
|
|
|
|
buffer[offset + i - d] |= s * 128
|
|
}
|
|
|
|
},{}],86:[function(require,module,exports){
|
|
(function (global){
|
|
'use strict';
|
|
var Mutation = global.MutationObserver || global.WebKitMutationObserver;
|
|
|
|
var scheduleDrain;
|
|
|
|
{
|
|
if (Mutation) {
|
|
var called = 0;
|
|
var observer = new Mutation(nextTick);
|
|
var element = global.document.createTextNode('');
|
|
observer.observe(element, {
|
|
characterData: true
|
|
});
|
|
scheduleDrain = function () {
|
|
element.data = (called = ++called % 2);
|
|
};
|
|
} else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') {
|
|
var channel = new global.MessageChannel();
|
|
channel.port1.onmessage = nextTick;
|
|
scheduleDrain = function () {
|
|
channel.port2.postMessage(0);
|
|
};
|
|
} else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) {
|
|
scheduleDrain = function () {
|
|
|
|
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
|
|
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
|
|
var scriptEl = global.document.createElement('script');
|
|
scriptEl.onreadystatechange = function () {
|
|
nextTick();
|
|
|
|
scriptEl.onreadystatechange = null;
|
|
scriptEl.parentNode.removeChild(scriptEl);
|
|
scriptEl = null;
|
|
};
|
|
global.document.documentElement.appendChild(scriptEl);
|
|
};
|
|
} else {
|
|
scheduleDrain = function () {
|
|
setTimeout(nextTick, 0);
|
|
};
|
|
}
|
|
}
|
|
|
|
var draining;
|
|
var queue = [];
|
|
//named nextTick for less confusing stack traces
|
|
function nextTick() {
|
|
draining = true;
|
|
var i, oldQueue;
|
|
var len = queue.length;
|
|
while (len) {
|
|
oldQueue = queue;
|
|
queue = [];
|
|
i = -1;
|
|
while (++i < len) {
|
|
oldQueue[i]();
|
|
}
|
|
len = queue.length;
|
|
}
|
|
draining = false;
|
|
}
|
|
|
|
module.exports = immediate;
|
|
function immediate(task) {
|
|
if (queue.push(task) === 1 && !draining) {
|
|
scheduleDrain();
|
|
}
|
|
}
|
|
|
|
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{}],87:[function(require,module,exports){
|
|
if (typeof Object.create === 'function') {
|
|
// implementation from standard node.js 'util' module
|
|
module.exports = function inherits(ctor, superCtor) {
|
|
ctor.super_ = superCtor
|
|
ctor.prototype = Object.create(superCtor.prototype, {
|
|
constructor: {
|
|
value: ctor,
|
|
enumerable: false,
|
|
writable: true,
|
|
configurable: true
|
|
}
|
|
});
|
|
};
|
|
} else {
|
|
// old school shim for old browsers
|
|
module.exports = function inherits(ctor, superCtor) {
|
|
ctor.super_ = superCtor
|
|
var TempCtor = function () {}
|
|
TempCtor.prototype = superCtor.prototype
|
|
ctor.prototype = new TempCtor()
|
|
ctor.prototype.constructor = ctor
|
|
}
|
|
}
|
|
|
|
},{}],88:[function(require,module,exports){
|
|
/*!
|
|
* Determine if an object is a Buffer
|
|
*
|
|
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|
|
* @license MIT
|
|
*/
|
|
|
|
// The _isBuffer check is for Safari 5-7 support, because it's missing
|
|
// Object.prototype.constructor. Remove this eventually
|
|
module.exports = function (obj) {
|
|
return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)
|
|
}
|
|
|
|
function isBuffer (obj) {
|
|
return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
|
|
}
|
|
|
|
// For Node v0.10 support. Remove this eventually.
|
|
function isSlowBuffer (obj) {
|
|
return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))
|
|
}
|
|
|
|
},{}],89:[function(require,module,exports){
|
|
var toString = {}.toString;
|
|
|
|
module.exports = Array.isArray || function (arr) {
|
|
return toString.call(arr) == '[object Array]';
|
|
};
|
|
|
|
},{}],90:[function(require,module,exports){
|
|
(function() {
|
|
|
|
var slice = Array.prototype.slice,
|
|
each = Array.prototype.forEach;
|
|
|
|
var extend = function(obj) {
|
|
if(typeof obj !== 'object') throw obj + ' is not an object' ;
|
|
|
|
var sources = slice.call(arguments, 1);
|
|
|
|
each.call(sources, function(source) {
|
|
if(source) {
|
|
for(var prop in source) {
|
|
if(typeof source[prop] === 'object' && obj[prop]) {
|
|
extend.call(obj, obj[prop], source[prop]);
|
|
} else {
|
|
obj[prop] = source[prop];
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
return obj;
|
|
}
|
|
|
|
this.extend = extend;
|
|
|
|
}).call(this);
|
|
},{}],91:[function(require,module,exports){
|
|
var encodings = require('./lib/encodings');
|
|
|
|
module.exports = Codec;
|
|
|
|
function Codec(opts){
|
|
this.opts = opts || {};
|
|
this.encodings = encodings;
|
|
}
|
|
|
|
Codec.prototype._encoding = function(encoding){
|
|
if (typeof encoding == 'string') encoding = encodings[encoding];
|
|
if (!encoding) encoding = encodings.id;
|
|
return encoding;
|
|
};
|
|
|
|
Codec.prototype._keyEncoding = function(opts, batchOpts){
|
|
return this._encoding(batchOpts && batchOpts.keyEncoding
|
|
|| opts && opts.keyEncoding
|
|
|| this.opts.keyEncoding);
|
|
};
|
|
|
|
Codec.prototype._valueEncoding = function(opts, batchOpts){
|
|
return this._encoding(
|
|
batchOpts && (batchOpts.valueEncoding || batchOpts.encoding)
|
|
|| opts && (opts.valueEncoding || opts.encoding)
|
|
|| (this.opts.valueEncoding || this.opts.encoding));
|
|
};
|
|
|
|
Codec.prototype.encodeKey = function(key, opts, batchOpts){
|
|
return this._keyEncoding(opts, batchOpts).encode(key);
|
|
};
|
|
|
|
Codec.prototype.encodeValue = function(value, opts, batchOpts){
|
|
return this._valueEncoding(opts, batchOpts).encode(value);
|
|
};
|
|
|
|
Codec.prototype.decodeKey = function(key, opts){
|
|
return this._keyEncoding(opts).decode(key);
|
|
};
|
|
|
|
Codec.prototype.decodeValue = function(value, opts){
|
|
return this._valueEncoding(opts).decode(value);
|
|
};
|
|
|
|
Codec.prototype.encodeBatch = function(ops, opts){
|
|
var self = this;
|
|
|
|
return ops.map(function(_op){
|
|
var op = {
|
|
type: _op.type,
|
|
key: self.encodeKey(_op.key, opts, _op)
|
|
};
|
|
if (self.keyAsBuffer(opts, _op)) op.keyEncoding = 'binary';
|
|
if (_op.prefix) op.prefix = _op.prefix;
|
|
if ('value' in _op) {
|
|
op.value = self.encodeValue(_op.value, opts, _op);
|
|
if (self.valueAsBuffer(opts, _op)) op.valueEncoding = 'binary';
|
|
}
|
|
return op;
|
|
});
|
|
};
|
|
|
|
var ltgtKeys = ['lt', 'gt', 'lte', 'gte', 'start', 'end'];
|
|
|
|
Codec.prototype.encodeLtgt = function(ltgt){
|
|
var self = this;
|
|
var ret = {};
|
|
Object.keys(ltgt).forEach(function(key){
|
|
ret[key] = ltgtKeys.indexOf(key) > -1
|
|
? self.encodeKey(ltgt[key], ltgt)
|
|
: ltgt[key]
|
|
});
|
|
return ret;
|
|
};
|
|
|
|
Codec.prototype.createStreamDecoder = function(opts){
|
|
var self = this;
|
|
|
|
if (opts.keys && opts.values) {
|
|
return function(key, value){
|
|
return {
|
|
key: self.decodeKey(key, opts),
|
|
value: self.decodeValue(value, opts)
|
|
};
|
|
};
|
|
} else if (opts.keys) {
|
|
return function(key) {
|
|
return self.decodeKey(key, opts);
|
|
};
|
|
} else if (opts.values) {
|
|
return function(_, value){
|
|
return self.decodeValue(value, opts);
|
|
}
|
|
} else {
|
|
return function(){};
|
|
}
|
|
};
|
|
|
|
Codec.prototype.keyAsBuffer = function(opts){
|
|
return this._keyEncoding(opts).buffer;
|
|
};
|
|
|
|
Codec.prototype.valueAsBuffer = function(opts){
|
|
return this._valueEncoding(opts).buffer;
|
|
};
|
|
|
|
|
|
},{"./lib/encodings":92}],92:[function(require,module,exports){
|
|
(function (Buffer){
|
|
|
|
exports.utf8 = exports['utf-8'] = {
|
|
encode: function(data){
|
|
return isBinary(data)
|
|
? data
|
|
: String(data);
|
|
},
|
|
decode: identity,
|
|
buffer: false,
|
|
type: 'utf8'
|
|
};
|
|
|
|
exports.json = {
|
|
encode: JSON.stringify,
|
|
decode: JSON.parse,
|
|
buffer: false,
|
|
type: 'json'
|
|
};
|
|
|
|
exports.binary = {
|
|
encode: function(data){
|
|
return isBinary(data)
|
|
? data
|
|
: new Buffer(data);
|
|
},
|
|
decode: identity,
|
|
buffer: true,
|
|
type: 'binary'
|
|
};
|
|
|
|
exports.id = {
|
|
encode: function(data){
|
|
return data;
|
|
},
|
|
decode: function(data){
|
|
return data;
|
|
},
|
|
buffer: false,
|
|
type: 'id'
|
|
};
|
|
|
|
var bufferEncodings = [
|
|
'hex',
|
|
'ascii',
|
|
'base64',
|
|
'ucs2',
|
|
'ucs-2',
|
|
'utf16le',
|
|
'utf-16le'
|
|
];
|
|
|
|
bufferEncodings.forEach(function(type){
|
|
exports[type] = {
|
|
encode: function(data){
|
|
return isBinary(data)
|
|
? data
|
|
: new Buffer(data, type);
|
|
},
|
|
decode: function(buffer){
|
|
return buffer.toString(type);
|
|
},
|
|
buffer: true,
|
|
type: type
|
|
};
|
|
});
|
|
|
|
function identity(value){
|
|
return value;
|
|
}
|
|
|
|
function isBinary(data){
|
|
return data === undefined
|
|
|| data === null
|
|
|| Buffer.isBuffer(data);
|
|
}
|
|
|
|
|
|
}).call(this,require("buffer").Buffer)
|
|
},{"buffer":43}],93:[function(require,module,exports){
|
|
/* Copyright (c) 2012-2015 LevelUP contributors
|
|
* See list at <https://github.com/rvagg/node-levelup#contributing>
|
|
* MIT License
|
|
* <https://github.com/rvagg/node-levelup/blob/master/LICENSE.md>
|
|
*/
|
|
|
|
var createError = require('errno').create
|
|
, LevelUPError = createError('LevelUPError')
|
|
, NotFoundError = createError('NotFoundError', LevelUPError)
|
|
|
|
NotFoundError.prototype.notFound = true
|
|
NotFoundError.prototype.status = 404
|
|
|
|
module.exports = {
|
|
LevelUPError : LevelUPError
|
|
, InitializationError : createError('InitializationError', LevelUPError)
|
|
, OpenError : createError('OpenError', LevelUPError)
|
|
, ReadError : createError('ReadError', LevelUPError)
|
|
, WriteError : createError('WriteError', LevelUPError)
|
|
, NotFoundError : NotFoundError
|
|
, EncodingError : createError('EncodingError', LevelUPError)
|
|
}
|
|
|
|
},{"errno":54}],94:[function(require,module,exports){
|
|
var inherits = require('inherits');
|
|
var Readable = require('readable-stream').Readable;
|
|
var extend = require('xtend');
|
|
var EncodingError = require('level-errors').EncodingError;
|
|
|
|
module.exports = ReadStream;
|
|
inherits(ReadStream, Readable);
|
|
|
|
function ReadStream(iterator, options){
|
|
if (!(this instanceof ReadStream)) return new ReadStream(iterator, options);
|
|
Readable.call(this, extend(options, {
|
|
objectMode: true
|
|
}));
|
|
this._iterator = iterator;
|
|
this._destroyed = false;
|
|
this._decoder = null;
|
|
if (options && options.decoder) this._decoder = options.decoder;
|
|
this.on('end', this._cleanup.bind(this));
|
|
}
|
|
|
|
ReadStream.prototype._read = function(){
|
|
var self = this;
|
|
if (this._destroyed) return;
|
|
|
|
this._iterator.next(function(err, key, value){
|
|
if (self._destroyed) return;
|
|
if (err) return self.emit('error', err);
|
|
if (key === undefined && value === undefined) {
|
|
self.push(null);
|
|
} else {
|
|
if (!self._decoder) return self.push({ key: key, value: value });
|
|
|
|
try {
|
|
var value = self._decoder(key, value);
|
|
} catch (err) {
|
|
self.emit('error', new EncodingError(err));
|
|
self.push(null);
|
|
return;
|
|
}
|
|
self.push(value);
|
|
}
|
|
});
|
|
};
|
|
|
|
ReadStream.prototype.destroy =
|
|
ReadStream.prototype._cleanup = function(){
|
|
var self = this;
|
|
if (this._destroyed) return;
|
|
this._destroyed = true;
|
|
|
|
this._iterator.end(function(err){
|
|
if (err) return self.emit('error', err);
|
|
self.emit('close');
|
|
});
|
|
};
|
|
|
|
|
|
},{"inherits":87,"level-errors":93,"readable-stream":101,"xtend":299}],95:[function(require,module,exports){
|
|
module.exports = Array.isArray || function (arr) {
|
|
return Object.prototype.toString.call(arr) == '[object Array]';
|
|
};
|
|
|
|
},{}],96:[function(require,module,exports){
|
|
(function (process){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
// a duplex stream is just a stream that is both readable and writable.
|
|
// Since JS doesn't have multiple prototypal inheritance, this class
|
|
// prototypally inherits from Readable, and then parasitically from
|
|
// Writable.
|
|
|
|
module.exports = Duplex;
|
|
|
|
/*<replacement>*/
|
|
var objectKeys = Object.keys || function (obj) {
|
|
var keys = [];
|
|
for (var key in obj) keys.push(key);
|
|
return keys;
|
|
}
|
|
/*</replacement>*/
|
|
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
var Readable = require('./_stream_readable');
|
|
var Writable = require('./_stream_writable');
|
|
|
|
util.inherits(Duplex, Readable);
|
|
|
|
forEach(objectKeys(Writable.prototype), function(method) {
|
|
if (!Duplex.prototype[method])
|
|
Duplex.prototype[method] = Writable.prototype[method];
|
|
});
|
|
|
|
function Duplex(options) {
|
|
if (!(this instanceof Duplex))
|
|
return new Duplex(options);
|
|
|
|
Readable.call(this, options);
|
|
Writable.call(this, options);
|
|
|
|
if (options && options.readable === false)
|
|
this.readable = false;
|
|
|
|
if (options && options.writable === false)
|
|
this.writable = false;
|
|
|
|
this.allowHalfOpen = true;
|
|
if (options && options.allowHalfOpen === false)
|
|
this.allowHalfOpen = false;
|
|
|
|
this.once('end', onend);
|
|
}
|
|
|
|
// the no-half-open enforcer
|
|
function onend() {
|
|
// if we allow half-open state, or if the writable side ended,
|
|
// then we're ok.
|
|
if (this.allowHalfOpen || this._writableState.ended)
|
|
return;
|
|
|
|
// no more data can be written.
|
|
// But allow more writes to happen in this tick.
|
|
process.nextTick(this.end.bind(this));
|
|
}
|
|
|
|
function forEach (xs, f) {
|
|
for (var i = 0, l = xs.length; i < l; i++) {
|
|
f(xs[i], i);
|
|
}
|
|
}
|
|
|
|
}).call(this,require('_process'))
|
|
},{"./_stream_readable":98,"./_stream_writable":100,"_process":133,"core-util-is":44,"inherits":87}],97:[function(require,module,exports){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
// a passthrough stream.
|
|
// basically just the most minimal sort of Transform stream.
|
|
// Every written chunk gets output as-is.
|
|
|
|
module.exports = PassThrough;
|
|
|
|
var Transform = require('./_stream_transform');
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
util.inherits(PassThrough, Transform);
|
|
|
|
function PassThrough(options) {
|
|
if (!(this instanceof PassThrough))
|
|
return new PassThrough(options);
|
|
|
|
Transform.call(this, options);
|
|
}
|
|
|
|
PassThrough.prototype._transform = function(chunk, encoding, cb) {
|
|
cb(null, chunk);
|
|
};
|
|
|
|
},{"./_stream_transform":99,"core-util-is":44,"inherits":87}],98:[function(require,module,exports){
|
|
(function (process){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
module.exports = Readable;
|
|
|
|
/*<replacement>*/
|
|
var isArray = require('isarray');
|
|
/*</replacement>*/
|
|
|
|
|
|
/*<replacement>*/
|
|
var Buffer = require('buffer').Buffer;
|
|
/*</replacement>*/
|
|
|
|
Readable.ReadableState = ReadableState;
|
|
|
|
var EE = require('events').EventEmitter;
|
|
|
|
/*<replacement>*/
|
|
if (!EE.listenerCount) EE.listenerCount = function(emitter, type) {
|
|
return emitter.listeners(type).length;
|
|
};
|
|
/*</replacement>*/
|
|
|
|
var Stream = require('stream');
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
var StringDecoder;
|
|
|
|
|
|
/*<replacement>*/
|
|
var debug = require('util');
|
|
if (debug && debug.debuglog) {
|
|
debug = debug.debuglog('stream');
|
|
} else {
|
|
debug = function () {};
|
|
}
|
|
/*</replacement>*/
|
|
|
|
|
|
util.inherits(Readable, Stream);
|
|
|
|
function ReadableState(options, stream) {
|
|
var Duplex = require('./_stream_duplex');
|
|
|
|
options = options || {};
|
|
|
|
// the point at which it stops calling _read() to fill the buffer
|
|
// Note: 0 is a valid value, means "don't call _read preemptively ever"
|
|
var hwm = options.highWaterMark;
|
|
var defaultHwm = options.objectMode ? 16 : 16 * 1024;
|
|
this.highWaterMark = (hwm || hwm === 0) ? hwm : defaultHwm;
|
|
|
|
// cast to ints.
|
|
this.highWaterMark = ~~this.highWaterMark;
|
|
|
|
this.buffer = [];
|
|
this.length = 0;
|
|
this.pipes = null;
|
|
this.pipesCount = 0;
|
|
this.flowing = null;
|
|
this.ended = false;
|
|
this.endEmitted = false;
|
|
this.reading = false;
|
|
|
|
// a flag to be able to tell if the onwrite cb is called immediately,
|
|
// or on a later tick. We set this to true at first, because any
|
|
// actions that shouldn't happen until "later" should generally also
|
|
// not happen before the first write call.
|
|
this.sync = true;
|
|
|
|
// whenever we return null, then we set a flag to say
|
|
// that we're awaiting a 'readable' event emission.
|
|
this.needReadable = false;
|
|
this.emittedReadable = false;
|
|
this.readableListening = false;
|
|
|
|
|
|
// object stream flag. Used to make read(n) ignore n and to
|
|
// make all the buffer merging and length checks go away
|
|
this.objectMode = !!options.objectMode;
|
|
|
|
if (stream instanceof Duplex)
|
|
this.objectMode = this.objectMode || !!options.readableObjectMode;
|
|
|
|
// Crypto is kind of old and crusty. Historically, its default string
|
|
// encoding is 'binary' so we have to make this configurable.
|
|
// Everything else in the universe uses 'utf8', though.
|
|
this.defaultEncoding = options.defaultEncoding || 'utf8';
|
|
|
|
// when piping, we only care about 'readable' events that happen
|
|
// after read()ing all the bytes and not getting any pushback.
|
|
this.ranOut = false;
|
|
|
|
// the number of writers that are awaiting a drain event in .pipe()s
|
|
this.awaitDrain = 0;
|
|
|
|
// if true, a maybeReadMore has been scheduled
|
|
this.readingMore = false;
|
|
|
|
this.decoder = null;
|
|
this.encoding = null;
|
|
if (options.encoding) {
|
|
if (!StringDecoder)
|
|
StringDecoder = require('string_decoder/').StringDecoder;
|
|
this.decoder = new StringDecoder(options.encoding);
|
|
this.encoding = options.encoding;
|
|
}
|
|
}
|
|
|
|
function Readable(options) {
|
|
var Duplex = require('./_stream_duplex');
|
|
|
|
if (!(this instanceof Readable))
|
|
return new Readable(options);
|
|
|
|
this._readableState = new ReadableState(options, this);
|
|
|
|
// legacy
|
|
this.readable = true;
|
|
|
|
Stream.call(this);
|
|
}
|
|
|
|
// Manually shove something into the read() buffer.
|
|
// This returns true if the highWaterMark has not been hit yet,
|
|
// similar to how Writable.write() returns true if you should
|
|
// write() some more.
|
|
Readable.prototype.push = function(chunk, encoding) {
|
|
var state = this._readableState;
|
|
|
|
if (util.isString(chunk) && !state.objectMode) {
|
|
encoding = encoding || state.defaultEncoding;
|
|
if (encoding !== state.encoding) {
|
|
chunk = new Buffer(chunk, encoding);
|
|
encoding = '';
|
|
}
|
|
}
|
|
|
|
return readableAddChunk(this, state, chunk, encoding, false);
|
|
};
|
|
|
|
// Unshift should *always* be something directly out of read()
|
|
Readable.prototype.unshift = function(chunk) {
|
|
var state = this._readableState;
|
|
return readableAddChunk(this, state, chunk, '', true);
|
|
};
|
|
|
|
function readableAddChunk(stream, state, chunk, encoding, addToFront) {
|
|
var er = chunkInvalid(state, chunk);
|
|
if (er) {
|
|
stream.emit('error', er);
|
|
} else if (util.isNullOrUndefined(chunk)) {
|
|
state.reading = false;
|
|
if (!state.ended)
|
|
onEofChunk(stream, state);
|
|
} else if (state.objectMode || chunk && chunk.length > 0) {
|
|
if (state.ended && !addToFront) {
|
|
var e = new Error('stream.push() after EOF');
|
|
stream.emit('error', e);
|
|
} else if (state.endEmitted && addToFront) {
|
|
var e = new Error('stream.unshift() after end event');
|
|
stream.emit('error', e);
|
|
} else {
|
|
if (state.decoder && !addToFront && !encoding)
|
|
chunk = state.decoder.write(chunk);
|
|
|
|
if (!addToFront)
|
|
state.reading = false;
|
|
|
|
// if we want the data now, just emit it.
|
|
if (state.flowing && state.length === 0 && !state.sync) {
|
|
stream.emit('data', chunk);
|
|
stream.read(0);
|
|
} else {
|
|
// update the buffer info.
|
|
state.length += state.objectMode ? 1 : chunk.length;
|
|
if (addToFront)
|
|
state.buffer.unshift(chunk);
|
|
else
|
|
state.buffer.push(chunk);
|
|
|
|
if (state.needReadable)
|
|
emitReadable(stream);
|
|
}
|
|
|
|
maybeReadMore(stream, state);
|
|
}
|
|
} else if (!addToFront) {
|
|
state.reading = false;
|
|
}
|
|
|
|
return needMoreData(state);
|
|
}
|
|
|
|
|
|
|
|
// if it's past the high water mark, we can push in some more.
|
|
// Also, if we have no data yet, we can stand some
|
|
// more bytes. This is to work around cases where hwm=0,
|
|
// such as the repl. Also, if the push() triggered a
|
|
// readable event, and the user called read(largeNumber) such that
|
|
// needReadable was set, then we ought to push more, so that another
|
|
// 'readable' event will be triggered.
|
|
function needMoreData(state) {
|
|
return !state.ended &&
|
|
(state.needReadable ||
|
|
state.length < state.highWaterMark ||
|
|
state.length === 0);
|
|
}
|
|
|
|
// backwards compatibility.
|
|
Readable.prototype.setEncoding = function(enc) {
|
|
if (!StringDecoder)
|
|
StringDecoder = require('string_decoder/').StringDecoder;
|
|
this._readableState.decoder = new StringDecoder(enc);
|
|
this._readableState.encoding = enc;
|
|
return this;
|
|
};
|
|
|
|
// Don't raise the hwm > 128MB
|
|
var MAX_HWM = 0x800000;
|
|
function roundUpToNextPowerOf2(n) {
|
|
if (n >= MAX_HWM) {
|
|
n = MAX_HWM;
|
|
} else {
|
|
// Get the next highest power of 2
|
|
n--;
|
|
for (var p = 1; p < 32; p <<= 1) n |= n >> p;
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
function howMuchToRead(n, state) {
|
|
if (state.length === 0 && state.ended)
|
|
return 0;
|
|
|
|
if (state.objectMode)
|
|
return n === 0 ? 0 : 1;
|
|
|
|
if (isNaN(n) || util.isNull(n)) {
|
|
// only flow one buffer at a time
|
|
if (state.flowing && state.buffer.length)
|
|
return state.buffer[0].length;
|
|
else
|
|
return state.length;
|
|
}
|
|
|
|
if (n <= 0)
|
|
return 0;
|
|
|
|
// If we're asking for more than the target buffer level,
|
|
// then raise the water mark. Bump up to the next highest
|
|
// power of 2, to prevent increasing it excessively in tiny
|
|
// amounts.
|
|
if (n > state.highWaterMark)
|
|
state.highWaterMark = roundUpToNextPowerOf2(n);
|
|
|
|
// don't have that much. return null, unless we've ended.
|
|
if (n > state.length) {
|
|
if (!state.ended) {
|
|
state.needReadable = true;
|
|
return 0;
|
|
} else
|
|
return state.length;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
// you can override either this method, or the async _read(n) below.
|
|
Readable.prototype.read = function(n) {
|
|
debug('read', n);
|
|
var state = this._readableState;
|
|
var nOrig = n;
|
|
|
|
if (!util.isNumber(n) || n > 0)
|
|
state.emittedReadable = false;
|
|
|
|
// if we're doing read(0) to trigger a readable event, but we
|
|
// already have a bunch of data in the buffer, then just trigger
|
|
// the 'readable' event and move on.
|
|
if (n === 0 &&
|
|
state.needReadable &&
|
|
(state.length >= state.highWaterMark || state.ended)) {
|
|
debug('read: emitReadable', state.length, state.ended);
|
|
if (state.length === 0 && state.ended)
|
|
endReadable(this);
|
|
else
|
|
emitReadable(this);
|
|
return null;
|
|
}
|
|
|
|
n = howMuchToRead(n, state);
|
|
|
|
// if we've ended, and we're now clear, then finish it up.
|
|
if (n === 0 && state.ended) {
|
|
if (state.length === 0)
|
|
endReadable(this);
|
|
return null;
|
|
}
|
|
|
|
// All the actual chunk generation logic needs to be
|
|
// *below* the call to _read. The reason is that in certain
|
|
// synthetic stream cases, such as passthrough streams, _read
|
|
// may be a completely synchronous operation which may change
|
|
// the state of the read buffer, providing enough data when
|
|
// before there was *not* enough.
|
|
//
|
|
// So, the steps are:
|
|
// 1. Figure out what the state of things will be after we do
|
|
// a read from the buffer.
|
|
//
|
|
// 2. If that resulting state will trigger a _read, then call _read.
|
|
// Note that this may be asynchronous, or synchronous. Yes, it is
|
|
// deeply ugly to write APIs this way, but that still doesn't mean
|
|
// that the Readable class should behave improperly, as streams are
|
|
// designed to be sync/async agnostic.
|
|
// Take note if the _read call is sync or async (ie, if the read call
|
|
// has returned yet), so that we know whether or not it's safe to emit
|
|
// 'readable' etc.
|
|
//
|
|
// 3. Actually pull the requested chunks out of the buffer and return.
|
|
|
|
// if we need a readable event, then we need to do some reading.
|
|
var doRead = state.needReadable;
|
|
debug('need readable', doRead);
|
|
|
|
// if we currently have less than the highWaterMark, then also read some
|
|
if (state.length === 0 || state.length - n < state.highWaterMark) {
|
|
doRead = true;
|
|
debug('length less than watermark', doRead);
|
|
}
|
|
|
|
// however, if we've ended, then there's no point, and if we're already
|
|
// reading, then it's unnecessary.
|
|
if (state.ended || state.reading) {
|
|
doRead = false;
|
|
debug('reading or ended', doRead);
|
|
}
|
|
|
|
if (doRead) {
|
|
debug('do read');
|
|
state.reading = true;
|
|
state.sync = true;
|
|
// if the length is currently zero, then we *need* a readable event.
|
|
if (state.length === 0)
|
|
state.needReadable = true;
|
|
// call internal read method
|
|
this._read(state.highWaterMark);
|
|
state.sync = false;
|
|
}
|
|
|
|
// If _read pushed data synchronously, then `reading` will be false,
|
|
// and we need to re-evaluate how much data we can return to the user.
|
|
if (doRead && !state.reading)
|
|
n = howMuchToRead(nOrig, state);
|
|
|
|
var ret;
|
|
if (n > 0)
|
|
ret = fromList(n, state);
|
|
else
|
|
ret = null;
|
|
|
|
if (util.isNull(ret)) {
|
|
state.needReadable = true;
|
|
n = 0;
|
|
}
|
|
|
|
state.length -= n;
|
|
|
|
// If we have nothing in the buffer, then we want to know
|
|
// as soon as we *do* get something into the buffer.
|
|
if (state.length === 0 && !state.ended)
|
|
state.needReadable = true;
|
|
|
|
// If we tried to read() past the EOF, then emit end on the next tick.
|
|
if (nOrig !== n && state.ended && state.length === 0)
|
|
endReadable(this);
|
|
|
|
if (!util.isNull(ret))
|
|
this.emit('data', ret);
|
|
|
|
return ret;
|
|
};
|
|
|
|
function chunkInvalid(state, chunk) {
|
|
var er = null;
|
|
if (!util.isBuffer(chunk) &&
|
|
!util.isString(chunk) &&
|
|
!util.isNullOrUndefined(chunk) &&
|
|
!state.objectMode) {
|
|
er = new TypeError('Invalid non-string/buffer chunk');
|
|
}
|
|
return er;
|
|
}
|
|
|
|
|
|
function onEofChunk(stream, state) {
|
|
if (state.decoder && !state.ended) {
|
|
var chunk = state.decoder.end();
|
|
if (chunk && chunk.length) {
|
|
state.buffer.push(chunk);
|
|
state.length += state.objectMode ? 1 : chunk.length;
|
|
}
|
|
}
|
|
state.ended = true;
|
|
|
|
// emit 'readable' now to make sure it gets picked up.
|
|
emitReadable(stream);
|
|
}
|
|
|
|
// Don't emit readable right away in sync mode, because this can trigger
|
|
// another read() call => stack overflow. This way, it might trigger
|
|
// a nextTick recursion warning, but that's not so bad.
|
|
function emitReadable(stream) {
|
|
var state = stream._readableState;
|
|
state.needReadable = false;
|
|
if (!state.emittedReadable) {
|
|
debug('emitReadable', state.flowing);
|
|
state.emittedReadable = true;
|
|
if (state.sync)
|
|
process.nextTick(function() {
|
|
emitReadable_(stream);
|
|
});
|
|
else
|
|
emitReadable_(stream);
|
|
}
|
|
}
|
|
|
|
function emitReadable_(stream) {
|
|
debug('emit readable');
|
|
stream.emit('readable');
|
|
flow(stream);
|
|
}
|
|
|
|
|
|
// at this point, the user has presumably seen the 'readable' event,
|
|
// and called read() to consume some data. that may have triggered
|
|
// in turn another _read(n) call, in which case reading = true if
|
|
// it's in progress.
|
|
// However, if we're not ended, or reading, and the length < hwm,
|
|
// then go ahead and try to read some more preemptively.
|
|
function maybeReadMore(stream, state) {
|
|
if (!state.readingMore) {
|
|
state.readingMore = true;
|
|
process.nextTick(function() {
|
|
maybeReadMore_(stream, state);
|
|
});
|
|
}
|
|
}
|
|
|
|
function maybeReadMore_(stream, state) {
|
|
var len = state.length;
|
|
while (!state.reading && !state.flowing && !state.ended &&
|
|
state.length < state.highWaterMark) {
|
|
debug('maybeReadMore read 0');
|
|
stream.read(0);
|
|
if (len === state.length)
|
|
// didn't get any data, stop spinning.
|
|
break;
|
|
else
|
|
len = state.length;
|
|
}
|
|
state.readingMore = false;
|
|
}
|
|
|
|
// abstract method. to be overridden in specific implementation classes.
|
|
// call cb(er, data) where data is <= n in length.
|
|
// for virtual (non-string, non-buffer) streams, "length" is somewhat
|
|
// arbitrary, and perhaps not very meaningful.
|
|
Readable.prototype._read = function(n) {
|
|
this.emit('error', new Error('not implemented'));
|
|
};
|
|
|
|
Readable.prototype.pipe = function(dest, pipeOpts) {
|
|
var src = this;
|
|
var state = this._readableState;
|
|
|
|
switch (state.pipesCount) {
|
|
case 0:
|
|
state.pipes = dest;
|
|
break;
|
|
case 1:
|
|
state.pipes = [state.pipes, dest];
|
|
break;
|
|
default:
|
|
state.pipes.push(dest);
|
|
break;
|
|
}
|
|
state.pipesCount += 1;
|
|
debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts);
|
|
|
|
var doEnd = (!pipeOpts || pipeOpts.end !== false) &&
|
|
dest !== process.stdout &&
|
|
dest !== process.stderr;
|
|
|
|
var endFn = doEnd ? onend : cleanup;
|
|
if (state.endEmitted)
|
|
process.nextTick(endFn);
|
|
else
|
|
src.once('end', endFn);
|
|
|
|
dest.on('unpipe', onunpipe);
|
|
function onunpipe(readable) {
|
|
debug('onunpipe');
|
|
if (readable === src) {
|
|
cleanup();
|
|
}
|
|
}
|
|
|
|
function onend() {
|
|
debug('onend');
|
|
dest.end();
|
|
}
|
|
|
|
// when the dest drains, it reduces the awaitDrain counter
|
|
// on the source. This would be more elegant with a .once()
|
|
// handler in flow(), but adding and removing repeatedly is
|
|
// too slow.
|
|
var ondrain = pipeOnDrain(src);
|
|
dest.on('drain', ondrain);
|
|
|
|
function cleanup() {
|
|
debug('cleanup');
|
|
// cleanup event handlers once the pipe is broken
|
|
dest.removeListener('close', onclose);
|
|
dest.removeListener('finish', onfinish);
|
|
dest.removeListener('drain', ondrain);
|
|
dest.removeListener('error', onerror);
|
|
dest.removeListener('unpipe', onunpipe);
|
|
src.removeListener('end', onend);
|
|
src.removeListener('end', cleanup);
|
|
src.removeListener('data', ondata);
|
|
|
|
// if the reader is waiting for a drain event from this
|
|
// specific writer, then it would cause it to never start
|
|
// flowing again.
|
|
// So, if this is awaiting a drain, then we just call it now.
|
|
// If we don't know, then assume that we are waiting for one.
|
|
if (state.awaitDrain &&
|
|
(!dest._writableState || dest._writableState.needDrain))
|
|
ondrain();
|
|
}
|
|
|
|
src.on('data', ondata);
|
|
function ondata(chunk) {
|
|
debug('ondata');
|
|
var ret = dest.write(chunk);
|
|
if (false === ret) {
|
|
debug('false write response, pause',
|
|
src._readableState.awaitDrain);
|
|
src._readableState.awaitDrain++;
|
|
src.pause();
|
|
}
|
|
}
|
|
|
|
// if the dest has an error, then stop piping into it.
|
|
// however, don't suppress the throwing behavior for this.
|
|
function onerror(er) {
|
|
debug('onerror', er);
|
|
unpipe();
|
|
dest.removeListener('error', onerror);
|
|
if (EE.listenerCount(dest, 'error') === 0)
|
|
dest.emit('error', er);
|
|
}
|
|
// This is a brutally ugly hack to make sure that our error handler
|
|
// is attached before any userland ones. NEVER DO THIS.
|
|
if (!dest._events || !dest._events.error)
|
|
dest.on('error', onerror);
|
|
else if (isArray(dest._events.error))
|
|
dest._events.error.unshift(onerror);
|
|
else
|
|
dest._events.error = [onerror, dest._events.error];
|
|
|
|
|
|
|
|
// Both close and finish should trigger unpipe, but only once.
|
|
function onclose() {
|
|
dest.removeListener('finish', onfinish);
|
|
unpipe();
|
|
}
|
|
dest.once('close', onclose);
|
|
function onfinish() {
|
|
debug('onfinish');
|
|
dest.removeListener('close', onclose);
|
|
unpipe();
|
|
}
|
|
dest.once('finish', onfinish);
|
|
|
|
function unpipe() {
|
|
debug('unpipe');
|
|
src.unpipe(dest);
|
|
}
|
|
|
|
// tell the dest that it's being piped to
|
|
dest.emit('pipe', src);
|
|
|
|
// start the flow if it hasn't been started already.
|
|
if (!state.flowing) {
|
|
debug('pipe resume');
|
|
src.resume();
|
|
}
|
|
|
|
return dest;
|
|
};
|
|
|
|
function pipeOnDrain(src) {
|
|
return function() {
|
|
var state = src._readableState;
|
|
debug('pipeOnDrain', state.awaitDrain);
|
|
if (state.awaitDrain)
|
|
state.awaitDrain--;
|
|
if (state.awaitDrain === 0 && EE.listenerCount(src, 'data')) {
|
|
state.flowing = true;
|
|
flow(src);
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
Readable.prototype.unpipe = function(dest) {
|
|
var state = this._readableState;
|
|
|
|
// if we're not piping anywhere, then do nothing.
|
|
if (state.pipesCount === 0)
|
|
return this;
|
|
|
|
// just one destination. most common case.
|
|
if (state.pipesCount === 1) {
|
|
// passed in one, but it's not the right one.
|
|
if (dest && dest !== state.pipes)
|
|
return this;
|
|
|
|
if (!dest)
|
|
dest = state.pipes;
|
|
|
|
// got a match.
|
|
state.pipes = null;
|
|
state.pipesCount = 0;
|
|
state.flowing = false;
|
|
if (dest)
|
|
dest.emit('unpipe', this);
|
|
return this;
|
|
}
|
|
|
|
// slow case. multiple pipe destinations.
|
|
|
|
if (!dest) {
|
|
// remove all.
|
|
var dests = state.pipes;
|
|
var len = state.pipesCount;
|
|
state.pipes = null;
|
|
state.pipesCount = 0;
|
|
state.flowing = false;
|
|
|
|
for (var i = 0; i < len; i++)
|
|
dests[i].emit('unpipe', this);
|
|
return this;
|
|
}
|
|
|
|
// try to find the right one.
|
|
var i = indexOf(state.pipes, dest);
|
|
if (i === -1)
|
|
return this;
|
|
|
|
state.pipes.splice(i, 1);
|
|
state.pipesCount -= 1;
|
|
if (state.pipesCount === 1)
|
|
state.pipes = state.pipes[0];
|
|
|
|
dest.emit('unpipe', this);
|
|
|
|
return this;
|
|
};
|
|
|
|
// set up data events if they are asked for
|
|
// Ensure readable listeners eventually get something
|
|
Readable.prototype.on = function(ev, fn) {
|
|
var res = Stream.prototype.on.call(this, ev, fn);
|
|
|
|
// If listening to data, and it has not explicitly been paused,
|
|
// then call resume to start the flow of data on the next tick.
|
|
if (ev === 'data' && false !== this._readableState.flowing) {
|
|
this.resume();
|
|
}
|
|
|
|
if (ev === 'readable' && this.readable) {
|
|
var state = this._readableState;
|
|
if (!state.readableListening) {
|
|
state.readableListening = true;
|
|
state.emittedReadable = false;
|
|
state.needReadable = true;
|
|
if (!state.reading) {
|
|
var self = this;
|
|
process.nextTick(function() {
|
|
debug('readable nexttick read 0');
|
|
self.read(0);
|
|
});
|
|
} else if (state.length) {
|
|
emitReadable(this, state);
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
};
|
|
Readable.prototype.addListener = Readable.prototype.on;
|
|
|
|
// pause() and resume() are remnants of the legacy readable stream API
|
|
// If the user uses them, then switch into old mode.
|
|
Readable.prototype.resume = function() {
|
|
var state = this._readableState;
|
|
if (!state.flowing) {
|
|
debug('resume');
|
|
state.flowing = true;
|
|
if (!state.reading) {
|
|
debug('resume read 0');
|
|
this.read(0);
|
|
}
|
|
resume(this, state);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
function resume(stream, state) {
|
|
if (!state.resumeScheduled) {
|
|
state.resumeScheduled = true;
|
|
process.nextTick(function() {
|
|
resume_(stream, state);
|
|
});
|
|
}
|
|
}
|
|
|
|
function resume_(stream, state) {
|
|
state.resumeScheduled = false;
|
|
stream.emit('resume');
|
|
flow(stream);
|
|
if (state.flowing && !state.reading)
|
|
stream.read(0);
|
|
}
|
|
|
|
Readable.prototype.pause = function() {
|
|
debug('call pause flowing=%j', this._readableState.flowing);
|
|
if (false !== this._readableState.flowing) {
|
|
debug('pause');
|
|
this._readableState.flowing = false;
|
|
this.emit('pause');
|
|
}
|
|
return this;
|
|
};
|
|
|
|
function flow(stream) {
|
|
var state = stream._readableState;
|
|
debug('flow', state.flowing);
|
|
if (state.flowing) {
|
|
do {
|
|
var chunk = stream.read();
|
|
} while (null !== chunk && state.flowing);
|
|
}
|
|
}
|
|
|
|
// wrap an old-style stream as the async data source.
|
|
// This is *not* part of the readable stream interface.
|
|
// It is an ugly unfortunate mess of history.
|
|
Readable.prototype.wrap = function(stream) {
|
|
var state = this._readableState;
|
|
var paused = false;
|
|
|
|
var self = this;
|
|
stream.on('end', function() {
|
|
debug('wrapped end');
|
|
if (state.decoder && !state.ended) {
|
|
var chunk = state.decoder.end();
|
|
if (chunk && chunk.length)
|
|
self.push(chunk);
|
|
}
|
|
|
|
self.push(null);
|
|
});
|
|
|
|
stream.on('data', function(chunk) {
|
|
debug('wrapped data');
|
|
if (state.decoder)
|
|
chunk = state.decoder.write(chunk);
|
|
if (!chunk || !state.objectMode && !chunk.length)
|
|
return;
|
|
|
|
var ret = self.push(chunk);
|
|
if (!ret) {
|
|
paused = true;
|
|
stream.pause();
|
|
}
|
|
});
|
|
|
|
// proxy all the other methods.
|
|
// important when wrapping filters and duplexes.
|
|
for (var i in stream) {
|
|
if (util.isFunction(stream[i]) && util.isUndefined(this[i])) {
|
|
this[i] = function(method) { return function() {
|
|
return stream[method].apply(stream, arguments);
|
|
}}(i);
|
|
}
|
|
}
|
|
|
|
// proxy certain important events.
|
|
var events = ['error', 'close', 'destroy', 'pause', 'resume'];
|
|
forEach(events, function(ev) {
|
|
stream.on(ev, self.emit.bind(self, ev));
|
|
});
|
|
|
|
// when we try to consume some more bytes, simply unpause the
|
|
// underlying stream.
|
|
self._read = function(n) {
|
|
debug('wrapped _read', n);
|
|
if (paused) {
|
|
paused = false;
|
|
stream.resume();
|
|
}
|
|
};
|
|
|
|
return self;
|
|
};
|
|
|
|
|
|
|
|
// exposed for testing purposes only.
|
|
Readable._fromList = fromList;
|
|
|
|
// Pluck off n bytes from an array of buffers.
|
|
// Length is the combined lengths of all the buffers in the list.
|
|
function fromList(n, state) {
|
|
var list = state.buffer;
|
|
var length = state.length;
|
|
var stringMode = !!state.decoder;
|
|
var objectMode = !!state.objectMode;
|
|
var ret;
|
|
|
|
// nothing in the list, definitely empty.
|
|
if (list.length === 0)
|
|
return null;
|
|
|
|
if (length === 0)
|
|
ret = null;
|
|
else if (objectMode)
|
|
ret = list.shift();
|
|
else if (!n || n >= length) {
|
|
// read it all, truncate the array.
|
|
if (stringMode)
|
|
ret = list.join('');
|
|
else
|
|
ret = Buffer.concat(list, length);
|
|
list.length = 0;
|
|
} else {
|
|
// read just some of it.
|
|
if (n < list[0].length) {
|
|
// just take a part of the first list item.
|
|
// slice is the same for buffers and strings.
|
|
var buf = list[0];
|
|
ret = buf.slice(0, n);
|
|
list[0] = buf.slice(n);
|
|
} else if (n === list[0].length) {
|
|
// first list is a perfect match
|
|
ret = list.shift();
|
|
} else {
|
|
// complex case.
|
|
// we have enough to cover it, but it spans past the first buffer.
|
|
if (stringMode)
|
|
ret = '';
|
|
else
|
|
ret = new Buffer(n);
|
|
|
|
var c = 0;
|
|
for (var i = 0, l = list.length; i < l && c < n; i++) {
|
|
var buf = list[0];
|
|
var cpy = Math.min(n - c, buf.length);
|
|
|
|
if (stringMode)
|
|
ret += buf.slice(0, cpy);
|
|
else
|
|
buf.copy(ret, c, 0, cpy);
|
|
|
|
if (cpy < buf.length)
|
|
list[0] = buf.slice(cpy);
|
|
else
|
|
list.shift();
|
|
|
|
c += cpy;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
function endReadable(stream) {
|
|
var state = stream._readableState;
|
|
|
|
// If we get here before consuming all the bytes, then that is a
|
|
// bug in node. Should never happen.
|
|
if (state.length > 0)
|
|
throw new Error('endReadable called on non-empty stream');
|
|
|
|
if (!state.endEmitted) {
|
|
state.ended = true;
|
|
process.nextTick(function() {
|
|
// Check that we didn't get one last unshift.
|
|
if (!state.endEmitted && state.length === 0) {
|
|
state.endEmitted = true;
|
|
stream.readable = false;
|
|
stream.emit('end');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function forEach (xs, f) {
|
|
for (var i = 0, l = xs.length; i < l; i++) {
|
|
f(xs[i], i);
|
|
}
|
|
}
|
|
|
|
function indexOf (xs, x) {
|
|
for (var i = 0, l = xs.length; i < l; i++) {
|
|
if (xs[i] === x) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
}).call(this,require('_process'))
|
|
},{"./_stream_duplex":96,"_process":133,"buffer":43,"core-util-is":44,"events":56,"inherits":87,"isarray":95,"stream":278,"string_decoder/":279,"util":41}],99:[function(require,module,exports){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
|
|
// a transform stream is a readable/writable stream where you do
|
|
// something with the data. Sometimes it's called a "filter",
|
|
// but that's not a great name for it, since that implies a thing where
|
|
// some bits pass through, and others are simply ignored. (That would
|
|
// be a valid example of a transform, of course.)
|
|
//
|
|
// While the output is causally related to the input, it's not a
|
|
// necessarily symmetric or synchronous transformation. For example,
|
|
// a zlib stream might take multiple plain-text writes(), and then
|
|
// emit a single compressed chunk some time in the future.
|
|
//
|
|
// Here's how this works:
|
|
//
|
|
// The Transform stream has all the aspects of the readable and writable
|
|
// stream classes. When you write(chunk), that calls _write(chunk,cb)
|
|
// internally, and returns false if there's a lot of pending writes
|
|
// buffered up. When you call read(), that calls _read(n) until
|
|
// there's enough pending readable data buffered up.
|
|
//
|
|
// In a transform stream, the written data is placed in a buffer. When
|
|
// _read(n) is called, it transforms the queued up data, calling the
|
|
// buffered _write cb's as it consumes chunks. If consuming a single
|
|
// written chunk would result in multiple output chunks, then the first
|
|
// outputted bit calls the readcb, and subsequent chunks just go into
|
|
// the read buffer, and will cause it to emit 'readable' if necessary.
|
|
//
|
|
// This way, back-pressure is actually determined by the reading side,
|
|
// since _read has to be called to start processing a new chunk. However,
|
|
// a pathological inflate type of transform can cause excessive buffering
|
|
// here. For example, imagine a stream where every byte of input is
|
|
// interpreted as an integer from 0-255, and then results in that many
|
|
// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
|
|
// 1kb of data being output. In this case, you could write a very small
|
|
// amount of input, and end up with a very large amount of output. In
|
|
// such a pathological inflating mechanism, there'd be no way to tell
|
|
// the system to stop doing the transform. A single 4MB write could
|
|
// cause the system to run out of memory.
|
|
//
|
|
// However, even in such a pathological case, only a single written chunk
|
|
// would be consumed, and then the rest would wait (un-transformed) until
|
|
// the results of the previous transformed chunk were consumed.
|
|
|
|
module.exports = Transform;
|
|
|
|
var Duplex = require('./_stream_duplex');
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
util.inherits(Transform, Duplex);
|
|
|
|
|
|
function TransformState(options, stream) {
|
|
this.afterTransform = function(er, data) {
|
|
return afterTransform(stream, er, data);
|
|
};
|
|
|
|
this.needTransform = false;
|
|
this.transforming = false;
|
|
this.writecb = null;
|
|
this.writechunk = null;
|
|
}
|
|
|
|
function afterTransform(stream, er, data) {
|
|
var ts = stream._transformState;
|
|
ts.transforming = false;
|
|
|
|
var cb = ts.writecb;
|
|
|
|
if (!cb)
|
|
return stream.emit('error', new Error('no writecb in Transform class'));
|
|
|
|
ts.writechunk = null;
|
|
ts.writecb = null;
|
|
|
|
if (!util.isNullOrUndefined(data))
|
|
stream.push(data);
|
|
|
|
if (cb)
|
|
cb(er);
|
|
|
|
var rs = stream._readableState;
|
|
rs.reading = false;
|
|
if (rs.needReadable || rs.length < rs.highWaterMark) {
|
|
stream._read(rs.highWaterMark);
|
|
}
|
|
}
|
|
|
|
|
|
function Transform(options) {
|
|
if (!(this instanceof Transform))
|
|
return new Transform(options);
|
|
|
|
Duplex.call(this, options);
|
|
|
|
this._transformState = new TransformState(options, this);
|
|
|
|
// when the writable side finishes, then flush out anything remaining.
|
|
var stream = this;
|
|
|
|
// start out asking for a readable event once data is transformed.
|
|
this._readableState.needReadable = true;
|
|
|
|
// we have implemented the _read method, and done the other things
|
|
// that Readable wants before the first _read call, so unset the
|
|
// sync guard flag.
|
|
this._readableState.sync = false;
|
|
|
|
this.once('prefinish', function() {
|
|
if (util.isFunction(this._flush))
|
|
this._flush(function(er) {
|
|
done(stream, er);
|
|
});
|
|
else
|
|
done(stream);
|
|
});
|
|
}
|
|
|
|
Transform.prototype.push = function(chunk, encoding) {
|
|
this._transformState.needTransform = false;
|
|
return Duplex.prototype.push.call(this, chunk, encoding);
|
|
};
|
|
|
|
// This is the part where you do stuff!
|
|
// override this function in implementation classes.
|
|
// 'chunk' is an input chunk.
|
|
//
|
|
// Call `push(newChunk)` to pass along transformed output
|
|
// to the readable side. You may call 'push' zero or more times.
|
|
//
|
|
// Call `cb(err)` when you are done with this chunk. If you pass
|
|
// an error, then that'll put the hurt on the whole operation. If you
|
|
// never call cb(), then you'll never get another chunk.
|
|
Transform.prototype._transform = function(chunk, encoding, cb) {
|
|
throw new Error('not implemented');
|
|
};
|
|
|
|
Transform.prototype._write = function(chunk, encoding, cb) {
|
|
var ts = this._transformState;
|
|
ts.writecb = cb;
|
|
ts.writechunk = chunk;
|
|
ts.writeencoding = encoding;
|
|
if (!ts.transforming) {
|
|
var rs = this._readableState;
|
|
if (ts.needTransform ||
|
|
rs.needReadable ||
|
|
rs.length < rs.highWaterMark)
|
|
this._read(rs.highWaterMark);
|
|
}
|
|
};
|
|
|
|
// Doesn't matter what the args are here.
|
|
// _transform does all the work.
|
|
// That we got here means that the readable side wants more data.
|
|
Transform.prototype._read = function(n) {
|
|
var ts = this._transformState;
|
|
|
|
if (!util.isNull(ts.writechunk) && ts.writecb && !ts.transforming) {
|
|
ts.transforming = true;
|
|
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
|
|
} else {
|
|
// mark that we need a transform, so that any data that comes in
|
|
// will get processed, now that we've asked for it.
|
|
ts.needTransform = true;
|
|
}
|
|
};
|
|
|
|
|
|
function done(stream, er) {
|
|
if (er)
|
|
return stream.emit('error', er);
|
|
|
|
// if there's nothing in the write buffer, then that means
|
|
// that nothing more will ever be provided
|
|
var ws = stream._writableState;
|
|
var ts = stream._transformState;
|
|
|
|
if (ws.length)
|
|
throw new Error('calling transform done when ws.length != 0');
|
|
|
|
if (ts.transforming)
|
|
throw new Error('calling transform done when still transforming');
|
|
|
|
return stream.push(null);
|
|
}
|
|
|
|
},{"./_stream_duplex":96,"core-util-is":44,"inherits":87}],100:[function(require,module,exports){
|
|
(function (process){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
// A bit simpler than readable streams.
|
|
// Implement an async ._write(chunk, cb), and it'll handle all
|
|
// the drain event emission and buffering.
|
|
|
|
module.exports = Writable;
|
|
|
|
/*<replacement>*/
|
|
var Buffer = require('buffer').Buffer;
|
|
/*</replacement>*/
|
|
|
|
Writable.WritableState = WritableState;
|
|
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
var Stream = require('stream');
|
|
|
|
util.inherits(Writable, Stream);
|
|
|
|
function WriteReq(chunk, encoding, cb) {
|
|
this.chunk = chunk;
|
|
this.encoding = encoding;
|
|
this.callback = cb;
|
|
}
|
|
|
|
function WritableState(options, stream) {
|
|
var Duplex = require('./_stream_duplex');
|
|
|
|
options = options || {};
|
|
|
|
// the point at which write() starts returning false
|
|
// Note: 0 is a valid value, means that we always return false if
|
|
// the entire buffer is not flushed immediately on write()
|
|
var hwm = options.highWaterMark;
|
|
var defaultHwm = options.objectMode ? 16 : 16 * 1024;
|
|
this.highWaterMark = (hwm || hwm === 0) ? hwm : defaultHwm;
|
|
|
|
// object stream flag to indicate whether or not this stream
|
|
// contains buffers or objects.
|
|
this.objectMode = !!options.objectMode;
|
|
|
|
if (stream instanceof Duplex)
|
|
this.objectMode = this.objectMode || !!options.writableObjectMode;
|
|
|
|
// cast to ints.
|
|
this.highWaterMark = ~~this.highWaterMark;
|
|
|
|
this.needDrain = false;
|
|
// at the start of calling end()
|
|
this.ending = false;
|
|
// when end() has been called, and returned
|
|
this.ended = false;
|
|
// when 'finish' is emitted
|
|
this.finished = false;
|
|
|
|
// should we decode strings into buffers before passing to _write?
|
|
// this is here so that some node-core streams can optimize string
|
|
// handling at a lower level.
|
|
var noDecode = options.decodeStrings === false;
|
|
this.decodeStrings = !noDecode;
|
|
|
|
// Crypto is kind of old and crusty. Historically, its default string
|
|
// encoding is 'binary' so we have to make this configurable.
|
|
// Everything else in the universe uses 'utf8', though.
|
|
this.defaultEncoding = options.defaultEncoding || 'utf8';
|
|
|
|
// not an actual buffer we keep track of, but a measurement
|
|
// of how much we're waiting to get pushed to some underlying
|
|
// socket or file.
|
|
this.length = 0;
|
|
|
|
// a flag to see when we're in the middle of a write.
|
|
this.writing = false;
|
|
|
|
// when true all writes will be buffered until .uncork() call
|
|
this.corked = 0;
|
|
|
|
// a flag to be able to tell if the onwrite cb is called immediately,
|
|
// or on a later tick. We set this to true at first, because any
|
|
// actions that shouldn't happen until "later" should generally also
|
|
// not happen before the first write call.
|
|
this.sync = true;
|
|
|
|
// a flag to know if we're processing previously buffered items, which
|
|
// may call the _write() callback in the same tick, so that we don't
|
|
// end up in an overlapped onwrite situation.
|
|
this.bufferProcessing = false;
|
|
|
|
// the callback that's passed to _write(chunk,cb)
|
|
this.onwrite = function(er) {
|
|
onwrite(stream, er);
|
|
};
|
|
|
|
// the callback that the user supplies to write(chunk,encoding,cb)
|
|
this.writecb = null;
|
|
|
|
// the amount that is being written when _write is called.
|
|
this.writelen = 0;
|
|
|
|
this.buffer = [];
|
|
|
|
// number of pending user-supplied write callbacks
|
|
// this must be 0 before 'finish' can be emitted
|
|
this.pendingcb = 0;
|
|
|
|
// emit prefinish if the only thing we're waiting for is _write cbs
|
|
// This is relevant for synchronous Transform streams
|
|
this.prefinished = false;
|
|
|
|
// True if the error was already emitted and should not be thrown again
|
|
this.errorEmitted = false;
|
|
}
|
|
|
|
function Writable(options) {
|
|
var Duplex = require('./_stream_duplex');
|
|
|
|
// Writable ctor is applied to Duplexes, though they're not
|
|
// instanceof Writable, they're instanceof Readable.
|
|
if (!(this instanceof Writable) && !(this instanceof Duplex))
|
|
return new Writable(options);
|
|
|
|
this._writableState = new WritableState(options, this);
|
|
|
|
// legacy.
|
|
this.writable = true;
|
|
|
|
Stream.call(this);
|
|
}
|
|
|
|
// Otherwise people can pipe Writable streams, which is just wrong.
|
|
Writable.prototype.pipe = function() {
|
|
this.emit('error', new Error('Cannot pipe. Not readable.'));
|
|
};
|
|
|
|
|
|
function writeAfterEnd(stream, state, cb) {
|
|
var er = new Error('write after end');
|
|
// TODO: defer error events consistently everywhere, not just the cb
|
|
stream.emit('error', er);
|
|
process.nextTick(function() {
|
|
cb(er);
|
|
});
|
|
}
|
|
|
|
// If we get something that is not a buffer, string, null, or undefined,
|
|
// and we're not in objectMode, then that's an error.
|
|
// Otherwise stream chunks are all considered to be of length=1, and the
|
|
// watermarks determine how many objects to keep in the buffer, rather than
|
|
// how many bytes or characters.
|
|
function validChunk(stream, state, chunk, cb) {
|
|
var valid = true;
|
|
if (!util.isBuffer(chunk) &&
|
|
!util.isString(chunk) &&
|
|
!util.isNullOrUndefined(chunk) &&
|
|
!state.objectMode) {
|
|
var er = new TypeError('Invalid non-string/buffer chunk');
|
|
stream.emit('error', er);
|
|
process.nextTick(function() {
|
|
cb(er);
|
|
});
|
|
valid = false;
|
|
}
|
|
return valid;
|
|
}
|
|
|
|
Writable.prototype.write = function(chunk, encoding, cb) {
|
|
var state = this._writableState;
|
|
var ret = false;
|
|
|
|
if (util.isFunction(encoding)) {
|
|
cb = encoding;
|
|
encoding = null;
|
|
}
|
|
|
|
if (util.isBuffer(chunk))
|
|
encoding = 'buffer';
|
|
else if (!encoding)
|
|
encoding = state.defaultEncoding;
|
|
|
|
if (!util.isFunction(cb))
|
|
cb = function() {};
|
|
|
|
if (state.ended)
|
|
writeAfterEnd(this, state, cb);
|
|
else if (validChunk(this, state, chunk, cb)) {
|
|
state.pendingcb++;
|
|
ret = writeOrBuffer(this, state, chunk, encoding, cb);
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
Writable.prototype.cork = function() {
|
|
var state = this._writableState;
|
|
|
|
state.corked++;
|
|
};
|
|
|
|
Writable.prototype.uncork = function() {
|
|
var state = this._writableState;
|
|
|
|
if (state.corked) {
|
|
state.corked--;
|
|
|
|
if (!state.writing &&
|
|
!state.corked &&
|
|
!state.finished &&
|
|
!state.bufferProcessing &&
|
|
state.buffer.length)
|
|
clearBuffer(this, state);
|
|
}
|
|
};
|
|
|
|
function decodeChunk(state, chunk, encoding) {
|
|
if (!state.objectMode &&
|
|
state.decodeStrings !== false &&
|
|
util.isString(chunk)) {
|
|
chunk = new Buffer(chunk, encoding);
|
|
}
|
|
return chunk;
|
|
}
|
|
|
|
// if we're already writing something, then just put this
|
|
// in the queue, and wait our turn. Otherwise, call _write
|
|
// If we return false, then we need a drain event, so set that flag.
|
|
function writeOrBuffer(stream, state, chunk, encoding, cb) {
|
|
chunk = decodeChunk(state, chunk, encoding);
|
|
if (util.isBuffer(chunk))
|
|
encoding = 'buffer';
|
|
var len = state.objectMode ? 1 : chunk.length;
|
|
|
|
state.length += len;
|
|
|
|
var ret = state.length < state.highWaterMark;
|
|
// we must ensure that previous needDrain will not be reset to false.
|
|
if (!ret)
|
|
state.needDrain = true;
|
|
|
|
if (state.writing || state.corked)
|
|
state.buffer.push(new WriteReq(chunk, encoding, cb));
|
|
else
|
|
doWrite(stream, state, false, len, chunk, encoding, cb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
function doWrite(stream, state, writev, len, chunk, encoding, cb) {
|
|
state.writelen = len;
|
|
state.writecb = cb;
|
|
state.writing = true;
|
|
state.sync = true;
|
|
if (writev)
|
|
stream._writev(chunk, state.onwrite);
|
|
else
|
|
stream._write(chunk, encoding, state.onwrite);
|
|
state.sync = false;
|
|
}
|
|
|
|
function onwriteError(stream, state, sync, er, cb) {
|
|
if (sync)
|
|
process.nextTick(function() {
|
|
state.pendingcb--;
|
|
cb(er);
|
|
});
|
|
else {
|
|
state.pendingcb--;
|
|
cb(er);
|
|
}
|
|
|
|
stream._writableState.errorEmitted = true;
|
|
stream.emit('error', er);
|
|
}
|
|
|
|
function onwriteStateUpdate(state) {
|
|
state.writing = false;
|
|
state.writecb = null;
|
|
state.length -= state.writelen;
|
|
state.writelen = 0;
|
|
}
|
|
|
|
function onwrite(stream, er) {
|
|
var state = stream._writableState;
|
|
var sync = state.sync;
|
|
var cb = state.writecb;
|
|
|
|
onwriteStateUpdate(state);
|
|
|
|
if (er)
|
|
onwriteError(stream, state, sync, er, cb);
|
|
else {
|
|
// Check if we're actually ready to finish, but don't emit yet
|
|
var finished = needFinish(stream, state);
|
|
|
|
if (!finished &&
|
|
!state.corked &&
|
|
!state.bufferProcessing &&
|
|
state.buffer.length) {
|
|
clearBuffer(stream, state);
|
|
}
|
|
|
|
if (sync) {
|
|
process.nextTick(function() {
|
|
afterWrite(stream, state, finished, cb);
|
|
});
|
|
} else {
|
|
afterWrite(stream, state, finished, cb);
|
|
}
|
|
}
|
|
}
|
|
|
|
function afterWrite(stream, state, finished, cb) {
|
|
if (!finished)
|
|
onwriteDrain(stream, state);
|
|
state.pendingcb--;
|
|
cb();
|
|
finishMaybe(stream, state);
|
|
}
|
|
|
|
// Must force callback to be called on nextTick, so that we don't
|
|
// emit 'drain' before the write() consumer gets the 'false' return
|
|
// value, and has a chance to attach a 'drain' listener.
|
|
function onwriteDrain(stream, state) {
|
|
if (state.length === 0 && state.needDrain) {
|
|
state.needDrain = false;
|
|
stream.emit('drain');
|
|
}
|
|
}
|
|
|
|
|
|
// if there's something in the buffer waiting, then process it
|
|
function clearBuffer(stream, state) {
|
|
state.bufferProcessing = true;
|
|
|
|
if (stream._writev && state.buffer.length > 1) {
|
|
// Fast case, write everything using _writev()
|
|
var cbs = [];
|
|
for (var c = 0; c < state.buffer.length; c++)
|
|
cbs.push(state.buffer[c].callback);
|
|
|
|
// count the one we are adding, as well.
|
|
// TODO(isaacs) clean this up
|
|
state.pendingcb++;
|
|
doWrite(stream, state, true, state.length, state.buffer, '', function(err) {
|
|
for (var i = 0; i < cbs.length; i++) {
|
|
state.pendingcb--;
|
|
cbs[i](err);
|
|
}
|
|
});
|
|
|
|
// Clear buffer
|
|
state.buffer = [];
|
|
} else {
|
|
// Slow case, write chunks one-by-one
|
|
for (var c = 0; c < state.buffer.length; c++) {
|
|
var entry = state.buffer[c];
|
|
var chunk = entry.chunk;
|
|
var encoding = entry.encoding;
|
|
var cb = entry.callback;
|
|
var len = state.objectMode ? 1 : chunk.length;
|
|
|
|
doWrite(stream, state, false, len, chunk, encoding, cb);
|
|
|
|
// if we didn't call the onwrite immediately, then
|
|
// it means that we need to wait until it does.
|
|
// also, that means that the chunk and cb are currently
|
|
// being processed, so move the buffer counter past them.
|
|
if (state.writing) {
|
|
c++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (c < state.buffer.length)
|
|
state.buffer = state.buffer.slice(c);
|
|
else
|
|
state.buffer.length = 0;
|
|
}
|
|
|
|
state.bufferProcessing = false;
|
|
}
|
|
|
|
Writable.prototype._write = function(chunk, encoding, cb) {
|
|
cb(new Error('not implemented'));
|
|
|
|
};
|
|
|
|
Writable.prototype._writev = null;
|
|
|
|
Writable.prototype.end = function(chunk, encoding, cb) {
|
|
var state = this._writableState;
|
|
|
|
if (util.isFunction(chunk)) {
|
|
cb = chunk;
|
|
chunk = null;
|
|
encoding = null;
|
|
} else if (util.isFunction(encoding)) {
|
|
cb = encoding;
|
|
encoding = null;
|
|
}
|
|
|
|
if (!util.isNullOrUndefined(chunk))
|
|
this.write(chunk, encoding);
|
|
|
|
// .end() fully uncorks
|
|
if (state.corked) {
|
|
state.corked = 1;
|
|
this.uncork();
|
|
}
|
|
|
|
// ignore unnecessary end() calls.
|
|
if (!state.ending && !state.finished)
|
|
endWritable(this, state, cb);
|
|
};
|
|
|
|
|
|
function needFinish(stream, state) {
|
|
return (state.ending &&
|
|
state.length === 0 &&
|
|
!state.finished &&
|
|
!state.writing);
|
|
}
|
|
|
|
function prefinish(stream, state) {
|
|
if (!state.prefinished) {
|
|
state.prefinished = true;
|
|
stream.emit('prefinish');
|
|
}
|
|
}
|
|
|
|
function finishMaybe(stream, state) {
|
|
var need = needFinish(stream, state);
|
|
if (need) {
|
|
if (state.pendingcb === 0) {
|
|
prefinish(stream, state);
|
|
state.finished = true;
|
|
stream.emit('finish');
|
|
} else
|
|
prefinish(stream, state);
|
|
}
|
|
return need;
|
|
}
|
|
|
|
function endWritable(stream, state, cb) {
|
|
state.ending = true;
|
|
finishMaybe(stream, state);
|
|
if (cb) {
|
|
if (state.finished)
|
|
process.nextTick(cb);
|
|
else
|
|
stream.once('finish', cb);
|
|
}
|
|
state.ended = true;
|
|
}
|
|
|
|
}).call(this,require('_process'))
|
|
},{"./_stream_duplex":96,"_process":133,"buffer":43,"core-util-is":44,"inherits":87,"stream":278}],101:[function(require,module,exports){
|
|
(function (process){
|
|
exports = module.exports = require('./lib/_stream_readable.js');
|
|
exports.Stream = require('stream');
|
|
exports.Readable = exports;
|
|
exports.Writable = require('./lib/_stream_writable.js');
|
|
exports.Duplex = require('./lib/_stream_duplex.js');
|
|
exports.Transform = require('./lib/_stream_transform.js');
|
|
exports.PassThrough = require('./lib/_stream_passthrough.js');
|
|
if (!process.browser && process.env.READABLE_STREAM === 'disable') {
|
|
module.exports = require('stream');
|
|
}
|
|
|
|
}).call(this,require('_process'))
|
|
},{"./lib/_stream_duplex.js":96,"./lib/_stream_passthrough.js":97,"./lib/_stream_readable.js":98,"./lib/_stream_transform.js":99,"./lib/_stream_writable.js":100,"_process":133,"stream":278}],102:[function(require,module,exports){
|
|
/* Copyright (c) 2012-2016 LevelUP contributors
|
|
* See list at <https://github.com/level/levelup#contributing>
|
|
* MIT License
|
|
* <https://github.com/level/levelup/blob/master/LICENSE.md>
|
|
*/
|
|
|
|
var util = require('./util')
|
|
, WriteError = require('level-errors').WriteError
|
|
|
|
, getOptions = util.getOptions
|
|
, dispatchError = util.dispatchError
|
|
|
|
function Batch (levelup, codec) {
|
|
this._levelup = levelup
|
|
this._codec = codec
|
|
this.batch = levelup.db.batch()
|
|
this.ops = []
|
|
this.length = 0
|
|
}
|
|
|
|
Batch.prototype.put = function (key_, value_, options) {
|
|
options = getOptions(options)
|
|
|
|
var key = this._codec.encodeKey(key_, options)
|
|
, value = this._codec.encodeValue(value_, options)
|
|
|
|
try {
|
|
this.batch.put(key, value)
|
|
} catch (e) {
|
|
throw new WriteError(e)
|
|
}
|
|
this.ops.push({ type : 'put', key : key, value : value })
|
|
this.length++
|
|
|
|
return this
|
|
}
|
|
|
|
Batch.prototype.del = function (key_, options) {
|
|
options = getOptions(options)
|
|
|
|
var key = this._codec.encodeKey(key_, options)
|
|
|
|
try {
|
|
this.batch.del(key)
|
|
} catch (err) {
|
|
throw new WriteError(err)
|
|
}
|
|
this.ops.push({ type : 'del', key : key })
|
|
this.length++
|
|
|
|
return this
|
|
}
|
|
|
|
Batch.prototype.clear = function () {
|
|
try {
|
|
this.batch.clear()
|
|
} catch (err) {
|
|
throw new WriteError(err)
|
|
}
|
|
|
|
this.ops = []
|
|
this.length = 0
|
|
return this
|
|
}
|
|
|
|
Batch.prototype.write = function (callback) {
|
|
var levelup = this._levelup
|
|
, ops = this.ops
|
|
|
|
try {
|
|
this.batch.write(function (err) {
|
|
if (err)
|
|
return dispatchError(levelup, new WriteError(err), callback)
|
|
levelup.emit('batch', ops)
|
|
if (callback)
|
|
callback()
|
|
})
|
|
} catch (err) {
|
|
throw new WriteError(err)
|
|
}
|
|
}
|
|
|
|
module.exports = Batch
|
|
|
|
},{"./util":104,"level-errors":93}],103:[function(require,module,exports){
|
|
(function (process){
|
|
/* Copyright (c) 2012-2016 LevelUP contributors
|
|
* See list at <https://github.com/level/levelup#contributing>
|
|
* MIT License
|
|
* <https://github.com/level/levelup/blob/master/LICENSE.md>
|
|
*/
|
|
|
|
var EventEmitter = require('events').EventEmitter
|
|
, inherits = require('util').inherits
|
|
, deprecate = require('util').deprecate
|
|
, extend = require('xtend')
|
|
, prr = require('prr')
|
|
, DeferredLevelDOWN = require('deferred-leveldown')
|
|
, IteratorStream = require('level-iterator-stream')
|
|
|
|
, errors = require('level-errors')
|
|
, WriteError = errors.WriteError
|
|
, ReadError = errors.ReadError
|
|
, NotFoundError = errors.NotFoundError
|
|
, OpenError = errors.OpenError
|
|
, EncodingError = errors.EncodingError
|
|
, InitializationError = errors.InitializationError
|
|
|
|
, util = require('./util')
|
|
, Batch = require('./batch')
|
|
, Codec = require('level-codec')
|
|
|
|
, getOptions = util.getOptions
|
|
, defaultOptions = util.defaultOptions
|
|
, getLevelDOWN = util.getLevelDOWN
|
|
, dispatchError = util.dispatchError
|
|
, isDefined = util.isDefined
|
|
|
|
function getCallback (options, callback) {
|
|
return typeof options == 'function' ? options : callback
|
|
}
|
|
|
|
// Possible LevelUP#_status values:
|
|
// - 'new' - newly created, not opened or closed
|
|
// - 'opening' - waiting for the database to be opened, post open()
|
|
// - 'open' - successfully opened the database, available for use
|
|
// - 'closing' - waiting for the database to be closed, post close()
|
|
// - 'closed' - database has been successfully closed, should not be
|
|
// used except for another open() operation
|
|
|
|
function LevelUP (location, options, callback) {
|
|
if (!(this instanceof LevelUP))
|
|
return new LevelUP(location, options, callback)
|
|
|
|
var error
|
|
|
|
EventEmitter.call(this)
|
|
this.setMaxListeners(Infinity)
|
|
|
|
if (typeof location == 'function') {
|
|
options = typeof options == 'object' ? options : {}
|
|
options.db = location
|
|
location = null
|
|
} else if (typeof location == 'object' && typeof location.db == 'function') {
|
|
options = location
|
|
location = null
|
|
}
|
|
|
|
|
|
if (typeof options == 'function') {
|
|
callback = options
|
|
options = {}
|
|
}
|
|
|
|
if ((!options || typeof options.db != 'function') && typeof location != 'string') {
|
|
error = new InitializationError(
|
|
'Must provide a location for the database')
|
|
if (callback) {
|
|
return process.nextTick(function () {
|
|
callback(error)
|
|
})
|
|
}
|
|
throw error
|
|
}
|
|
|
|
options = getOptions(options)
|
|
this.options = extend(defaultOptions, options)
|
|
this._codec = new Codec(this.options)
|
|
this._status = 'new'
|
|
// set this.location as enumerable but not configurable or writable
|
|
prr(this, 'location', location, 'e')
|
|
|
|
this.open(callback)
|
|
}
|
|
|
|
inherits(LevelUP, EventEmitter)
|
|
|
|
LevelUP.prototype.open = function (callback) {
|
|
var self = this
|
|
, dbFactory
|
|
, db
|
|
|
|
if (this.isOpen()) {
|
|
if (callback)
|
|
process.nextTick(function () { callback(null, self) })
|
|
return this
|
|
}
|
|
|
|
if (this._isOpening()) {
|
|
return callback && this.once(
|
|
'open'
|
|
, function () { callback(null, self) }
|
|
)
|
|
}
|
|
|
|
this.emit('opening')
|
|
|
|
this._status = 'opening'
|
|
this.db = new DeferredLevelDOWN(this.location)
|
|
dbFactory = this.options.db || getLevelDOWN()
|
|
db = dbFactory(this.location)
|
|
|
|
db.open(this.options, function (err) {
|
|
if (err) {
|
|
return dispatchError(self, new OpenError(err), callback)
|
|
} else {
|
|
self.db.setDb(db)
|
|
self.db = db
|
|
self._status = 'open'
|
|
if (callback)
|
|
callback(null, self)
|
|
self.emit('open')
|
|
self.emit('ready')
|
|
}
|
|
})
|
|
}
|
|
|
|
LevelUP.prototype.close = function (callback) {
|
|
var self = this
|
|
|
|
if (this.isOpen()) {
|
|
this._status = 'closing'
|
|
this.db.close(function () {
|
|
self._status = 'closed'
|
|
self.emit('closed')
|
|
if (callback)
|
|
callback.apply(null, arguments)
|
|
})
|
|
this.emit('closing')
|
|
this.db = new DeferredLevelDOWN(this.location)
|
|
} else if (this._status == 'closed' && callback) {
|
|
return process.nextTick(callback)
|
|
} else if (this._status == 'closing' && callback) {
|
|
this.once('closed', callback)
|
|
} else if (this._isOpening()) {
|
|
this.once('open', function () {
|
|
self.close(callback)
|
|
})
|
|
}
|
|
}
|
|
|
|
LevelUP.prototype.isOpen = function () {
|
|
return this._status == 'open'
|
|
}
|
|
|
|
LevelUP.prototype._isOpening = function () {
|
|
return this._status == 'opening'
|
|
}
|
|
|
|
LevelUP.prototype.isClosed = function () {
|
|
return (/^clos/).test(this._status)
|
|
}
|
|
|
|
function maybeError(db, options, callback) {
|
|
if (!db._isOpening() && !db.isOpen()) {
|
|
dispatchError(
|
|
db
|
|
, new ReadError('Database is not open')
|
|
, callback
|
|
)
|
|
return true
|
|
}
|
|
}
|
|
|
|
function writeError (db, message, callback) {
|
|
dispatchError(
|
|
db
|
|
, new WriteError(message)
|
|
, callback
|
|
)
|
|
}
|
|
|
|
function readError (db, message, callback) {
|
|
dispatchError(
|
|
db
|
|
, new ReadError(message)
|
|
, callback
|
|
)
|
|
}
|
|
|
|
|
|
LevelUP.prototype.get = function (key_, options, callback) {
|
|
var self = this
|
|
, key
|
|
|
|
callback = getCallback(options, callback)
|
|
|
|
if (maybeError(this, options, callback))
|
|
return
|
|
|
|
if (key_ === null || key_ === undefined || 'function' !== typeof callback)
|
|
return readError(this
|
|
, 'get() requires key and callback arguments', callback)
|
|
|
|
options = util.getOptions(options)
|
|
key = this._codec.encodeKey(key_, options)
|
|
|
|
options.asBuffer = this._codec.valueAsBuffer(options)
|
|
|
|
this.db.get(key, options, function (err, value) {
|
|
if (err) {
|
|
if ((/notfound/i).test(err) || err.notFound) {
|
|
err = new NotFoundError(
|
|
'Key not found in database [' + key_ + ']', err)
|
|
} else {
|
|
err = new ReadError(err)
|
|
}
|
|
return dispatchError(self, err, callback)
|
|
}
|
|
if (callback) {
|
|
try {
|
|
value = self._codec.decodeValue(value, options)
|
|
} catch (e) {
|
|
return callback(new EncodingError(e))
|
|
}
|
|
callback(null, value)
|
|
}
|
|
})
|
|
}
|
|
|
|
LevelUP.prototype.put = function (key_, value_, options, callback) {
|
|
var self = this
|
|
, key
|
|
, value
|
|
|
|
callback = getCallback(options, callback)
|
|
|
|
if (key_ === null || key_ === undefined)
|
|
return writeError(this, 'put() requires a key argument', callback)
|
|
|
|
if (maybeError(this, options, callback))
|
|
return
|
|
|
|
options = getOptions(options)
|
|
key = this._codec.encodeKey(key_, options)
|
|
value = this._codec.encodeValue(value_, options)
|
|
|
|
this.db.put(key, value, options, function (err) {
|
|
if (err) {
|
|
return dispatchError(self, new WriteError(err), callback)
|
|
} else {
|
|
self.emit('put', key_, value_)
|
|
if (callback)
|
|
callback()
|
|
}
|
|
})
|
|
}
|
|
|
|
LevelUP.prototype.del = function (key_, options, callback) {
|
|
var self = this
|
|
, key
|
|
|
|
callback = getCallback(options, callback)
|
|
|
|
if (key_ === null || key_ === undefined)
|
|
return writeError(this, 'del() requires a key argument', callback)
|
|
|
|
if (maybeError(this, options, callback))
|
|
return
|
|
|
|
options = getOptions(options)
|
|
key = this._codec.encodeKey(key_, options)
|
|
|
|
this.db.del(key, options, function (err) {
|
|
if (err) {
|
|
return dispatchError(self, new WriteError(err), callback)
|
|
} else {
|
|
self.emit('del', key_)
|
|
if (callback)
|
|
callback()
|
|
}
|
|
})
|
|
}
|
|
|
|
LevelUP.prototype.batch = function (arr_, options, callback) {
|
|
var self = this
|
|
, keyEnc
|
|
, valueEnc
|
|
, arr
|
|
|
|
if (!arguments.length)
|
|
return new Batch(this, this._codec)
|
|
|
|
callback = getCallback(options, callback)
|
|
|
|
if (!Array.isArray(arr_))
|
|
return writeError(this, 'batch() requires an array argument', callback)
|
|
|
|
if (maybeError(this, options, callback))
|
|
return
|
|
|
|
options = getOptions(options)
|
|
arr = self._codec.encodeBatch(arr_, options)
|
|
arr = arr.map(function (op) {
|
|
if (!op.type && op.key !== undefined && op.value !== undefined)
|
|
op.type = 'put'
|
|
return op
|
|
})
|
|
|
|
this.db.batch(arr, options, function (err) {
|
|
if (err) {
|
|
return dispatchError(self, new WriteError(err), callback)
|
|
} else {
|
|
self.emit('batch', arr_)
|
|
if (callback)
|
|
callback()
|
|
}
|
|
})
|
|
}
|
|
|
|
LevelUP.prototype.approximateSize = deprecate(function (start_, end_, options, callback) {
|
|
var self = this
|
|
, start
|
|
, end
|
|
|
|
callback = getCallback(options, callback)
|
|
|
|
options = getOptions(options)
|
|
|
|
if (start_ === null || start_ === undefined
|
|
|| end_ === null || end_ === undefined || 'function' !== typeof callback)
|
|
return readError(this, 'approximateSize() requires start, end and callback arguments', callback)
|
|
|
|
start = this._codec.encodeKey(start_, options)
|
|
end = this._codec.encodeKey(end_, options)
|
|
|
|
this.db.approximateSize(start, end, function (err, size) {
|
|
if (err) {
|
|
return dispatchError(self, new OpenError(err), callback)
|
|
} else if (callback) {
|
|
callback(null, size)
|
|
}
|
|
})
|
|
}, 'db.approximateSize() is deprecated. Use db.db.approximateSize() instead')
|
|
|
|
LevelUP.prototype.readStream =
|
|
LevelUP.prototype.createReadStream = function (options) {
|
|
options = extend( {keys: true, values: true}, this.options, options)
|
|
|
|
options.keyEncoding = options.keyEncoding
|
|
options.valueEncoding = options.valueEncoding
|
|
|
|
options = this._codec.encodeLtgt(options);
|
|
options.keyAsBuffer = this._codec.keyAsBuffer(options)
|
|
options.valueAsBuffer = this._codec.valueAsBuffer(options)
|
|
|
|
if ('number' !== typeof options.limit)
|
|
options.limit = -1
|
|
|
|
return new IteratorStream(this.db.iterator(options), extend(options, {
|
|
decoder: this._codec.createStreamDecoder(options)
|
|
}))
|
|
}
|
|
|
|
LevelUP.prototype.keyStream =
|
|
LevelUP.prototype.createKeyStream = function (options) {
|
|
return this.createReadStream(extend(options, { keys: true, values: false }))
|
|
}
|
|
|
|
LevelUP.prototype.valueStream =
|
|
LevelUP.prototype.createValueStream = function (options) {
|
|
return this.createReadStream(extend(options, { keys: false, values: true }))
|
|
}
|
|
|
|
LevelUP.prototype.toString = function () {
|
|
return 'LevelUP'
|
|
}
|
|
|
|
function utilStatic (name) {
|
|
return function (location, callback) {
|
|
getLevelDOWN()[name](location, callback || function () {})
|
|
}
|
|
}
|
|
|
|
module.exports = LevelUP
|
|
module.exports.errors = require('level-errors')
|
|
module.exports.destroy = deprecate(
|
|
utilStatic('destroy')
|
|
, 'levelup.destroy() is deprecated. Use leveldown.destroy() instead'
|
|
)
|
|
module.exports.repair = deprecate(
|
|
utilStatic('repair')
|
|
, 'levelup.repair() is deprecated. Use leveldown.repair() instead'
|
|
)
|
|
|
|
|
|
}).call(this,require('_process'))
|
|
},{"./batch":102,"./util":104,"_process":133,"deferred-leveldown":46,"events":56,"level-codec":91,"level-errors":93,"level-iterator-stream":94,"prr":105,"util":297,"xtend":299}],104:[function(require,module,exports){
|
|
/* Copyright (c) 2012-2016 LevelUP contributors
|
|
* See list at <https://github.com/level/levelup#contributing>
|
|
* MIT License
|
|
* <https://github.com/level/levelup/blob/master/LICENSE.md>
|
|
*/
|
|
|
|
var extend = require('xtend')
|
|
, LevelUPError = require('level-errors').LevelUPError
|
|
, format = require('util').format
|
|
, defaultOptions = {
|
|
createIfMissing : true
|
|
, errorIfExists : false
|
|
, keyEncoding : 'utf8'
|
|
, valueEncoding : 'utf8'
|
|
, compression : true
|
|
}
|
|
|
|
, leveldown
|
|
|
|
function getOptions (options) {
|
|
if (typeof options == 'string')
|
|
options = { valueEncoding: options }
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
return options
|
|
}
|
|
|
|
function getLevelDOWN () {
|
|
if (leveldown)
|
|
return leveldown
|
|
|
|
var requiredVersion = require('../package.json').devDependencies.leveldown
|
|
, leveldownVersion
|
|
|
|
try {
|
|
leveldownVersion = require('leveldown/package').version
|
|
} catch (e) {
|
|
throw requireError(e)
|
|
}
|
|
|
|
if (!require('semver').satisfies(leveldownVersion, requiredVersion)) {
|
|
throw new LevelUPError(
|
|
'Installed version of LevelDOWN ('
|
|
+ leveldownVersion
|
|
+ ') does not match required version ('
|
|
+ requiredVersion
|
|
+ ')'
|
|
)
|
|
}
|
|
|
|
try {
|
|
return leveldown = require('leveldown')
|
|
} catch (e) {
|
|
throw requireError(e)
|
|
}
|
|
}
|
|
|
|
function requireError (e) {
|
|
var template = 'Failed to require LevelDOWN (%s). Try `npm install leveldown` if it\'s missing'
|
|
return new LevelUPError(format(template, e.message))
|
|
}
|
|
|
|
function dispatchError (db, error, callback) {
|
|
typeof callback == 'function' ? callback(error) : db.emit('error', error)
|
|
}
|
|
|
|
function isDefined (v) {
|
|
return typeof v !== 'undefined'
|
|
}
|
|
|
|
module.exports = {
|
|
defaultOptions : defaultOptions
|
|
, getOptions : getOptions
|
|
, getLevelDOWN : getLevelDOWN
|
|
, dispatchError : dispatchError
|
|
, isDefined : isDefined
|
|
}
|
|
|
|
},{"../package.json":106,"level-errors":93,"leveldown":41,"leveldown/package":41,"semver":41,"util":297,"xtend":299}],105:[function(require,module,exports){
|
|
/*!
|
|
* prr
|
|
* (c) 2013 Rod Vagg <rod@vagg.org>
|
|
* https://github.com/rvagg/prr
|
|
* License: MIT
|
|
*/
|
|
|
|
(function (name, context, definition) {
|
|
if (typeof module != 'undefined' && module.exports)
|
|
module.exports = definition()
|
|
else
|
|
context[name] = definition()
|
|
})('prr', this, function() {
|
|
|
|
var setProperty = typeof Object.defineProperty == 'function'
|
|
? function (obj, key, options) {
|
|
Object.defineProperty(obj, key, options)
|
|
return obj
|
|
}
|
|
: function (obj, key, options) { // < es5
|
|
obj[key] = options.value
|
|
return obj
|
|
}
|
|
|
|
, makeOptions = function (value, options) {
|
|
var oo = typeof options == 'object'
|
|
, os = !oo && typeof options == 'string'
|
|
, op = function (p) {
|
|
return oo
|
|
? !!options[p]
|
|
: os
|
|
? options.indexOf(p[0]) > -1
|
|
: false
|
|
}
|
|
|
|
return {
|
|
enumerable : op('enumerable')
|
|
, configurable : op('configurable')
|
|
, writable : op('writable')
|
|
, value : value
|
|
}
|
|
}
|
|
|
|
, prr = function (obj, key, value, options) {
|
|
var k
|
|
|
|
options = makeOptions(value, options)
|
|
|
|
if (typeof key == 'object') {
|
|
for (k in key) {
|
|
if (Object.hasOwnProperty.call(key, k)) {
|
|
options.value = key[k]
|
|
setProperty(obj, k, options)
|
|
}
|
|
}
|
|
return obj
|
|
}
|
|
|
|
return setProperty(obj, key, options)
|
|
}
|
|
|
|
return prr
|
|
})
|
|
},{}],106:[function(require,module,exports){
|
|
module.exports={
|
|
"_args": [
|
|
[
|
|
"levelup@1.3.2",
|
|
"/home/travis/build/Microsoft/pxt/node_modules/pouchdb"
|
|
]
|
|
],
|
|
"_from": "levelup@1.3.2",
|
|
"_id": "levelup@1.3.2",
|
|
"_inCache": true,
|
|
"_installable": true,
|
|
"_location": "/levelup",
|
|
"_nodeVersion": "6.1.0",
|
|
"_npmOperationalInternal": {
|
|
"host": "packages-16-east.internal.npmjs.com",
|
|
"tmp": "tmp/levelup-1.3.2.tgz_1463496525467_0.4644940535072237"
|
|
},
|
|
"_npmUser": {
|
|
"email": "ralphtheninja@riseup.net",
|
|
"name": "ralphtheninja"
|
|
},
|
|
"_npmVersion": "3.8.6",
|
|
"_phantomChildren": {},
|
|
"_requested": {
|
|
"name": "levelup",
|
|
"raw": "levelup@1.3.2",
|
|
"rawSpec": "1.3.2",
|
|
"scope": null,
|
|
"spec": "1.3.2",
|
|
"type": "version"
|
|
},
|
|
"_requiredBy": [
|
|
"/pouchdb"
|
|
],
|
|
"_resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.2.tgz",
|
|
"_shasum": "b321d3071f0e75c2dfaf2f0fe8864e5b9a387bc9",
|
|
"_shrinkwrap": null,
|
|
"_spec": "levelup@1.3.2",
|
|
"_where": "/home/travis/build/Microsoft/pxt/node_modules/pouchdb",
|
|
"browser": {
|
|
"leveldown": false,
|
|
"leveldown/package": false,
|
|
"semver": false
|
|
},
|
|
"bugs": {
|
|
"url": "https://github.com/level/levelup/issues"
|
|
},
|
|
"contributors": [
|
|
{
|
|
"name": "Julian Gruber",
|
|
"email": "julian@juliangruber.com",
|
|
"url": "https://github.com/juliangruber"
|
|
},
|
|
{
|
|
"name": "Rod Vagg",
|
|
"email": "r@va.gg",
|
|
"url": "https://github.com/rvagg"
|
|
},
|
|
{
|
|
"name": "Jake Verbaten",
|
|
"email": "raynos2@gmail.com",
|
|
"url": "https://github.com/raynos"
|
|
},
|
|
{
|
|
"name": "Dominic Tarr",
|
|
"email": "dominic.tarr@gmail.com",
|
|
"url": "https://github.com/dominictarr"
|
|
},
|
|
{
|
|
"name": "Max Ogden",
|
|
"email": "max@maxogden.com",
|
|
"url": "https://github.com/maxogden"
|
|
},
|
|
{
|
|
"name": "Lars-Magnus Skog",
|
|
"email": "ralphtheninja@riseup.net",
|
|
"url": "https://github.com/ralphtheninja"
|
|
},
|
|
{
|
|
"name": "David Björklund",
|
|
"email": "david.bjorklund@gmail.com",
|
|
"url": "https://github.com/kesla"
|
|
},
|
|
{
|
|
"name": "John Chesley",
|
|
"email": "john@chesl.es",
|
|
"url": "https://github.com/chesles/"
|
|
},
|
|
{
|
|
"name": "Paolo Fragomeni",
|
|
"email": "paolo@async.ly",
|
|
"url": "https://github.com/0x00a"
|
|
},
|
|
{
|
|
"name": "Anton Whalley",
|
|
"email": "anton.whalley@nearform.com",
|
|
"url": "https://github.com/No9"
|
|
},
|
|
{
|
|
"name": "Matteo Collina",
|
|
"email": "matteo.collina@gmail.com",
|
|
"url": "https://github.com/mcollina"
|
|
},
|
|
{
|
|
"name": "Pedro Teixeira",
|
|
"email": "pedro.teixeira@gmail.com",
|
|
"url": "https://github.com/pgte"
|
|
},
|
|
{
|
|
"name": "James Halliday",
|
|
"email": "mail@substack.net",
|
|
"url": "https://github.com/substack"
|
|
},
|
|
{
|
|
"name": "Jarrett Cruger",
|
|
"email": "jcrugzz@gmail.com",
|
|
"url": "https://github.com/jcrugzz"
|
|
}
|
|
],
|
|
"dependencies": {
|
|
"deferred-leveldown": "~1.2.1",
|
|
"level-codec": "~6.1.0",
|
|
"level-errors": "~1.0.3",
|
|
"level-iterator-stream": "~1.3.0",
|
|
"prr": "~1.0.1",
|
|
"semver": "~5.1.0",
|
|
"xtend": "~4.0.0"
|
|
},
|
|
"description": "Fast & simple storage - a Node.js-style LevelDB wrapper",
|
|
"devDependencies": {
|
|
"async": "~1.5.0",
|
|
"bustermove": "~1.0.0",
|
|
"delayed": "~1.0.1",
|
|
"faucet": "~0.0.1",
|
|
"leveldown": "^1.1.0",
|
|
"memdown": "~1.1.0",
|
|
"msgpack-js": "~0.3.0",
|
|
"referee": "~1.2.0",
|
|
"rimraf": "~2.4.3",
|
|
"slow-stream": "0.0.4",
|
|
"tap": "~2.3.1",
|
|
"tape": "~4.2.1"
|
|
},
|
|
"directories": {},
|
|
"dist": {
|
|
"shasum": "b321d3071f0e75c2dfaf2f0fe8864e5b9a387bc9",
|
|
"tarball": "https://registry.npmjs.org/levelup/-/levelup-1.3.2.tgz"
|
|
},
|
|
"gitHead": "bcc242cfc4ec035f9228a5cd54903cb126659a00",
|
|
"homepage": "https://github.com/level/levelup",
|
|
"keywords": [
|
|
"database",
|
|
"db",
|
|
"json",
|
|
"leveldb",
|
|
"storage",
|
|
"store",
|
|
"stream"
|
|
],
|
|
"license": "MIT",
|
|
"main": "lib/levelup.js",
|
|
"maintainers": [
|
|
{
|
|
"name": "rvagg",
|
|
"email": "rod@vagg.org"
|
|
},
|
|
{
|
|
"name": "ralphtheninja",
|
|
"email": "ralphtheninja@riseup.net"
|
|
},
|
|
{
|
|
"name": "juliangruber",
|
|
"email": "julian@juliangruber.com"
|
|
}
|
|
],
|
|
"name": "levelup",
|
|
"optionalDependencies": {},
|
|
"readme": "ERROR: No README data found!",
|
|
"repository": {
|
|
"type": "git",
|
|
"url": "git+https://github.com/level/levelup.git"
|
|
},
|
|
"scripts": {
|
|
"test": "tape test/*-test.js | faucet"
|
|
},
|
|
"version": "1.3.2"
|
|
}
|
|
|
|
},{}],107:[function(require,module,exports){
|
|
(function (Buffer){
|
|
|
|
exports.compare = function (a, b) {
|
|
|
|
if(Buffer.isBuffer(a)) {
|
|
var l = Math.min(a.length, b.length)
|
|
for(var i = 0; i < l; i++) {
|
|
var cmp = a[i] - b[i]
|
|
if(cmp) return cmp
|
|
}
|
|
return a.length - b.length
|
|
}
|
|
|
|
return a < b ? -1 : a > b ? 1 : 0
|
|
}
|
|
|
|
function has(obj, key) {
|
|
return Object.hasOwnProperty.call(obj, key)
|
|
}
|
|
|
|
// to be compatible with the current abstract-leveldown tests
|
|
// nullish or empty strings.
|
|
// I could use !!val but I want to permit numbers and booleans,
|
|
// if possible.
|
|
|
|
function isDef (val) {
|
|
return val != null && val !== ''
|
|
}
|
|
|
|
var lowerBound = exports.lowerBound = function (range) {
|
|
return (
|
|
isDef(range.gt) ? range.gt
|
|
: isDef(range.gte) ? range.gte
|
|
: isDef(range.min) ? range.min
|
|
: isDef(range.start) && !range.reverse ? range.start
|
|
: isDef(range.end) && range.reverse ? range.end
|
|
: undefined
|
|
)
|
|
}
|
|
|
|
exports.lowerBoundInclusive = function (range) {
|
|
return isDef(range.gt) ? false : true
|
|
}
|
|
|
|
exports.upperBoundInclusive =
|
|
function (range) {
|
|
return isDef(range.lt) ? false : true
|
|
}
|
|
|
|
var lowerBoundExclusive = exports.lowerBoundExclusive =
|
|
function (range) {
|
|
return isDef(range.gt) ? true : false
|
|
}
|
|
|
|
var upperBoundExclusive = exports.upperBoundExclusive =
|
|
function (range) {
|
|
return isDef(range.lt) ? true : false
|
|
}
|
|
|
|
var upperBound = exports.upperBound = function (range) {
|
|
return (
|
|
isDef(range.lt) ? range.lt
|
|
: isDef(range.lte) ? range.lte
|
|
: isDef(range.max) ? range.max
|
|
: isDef(range.start) && range.reverse ? range.start
|
|
: isDef(range.end) && !range.reverse ? range.end
|
|
: undefined
|
|
)
|
|
}
|
|
|
|
|
|
exports.contains = function (range, key, compare) {
|
|
compare = compare || exports.compare
|
|
|
|
var lb = lowerBound(range)
|
|
if(isDef(lb)) {
|
|
var cmp = compare(key, lb)
|
|
if(cmp < 0 || (cmp === 0 && lowerBoundExclusive(range)))
|
|
return false
|
|
}
|
|
|
|
var ub = upperBound(range)
|
|
if(isDef(ub)) {
|
|
var cmp = compare(key, ub)
|
|
if(cmp > 0 || (cmp === 0) && upperBoundExclusive(range))
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
exports.filter = function (range, compare) {
|
|
return function (key) {
|
|
return exports.contains(range, key, compare)
|
|
}
|
|
}
|
|
|
|
}).call(this,{"isBuffer":require("../is-buffer/index.js")})
|
|
},{"../is-buffer/index.js":88}],108:[function(require,module,exports){
|
|
(function (global){
|
|
/**
|
|
* marked - a markdown parser
|
|
* Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
|
|
* https://github.com/chjj/marked
|
|
*/
|
|
|
|
;(function() {
|
|
|
|
/**
|
|
* Block-Level Grammar
|
|
*/
|
|
|
|
var block = {
|
|
newline: /^\n+/,
|
|
code: /^( {4}[^\n]+\n*)+/,
|
|
fences: noop,
|
|
hr: /^( *[-*_]){3,} *(?:\n+|$)/,
|
|
heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
|
|
nptable: noop,
|
|
lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
|
|
blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
|
|
list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
|
|
html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
|
|
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
|
|
table: noop,
|
|
paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
|
|
text: /^[^\n]+/
|
|
};
|
|
|
|
block.bullet = /(?:[*+-]|\d+\.)/;
|
|
block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
|
|
block.item = replace(block.item, 'gm')
|
|
(/bull/g, block.bullet)
|
|
();
|
|
|
|
block.list = replace(block.list)
|
|
(/bull/g, block.bullet)
|
|
('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
|
|
('def', '\\n+(?=' + block.def.source + ')')
|
|
();
|
|
|
|
block.blockquote = replace(block.blockquote)
|
|
('def', block.def)
|
|
();
|
|
|
|
block._tag = '(?!(?:'
|
|
+ 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
|
|
+ '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
|
|
+ '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
|
|
|
|
block.html = replace(block.html)
|
|
('comment', /<!--[\s\S]*?-->/)
|
|
('closed', /<(tag)[\s\S]+?<\/\1>/)
|
|
('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
|
|
(/tag/g, block._tag)
|
|
();
|
|
|
|
block.paragraph = replace(block.paragraph)
|
|
('hr', block.hr)
|
|
('heading', block.heading)
|
|
('lheading', block.lheading)
|
|
('blockquote', block.blockquote)
|
|
('tag', '<' + block._tag)
|
|
('def', block.def)
|
|
();
|
|
|
|
/**
|
|
* Normal Block Grammar
|
|
*/
|
|
|
|
block.normal = merge({}, block);
|
|
|
|
/**
|
|
* GFM Block Grammar
|
|
*/
|
|
|
|
block.gfm = merge({}, block.normal, {
|
|
fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
|
|
paragraph: /^/,
|
|
heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
|
|
});
|
|
|
|
block.gfm.paragraph = replace(block.paragraph)
|
|
('(?!', '(?!'
|
|
+ block.gfm.fences.source.replace('\\1', '\\2') + '|'
|
|
+ block.list.source.replace('\\1', '\\3') + '|')
|
|
();
|
|
|
|
/**
|
|
* GFM + Tables Block Grammar
|
|
*/
|
|
|
|
block.tables = merge({}, block.gfm, {
|
|
nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
|
|
table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
|
|
});
|
|
|
|
/**
|
|
* Block Lexer
|
|
*/
|
|
|
|
function Lexer(options) {
|
|
this.tokens = [];
|
|
this.tokens.links = {};
|
|
this.options = options || marked.defaults;
|
|
this.rules = block.normal;
|
|
|
|
if (this.options.gfm) {
|
|
if (this.options.tables) {
|
|
this.rules = block.tables;
|
|
} else {
|
|
this.rules = block.gfm;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Expose Block Rules
|
|
*/
|
|
|
|
Lexer.rules = block;
|
|
|
|
/**
|
|
* Static Lex Method
|
|
*/
|
|
|
|
Lexer.lex = function(src, options) {
|
|
var lexer = new Lexer(options);
|
|
return lexer.lex(src);
|
|
};
|
|
|
|
/**
|
|
* Preprocessing
|
|
*/
|
|
|
|
Lexer.prototype.lex = function(src) {
|
|
src = src
|
|
.replace(/\r\n|\r/g, '\n')
|
|
.replace(/\t/g, ' ')
|
|
.replace(/\u00a0/g, ' ')
|
|
.replace(/\u2424/g, '\n');
|
|
|
|
return this.token(src, true);
|
|
};
|
|
|
|
/**
|
|
* Lexing
|
|
*/
|
|
|
|
Lexer.prototype.token = function(src, top, bq) {
|
|
var src = src.replace(/^ +$/gm, '')
|
|
, next
|
|
, loose
|
|
, cap
|
|
, bull
|
|
, b
|
|
, item
|
|
, space
|
|
, i
|
|
, l;
|
|
|
|
while (src) {
|
|
// newline
|
|
if (cap = this.rules.newline.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
if (cap[0].length > 1) {
|
|
this.tokens.push({
|
|
type: 'space'
|
|
});
|
|
}
|
|
}
|
|
|
|
// code
|
|
if (cap = this.rules.code.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
cap = cap[0].replace(/^ {4}/gm, '');
|
|
this.tokens.push({
|
|
type: 'code',
|
|
text: !this.options.pedantic
|
|
? cap.replace(/\n+$/, '')
|
|
: cap
|
|
});
|
|
continue;
|
|
}
|
|
|
|
// fences (gfm)
|
|
if (cap = this.rules.fences.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
this.tokens.push({
|
|
type: 'code',
|
|
lang: cap[2],
|
|
text: cap[3] || ''
|
|
});
|
|
continue;
|
|
}
|
|
|
|
// heading
|
|
if (cap = this.rules.heading.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
this.tokens.push({
|
|
type: 'heading',
|
|
depth: cap[1].length,
|
|
text: cap[2]
|
|
});
|
|
continue;
|
|
}
|
|
|
|
// table no leading pipe (gfm)
|
|
if (top && (cap = this.rules.nptable.exec(src))) {
|
|
src = src.substring(cap[0].length);
|
|
|
|
item = {
|
|
type: 'table',
|
|
header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
|
|
align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
|
|
cells: cap[3].replace(/\n$/, '').split('\n')
|
|
};
|
|
|
|
for (i = 0; i < item.align.length; i++) {
|
|
if (/^ *-+: *$/.test(item.align[i])) {
|
|
item.align[i] = 'right';
|
|
} else if (/^ *:-+: *$/.test(item.align[i])) {
|
|
item.align[i] = 'center';
|
|
} else if (/^ *:-+ *$/.test(item.align[i])) {
|
|
item.align[i] = 'left';
|
|
} else {
|
|
item.align[i] = null;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < item.cells.length; i++) {
|
|
item.cells[i] = item.cells[i].split(/ *\| */);
|
|
}
|
|
|
|
this.tokens.push(item);
|
|
|
|
continue;
|
|
}
|
|
|
|
// lheading
|
|
if (cap = this.rules.lheading.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
this.tokens.push({
|
|
type: 'heading',
|
|
depth: cap[2] === '=' ? 1 : 2,
|
|
text: cap[1]
|
|
});
|
|
continue;
|
|
}
|
|
|
|
// hr
|
|
if (cap = this.rules.hr.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
this.tokens.push({
|
|
type: 'hr'
|
|
});
|
|
continue;
|
|
}
|
|
|
|
// blockquote
|
|
if (cap = this.rules.blockquote.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
|
|
this.tokens.push({
|
|
type: 'blockquote_start'
|
|
});
|
|
|
|
cap = cap[0].replace(/^ *> ?/gm, '');
|
|
|
|
// Pass `top` to keep the current
|
|
// "toplevel" state. This is exactly
|
|
// how markdown.pl works.
|
|
this.token(cap, top, true);
|
|
|
|
this.tokens.push({
|
|
type: 'blockquote_end'
|
|
});
|
|
|
|
continue;
|
|
}
|
|
|
|
// list
|
|
if (cap = this.rules.list.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
bull = cap[2];
|
|
|
|
this.tokens.push({
|
|
type: 'list_start',
|
|
ordered: bull.length > 1
|
|
});
|
|
|
|
// Get each top-level item.
|
|
cap = cap[0].match(this.rules.item);
|
|
|
|
next = false;
|
|
l = cap.length;
|
|
i = 0;
|
|
|
|
for (; i < l; i++) {
|
|
item = cap[i];
|
|
|
|
// Remove the list item's bullet
|
|
// so it is seen as the next token.
|
|
space = item.length;
|
|
item = item.replace(/^ *([*+-]|\d+\.) +/, '');
|
|
|
|
// Outdent whatever the
|
|
// list item contains. Hacky.
|
|
if (~item.indexOf('\n ')) {
|
|
space -= item.length;
|
|
item = !this.options.pedantic
|
|
? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
|
|
: item.replace(/^ {1,4}/gm, '');
|
|
}
|
|
|
|
// Determine whether the next list item belongs here.
|
|
// Backpedal if it does not belong in this list.
|
|
if (this.options.smartLists && i !== l - 1) {
|
|
b = block.bullet.exec(cap[i + 1])[0];
|
|
if (bull !== b && !(bull.length > 1 && b.length > 1)) {
|
|
src = cap.slice(i + 1).join('\n') + src;
|
|
i = l - 1;
|
|
}
|
|
}
|
|
|
|
// Determine whether item is loose or not.
|
|
// Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
|
|
// for discount behavior.
|
|
loose = next || /\n\n(?!\s*$)/.test(item);
|
|
if (i !== l - 1) {
|
|
next = item.charAt(item.length - 1) === '\n';
|
|
if (!loose) loose = next;
|
|
}
|
|
|
|
this.tokens.push({
|
|
type: loose
|
|
? 'loose_item_start'
|
|
: 'list_item_start'
|
|
});
|
|
|
|
// Recurse.
|
|
this.token(item, false, bq);
|
|
|
|
this.tokens.push({
|
|
type: 'list_item_end'
|
|
});
|
|
}
|
|
|
|
this.tokens.push({
|
|
type: 'list_end'
|
|
});
|
|
|
|
continue;
|
|
}
|
|
|
|
// html
|
|
if (cap = this.rules.html.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
this.tokens.push({
|
|
type: this.options.sanitize
|
|
? 'paragraph'
|
|
: 'html',
|
|
pre: !this.options.sanitizer
|
|
&& (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
|
|
text: cap[0]
|
|
});
|
|
continue;
|
|
}
|
|
|
|
// def
|
|
if ((!bq && top) && (cap = this.rules.def.exec(src))) {
|
|
src = src.substring(cap[0].length);
|
|
this.tokens.links[cap[1].toLowerCase()] = {
|
|
href: cap[2],
|
|
title: cap[3]
|
|
};
|
|
continue;
|
|
}
|
|
|
|
// table (gfm)
|
|
if (top && (cap = this.rules.table.exec(src))) {
|
|
src = src.substring(cap[0].length);
|
|
|
|
item = {
|
|
type: 'table',
|
|
header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
|
|
align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
|
|
cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
|
|
};
|
|
|
|
for (i = 0; i < item.align.length; i++) {
|
|
if (/^ *-+: *$/.test(item.align[i])) {
|
|
item.align[i] = 'right';
|
|
} else if (/^ *:-+: *$/.test(item.align[i])) {
|
|
item.align[i] = 'center';
|
|
} else if (/^ *:-+ *$/.test(item.align[i])) {
|
|
item.align[i] = 'left';
|
|
} else {
|
|
item.align[i] = null;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < item.cells.length; i++) {
|
|
item.cells[i] = item.cells[i]
|
|
.replace(/^ *\| *| *\| *$/g, '')
|
|
.split(/ *\| */);
|
|
}
|
|
|
|
this.tokens.push(item);
|
|
|
|
continue;
|
|
}
|
|
|
|
// top-level paragraph
|
|
if (top && (cap = this.rules.paragraph.exec(src))) {
|
|
src = src.substring(cap[0].length);
|
|
this.tokens.push({
|
|
type: 'paragraph',
|
|
text: cap[1].charAt(cap[1].length - 1) === '\n'
|
|
? cap[1].slice(0, -1)
|
|
: cap[1]
|
|
});
|
|
continue;
|
|
}
|
|
|
|
// text
|
|
if (cap = this.rules.text.exec(src)) {
|
|
// Top-level should never reach here.
|
|
src = src.substring(cap[0].length);
|
|
this.tokens.push({
|
|
type: 'text',
|
|
text: cap[0]
|
|
});
|
|
continue;
|
|
}
|
|
|
|
if (src) {
|
|
throw new
|
|
Error('Infinite loop on byte: ' + src.charCodeAt(0));
|
|
}
|
|
}
|
|
|
|
return this.tokens;
|
|
};
|
|
|
|
/**
|
|
* Inline-Level Grammar
|
|
*/
|
|
|
|
var inline = {
|
|
escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
|
|
autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
|
|
url: noop,
|
|
tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
|
|
link: /^!?\[(inside)\]\(href\)/,
|
|
reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
|
|
nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
|
|
strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
|
|
em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
|
|
code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
|
|
br: /^ {2,}\n(?!\s*$)/,
|
|
del: noop,
|
|
text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
|
|
};
|
|
|
|
inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
|
|
inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
|
|
|
|
inline.link = replace(inline.link)
|
|
('inside', inline._inside)
|
|
('href', inline._href)
|
|
();
|
|
|
|
inline.reflink = replace(inline.reflink)
|
|
('inside', inline._inside)
|
|
();
|
|
|
|
/**
|
|
* Normal Inline Grammar
|
|
*/
|
|
|
|
inline.normal = merge({}, inline);
|
|
|
|
/**
|
|
* Pedantic Inline Grammar
|
|
*/
|
|
|
|
inline.pedantic = merge({}, inline.normal, {
|
|
strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
|
|
em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
|
|
});
|
|
|
|
/**
|
|
* GFM Inline Grammar
|
|
*/
|
|
|
|
inline.gfm = merge({}, inline.normal, {
|
|
escape: replace(inline.escape)('])', '~|])')(),
|
|
url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
|
|
del: /^~~(?=\S)([\s\S]*?\S)~~/,
|
|
text: replace(inline.text)
|
|
(']|', '~]|')
|
|
('|', '|https?://|')
|
|
()
|
|
});
|
|
|
|
/**
|
|
* GFM + Line Breaks Inline Grammar
|
|
*/
|
|
|
|
inline.breaks = merge({}, inline.gfm, {
|
|
br: replace(inline.br)('{2,}', '*')(),
|
|
text: replace(inline.gfm.text)('{2,}', '*')()
|
|
});
|
|
|
|
/**
|
|
* Inline Lexer & Compiler
|
|
*/
|
|
|
|
function InlineLexer(links, options) {
|
|
this.options = options || marked.defaults;
|
|
this.links = links;
|
|
this.rules = inline.normal;
|
|
this.renderer = this.options.renderer || new Renderer;
|
|
this.renderer.options = this.options;
|
|
|
|
if (!this.links) {
|
|
throw new
|
|
Error('Tokens array requires a `links` property.');
|
|
}
|
|
|
|
if (this.options.gfm) {
|
|
if (this.options.breaks) {
|
|
this.rules = inline.breaks;
|
|
} else {
|
|
this.rules = inline.gfm;
|
|
}
|
|
} else if (this.options.pedantic) {
|
|
this.rules = inline.pedantic;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Expose Inline Rules
|
|
*/
|
|
|
|
InlineLexer.rules = inline;
|
|
|
|
/**
|
|
* Static Lexing/Compiling Method
|
|
*/
|
|
|
|
InlineLexer.output = function(src, links, options) {
|
|
var inline = new InlineLexer(links, options);
|
|
return inline.output(src);
|
|
};
|
|
|
|
/**
|
|
* Lexing/Compiling
|
|
*/
|
|
|
|
InlineLexer.prototype.output = function(src) {
|
|
var out = ''
|
|
, link
|
|
, text
|
|
, href
|
|
, cap;
|
|
|
|
while (src) {
|
|
// escape
|
|
if (cap = this.rules.escape.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
out += cap[1];
|
|
continue;
|
|
}
|
|
|
|
// autolink
|
|
if (cap = this.rules.autolink.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
if (cap[2] === '@') {
|
|
text = cap[1].charAt(6) === ':'
|
|
? this.mangle(cap[1].substring(7))
|
|
: this.mangle(cap[1]);
|
|
href = this.mangle('mailto:') + text;
|
|
} else {
|
|
text = escape(cap[1]);
|
|
href = text;
|
|
}
|
|
out += this.renderer.link(href, null, text);
|
|
continue;
|
|
}
|
|
|
|
// url (gfm)
|
|
if (!this.inLink && (cap = this.rules.url.exec(src))) {
|
|
src = src.substring(cap[0].length);
|
|
text = escape(cap[1]);
|
|
href = text;
|
|
out += this.renderer.link(href, null, text);
|
|
continue;
|
|
}
|
|
|
|
// tag
|
|
if (cap = this.rules.tag.exec(src)) {
|
|
if (!this.inLink && /^<a /i.test(cap[0])) {
|
|
this.inLink = true;
|
|
} else if (this.inLink && /^<\/a>/i.test(cap[0])) {
|
|
this.inLink = false;
|
|
}
|
|
src = src.substring(cap[0].length);
|
|
out += this.options.sanitize
|
|
? this.options.sanitizer
|
|
? this.options.sanitizer(cap[0])
|
|
: escape(cap[0])
|
|
: cap[0]
|
|
continue;
|
|
}
|
|
|
|
// link
|
|
if (cap = this.rules.link.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
this.inLink = true;
|
|
out += this.outputLink(cap, {
|
|
href: cap[2],
|
|
title: cap[3]
|
|
});
|
|
this.inLink = false;
|
|
continue;
|
|
}
|
|
|
|
// reflink, nolink
|
|
if ((cap = this.rules.reflink.exec(src))
|
|
|| (cap = this.rules.nolink.exec(src))) {
|
|
src = src.substring(cap[0].length);
|
|
link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
|
|
link = this.links[link.toLowerCase()];
|
|
if (!link || !link.href) {
|
|
out += cap[0].charAt(0);
|
|
src = cap[0].substring(1) + src;
|
|
continue;
|
|
}
|
|
this.inLink = true;
|
|
out += this.outputLink(cap, link);
|
|
this.inLink = false;
|
|
continue;
|
|
}
|
|
|
|
// strong
|
|
if (cap = this.rules.strong.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
out += this.renderer.strong(this.output(cap[2] || cap[1]));
|
|
continue;
|
|
}
|
|
|
|
// em
|
|
if (cap = this.rules.em.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
out += this.renderer.em(this.output(cap[2] || cap[1]));
|
|
continue;
|
|
}
|
|
|
|
// code
|
|
if (cap = this.rules.code.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
out += this.renderer.codespan(escape(cap[2], true));
|
|
continue;
|
|
}
|
|
|
|
// br
|
|
if (cap = this.rules.br.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
out += this.renderer.br();
|
|
continue;
|
|
}
|
|
|
|
// del (gfm)
|
|
if (cap = this.rules.del.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
out += this.renderer.del(this.output(cap[1]));
|
|
continue;
|
|
}
|
|
|
|
// text
|
|
if (cap = this.rules.text.exec(src)) {
|
|
src = src.substring(cap[0].length);
|
|
out += this.renderer.text(escape(this.smartypants(cap[0])));
|
|
continue;
|
|
}
|
|
|
|
if (src) {
|
|
throw new
|
|
Error('Infinite loop on byte: ' + src.charCodeAt(0));
|
|
}
|
|
}
|
|
|
|
return out;
|
|
};
|
|
|
|
/**
|
|
* Compile Link
|
|
*/
|
|
|
|
InlineLexer.prototype.outputLink = function(cap, link) {
|
|
var href = escape(link.href)
|
|
, title = link.title ? escape(link.title) : null;
|
|
|
|
return cap[0].charAt(0) !== '!'
|
|
? this.renderer.link(href, title, this.output(cap[1]))
|
|
: this.renderer.image(href, title, escape(cap[1]));
|
|
};
|
|
|
|
/**
|
|
* Smartypants Transformations
|
|
*/
|
|
|
|
InlineLexer.prototype.smartypants = function(text) {
|
|
if (!this.options.smartypants) return text;
|
|
return text
|
|
// em-dashes
|
|
.replace(/---/g, '\u2014')
|
|
// en-dashes
|
|
.replace(/--/g, '\u2013')
|
|
// opening singles
|
|
.replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
|
|
// closing singles & apostrophes
|
|
.replace(/'/g, '\u2019')
|
|
// opening doubles
|
|
.replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
|
|
// closing doubles
|
|
.replace(/"/g, '\u201d')
|
|
// ellipses
|
|
.replace(/\.{3}/g, '\u2026');
|
|
};
|
|
|
|
/**
|
|
* Mangle Links
|
|
*/
|
|
|
|
InlineLexer.prototype.mangle = function(text) {
|
|
if (!this.options.mangle) return text;
|
|
var out = ''
|
|
, l = text.length
|
|
, i = 0
|
|
, ch;
|
|
|
|
for (; i < l; i++) {
|
|
ch = text.charCodeAt(i);
|
|
if (Math.random() > 0.5) {
|
|
ch = 'x' + ch.toString(16);
|
|
}
|
|
out += '&#' + ch + ';';
|
|
}
|
|
|
|
return out;
|
|
};
|
|
|
|
/**
|
|
* Renderer
|
|
*/
|
|
|
|
function Renderer(options) {
|
|
this.options = options || {};
|
|
}
|
|
|
|
Renderer.prototype.code = function(code, lang, escaped) {
|
|
if (this.options.highlight) {
|
|
var out = this.options.highlight(code, lang);
|
|
if (out != null && out !== code) {
|
|
escaped = true;
|
|
code = out;
|
|
}
|
|
}
|
|
|
|
if (!lang) {
|
|
return '<pre><code>'
|
|
+ (escaped ? code : escape(code, true))
|
|
+ '\n</code></pre>';
|
|
}
|
|
|
|
return '<pre><code class="'
|
|
+ this.options.langPrefix
|
|
+ escape(lang, true)
|
|
+ '">'
|
|
+ (escaped ? code : escape(code, true))
|
|
+ '\n</code></pre>\n';
|
|
};
|
|
|
|
Renderer.prototype.blockquote = function(quote) {
|
|
return '<blockquote>\n' + quote + '</blockquote>\n';
|
|
};
|
|
|
|
Renderer.prototype.html = function(html) {
|
|
return html;
|
|
};
|
|
|
|
Renderer.prototype.heading = function(text, level, raw) {
|
|
return '<h'
|
|
+ level
|
|
+ ' id="'
|
|
+ this.options.headerPrefix
|
|
+ raw.toLowerCase().replace(/[^\w]+/g, '-')
|
|
+ '">'
|
|
+ text
|
|
+ '</h'
|
|
+ level
|
|
+ '>\n';
|
|
};
|
|
|
|
Renderer.prototype.hr = function() {
|
|
return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
|
|
};
|
|
|
|
Renderer.prototype.list = function(body, ordered) {
|
|
var type = ordered ? 'ol' : 'ul';
|
|
return '<' + type + '>\n' + body + '</' + type + '>\n';
|
|
};
|
|
|
|
Renderer.prototype.listitem = function(text) {
|
|
return '<li>' + text + '</li>\n';
|
|
};
|
|
|
|
Renderer.prototype.paragraph = function(text) {
|
|
return '<p>' + text + '</p>\n';
|
|
};
|
|
|
|
Renderer.prototype.table = function(header, body) {
|
|
return '<table>\n'
|
|
+ '<thead>\n'
|
|
+ header
|
|
+ '</thead>\n'
|
|
+ '<tbody>\n'
|
|
+ body
|
|
+ '</tbody>\n'
|
|
+ '</table>\n';
|
|
};
|
|
|
|
Renderer.prototype.tablerow = function(content) {
|
|
return '<tr>\n' + content + '</tr>\n';
|
|
};
|
|
|
|
Renderer.prototype.tablecell = function(content, flags) {
|
|
var type = flags.header ? 'th' : 'td';
|
|
var tag = flags.align
|
|
? '<' + type + ' style="text-align:' + flags.align + '">'
|
|
: '<' + type + '>';
|
|
return tag + content + '</' + type + '>\n';
|
|
};
|
|
|
|
// span level renderer
|
|
Renderer.prototype.strong = function(text) {
|
|
return '<strong>' + text + '</strong>';
|
|
};
|
|
|
|
Renderer.prototype.em = function(text) {
|
|
return '<em>' + text + '</em>';
|
|
};
|
|
|
|
Renderer.prototype.codespan = function(text) {
|
|
return '<code>' + text + '</code>';
|
|
};
|
|
|
|
Renderer.prototype.br = function() {
|
|
return this.options.xhtml ? '<br/>' : '<br>';
|
|
};
|
|
|
|
Renderer.prototype.del = function(text) {
|
|
return '<del>' + text + '</del>';
|
|
};
|
|
|
|
Renderer.prototype.link = function(href, title, text) {
|
|
if (this.options.sanitize) {
|
|
try {
|
|
var prot = decodeURIComponent(unescape(href))
|
|
.replace(/[^\w:]/g, '')
|
|
.toLowerCase();
|
|
} catch (e) {
|
|
return '';
|
|
}
|
|
if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
|
|
return '';
|
|
}
|
|
}
|
|
var out = '<a href="' + href + '"';
|
|
if (title) {
|
|
out += ' title="' + title + '"';
|
|
}
|
|
out += '>' + text + '</a>';
|
|
return out;
|
|
};
|
|
|
|
Renderer.prototype.image = function(href, title, text) {
|
|
var out = '<img src="' + href + '" alt="' + text + '"';
|
|
if (title) {
|
|
out += ' title="' + title + '"';
|
|
}
|
|
out += this.options.xhtml ? '/>' : '>';
|
|
return out;
|
|
};
|
|
|
|
Renderer.prototype.text = function(text) {
|
|
return text;
|
|
};
|
|
|
|
/**
|
|
* Parsing & Compiling
|
|
*/
|
|
|
|
function Parser(options) {
|
|
this.tokens = [];
|
|
this.token = null;
|
|
this.options = options || marked.defaults;
|
|
this.options.renderer = this.options.renderer || new Renderer;
|
|
this.renderer = this.options.renderer;
|
|
this.renderer.options = this.options;
|
|
}
|
|
|
|
/**
|
|
* Static Parse Method
|
|
*/
|
|
|
|
Parser.parse = function(src, options, renderer) {
|
|
var parser = new Parser(options, renderer);
|
|
return parser.parse(src);
|
|
};
|
|
|
|
/**
|
|
* Parse Loop
|
|
*/
|
|
|
|
Parser.prototype.parse = function(src) {
|
|
this.inline = new InlineLexer(src.links, this.options, this.renderer);
|
|
this.tokens = src.reverse();
|
|
|
|
var out = '';
|
|
while (this.next()) {
|
|
out += this.tok();
|
|
}
|
|
|
|
return out;
|
|
};
|
|
|
|
/**
|
|
* Next Token
|
|
*/
|
|
|
|
Parser.prototype.next = function() {
|
|
return this.token = this.tokens.pop();
|
|
};
|
|
|
|
/**
|
|
* Preview Next Token
|
|
*/
|
|
|
|
Parser.prototype.peek = function() {
|
|
return this.tokens[this.tokens.length - 1] || 0;
|
|
};
|
|
|
|
/**
|
|
* Parse Text Tokens
|
|
*/
|
|
|
|
Parser.prototype.parseText = function() {
|
|
var body = this.token.text;
|
|
|
|
while (this.peek().type === 'text') {
|
|
body += '\n' + this.next().text;
|
|
}
|
|
|
|
return this.inline.output(body);
|
|
};
|
|
|
|
/**
|
|
* Parse Current Token
|
|
*/
|
|
|
|
Parser.prototype.tok = function() {
|
|
switch (this.token.type) {
|
|
case 'space': {
|
|
return '';
|
|
}
|
|
case 'hr': {
|
|
return this.renderer.hr();
|
|
}
|
|
case 'heading': {
|
|
return this.renderer.heading(
|
|
this.inline.output(this.token.text),
|
|
this.token.depth,
|
|
this.token.text);
|
|
}
|
|
case 'code': {
|
|
return this.renderer.code(this.token.text,
|
|
this.token.lang,
|
|
this.token.escaped);
|
|
}
|
|
case 'table': {
|
|
var header = ''
|
|
, body = ''
|
|
, i
|
|
, row
|
|
, cell
|
|
, flags
|
|
, j;
|
|
|
|
// header
|
|
cell = '';
|
|
for (i = 0; i < this.token.header.length; i++) {
|
|
flags = { header: true, align: this.token.align[i] };
|
|
cell += this.renderer.tablecell(
|
|
this.inline.output(this.token.header[i]),
|
|
{ header: true, align: this.token.align[i] }
|
|
);
|
|
}
|
|
header += this.renderer.tablerow(cell);
|
|
|
|
for (i = 0; i < this.token.cells.length; i++) {
|
|
row = this.token.cells[i];
|
|
|
|
cell = '';
|
|
for (j = 0; j < row.length; j++) {
|
|
cell += this.renderer.tablecell(
|
|
this.inline.output(row[j]),
|
|
{ header: false, align: this.token.align[j] }
|
|
);
|
|
}
|
|
|
|
body += this.renderer.tablerow(cell);
|
|
}
|
|
return this.renderer.table(header, body);
|
|
}
|
|
case 'blockquote_start': {
|
|
var body = '';
|
|
|
|
while (this.next().type !== 'blockquote_end') {
|
|
body += this.tok();
|
|
}
|
|
|
|
return this.renderer.blockquote(body);
|
|
}
|
|
case 'list_start': {
|
|
var body = ''
|
|
, ordered = this.token.ordered;
|
|
|
|
while (this.next().type !== 'list_end') {
|
|
body += this.tok();
|
|
}
|
|
|
|
return this.renderer.list(body, ordered);
|
|
}
|
|
case 'list_item_start': {
|
|
var body = '';
|
|
|
|
while (this.next().type !== 'list_item_end') {
|
|
body += this.token.type === 'text'
|
|
? this.parseText()
|
|
: this.tok();
|
|
}
|
|
|
|
return this.renderer.listitem(body);
|
|
}
|
|
case 'loose_item_start': {
|
|
var body = '';
|
|
|
|
while (this.next().type !== 'list_item_end') {
|
|
body += this.tok();
|
|
}
|
|
|
|
return this.renderer.listitem(body);
|
|
}
|
|
case 'html': {
|
|
var html = !this.token.pre && !this.options.pedantic
|
|
? this.inline.output(this.token.text)
|
|
: this.token.text;
|
|
return this.renderer.html(html);
|
|
}
|
|
case 'paragraph': {
|
|
return this.renderer.paragraph(this.inline.output(this.token.text));
|
|
}
|
|
case 'text': {
|
|
return this.renderer.paragraph(this.parseText());
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Helpers
|
|
*/
|
|
|
|
function escape(html, encode) {
|
|
return html
|
|
.replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
function unescape(html) {
|
|
// explicitly match decimal, hex, and named HTML entities
|
|
return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
|
|
n = n.toLowerCase();
|
|
if (n === 'colon') return ':';
|
|
if (n.charAt(0) === '#') {
|
|
return n.charAt(1) === 'x'
|
|
? String.fromCharCode(parseInt(n.substring(2), 16))
|
|
: String.fromCharCode(+n.substring(1));
|
|
}
|
|
return '';
|
|
});
|
|
}
|
|
|
|
function replace(regex, opt) {
|
|
regex = regex.source;
|
|
opt = opt || '';
|
|
return function self(name, val) {
|
|
if (!name) return new RegExp(regex, opt);
|
|
val = val.source || val;
|
|
val = val.replace(/(^|[^\[])\^/g, '$1');
|
|
regex = regex.replace(name, val);
|
|
return self;
|
|
};
|
|
}
|
|
|
|
function noop() {}
|
|
noop.exec = noop;
|
|
|
|
function merge(obj) {
|
|
var i = 1
|
|
, target
|
|
, key;
|
|
|
|
for (; i < arguments.length; i++) {
|
|
target = arguments[i];
|
|
for (key in target) {
|
|
if (Object.prototype.hasOwnProperty.call(target, key)) {
|
|
obj[key] = target[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
|
|
/**
|
|
* Marked
|
|
*/
|
|
|
|
function marked(src, opt, callback) {
|
|
if (callback || typeof opt === 'function') {
|
|
if (!callback) {
|
|
callback = opt;
|
|
opt = null;
|
|
}
|
|
|
|
opt = merge({}, marked.defaults, opt || {});
|
|
|
|
var highlight = opt.highlight
|
|
, tokens
|
|
, pending
|
|
, i = 0;
|
|
|
|
try {
|
|
tokens = Lexer.lex(src, opt)
|
|
} catch (e) {
|
|
return callback(e);
|
|
}
|
|
|
|
pending = tokens.length;
|
|
|
|
var done = function(err) {
|
|
if (err) {
|
|
opt.highlight = highlight;
|
|
return callback(err);
|
|
}
|
|
|
|
var out;
|
|
|
|
try {
|
|
out = Parser.parse(tokens, opt);
|
|
} catch (e) {
|
|
err = e;
|
|
}
|
|
|
|
opt.highlight = highlight;
|
|
|
|
return err
|
|
? callback(err)
|
|
: callback(null, out);
|
|
};
|
|
|
|
if (!highlight || highlight.length < 3) {
|
|
return done();
|
|
}
|
|
|
|
delete opt.highlight;
|
|
|
|
if (!pending) return done();
|
|
|
|
for (; i < tokens.length; i++) {
|
|
(function(token) {
|
|
if (token.type !== 'code') {
|
|
return --pending || done();
|
|
}
|
|
return highlight(token.text, token.lang, function(err, code) {
|
|
if (err) return done(err);
|
|
if (code == null || code === token.text) {
|
|
return --pending || done();
|
|
}
|
|
token.text = code;
|
|
token.escaped = true;
|
|
--pending || done();
|
|
});
|
|
})(tokens[i]);
|
|
}
|
|
|
|
return;
|
|
}
|
|
try {
|
|
if (opt) opt = merge({}, marked.defaults, opt);
|
|
return Parser.parse(Lexer.lex(src, opt), opt);
|
|
} catch (e) {
|
|
e.message += '\nPlease report this to https://github.com/chjj/marked.';
|
|
if ((opt || marked.defaults).silent) {
|
|
return '<p>An error occured:</p><pre>'
|
|
+ escape(e.message + '', true)
|
|
+ '</pre>';
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Options
|
|
*/
|
|
|
|
marked.options =
|
|
marked.setOptions = function(opt) {
|
|
merge(marked.defaults, opt);
|
|
return marked;
|
|
};
|
|
|
|
marked.defaults = {
|
|
gfm: true,
|
|
tables: true,
|
|
breaks: false,
|
|
pedantic: false,
|
|
sanitize: false,
|
|
sanitizer: null,
|
|
mangle: true,
|
|
smartLists: false,
|
|
silent: false,
|
|
highlight: null,
|
|
langPrefix: 'lang-',
|
|
smartypants: false,
|
|
headerPrefix: '',
|
|
renderer: new Renderer,
|
|
xhtml: false
|
|
};
|
|
|
|
/**
|
|
* Expose
|
|
*/
|
|
|
|
marked.Parser = Parser;
|
|
marked.parser = Parser.parse;
|
|
|
|
marked.Renderer = Renderer;
|
|
|
|
marked.Lexer = Lexer;
|
|
marked.lexer = Lexer.lex;
|
|
|
|
marked.InlineLexer = InlineLexer;
|
|
marked.inlineLexer = InlineLexer.output;
|
|
|
|
marked.parse = marked;
|
|
|
|
if (typeof module !== 'undefined' && typeof exports === 'object') {
|
|
module.exports = marked;
|
|
} else if (typeof define === 'function' && define.amd) {
|
|
define(function() { return marked; });
|
|
} else {
|
|
this.marked = marked;
|
|
}
|
|
|
|
}).call(function() {
|
|
return this || (typeof window !== 'undefined' ? window : global);
|
|
}());
|
|
|
|
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{}],109:[function(require,module,exports){
|
|
(function (process,global,Buffer){
|
|
var inherits = require('inherits')
|
|
, AbstractLevelDOWN = require('abstract-leveldown').AbstractLevelDOWN
|
|
, AbstractIterator = require('abstract-leveldown').AbstractIterator
|
|
, ltgt = require('ltgt')
|
|
, setImmediate = global.setImmediate || process.nextTick
|
|
, createRBT = require('functional-red-black-tree')
|
|
, globalStore = {}
|
|
|
|
function toKey (key) {
|
|
return typeof key == 'string' ? '$' + key : JSON.stringify(key)
|
|
}
|
|
|
|
function gt(value) {
|
|
return ltgt.compare(value, this._end) > 0
|
|
}
|
|
|
|
function gte(value) {
|
|
return ltgt.compare(value, this._end) >= 0
|
|
}
|
|
|
|
function lt(value) {
|
|
return ltgt.compare(value, this._end) < 0
|
|
}
|
|
|
|
function lte(value) {
|
|
return ltgt.compare(value, this._end) <= 0
|
|
}
|
|
|
|
|
|
function MemIterator (db, options) {
|
|
AbstractIterator.call(this, db)
|
|
this._limit = options.limit
|
|
|
|
if (this._limit === -1)
|
|
this._limit = Infinity
|
|
|
|
var tree = db._store[db._location];
|
|
|
|
this.keyAsBuffer = options.keyAsBuffer !== false
|
|
this.valueAsBuffer = options.valueAsBuffer !== false
|
|
this._reverse = options.reverse
|
|
this._options = options
|
|
this._done = 0
|
|
|
|
if (!this._reverse) {
|
|
this._incr = 'next';
|
|
this._start = ltgt.lowerBound(options);
|
|
this._end = ltgt.upperBound(options)
|
|
|
|
if (typeof this._start === 'undefined')
|
|
this._tree = tree.begin;
|
|
else if (ltgt.lowerBoundInclusive(options))
|
|
this._tree = tree.ge(this._start);
|
|
else
|
|
this._tree = tree.gt(this._start);
|
|
|
|
if (this._end) {
|
|
if (ltgt.upperBoundInclusive(options))
|
|
this._test = lte
|
|
else
|
|
this._test = lt
|
|
}
|
|
|
|
} else {
|
|
this._incr = 'prev';
|
|
this._start = ltgt.upperBound(options)
|
|
this._end = ltgt.lowerBound(options)
|
|
|
|
if (typeof this._start === 'undefined')
|
|
this._tree = tree.end;
|
|
else if (ltgt.upperBoundInclusive(options))
|
|
this._tree = tree.le(this._start)
|
|
else
|
|
this._tree = tree.lt(this._start)
|
|
|
|
if (this._end) {
|
|
if (ltgt.lowerBoundInclusive(options))
|
|
this._test = gte
|
|
else
|
|
this._test = gt
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inherits(MemIterator, AbstractIterator)
|
|
|
|
MemIterator.prototype._next = function (callback) {
|
|
var key
|
|
, value
|
|
|
|
if (this._done++ >= this._limit)
|
|
return setImmediate(callback)
|
|
|
|
if (!this._tree.valid)
|
|
return setImmediate(callback)
|
|
|
|
key = this._tree.key
|
|
value = this._tree.value
|
|
|
|
if (!this._test(key))
|
|
return setImmediate(callback)
|
|
|
|
if (this.keyAsBuffer)
|
|
key = new Buffer(key)
|
|
|
|
if (this.valueAsBuffer)
|
|
value = new Buffer(value)
|
|
|
|
this._tree[this._incr]()
|
|
|
|
setImmediate(function callNext() {
|
|
callback(null, key, value)
|
|
})
|
|
}
|
|
|
|
MemIterator.prototype._test = function () {return true}
|
|
|
|
function MemDOWN (location) {
|
|
if (!(this instanceof MemDOWN))
|
|
return new MemDOWN(location)
|
|
|
|
AbstractLevelDOWN.call(this, typeof location == 'string' ? location : '')
|
|
|
|
this._location = this.location ? toKey(this.location) : '_tree'
|
|
this._store = this.location ? globalStore: this
|
|
this._store[this._location] = this._store[this._location] || createRBT(ltgt.compare)
|
|
}
|
|
|
|
MemDOWN.clearGlobalStore = function (strict) {
|
|
if (strict) {
|
|
Object.keys(globalStore).forEach(function (key) {
|
|
delete globalStore[key];
|
|
})
|
|
} else {
|
|
globalStore = {}
|
|
}
|
|
}
|
|
|
|
inherits(MemDOWN, AbstractLevelDOWN)
|
|
|
|
MemDOWN.prototype._open = function (options, callback) {
|
|
var self = this
|
|
setImmediate(function callNext() { callback(null, self) })
|
|
}
|
|
|
|
MemDOWN.prototype._put = function (key, value, options, callback) {
|
|
if (typeof value === 'undefined' || value === null) value = ''
|
|
|
|
var iter = this._store[this._location].find(key)
|
|
|
|
if (iter.valid) {
|
|
this._store[this._location] = iter.update(value)
|
|
} else {
|
|
this._store[this._location] = this._store[this._location].insert(key, value)
|
|
}
|
|
|
|
setImmediate(callback)
|
|
}
|
|
|
|
MemDOWN.prototype._get = function (key, options, callback) {
|
|
var value = this._store[this._location].get(key)
|
|
|
|
if (value === undefined) {
|
|
// 'NotFound' error, consistent with LevelDOWN API
|
|
var err = new Error('NotFound')
|
|
return setImmediate(function callNext() { callback(err) })
|
|
}
|
|
|
|
if (options.asBuffer !== false && !this._isBuffer(value))
|
|
value = new Buffer(String(value))
|
|
|
|
setImmediate(function callNext () {
|
|
callback(null, value)
|
|
})
|
|
|
|
}
|
|
|
|
MemDOWN.prototype._del = function (key, options, callback) {
|
|
this._store[this._location] = this._store[this._location].remove(key)
|
|
setImmediate(callback)
|
|
}
|
|
|
|
MemDOWN.prototype._batch = function (array, options, callback) {
|
|
var err
|
|
, i = -1
|
|
, key
|
|
, value
|
|
, iter
|
|
, len = array.length
|
|
, tree = this._store[this._location]
|
|
|
|
while (++i < len) {
|
|
if (!array[i])
|
|
continue;
|
|
|
|
key = this._isBuffer(array[i].key) ? array[i].key : String(array[i].key)
|
|
err = this._checkKey(key, 'key')
|
|
if (err)
|
|
return setImmediate(function errorCall() { callback(err) })
|
|
|
|
iter = tree.find(key)
|
|
|
|
if (array[i].type === 'put') {
|
|
value = this._isBuffer(array[i].value) ? array[i].value : String(array[i].value)
|
|
err = this._checkKey(value, 'value')
|
|
|
|
if (err)
|
|
return setImmediate(function errorCall() { callback(err) })
|
|
|
|
tree = iter.valid ? iter.update(value) : tree.insert(key, value)
|
|
} else {
|
|
tree = iter.remove()
|
|
}
|
|
}
|
|
|
|
this._store[this._location] = tree;
|
|
|
|
setImmediate(callback)
|
|
}
|
|
|
|
MemDOWN.prototype._iterator = function (options) {
|
|
return new MemIterator(this, options)
|
|
}
|
|
|
|
MemDOWN.prototype._isBuffer = function (obj) {
|
|
return Buffer.isBuffer(obj)
|
|
}
|
|
|
|
MemDOWN.destroy = function (name, callback) {
|
|
var key = toKey(name)
|
|
|
|
if (key in globalStore)
|
|
delete globalStore[key]
|
|
|
|
setImmediate(callback)
|
|
}
|
|
|
|
module.exports = MemDOWN
|
|
|
|
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer)
|
|
},{"_process":133,"abstract-leveldown":113,"buffer":43,"functional-red-black-tree":84,"inherits":87,"ltgt":107}],110:[function(require,module,exports){
|
|
(function (process){
|
|
/* Copyright (c) 2013 Rod Vagg, MIT License */
|
|
|
|
function AbstractChainedBatch (db) {
|
|
this._db = db
|
|
this._operations = []
|
|
this._written = false
|
|
}
|
|
|
|
AbstractChainedBatch.prototype._serializeKey = function (key) {
|
|
return this._db._serializeKey(key)
|
|
}
|
|
|
|
AbstractChainedBatch.prototype._serializeValue = function (value) {
|
|
return this._db._serializeValue(value)
|
|
}
|
|
|
|
AbstractChainedBatch.prototype._checkWritten = function () {
|
|
if (this._written)
|
|
throw new Error('write() already called on this batch')
|
|
}
|
|
|
|
AbstractChainedBatch.prototype.put = function (key, value) {
|
|
this._checkWritten()
|
|
|
|
var err = this._db._checkKey(key, 'key', this._db._isBuffer)
|
|
if (err)
|
|
throw err
|
|
|
|
key = this._serializeKey(key)
|
|
value = this._serializeValue(value)
|
|
|
|
if (typeof this._put == 'function' )
|
|
this._put(key, value)
|
|
else
|
|
this._operations.push({ type: 'put', key: key, value: value })
|
|
|
|
return this
|
|
}
|
|
|
|
AbstractChainedBatch.prototype.del = function (key) {
|
|
this._checkWritten()
|
|
|
|
var err = this._db._checkKey(key, 'key', this._db._isBuffer)
|
|
if (err) throw err
|
|
|
|
key = this._serializeKey(key)
|
|
|
|
if (typeof this._del == 'function' )
|
|
this._del(key)
|
|
else
|
|
this._operations.push({ type: 'del', key: key })
|
|
|
|
return this
|
|
}
|
|
|
|
AbstractChainedBatch.prototype.clear = function () {
|
|
this._checkWritten()
|
|
|
|
this._operations = []
|
|
|
|
if (typeof this._clear == 'function' )
|
|
this._clear()
|
|
|
|
return this
|
|
}
|
|
|
|
AbstractChainedBatch.prototype.write = function (options, callback) {
|
|
this._checkWritten()
|
|
|
|
if (typeof options == 'function')
|
|
callback = options
|
|
if (typeof callback != 'function')
|
|
throw new Error('write() requires a callback argument')
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
|
|
this._written = true
|
|
|
|
if (typeof this._write == 'function' )
|
|
return this._write(callback)
|
|
|
|
if (typeof this._db._batch == 'function')
|
|
return this._db._batch(this._operations, options, callback)
|
|
|
|
process.nextTick(callback)
|
|
}
|
|
|
|
module.exports = AbstractChainedBatch
|
|
|
|
}).call(this,require('_process'))
|
|
},{"_process":133}],111:[function(require,module,exports){
|
|
arguments[4][48][0].apply(exports,arguments)
|
|
},{"_process":133,"dup":48}],112:[function(require,module,exports){
|
|
(function (Buffer,process){
|
|
/* Copyright (c) 2013 Rod Vagg, MIT License */
|
|
|
|
var xtend = require('xtend')
|
|
, AbstractIterator = require('./abstract-iterator')
|
|
, AbstractChainedBatch = require('./abstract-chained-batch')
|
|
|
|
function AbstractLevelDOWN (location) {
|
|
if (!arguments.length || location === undefined)
|
|
throw new Error('constructor requires at least a location argument')
|
|
|
|
if (typeof location != 'string')
|
|
throw new Error('constructor requires a location string argument')
|
|
|
|
this.location = location
|
|
this.status = 'new'
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.open = function (options, callback) {
|
|
var self = this
|
|
, oldStatus = this.status
|
|
|
|
if (typeof options == 'function')
|
|
callback = options
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('open() requires a callback argument')
|
|
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
|
|
options.createIfMissing = options.createIfMissing != false
|
|
options.errorIfExists = !!options.errorIfExists
|
|
|
|
if (typeof this._open == 'function') {
|
|
this.status = 'opening'
|
|
this._open(options, function (err) {
|
|
if (err) {
|
|
self.status = oldStatus
|
|
return callback(err)
|
|
}
|
|
self.status = 'open'
|
|
callback()
|
|
})
|
|
} else {
|
|
this.status = 'open'
|
|
process.nextTick(callback)
|
|
}
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.close = function (callback) {
|
|
var self = this
|
|
, oldStatus = this.status
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('close() requires a callback argument')
|
|
|
|
if (typeof this._close == 'function') {
|
|
this.status = 'closing'
|
|
this._close(function (err) {
|
|
if (err) {
|
|
self.status = oldStatus
|
|
return callback(err)
|
|
}
|
|
self.status = 'closed'
|
|
callback()
|
|
})
|
|
} else {
|
|
this.status = 'closed'
|
|
process.nextTick(callback)
|
|
}
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.get = function (key, options, callback) {
|
|
var err
|
|
|
|
if (typeof options == 'function')
|
|
callback = options
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('get() requires a callback argument')
|
|
|
|
if (err = this._checkKey(key, 'key'))
|
|
return callback(err)
|
|
|
|
key = this._serializeKey(key)
|
|
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
|
|
options.asBuffer = options.asBuffer != false
|
|
|
|
if (typeof this._get == 'function')
|
|
return this._get(key, options, callback)
|
|
|
|
process.nextTick(function () { callback(new Error('NotFound')) })
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.put = function (key, value, options, callback) {
|
|
var err
|
|
|
|
if (typeof options == 'function')
|
|
callback = options
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('put() requires a callback argument')
|
|
|
|
if (err = this._checkKey(key, 'key'))
|
|
return callback(err)
|
|
|
|
key = this._serializeKey(key)
|
|
value = this._serializeValue(value)
|
|
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
|
|
if (typeof this._put == 'function')
|
|
return this._put(key, value, options, callback)
|
|
|
|
process.nextTick(callback)
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.del = function (key, options, callback) {
|
|
var err
|
|
|
|
if (typeof options == 'function')
|
|
callback = options
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('del() requires a callback argument')
|
|
|
|
if (err = this._checkKey(key, 'key'))
|
|
return callback(err)
|
|
|
|
key = this._serializeKey(key)
|
|
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
|
|
if (typeof this._del == 'function')
|
|
return this._del(key, options, callback)
|
|
|
|
process.nextTick(callback)
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.batch = function (array, options, callback) {
|
|
if (!arguments.length)
|
|
return this._chainedBatch()
|
|
|
|
if (typeof options == 'function')
|
|
callback = options
|
|
|
|
if (typeof array == 'function')
|
|
callback = array
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('batch(array) requires a callback argument')
|
|
|
|
if (!Array.isArray(array))
|
|
return callback(new Error('batch(array) requires an array argument'))
|
|
|
|
if (!options || typeof options != 'object')
|
|
options = {}
|
|
|
|
var i = 0
|
|
, l = array.length
|
|
, e
|
|
, err
|
|
|
|
for (; i < l; i++) {
|
|
e = array[i]
|
|
if (typeof e != 'object')
|
|
continue
|
|
|
|
if (err = this._checkKey(e.type, 'type'))
|
|
return callback(err)
|
|
|
|
if (err = this._checkKey(e.key, 'key'))
|
|
return callback(err)
|
|
}
|
|
|
|
if (typeof this._batch == 'function')
|
|
return this._batch(array, options, callback)
|
|
|
|
process.nextTick(callback)
|
|
}
|
|
|
|
//TODO: remove from here, not a necessary primitive
|
|
AbstractLevelDOWN.prototype.approximateSize = function (start, end, callback) {
|
|
if ( start == null
|
|
|| end == null
|
|
|| typeof start == 'function'
|
|
|| typeof end == 'function') {
|
|
throw new Error('approximateSize() requires valid `start`, `end` and `callback` arguments')
|
|
}
|
|
|
|
if (typeof callback != 'function')
|
|
throw new Error('approximateSize() requires a callback argument')
|
|
|
|
start = this._serializeKey(start)
|
|
end = this._serializeKey(end)
|
|
|
|
if (typeof this._approximateSize == 'function')
|
|
return this._approximateSize(start, end, callback)
|
|
|
|
process.nextTick(function () {
|
|
callback(null, 0)
|
|
})
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype._setupIteratorOptions = function (options) {
|
|
var self = this
|
|
|
|
options = xtend(options)
|
|
|
|
;[ 'start', 'end', 'gt', 'gte', 'lt', 'lte' ].forEach(function (o) {
|
|
if (options[o] && self._isBuffer(options[o]) && options[o].length === 0)
|
|
delete options[o]
|
|
})
|
|
|
|
options.reverse = !!options.reverse
|
|
options.keys = options.keys != false
|
|
options.values = options.values != false
|
|
options.limit = 'limit' in options ? options.limit : -1
|
|
options.keyAsBuffer = options.keyAsBuffer != false
|
|
options.valueAsBuffer = options.valueAsBuffer != false
|
|
|
|
return options
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype.iterator = function (options) {
|
|
if (typeof options != 'object')
|
|
options = {}
|
|
|
|
options = this._setupIteratorOptions(options)
|
|
|
|
if (typeof this._iterator == 'function')
|
|
return this._iterator(options)
|
|
|
|
return new AbstractIterator(this)
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype._chainedBatch = function () {
|
|
return new AbstractChainedBatch(this)
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype._isBuffer = function (obj) {
|
|
return Buffer.isBuffer(obj)
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype._serializeKey = function (key) {
|
|
return this._isBuffer(key)
|
|
? key
|
|
: String(key)
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype._serializeValue = function (value) {
|
|
return this._isBuffer(value) || process.browser || value == null
|
|
? value
|
|
: String(value)
|
|
}
|
|
|
|
AbstractLevelDOWN.prototype._checkKey = function (obj, type) {
|
|
if (obj === null || obj === undefined)
|
|
return new Error(type + ' cannot be `null` or `undefined`')
|
|
|
|
if (this._isBuffer(obj) && obj.length === 0)
|
|
return new Error(type + ' cannot be an empty Buffer')
|
|
else if (String(obj) === '')
|
|
return new Error(type + ' cannot be an empty String')
|
|
}
|
|
|
|
module.exports = AbstractLevelDOWN
|
|
|
|
}).call(this,{"isBuffer":require("../../../is-buffer/index.js")},require('_process'))
|
|
},{"../../../is-buffer/index.js":88,"./abstract-chained-batch":110,"./abstract-iterator":111,"_process":133,"xtend":299}],113:[function(require,module,exports){
|
|
arguments[4][50][0].apply(exports,arguments)
|
|
},{"./abstract-chained-batch":110,"./abstract-iterator":111,"./abstract-leveldown":112,"./is-leveldown":114,"dup":50}],114:[function(require,module,exports){
|
|
arguments[4][51][0].apply(exports,arguments)
|
|
},{"./abstract-leveldown":112,"dup":51}],115:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var MIN_MAGNITUDE = -324; // verified by -Number.MIN_VALUE
|
|
var MAGNITUDE_DIGITS = 3; // ditto
|
|
var SEP = ''; // set to '_' for easier debugging
|
|
|
|
var utils = require('./utils');
|
|
|
|
exports.collate = function (a, b) {
|
|
|
|
if (a === b) {
|
|
return 0;
|
|
}
|
|
|
|
a = exports.normalizeKey(a);
|
|
b = exports.normalizeKey(b);
|
|
|
|
var ai = collationIndex(a);
|
|
var bi = collationIndex(b);
|
|
if ((ai - bi) !== 0) {
|
|
return ai - bi;
|
|
}
|
|
if (a === null) {
|
|
return 0;
|
|
}
|
|
switch (typeof a) {
|
|
case 'number':
|
|
return a - b;
|
|
case 'boolean':
|
|
return a === b ? 0 : (a < b ? -1 : 1);
|
|
case 'string':
|
|
return stringCollate(a, b);
|
|
}
|
|
return Array.isArray(a) ? arrayCollate(a, b) : objectCollate(a, b);
|
|
};
|
|
|
|
// couch considers null/NaN/Infinity/-Infinity === undefined,
|
|
// for the purposes of mapreduce indexes. also, dates get stringified.
|
|
exports.normalizeKey = function (key) {
|
|
switch (typeof key) {
|
|
case 'undefined':
|
|
return null;
|
|
case 'number':
|
|
if (key === Infinity || key === -Infinity || isNaN(key)) {
|
|
return null;
|
|
}
|
|
return key;
|
|
case 'object':
|
|
var origKey = key;
|
|
if (Array.isArray(key)) {
|
|
var len = key.length;
|
|
key = new Array(len);
|
|
for (var i = 0; i < len; i++) {
|
|
key[i] = exports.normalizeKey(origKey[i]);
|
|
}
|
|
} else if (key instanceof Date) {
|
|
return key.toJSON();
|
|
} else if (key !== null) { // generic object
|
|
key = {};
|
|
for (var k in origKey) {
|
|
if (origKey.hasOwnProperty(k)) {
|
|
var val = origKey[k];
|
|
if (typeof val !== 'undefined') {
|
|
key[k] = exports.normalizeKey(val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return key;
|
|
};
|
|
|
|
function indexify(key) {
|
|
if (key !== null) {
|
|
switch (typeof key) {
|
|
case 'boolean':
|
|
return key ? 1 : 0;
|
|
case 'number':
|
|
return numToIndexableString(key);
|
|
case 'string':
|
|
// We've to be sure that key does not contain \u0000
|
|
// Do order-preserving replacements:
|
|
// 0 -> 1, 1
|
|
// 1 -> 1, 2
|
|
// 2 -> 2, 2
|
|
return key
|
|
.replace(/\u0002/g, '\u0002\u0002')
|
|
.replace(/\u0001/g, '\u0001\u0002')
|
|
.replace(/\u0000/g, '\u0001\u0001');
|
|
case 'object':
|
|
var isArray = Array.isArray(key);
|
|
var arr = isArray ? key : Object.keys(key);
|
|
var i = -1;
|
|
var len = arr.length;
|
|
var result = '';
|
|
if (isArray) {
|
|
while (++i < len) {
|
|
result += exports.toIndexableString(arr[i]);
|
|
}
|
|
} else {
|
|
while (++i < len) {
|
|
var objKey = arr[i];
|
|
result += exports.toIndexableString(objKey) +
|
|
exports.toIndexableString(key[objKey]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
// convert the given key to a string that would be appropriate
|
|
// for lexical sorting, e.g. within a database, where the
|
|
// sorting is the same given by the collate() function.
|
|
exports.toIndexableString = function (key) {
|
|
var zero = '\u0000';
|
|
key = exports.normalizeKey(key);
|
|
return collationIndex(key) + SEP + indexify(key) + zero;
|
|
};
|
|
|
|
function parseNumber(str, i) {
|
|
var originalIdx = i;
|
|
var num;
|
|
var zero = str[i] === '1';
|
|
if (zero) {
|
|
num = 0;
|
|
i++;
|
|
} else {
|
|
var neg = str[i] === '0';
|
|
i++;
|
|
var numAsString = '';
|
|
var magAsString = str.substring(i, i + MAGNITUDE_DIGITS);
|
|
var magnitude = parseInt(magAsString, 10) + MIN_MAGNITUDE;
|
|
if (neg) {
|
|
magnitude = -magnitude;
|
|
}
|
|
i += MAGNITUDE_DIGITS;
|
|
while (true) {
|
|
var ch = str[i];
|
|
if (ch === '\u0000') {
|
|
break;
|
|
} else {
|
|
numAsString += ch;
|
|
}
|
|
i++;
|
|
}
|
|
numAsString = numAsString.split('.');
|
|
if (numAsString.length === 1) {
|
|
num = parseInt(numAsString, 10);
|
|
} else {
|
|
num = parseFloat(numAsString[0] + '.' + numAsString[1]);
|
|
}
|
|
if (neg) {
|
|
num = num - 10;
|
|
}
|
|
if (magnitude !== 0) {
|
|
// parseFloat is more reliable than pow due to rounding errors
|
|
// e.g. Number.MAX_VALUE would return Infinity if we did
|
|
// num * Math.pow(10, magnitude);
|
|
num = parseFloat(num + 'e' + magnitude);
|
|
}
|
|
}
|
|
return {num: num, length : i - originalIdx};
|
|
}
|
|
|
|
// move up the stack while parsing
|
|
// this function moved outside of parseIndexableString for performance
|
|
function pop(stack, metaStack) {
|
|
var obj = stack.pop();
|
|
|
|
if (metaStack.length) {
|
|
var lastMetaElement = metaStack[metaStack.length - 1];
|
|
if (obj === lastMetaElement.element) {
|
|
// popping a meta-element, e.g. an object whose value is another object
|
|
metaStack.pop();
|
|
lastMetaElement = metaStack[metaStack.length - 1];
|
|
}
|
|
var element = lastMetaElement.element;
|
|
var lastElementIndex = lastMetaElement.index;
|
|
if (Array.isArray(element)) {
|
|
element.push(obj);
|
|
} else if (lastElementIndex === stack.length - 2) { // obj with key+value
|
|
var key = stack.pop();
|
|
element[key] = obj;
|
|
} else {
|
|
stack.push(obj); // obj with key only
|
|
}
|
|
}
|
|
}
|
|
|
|
exports.parseIndexableString = function (str) {
|
|
var stack = [];
|
|
var metaStack = []; // stack for arrays and objects
|
|
var i = 0;
|
|
|
|
while (true) {
|
|
var collationIndex = str[i++];
|
|
if (collationIndex === '\u0000') {
|
|
if (stack.length === 1) {
|
|
return stack.pop();
|
|
} else {
|
|
pop(stack, metaStack);
|
|
continue;
|
|
}
|
|
}
|
|
switch (collationIndex) {
|
|
case '1':
|
|
stack.push(null);
|
|
break;
|
|
case '2':
|
|
stack.push(str[i] === '1');
|
|
i++;
|
|
break;
|
|
case '3':
|
|
var parsedNum = parseNumber(str, i);
|
|
stack.push(parsedNum.num);
|
|
i += parsedNum.length;
|
|
break;
|
|
case '4':
|
|
var parsedStr = '';
|
|
while (true) {
|
|
var ch = str[i];
|
|
if (ch === '\u0000') {
|
|
break;
|
|
}
|
|
parsedStr += ch;
|
|
i++;
|
|
}
|
|
// perform the reverse of the order-preserving replacement
|
|
// algorithm (see above)
|
|
parsedStr = parsedStr.replace(/\u0001\u0001/g, '\u0000')
|
|
.replace(/\u0001\u0002/g, '\u0001')
|
|
.replace(/\u0002\u0002/g, '\u0002');
|
|
stack.push(parsedStr);
|
|
break;
|
|
case '5':
|
|
var arrayElement = { element: [], index: stack.length };
|
|
stack.push(arrayElement.element);
|
|
metaStack.push(arrayElement);
|
|
break;
|
|
case '6':
|
|
var objElement = { element: {}, index: stack.length };
|
|
stack.push(objElement.element);
|
|
metaStack.push(objElement);
|
|
break;
|
|
default:
|
|
throw new Error(
|
|
'bad collationIndex or unexpectedly reached end of input: ' + collationIndex);
|
|
}
|
|
}
|
|
};
|
|
|
|
function arrayCollate(a, b) {
|
|
var len = Math.min(a.length, b.length);
|
|
for (var i = 0; i < len; i++) {
|
|
var sort = exports.collate(a[i], b[i]);
|
|
if (sort !== 0) {
|
|
return sort;
|
|
}
|
|
}
|
|
return (a.length === b.length) ? 0 :
|
|
(a.length > b.length) ? 1 : -1;
|
|
}
|
|
function stringCollate(a, b) {
|
|
// See: https://github.com/daleharvey/pouchdb/issues/40
|
|
// This is incompatible with the CouchDB implementation, but its the
|
|
// best we can do for now
|
|
return (a === b) ? 0 : ((a > b) ? 1 : -1);
|
|
}
|
|
function objectCollate(a, b) {
|
|
var ak = Object.keys(a), bk = Object.keys(b);
|
|
var len = Math.min(ak.length, bk.length);
|
|
for (var i = 0; i < len; i++) {
|
|
// First sort the keys
|
|
var sort = exports.collate(ak[i], bk[i]);
|
|
if (sort !== 0) {
|
|
return sort;
|
|
}
|
|
// if the keys are equal sort the values
|
|
sort = exports.collate(a[ak[i]], b[bk[i]]);
|
|
if (sort !== 0) {
|
|
return sort;
|
|
}
|
|
|
|
}
|
|
return (ak.length === bk.length) ? 0 :
|
|
(ak.length > bk.length) ? 1 : -1;
|
|
}
|
|
// The collation is defined by erlangs ordered terms
|
|
// the atoms null, true, false come first, then numbers, strings,
|
|
// arrays, then objects
|
|
// null/undefined/NaN/Infinity/-Infinity are all considered null
|
|
function collationIndex(x) {
|
|
var id = ['boolean', 'number', 'string', 'object'];
|
|
var idx = id.indexOf(typeof x);
|
|
//false if -1 otherwise true, but fast!!!!1
|
|
if (~idx) {
|
|
if (x === null) {
|
|
return 1;
|
|
}
|
|
if (Array.isArray(x)) {
|
|
return 5;
|
|
}
|
|
return idx < 3 ? (idx + 2) : (idx + 3);
|
|
}
|
|
if (Array.isArray(x)) {
|
|
return 5;
|
|
}
|
|
}
|
|
|
|
// conversion:
|
|
// x yyy zz...zz
|
|
// x = 0 for negative, 1 for 0, 2 for positive
|
|
// y = exponent (for negative numbers negated) moved so that it's >= 0
|
|
// z = mantisse
|
|
function numToIndexableString(num) {
|
|
|
|
if (num === 0) {
|
|
return '1';
|
|
}
|
|
|
|
// convert number to exponential format for easier and
|
|
// more succinct string sorting
|
|
var expFormat = num.toExponential().split(/e\+?/);
|
|
var magnitude = parseInt(expFormat[1], 10);
|
|
|
|
var neg = num < 0;
|
|
|
|
var result = neg ? '0' : '2';
|
|
|
|
// first sort by magnitude
|
|
// it's easier if all magnitudes are positive
|
|
var magForComparison = ((neg ? -magnitude : magnitude) - MIN_MAGNITUDE);
|
|
var magString = utils.padLeft((magForComparison).toString(), '0', MAGNITUDE_DIGITS);
|
|
|
|
result += SEP + magString;
|
|
|
|
// then sort by the factor
|
|
var factor = Math.abs(parseFloat(expFormat[0])); // [1..10)
|
|
if (neg) { // for negative reverse ordering
|
|
factor = 10 - factor;
|
|
}
|
|
|
|
var factorStr = factor.toFixed(20);
|
|
|
|
// strip zeros from the end
|
|
factorStr = factorStr.replace(/\.?0+$/, '');
|
|
|
|
result += SEP + factorStr;
|
|
|
|
return result;
|
|
}
|
|
|
|
},{"./utils":116}],116:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
function pad(str, padWith, upToLength) {
|
|
var padding = '';
|
|
var targetLength = upToLength - str.length;
|
|
while (padding.length < targetLength) {
|
|
padding += padWith;
|
|
}
|
|
return padding;
|
|
}
|
|
|
|
exports.padLeft = function (str, padWith, upToLength) {
|
|
var padding = pad(str, padWith, upToLength);
|
|
return padding + str;
|
|
};
|
|
|
|
exports.padRight = function (str, padWith, upToLength) {
|
|
var padding = pad(str, padWith, upToLength);
|
|
return str + padding;
|
|
};
|
|
|
|
exports.stringLexCompare = function (a, b) {
|
|
|
|
var aLen = a.length;
|
|
var bLen = b.length;
|
|
|
|
var i;
|
|
for (i = 0; i < aLen; i++) {
|
|
if (i === bLen) {
|
|
// b is shorter substring of a
|
|
return 1;
|
|
}
|
|
var aChar = a.charAt(i);
|
|
var bChar = b.charAt(i);
|
|
if (aChar !== bChar) {
|
|
return aChar < bChar ? -1 : 1;
|
|
}
|
|
}
|
|
|
|
if (aLen < bLen) {
|
|
// a is shorter substring of b
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
/*
|
|
* returns the decimal form for the given integer, i.e. writes
|
|
* out all the digits (in base-10) instead of using scientific notation
|
|
*/
|
|
exports.intToDecimalForm = function (int) {
|
|
|
|
var isNeg = int < 0;
|
|
var result = '';
|
|
|
|
do {
|
|
var remainder = isNeg ? -Math.ceil(int % 10) : Math.floor(int % 10);
|
|
|
|
result = remainder + result;
|
|
int = isNeg ? Math.ceil(int / 10) : Math.floor(int / 10);
|
|
} while (int);
|
|
|
|
|
|
if (isNeg && result !== '0') {
|
|
result = '-' + result;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
},{}],117:[function(require,module,exports){
|
|
'use strict';
|
|
exports.Map = LazyMap; // TODO: use ES6 map
|
|
exports.Set = LazySet; // TODO: use ES6 set
|
|
// based on https://github.com/montagejs/collections
|
|
function LazyMap() {
|
|
this.store = {};
|
|
}
|
|
LazyMap.prototype.mangle = function (key) {
|
|
if (typeof key !== "string") {
|
|
throw new TypeError("key must be a string but Got " + key);
|
|
}
|
|
return '$' + key;
|
|
};
|
|
LazyMap.prototype.unmangle = function (key) {
|
|
return key.substring(1);
|
|
};
|
|
LazyMap.prototype.get = function (key) {
|
|
var mangled = this.mangle(key);
|
|
if (mangled in this.store) {
|
|
return this.store[mangled];
|
|
}
|
|
return void 0;
|
|
};
|
|
LazyMap.prototype.set = function (key, value) {
|
|
var mangled = this.mangle(key);
|
|
this.store[mangled] = value;
|
|
return true;
|
|
};
|
|
LazyMap.prototype.has = function (key) {
|
|
var mangled = this.mangle(key);
|
|
return mangled in this.store;
|
|
};
|
|
LazyMap.prototype.delete = function (key) {
|
|
var mangled = this.mangle(key);
|
|
if (mangled in this.store) {
|
|
delete this.store[mangled];
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
LazyMap.prototype.forEach = function (cb) {
|
|
var keys = Object.keys(this.store);
|
|
for (var i = 0, len = keys.length; i < len; i++) {
|
|
var key = keys[i];
|
|
var value = this.store[key];
|
|
key = this.unmangle(key);
|
|
cb(value, key);
|
|
}
|
|
};
|
|
|
|
function LazySet(array) {
|
|
this.store = new LazyMap();
|
|
|
|
// init with an array
|
|
if (array && Array.isArray(array)) {
|
|
for (var i = 0, len = array.length; i < len; i++) {
|
|
this.add(array[i]);
|
|
}
|
|
}
|
|
}
|
|
LazySet.prototype.add = function (key) {
|
|
return this.store.set(key, true);
|
|
};
|
|
LazySet.prototype.has = function (key) {
|
|
return this.store.has(key);
|
|
};
|
|
LazySet.prototype.delete = function (key) {
|
|
return this.store.delete(key);
|
|
};
|
|
|
|
},{}],118:[function(require,module,exports){
|
|
module.exports = require('../lib/extras/memory');
|
|
},{"../lib/extras/memory":119}],119:[function(require,module,exports){
|
|
(function (process,global,Buffer){
|
|
'use strict';
|
|
|
|
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
|
|
|
var levelup = _interopDefault(require('levelup'));
|
|
var sublevel = _interopDefault(require('sublevel-pouchdb'));
|
|
var through2 = require('through2');
|
|
var getArguments = _interopDefault(require('argsarray'));
|
|
var pouchdbCollections = require('pouchdb-collections');
|
|
var Deque = _interopDefault(require('double-ended-queue'));
|
|
var lie = _interopDefault(require('lie'));
|
|
var debug = _interopDefault(require('debug'));
|
|
var events = require('events');
|
|
var inherits = _interopDefault(require('inherits'));
|
|
var Md5 = _interopDefault(require('spark-md5'));
|
|
var vuvuzela = _interopDefault(require('vuvuzela'));
|
|
var jsExtend = require('js-extend');
|
|
var memdown = _interopDefault(require('memdown'));
|
|
|
|
// in the browser, LevelAlt doesn't need the
|
|
// pre-2.2.0 LevelDB-specific migrations
|
|
var toSublevel = function (name, db, callback) {
|
|
process.nextTick(function () {
|
|
callback();
|
|
});
|
|
};
|
|
|
|
var localAndMetaStores = function (db, stores, callback) {
|
|
process.nextTick(function () {
|
|
callback();
|
|
});
|
|
};
|
|
|
|
var migrate = {
|
|
toSublevel: toSublevel,
|
|
localAndMetaStores: localAndMetaStores
|
|
};
|
|
|
|
/* istanbul ignore next */
|
|
var PouchPromise = typeof Promise === 'function' ? Promise : lie;
|
|
|
|
function isBinaryObject(object) {
|
|
return object instanceof ArrayBuffer ||
|
|
(typeof Blob !== 'undefined' && object instanceof Blob);
|
|
}
|
|
|
|
function cloneArrayBuffer(buff) {
|
|
if (typeof buff.slice === 'function') {
|
|
return buff.slice(0);
|
|
}
|
|
// IE10-11 slice() polyfill
|
|
var target = new ArrayBuffer(buff.byteLength);
|
|
var targetArray = new Uint8Array(target);
|
|
var sourceArray = new Uint8Array(buff);
|
|
targetArray.set(sourceArray);
|
|
return target;
|
|
}
|
|
|
|
function cloneBinaryObject(object) {
|
|
if (object instanceof ArrayBuffer) {
|
|
return cloneArrayBuffer(object);
|
|
}
|
|
var size = object.size;
|
|
var type = object.type;
|
|
// Blob
|
|
if (typeof object.slice === 'function') {
|
|
return object.slice(0, size, type);
|
|
}
|
|
// PhantomJS slice() replacement
|
|
return object.webkitSlice(0, size, type);
|
|
}
|
|
|
|
// most of this is borrowed from lodash.isPlainObject:
|
|
// https://github.com/fis-components/lodash.isplainobject/
|
|
// blob/29c358140a74f252aeb08c9eb28bef86f2217d4a/index.js
|
|
|
|
var funcToString = Function.prototype.toString;
|
|
var objectCtorString = funcToString.call(Object);
|
|
|
|
function isPlainObject(value) {
|
|
var proto = Object.getPrototypeOf(value);
|
|
/* istanbul ignore if */
|
|
if (proto === null) { // not sure when this happens, but I guess it can
|
|
return true;
|
|
}
|
|
var Ctor = proto.constructor;
|
|
return (typeof Ctor == 'function' &&
|
|
Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
|
|
}
|
|
|
|
function clone(object) {
|
|
var newObject;
|
|
var i;
|
|
var len;
|
|
|
|
if (!object || typeof object !== 'object') {
|
|
return object;
|
|
}
|
|
|
|
if (Array.isArray(object)) {
|
|
newObject = [];
|
|
for (i = 0, len = object.length; i < len; i++) {
|
|
newObject[i] = clone(object[i]);
|
|
}
|
|
return newObject;
|
|
}
|
|
|
|
// special case: to avoid inconsistencies between IndexedDB
|
|
// and other backends, we automatically stringify Dates
|
|
if (object instanceof Date) {
|
|
return object.toISOString();
|
|
}
|
|
|
|
if (isBinaryObject(object)) {
|
|
return cloneBinaryObject(object);
|
|
}
|
|
|
|
if (!isPlainObject(object)) {
|
|
return object; // don't clone objects like Workers
|
|
}
|
|
|
|
newObject = {};
|
|
for (i in object) {
|
|
if (Object.prototype.hasOwnProperty.call(object, i)) {
|
|
var value = clone(object[i]);
|
|
if (typeof value !== 'undefined') {
|
|
newObject[i] = value;
|
|
}
|
|
}
|
|
}
|
|
return newObject;
|
|
}
|
|
|
|
var log = debug('pouchdb:api');
|
|
|
|
// like underscore/lodash _.pick()
|
|
function pick(obj, arr) {
|
|
var res = {};
|
|
for (var i = 0, len = arr.length; i < len; i++) {
|
|
var prop = arr[i];
|
|
if (prop in obj) {
|
|
res[prop] = obj[prop];
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function isChromeApp() {
|
|
return (typeof chrome !== "undefined" &&
|
|
typeof chrome.storage !== "undefined" &&
|
|
typeof chrome.storage.local !== "undefined");
|
|
}
|
|
|
|
var hasLocal;
|
|
|
|
if (isChromeApp()) {
|
|
hasLocal = false;
|
|
} else {
|
|
try {
|
|
localStorage.setItem('_pouch_check_localstorage', 1);
|
|
hasLocal = !!localStorage.getItem('_pouch_check_localstorage');
|
|
} catch (e) {
|
|
hasLocal = false;
|
|
}
|
|
}
|
|
|
|
function hasLocalStorage() {
|
|
return hasLocal;
|
|
}
|
|
|
|
inherits(Changes, events.EventEmitter);
|
|
|
|
/* istanbul ignore next */
|
|
function attachBrowserEvents(self) {
|
|
if (isChromeApp()) {
|
|
chrome.storage.onChanged.addListener(function (e) {
|
|
// make sure it's event addressed to us
|
|
if (e.db_name != null) {
|
|
//object only has oldValue, newValue members
|
|
self.emit(e.dbName.newValue);
|
|
}
|
|
});
|
|
} else if (hasLocalStorage()) {
|
|
if (typeof addEventListener !== 'undefined') {
|
|
addEventListener("storage", function (e) {
|
|
self.emit(e.key);
|
|
});
|
|
} else { // old IE
|
|
window.attachEvent("storage", function (e) {
|
|
self.emit(e.key);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
function Changes() {
|
|
events.EventEmitter.call(this);
|
|
this._listeners = {};
|
|
|
|
attachBrowserEvents(this);
|
|
}
|
|
Changes.prototype.addListener = function (dbName, id, db, opts) {
|
|
/* istanbul ignore if */
|
|
if (this._listeners[id]) {
|
|
return;
|
|
}
|
|
var self = this;
|
|
var inprogress = false;
|
|
function eventFunction() {
|
|
/* istanbul ignore if */
|
|
if (!self._listeners[id]) {
|
|
return;
|
|
}
|
|
if (inprogress) {
|
|
inprogress = 'waiting';
|
|
return;
|
|
}
|
|
inprogress = true;
|
|
var changesOpts = pick(opts, [
|
|
'style', 'include_docs', 'attachments', 'conflicts', 'filter',
|
|
'doc_ids', 'view', 'since', 'query_params', 'binary'
|
|
]);
|
|
|
|
/* istanbul ignore next */
|
|
function onError() {
|
|
inprogress = false;
|
|
}
|
|
|
|
db.changes(changesOpts).on('change', function (c) {
|
|
if (c.seq > opts.since && !opts.cancelled) {
|
|
opts.since = c.seq;
|
|
opts.onChange(c);
|
|
}
|
|
}).on('complete', function () {
|
|
if (inprogress === 'waiting') {
|
|
setTimeout(function (){
|
|
eventFunction();
|
|
},0);
|
|
}
|
|
inprogress = false;
|
|
}).on('error', onError);
|
|
}
|
|
this._listeners[id] = eventFunction;
|
|
this.on(dbName, eventFunction);
|
|
};
|
|
|
|
Changes.prototype.removeListener = function (dbName, id) {
|
|
/* istanbul ignore if */
|
|
if (!(id in this._listeners)) {
|
|
return;
|
|
}
|
|
events.EventEmitter.prototype.removeListener.call(this, dbName,
|
|
this._listeners[id]);
|
|
};
|
|
|
|
|
|
/* istanbul ignore next */
|
|
Changes.prototype.notifyLocalWindows = function (dbName) {
|
|
//do a useless change on a storage thing
|
|
//in order to get other windows's listeners to activate
|
|
if (isChromeApp()) {
|
|
chrome.storage.local.set({dbName: dbName});
|
|
} else if (hasLocalStorage()) {
|
|
localStorage[dbName] = (localStorage[dbName] === "a") ? "b" : "a";
|
|
}
|
|
};
|
|
|
|
Changes.prototype.notify = function (dbName) {
|
|
this.emit(dbName);
|
|
this.notifyLocalWindows(dbName);
|
|
};
|
|
|
|
function guardedConsole(method) {
|
|
if (console !== 'undefined' && method in console) {
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
console[method].apply(console, args);
|
|
}
|
|
}
|
|
|
|
inherits(PouchError, Error);
|
|
|
|
function PouchError(opts) {
|
|
Error.call(this, opts.reason);
|
|
this.status = opts.status;
|
|
this.name = opts.error;
|
|
this.message = opts.reason;
|
|
this.error = true;
|
|
}
|
|
|
|
PouchError.prototype.toString = function () {
|
|
return JSON.stringify({
|
|
status: this.status,
|
|
name: this.name,
|
|
message: this.message,
|
|
reason: this.reason
|
|
});
|
|
};
|
|
|
|
var UNAUTHORIZED = new PouchError({
|
|
status: 401,
|
|
error: 'unauthorized',
|
|
reason: "Name or password is incorrect."
|
|
});
|
|
|
|
var MISSING_BULK_DOCS = new PouchError({
|
|
status: 400,
|
|
error: 'bad_request',
|
|
reason: "Missing JSON list of 'docs'"
|
|
});
|
|
|
|
var MISSING_DOC = new PouchError({
|
|
status: 404,
|
|
error: 'not_found',
|
|
reason: 'missing'
|
|
});
|
|
|
|
var REV_CONFLICT = new PouchError({
|
|
status: 409,
|
|
error: 'conflict',
|
|
reason: 'Document update conflict'
|
|
});
|
|
|
|
var INVALID_ID = new PouchError({
|
|
status: 400,
|
|
error: 'bad_request',
|
|
reason: '_id field must contain a string'
|
|
});
|
|
|
|
var MISSING_ID = new PouchError({
|
|
status: 412,
|
|
error: 'missing_id',
|
|
reason: '_id is required for puts'
|
|
});
|
|
|
|
var RESERVED_ID = new PouchError({
|
|
status: 400,
|
|
error: 'bad_request',
|
|
reason: 'Only reserved document ids may start with underscore.'
|
|
});
|
|
|
|
var NOT_OPEN = new PouchError({
|
|
status: 412,
|
|
error: 'precondition_failed',
|
|
reason: 'Database not open'
|
|
});
|
|
|
|
var UNKNOWN_ERROR = new PouchError({
|
|
status: 500,
|
|
error: 'unknown_error',
|
|
reason: 'Database encountered an unknown error'
|
|
});
|
|
|
|
var BAD_ARG = new PouchError({
|
|
status: 500,
|
|
error: 'badarg',
|
|
reason: 'Some query argument is invalid'
|
|
});
|
|
|
|
var INVALID_REQUEST = new PouchError({
|
|
status: 400,
|
|
error: 'invalid_request',
|
|
reason: 'Request was invalid'
|
|
});
|
|
|
|
var QUERY_PARSE_ERROR = new PouchError({
|
|
status: 400,
|
|
error: 'query_parse_error',
|
|
reason: 'Some query parameter is invalid'
|
|
});
|
|
|
|
var DOC_VALIDATION = new PouchError({
|
|
status: 500,
|
|
error: 'doc_validation',
|
|
reason: 'Bad special document member'
|
|
});
|
|
|
|
var BAD_REQUEST = new PouchError({
|
|
status: 400,
|
|
error: 'bad_request',
|
|
reason: 'Something wrong with the request'
|
|
});
|
|
|
|
var NOT_AN_OBJECT = new PouchError({
|
|
status: 400,
|
|
error: 'bad_request',
|
|
reason: 'Document must be a JSON object'
|
|
});
|
|
|
|
var DB_MISSING = new PouchError({
|
|
status: 404,
|
|
error: 'not_found',
|
|
reason: 'Database not found'
|
|
});
|
|
|
|
var IDB_ERROR = new PouchError({
|
|
status: 500,
|
|
error: 'indexed_db_went_bad',
|
|
reason: 'unknown'
|
|
});
|
|
|
|
var WSQ_ERROR = new PouchError({
|
|
status: 500,
|
|
error: 'web_sql_went_bad',
|
|
reason: 'unknown'
|
|
});
|
|
|
|
var LDB_ERROR = new PouchError({
|
|
status: 500,
|
|
error: 'levelDB_went_went_bad',
|
|
reason: 'unknown'
|
|
});
|
|
|
|
var FORBIDDEN = new PouchError({
|
|
status: 403,
|
|
error: 'forbidden',
|
|
reason: 'Forbidden by design doc validate_doc_update function'
|
|
});
|
|
|
|
var INVALID_REV = new PouchError({
|
|
status: 400,
|
|
error: 'bad_request',
|
|
reason: 'Invalid rev format'
|
|
});
|
|
|
|
var FILE_EXISTS = new PouchError({
|
|
status: 412,
|
|
error: 'file_exists',
|
|
reason: 'The database could not be created, the file already exists.'
|
|
});
|
|
|
|
var MISSING_STUB = new PouchError({
|
|
status: 412,
|
|
error: 'missing_stub'
|
|
});
|
|
|
|
var INVALID_URL = new PouchError({
|
|
status: 413,
|
|
error: 'invalid_url',
|
|
reason: 'Provided URL is invalid'
|
|
});
|
|
|
|
function createError(error, reason) {
|
|
function CustomPouchError(reason) {
|
|
// inherit error properties from our parent error manually
|
|
// so as to allow proper JSON parsing.
|
|
/* jshint ignore:start */
|
|
for (var p in error) {
|
|
if (typeof error[p] !== 'function') {
|
|
this[p] = error[p];
|
|
}
|
|
}
|
|
/* jshint ignore:end */
|
|
if (reason !== undefined) {
|
|
this.reason = reason;
|
|
}
|
|
}
|
|
CustomPouchError.prototype = PouchError.prototype;
|
|
return new CustomPouchError(reason);
|
|
}
|
|
|
|
function tryFilter(filter, doc, req) {
|
|
try {
|
|
return !filter(doc, req);
|
|
} catch (err) {
|
|
var msg = 'Filter function threw: ' + err.toString();
|
|
return createError(BAD_REQUEST, msg);
|
|
}
|
|
}
|
|
|
|
function filterChange(opts) {
|
|
var req = {};
|
|
var hasFilter = opts.filter && typeof opts.filter === 'function';
|
|
req.query = opts.query_params;
|
|
|
|
return function filter(change) {
|
|
if (!change.doc) {
|
|
// CSG sends events on the changes feed that don't have documents,
|
|
// this hack makes a whole lot of existing code robust.
|
|
change.doc = {};
|
|
}
|
|
|
|
var filterReturn = hasFilter && tryFilter(opts.filter, change.doc, req);
|
|
|
|
if (typeof filterReturn === 'object') {
|
|
return filterReturn;
|
|
}
|
|
|
|
if (filterReturn) {
|
|
return false;
|
|
}
|
|
|
|
if (!opts.include_docs) {
|
|
delete change.doc;
|
|
} else if (!opts.attachments) {
|
|
for (var att in change.doc._attachments) {
|
|
/* istanbul ignore else */
|
|
if (change.doc._attachments.hasOwnProperty(att)) {
|
|
change.doc._attachments[att].stub = true;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
}
|
|
|
|
// shim for Function.prototype.name,
|
|
// for browsers that don't support it like IE
|
|
|
|
/* istanbul ignore next */
|
|
function f() {}
|
|
|
|
var hasName = f.name;
|
|
var res;
|
|
|
|
// We dont run coverage in IE
|
|
/* istanbul ignore else */
|
|
if (hasName) {
|
|
res = function (fun) {
|
|
return fun.name;
|
|
};
|
|
} else {
|
|
res = function (fun) {
|
|
return fun.toString().match(/^\s*function\s*(\S*)\s*\(/)[1];
|
|
};
|
|
}
|
|
|
|
var functionName = res;
|
|
|
|
// Determine id an ID is valid
|
|
// - invalid IDs begin with an underescore that does not begin '_design' or
|
|
// '_local'
|
|
// - any other string value is a valid id
|
|
// Returns the specific error object for each case
|
|
function invalidIdError(id) {
|
|
var err;
|
|
if (!id) {
|
|
err = createError(MISSING_ID);
|
|
} else if (typeof id !== 'string') {
|
|
err = createError(INVALID_ID);
|
|
} else if (/^_/.test(id) && !(/^_(design|local)/).test(id)) {
|
|
err = createError(RESERVED_ID);
|
|
}
|
|
if (err) {
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
// BEGIN Math.uuid.js
|
|
|
|
/*!
|
|
Math.uuid.js (v1.4)
|
|
http://www.broofa.com
|
|
mailto:robert@broofa.com
|
|
|
|
Copyright (c) 2010 Robert Kieffer
|
|
Dual licensed under the MIT and GPL licenses.
|
|
*/
|
|
|
|
/*
|
|
* Generate a random uuid.
|
|
*
|
|
* USAGE: Math.uuid(length, radix)
|
|
* length - the desired number of characters
|
|
* radix - the number of allowable values for each character.
|
|
*
|
|
* EXAMPLES:
|
|
* // No arguments - returns RFC4122, version 4 ID
|
|
* >>> Math.uuid()
|
|
* "92329D39-6F5C-4520-ABFC-AAB64544E172"
|
|
*
|
|
* // One argument - returns ID of the specified length
|
|
* >>> Math.uuid(15) // 15 character ID (default base=62)
|
|
* "VcydxgltxrVZSTV"
|
|
*
|
|
* // Two arguments - returns ID of the specified length, and radix.
|
|
* // (Radix must be <= 62)
|
|
* >>> Math.uuid(8, 2) // 8 character ID (base=2)
|
|
* "01001010"
|
|
* >>> Math.uuid(8, 10) // 8 character ID (base=10)
|
|
* "47473046"
|
|
* >>> Math.uuid(8, 16) // 8 character ID (base=16)
|
|
* "098F4D35"
|
|
*/
|
|
var chars = (
|
|
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
|
|
'abcdefghijklmnopqrstuvwxyz'
|
|
).split('');
|
|
function getValue(radix) {
|
|
return 0 | Math.random() * radix;
|
|
}
|
|
function uuid(len, radix) {
|
|
radix = radix || chars.length;
|
|
var out = '';
|
|
var i = -1;
|
|
|
|
if (len) {
|
|
// Compact form
|
|
while (++i < len) {
|
|
out += chars[getValue(radix)];
|
|
}
|
|
return out;
|
|
}
|
|
// rfc4122, version 4 form
|
|
// Fill in random data. At i==19 set the high bits of clock sequence as
|
|
// per rfc4122, sec. 4.1.5
|
|
while (++i < 36) {
|
|
switch (i) {
|
|
case 8:
|
|
case 13:
|
|
case 18:
|
|
case 23:
|
|
out += '-';
|
|
break;
|
|
case 19:
|
|
out += chars[(getValue(16) & 0x3) | 0x8];
|
|
break;
|
|
default:
|
|
out += chars[getValue(16)];
|
|
}
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
function toObject(array) {
|
|
return array.reduce(function (obj, item) {
|
|
obj[item] = true;
|
|
return obj;
|
|
}, {});
|
|
}
|
|
// List of top level reserved words for doc
|
|
var reservedWords = toObject([
|
|
'_id',
|
|
'_rev',
|
|
'_attachments',
|
|
'_deleted',
|
|
'_revisions',
|
|
'_revs_info',
|
|
'_conflicts',
|
|
'_deleted_conflicts',
|
|
'_local_seq',
|
|
'_rev_tree',
|
|
//replication documents
|
|
'_replication_id',
|
|
'_replication_state',
|
|
'_replication_state_time',
|
|
'_replication_state_reason',
|
|
'_replication_stats',
|
|
// Specific to Couchbase Sync Gateway
|
|
'_removed'
|
|
]);
|
|
|
|
// List of reserved words that should end up the document
|
|
var dataWords = toObject([
|
|
'_attachments',
|
|
//replication documents
|
|
'_replication_id',
|
|
'_replication_state',
|
|
'_replication_state_time',
|
|
'_replication_state_reason',
|
|
'_replication_stats'
|
|
]);
|
|
|
|
function parseRevisionInfo(rev) {
|
|
if (!/^\d+\-./.test(rev)) {
|
|
return createError(INVALID_REV);
|
|
}
|
|
var idx = rev.indexOf('-');
|
|
var left = rev.substring(0, idx);
|
|
var right = rev.substring(idx + 1);
|
|
return {
|
|
prefix: parseInt(left, 10),
|
|
id: right
|
|
};
|
|
}
|
|
|
|
function makeRevTreeFromRevisions(revisions, opts) {
|
|
var pos = revisions.start - revisions.ids.length + 1;
|
|
|
|
var revisionIds = revisions.ids;
|
|
var ids = [revisionIds[0], opts, []];
|
|
|
|
for (var i = 1, len = revisionIds.length; i < len; i++) {
|
|
ids = [revisionIds[i], {status: 'missing'}, [ids]];
|
|
}
|
|
|
|
return [{
|
|
pos: pos,
|
|
ids: ids
|
|
}];
|
|
}
|
|
|
|
// Preprocess documents, parse their revisions, assign an id and a
|
|
// revision for new writes that are missing them, etc
|
|
function parseDoc(doc, newEdits) {
|
|
|
|
var nRevNum;
|
|
var newRevId;
|
|
var revInfo;
|
|
var opts = {status: 'available'};
|
|
if (doc._deleted) {
|
|
opts.deleted = true;
|
|
}
|
|
|
|
if (newEdits) {
|
|
if (!doc._id) {
|
|
doc._id = uuid();
|
|
}
|
|
newRevId = uuid(32, 16).toLowerCase();
|
|
if (doc._rev) {
|
|
revInfo = parseRevisionInfo(doc._rev);
|
|
if (revInfo.error) {
|
|
return revInfo;
|
|
}
|
|
doc._rev_tree = [{
|
|
pos: revInfo.prefix,
|
|
ids: [revInfo.id, {status: 'missing'}, [[newRevId, opts, []]]]
|
|
}];
|
|
nRevNum = revInfo.prefix + 1;
|
|
} else {
|
|
doc._rev_tree = [{
|
|
pos: 1,
|
|
ids : [newRevId, opts, []]
|
|
}];
|
|
nRevNum = 1;
|
|
}
|
|
} else {
|
|
if (doc._revisions) {
|
|
doc._rev_tree = makeRevTreeFromRevisions(doc._revisions, opts);
|
|
nRevNum = doc._revisions.start;
|
|
newRevId = doc._revisions.ids[0];
|
|
}
|
|
if (!doc._rev_tree) {
|
|
revInfo = parseRevisionInfo(doc._rev);
|
|
if (revInfo.error) {
|
|
return revInfo;
|
|
}
|
|
nRevNum = revInfo.prefix;
|
|
newRevId = revInfo.id;
|
|
doc._rev_tree = [{
|
|
pos: nRevNum,
|
|
ids: [newRevId, opts, []]
|
|
}];
|
|
}
|
|
}
|
|
|
|
invalidIdError(doc._id);
|
|
|
|
doc._rev = nRevNum + '-' + newRevId;
|
|
|
|
var result = {metadata : {}, data : {}};
|
|
for (var key in doc) {
|
|
/* istanbul ignore else */
|
|
if (Object.prototype.hasOwnProperty.call(doc, key)) {
|
|
var specialKey = key[0] === '_';
|
|
if (specialKey && !reservedWords[key]) {
|
|
var error = createError(DOC_VALIDATION, key);
|
|
error.message = DOC_VALIDATION.message + ': ' + key;
|
|
throw error;
|
|
} else if (specialKey && !dataWords[key]) {
|
|
result.metadata[key.slice(1)] = doc[key];
|
|
} else {
|
|
result.data[key] = doc[key];
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// We fetch all leafs of the revision tree, and sort them based on tree length
|
|
// and whether they were deleted, undeleted documents with the longest revision
|
|
// tree (most edits) win
|
|
// The final sort algorithm is slightly documented in a sidebar here:
|
|
// http://guide.couchdb.org/draft/conflicts.html
|
|
function winningRev(metadata) {
|
|
var winningId;
|
|
var winningPos;
|
|
var winningDeleted;
|
|
var toVisit = metadata.rev_tree.slice();
|
|
var node;
|
|
while ((node = toVisit.pop())) {
|
|
var tree = node.ids;
|
|
var branches = tree[2];
|
|
var pos = node.pos;
|
|
if (branches.length) { // non-leaf
|
|
for (var i = 0, len = branches.length; i < len; i++) {
|
|
toVisit.push({pos: pos + 1, ids: branches[i]});
|
|
}
|
|
continue;
|
|
}
|
|
var deleted = !!tree[1].deleted;
|
|
var id = tree[0];
|
|
// sort by deleted, then pos, then id
|
|
if (!winningId || (winningDeleted !== deleted ? winningDeleted :
|
|
winningPos !== pos ? winningPos < pos : winningId < id)) {
|
|
winningId = id;
|
|
winningPos = pos;
|
|
winningDeleted = deleted;
|
|
}
|
|
}
|
|
|
|
return winningPos + '-' + winningId;
|
|
}
|
|
|
|
// Pretty much all below can be combined into a higher order function to
|
|
// traverse revisions
|
|
// The return value from the callback will be passed as context to all
|
|
// children of that node
|
|
function traverseRevTree(revs, callback) {
|
|
var toVisit = revs.slice();
|
|
|
|
var node;
|
|
while ((node = toVisit.pop())) {
|
|
var pos = node.pos;
|
|
var tree = node.ids;
|
|
var branches = tree[2];
|
|
var newCtx =
|
|
callback(branches.length === 0, pos, tree[0], node.ctx, tree[1]);
|
|
for (var i = 0, len = branches.length; i < len; i++) {
|
|
toVisit.push({pos: pos + 1, ids: branches[i], ctx: newCtx});
|
|
}
|
|
}
|
|
}
|
|
|
|
function sortByPos(a, b) {
|
|
return a.pos - b.pos;
|
|
}
|
|
|
|
function collectLeaves(revs) {
|
|
var leaves = [];
|
|
traverseRevTree(revs, function (isLeaf, pos, id, acc, opts) {
|
|
if (isLeaf) {
|
|
leaves.push({rev: pos + "-" + id, pos: pos, opts: opts});
|
|
}
|
|
});
|
|
leaves.sort(sortByPos).reverse();
|
|
for (var i = 0, len = leaves.length; i < len; i++) {
|
|
delete leaves[i].pos;
|
|
}
|
|
return leaves;
|
|
}
|
|
|
|
// returns revs of all conflicts that is leaves such that
|
|
// 1. are not deleted and
|
|
// 2. are different than winning revision
|
|
function collectConflicts(metadata) {
|
|
var win = winningRev(metadata);
|
|
var leaves = collectLeaves(metadata.rev_tree);
|
|
var conflicts = [];
|
|
for (var i = 0, len = leaves.length; i < len; i++) {
|
|
var leaf = leaves[i];
|
|
if (leaf.rev !== win && !leaf.opts.deleted) {
|
|
conflicts.push(leaf.rev);
|
|
}
|
|
}
|
|
return conflicts;
|
|
}
|
|
|
|
// compact a tree by marking its non-leafs as missing,
|
|
// and return a list of revs to delete
|
|
function compactTree(metadata) {
|
|
var revs = [];
|
|
traverseRevTree(metadata.rev_tree, function (isLeaf, pos,
|
|
revHash, ctx, opts) {
|
|
if (opts.status === 'available' && !isLeaf) {
|
|
revs.push(pos + '-' + revHash);
|
|
opts.status = 'missing';
|
|
}
|
|
});
|
|
return revs;
|
|
}
|
|
|
|
// build up a list of all the paths to the leafs in this revision tree
|
|
function rootToLeaf(revs) {
|
|
var paths = [];
|
|
var toVisit = revs.slice();
|
|
var node;
|
|
while ((node = toVisit.pop())) {
|
|
var pos = node.pos;
|
|
var tree = node.ids;
|
|
var id = tree[0];
|
|
var opts = tree[1];
|
|
var branches = tree[2];
|
|
var isLeaf = branches.length === 0;
|
|
|
|
var history = node.history ? node.history.slice() : [];
|
|
history.push({id: id, opts: opts});
|
|
if (isLeaf) {
|
|
paths.push({pos: (pos + 1 - history.length), ids: history});
|
|
}
|
|
for (var i = 0, len = branches.length; i < len; i++) {
|
|
toVisit.push({pos: pos + 1, ids: branches[i], history: history});
|
|
}
|
|
}
|
|
return paths.reverse();
|
|
}
|
|
|
|
function sortByPos$1(a, b) {
|
|
return a.pos - b.pos;
|
|
}
|
|
|
|
// classic binary search
|
|
function binarySearch(arr, item, comparator) {
|
|
var low = 0;
|
|
var high = arr.length;
|
|
var mid;
|
|
while (low < high) {
|
|
mid = (low + high) >>> 1;
|
|
if (comparator(arr[mid], item) < 0) {
|
|
low = mid + 1;
|
|
} else {
|
|
high = mid;
|
|
}
|
|
}
|
|
return low;
|
|
}
|
|
|
|
// assuming the arr is sorted, insert the item in the proper place
|
|
function insertSorted(arr, item, comparator) {
|
|
var idx = binarySearch(arr, item, comparator);
|
|
arr.splice(idx, 0, item);
|
|
}
|
|
|
|
// Turn a path as a flat array into a tree with a single branch.
|
|
// If any should be stemmed from the beginning of the array, that's passed
|
|
// in as the second argument
|
|
function pathToTree(path, numStemmed) {
|
|
var root;
|
|
var leaf;
|
|
for (var i = numStemmed, len = path.length; i < len; i++) {
|
|
var node = path[i];
|
|
var currentLeaf = [node.id, node.opts, []];
|
|
if (leaf) {
|
|
leaf[2].push(currentLeaf);
|
|
leaf = currentLeaf;
|
|
} else {
|
|
root = leaf = currentLeaf;
|
|
}
|
|
}
|
|
return root;
|
|
}
|
|
|
|
// compare the IDs of two trees
|
|
function compareTree(a, b) {
|
|
return a[0] < b[0] ? -1 : 1;
|
|
}
|
|
|
|
// Merge two trees together
|
|
// The roots of tree1 and tree2 must be the same revision
|
|
function mergeTree(in_tree1, in_tree2) {
|
|
var queue = [{tree1: in_tree1, tree2: in_tree2}];
|
|
var conflicts = false;
|
|
while (queue.length > 0) {
|
|
var item = queue.pop();
|
|
var tree1 = item.tree1;
|
|
var tree2 = item.tree2;
|
|
|
|
if (tree1[1].status || tree2[1].status) {
|
|
tree1[1].status =
|
|
(tree1[1].status === 'available' ||
|
|
tree2[1].status === 'available') ? 'available' : 'missing';
|
|
}
|
|
|
|
for (var i = 0; i < tree2[2].length; i++) {
|
|
if (!tree1[2][0]) {
|
|
conflicts = 'new_leaf';
|
|
tree1[2][0] = tree2[2][i];
|
|
continue;
|
|
}
|
|
|
|
var merged = false;
|
|
for (var j = 0; j < tree1[2].length; j++) {
|
|
if (tree1[2][j][0] === tree2[2][i][0]) {
|
|
queue.push({tree1: tree1[2][j], tree2: tree2[2][i]});
|
|
merged = true;
|
|
}
|
|
}
|
|
if (!merged) {
|
|
conflicts = 'new_branch';
|
|
insertSorted(tree1[2], tree2[2][i], compareTree);
|
|
}
|
|
}
|
|
}
|
|
return {conflicts: conflicts, tree: in_tree1};
|
|
}
|
|
|
|
function doMerge(tree, path, dontExpand) {
|
|
var restree = [];
|
|
var conflicts = false;
|
|
var merged = false;
|
|
var res;
|
|
|
|
if (!tree.length) {
|
|
return {tree: [path], conflicts: 'new_leaf'};
|
|
}
|
|
|
|
for (var i = 0, len = tree.length; i < len; i++) {
|
|
var branch = tree[i];
|
|
if (branch.pos === path.pos && branch.ids[0] === path.ids[0]) {
|
|
// Paths start at the same position and have the same root, so they need
|
|
// merged
|
|
res = mergeTree(branch.ids, path.ids);
|
|
restree.push({pos: branch.pos, ids: res.tree});
|
|
conflicts = conflicts || res.conflicts;
|
|
merged = true;
|
|
} else if (dontExpand !== true) {
|
|
// The paths start at a different position, take the earliest path and
|
|
// traverse up until it as at the same point from root as the path we
|
|
// want to merge. If the keys match we return the longer path with the
|
|
// other merged After stemming we dont want to expand the trees
|
|
|
|
var t1 = branch.pos < path.pos ? branch : path;
|
|
var t2 = branch.pos < path.pos ? path : branch;
|
|
var diff = t2.pos - t1.pos;
|
|
|
|
var candidateParents = [];
|
|
|
|
var trees = [];
|
|
trees.push({ids: t1.ids, diff: diff, parent: null, parentIdx: null});
|
|
while (trees.length > 0) {
|
|
var item = trees.pop();
|
|
if (item.diff === 0) {
|
|
if (item.ids[0] === t2.ids[0]) {
|
|
candidateParents.push(item);
|
|
}
|
|
continue;
|
|
}
|
|
var elements = item.ids[2];
|
|
for (var j = 0, elementsLen = elements.length; j < elementsLen; j++) {
|
|
trees.push({
|
|
ids: elements[j],
|
|
diff: item.diff - 1,
|
|
parent: item.ids,
|
|
parentIdx: j
|
|
});
|
|
}
|
|
}
|
|
|
|
var el = candidateParents[0];
|
|
|
|
if (!el) {
|
|
restree.push(branch);
|
|
} else {
|
|
res = mergeTree(el.ids, t2.ids);
|
|
el.parent[2][el.parentIdx] = res.tree;
|
|
restree.push({pos: t1.pos, ids: t1.ids});
|
|
conflicts = conflicts || res.conflicts;
|
|
merged = true;
|
|
}
|
|
} else {
|
|
restree.push(branch);
|
|
}
|
|
}
|
|
|
|
// We didnt find
|
|
if (!merged) {
|
|
restree.push(path);
|
|
}
|
|
|
|
restree.sort(sortByPos$1);
|
|
|
|
return {
|
|
tree: restree,
|
|
conflicts: conflicts || 'internal_node'
|
|
};
|
|
}
|
|
|
|
// To ensure we dont grow the revision tree infinitely, we stem old revisions
|
|
function stem(tree, depth) {
|
|
// First we break out the tree into a complete list of root to leaf paths
|
|
var paths = rootToLeaf(tree);
|
|
var maybeStem = {};
|
|
|
|
var result;
|
|
for (var i = 0, len = paths.length; i < len; i++) {
|
|
// Then for each path, we cut off the start of the path based on the
|
|
// `depth` to stem to, and generate a new set of flat trees
|
|
var path = paths[i];
|
|
var stemmed = path.ids;
|
|
var numStemmed = Math.max(0, stemmed.length - depth);
|
|
var stemmedNode = {
|
|
pos: path.pos + numStemmed,
|
|
ids: pathToTree(stemmed, numStemmed)
|
|
};
|
|
|
|
for (var s = 0; s < numStemmed; s++) {
|
|
var rev = (path.pos + s) + '-' + stemmed[s].id;
|
|
maybeStem[rev] = true;
|
|
}
|
|
|
|
// Then we remerge all those flat trees together, ensuring that we dont
|
|
// connect trees that would go beyond the depth limit
|
|
if (result) {
|
|
result = doMerge(result, stemmedNode, true).tree;
|
|
} else {
|
|
result = [stemmedNode];
|
|
}
|
|
}
|
|
|
|
traverseRevTree(result, function (isLeaf, pos, revHash) {
|
|
// some revisions may have been removed in a branch but not in another
|
|
delete maybeStem[pos + '-' + revHash];
|
|
});
|
|
|
|
return {
|
|
tree: result,
|
|
revs: Object.keys(maybeStem)
|
|
};
|
|
}
|
|
|
|
function merge(tree, path, depth) {
|
|
var newTree = doMerge(tree, path);
|
|
var stemmed = stem(newTree.tree, depth);
|
|
return {
|
|
tree: stemmed.tree,
|
|
stemmedRevs: stemmed.revs,
|
|
conflicts: newTree.conflicts
|
|
};
|
|
}
|
|
|
|
// return true if a rev exists in the rev tree, false otherwise
|
|
function revExists(revs, rev) {
|
|
var toVisit = revs.slice();
|
|
var splitRev = rev.split('-');
|
|
var targetPos = parseInt(splitRev[0], 10);
|
|
var targetId = splitRev[1];
|
|
|
|
var node;
|
|
while ((node = toVisit.pop())) {
|
|
if (node.pos === targetPos && node.ids[0] === targetId) {
|
|
return true;
|
|
}
|
|
var branches = node.ids[2];
|
|
for (var i = 0, len = branches.length; i < len; i++) {
|
|
toVisit.push({pos: node.pos + 1, ids: branches[i]});
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function getTrees(node) {
|
|
return node.ids;
|
|
}
|
|
|
|
// check if a specific revision of a doc has been deleted
|
|
// - metadata: the metadata object from the doc store
|
|
// - rev: (optional) the revision to check. defaults to winning revision
|
|
function isDeleted(metadata, rev) {
|
|
if (!rev) {
|
|
rev = winningRev(metadata);
|
|
}
|
|
var id = rev.substring(rev.indexOf('-') + 1);
|
|
var toVisit = metadata.rev_tree.map(getTrees);
|
|
|
|
var tree;
|
|
while ((tree = toVisit.pop())) {
|
|
if (tree[0] === id) {
|
|
return !!tree[1].deleted;
|
|
}
|
|
toVisit = toVisit.concat(tree[2]);
|
|
}
|
|
}
|
|
|
|
function isLocalId(id) {
|
|
return (/^_local/).test(id);
|
|
}
|
|
|
|
var atob$1 = function (str) {
|
|
return atob(str);
|
|
};
|
|
|
|
var btoa$1 = function (str) {
|
|
return btoa(str);
|
|
};
|
|
|
|
// Abstracts constructing a Blob object, so it also works in older
|
|
// browsers that don't support the native Blob constructor (e.g.
|
|
// old QtWebKit versions, Android < 4.4).
|
|
function createBlob(parts, properties) {
|
|
/* global BlobBuilder,MSBlobBuilder,MozBlobBuilder,WebKitBlobBuilder */
|
|
parts = parts || [];
|
|
properties = properties || {};
|
|
try {
|
|
return new Blob(parts, properties);
|
|
} catch (e) {
|
|
if (e.name !== "TypeError") {
|
|
throw e;
|
|
}
|
|
var Builder = typeof BlobBuilder !== 'undefined' ? BlobBuilder :
|
|
typeof MSBlobBuilder !== 'undefined' ? MSBlobBuilder :
|
|
typeof MozBlobBuilder !== 'undefined' ? MozBlobBuilder :
|
|
WebKitBlobBuilder;
|
|
var builder = new Builder();
|
|
for (var i = 0; i < parts.length; i += 1) {
|
|
builder.append(parts[i]);
|
|
}
|
|
return builder.getBlob(properties.type);
|
|
}
|
|
}
|
|
|
|
// From http://stackoverflow.com/questions/14967647/ (continues on next line)
|
|
// encode-decode-image-with-base64-breaks-image (2013-04-21)
|
|
function binaryStringToArrayBuffer(bin) {
|
|
var length = bin.length;
|
|
var buf = new ArrayBuffer(length);
|
|
var arr = new Uint8Array(buf);
|
|
for (var i = 0; i < length; i++) {
|
|
arr[i] = bin.charCodeAt(i);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
function binStringToBluffer(binString, type) {
|
|
return createBlob([binaryStringToArrayBuffer(binString)], {type: type});
|
|
}
|
|
|
|
//Can't find original post, but this is close
|
|
//http://stackoverflow.com/questions/6965107/ (continues on next line)
|
|
//converting-between-strings-and-arraybuffers
|
|
function arrayBufferToBinaryString(buffer) {
|
|
var binary = '';
|
|
var bytes = new Uint8Array(buffer);
|
|
var length = bytes.byteLength;
|
|
for (var i = 0; i < length; i++) {
|
|
binary += String.fromCharCode(bytes[i]);
|
|
}
|
|
return binary;
|
|
}
|
|
|
|
// shim for browsers that don't support it
|
|
function readAsBinaryString(blob, callback) {
|
|
if (typeof FileReader === 'undefined') {
|
|
// fix for Firefox in a web worker
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=901097
|
|
return callback(arrayBufferToBinaryString(
|
|
new FileReaderSync().readAsArrayBuffer(blob)));
|
|
}
|
|
|
|
var reader = new FileReader();
|
|
var hasBinaryString = typeof reader.readAsBinaryString === 'function';
|
|
reader.onloadend = function (e) {
|
|
var result = e.target.result || '';
|
|
if (hasBinaryString) {
|
|
return callback(result);
|
|
}
|
|
callback(arrayBufferToBinaryString(result));
|
|
};
|
|
if (hasBinaryString) {
|
|
reader.readAsBinaryString(blob);
|
|
} else {
|
|
reader.readAsArrayBuffer(blob);
|
|
}
|
|
}
|
|
|
|
// simplified API. universal browser support is assumed
|
|
function readAsArrayBuffer(blob, callback) {
|
|
if (typeof FileReader === 'undefined') {
|
|
// fix for Firefox in a web worker:
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=901097
|
|
return callback(new FileReaderSync().readAsArrayBuffer(blob));
|
|
}
|
|
|
|
var reader = new FileReader();
|
|
reader.onloadend = function (e) {
|
|
var result = e.target.result || new ArrayBuffer(0);
|
|
callback(result);
|
|
};
|
|
reader.readAsArrayBuffer(blob);
|
|
}
|
|
|
|
var setImmediateShim = global.setImmediate || global.setTimeout;
|
|
var MD5_CHUNK_SIZE = 32768;
|
|
|
|
function rawToBase64(raw) {
|
|
return btoa$1(raw);
|
|
}
|
|
|
|
function sliceBlob(blob, start, end) {
|
|
if (blob.webkitSlice) {
|
|
return blob.webkitSlice(start, end);
|
|
}
|
|
return blob.slice(start, end);
|
|
}
|
|
|
|
function appendBlob(buffer, blob, start, end, callback) {
|
|
if (start > 0 || end < blob.size) {
|
|
// only slice blob if we really need to
|
|
blob = sliceBlob(blob, start, end);
|
|
}
|
|
readAsArrayBuffer(blob, function (arrayBuffer) {
|
|
buffer.append(arrayBuffer);
|
|
callback();
|
|
});
|
|
}
|
|
|
|
function appendString(buffer, string, start, end, callback) {
|
|
if (start > 0 || end < string.length) {
|
|
// only create a substring if we really need to
|
|
string = string.substring(start, end);
|
|
}
|
|
buffer.appendBinary(string);
|
|
callback();
|
|
}
|
|
|
|
function binaryMd5(data, callback) {
|
|
var inputIsString = typeof data === 'string';
|
|
var len = inputIsString ? data.length : data.size;
|
|
var chunkSize = Math.min(MD5_CHUNK_SIZE, len);
|
|
var chunks = Math.ceil(len / chunkSize);
|
|
var currentChunk = 0;
|
|
var buffer = inputIsString ? new Md5() : new Md5.ArrayBuffer();
|
|
|
|
var append = inputIsString ? appendString : appendBlob;
|
|
|
|
function next() {
|
|
setImmediateShim(loadNextChunk);
|
|
}
|
|
|
|
function done() {
|
|
var raw = buffer.end(true);
|
|
var base64 = rawToBase64(raw);
|
|
callback(base64);
|
|
buffer.destroy();
|
|
}
|
|
|
|
function loadNextChunk() {
|
|
var start = currentChunk * chunkSize;
|
|
var end = start + chunkSize;
|
|
currentChunk++;
|
|
if (currentChunk < chunks) {
|
|
append(buffer, data, start, end, next);
|
|
} else {
|
|
append(buffer, data, start, end, done);
|
|
}
|
|
}
|
|
loadNextChunk();
|
|
}
|
|
|
|
function updateDoc(revLimit, prev, docInfo, results,
|
|
i, cb, writeDoc, newEdits) {
|
|
|
|
if (revExists(prev.rev_tree, docInfo.metadata.rev)) {
|
|
results[i] = docInfo;
|
|
return cb();
|
|
}
|
|
|
|
// sometimes this is pre-calculated. historically not always
|
|
var previousWinningRev = prev.winningRev || winningRev(prev);
|
|
var previouslyDeleted = 'deleted' in prev ? prev.deleted :
|
|
isDeleted(prev, previousWinningRev);
|
|
var deleted = 'deleted' in docInfo.metadata ? docInfo.metadata.deleted :
|
|
isDeleted(docInfo.metadata);
|
|
var isRoot = /^1-/.test(docInfo.metadata.rev);
|
|
|
|
if (previouslyDeleted && !deleted && newEdits && isRoot) {
|
|
var newDoc = docInfo.data;
|
|
newDoc._rev = previousWinningRev;
|
|
newDoc._id = docInfo.metadata.id;
|
|
docInfo = parseDoc(newDoc, newEdits);
|
|
}
|
|
|
|
var merged = merge(prev.rev_tree, docInfo.metadata.rev_tree[0], revLimit);
|
|
|
|
var inConflict = newEdits && (((previouslyDeleted && deleted) ||
|
|
(!previouslyDeleted && merged.conflicts !== 'new_leaf') ||
|
|
(previouslyDeleted && !deleted && merged.conflicts === 'new_branch')));
|
|
|
|
if (inConflict) {
|
|
var err = createError(REV_CONFLICT);
|
|
results[i] = err;
|
|
return cb();
|
|
}
|
|
|
|
var newRev = docInfo.metadata.rev;
|
|
docInfo.metadata.rev_tree = merged.tree;
|
|
docInfo.stemmedRevs = merged.stemmedRevs || [];
|
|
/* istanbul ignore else */
|
|
if (prev.rev_map) {
|
|
docInfo.metadata.rev_map = prev.rev_map; // used only by leveldb
|
|
}
|
|
|
|
// recalculate
|
|
var winningRev$$ = winningRev(docInfo.metadata);
|
|
var winningRevIsDeleted = isDeleted(docInfo.metadata, winningRev$$);
|
|
|
|
// calculate the total number of documents that were added/removed,
|
|
// from the perspective of total_rows/doc_count
|
|
var delta = (previouslyDeleted === winningRevIsDeleted) ? 0 :
|
|
previouslyDeleted < winningRevIsDeleted ? -1 : 1;
|
|
|
|
var newRevIsDeleted;
|
|
if (newRev === winningRev$$) {
|
|
// if the new rev is the same as the winning rev, we can reuse that value
|
|
newRevIsDeleted = winningRevIsDeleted;
|
|
} else {
|
|
// if they're not the same, then we need to recalculate
|
|
newRevIsDeleted = isDeleted(docInfo.metadata, newRev);
|
|
}
|
|
|
|
writeDoc(docInfo, winningRev$$, winningRevIsDeleted, newRevIsDeleted,
|
|
true, delta, i, cb);
|
|
}
|
|
|
|
function rootIsMissing(docInfo) {
|
|
return docInfo.metadata.rev_tree[0].ids[1].status === 'missing';
|
|
}
|
|
|
|
function processDocs(revLimit, docInfos, api, fetchedDocs, tx, results,
|
|
writeDoc, opts, overallCallback) {
|
|
|
|
// Default to 1000 locally
|
|
revLimit = revLimit || 1000;
|
|
|
|
function insertDoc(docInfo, resultsIdx, callback) {
|
|
// Cant insert new deleted documents
|
|
var winningRev$$ = winningRev(docInfo.metadata);
|
|
var deleted = isDeleted(docInfo.metadata, winningRev$$);
|
|
if ('was_delete' in opts && deleted) {
|
|
results[resultsIdx] = createError(MISSING_DOC, 'deleted');
|
|
return callback();
|
|
}
|
|
|
|
// 4712 - detect whether a new document was inserted with a _rev
|
|
var inConflict = newEdits && rootIsMissing(docInfo);
|
|
|
|
if (inConflict) {
|
|
var err = createError(REV_CONFLICT);
|
|
results[resultsIdx] = err;
|
|
return callback();
|
|
}
|
|
|
|
var delta = deleted ? 0 : 1;
|
|
|
|
writeDoc(docInfo, winningRev$$, deleted, deleted, false,
|
|
delta, resultsIdx, callback);
|
|
}
|
|
|
|
var newEdits = opts.new_edits;
|
|
var idsToDocs = new pouchdbCollections.Map();
|
|
|
|
var docsDone = 0;
|
|
var docsToDo = docInfos.length;
|
|
|
|
function checkAllDocsDone() {
|
|
if (++docsDone === docsToDo && overallCallback) {
|
|
overallCallback();
|
|
}
|
|
}
|
|
|
|
docInfos.forEach(function (currentDoc, resultsIdx) {
|
|
|
|
if (currentDoc._id && isLocalId(currentDoc._id)) {
|
|
var fun = currentDoc._deleted ? '_removeLocal' : '_putLocal';
|
|
api[fun](currentDoc, {ctx: tx}, function (err, res) {
|
|
results[resultsIdx] = err || res;
|
|
checkAllDocsDone();
|
|
});
|
|
return;
|
|
}
|
|
|
|
var id = currentDoc.metadata.id;
|
|
if (idsToDocs.has(id)) {
|
|
docsToDo--; // duplicate
|
|
idsToDocs.get(id).push([currentDoc, resultsIdx]);
|
|
} else {
|
|
idsToDocs.set(id, [[currentDoc, resultsIdx]]);
|
|
}
|
|
});
|
|
|
|
// in the case of new_edits, the user can provide multiple docs
|
|
// with the same id. these need to be processed sequentially
|
|
idsToDocs.forEach(function (docs, id) {
|
|
var numDone = 0;
|
|
|
|
function docWritten() {
|
|
if (++numDone < docs.length) {
|
|
nextDoc();
|
|
} else {
|
|
checkAllDocsDone();
|
|
}
|
|
}
|
|
function nextDoc() {
|
|
var value = docs[numDone];
|
|
var currentDoc = value[0];
|
|
var resultsIdx = value[1];
|
|
|
|
if (fetchedDocs.has(id)) {
|
|
updateDoc(revLimit, fetchedDocs.get(id), currentDoc, results,
|
|
resultsIdx, docWritten, writeDoc, newEdits);
|
|
} else {
|
|
// Ensure stemming applies to new writes as well
|
|
var merged = merge([], currentDoc.metadata.rev_tree[0], revLimit);
|
|
currentDoc.metadata.rev_tree = merged.tree;
|
|
currentDoc.stemmedRevs = merged.stemmedRevs || [];
|
|
insertDoc(currentDoc, resultsIdx, docWritten);
|
|
}
|
|
}
|
|
nextDoc();
|
|
});
|
|
}
|
|
|
|
function slowJsonParse(str) {
|
|
try {
|
|
return JSON.parse(str);
|
|
} catch (e) {
|
|
/* istanbul ignore next */
|
|
return vuvuzela.parse(str);
|
|
}
|
|
}
|
|
|
|
function safeJsonParse(str) {
|
|
// try/catch is deoptimized in V8, leading to slower
|
|
// times than we'd like to have. Most documents are _not_
|
|
// huge, and do not require a slower code path just to parse them.
|
|
// We can be pretty sure that a document under 50000 characters
|
|
// will not be so deeply nested as to throw a stack overflow error
|
|
// (depends on the engine and available memory, though, so this is
|
|
// just a hunch). 50000 was chosen based on the average length
|
|
// of this string in our test suite, to try to find a number that covers
|
|
// most of our test cases (26 over this size, 26378 under it).
|
|
if (str.length < 50000) {
|
|
return JSON.parse(str);
|
|
}
|
|
return slowJsonParse(str);
|
|
}
|
|
|
|
function safeJsonStringify(json) {
|
|
try {
|
|
return JSON.stringify(json);
|
|
} catch (e) {
|
|
/* istanbul ignore next */
|
|
return vuvuzela.stringify(json);
|
|
}
|
|
}
|
|
|
|
function readAsBlobOrBuffer(storedObject, type) {
|
|
// In the browser, we've stored a binary string. This now comes back as a
|
|
// browserified Node-style Buffer (implemented as a typed array),
|
|
// but we want a Blob instead.
|
|
var byteArray = new Uint8Array(storedObject);
|
|
return createBlob([byteArray], {type: type});
|
|
}
|
|
|
|
// In the browser, we store a binary string
|
|
function prepareAttachmentForStorage(attData, cb) {
|
|
readAsBinaryString(attData, cb);
|
|
}
|
|
|
|
function createEmptyBlobOrBuffer(type) {
|
|
return createBlob([''], {type: type});
|
|
}
|
|
|
|
function getCacheFor(transaction, store) {
|
|
var prefix = store.prefix()[0];
|
|
var cache = transaction._cache;
|
|
var subCache = cache.get(prefix);
|
|
if (!subCache) {
|
|
subCache = new pouchdbCollections.Map();
|
|
cache.set(prefix, subCache);
|
|
}
|
|
return subCache;
|
|
}
|
|
|
|
function LevelTransaction() {
|
|
this._batch = [];
|
|
this._cache = new pouchdbCollections.Map();
|
|
}
|
|
|
|
LevelTransaction.prototype.get = function (store, key, callback) {
|
|
var cache = getCacheFor(this, store);
|
|
var exists = cache.get(key);
|
|
if (exists) {
|
|
return process.nextTick(function () {
|
|
callback(null, exists);
|
|
});
|
|
} else if (exists === null) { // deleted marker
|
|
/* istanbul ignore next */
|
|
return process.nextTick(function () {
|
|
callback({name: 'NotFoundError'});
|
|
});
|
|
}
|
|
store.get(key, function (err, res) {
|
|
if (err) {
|
|
/* istanbul ignore else */
|
|
if (err.name === 'NotFoundError') {
|
|
cache.set(key, null);
|
|
}
|
|
return callback(err);
|
|
}
|
|
cache.set(key, res);
|
|
callback(null, res);
|
|
});
|
|
};
|
|
|
|
LevelTransaction.prototype.batch = function (batch) {
|
|
for (var i = 0, len = batch.length; i < len; i++) {
|
|
var operation = batch[i];
|
|
|
|
var cache = getCacheFor(this, operation.prefix);
|
|
|
|
if (operation.type === 'put') {
|
|
cache.set(operation.key, operation.value);
|
|
} else {
|
|
cache.set(operation.key, null);
|
|
}
|
|
}
|
|
this._batch = this._batch.concat(batch);
|
|
};
|
|
|
|
LevelTransaction.prototype.execute = function (db, callback) {
|
|
|
|
var keys = new pouchdbCollections.Set();
|
|
var uniqBatches = [];
|
|
|
|
// remove duplicates; last one wins
|
|
for (var i = this._batch.length - 1; i >= 0; i--) {
|
|
var operation = this._batch[i];
|
|
var lookupKey = operation.prefix.prefix()[0] + '\xff' + operation.key;
|
|
if (keys.has(lookupKey)) {
|
|
continue;
|
|
}
|
|
keys.add(lookupKey);
|
|
uniqBatches.push(operation);
|
|
}
|
|
|
|
db.batch(uniqBatches, callback);
|
|
};
|
|
|
|
var DOC_STORE = 'document-store';
|
|
var BY_SEQ_STORE = 'by-sequence';
|
|
var ATTACHMENT_STORE = 'attach-store';
|
|
var BINARY_STORE = 'attach-binary-store';
|
|
var LOCAL_STORE = 'local-store';
|
|
var META_STORE = 'meta-store';
|
|
|
|
// leveldb barks if we try to open a db multiple times
|
|
// so we cache opened connections here for initstore()
|
|
var dbStores = new pouchdbCollections.Map();
|
|
|
|
// store the value of update_seq in the by-sequence store the key name will
|
|
// never conflict, since the keys in the by-sequence store are integers
|
|
var UPDATE_SEQ_KEY = '_local_last_update_seq';
|
|
var DOC_COUNT_KEY = '_local_doc_count';
|
|
var UUID_KEY = '_local_uuid';
|
|
|
|
var MD5_PREFIX = 'md5-';
|
|
|
|
var safeJsonEncoding = {
|
|
encode: safeJsonStringify,
|
|
decode: safeJsonParse,
|
|
buffer: false,
|
|
type: 'cheap-json'
|
|
};
|
|
|
|
var levelChanges = new Changes();
|
|
|
|
// winningRev and deleted are performance-killers, but
|
|
// in newer versions of PouchDB, they are cached on the metadata
|
|
function getWinningRev(metadata) {
|
|
return 'winningRev' in metadata ?
|
|
metadata.winningRev : winningRev(metadata);
|
|
}
|
|
|
|
function getIsDeleted(metadata, winningRev) {
|
|
return 'deleted' in metadata ?
|
|
metadata.deleted : isDeleted(metadata, winningRev);
|
|
}
|
|
|
|
function fetchAttachment(att, stores, opts) {
|
|
var type = att.content_type;
|
|
return new PouchPromise(function (resolve, reject) {
|
|
stores.binaryStore.get(att.digest, function (err, buffer) {
|
|
var data;
|
|
if (err) {
|
|
/* istanbul ignore if */
|
|
if (err.name !== 'NotFoundError') {
|
|
return reject(err);
|
|
} else {
|
|
// empty
|
|
if (!opts.binary) {
|
|
data = '';
|
|
} else {
|
|
data = binStringToBluffer('', type);
|
|
}
|
|
}
|
|
} else { // non-empty
|
|
if (opts.binary) {
|
|
data = readAsBlobOrBuffer(buffer, type);
|
|
} else {
|
|
data = buffer.toString('base64');
|
|
}
|
|
}
|
|
delete att.stub;
|
|
delete att.length;
|
|
att.data = data;
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
function fetchAttachments(results, stores, opts) {
|
|
var atts = [];
|
|
results.forEach(function (row) {
|
|
if (!(row.doc && row.doc._attachments)) {
|
|
return;
|
|
}
|
|
var attNames = Object.keys(row.doc._attachments);
|
|
attNames.forEach(function (attName) {
|
|
var att = row.doc._attachments[attName];
|
|
if (!('data' in att)) {
|
|
atts.push(att);
|
|
}
|
|
});
|
|
});
|
|
|
|
return PouchPromise.all(atts.map(function (att) {
|
|
return fetchAttachment(att, stores, opts);
|
|
}));
|
|
}
|
|
|
|
function LevelPouch(opts, callback) {
|
|
opts = clone(opts);
|
|
var api = this;
|
|
var instanceId;
|
|
var stores = {};
|
|
var revLimit = opts.revs_limit;
|
|
var db;
|
|
var name = opts.name;
|
|
// TODO: this is undocumented and unused probably
|
|
/* istanbul ignore else */
|
|
if (typeof opts.createIfMissing === 'undefined') {
|
|
opts.createIfMissing = true;
|
|
}
|
|
|
|
var leveldown = opts.db;
|
|
|
|
var dbStore;
|
|
var leveldownName = functionName(leveldown);
|
|
if (dbStores.has(leveldownName)) {
|
|
dbStore = dbStores.get(leveldownName);
|
|
} else {
|
|
dbStore = new pouchdbCollections.Map();
|
|
dbStores.set(leveldownName, dbStore);
|
|
}
|
|
if (dbStore.has(name)) {
|
|
db = dbStore.get(name);
|
|
afterDBCreated();
|
|
} else {
|
|
dbStore.set(name, sublevel(levelup(name, opts, function (err) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
dbStore.delete(name);
|
|
return callback(err);
|
|
}
|
|
db = dbStore.get(name);
|
|
db._docCount = -1;
|
|
db._queue = new Deque();
|
|
/* istanbul ignore else */
|
|
if (opts.migrate) { // migration for leveldown
|
|
migrate.toSublevel(name, db, afterDBCreated);
|
|
} else {
|
|
afterDBCreated();
|
|
}
|
|
})));
|
|
}
|
|
|
|
function afterDBCreated() {
|
|
stores.docStore = db.sublevel(DOC_STORE, {valueEncoding: safeJsonEncoding});
|
|
stores.bySeqStore = db.sublevel(BY_SEQ_STORE, {valueEncoding: 'json'});
|
|
stores.attachmentStore =
|
|
db.sublevel(ATTACHMENT_STORE, {valueEncoding: 'json'});
|
|
stores.binaryStore = db.sublevel(BINARY_STORE, {valueEncoding: 'binary'});
|
|
stores.localStore = db.sublevel(LOCAL_STORE, {valueEncoding: 'json'});
|
|
stores.metaStore = db.sublevel(META_STORE, {valueEncoding: 'json'});
|
|
migrate.localAndMetaStores(db, stores, function () {
|
|
stores.metaStore.get(UPDATE_SEQ_KEY, function (err, value) {
|
|
if (typeof db._updateSeq === 'undefined') {
|
|
db._updateSeq = value || 0;
|
|
}
|
|
stores.metaStore.get(DOC_COUNT_KEY, function (err, value) {
|
|
db._docCount = !err ? value : 0;
|
|
stores.metaStore.get(UUID_KEY, function (err, value) {
|
|
instanceId = !err ? value : uuid();
|
|
stores.metaStore.put(UUID_KEY, instanceId, function () {
|
|
process.nextTick(function () {
|
|
callback(null, api);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
function countDocs(callback) {
|
|
/* istanbul ignore if */
|
|
if (db.isClosed()) {
|
|
return callback(new Error('database is closed'));
|
|
}
|
|
return callback(null, db._docCount); // use cached value
|
|
}
|
|
|
|
api.type = function () {
|
|
return 'leveldb';
|
|
};
|
|
|
|
api._id = function (callback) {
|
|
callback(null, instanceId);
|
|
};
|
|
|
|
api._info = function (callback) {
|
|
var res = {
|
|
doc_count: db._docCount,
|
|
update_seq: db._updateSeq,
|
|
backend_adapter: functionName(leveldown)
|
|
};
|
|
return process.nextTick(function () {
|
|
callback(null, res);
|
|
});
|
|
};
|
|
|
|
function tryCode(fun, args) {
|
|
try {
|
|
fun.apply(null, args);
|
|
} catch (err) {
|
|
args[args.length - 1](err);
|
|
}
|
|
}
|
|
|
|
function executeNext() {
|
|
var firstTask = db._queue.peekFront();
|
|
|
|
if (firstTask.type === 'read') {
|
|
runReadOperation(firstTask);
|
|
} else { // write, only do one at a time
|
|
runWriteOperation(firstTask);
|
|
}
|
|
}
|
|
|
|
function runReadOperation(firstTask) {
|
|
// do multiple reads at once simultaneously, because it's safe
|
|
|
|
var readTasks = [firstTask];
|
|
var i = 1;
|
|
var nextTask = db._queue.get(i);
|
|
while (typeof nextTask !== 'undefined' && nextTask.type === 'read') {
|
|
readTasks.push(nextTask);
|
|
i++;
|
|
nextTask = db._queue.get(i);
|
|
}
|
|
|
|
var numDone = 0;
|
|
|
|
readTasks.forEach(function (readTask) {
|
|
var args = readTask.args;
|
|
var callback = args[args.length - 1];
|
|
args[args.length - 1] = getArguments(function (cbArgs) {
|
|
callback.apply(null, cbArgs);
|
|
if (++numDone === readTasks.length) {
|
|
process.nextTick(function () {
|
|
// all read tasks have finished
|
|
readTasks.forEach(function () {
|
|
db._queue.shift();
|
|
});
|
|
if (db._queue.length) {
|
|
executeNext();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
tryCode(readTask.fun, args);
|
|
});
|
|
}
|
|
|
|
function runWriteOperation(firstTask) {
|
|
var args = firstTask.args;
|
|
var callback = args[args.length - 1];
|
|
args[args.length - 1] = getArguments(function (cbArgs) {
|
|
callback.apply(null, cbArgs);
|
|
process.nextTick(function () {
|
|
db._queue.shift();
|
|
if (db._queue.length) {
|
|
executeNext();
|
|
}
|
|
});
|
|
});
|
|
tryCode(firstTask.fun, args);
|
|
}
|
|
|
|
// all read/write operations to the database are done in a queue,
|
|
// similar to how websql/idb works. this avoids problems such
|
|
// as e.g. compaction needing to have a lock on the database while
|
|
// it updates stuff. in the future we can revisit this.
|
|
function writeLock(fun) {
|
|
return getArguments(function (args) {
|
|
db._queue.push({
|
|
fun: fun,
|
|
args: args,
|
|
type: 'write'
|
|
});
|
|
|
|
if (db._queue.length === 1) {
|
|
process.nextTick(executeNext);
|
|
}
|
|
});
|
|
}
|
|
|
|
// same as the writelock, but multiple can run at once
|
|
function readLock(fun) {
|
|
return getArguments(function (args) {
|
|
db._queue.push({
|
|
fun: fun,
|
|
args: args,
|
|
type: 'read'
|
|
});
|
|
|
|
if (db._queue.length === 1) {
|
|
process.nextTick(executeNext);
|
|
}
|
|
});
|
|
}
|
|
|
|
function formatSeq(n) {
|
|
return ('0000000000000000' + n).slice(-16);
|
|
}
|
|
|
|
function parseSeq(s) {
|
|
return parseInt(s, 10);
|
|
}
|
|
|
|
api._get = readLock(function (id, opts, callback) {
|
|
opts = clone(opts);
|
|
|
|
stores.docStore.get(id, function (err, metadata) {
|
|
|
|
if (err || !metadata) {
|
|
return callback(createError(MISSING_DOC, 'missing'));
|
|
}
|
|
|
|
var rev = getWinningRev(metadata);
|
|
var deleted = getIsDeleted(metadata, rev);
|
|
if (deleted && !opts.rev) {
|
|
return callback(createError(MISSING_DOC, "deleted"));
|
|
}
|
|
|
|
rev = opts.rev ? opts.rev : rev;
|
|
|
|
var seq = metadata.rev_map[rev];
|
|
|
|
stores.bySeqStore.get(formatSeq(seq), function (err, doc) {
|
|
if (!doc) {
|
|
return callback(createError(MISSING_DOC));
|
|
}
|
|
/* istanbul ignore if */
|
|
if ('_id' in doc && doc._id !== metadata.id) {
|
|
// this failing implies something very wrong
|
|
return callback(new Error('wrong doc returned'));
|
|
}
|
|
doc._id = metadata.id;
|
|
if ('_rev' in doc) {
|
|
/* istanbul ignore if */
|
|
if (doc._rev !== rev) {
|
|
// this failing implies something very wrong
|
|
return callback(new Error('wrong doc returned'));
|
|
}
|
|
} else {
|
|
// we didn't always store this
|
|
doc._rev = rev;
|
|
}
|
|
return callback(null, {doc: doc, metadata: metadata});
|
|
});
|
|
});
|
|
});
|
|
|
|
// not technically part of the spec, but if putAttachment has its own
|
|
// method...
|
|
api._getAttachment = function (docId, attachId, attachment, opts, callback) {
|
|
var digest = attachment.digest;
|
|
var type = attachment.content_type;
|
|
|
|
stores.binaryStore.get(digest, function (err, attach) {
|
|
if (err) {
|
|
/* istanbul ignore if */
|
|
if (err.name !== 'NotFoundError') {
|
|
return callback(err);
|
|
}
|
|
// Empty attachment
|
|
return callback(null, opts.binary ? createEmptyBlobOrBuffer(type) : '');
|
|
}
|
|
|
|
if (opts.binary) {
|
|
callback(null, readAsBlobOrBuffer(attach, type));
|
|
} else {
|
|
callback(null, attach.toString('base64'));
|
|
}
|
|
});
|
|
};
|
|
|
|
api._bulkDocs = writeLock(function (req, opts, callback) {
|
|
var newEdits = opts.new_edits;
|
|
var results = new Array(req.docs.length);
|
|
var fetchedDocs = new pouchdbCollections.Map();
|
|
var stemmedRevs = new pouchdbCollections.Map();
|
|
|
|
var txn = new LevelTransaction();
|
|
var docCountDelta = 0;
|
|
var newUpdateSeq = db._updateSeq;
|
|
|
|
// parse the docs and give each a sequence number
|
|
var userDocs = req.docs;
|
|
var docInfos = userDocs.map(function (doc) {
|
|
if (doc._id && isLocalId(doc._id)) {
|
|
return doc;
|
|
}
|
|
var newDoc = parseDoc(doc, newEdits);
|
|
|
|
if (newDoc.metadata && !newDoc.metadata.rev_map) {
|
|
newDoc.metadata.rev_map = {};
|
|
}
|
|
|
|
return newDoc;
|
|
});
|
|
var infoErrors = docInfos.filter(function (doc) {
|
|
return doc.error;
|
|
});
|
|
|
|
if (infoErrors.length) {
|
|
return callback(infoErrors[0]);
|
|
}
|
|
|
|
// verify any stub attachments as a precondition test
|
|
|
|
function verifyAttachment(digest, callback) {
|
|
txn.get(stores.attachmentStore, digest, function (levelErr) {
|
|
if (levelErr) {
|
|
var err = createError(MISSING_STUB,
|
|
'unknown stub attachment with digest ' +
|
|
digest);
|
|
callback(err);
|
|
} else {
|
|
callback();
|
|
}
|
|
});
|
|
}
|
|
|
|
function verifyAttachments(finish) {
|
|
var digests = [];
|
|
userDocs.forEach(function (doc) {
|
|
if (doc && doc._attachments) {
|
|
Object.keys(doc._attachments).forEach(function (filename) {
|
|
var att = doc._attachments[filename];
|
|
if (att.stub) {
|
|
digests.push(att.digest);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
if (!digests.length) {
|
|
return finish();
|
|
}
|
|
var numDone = 0;
|
|
var err;
|
|
|
|
digests.forEach(function (digest) {
|
|
verifyAttachment(digest, function (attErr) {
|
|
if (attErr && !err) {
|
|
err = attErr;
|
|
}
|
|
|
|
if (++numDone === digests.length) {
|
|
finish(err);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function fetchExistingDocs(finish) {
|
|
var numDone = 0;
|
|
var overallErr;
|
|
function checkDone() {
|
|
if (++numDone === userDocs.length) {
|
|
return finish(overallErr);
|
|
}
|
|
}
|
|
|
|
userDocs.forEach(function (doc) {
|
|
if (doc._id && isLocalId(doc._id)) {
|
|
// skip local docs
|
|
return checkDone();
|
|
}
|
|
txn.get(stores.docStore, doc._id, function (err, info) {
|
|
if (err) {
|
|
/* istanbul ignore if */
|
|
if (err.name !== 'NotFoundError') {
|
|
overallErr = err;
|
|
}
|
|
} else {
|
|
fetchedDocs.set(doc._id, info);
|
|
}
|
|
checkDone();
|
|
});
|
|
});
|
|
}
|
|
|
|
function compact(revsMap, callback) {
|
|
var promise = PouchPromise.resolve();
|
|
revsMap.forEach(function (revs, docId) {
|
|
// TODO: parallelize, for now need to be sequential to
|
|
// pass orphaned attachment tests
|
|
promise = promise.then(function () {
|
|
return new PouchPromise(function (resolve, reject) {
|
|
api._doCompactionNoLock(docId, revs, {ctx: txn}, function (err) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
resolve();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
promise.then(function () {
|
|
callback();
|
|
}, callback);
|
|
}
|
|
|
|
function autoCompact(callback) {
|
|
var revsMap = new pouchdbCollections.Map();
|
|
fetchedDocs.forEach(function (metadata, docId) {
|
|
revsMap.set(docId, compactTree(metadata));
|
|
});
|
|
compact(revsMap, callback);
|
|
}
|
|
|
|
function finish() {
|
|
compact(stemmedRevs, function (error) {
|
|
/* istanbul ignore if */
|
|
if (error) {
|
|
complete(error);
|
|
}
|
|
if (api.auto_compaction) {
|
|
return autoCompact(complete);
|
|
}
|
|
complete();
|
|
});
|
|
}
|
|
|
|
function writeDoc(docInfo, winningRev, winningRevIsDeleted, newRevIsDeleted,
|
|
isUpdate, delta, resultsIdx, callback2) {
|
|
docCountDelta += delta;
|
|
|
|
var err = null;
|
|
var recv = 0;
|
|
|
|
docInfo.metadata.winningRev = winningRev;
|
|
docInfo.metadata.deleted = winningRevIsDeleted;
|
|
|
|
docInfo.data._id = docInfo.metadata.id;
|
|
docInfo.data._rev = docInfo.metadata.rev;
|
|
|
|
if (newRevIsDeleted) {
|
|
docInfo.data._deleted = true;
|
|
}
|
|
|
|
if (docInfo.stemmedRevs.length) {
|
|
stemmedRevs.set(docInfo.metadata.id, docInfo.stemmedRevs);
|
|
}
|
|
|
|
var attachments = docInfo.data._attachments ?
|
|
Object.keys(docInfo.data._attachments) :
|
|
[];
|
|
|
|
function attachmentSaved(attachmentErr) {
|
|
recv++;
|
|
if (!err) {
|
|
/* istanbul ignore if */
|
|
if (attachmentErr) {
|
|
err = attachmentErr;
|
|
callback2(err);
|
|
} else if (recv === attachments.length) {
|
|
finish();
|
|
}
|
|
}
|
|
}
|
|
|
|
function onMD5Load(doc, key, data, attachmentSaved) {
|
|
return function (result) {
|
|
saveAttachment(doc, MD5_PREFIX + result, key, data, attachmentSaved);
|
|
};
|
|
}
|
|
|
|
function doMD5(doc, key, attachmentSaved) {
|
|
return function (data) {
|
|
binaryMd5(data, onMD5Load(doc, key, data, attachmentSaved));
|
|
};
|
|
}
|
|
|
|
for (var i = 0; i < attachments.length; i++) {
|
|
var key = attachments[i];
|
|
var att = docInfo.data._attachments[key];
|
|
|
|
if (att.stub) {
|
|
// still need to update the refs mapping
|
|
var id = docInfo.data._id;
|
|
var rev = docInfo.data._rev;
|
|
saveAttachmentRefs(id, rev, att.digest, attachmentSaved);
|
|
continue;
|
|
}
|
|
var data;
|
|
if (typeof att.data === 'string') {
|
|
// input is assumed to be a base64 string
|
|
try {
|
|
data = atob$1(att.data);
|
|
} catch (e) {
|
|
callback(createError(BAD_ARG,
|
|
'Attachment is not a valid base64 string'));
|
|
return;
|
|
}
|
|
doMD5(docInfo, key, attachmentSaved)(data);
|
|
} else {
|
|
prepareAttachmentForStorage(att.data,
|
|
doMD5(docInfo, key, attachmentSaved));
|
|
}
|
|
}
|
|
|
|
function finish() {
|
|
var seq = docInfo.metadata.rev_map[docInfo.metadata.rev];
|
|
/* istanbul ignore if */
|
|
if (seq) {
|
|
// check that there aren't any existing revisions with the same
|
|
// revision id, else we shouldn't do anything
|
|
return callback2();
|
|
}
|
|
seq = ++newUpdateSeq;
|
|
docInfo.metadata.rev_map[docInfo.metadata.rev] =
|
|
docInfo.metadata.seq = seq;
|
|
var seqKey = formatSeq(seq);
|
|
var batch = [{
|
|
key: seqKey,
|
|
value: docInfo.data,
|
|
prefix: stores.bySeqStore,
|
|
type: 'put'
|
|
}, {
|
|
key: docInfo.metadata.id,
|
|
value: docInfo.metadata,
|
|
prefix: stores.docStore,
|
|
type: 'put'
|
|
}];
|
|
txn.batch(batch);
|
|
results[resultsIdx] = {
|
|
ok: true,
|
|
id: docInfo.metadata.id,
|
|
rev: winningRev
|
|
};
|
|
fetchedDocs.set(docInfo.metadata.id, docInfo.metadata);
|
|
callback2();
|
|
}
|
|
|
|
if (!attachments.length) {
|
|
finish();
|
|
}
|
|
}
|
|
|
|
// attachments are queued per-digest, otherwise the refs could be
|
|
// overwritten by concurrent writes in the same bulkDocs session
|
|
var attachmentQueues = {};
|
|
|
|
function saveAttachmentRefs(id, rev, digest, callback) {
|
|
|
|
function fetchAtt() {
|
|
return new PouchPromise(function (resolve, reject) {
|
|
txn.get(stores.attachmentStore, digest, function (err, oldAtt) {
|
|
/* istanbul ignore if */
|
|
if (err && err.name !== 'NotFoundError') {
|
|
return reject(err);
|
|
}
|
|
resolve(oldAtt);
|
|
});
|
|
});
|
|
}
|
|
|
|
function saveAtt(oldAtt) {
|
|
var ref = [id, rev].join('@');
|
|
var newAtt = {};
|
|
|
|
if (oldAtt) {
|
|
if (oldAtt.refs) {
|
|
// only update references if this attachment already has them
|
|
// since we cannot migrate old style attachments here without
|
|
// doing a full db scan for references
|
|
newAtt.refs = oldAtt.refs;
|
|
newAtt.refs[ref] = true;
|
|
}
|
|
} else {
|
|
newAtt.refs = {};
|
|
newAtt.refs[ref] = true;
|
|
}
|
|
|
|
return new PouchPromise(function (resolve) {
|
|
txn.batch([{
|
|
type: 'put',
|
|
prefix: stores.attachmentStore,
|
|
key: digest,
|
|
value: newAtt
|
|
}]);
|
|
resolve(!oldAtt);
|
|
});
|
|
}
|
|
|
|
// put attachments in a per-digest queue, to avoid two docs with the same
|
|
// attachment overwriting each other
|
|
var queue = attachmentQueues[digest] || PouchPromise.resolve();
|
|
attachmentQueues[digest] = queue.then(function () {
|
|
return fetchAtt().then(saveAtt).then(function (isNewAttachment) {
|
|
callback(null, isNewAttachment);
|
|
}, callback);
|
|
});
|
|
}
|
|
|
|
function saveAttachment(docInfo, digest, key, data, callback) {
|
|
var att = docInfo.data._attachments[key];
|
|
delete att.data;
|
|
att.digest = digest;
|
|
att.length = data.length;
|
|
var id = docInfo.metadata.id;
|
|
var rev = docInfo.metadata.rev;
|
|
att.revpos = parseInt(rev, 10);
|
|
|
|
saveAttachmentRefs(id, rev, digest, function (err, isNewAttachment) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
// do not try to store empty attachments
|
|
if (data.length === 0) {
|
|
return callback(err);
|
|
}
|
|
if (!isNewAttachment) {
|
|
// small optimization - don't bother writing it again
|
|
return callback(err);
|
|
}
|
|
txn.batch([{
|
|
type: 'put',
|
|
prefix: stores.binaryStore,
|
|
key: digest,
|
|
value: new Buffer(data, 'binary')
|
|
}]);
|
|
callback();
|
|
});
|
|
}
|
|
|
|
function complete(err) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return process.nextTick(function () {
|
|
callback(err);
|
|
});
|
|
}
|
|
txn.batch([
|
|
{
|
|
prefix: stores.metaStore,
|
|
type: 'put',
|
|
key: UPDATE_SEQ_KEY,
|
|
value: newUpdateSeq
|
|
},
|
|
{
|
|
prefix: stores.metaStore,
|
|
type: 'put',
|
|
key: DOC_COUNT_KEY,
|
|
value: db._docCount + docCountDelta
|
|
}
|
|
]);
|
|
txn.execute(db, function (err) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
db._docCount += docCountDelta;
|
|
db._updateSeq = newUpdateSeq;
|
|
levelChanges.notify(name);
|
|
process.nextTick(function () {
|
|
callback(null, results);
|
|
});
|
|
});
|
|
}
|
|
|
|
if (!docInfos.length) {
|
|
return callback(null, []);
|
|
}
|
|
|
|
verifyAttachments(function (err) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
fetchExistingDocs(function (err) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
processDocs(revLimit, docInfos, api, fetchedDocs, txn, results,
|
|
writeDoc, opts, finish);
|
|
});
|
|
});
|
|
});
|
|
api._allDocs = readLock(function (opts, callback) {
|
|
opts = clone(opts);
|
|
countDocs(function (err, docCount) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
var readstreamOpts = {};
|
|
var skip = opts.skip || 0;
|
|
if (opts.startkey) {
|
|
readstreamOpts.gte = opts.startkey;
|
|
}
|
|
if (opts.endkey) {
|
|
readstreamOpts.lte = opts.endkey;
|
|
}
|
|
if (opts.key) {
|
|
readstreamOpts.gte = readstreamOpts.lte = opts.key;
|
|
}
|
|
if (opts.descending) {
|
|
readstreamOpts.reverse = true;
|
|
// switch start and ends
|
|
var tmp = readstreamOpts.lte;
|
|
readstreamOpts.lte = readstreamOpts.gte;
|
|
readstreamOpts.gte = tmp;
|
|
}
|
|
var limit;
|
|
if (typeof opts.limit === 'number') {
|
|
limit = opts.limit;
|
|
}
|
|
if (limit === 0 ||
|
|
('start' in readstreamOpts && 'end' in readstreamOpts &&
|
|
readstreamOpts.start > readstreamOpts.end)) {
|
|
// should return 0 results when start is greater than end.
|
|
// normally level would "fix" this for us by reversing the order,
|
|
// so short-circuit instead
|
|
return callback(null, {
|
|
total_rows: docCount,
|
|
offset: opts.skip,
|
|
rows: []
|
|
});
|
|
}
|
|
var results = [];
|
|
var docstream = stores.docStore.readStream(readstreamOpts);
|
|
|
|
var throughStream = through2.obj(function (entry, _, next) {
|
|
var metadata = entry.value;
|
|
// winningRev and deleted are performance-killers, but
|
|
// in newer versions of PouchDB, they are cached on the metadata
|
|
var winningRev = getWinningRev(metadata);
|
|
var deleted = getIsDeleted(metadata, winningRev);
|
|
if (!deleted) {
|
|
if (skip-- > 0) {
|
|
next();
|
|
return;
|
|
} else if (typeof limit === 'number' && limit-- <= 0) {
|
|
docstream.unpipe();
|
|
docstream.destroy();
|
|
next();
|
|
return;
|
|
}
|
|
} else if (opts.deleted !== 'ok') {
|
|
next();
|
|
return;
|
|
}
|
|
function allDocsInner(data) {
|
|
var doc = {
|
|
id: metadata.id,
|
|
key: metadata.id,
|
|
value: {
|
|
rev: winningRev
|
|
}
|
|
};
|
|
if (opts.include_docs) {
|
|
doc.doc = data;
|
|
doc.doc._rev = doc.value.rev;
|
|
if (opts.conflicts) {
|
|
doc.doc._conflicts = collectConflicts(metadata);
|
|
}
|
|
for (var att in doc.doc._attachments) {
|
|
if (doc.doc._attachments.hasOwnProperty(att)) {
|
|
doc.doc._attachments[att].stub = true;
|
|
}
|
|
}
|
|
}
|
|
if (opts.inclusive_end === false && metadata.id === opts.endkey) {
|
|
return next();
|
|
} else if (deleted) {
|
|
if (opts.deleted === 'ok') {
|
|
doc.value.deleted = true;
|
|
doc.doc = null;
|
|
} else {
|
|
/* istanbul ignore next */
|
|
return next();
|
|
}
|
|
}
|
|
results.push(doc);
|
|
next();
|
|
}
|
|
if (opts.include_docs) {
|
|
var seq = metadata.rev_map[winningRev];
|
|
stores.bySeqStore.get(formatSeq(seq), function (err, data) {
|
|
allDocsInner(data);
|
|
});
|
|
}
|
|
else {
|
|
allDocsInner();
|
|
}
|
|
}, function (next) {
|
|
PouchPromise.resolve().then(function () {
|
|
if (opts.include_docs && opts.attachments) {
|
|
return fetchAttachments(results, stores, opts);
|
|
}
|
|
}).then(function () {
|
|
callback(null, {
|
|
total_rows: docCount,
|
|
offset: opts.skip,
|
|
rows: results
|
|
});
|
|
}, callback);
|
|
next();
|
|
}).on('unpipe', function () {
|
|
throughStream.end();
|
|
});
|
|
|
|
docstream.on('error', callback);
|
|
|
|
docstream.pipe(throughStream);
|
|
});
|
|
});
|
|
|
|
api._changes = function (opts) {
|
|
opts = clone(opts);
|
|
|
|
if (opts.continuous) {
|
|
var id = name + ':' + uuid();
|
|
levelChanges.addListener(name, id, api, opts);
|
|
levelChanges.notify(name);
|
|
return {
|
|
cancel: function () {
|
|
levelChanges.removeListener(name, id);
|
|
}
|
|
};
|
|
}
|
|
|
|
var descending = opts.descending;
|
|
var results = [];
|
|
var lastSeq = opts.since || 0;
|
|
var called = 0;
|
|
var streamOpts = {
|
|
reverse: descending
|
|
};
|
|
var limit;
|
|
if ('limit' in opts && opts.limit > 0) {
|
|
limit = opts.limit;
|
|
}
|
|
if (!streamOpts.reverse) {
|
|
streamOpts.start = formatSeq(opts.since || 0);
|
|
}
|
|
|
|
var docIds = opts.doc_ids && new pouchdbCollections.Set(opts.doc_ids);
|
|
var filter = filterChange(opts);
|
|
var docIdsToMetadata = new pouchdbCollections.Map();
|
|
|
|
var returnDocs;
|
|
if ('return_docs' in opts) {
|
|
returnDocs = opts.return_docs;
|
|
} else if ('returnDocs' in opts) {
|
|
// TODO: Remove 'returnDocs' in favor of 'return_docs' in a future release
|
|
returnDocs = opts.returnDocs;
|
|
} else {
|
|
returnDocs = true;
|
|
}
|
|
|
|
function complete() {
|
|
opts.done = true;
|
|
if (returnDocs && opts.limit) {
|
|
/* istanbul ignore if */
|
|
if (opts.limit < results.length) {
|
|
results.length = opts.limit;
|
|
}
|
|
}
|
|
changeStream.unpipe(throughStream);
|
|
changeStream.destroy();
|
|
if (!opts.continuous && !opts.cancelled) {
|
|
if (opts.include_docs && opts.attachments) {
|
|
fetchAttachments(results, stores, opts).then(function () {
|
|
opts.complete(null, {results: results, last_seq: lastSeq});
|
|
});
|
|
} else {
|
|
opts.complete(null, {results: results, last_seq: lastSeq});
|
|
}
|
|
}
|
|
}
|
|
var changeStream = stores.bySeqStore.readStream(streamOpts);
|
|
var throughStream = through2.obj(function (data, _, next) {
|
|
if (limit && called >= limit) {
|
|
complete();
|
|
return next();
|
|
}
|
|
if (opts.cancelled || opts.done) {
|
|
return next();
|
|
}
|
|
|
|
var seq = parseSeq(data.key);
|
|
var doc = data.value;
|
|
|
|
if (seq === opts.since && !descending) {
|
|
// couchdb ignores `since` if descending=true
|
|
return next();
|
|
}
|
|
|
|
if (docIds && !docIds.has(doc._id)) {
|
|
return next();
|
|
}
|
|
|
|
var metadata;
|
|
|
|
function onGetMetadata(metadata) {
|
|
var winningRev = getWinningRev(metadata);
|
|
|
|
function onGetWinningDoc(winningDoc) {
|
|
|
|
var change = opts.processChange(winningDoc, metadata, opts);
|
|
change.seq = metadata.seq;
|
|
|
|
var filtered = filter(change);
|
|
if (typeof filtered === 'object') {
|
|
return opts.complete(filtered);
|
|
}
|
|
|
|
if (filtered) {
|
|
called++;
|
|
|
|
if (opts.attachments && opts.include_docs) {
|
|
// fetch attachment immediately for the benefit
|
|
// of live listeners
|
|
fetchAttachments([change], stores, opts).then(function () {
|
|
opts.onChange(change);
|
|
});
|
|
} else {
|
|
opts.onChange(change);
|
|
}
|
|
|
|
if (returnDocs) {
|
|
results.push(change);
|
|
}
|
|
}
|
|
next();
|
|
}
|
|
|
|
if (metadata.seq !== seq) {
|
|
// some other seq is later
|
|
return next();
|
|
}
|
|
|
|
lastSeq = seq;
|
|
|
|
if (winningRev === doc._rev) {
|
|
return onGetWinningDoc(doc);
|
|
}
|
|
|
|
// fetch the winner
|
|
|
|
var winningSeq = metadata.rev_map[winningRev];
|
|
|
|
stores.bySeqStore.get(formatSeq(winningSeq), function (err, doc) {
|
|
onGetWinningDoc(doc);
|
|
});
|
|
}
|
|
|
|
metadata = docIdsToMetadata.get(doc._id);
|
|
if (metadata) { // cached
|
|
return onGetMetadata(metadata);
|
|
}
|
|
// metadata not cached, have to go fetch it
|
|
stores.docStore.get(doc._id, function (err, metadata) {
|
|
/* istanbul ignore if */
|
|
if (opts.cancelled || opts.done || db.isClosed() ||
|
|
isLocalId(metadata.id)) {
|
|
return next();
|
|
}
|
|
docIdsToMetadata.set(doc._id, metadata);
|
|
onGetMetadata(metadata);
|
|
});
|
|
}, function (next) {
|
|
if (opts.cancelled) {
|
|
return next();
|
|
}
|
|
if (returnDocs && opts.limit) {
|
|
/* istanbul ignore if */
|
|
if (opts.limit < results.length) {
|
|
results.length = opts.limit;
|
|
}
|
|
}
|
|
|
|
next();
|
|
}).on('unpipe', function () {
|
|
throughStream.end();
|
|
complete();
|
|
});
|
|
changeStream.pipe(throughStream);
|
|
return {
|
|
cancel: function () {
|
|
opts.cancelled = true;
|
|
complete();
|
|
}
|
|
};
|
|
};
|
|
|
|
api._close = function (callback) {
|
|
/* istanbul ignore if */
|
|
if (db.isClosed()) {
|
|
return callback(createError(NOT_OPEN));
|
|
}
|
|
db.close(function (err) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
dbStore.delete(name);
|
|
callback();
|
|
}
|
|
});
|
|
};
|
|
|
|
api._getRevisionTree = function (docId, callback) {
|
|
stores.docStore.get(docId, function (err, metadata) {
|
|
if (err) {
|
|
callback(createError(MISSING_DOC));
|
|
} else {
|
|
callback(null, metadata.rev_tree);
|
|
}
|
|
});
|
|
};
|
|
|
|
api._doCompaction = writeLock(function (docId, revs, opts, callback) {
|
|
api._doCompactionNoLock(docId, revs, opts, callback);
|
|
});
|
|
|
|
// the NoLock version is for use by bulkDocs
|
|
api._doCompactionNoLock = function (docId, revs, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
|
|
if (!revs.length) {
|
|
return callback();
|
|
}
|
|
var txn = opts.ctx || new LevelTransaction();
|
|
|
|
txn.get(stores.docStore, docId, function (err, metadata) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
var seqs = revs.map(function (rev) {
|
|
var seq = metadata.rev_map[rev];
|
|
delete metadata.rev_map[rev];
|
|
return seq;
|
|
});
|
|
traverseRevTree(metadata.rev_tree, function (isLeaf, pos,
|
|
revHash, ctx, opts) {
|
|
var rev = pos + '-' + revHash;
|
|
if (revs.indexOf(rev) !== -1) {
|
|
opts.status = 'missing';
|
|
}
|
|
});
|
|
|
|
var batch = [];
|
|
batch.push({
|
|
key: metadata.id,
|
|
value: metadata,
|
|
type: 'put',
|
|
prefix: stores.docStore
|
|
});
|
|
|
|
var digestMap = {};
|
|
var numDone = 0;
|
|
var overallErr;
|
|
function checkDone(err) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
overallErr = err;
|
|
}
|
|
if (++numDone === revs.length) { // done
|
|
/* istanbul ignore if */
|
|
if (overallErr) {
|
|
return callback(overallErr);
|
|
}
|
|
deleteOrphanedAttachments();
|
|
}
|
|
}
|
|
|
|
function finish(err) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
txn.batch(batch);
|
|
if (opts.ctx) {
|
|
// don't execute immediately
|
|
return callback();
|
|
}
|
|
txn.execute(db, callback);
|
|
}
|
|
|
|
function deleteOrphanedAttachments() {
|
|
var possiblyOrphanedAttachments = Object.keys(digestMap);
|
|
if (!possiblyOrphanedAttachments.length) {
|
|
return finish();
|
|
}
|
|
var numDone = 0;
|
|
var overallErr;
|
|
function checkDone(err) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
overallErr = err;
|
|
}
|
|
if (++numDone === possiblyOrphanedAttachments.length) {
|
|
finish(overallErr);
|
|
}
|
|
}
|
|
var refsToDelete = new pouchdbCollections.Map();
|
|
revs.forEach(function (rev) {
|
|
refsToDelete.set(docId + '@' + rev, true);
|
|
});
|
|
possiblyOrphanedAttachments.forEach(function (digest) {
|
|
txn.get(stores.attachmentStore, digest, function (err, attData) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
if (err.name === 'NotFoundError') {
|
|
return checkDone();
|
|
} else {
|
|
return checkDone(err);
|
|
}
|
|
}
|
|
var refs = Object.keys(attData.refs || {}).filter(function (ref) {
|
|
return !refsToDelete.has(ref);
|
|
});
|
|
var newRefs = {};
|
|
refs.forEach(function (ref) {
|
|
newRefs[ref] = true;
|
|
});
|
|
if (refs.length) { // not orphaned
|
|
batch.push({
|
|
key: digest,
|
|
type: 'put',
|
|
value: {refs: newRefs},
|
|
prefix: stores.attachmentStore
|
|
});
|
|
} else { // orphaned, can safely delete
|
|
batch = batch.concat([{
|
|
key: digest,
|
|
type: 'del',
|
|
prefix: stores.attachmentStore
|
|
}, {
|
|
key: digest,
|
|
type: 'del',
|
|
prefix: stores.binaryStore
|
|
}]);
|
|
}
|
|
checkDone();
|
|
});
|
|
});
|
|
}
|
|
|
|
seqs.forEach(function (seq) {
|
|
batch.push({
|
|
key: formatSeq(seq),
|
|
type: 'del',
|
|
prefix: stores.bySeqStore
|
|
});
|
|
txn.get(stores.bySeqStore, formatSeq(seq), function (err, doc) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
if (err.name === 'NotFoundError') {
|
|
return checkDone();
|
|
} else {
|
|
return checkDone(err);
|
|
}
|
|
}
|
|
var atts = Object.keys(doc._attachments || {});
|
|
atts.forEach(function (attName) {
|
|
var digest = doc._attachments[attName].digest;
|
|
digestMap[digest] = true;
|
|
});
|
|
checkDone();
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
api._getLocal = function (id, callback) {
|
|
stores.localStore.get(id, function (err, doc) {
|
|
if (err) {
|
|
callback(createError(MISSING_DOC));
|
|
} else {
|
|
callback(null, doc);
|
|
}
|
|
});
|
|
};
|
|
|
|
api._putLocal = function (doc, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
if (opts.ctx) {
|
|
api._putLocalNoLock(doc, opts, callback);
|
|
} else {
|
|
api._putLocalWithLock(doc, opts, callback);
|
|
}
|
|
};
|
|
|
|
api._putLocalWithLock = writeLock(function (doc, opts, callback) {
|
|
api._putLocalNoLock(doc, opts, callback);
|
|
});
|
|
|
|
// the NoLock version is for use by bulkDocs
|
|
api._putLocalNoLock = function (doc, opts, callback) {
|
|
delete doc._revisions; // ignore this, trust the rev
|
|
var oldRev = doc._rev;
|
|
var id = doc._id;
|
|
|
|
var txn = opts.ctx || new LevelTransaction();
|
|
|
|
txn.get(stores.localStore, id, function (err, resp) {
|
|
if (err && oldRev) {
|
|
return callback(createError(REV_CONFLICT));
|
|
}
|
|
if (resp && resp._rev !== oldRev) {
|
|
return callback(createError(REV_CONFLICT));
|
|
}
|
|
doc._rev =
|
|
oldRev ? '0-' + (parseInt(oldRev.split('-')[1], 10) + 1) : '0-1';
|
|
var batch = [
|
|
{
|
|
type: 'put',
|
|
prefix: stores.localStore,
|
|
key: id,
|
|
value: doc
|
|
}
|
|
];
|
|
|
|
txn.batch(batch);
|
|
var ret = {ok: true, id: doc._id, rev: doc._rev};
|
|
|
|
if (opts.ctx) {
|
|
// don't execute immediately
|
|
return callback(null, ret);
|
|
}
|
|
txn.execute(db, function (err) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
callback(null, ret);
|
|
});
|
|
});
|
|
};
|
|
|
|
api._removeLocal = function (doc, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
if (opts.ctx) {
|
|
api._removeLocalNoLock(doc, opts, callback);
|
|
} else {
|
|
api._removeLocalWithLock(doc, opts, callback);
|
|
}
|
|
};
|
|
|
|
api._removeLocalWithLock = writeLock(function (doc, opts, callback) {
|
|
api._removeLocalNoLock(doc, opts, callback);
|
|
});
|
|
|
|
// the NoLock version is for use by bulkDocs
|
|
api._removeLocalNoLock = function (doc, opts, callback) {
|
|
var txn = opts.ctx || new LevelTransaction();
|
|
txn.get(stores.localStore, doc._id, function (err, resp) {
|
|
if (err) {
|
|
/* istanbul ignore if */
|
|
if (err.name !== 'NotFoundError') {
|
|
return callback(err);
|
|
} else {
|
|
return callback(createError(MISSING_DOC));
|
|
}
|
|
}
|
|
if (resp._rev !== doc._rev) {
|
|
return callback(createError(REV_CONFLICT));
|
|
}
|
|
txn.batch([{
|
|
prefix: stores.localStore,
|
|
type: 'del',
|
|
key: doc._id
|
|
}]);
|
|
var ret = {ok: true, id: doc._id, rev: '0-0'};
|
|
if (opts.ctx) {
|
|
// don't execute immediately
|
|
return callback(null, ret);
|
|
}
|
|
txn.execute(db, function (err) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
callback(null, ret);
|
|
});
|
|
});
|
|
};
|
|
|
|
// close and delete open leveldb stores
|
|
api._destroy = function (opts, callback) {
|
|
var dbStore;
|
|
var leveldownName = functionName(leveldown);
|
|
/* istanbul ignore else */
|
|
if (dbStores.has(leveldownName)) {
|
|
dbStore = dbStores.get(leveldownName);
|
|
} else {
|
|
return callDestroy(name, callback);
|
|
}
|
|
|
|
/* istanbul ignore else */
|
|
if (dbStore.has(name)) {
|
|
levelChanges.removeAllListeners(name);
|
|
|
|
dbStore.get(name).close(function () {
|
|
dbStore.delete(name);
|
|
callDestroy(name, callback);
|
|
});
|
|
} else {
|
|
callDestroy(name, callback);
|
|
}
|
|
};
|
|
function callDestroy(name, cb) {
|
|
leveldown.destroy(name, cb);
|
|
}
|
|
}
|
|
|
|
function MemDownPouch(opts, callback) {
|
|
var _opts = jsExtend.extend({
|
|
db: memdown
|
|
}, opts);
|
|
|
|
LevelPouch.call(this, _opts, callback);
|
|
}
|
|
|
|
// overrides for normal LevelDB behavior on Node
|
|
MemDownPouch.valid = function () {
|
|
return true;
|
|
};
|
|
MemDownPouch.use_prefix = false;
|
|
|
|
function MemoryPouchPlugin (PouchDB) {
|
|
PouchDB.adapter('memory', MemDownPouch, true);
|
|
}
|
|
|
|
var PDB = (typeof PouchDB !== 'undefined') ? PouchDB : require('pouchdb');
|
|
if (!PDB) {
|
|
guardedConsole('error', 'memory adapter plugin error: ' +
|
|
'Cannot find global "PouchDB" object! ' +
|
|
'Did you remember to include pouchdb.js?');
|
|
} else {
|
|
MemoryPouchPlugin(PDB);
|
|
}
|
|
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer)
|
|
},{"_process":133,"argsarray":38,"buffer":43,"debug":121,"double-ended-queue":52,"events":56,"inherits":123,"js-extend":90,"levelup":103,"lie":124,"memdown":109,"pouchdb":120,"pouchdb-collections":117,"spark-md5":277,"sublevel-pouchdb":282,"through2":131,"vuvuzela":298}],120:[function(require,module,exports){
|
|
(function (process,global){
|
|
'use strict';
|
|
|
|
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
|
|
|
var jsExtend = require('js-extend');
|
|
var debug = _interopDefault(require('debug'));
|
|
var inherits = _interopDefault(require('inherits'));
|
|
var lie = _interopDefault(require('lie'));
|
|
var pouchdbCollections = require('pouchdb-collections');
|
|
var getArguments = _interopDefault(require('argsarray'));
|
|
var events = require('events');
|
|
var scopedEval = _interopDefault(require('scope-eval'));
|
|
var Md5 = _interopDefault(require('spark-md5'));
|
|
var vuvuzela = _interopDefault(require('vuvuzela'));
|
|
var PromisePool = _interopDefault(require('es6-promise-pool'));
|
|
var pouchdbCollate = require('pouchdb-collate');
|
|
|
|
/* istanbul ignore next */
|
|
var PouchPromise = typeof Promise === 'function' ? Promise : lie;
|
|
|
|
function isBinaryObject(object) {
|
|
return object instanceof ArrayBuffer ||
|
|
(typeof Blob !== 'undefined' && object instanceof Blob);
|
|
}
|
|
|
|
function cloneArrayBuffer(buff) {
|
|
if (typeof buff.slice === 'function') {
|
|
return buff.slice(0);
|
|
}
|
|
// IE10-11 slice() polyfill
|
|
var target = new ArrayBuffer(buff.byteLength);
|
|
var targetArray = new Uint8Array(target);
|
|
var sourceArray = new Uint8Array(buff);
|
|
targetArray.set(sourceArray);
|
|
return target;
|
|
}
|
|
|
|
function cloneBinaryObject(object) {
|
|
if (object instanceof ArrayBuffer) {
|
|
return cloneArrayBuffer(object);
|
|
}
|
|
var size = object.size;
|
|
var type = object.type;
|
|
// Blob
|
|
if (typeof object.slice === 'function') {
|
|
return object.slice(0, size, type);
|
|
}
|
|
// PhantomJS slice() replacement
|
|
return object.webkitSlice(0, size, type);
|
|
}
|
|
|
|
// most of this is borrowed from lodash.isPlainObject:
|
|
// https://github.com/fis-components/lodash.isplainobject/
|
|
// blob/29c358140a74f252aeb08c9eb28bef86f2217d4a/index.js
|
|
|
|
var funcToString = Function.prototype.toString;
|
|
var objectCtorString = funcToString.call(Object);
|
|
|
|
function isPlainObject(value) {
|
|
var proto = Object.getPrototypeOf(value);
|
|
/* istanbul ignore if */
|
|
if (proto === null) { // not sure when this happens, but I guess it can
|
|
return true;
|
|
}
|
|
var Ctor = proto.constructor;
|
|
return (typeof Ctor == 'function' &&
|
|
Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
|
|
}
|
|
|
|
function clone(object) {
|
|
var newObject;
|
|
var i;
|
|
var len;
|
|
|
|
if (!object || typeof object !== 'object') {
|
|
return object;
|
|
}
|
|
|
|
if (Array.isArray(object)) {
|
|
newObject = [];
|
|
for (i = 0, len = object.length; i < len; i++) {
|
|
newObject[i] = clone(object[i]);
|
|
}
|
|
return newObject;
|
|
}
|
|
|
|
// special case: to avoid inconsistencies between IndexedDB
|
|
// and other backends, we automatically stringify Dates
|
|
if (object instanceof Date) {
|
|
return object.toISOString();
|
|
}
|
|
|
|
if (isBinaryObject(object)) {
|
|
return cloneBinaryObject(object);
|
|
}
|
|
|
|
if (!isPlainObject(object)) {
|
|
return object; // don't clone objects like Workers
|
|
}
|
|
|
|
newObject = {};
|
|
for (i in object) {
|
|
if (Object.prototype.hasOwnProperty.call(object, i)) {
|
|
var value = clone(object[i]);
|
|
if (typeof value !== 'undefined') {
|
|
newObject[i] = value;
|
|
}
|
|
}
|
|
}
|
|
return newObject;
|
|
}
|
|
|
|
function once(fun) {
|
|
var called = false;
|
|
return getArguments(function (args) {
|
|
/* istanbul ignore if */
|
|
if (called) {
|
|
// this is a smoke test and should never actually happen
|
|
throw new Error('once called more than once');
|
|
} else {
|
|
called = true;
|
|
fun.apply(this, args);
|
|
}
|
|
});
|
|
}
|
|
|
|
function toPromise(func) {
|
|
//create the function we will be returning
|
|
return getArguments(function (args) {
|
|
// Clone arguments
|
|
args = clone(args);
|
|
var self = this;
|
|
var tempCB =
|
|
(typeof args[args.length - 1] === 'function') ? args.pop() : false;
|
|
// if the last argument is a function, assume its a callback
|
|
var usedCB;
|
|
if (tempCB) {
|
|
// if it was a callback, create a new callback which calls it,
|
|
// but do so async so we don't trap any errors
|
|
usedCB = function (err, resp) {
|
|
process.nextTick(function () {
|
|
tempCB(err, resp);
|
|
});
|
|
};
|
|
}
|
|
var promise = new PouchPromise(function (fulfill, reject) {
|
|
var resp;
|
|
try {
|
|
var callback = once(function (err, mesg) {
|
|
if (err) {
|
|
reject(err);
|
|
} else {
|
|
fulfill(mesg);
|
|
}
|
|
});
|
|
// create a callback for this invocation
|
|
// apply the function in the orig context
|
|
args.push(callback);
|
|
resp = func.apply(self, args);
|
|
if (resp && typeof resp.then === 'function') {
|
|
fulfill(resp);
|
|
}
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
});
|
|
// if there is a callback, call it back
|
|
if (usedCB) {
|
|
promise.then(function (result) {
|
|
usedCB(null, result);
|
|
}, usedCB);
|
|
}
|
|
return promise;
|
|
});
|
|
}
|
|
|
|
var log = debug('pouchdb:api');
|
|
|
|
function adapterFun(name, callback) {
|
|
function logApiCall(self, name, args) {
|
|
/* istanbul ignore if */
|
|
if (log.enabled) {
|
|
var logArgs = [self._db_name, name];
|
|
for (var i = 0; i < args.length - 1; i++) {
|
|
logArgs.push(args[i]);
|
|
}
|
|
log.apply(null, logArgs);
|
|
|
|
// override the callback itself to log the response
|
|
var origCallback = args[args.length - 1];
|
|
args[args.length - 1] = function (err, res) {
|
|
var responseArgs = [self._db_name, name];
|
|
responseArgs = responseArgs.concat(
|
|
err ? ['error', err] : ['success', res]
|
|
);
|
|
log.apply(null, responseArgs);
|
|
origCallback(err, res);
|
|
};
|
|
}
|
|
}
|
|
|
|
return toPromise(getArguments(function (args) {
|
|
if (this._closed) {
|
|
return PouchPromise.reject(new Error('database is closed'));
|
|
}
|
|
if (this._destroyed) {
|
|
return PouchPromise.reject(new Error('database is destroyed'));
|
|
}
|
|
var self = this;
|
|
logApiCall(self, name, args);
|
|
if (!this.taskqueue.isReady) {
|
|
return new PouchPromise(function (fulfill, reject) {
|
|
self.taskqueue.addTask(function (failed) {
|
|
if (failed) {
|
|
reject(failed);
|
|
} else {
|
|
fulfill(self[name].apply(self, args));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
return callback.apply(this, args);
|
|
}));
|
|
}
|
|
|
|
// like underscore/lodash _.pick()
|
|
function pick(obj, arr) {
|
|
var res = {};
|
|
for (var i = 0, len = arr.length; i < len; i++) {
|
|
var prop = arr[i];
|
|
if (prop in obj) {
|
|
res[prop] = obj[prop];
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// Most browsers throttle concurrent requests at 6, so it's silly
|
|
// to shim _bulk_get by trying to launch potentially hundreds of requests
|
|
// and then letting the majority time out. We can handle this ourselves.
|
|
var MAX_NUM_CONCURRENT_REQUESTS = 6;
|
|
|
|
function identityFunction(x) {
|
|
return x;
|
|
}
|
|
|
|
function formatResultForOpenRevsGet(result) {
|
|
return [{
|
|
ok: result
|
|
}];
|
|
}
|
|
|
|
// shim for P/CouchDB adapters that don't directly implement _bulk_get
|
|
function bulkGet(db, opts, callback) {
|
|
var requests = opts.docs;
|
|
|
|
// consolidate into one request per doc if possible
|
|
var requestsById = {};
|
|
requests.forEach(function (request) {
|
|
if (request.id in requestsById) {
|
|
requestsById[request.id].push(request);
|
|
} else {
|
|
requestsById[request.id] = [request];
|
|
}
|
|
});
|
|
|
|
var numDocs = Object.keys(requestsById).length;
|
|
var numDone = 0;
|
|
var perDocResults = new Array(numDocs);
|
|
|
|
function collapseResultsAndFinish() {
|
|
var results = [];
|
|
perDocResults.forEach(function (res) {
|
|
res.docs.forEach(function (info) {
|
|
results.push({
|
|
id: res.id,
|
|
docs: [info]
|
|
});
|
|
});
|
|
});
|
|
callback(null, {results: results});
|
|
}
|
|
|
|
function checkDone() {
|
|
if (++numDone === numDocs) {
|
|
collapseResultsAndFinish();
|
|
}
|
|
}
|
|
|
|
function gotResult(docIndex, id, docs) {
|
|
perDocResults[docIndex] = {id: id, docs: docs};
|
|
checkDone();
|
|
}
|
|
|
|
var allRequests = Object.keys(requestsById);
|
|
|
|
var i = 0;
|
|
|
|
function nextBatch() {
|
|
|
|
if (i >= allRequests.length) {
|
|
return;
|
|
}
|
|
|
|
var upTo = Math.min(i + MAX_NUM_CONCURRENT_REQUESTS, allRequests.length);
|
|
var batch = allRequests.slice(i, upTo);
|
|
processBatch(batch, i);
|
|
i += batch.length;
|
|
}
|
|
|
|
function processBatch(batch, offset) {
|
|
batch.forEach(function (docId, j) {
|
|
var docIdx = offset + j;
|
|
var docRequests = requestsById[docId];
|
|
|
|
// just use the first request as the "template"
|
|
// TODO: The _bulk_get API allows for more subtle use cases than this,
|
|
// but for now it is unlikely that there will be a mix of different
|
|
// "atts_since" or "attachments" in the same request, since it's just
|
|
// replicate.js that is using this for the moment.
|
|
// Also, atts_since is aspirational, since we don't support it yet.
|
|
var docOpts = pick(docRequests[0], ['atts_since', 'attachments']);
|
|
docOpts.open_revs = docRequests.map(function (request) {
|
|
// rev is optional, open_revs disallowed
|
|
return request.rev;
|
|
});
|
|
|
|
// remove falsey / undefined revisions
|
|
docOpts.open_revs = docOpts.open_revs.filter(identityFunction);
|
|
|
|
var formatResult = identityFunction;
|
|
|
|
if (docOpts.open_revs.length === 0) {
|
|
delete docOpts.open_revs;
|
|
|
|
// when fetching only the "winning" leaf,
|
|
// transform the result so it looks like an open_revs
|
|
// request
|
|
formatResult = formatResultForOpenRevsGet;
|
|
}
|
|
|
|
// globally-supplied options
|
|
['revs', 'attachments', 'binary', 'ajax'].forEach(function (param) {
|
|
if (param in opts) {
|
|
docOpts[param] = opts[param];
|
|
}
|
|
});
|
|
db.get(docId, docOpts, function (err, res) {
|
|
var result;
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
result = [{error: err}];
|
|
} else {
|
|
result = formatResult(res);
|
|
}
|
|
gotResult(docIdx, docId, result);
|
|
nextBatch();
|
|
});
|
|
});
|
|
}
|
|
|
|
nextBatch();
|
|
|
|
}
|
|
|
|
function isChromeApp() {
|
|
return (typeof chrome !== "undefined" &&
|
|
typeof chrome.storage !== "undefined" &&
|
|
typeof chrome.storage.local !== "undefined");
|
|
}
|
|
|
|
var hasLocal;
|
|
|
|
if (isChromeApp()) {
|
|
hasLocal = false;
|
|
} else {
|
|
try {
|
|
localStorage.setItem('_pouch_check_localstorage', 1);
|
|
hasLocal = !!localStorage.getItem('_pouch_check_localstorage');
|
|
} catch (e) {
|
|
hasLocal = false;
|
|
}
|
|
}
|
|
|
|
function hasLocalStorage() {
|
|
return hasLocal;
|
|
}
|
|
|
|
inherits(Changes$1, events.EventEmitter);
|
|
|
|
/* istanbul ignore next */
|
|
function attachBrowserEvents(self) {
|
|
if (isChromeApp()) {
|
|
chrome.storage.onChanged.addListener(function (e) {
|
|
// make sure it's event addressed to us
|
|
if (e.db_name != null) {
|
|
//object only has oldValue, newValue members
|
|
self.emit(e.dbName.newValue);
|
|
}
|
|
});
|
|
} else if (hasLocalStorage()) {
|
|
if (typeof addEventListener !== 'undefined') {
|
|
addEventListener("storage", function (e) {
|
|
self.emit(e.key);
|
|
});
|
|
} else { // old IE
|
|
window.attachEvent("storage", function (e) {
|
|
self.emit(e.key);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
function Changes$1() {
|
|
events.EventEmitter.call(this);
|
|
this._listeners = {};
|
|
|
|
attachBrowserEvents(this);
|
|
}
|
|
Changes$1.prototype.addListener = function (dbName, id, db, opts) {
|
|
/* istanbul ignore if */
|
|
if (this._listeners[id]) {
|
|
return;
|
|
}
|
|
var self = this;
|
|
var inprogress = false;
|
|
function eventFunction() {
|
|
/* istanbul ignore if */
|
|
if (!self._listeners[id]) {
|
|
return;
|
|
}
|
|
if (inprogress) {
|
|
inprogress = 'waiting';
|
|
return;
|
|
}
|
|
inprogress = true;
|
|
var changesOpts = pick(opts, [
|
|
'style', 'include_docs', 'attachments', 'conflicts', 'filter',
|
|
'doc_ids', 'view', 'since', 'query_params', 'binary'
|
|
]);
|
|
|
|
/* istanbul ignore next */
|
|
function onError() {
|
|
inprogress = false;
|
|
}
|
|
|
|
db.changes(changesOpts).on('change', function (c) {
|
|
if (c.seq > opts.since && !opts.cancelled) {
|
|
opts.since = c.seq;
|
|
opts.onChange(c);
|
|
}
|
|
}).on('complete', function () {
|
|
if (inprogress === 'waiting') {
|
|
setTimeout(function (){
|
|
eventFunction();
|
|
},0);
|
|
}
|
|
inprogress = false;
|
|
}).on('error', onError);
|
|
}
|
|
this._listeners[id] = eventFunction;
|
|
this.on(dbName, eventFunction);
|
|
};
|
|
|
|
Changes$1.prototype.removeListener = function (dbName, id) {
|
|
/* istanbul ignore if */
|
|
if (!(id in this._listeners)) {
|
|
return;
|
|
}
|
|
events.EventEmitter.prototype.removeListener.call(this, dbName,
|
|
this._listeners[id]);
|
|
};
|
|
|
|
|
|
/* istanbul ignore next */
|
|
Changes$1.prototype.notifyLocalWindows = function (dbName) {
|
|
//do a useless change on a storage thing
|
|
//in order to get other windows's listeners to activate
|
|
if (isChromeApp()) {
|
|
chrome.storage.local.set({dbName: dbName});
|
|
} else if (hasLocalStorage()) {
|
|
localStorage[dbName] = (localStorage[dbName] === "a") ? "b" : "a";
|
|
}
|
|
};
|
|
|
|
Changes$1.prototype.notify = function (dbName) {
|
|
this.emit(dbName);
|
|
this.notifyLocalWindows(dbName);
|
|
};
|
|
|
|
function guardedConsole(method) {
|
|
if (console !== 'undefined' && method in console) {
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
console[method].apply(console, args);
|
|
}
|
|
}
|
|
|
|
function randomNumber(min, max) {
|
|
var maxTimeout = 600000; // Hard-coded default of 10 minutes
|
|
min = parseInt(min, 10) || 0;
|
|
max = parseInt(max, 10);
|
|
if (max !== max || max <= min) {
|
|
max = (min || 1) << 1; //doubling
|
|
} else {
|
|
max = max + 1;
|
|
}
|
|
// In order to not exceed maxTimeout, pick a random value between half of maxTimeout and maxTimeout
|
|
if(max > maxTimeout) {
|
|
min = maxTimeout >> 1; // divide by two
|
|
max = maxTimeout;
|
|
}
|
|
var ratio = Math.random();
|
|
var range = max - min;
|
|
|
|
return ~~(range * ratio + min); // ~~ coerces to an int, but fast.
|
|
}
|
|
|
|
function defaultBackOff(min) {
|
|
var max = 0;
|
|
if (!min) {
|
|
max = 2000;
|
|
}
|
|
return randomNumber(min, max);
|
|
}
|
|
|
|
// designed to give info to browser users, who are disturbed
|
|
// when they see http errors in the console
|
|
function explainError(status, str) {
|
|
guardedConsole('info', 'The above ' + status + ' is totally normal. ' + str);
|
|
}
|
|
|
|
inherits(PouchError, Error);
|
|
|
|
function PouchError(opts) {
|
|
Error.call(this, opts.reason);
|
|
this.status = opts.status;
|
|
this.name = opts.error;
|
|
this.message = opts.reason;
|
|
this.error = true;
|
|
}
|
|
|
|
PouchError.prototype.toString = function () {
|
|
return JSON.stringify({
|
|
status: this.status,
|
|
name: this.name,
|
|
message: this.message,
|
|
reason: this.reason
|
|
});
|
|
};
|
|
|
|
var UNAUTHORIZED = new PouchError({
|
|
status: 401,
|
|
error: 'unauthorized',
|
|
reason: "Name or password is incorrect."
|
|
});
|
|
|
|
var MISSING_BULK_DOCS = new PouchError({
|
|
status: 400,
|
|
error: 'bad_request',
|
|
reason: "Missing JSON list of 'docs'"
|
|
});
|
|
|
|
var MISSING_DOC = new PouchError({
|
|
status: 404,
|
|
error: 'not_found',
|
|
reason: 'missing'
|
|
});
|
|
|
|
var REV_CONFLICT = new PouchError({
|
|
status: 409,
|
|
error: 'conflict',
|
|
reason: 'Document update conflict'
|
|
});
|
|
|
|
var INVALID_ID = new PouchError({
|
|
status: 400,
|
|
error: 'bad_request',
|
|
reason: '_id field must contain a string'
|
|
});
|
|
|
|
var MISSING_ID = new PouchError({
|
|
status: 412,
|
|
error: 'missing_id',
|
|
reason: '_id is required for puts'
|
|
});
|
|
|
|
var RESERVED_ID = new PouchError({
|
|
status: 400,
|
|
error: 'bad_request',
|
|
reason: 'Only reserved document ids may start with underscore.'
|
|
});
|
|
|
|
var NOT_OPEN = new PouchError({
|
|
status: 412,
|
|
error: 'precondition_failed',
|
|
reason: 'Database not open'
|
|
});
|
|
|
|
var UNKNOWN_ERROR = new PouchError({
|
|
status: 500,
|
|
error: 'unknown_error',
|
|
reason: 'Database encountered an unknown error'
|
|
});
|
|
|
|
var BAD_ARG = new PouchError({
|
|
status: 500,
|
|
error: 'badarg',
|
|
reason: 'Some query argument is invalid'
|
|
});
|
|
|
|
var INVALID_REQUEST = new PouchError({
|
|
status: 400,
|
|
error: 'invalid_request',
|
|
reason: 'Request was invalid'
|
|
});
|
|
|
|
var QUERY_PARSE_ERROR = new PouchError({
|
|
status: 400,
|
|
error: 'query_parse_error',
|
|
reason: 'Some query parameter is invalid'
|
|
});
|
|
|
|
var DOC_VALIDATION = new PouchError({
|
|
status: 500,
|
|
error: 'doc_validation',
|
|
reason: 'Bad special document member'
|
|
});
|
|
|
|
var BAD_REQUEST = new PouchError({
|
|
status: 400,
|
|
error: 'bad_request',
|
|
reason: 'Something wrong with the request'
|
|
});
|
|
|
|
var NOT_AN_OBJECT = new PouchError({
|
|
status: 400,
|
|
error: 'bad_request',
|
|
reason: 'Document must be a JSON object'
|
|
});
|
|
|
|
var DB_MISSING = new PouchError({
|
|
status: 404,
|
|
error: 'not_found',
|
|
reason: 'Database not found'
|
|
});
|
|
|
|
var IDB_ERROR = new PouchError({
|
|
status: 500,
|
|
error: 'indexed_db_went_bad',
|
|
reason: 'unknown'
|
|
});
|
|
|
|
var WSQ_ERROR = new PouchError({
|
|
status: 500,
|
|
error: 'web_sql_went_bad',
|
|
reason: 'unknown'
|
|
});
|
|
|
|
var LDB_ERROR = new PouchError({
|
|
status: 500,
|
|
error: 'levelDB_went_went_bad',
|
|
reason: 'unknown'
|
|
});
|
|
|
|
var FORBIDDEN = new PouchError({
|
|
status: 403,
|
|
error: 'forbidden',
|
|
reason: 'Forbidden by design doc validate_doc_update function'
|
|
});
|
|
|
|
var INVALID_REV = new PouchError({
|
|
status: 400,
|
|
error: 'bad_request',
|
|
reason: 'Invalid rev format'
|
|
});
|
|
|
|
var FILE_EXISTS = new PouchError({
|
|
status: 412,
|
|
error: 'file_exists',
|
|
reason: 'The database could not be created, the file already exists.'
|
|
});
|
|
|
|
var MISSING_STUB = new PouchError({
|
|
status: 412,
|
|
error: 'missing_stub'
|
|
});
|
|
|
|
var INVALID_URL = new PouchError({
|
|
status: 413,
|
|
error: 'invalid_url',
|
|
reason: 'Provided URL is invalid'
|
|
});
|
|
|
|
function createError(error, reason) {
|
|
function CustomPouchError(reason) {
|
|
// inherit error properties from our parent error manually
|
|
// so as to allow proper JSON parsing.
|
|
/* jshint ignore:start */
|
|
for (var p in error) {
|
|
if (typeof error[p] !== 'function') {
|
|
this[p] = error[p];
|
|
}
|
|
}
|
|
/* jshint ignore:end */
|
|
if (reason !== undefined) {
|
|
this.reason = reason;
|
|
}
|
|
}
|
|
CustomPouchError.prototype = PouchError.prototype;
|
|
return new CustomPouchError(reason);
|
|
}
|
|
|
|
function generateErrorFromResponse(err) {
|
|
|
|
if (typeof err !== 'object') {
|
|
var data = err;
|
|
err = UNKNOWN_ERROR;
|
|
err.data = data;
|
|
}
|
|
|
|
if ('error' in err && err.error === 'conflict') {
|
|
err.name = 'conflict';
|
|
err.status = 409;
|
|
}
|
|
|
|
if (!('name' in err)) {
|
|
err.name = err.error || 'unknown';
|
|
}
|
|
|
|
if (!('status' in err)) {
|
|
err.status = 500;
|
|
}
|
|
|
|
if (!('message' in err)) {
|
|
err.message = err.message || err.reason;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
function tryFilter(filter, doc, req) {
|
|
try {
|
|
return !filter(doc, req);
|
|
} catch (err) {
|
|
var msg = 'Filter function threw: ' + err.toString();
|
|
return createError(BAD_REQUEST, msg);
|
|
}
|
|
}
|
|
|
|
function filterChange(opts) {
|
|
var req = {};
|
|
var hasFilter = opts.filter && typeof opts.filter === 'function';
|
|
req.query = opts.query_params;
|
|
|
|
return function filter(change) {
|
|
if (!change.doc) {
|
|
// CSG sends events on the changes feed that don't have documents,
|
|
// this hack makes a whole lot of existing code robust.
|
|
change.doc = {};
|
|
}
|
|
|
|
var filterReturn = hasFilter && tryFilter(opts.filter, change.doc, req);
|
|
|
|
if (typeof filterReturn === 'object') {
|
|
return filterReturn;
|
|
}
|
|
|
|
if (filterReturn) {
|
|
return false;
|
|
}
|
|
|
|
if (!opts.include_docs) {
|
|
delete change.doc;
|
|
} else if (!opts.attachments) {
|
|
for (var att in change.doc._attachments) {
|
|
/* istanbul ignore else */
|
|
if (change.doc._attachments.hasOwnProperty(att)) {
|
|
change.doc._attachments[att].stub = true;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
}
|
|
|
|
function flatten(arrs) {
|
|
var res = [];
|
|
for (var i = 0, len = arrs.length; i < len; i++) {
|
|
res = res.concat(arrs[i]);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// Determine id an ID is valid
|
|
// - invalid IDs begin with an underescore that does not begin '_design' or
|
|
// '_local'
|
|
// - any other string value is a valid id
|
|
// Returns the specific error object for each case
|
|
function invalidIdError(id) {
|
|
var err;
|
|
if (!id) {
|
|
err = createError(MISSING_ID);
|
|
} else if (typeof id !== 'string') {
|
|
err = createError(INVALID_ID);
|
|
} else if (/^_/.test(id) && !(/^_(design|local)/).test(id)) {
|
|
err = createError(RESERVED_ID);
|
|
}
|
|
if (err) {
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
function listenerCount(ee, type) {
|
|
return 'listenerCount' in ee ? ee.listenerCount(type) :
|
|
events.EventEmitter.listenerCount(ee, type);
|
|
}
|
|
|
|
function parseDesignDocFunctionName(s) {
|
|
if (!s) {
|
|
return null;
|
|
}
|
|
var parts = s.split('/');
|
|
if (parts.length === 2) {
|
|
return parts;
|
|
}
|
|
if (parts.length === 1) {
|
|
return [s, s];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function normalizeDesignDocFunctionName(s) {
|
|
var normalized = parseDesignDocFunctionName(s);
|
|
return normalized ? normalized.join('/') : null;
|
|
}
|
|
|
|
// originally parseUri 1.2.2, now patched by us
|
|
// (c) Steven Levithan <stevenlevithan.com>
|
|
// MIT License
|
|
var keys = ["source", "protocol", "authority", "userInfo", "user", "password",
|
|
"host", "port", "relative", "path", "directory", "file", "query", "anchor"];
|
|
var qName ="queryKey";
|
|
var qParser = /(?:^|&)([^&=]*)=?([^&]*)/g;
|
|
|
|
// use the "loose" parser
|
|
/* jshint maxlen: false */
|
|
var parser = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
|
|
|
|
function parseUri(str) {
|
|
var m = parser.exec(str);
|
|
var uri = {};
|
|
var i = 14;
|
|
|
|
while (i--) {
|
|
var key = keys[i];
|
|
var value = m[i] || "";
|
|
var encoded = ['user', 'password'].indexOf(key) !== -1;
|
|
uri[key] = encoded ? decodeURIComponent(value) : value;
|
|
}
|
|
|
|
uri[qName] = {};
|
|
uri[keys[12]].replace(qParser, function ($0, $1, $2) {
|
|
if ($1) {
|
|
uri[qName][$1] = $2;
|
|
}
|
|
});
|
|
|
|
return uri;
|
|
}
|
|
|
|
// this is essentially the "update sugar" function from daleharvey/pouchdb#1388
|
|
// the diffFun tells us what delta to apply to the doc. it either returns
|
|
// the doc, or false if it doesn't need to do an update after all
|
|
function upsert(db, docId, diffFun) {
|
|
return new PouchPromise(function (fulfill, reject) {
|
|
db.get(docId, function (err, doc) {
|
|
if (err) {
|
|
/* istanbul ignore next */
|
|
if (err.status !== 404) {
|
|
return reject(err);
|
|
}
|
|
doc = {};
|
|
}
|
|
|
|
// the user might change the _rev, so save it for posterity
|
|
var docRev = doc._rev;
|
|
var newDoc = diffFun(doc);
|
|
|
|
if (!newDoc) {
|
|
// if the diffFun returns falsy, we short-circuit as
|
|
// an optimization
|
|
return fulfill({updated: false, rev: docRev});
|
|
}
|
|
|
|
// users aren't allowed to modify these values,
|
|
// so reset them here
|
|
newDoc._id = docId;
|
|
newDoc._rev = docRev;
|
|
fulfill(tryAndPut(db, newDoc, diffFun));
|
|
});
|
|
});
|
|
}
|
|
|
|
function tryAndPut(db, doc, diffFun) {
|
|
return db.put(doc).then(function (res) {
|
|
return {
|
|
updated: true,
|
|
rev: res.rev
|
|
};
|
|
}, function (err) {
|
|
/* istanbul ignore next */
|
|
if (err.status !== 409) {
|
|
throw err;
|
|
}
|
|
return upsert(db, doc._id, diffFun);
|
|
});
|
|
}
|
|
|
|
// BEGIN Math.uuid.js
|
|
|
|
/*!
|
|
Math.uuid.js (v1.4)
|
|
http://www.broofa.com
|
|
mailto:robert@broofa.com
|
|
|
|
Copyright (c) 2010 Robert Kieffer
|
|
Dual licensed under the MIT and GPL licenses.
|
|
*/
|
|
|
|
/*
|
|
* Generate a random uuid.
|
|
*
|
|
* USAGE: Math.uuid(length, radix)
|
|
* length - the desired number of characters
|
|
* radix - the number of allowable values for each character.
|
|
*
|
|
* EXAMPLES:
|
|
* // No arguments - returns RFC4122, version 4 ID
|
|
* >>> Math.uuid()
|
|
* "92329D39-6F5C-4520-ABFC-AAB64544E172"
|
|
*
|
|
* // One argument - returns ID of the specified length
|
|
* >>> Math.uuid(15) // 15 character ID (default base=62)
|
|
* "VcydxgltxrVZSTV"
|
|
*
|
|
* // Two arguments - returns ID of the specified length, and radix.
|
|
* // (Radix must be <= 62)
|
|
* >>> Math.uuid(8, 2) // 8 character ID (base=2)
|
|
* "01001010"
|
|
* >>> Math.uuid(8, 10) // 8 character ID (base=10)
|
|
* "47473046"
|
|
* >>> Math.uuid(8, 16) // 8 character ID (base=16)
|
|
* "098F4D35"
|
|
*/
|
|
var chars = (
|
|
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
|
|
'abcdefghijklmnopqrstuvwxyz'
|
|
).split('');
|
|
function getValue(radix) {
|
|
return 0 | Math.random() * radix;
|
|
}
|
|
function uuid(len, radix) {
|
|
radix = radix || chars.length;
|
|
var out = '';
|
|
var i = -1;
|
|
|
|
if (len) {
|
|
// Compact form
|
|
while (++i < len) {
|
|
out += chars[getValue(radix)];
|
|
}
|
|
return out;
|
|
}
|
|
// rfc4122, version 4 form
|
|
// Fill in random data. At i==19 set the high bits of clock sequence as
|
|
// per rfc4122, sec. 4.1.5
|
|
while (++i < 36) {
|
|
switch (i) {
|
|
case 8:
|
|
case 13:
|
|
case 18:
|
|
case 23:
|
|
out += '-';
|
|
break;
|
|
case 19:
|
|
out += chars[(getValue(16) & 0x3) | 0x8];
|
|
break;
|
|
default:
|
|
out += chars[getValue(16)];
|
|
}
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
// We fetch all leafs of the revision tree, and sort them based on tree length
|
|
// and whether they were deleted, undeleted documents with the longest revision
|
|
// tree (most edits) win
|
|
// The final sort algorithm is slightly documented in a sidebar here:
|
|
// http://guide.couchdb.org/draft/conflicts.html
|
|
function winningRev(metadata) {
|
|
var winningId;
|
|
var winningPos;
|
|
var winningDeleted;
|
|
var toVisit = metadata.rev_tree.slice();
|
|
var node;
|
|
while ((node = toVisit.pop())) {
|
|
var tree = node.ids;
|
|
var branches = tree[2];
|
|
var pos = node.pos;
|
|
if (branches.length) { // non-leaf
|
|
for (var i = 0, len = branches.length; i < len; i++) {
|
|
toVisit.push({pos: pos + 1, ids: branches[i]});
|
|
}
|
|
continue;
|
|
}
|
|
var deleted = !!tree[1].deleted;
|
|
var id = tree[0];
|
|
// sort by deleted, then pos, then id
|
|
if (!winningId || (winningDeleted !== deleted ? winningDeleted :
|
|
winningPos !== pos ? winningPos < pos : winningId < id)) {
|
|
winningId = id;
|
|
winningPos = pos;
|
|
winningDeleted = deleted;
|
|
}
|
|
}
|
|
|
|
return winningPos + '-' + winningId;
|
|
}
|
|
|
|
// Pretty much all below can be combined into a higher order function to
|
|
// traverse revisions
|
|
// The return value from the callback will be passed as context to all
|
|
// children of that node
|
|
function traverseRevTree(revs, callback) {
|
|
var toVisit = revs.slice();
|
|
|
|
var node;
|
|
while ((node = toVisit.pop())) {
|
|
var pos = node.pos;
|
|
var tree = node.ids;
|
|
var branches = tree[2];
|
|
var newCtx =
|
|
callback(branches.length === 0, pos, tree[0], node.ctx, tree[1]);
|
|
for (var i = 0, len = branches.length; i < len; i++) {
|
|
toVisit.push({pos: pos + 1, ids: branches[i], ctx: newCtx});
|
|
}
|
|
}
|
|
}
|
|
|
|
function sortByPos(a, b) {
|
|
return a.pos - b.pos;
|
|
}
|
|
|
|
function collectLeaves(revs) {
|
|
var leaves = [];
|
|
traverseRevTree(revs, function (isLeaf, pos, id, acc, opts) {
|
|
if (isLeaf) {
|
|
leaves.push({rev: pos + "-" + id, pos: pos, opts: opts});
|
|
}
|
|
});
|
|
leaves.sort(sortByPos).reverse();
|
|
for (var i = 0, len = leaves.length; i < len; i++) {
|
|
delete leaves[i].pos;
|
|
}
|
|
return leaves;
|
|
}
|
|
|
|
// returns revs of all conflicts that is leaves such that
|
|
// 1. are not deleted and
|
|
// 2. are different than winning revision
|
|
function collectConflicts(metadata) {
|
|
var win = winningRev(metadata);
|
|
var leaves = collectLeaves(metadata.rev_tree);
|
|
var conflicts = [];
|
|
for (var i = 0, len = leaves.length; i < len; i++) {
|
|
var leaf = leaves[i];
|
|
if (leaf.rev !== win && !leaf.opts.deleted) {
|
|
conflicts.push(leaf.rev);
|
|
}
|
|
}
|
|
return conflicts;
|
|
}
|
|
|
|
// compact a tree by marking its non-leafs as missing,
|
|
// and return a list of revs to delete
|
|
function compactTree(metadata) {
|
|
var revs = [];
|
|
traverseRevTree(metadata.rev_tree, function (isLeaf, pos,
|
|
revHash, ctx, opts) {
|
|
if (opts.status === 'available' && !isLeaf) {
|
|
revs.push(pos + '-' + revHash);
|
|
opts.status = 'missing';
|
|
}
|
|
});
|
|
return revs;
|
|
}
|
|
|
|
// build up a list of all the paths to the leafs in this revision tree
|
|
function rootToLeaf(revs) {
|
|
var paths = [];
|
|
var toVisit = revs.slice();
|
|
var node;
|
|
while ((node = toVisit.pop())) {
|
|
var pos = node.pos;
|
|
var tree = node.ids;
|
|
var id = tree[0];
|
|
var opts = tree[1];
|
|
var branches = tree[2];
|
|
var isLeaf = branches.length === 0;
|
|
|
|
var history = node.history ? node.history.slice() : [];
|
|
history.push({id: id, opts: opts});
|
|
if (isLeaf) {
|
|
paths.push({pos: (pos + 1 - history.length), ids: history});
|
|
}
|
|
for (var i = 0, len = branches.length; i < len; i++) {
|
|
toVisit.push({pos: pos + 1, ids: branches[i], history: history});
|
|
}
|
|
}
|
|
return paths.reverse();
|
|
}
|
|
|
|
function sortByPos$1(a, b) {
|
|
return a.pos - b.pos;
|
|
}
|
|
|
|
// classic binary search
|
|
function binarySearch(arr, item, comparator) {
|
|
var low = 0;
|
|
var high = arr.length;
|
|
var mid;
|
|
while (low < high) {
|
|
mid = (low + high) >>> 1;
|
|
if (comparator(arr[mid], item) < 0) {
|
|
low = mid + 1;
|
|
} else {
|
|
high = mid;
|
|
}
|
|
}
|
|
return low;
|
|
}
|
|
|
|
// assuming the arr is sorted, insert the item in the proper place
|
|
function insertSorted(arr, item, comparator) {
|
|
var idx = binarySearch(arr, item, comparator);
|
|
arr.splice(idx, 0, item);
|
|
}
|
|
|
|
// Turn a path as a flat array into a tree with a single branch.
|
|
// If any should be stemmed from the beginning of the array, that's passed
|
|
// in as the second argument
|
|
function pathToTree(path, numStemmed) {
|
|
var root;
|
|
var leaf;
|
|
for (var i = numStemmed, len = path.length; i < len; i++) {
|
|
var node = path[i];
|
|
var currentLeaf = [node.id, node.opts, []];
|
|
if (leaf) {
|
|
leaf[2].push(currentLeaf);
|
|
leaf = currentLeaf;
|
|
} else {
|
|
root = leaf = currentLeaf;
|
|
}
|
|
}
|
|
return root;
|
|
}
|
|
|
|
// compare the IDs of two trees
|
|
function compareTree(a, b) {
|
|
return a[0] < b[0] ? -1 : 1;
|
|
}
|
|
|
|
// Merge two trees together
|
|
// The roots of tree1 and tree2 must be the same revision
|
|
function mergeTree(in_tree1, in_tree2) {
|
|
var queue = [{tree1: in_tree1, tree2: in_tree2}];
|
|
var conflicts = false;
|
|
while (queue.length > 0) {
|
|
var item = queue.pop();
|
|
var tree1 = item.tree1;
|
|
var tree2 = item.tree2;
|
|
|
|
if (tree1[1].status || tree2[1].status) {
|
|
tree1[1].status =
|
|
(tree1[1].status === 'available' ||
|
|
tree2[1].status === 'available') ? 'available' : 'missing';
|
|
}
|
|
|
|
for (var i = 0; i < tree2[2].length; i++) {
|
|
if (!tree1[2][0]) {
|
|
conflicts = 'new_leaf';
|
|
tree1[2][0] = tree2[2][i];
|
|
continue;
|
|
}
|
|
|
|
var merged = false;
|
|
for (var j = 0; j < tree1[2].length; j++) {
|
|
if (tree1[2][j][0] === tree2[2][i][0]) {
|
|
queue.push({tree1: tree1[2][j], tree2: tree2[2][i]});
|
|
merged = true;
|
|
}
|
|
}
|
|
if (!merged) {
|
|
conflicts = 'new_branch';
|
|
insertSorted(tree1[2], tree2[2][i], compareTree);
|
|
}
|
|
}
|
|
}
|
|
return {conflicts: conflicts, tree: in_tree1};
|
|
}
|
|
|
|
function doMerge(tree, path, dontExpand) {
|
|
var restree = [];
|
|
var conflicts = false;
|
|
var merged = false;
|
|
var res;
|
|
|
|
if (!tree.length) {
|
|
return {tree: [path], conflicts: 'new_leaf'};
|
|
}
|
|
|
|
for (var i = 0, len = tree.length; i < len; i++) {
|
|
var branch = tree[i];
|
|
if (branch.pos === path.pos && branch.ids[0] === path.ids[0]) {
|
|
// Paths start at the same position and have the same root, so they need
|
|
// merged
|
|
res = mergeTree(branch.ids, path.ids);
|
|
restree.push({pos: branch.pos, ids: res.tree});
|
|
conflicts = conflicts || res.conflicts;
|
|
merged = true;
|
|
} else if (dontExpand !== true) {
|
|
// The paths start at a different position, take the earliest path and
|
|
// traverse up until it as at the same point from root as the path we
|
|
// want to merge. If the keys match we return the longer path with the
|
|
// other merged After stemming we dont want to expand the trees
|
|
|
|
var t1 = branch.pos < path.pos ? branch : path;
|
|
var t2 = branch.pos < path.pos ? path : branch;
|
|
var diff = t2.pos - t1.pos;
|
|
|
|
var candidateParents = [];
|
|
|
|
var trees = [];
|
|
trees.push({ids: t1.ids, diff: diff, parent: null, parentIdx: null});
|
|
while (trees.length > 0) {
|
|
var item = trees.pop();
|
|
if (item.diff === 0) {
|
|
if (item.ids[0] === t2.ids[0]) {
|
|
candidateParents.push(item);
|
|
}
|
|
continue;
|
|
}
|
|
var elements = item.ids[2];
|
|
for (var j = 0, elementsLen = elements.length; j < elementsLen; j++) {
|
|
trees.push({
|
|
ids: elements[j],
|
|
diff: item.diff - 1,
|
|
parent: item.ids,
|
|
parentIdx: j
|
|
});
|
|
}
|
|
}
|
|
|
|
var el = candidateParents[0];
|
|
|
|
if (!el) {
|
|
restree.push(branch);
|
|
} else {
|
|
res = mergeTree(el.ids, t2.ids);
|
|
el.parent[2][el.parentIdx] = res.tree;
|
|
restree.push({pos: t1.pos, ids: t1.ids});
|
|
conflicts = conflicts || res.conflicts;
|
|
merged = true;
|
|
}
|
|
} else {
|
|
restree.push(branch);
|
|
}
|
|
}
|
|
|
|
// We didnt find
|
|
if (!merged) {
|
|
restree.push(path);
|
|
}
|
|
|
|
restree.sort(sortByPos$1);
|
|
|
|
return {
|
|
tree: restree,
|
|
conflicts: conflicts || 'internal_node'
|
|
};
|
|
}
|
|
|
|
// To ensure we dont grow the revision tree infinitely, we stem old revisions
|
|
function stem(tree, depth) {
|
|
// First we break out the tree into a complete list of root to leaf paths
|
|
var paths = rootToLeaf(tree);
|
|
var maybeStem = {};
|
|
|
|
var result;
|
|
for (var i = 0, len = paths.length; i < len; i++) {
|
|
// Then for each path, we cut off the start of the path based on the
|
|
// `depth` to stem to, and generate a new set of flat trees
|
|
var path = paths[i];
|
|
var stemmed = path.ids;
|
|
var numStemmed = Math.max(0, stemmed.length - depth);
|
|
var stemmedNode = {
|
|
pos: path.pos + numStemmed,
|
|
ids: pathToTree(stemmed, numStemmed)
|
|
};
|
|
|
|
for (var s = 0; s < numStemmed; s++) {
|
|
var rev = (path.pos + s) + '-' + stemmed[s].id;
|
|
maybeStem[rev] = true;
|
|
}
|
|
|
|
// Then we remerge all those flat trees together, ensuring that we dont
|
|
// connect trees that would go beyond the depth limit
|
|
if (result) {
|
|
result = doMerge(result, stemmedNode, true).tree;
|
|
} else {
|
|
result = [stemmedNode];
|
|
}
|
|
}
|
|
|
|
traverseRevTree(result, function (isLeaf, pos, revHash) {
|
|
// some revisions may have been removed in a branch but not in another
|
|
delete maybeStem[pos + '-' + revHash];
|
|
});
|
|
|
|
return {
|
|
tree: result,
|
|
revs: Object.keys(maybeStem)
|
|
};
|
|
}
|
|
|
|
function merge(tree, path, depth) {
|
|
var newTree = doMerge(tree, path);
|
|
var stemmed = stem(newTree.tree, depth);
|
|
return {
|
|
tree: stemmed.tree,
|
|
stemmedRevs: stemmed.revs,
|
|
conflicts: newTree.conflicts
|
|
};
|
|
}
|
|
|
|
// return true if a rev exists in the rev tree, false otherwise
|
|
function revExists(revs, rev) {
|
|
var toVisit = revs.slice();
|
|
var splitRev = rev.split('-');
|
|
var targetPos = parseInt(splitRev[0], 10);
|
|
var targetId = splitRev[1];
|
|
|
|
var node;
|
|
while ((node = toVisit.pop())) {
|
|
if (node.pos === targetPos && node.ids[0] === targetId) {
|
|
return true;
|
|
}
|
|
var branches = node.ids[2];
|
|
for (var i = 0, len = branches.length; i < len; i++) {
|
|
toVisit.push({pos: node.pos + 1, ids: branches[i]});
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function getTrees(node) {
|
|
return node.ids;
|
|
}
|
|
|
|
// check if a specific revision of a doc has been deleted
|
|
// - metadata: the metadata object from the doc store
|
|
// - rev: (optional) the revision to check. defaults to winning revision
|
|
function isDeleted(metadata, rev) {
|
|
if (!rev) {
|
|
rev = winningRev(metadata);
|
|
}
|
|
var id = rev.substring(rev.indexOf('-') + 1);
|
|
var toVisit = metadata.rev_tree.map(getTrees);
|
|
|
|
var tree;
|
|
while ((tree = toVisit.pop())) {
|
|
if (tree[0] === id) {
|
|
return !!tree[1].deleted;
|
|
}
|
|
toVisit = toVisit.concat(tree[2]);
|
|
}
|
|
}
|
|
|
|
function isLocalId(id) {
|
|
return (/^_local/).test(id);
|
|
}
|
|
|
|
function evalFilter(input) {
|
|
return scopedEval('return ' + input + ';', {});
|
|
}
|
|
|
|
function evalView(input) {
|
|
/* jshint evil:true */
|
|
return new Function('doc', [
|
|
'var emitted = false;',
|
|
'var emit = function (a, b) {',
|
|
' emitted = true;',
|
|
'};',
|
|
'var view = ' + input + ';',
|
|
'view(doc);',
|
|
'if (emitted) {',
|
|
' return true;',
|
|
'}'
|
|
].join('\n'));
|
|
}
|
|
|
|
inherits(Changes, events.EventEmitter);
|
|
|
|
function tryCatchInChangeListener(self, change) {
|
|
// isolate try/catches to avoid V8 deoptimizations
|
|
try {
|
|
self.emit('change', change);
|
|
} catch (e) {
|
|
guardedConsole('error', 'Error in .on("change", function):', e);
|
|
}
|
|
}
|
|
|
|
function Changes(db, opts, callback) {
|
|
events.EventEmitter.call(this);
|
|
var self = this;
|
|
this.db = db;
|
|
opts = opts ? clone(opts) : {};
|
|
var complete = opts.complete = once(function (err, resp) {
|
|
if (err) {
|
|
if (listenerCount(self, 'error') > 0) {
|
|
self.emit('error', err);
|
|
}
|
|
} else {
|
|
self.emit('complete', resp);
|
|
}
|
|
self.removeAllListeners();
|
|
db.removeListener('destroyed', onDestroy);
|
|
});
|
|
if (callback) {
|
|
self.on('complete', function (resp) {
|
|
callback(null, resp);
|
|
});
|
|
self.on('error', callback);
|
|
}
|
|
function onDestroy() {
|
|
self.cancel();
|
|
}
|
|
db.once('destroyed', onDestroy);
|
|
|
|
opts.onChange = function (change) {
|
|
/* istanbul ignore if */
|
|
if (opts.isCancelled) {
|
|
return;
|
|
}
|
|
tryCatchInChangeListener(self, change);
|
|
if (self.startSeq && self.startSeq <= change.seq) {
|
|
self.startSeq = false;
|
|
}
|
|
};
|
|
|
|
var promise = new PouchPromise(function (fulfill, reject) {
|
|
opts.complete = function (err, res) {
|
|
if (err) {
|
|
reject(err);
|
|
} else {
|
|
fulfill(res);
|
|
}
|
|
};
|
|
});
|
|
self.once('cancel', function () {
|
|
db.removeListener('destroyed', onDestroy);
|
|
opts.complete(null, {status: 'cancelled'});
|
|
});
|
|
this.then = promise.then.bind(promise);
|
|
this['catch'] = promise['catch'].bind(promise);
|
|
this.then(function (result) {
|
|
complete(null, result);
|
|
}, complete);
|
|
|
|
|
|
|
|
if (!db.taskqueue.isReady) {
|
|
db.taskqueue.addTask(function () {
|
|
if (self.isCancelled) {
|
|
self.emit('cancel');
|
|
} else {
|
|
self.doChanges(opts);
|
|
}
|
|
});
|
|
} else {
|
|
self.doChanges(opts);
|
|
}
|
|
}
|
|
Changes.prototype.cancel = function () {
|
|
this.isCancelled = true;
|
|
if (this.db.taskqueue.isReady) {
|
|
this.emit('cancel');
|
|
}
|
|
};
|
|
function processChange(doc, metadata, opts) {
|
|
var changeList = [{rev: doc._rev}];
|
|
if (opts.style === 'all_docs') {
|
|
changeList = collectLeaves(metadata.rev_tree)
|
|
.map(function (x) { return {rev: x.rev}; });
|
|
}
|
|
var change = {
|
|
id: metadata.id,
|
|
changes: changeList,
|
|
doc: doc
|
|
};
|
|
|
|
if (isDeleted(metadata, doc._rev)) {
|
|
change.deleted = true;
|
|
}
|
|
if (opts.conflicts) {
|
|
change.doc._conflicts = collectConflicts(metadata);
|
|
if (!change.doc._conflicts.length) {
|
|
delete change.doc._conflicts;
|
|
}
|
|
}
|
|
return change;
|
|
}
|
|
|
|
Changes.prototype.doChanges = function (opts) {
|
|
var self = this;
|
|
var callback = opts.complete;
|
|
|
|
opts = clone(opts);
|
|
if ('live' in opts && !('continuous' in opts)) {
|
|
opts.continuous = opts.live;
|
|
}
|
|
opts.processChange = processChange;
|
|
|
|
if (opts.since === 'latest') {
|
|
opts.since = 'now';
|
|
}
|
|
if (!opts.since) {
|
|
opts.since = 0;
|
|
}
|
|
if (opts.since === 'now') {
|
|
this.db.info().then(function (info) {
|
|
/* istanbul ignore if */
|
|
if (self.isCancelled) {
|
|
callback(null, {status: 'cancelled'});
|
|
return;
|
|
}
|
|
opts.since = info.update_seq;
|
|
self.doChanges(opts);
|
|
}, callback);
|
|
return;
|
|
}
|
|
|
|
if (opts.continuous && opts.since !== 'now') {
|
|
this.db.info().then(function (info) {
|
|
self.startSeq = info.update_seq;
|
|
/* istanbul ignore next */
|
|
}, function (err) {
|
|
if (err.id === 'idbNull') {
|
|
// db closed before this returned thats ok
|
|
return;
|
|
}
|
|
throw err;
|
|
});
|
|
}
|
|
|
|
if (opts.view && !opts.filter) {
|
|
opts.filter = '_view';
|
|
}
|
|
|
|
if (opts.filter && typeof opts.filter === 'string') {
|
|
if (opts.filter === '_view') {
|
|
opts.view = normalizeDesignDocFunctionName(opts.view);
|
|
} else {
|
|
opts.filter = normalizeDesignDocFunctionName(opts.filter);
|
|
}
|
|
|
|
if (this.db.type() !== 'http' && !opts.doc_ids) {
|
|
return this.filterChanges(opts);
|
|
}
|
|
}
|
|
|
|
if (!('descending' in opts)) {
|
|
opts.descending = false;
|
|
}
|
|
|
|
// 0 and 1 should return 1 document
|
|
opts.limit = opts.limit === 0 ? 1 : opts.limit;
|
|
opts.complete = callback;
|
|
var newPromise = this.db._changes(opts);
|
|
if (newPromise && typeof newPromise.cancel === 'function') {
|
|
var cancel = self.cancel;
|
|
self.cancel = getArguments(function (args) {
|
|
newPromise.cancel();
|
|
cancel.apply(this, args);
|
|
});
|
|
}
|
|
};
|
|
|
|
Changes.prototype.filterChanges = function (opts) {
|
|
var self = this;
|
|
var callback = opts.complete;
|
|
if (opts.filter === '_view') {
|
|
if (!opts.view || typeof opts.view !== 'string') {
|
|
var err = createError(BAD_REQUEST,
|
|
'`view` filter parameter not found or invalid.');
|
|
return callback(err);
|
|
}
|
|
// fetch a view from a design doc, make it behave like a filter
|
|
var viewName = parseDesignDocFunctionName(opts.view);
|
|
this.db.get('_design/' + viewName[0], function (err, ddoc) {
|
|
/* istanbul ignore if */
|
|
if (self.isCancelled) {
|
|
return callback(null, {status: 'cancelled'});
|
|
}
|
|
/* istanbul ignore next */
|
|
if (err) {
|
|
return callback(generateErrorFromResponse(err));
|
|
}
|
|
var mapFun = ddoc && ddoc.views && ddoc.views[viewName[1]] &&
|
|
ddoc.views[viewName[1]].map;
|
|
if (!mapFun) {
|
|
return callback(createError(MISSING_DOC,
|
|
(ddoc.views ? 'missing json key: ' + viewName[1] :
|
|
'missing json key: views')));
|
|
}
|
|
opts.filter = evalView(mapFun);
|
|
self.doChanges(opts);
|
|
});
|
|
} else {
|
|
// fetch a filter from a design doc
|
|
var filterName = parseDesignDocFunctionName(opts.filter);
|
|
if (!filterName) {
|
|
return self.doChanges(opts);
|
|
}
|
|
this.db.get('_design/' + filterName[0], function (err, ddoc) {
|
|
/* istanbul ignore if */
|
|
if (self.isCancelled) {
|
|
return callback(null, {status: 'cancelled'});
|
|
}
|
|
/* istanbul ignore next */
|
|
if (err) {
|
|
return callback(generateErrorFromResponse(err));
|
|
}
|
|
var filterFun = ddoc && ddoc.filters && ddoc.filters[filterName[1]];
|
|
if (!filterFun) {
|
|
return callback(createError(MISSING_DOC,
|
|
((ddoc && ddoc.filters) ? 'missing json key: ' + filterName[1]
|
|
: 'missing json key: filters')));
|
|
}
|
|
opts.filter = evalFilter(filterFun);
|
|
self.doChanges(opts);
|
|
});
|
|
}
|
|
};
|
|
|
|
/*
|
|
* A generic pouch adapter
|
|
*/
|
|
|
|
function compare(left, right) {
|
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
}
|
|
|
|
// returns first element of arr satisfying callback predicate
|
|
function arrayFirst(arr, callback) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
if (callback(arr[i], i) === true) {
|
|
return arr[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wrapper for functions that call the bulkdocs api with a single doc,
|
|
// if the first result is an error, return an error
|
|
function yankError(callback) {
|
|
return function (err, results) {
|
|
if (err || (results[0] && results[0].error)) {
|
|
callback(err || results[0]);
|
|
} else {
|
|
callback(null, results.length ? results[0] : results);
|
|
}
|
|
};
|
|
}
|
|
|
|
// clean docs given to us by the user
|
|
function cleanDocs(docs) {
|
|
for (var i = 0; i < docs.length; i++) {
|
|
var doc = docs[i];
|
|
if (doc._deleted) {
|
|
delete doc._attachments; // ignore atts for deleted docs
|
|
} else if (doc._attachments) {
|
|
// filter out extraneous keys from _attachments
|
|
var atts = Object.keys(doc._attachments);
|
|
for (var j = 0; j < atts.length; j++) {
|
|
var att = atts[j];
|
|
doc._attachments[att] = pick(doc._attachments[att],
|
|
['data', 'digest', 'content_type', 'length', 'revpos', 'stub']);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// compare two docs, first by _id then by _rev
|
|
function compareByIdThenRev(a, b) {
|
|
var idCompare = compare(a._id, b._id);
|
|
if (idCompare !== 0) {
|
|
return idCompare;
|
|
}
|
|
var aStart = a._revisions ? a._revisions.start : 0;
|
|
var bStart = b._revisions ? b._revisions.start : 0;
|
|
return compare(aStart, bStart);
|
|
}
|
|
|
|
// for every node in a revision tree computes its distance from the closest
|
|
// leaf
|
|
function computeHeight(revs) {
|
|
var height = {};
|
|
var edges = [];
|
|
traverseRevTree(revs, function (isLeaf, pos, id, prnt) {
|
|
var rev = pos + "-" + id;
|
|
if (isLeaf) {
|
|
height[rev] = 0;
|
|
}
|
|
if (prnt !== undefined) {
|
|
edges.push({from: prnt, to: rev});
|
|
}
|
|
return rev;
|
|
});
|
|
|
|
edges.reverse();
|
|
edges.forEach(function (edge) {
|
|
if (height[edge.from] === undefined) {
|
|
height[edge.from] = 1 + height[edge.to];
|
|
} else {
|
|
height[edge.from] = Math.min(height[edge.from], 1 + height[edge.to]);
|
|
}
|
|
});
|
|
return height;
|
|
}
|
|
|
|
function allDocsKeysQuery(api, opts, callback) {
|
|
var keys = ('limit' in opts) ?
|
|
opts.keys.slice(opts.skip, opts.limit + opts.skip) :
|
|
(opts.skip > 0) ? opts.keys.slice(opts.skip) : opts.keys;
|
|
if (opts.descending) {
|
|
keys.reverse();
|
|
}
|
|
if (!keys.length) {
|
|
return api._allDocs({limit: 0}, callback);
|
|
}
|
|
var finalResults = {
|
|
offset: opts.skip
|
|
};
|
|
return PouchPromise.all(keys.map(function (key) {
|
|
var subOpts = jsExtend.extend({key: key, deleted: 'ok'}, opts);
|
|
['limit', 'skip', 'keys'].forEach(function (optKey) {
|
|
delete subOpts[optKey];
|
|
});
|
|
return new PouchPromise(function (resolve, reject) {
|
|
api._allDocs(subOpts, function (err, res) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
finalResults.total_rows = res.total_rows;
|
|
resolve(res.rows[0] || {key: key, error: 'not_found'});
|
|
});
|
|
});
|
|
})).then(function (results) {
|
|
finalResults.rows = results;
|
|
return finalResults;
|
|
});
|
|
}
|
|
|
|
// all compaction is done in a queue, to avoid attaching
|
|
// too many listeners at once
|
|
function doNextCompaction(self) {
|
|
var task = self._compactionQueue[0];
|
|
var opts = task.opts;
|
|
var callback = task.callback;
|
|
self.get('_local/compaction').catch(function () {
|
|
return false;
|
|
}).then(function (doc) {
|
|
if (doc && doc.last_seq) {
|
|
opts.last_seq = doc.last_seq;
|
|
}
|
|
self._compact(opts, function (err, res) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
callback(null, res);
|
|
}
|
|
process.nextTick(function () {
|
|
self._compactionQueue.shift();
|
|
if (self._compactionQueue.length) {
|
|
doNextCompaction(self);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
function attachmentNameError(name) {
|
|
if (name.charAt(0) === '_') {
|
|
return name + 'is not a valid attachment name, attachment ' +
|
|
'names cannot start with \'_\'';
|
|
}
|
|
return false;
|
|
}
|
|
|
|
inherits(AbstractPouchDB, events.EventEmitter);
|
|
|
|
function AbstractPouchDB() {
|
|
events.EventEmitter.call(this);
|
|
}
|
|
|
|
AbstractPouchDB.prototype.post =
|
|
adapterFun('post', function (doc, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
if (typeof doc !== 'object' || Array.isArray(doc)) {
|
|
return callback(createError(NOT_AN_OBJECT));
|
|
}
|
|
this.bulkDocs({docs: [doc]}, opts, yankError(callback));
|
|
});
|
|
|
|
AbstractPouchDB.prototype.put =
|
|
adapterFun('put', getArguments(function (args) {
|
|
var temp, temptype, opts, callback;
|
|
var warned = false;
|
|
var doc = args.shift();
|
|
var id = '_id' in doc;
|
|
if (typeof doc !== 'object' || Array.isArray(doc)) {
|
|
callback = args.pop();
|
|
return callback(createError(NOT_AN_OBJECT));
|
|
}
|
|
|
|
function warn() {
|
|
if (warned) {
|
|
return;
|
|
}
|
|
guardedConsole('warn', 'db.put(doc, id, rev) has been deprecated and will be ' +
|
|
'removed in a future release, please use ' +
|
|
'db.put({_id: id, _rev: rev}) instead');
|
|
warned = true;
|
|
}
|
|
|
|
/* eslint no-constant-condition: 0 */
|
|
while (true) {
|
|
temp = args.shift();
|
|
temptype = typeof temp;
|
|
if (temptype === "string" && !id) {
|
|
warn();
|
|
doc._id = temp;
|
|
id = true;
|
|
} else if (temptype === "string" && id && !('_rev' in doc)) {
|
|
warn();
|
|
doc._rev = temp;
|
|
} else if (temptype === "object") {
|
|
opts = temp;
|
|
} else if (temptype === "function") {
|
|
callback = temp;
|
|
}
|
|
if (!args.length) {
|
|
break;
|
|
}
|
|
}
|
|
opts = opts || {};
|
|
invalidIdError(doc._id);
|
|
if (isLocalId(doc._id) && typeof this._putLocal === 'function') {
|
|
if (doc._deleted) {
|
|
return this._removeLocal(doc, callback);
|
|
} else {
|
|
return this._putLocal(doc, callback);
|
|
}
|
|
}
|
|
this.bulkDocs({docs: [doc]}, opts, yankError(callback));
|
|
}));
|
|
|
|
AbstractPouchDB.prototype.putAttachment =
|
|
adapterFun('putAttachment', function (docId, attachmentId, rev,
|
|
blob, type) {
|
|
var api = this;
|
|
if (typeof type === 'function') {
|
|
type = blob;
|
|
blob = rev;
|
|
rev = null;
|
|
}
|
|
// Lets fix in https://github.com/pouchdb/pouchdb/issues/3267
|
|
/* istanbul ignore if */
|
|
if (typeof type === 'undefined') {
|
|
type = blob;
|
|
blob = rev;
|
|
rev = null;
|
|
}
|
|
|
|
function createAttachment(doc) {
|
|
var prevrevpos = '_rev' in doc ? parseInt(doc._rev, 10) : 0;
|
|
doc._attachments = doc._attachments || {};
|
|
doc._attachments[attachmentId] = {
|
|
content_type: type,
|
|
data: blob,
|
|
revpos: ++prevrevpos
|
|
};
|
|
return api.put(doc);
|
|
}
|
|
|
|
return api.get(docId).then(function (doc) {
|
|
if (doc._rev !== rev) {
|
|
throw createError(REV_CONFLICT);
|
|
}
|
|
|
|
return createAttachment(doc);
|
|
}, function (err) {
|
|
// create new doc
|
|
/* istanbul ignore else */
|
|
if (err.reason === MISSING_DOC.message) {
|
|
return createAttachment({_id: docId});
|
|
} else {
|
|
throw err;
|
|
}
|
|
});
|
|
});
|
|
|
|
AbstractPouchDB.prototype.removeAttachment =
|
|
adapterFun('removeAttachment', function (docId, attachmentId, rev,
|
|
callback) {
|
|
var self = this;
|
|
self.get(docId, function (err, obj) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
callback(err);
|
|
return;
|
|
}
|
|
if (obj._rev !== rev) {
|
|
callback(createError(REV_CONFLICT));
|
|
return;
|
|
}
|
|
/* istanbul ignore if */
|
|
if (!obj._attachments) {
|
|
return callback();
|
|
}
|
|
delete obj._attachments[attachmentId];
|
|
if (Object.keys(obj._attachments).length === 0) {
|
|
delete obj._attachments;
|
|
}
|
|
self.put(obj, callback);
|
|
});
|
|
});
|
|
|
|
AbstractPouchDB.prototype.remove =
|
|
adapterFun('remove', function (docOrId, optsOrRev, opts, callback) {
|
|
var doc;
|
|
if (typeof optsOrRev === 'string') {
|
|
// id, rev, opts, callback style
|
|
doc = {
|
|
_id: docOrId,
|
|
_rev: optsOrRev
|
|
};
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
} else {
|
|
// doc, opts, callback style
|
|
doc = docOrId;
|
|
if (typeof optsOrRev === 'function') {
|
|
callback = optsOrRev;
|
|
opts = {};
|
|
} else {
|
|
callback = opts;
|
|
opts = optsOrRev;
|
|
}
|
|
}
|
|
opts = opts || {};
|
|
opts.was_delete = true;
|
|
var newDoc = {_id: doc._id, _rev: (doc._rev || opts.rev)};
|
|
newDoc._deleted = true;
|
|
if (isLocalId(newDoc._id) && typeof this._removeLocal === 'function') {
|
|
return this._removeLocal(doc, callback);
|
|
}
|
|
this.bulkDocs({docs: [newDoc]}, opts, yankError(callback));
|
|
});
|
|
|
|
AbstractPouchDB.prototype.revsDiff =
|
|
adapterFun('revsDiff', function (req, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
var ids = Object.keys(req);
|
|
|
|
if (!ids.length) {
|
|
return callback(null, {});
|
|
}
|
|
|
|
var count = 0;
|
|
var missing = new pouchdbCollections.Map();
|
|
|
|
function addToMissing(id, revId) {
|
|
if (!missing.has(id)) {
|
|
missing.set(id, {missing: []});
|
|
}
|
|
missing.get(id).missing.push(revId);
|
|
}
|
|
|
|
function processDoc(id, rev_tree) {
|
|
// Is this fast enough? Maybe we should switch to a set simulated by a map
|
|
var missingForId = req[id].slice(0);
|
|
traverseRevTree(rev_tree, function (isLeaf, pos, revHash, ctx,
|
|
opts) {
|
|
var rev = pos + '-' + revHash;
|
|
var idx = missingForId.indexOf(rev);
|
|
if (idx === -1) {
|
|
return;
|
|
}
|
|
|
|
missingForId.splice(idx, 1);
|
|
/* istanbul ignore if */
|
|
if (opts.status !== 'available') {
|
|
addToMissing(id, rev);
|
|
}
|
|
});
|
|
|
|
// Traversing the tree is synchronous, so now `missingForId` contains
|
|
// revisions that were not found in the tree
|
|
missingForId.forEach(function (rev) {
|
|
addToMissing(id, rev);
|
|
});
|
|
}
|
|
|
|
ids.map(function (id) {
|
|
this._getRevisionTree(id, function (err, rev_tree) {
|
|
if (err && err.status === 404 && err.message === 'missing') {
|
|
missing.set(id, {missing: req[id]});
|
|
} else if (err) {
|
|
/* istanbul ignore next */
|
|
return callback(err);
|
|
} else {
|
|
processDoc(id, rev_tree);
|
|
}
|
|
|
|
if (++count === ids.length) {
|
|
// convert LazyMap to object
|
|
var missingObj = {};
|
|
missing.forEach(function (value, key) {
|
|
missingObj[key] = value;
|
|
});
|
|
return callback(null, missingObj);
|
|
}
|
|
});
|
|
}, this);
|
|
});
|
|
|
|
// _bulk_get API for faster replication, as described in
|
|
// https://github.com/apache/couchdb-chttpd/pull/33
|
|
// At the "abstract" level, it will just run multiple get()s in
|
|
// parallel, because this isn't much of a performance cost
|
|
// for local databases (except the cost of multiple transactions, which is
|
|
// small). The http adapter overrides this in order
|
|
// to do a more efficient single HTTP request.
|
|
AbstractPouchDB.prototype.bulkGet =
|
|
adapterFun('bulkGet', function (opts, callback) {
|
|
bulkGet(this, opts, callback);
|
|
});
|
|
|
|
// compact one document and fire callback
|
|
// by compacting we mean removing all revisions which
|
|
// are further from the leaf in revision tree than max_height
|
|
AbstractPouchDB.prototype.compactDocument =
|
|
adapterFun('compactDocument', function (docId, maxHeight, callback) {
|
|
var self = this;
|
|
this._getRevisionTree(docId, function (err, revTree) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
var height = computeHeight(revTree);
|
|
var candidates = [];
|
|
var revs = [];
|
|
Object.keys(height).forEach(function (rev) {
|
|
if (height[rev] > maxHeight) {
|
|
candidates.push(rev);
|
|
}
|
|
});
|
|
|
|
traverseRevTree(revTree, function (isLeaf, pos, revHash, ctx, opts) {
|
|
var rev = pos + '-' + revHash;
|
|
if (opts.status === 'available' && candidates.indexOf(rev) !== -1) {
|
|
revs.push(rev);
|
|
}
|
|
});
|
|
self._doCompaction(docId, revs, callback);
|
|
});
|
|
});
|
|
|
|
// compact the whole database using single document
|
|
// compaction
|
|
AbstractPouchDB.prototype.compact =
|
|
adapterFun('compact', function (opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
|
|
var self = this;
|
|
opts = opts || {};
|
|
|
|
self._compactionQueue = self._compactionQueue || [];
|
|
self._compactionQueue.push({opts: opts, callback: callback});
|
|
if (self._compactionQueue.length === 1) {
|
|
doNextCompaction(self);
|
|
}
|
|
});
|
|
AbstractPouchDB.prototype._compact = function (opts, callback) {
|
|
var self = this;
|
|
var changesOpts = {
|
|
return_docs: false,
|
|
last_seq: opts.last_seq || 0
|
|
};
|
|
var promises = [];
|
|
|
|
function onChange(row) {
|
|
promises.push(self.compactDocument(row.id, 0));
|
|
}
|
|
function onComplete(resp) {
|
|
var lastSeq = resp.last_seq;
|
|
PouchPromise.all(promises).then(function () {
|
|
return upsert(self, '_local/compaction', function deltaFunc(doc) {
|
|
if (!doc.last_seq || doc.last_seq < lastSeq) {
|
|
doc.last_seq = lastSeq;
|
|
return doc;
|
|
}
|
|
return false; // somebody else got here first, don't update
|
|
});
|
|
}).then(function () {
|
|
callback(null, {ok: true});
|
|
}).catch(callback);
|
|
}
|
|
self.changes(changesOpts)
|
|
.on('change', onChange)
|
|
.on('complete', onComplete)
|
|
.on('error', callback);
|
|
};
|
|
/* Begin api wrappers. Specific functionality to storage belongs in the
|
|
_[method] */
|
|
AbstractPouchDB.prototype.get =
|
|
adapterFun('get', function (id, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
if (typeof id !== 'string') {
|
|
return callback(createError(INVALID_ID));
|
|
}
|
|
if (isLocalId(id) && typeof this._getLocal === 'function') {
|
|
return this._getLocal(id, callback);
|
|
}
|
|
var leaves = [], self = this;
|
|
|
|
function finishOpenRevs() {
|
|
var result = [];
|
|
var count = leaves.length;
|
|
/* istanbul ignore if */
|
|
if (!count) {
|
|
return callback(null, result);
|
|
}
|
|
// order with open_revs is unspecified
|
|
leaves.forEach(function (leaf) {
|
|
self.get(id, {
|
|
rev: leaf,
|
|
revs: opts.revs,
|
|
attachments: opts.attachments
|
|
}, function (err, doc) {
|
|
if (!err) {
|
|
result.push({ok: doc});
|
|
} else {
|
|
result.push({missing: leaf});
|
|
}
|
|
count--;
|
|
if (!count) {
|
|
callback(null, result);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
if (opts.open_revs) {
|
|
if (opts.open_revs === "all") {
|
|
this._getRevisionTree(id, function (err, rev_tree) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
leaves = collectLeaves(rev_tree).map(function (leaf) {
|
|
return leaf.rev;
|
|
});
|
|
finishOpenRevs();
|
|
});
|
|
} else {
|
|
if (Array.isArray(opts.open_revs)) {
|
|
leaves = opts.open_revs;
|
|
for (var i = 0; i < leaves.length; i++) {
|
|
var l = leaves[i];
|
|
// looks like it's the only thing couchdb checks
|
|
if (!(typeof (l) === "string" && /^\d+-/.test(l))) {
|
|
return callback(createError(INVALID_REV));
|
|
}
|
|
}
|
|
finishOpenRevs();
|
|
} else {
|
|
return callback(createError(UNKNOWN_ERROR,
|
|
'function_clause'));
|
|
}
|
|
}
|
|
return; // open_revs does not like other options
|
|
}
|
|
|
|
return this._get(id, opts, function (err, result) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
|
|
var doc = result.doc;
|
|
var metadata = result.metadata;
|
|
var ctx = result.ctx;
|
|
|
|
if (opts.conflicts) {
|
|
var conflicts = collectConflicts(metadata);
|
|
if (conflicts.length) {
|
|
doc._conflicts = conflicts;
|
|
}
|
|
}
|
|
|
|
if (isDeleted(metadata, doc._rev)) {
|
|
doc._deleted = true;
|
|
}
|
|
|
|
if (opts.revs || opts.revs_info) {
|
|
var paths = rootToLeaf(metadata.rev_tree);
|
|
var path = arrayFirst(paths, function (arr) {
|
|
return arr.ids.map(function (x) { return x.id; })
|
|
.indexOf(doc._rev.split('-')[1]) !== -1;
|
|
});
|
|
|
|
var indexOfRev = path.ids.map(function (x) {return x.id; })
|
|
.indexOf(doc._rev.split('-')[1]) + 1;
|
|
var howMany = path.ids.length - indexOfRev;
|
|
path.ids.splice(indexOfRev, howMany);
|
|
path.ids.reverse();
|
|
|
|
if (opts.revs) {
|
|
doc._revisions = {
|
|
start: (path.pos + path.ids.length) - 1,
|
|
ids: path.ids.map(function (rev) {
|
|
return rev.id;
|
|
})
|
|
};
|
|
}
|
|
if (opts.revs_info) {
|
|
var pos = path.pos + path.ids.length;
|
|
doc._revs_info = path.ids.map(function (rev) {
|
|
pos--;
|
|
return {
|
|
rev: pos + '-' + rev.id,
|
|
status: rev.opts.status
|
|
};
|
|
});
|
|
}
|
|
}
|
|
|
|
if (opts.attachments && doc._attachments) {
|
|
var attachments = doc._attachments;
|
|
var count = Object.keys(attachments).length;
|
|
if (count === 0) {
|
|
return callback(null, doc);
|
|
}
|
|
Object.keys(attachments).forEach(function (key) {
|
|
this._getAttachment(doc._id, key, attachments[key], {
|
|
// Previously the revision handling was done in adapter.js
|
|
// getAttachment, however since idb-next doesnt we need to
|
|
// pass the rev through
|
|
rev: doc._rev,
|
|
binary: opts.binary,
|
|
ctx: ctx
|
|
}, function (err, data) {
|
|
var att = doc._attachments[key];
|
|
att.data = data;
|
|
delete att.stub;
|
|
delete att.length;
|
|
if (!--count) {
|
|
callback(null, doc);
|
|
}
|
|
});
|
|
}, self);
|
|
} else {
|
|
if (doc._attachments) {
|
|
for (var key in doc._attachments) {
|
|
/* istanbul ignore else */
|
|
if (doc._attachments.hasOwnProperty(key)) {
|
|
doc._attachments[key].stub = true;
|
|
}
|
|
}
|
|
}
|
|
callback(null, doc);
|
|
}
|
|
});
|
|
});
|
|
|
|
// TODO: I dont like this, it forces an extra read for every
|
|
// attachment read and enforces a confusing api between
|
|
// adapter.js and the adapter implementation
|
|
AbstractPouchDB.prototype.getAttachment =
|
|
adapterFun('getAttachment', function (docId, attachmentId, opts,
|
|
callback) {
|
|
var self = this;
|
|
if (opts instanceof Function) {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
this._get(docId, opts, function (err, res) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (res.doc._attachments && res.doc._attachments[attachmentId]) {
|
|
opts.ctx = res.ctx;
|
|
opts.binary = true;
|
|
self._getAttachment(docId, attachmentId,
|
|
res.doc._attachments[attachmentId], opts, callback);
|
|
} else {
|
|
return callback(createError(MISSING_DOC));
|
|
}
|
|
});
|
|
});
|
|
|
|
AbstractPouchDB.prototype.allDocs =
|
|
adapterFun('allDocs', function (opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
opts.skip = typeof opts.skip !== 'undefined' ? opts.skip : 0;
|
|
if (opts.start_key) {
|
|
opts.startkey = opts.start_key;
|
|
}
|
|
if (opts.end_key) {
|
|
opts.endkey = opts.end_key;
|
|
}
|
|
if ('keys' in opts) {
|
|
if (!Array.isArray(opts.keys)) {
|
|
return callback(new TypeError('options.keys must be an array'));
|
|
}
|
|
var incompatibleOpt =
|
|
['startkey', 'endkey', 'key'].filter(function (incompatibleOpt) {
|
|
return incompatibleOpt in opts;
|
|
})[0];
|
|
if (incompatibleOpt) {
|
|
callback(createError(QUERY_PARSE_ERROR,
|
|
'Query parameter `' + incompatibleOpt +
|
|
'` is not compatible with multi-get'
|
|
));
|
|
return;
|
|
}
|
|
if (this.type() !== 'http') {
|
|
return allDocsKeysQuery(this, opts, callback);
|
|
}
|
|
}
|
|
|
|
return this._allDocs(opts, callback);
|
|
});
|
|
|
|
AbstractPouchDB.prototype.changes = function (opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
return new Changes(this, opts, callback);
|
|
};
|
|
|
|
AbstractPouchDB.prototype.close =
|
|
adapterFun('close', function (callback) {
|
|
this._closed = true;
|
|
return this._close(callback);
|
|
});
|
|
|
|
AbstractPouchDB.prototype.info = adapterFun('info', function (callback) {
|
|
var self = this;
|
|
this._info(function (err, info) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
// assume we know better than the adapter, unless it informs us
|
|
info.db_name = info.db_name || self._db_name;
|
|
info.auto_compaction = !!(self.auto_compaction && self.type() !== 'http');
|
|
info.adapter = self.type();
|
|
callback(null, info);
|
|
});
|
|
});
|
|
|
|
AbstractPouchDB.prototype.id = adapterFun('id', function (callback) {
|
|
return this._id(callback);
|
|
});
|
|
|
|
AbstractPouchDB.prototype.type = function () {
|
|
/* istanbul ignore next */
|
|
return (typeof this._type === 'function') ? this._type() : this.adapter;
|
|
};
|
|
|
|
AbstractPouchDB.prototype.bulkDocs =
|
|
adapterFun('bulkDocs', function (req, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
|
|
opts = opts || {};
|
|
|
|
if (Array.isArray(req)) {
|
|
req = {
|
|
docs: req
|
|
};
|
|
}
|
|
|
|
if (!req || !req.docs || !Array.isArray(req.docs)) {
|
|
return callback(createError(MISSING_BULK_DOCS));
|
|
}
|
|
|
|
for (var i = 0; i < req.docs.length; ++i) {
|
|
if (typeof req.docs[i] !== 'object' || Array.isArray(req.docs[i])) {
|
|
return callback(createError(NOT_AN_OBJECT));
|
|
}
|
|
}
|
|
|
|
var attachmentError;
|
|
req.docs.forEach(function (doc) {
|
|
if (doc._attachments) {
|
|
Object.keys(doc._attachments).forEach(function (name) {
|
|
attachmentError = attachmentError || attachmentNameError(name);
|
|
});
|
|
}
|
|
});
|
|
|
|
if (attachmentError) {
|
|
return callback(createError(BAD_REQUEST, attachmentError));
|
|
}
|
|
|
|
if (!('new_edits' in opts)) {
|
|
if ('new_edits' in req) {
|
|
opts.new_edits = req.new_edits;
|
|
} else {
|
|
opts.new_edits = true;
|
|
}
|
|
}
|
|
|
|
if (!opts.new_edits && this.type() !== 'http') {
|
|
// ensure revisions of the same doc are sorted, so that
|
|
// the local adapter processes them correctly (#2935)
|
|
req.docs.sort(compareByIdThenRev);
|
|
}
|
|
|
|
cleanDocs(req.docs);
|
|
|
|
return this._bulkDocs(req, opts, function (err, res) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!opts.new_edits) {
|
|
// this is what couch does when new_edits is false
|
|
res = res.filter(function (x) {
|
|
return x.error;
|
|
});
|
|
}
|
|
callback(null, res);
|
|
});
|
|
});
|
|
|
|
AbstractPouchDB.prototype.registerDependentDatabase =
|
|
adapterFun('registerDependentDatabase', function (dependentDb,
|
|
callback) {
|
|
var depDB = new this.constructor(dependentDb, this.__opts);
|
|
|
|
function diffFun(doc) {
|
|
doc.dependentDbs = doc.dependentDbs || {};
|
|
if (doc.dependentDbs[dependentDb]) {
|
|
return false; // no update required
|
|
}
|
|
doc.dependentDbs[dependentDb] = true;
|
|
return doc;
|
|
}
|
|
upsert(this, '_local/_pouch_dependentDbs', diffFun)
|
|
.then(function () {
|
|
callback(null, {db: depDB});
|
|
}).catch(callback);
|
|
});
|
|
|
|
AbstractPouchDB.prototype.destroy =
|
|
adapterFun('destroy', function (opts, callback) {
|
|
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
|
|
var self = this;
|
|
var usePrefix = 'use_prefix' in self ? self.use_prefix : true;
|
|
|
|
function destroyDb() {
|
|
// call destroy method of the particular adaptor
|
|
self._destroy(opts, function (err, resp) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
self._destroyed = true;
|
|
self.emit('destroyed');
|
|
callback(null, resp || { 'ok': true });
|
|
});
|
|
}
|
|
|
|
if (self.type() === 'http') {
|
|
// no need to check for dependent DBs if it's a remote DB
|
|
return destroyDb();
|
|
}
|
|
|
|
self.get('_local/_pouch_dependentDbs', function (err, localDoc) {
|
|
if (err) {
|
|
/* istanbul ignore if */
|
|
if (err.status !== 404) {
|
|
return callback(err);
|
|
} else { // no dependencies
|
|
return destroyDb();
|
|
}
|
|
}
|
|
var dependentDbs = localDoc.dependentDbs;
|
|
var PouchDB = self.constructor;
|
|
var deletedMap = Object.keys(dependentDbs).map(function (name) {
|
|
// use_prefix is only false in the browser
|
|
/* istanbul ignore next */
|
|
var trueName = usePrefix ?
|
|
name.replace(new RegExp('^' + PouchDB.prefix), '') : name;
|
|
return new PouchDB(trueName, self.__opts).destroy();
|
|
});
|
|
PouchPromise.all(deletedMap).then(destroyDb, callback);
|
|
});
|
|
});
|
|
|
|
function TaskQueue() {
|
|
this.isReady = false;
|
|
this.failed = false;
|
|
this.queue = [];
|
|
}
|
|
|
|
TaskQueue.prototype.execute = function () {
|
|
var fun;
|
|
if (this.failed) {
|
|
while ((fun = this.queue.shift())) {
|
|
fun(this.failed);
|
|
}
|
|
} else {
|
|
while ((fun = this.queue.shift())) {
|
|
fun();
|
|
}
|
|
}
|
|
};
|
|
|
|
TaskQueue.prototype.fail = function (err) {
|
|
this.failed = err;
|
|
this.execute();
|
|
};
|
|
|
|
TaskQueue.prototype.ready = function (db) {
|
|
this.isReady = true;
|
|
this.db = db;
|
|
this.execute();
|
|
};
|
|
|
|
TaskQueue.prototype.addTask = function (fun) {
|
|
this.queue.push(fun);
|
|
if (this.failed) {
|
|
this.execute();
|
|
}
|
|
};
|
|
|
|
function defaultCallback(err) {
|
|
/* istanbul ignore next */
|
|
if (err && global.debug) {
|
|
guardedConsole('error', err);
|
|
}
|
|
}
|
|
|
|
// OK, so here's the deal. Consider this code:
|
|
// var db1 = new PouchDB('foo');
|
|
// var db2 = new PouchDB('foo');
|
|
// db1.destroy();
|
|
// ^ these two both need to emit 'destroyed' events,
|
|
// as well as the PouchDB constructor itself.
|
|
// So we have one db object (whichever one got destroy() called on it)
|
|
// responsible for emitting the initial event, which then gets emitted
|
|
// by the constructor, which then broadcasts it to any other dbs
|
|
// that may have been created with the same name.
|
|
function prepareForDestruction(self, opts) {
|
|
var name = opts.originalName;
|
|
var ctor = self.constructor;
|
|
var destructionListeners = ctor._destructionListeners;
|
|
|
|
function onDestroyed() {
|
|
ctor.emit('destroyed', name);
|
|
}
|
|
|
|
function onConstructorDestroyed() {
|
|
self.removeListener('destroyed', onDestroyed);
|
|
self.emit('destroyed', self);
|
|
}
|
|
|
|
self.once('destroyed', onDestroyed);
|
|
|
|
// in setup.js, the constructor is primed to listen for destroy events
|
|
if (!destructionListeners.has(name)) {
|
|
destructionListeners.set(name, []);
|
|
}
|
|
destructionListeners.get(name).push(onConstructorDestroyed);
|
|
}
|
|
|
|
inherits(PouchDB, AbstractPouchDB);
|
|
function PouchDB(name, opts, callback) {
|
|
|
|
/* istanbul ignore if */
|
|
if (!(this instanceof PouchDB)) {
|
|
return new PouchDB(name, opts, callback);
|
|
}
|
|
|
|
var self = this;
|
|
if (typeof opts === 'function' || typeof opts === 'undefined') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
|
|
if (name && typeof name === 'object') {
|
|
opts = name;
|
|
name = undefined;
|
|
}
|
|
|
|
if (typeof callback === 'undefined') {
|
|
callback = defaultCallback;
|
|
} else {
|
|
var oldCallback = callback;
|
|
callback = function () {
|
|
guardedConsole('warn', 'Using a callback for new PouchDB()' +
|
|
'is deprecated.');
|
|
return oldCallback.apply(null, arguments);
|
|
};
|
|
}
|
|
|
|
name = name || opts.name;
|
|
opts = clone(opts);
|
|
// if name was specified via opts, ignore for the sake of dependentDbs
|
|
delete opts.name;
|
|
this.__opts = opts;
|
|
var oldCB = callback;
|
|
self.auto_compaction = opts.auto_compaction;
|
|
self.prefix = PouchDB.prefix;
|
|
AbstractPouchDB.call(self);
|
|
self.taskqueue = new TaskQueue();
|
|
var promise = new PouchPromise(function (fulfill, reject) {
|
|
callback = function (err, resp) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
delete resp.then;
|
|
fulfill(resp);
|
|
};
|
|
|
|
opts = clone(opts);
|
|
var backend, error;
|
|
(function () {
|
|
try {
|
|
|
|
if (typeof name !== 'string') {
|
|
error = new Error('Missing/invalid DB name');
|
|
error.code = 400;
|
|
throw error;
|
|
}
|
|
|
|
var prefixedName = (opts.prefix || '') + name;
|
|
backend = PouchDB.parseAdapter(prefixedName, opts);
|
|
|
|
opts.originalName = name;
|
|
opts.name = backend.name;
|
|
opts.adapter = opts.adapter || backend.adapter;
|
|
self._adapter = opts.adapter;
|
|
debug('pouchdb:adapter')('Picked adapter: ' + opts.adapter);
|
|
|
|
self._db_name = name;
|
|
if (!PouchDB.adapters[opts.adapter]) {
|
|
error = new Error('Adapter is missing');
|
|
error.code = 404;
|
|
throw error;
|
|
}
|
|
|
|
/* istanbul ignore if */
|
|
if (!PouchDB.adapters[opts.adapter].valid()) {
|
|
error = new Error('Invalid Adapter');
|
|
error.code = 404;
|
|
throw error;
|
|
}
|
|
} catch (err) {
|
|
self.taskqueue.fail(err);
|
|
}
|
|
}());
|
|
if (error) {
|
|
return reject(error); // constructor error, see above
|
|
}
|
|
self.adapter = opts.adapter;
|
|
|
|
// needs access to PouchDB;
|
|
self.replicate = {};
|
|
|
|
self.replicate.from = function (url, opts, callback) {
|
|
return self.constructor.replicate(url, self, opts, callback);
|
|
};
|
|
|
|
self.replicate.to = function (url, opts, callback) {
|
|
return self.constructor.replicate(self, url, opts, callback);
|
|
};
|
|
|
|
self.sync = function (dbName, opts, callback) {
|
|
return self.constructor.sync(self, dbName, opts, callback);
|
|
};
|
|
|
|
self.replicate.sync = self.sync;
|
|
|
|
PouchDB.adapters[opts.adapter].call(self, opts, function (err) {
|
|
/* istanbul ignore if */
|
|
if (err) {
|
|
self.taskqueue.fail(err);
|
|
callback(err);
|
|
return;
|
|
}
|
|
prepareForDestruction(self, opts);
|
|
|
|
self.emit('created', self);
|
|
PouchDB.emit('created', opts.originalName);
|
|
self.taskqueue.ready(self);
|
|
callback(null, self);
|
|
});
|
|
|
|
});
|
|
promise.then(function (resp) {
|
|
oldCB(null, resp);
|
|
}, oldCB);
|
|
self.then = promise.then.bind(promise);
|
|
self.catch = promise.catch.bind(promise);
|
|
}
|
|
|
|
PouchDB.debug = debug;
|
|
|
|
PouchDB.adapters = {};
|
|
PouchDB.preferredAdapters = [];
|
|
|
|
PouchDB.prefix = '_pouch_';
|
|
|
|
var eventEmitter = new events.EventEmitter();
|
|
|
|
function setUpEventEmitter(Pouch) {
|
|
Object.keys(events.EventEmitter.prototype).forEach(function (key) {
|
|
if (typeof events.EventEmitter.prototype[key] === 'function') {
|
|
Pouch[key] = eventEmitter[key].bind(eventEmitter);
|
|
}
|
|
});
|
|
|
|
// these are created in constructor.js, and allow us to notify each DB with
|
|
// the same name that it was destroyed, via the constructor object
|
|
var destructListeners = Pouch._destructionListeners = new pouchdbCollections.Map();
|
|
Pouch.on('destroyed', function onConstructorDestroyed(name) {
|
|
destructListeners.get(name).forEach(function (callback) {
|
|
callback();
|
|
});
|
|
destructListeners.delete(name);
|
|
});
|
|
}
|
|
|
|
setUpEventEmitter(PouchDB);
|
|
|
|
PouchDB.parseAdapter = function (name, opts) {
|
|
var match = name.match(/([a-z\-]*):\/\/(.*)/);
|
|
var adapter, adapterName;
|
|
if (match) {
|
|
// the http adapter expects the fully qualified name
|
|
name = /http(s?)/.test(match[1]) ? match[1] + '://' + match[2] : match[2];
|
|
adapter = match[1];
|
|
/* istanbul ignore if */
|
|
if (!PouchDB.adapters[adapter].valid()) {
|
|
throw 'Invalid adapter';
|
|
}
|
|
return {name: name, adapter: match[1]};
|
|
}
|
|
|
|
// check for browsers that have been upgraded from websql-only to websql+idb
|
|
var skipIdb = 'idb' in PouchDB.adapters && 'websql' in PouchDB.adapters &&
|
|
hasLocalStorage() &&
|
|
localStorage['_pouch__websqldb_' + PouchDB.prefix + name];
|
|
|
|
|
|
if (opts.adapter) {
|
|
adapterName = opts.adapter;
|
|
} else if (typeof opts !== 'undefined' && opts.db) {
|
|
adapterName = 'leveldb';
|
|
} else { // automatically determine adapter
|
|
for (var i = 0; i < PouchDB.preferredAdapters.length; ++i) {
|
|
adapterName = PouchDB.preferredAdapters[i];
|
|
if (adapterName in PouchDB.adapters) {
|
|
/* istanbul ignore if */
|
|
if (skipIdb && adapterName === 'idb') {
|
|
// log it, because this can be confusing during development
|
|
guardedConsole('log', 'PouchDB is downgrading "' + name + '" to WebSQL to' +
|
|
' avoid data loss, because it was already opened with WebSQL.');
|
|
continue; // keep using websql to avoid user data loss
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
adapter = PouchDB.adapters[adapterName];
|
|
|
|
// if adapter is invalid, then an error will be thrown later
|
|
var usePrefix = (adapter && 'use_prefix' in adapter) ?
|
|
adapter.use_prefix : true;
|
|
|
|
return {
|
|
name: usePrefix ? (PouchDB.prefix + name) : name,
|
|
adapter: adapterName
|
|
};
|
|
};
|
|
|
|
PouchDB.adapter = function (id, obj, addToPreferredAdapters) {
|
|
if (obj.valid()) {
|
|
PouchDB.adapters[id] = obj;
|
|
if (addToPreferredAdapters) {
|
|
PouchDB.preferredAdapters.push(id);
|
|
}
|
|
}
|
|
};
|
|
|
|
PouchDB.plugin = function (obj) {
|
|
if (typeof obj === 'function') { // function style for plugins
|
|
obj(PouchDB);
|
|
} else {
|
|
Object.keys(obj).forEach(function (id) { // object style for plugins
|
|
PouchDB.prototype[id] = obj[id];
|
|
});
|
|
}
|
|
return PouchDB;
|
|
};
|
|
|
|
PouchDB.defaults = function (defaultOpts) {
|
|
function PouchAlt(name, opts, callback) {
|
|
if (!(this instanceof PouchAlt)) {
|
|
return new PouchAlt(name, opts, callback);
|
|
}
|
|
|
|
if (typeof opts === 'function' || typeof opts === 'undefined') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
if (name && typeof name === 'object') {
|
|
opts = name;
|
|
name = undefined;
|
|
}
|
|
|
|
opts = jsExtend.extend({}, defaultOpts, opts);
|
|
PouchDB.call(this, name, opts, callback);
|
|
}
|
|
|
|
inherits(PouchAlt, PouchDB);
|
|
|
|
PouchAlt.preferredAdapters = PouchDB.preferredAdapters.slice();
|
|
Object.keys(PouchDB).forEach(function (key) {
|
|
if (!(key in PouchAlt)) {
|
|
PouchAlt[key] = PouchDB[key];
|
|
}
|
|
});
|
|
|
|
return PouchAlt;
|
|
};
|
|
|
|
// managed automatically by set-version.js
|
|
var version = "5.4.5";
|
|
|
|
PouchDB.version = version;
|
|
|
|
function toObject(array) {
|
|
return array.reduce(function (obj, item) {
|
|
obj[item] = true;
|
|
return obj;
|
|
}, {});
|
|
}
|
|
// List of top level reserved words for doc
|
|
var reservedWords = toObject([
|
|
'_id',
|
|
'_rev',
|
|
'_attachments',
|
|
'_deleted',
|
|
'_revisions',
|
|
'_revs_info',
|
|
'_conflicts',
|
|
'_deleted_conflicts',
|
|
'_local_seq',
|
|
'_rev_tree',
|
|
//replication documents
|
|
'_replication_id',
|
|
'_replication_state',
|
|
'_replication_state_time',
|
|
'_replication_state_reason',
|
|
'_replication_stats',
|
|
// Specific to Couchbase Sync Gateway
|
|
'_removed'
|
|
]);
|
|
|
|
// List of reserved words that should end up the document
|
|
var dataWords = toObject([
|
|
'_attachments',
|
|
//replication documents
|
|
'_replication_id',
|
|
'_replication_state',
|
|
'_replication_state_time',
|
|
'_replication_state_reason',
|
|
'_replication_stats'
|
|
]);
|
|
|
|
function parseRevisionInfo(rev) {
|
|
if (!/^\d+\-./.test(rev)) {
|
|
return createError(INVALID_REV);
|
|
}
|
|
var idx = rev.indexOf('-');
|
|
var left = rev.substring(0, idx);
|
|
var right = rev.substring(idx + 1);
|
|
return {
|
|
prefix: parseInt(left, 10),
|
|
id: right
|
|
};
|
|
}
|
|
|
|
function makeRevTreeFromRevisions(revisions, opts) {
|
|
var pos = revisions.start - revisions.ids.length + 1;
|
|
|
|
var revisionIds = revisions.ids;
|
|
var ids = [revisionIds[0], opts, []];
|
|
|
|
for (var i = 1, len = revisionIds.length; i < len; i++) {
|
|
ids = [revisionIds[i], {status: 'missing'}, [ids]];
|
|
}
|
|
|
|
return [{
|
|
pos: pos,
|
|
ids: ids
|
|
}];
|
|
}
|
|
|
|
// Preprocess documents, parse their revisions, assign an id and a
|
|
// revision for new writes that are missing them, etc
|
|
function parseDoc(doc, newEdits) {
|
|
|
|
var nRevNum;
|
|
var newRevId;
|
|
var revInfo;
|
|
var opts = {status: 'available'};
|
|
if (doc._deleted) {
|
|
opts.deleted = true;
|
|
}
|
|
|
|
if (newEdits) {
|
|
if (!doc._id) {
|
|
doc._id = uuid();
|
|
}
|
|
newRevId = uuid(32, 16).toLowerCase();
|
|
if (doc._rev) {
|
|
revInfo = parseRevisionInfo(doc._rev);
|
|
if (revInfo.error) {
|
|
return revInfo;
|
|
}
|
|
doc._rev_tree = [{
|
|
pos: revInfo.prefix,
|
|
ids: [revInfo.id, {status: 'missing'}, [[newRevId, opts, []]]]
|
|
}];
|
|
nRevNum = revInfo.prefix + 1;
|
|
} else {
|
|
doc._rev_tree = [{
|
|
pos: 1,
|
|
ids : [newRevId, opts, []]
|
|
}];
|
|
nRevNum = 1;
|
|
}
|
|
} else {
|
|
if (doc._revisions) {
|
|
doc._rev_tree = makeRevTreeFromRevisions(doc._revisions, opts);
|
|
nRevNum = doc._revisions.start;
|
|
newRevId = doc._revisions.ids[0];
|
|
}
|
|
if (!doc._rev_tree) {
|
|
revInfo = parseRevisionInfo(doc._rev);
|
|
if (revInfo.error) {
|
|
return revInfo;
|
|
}
|
|
nRevNum = revInfo.prefix;
|
|
newRevId = revInfo.id;
|
|
doc._rev_tree = [{
|
|
pos: nRevNum,
|
|
ids: [newRevId, opts, []]
|
|
}];
|
|
}
|
|
}
|
|
|
|
invalidIdError(doc._id);
|
|
|
|
doc._rev = nRevNum + '-' + newRevId;
|
|
|
|
var result = {metadata : {}, data : {}};
|
|
for (var key in doc) {
|
|
/* istanbul ignore else */
|
|
if (Object.prototype.hasOwnProperty.call(doc, key)) {
|
|
var specialKey = key[0] === '_';
|
|
if (specialKey && !reservedWords[key]) {
|
|
var error = createError(DOC_VALIDATION, key);
|
|
error.message = DOC_VALIDATION.message + ': ' + key;
|
|
throw error;
|
|
} else if (specialKey && !dataWords[key]) {
|
|
result.metadata[key.slice(1)] = doc[key];
|
|
} else {
|
|
result.data[key] = doc[key];
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
var atob$1 = function (str) {
|
|
return atob(str);
|
|
};
|
|
|
|
var btoa$1 = function (str) {
|
|
return btoa(str);
|
|
};
|
|
|
|
// Abstracts constructing a Blob object, so it also works in older
|
|
// browsers that don't support the native Blob constructor (e.g.
|
|
// old QtWebKit versions, Android < 4.4).
|
|
function createBlob(parts, properties) {
|
|
/* global BlobBuilder,MSBlobBuilder,MozBlobBuilder,WebKitBlobBuilder */
|
|
parts = parts || [];
|
|
properties = properties || {};
|
|
try {
|
|
return new Blob(parts, properties);
|
|
} catch (e) {
|
|
if (e.name !== "TypeError") {
|
|
throw e;
|
|
}
|
|
var Builder = typeof BlobBuilder !== 'undefined' ? BlobBuilder :
|
|
typeof MSBlobBuilder !== 'undefined' ? MSBlobBuilder :
|
|
typeof MozBlobBuilder !== 'undefined' ? MozBlobBuilder :
|
|
WebKitBlobBuilder;
|
|
var builder = new Builder();
|
|
for (var i = 0; i < parts.length; i += 1) {
|
|
builder.append(parts[i]);
|
|
}
|
|
return builder.getBlob(properties.type);
|
|
}
|
|
}
|
|
|
|
// From http://stackoverflow.com/questions/14967647/ (continues on next line)
|
|
// encode-decode-image-with-base64-breaks-image (2013-04-21)
|
|
function binaryStringToArrayBuffer(bin) {
|
|
var length = bin.length;
|
|
var buf = new ArrayBuffer(length);
|
|
var arr = new Uint8Array(buf);
|
|
for (var i = 0; i < length; i++) {
|
|
arr[i] = bin.charCodeAt(i);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
function binStringToBluffer(binString, type) {
|
|
return createBlob([binaryStringToArrayBuffer(binString)], {type: type});
|
|
}
|
|
|
|
function b64ToBluffer(b64, type) {
|
|
return binStringToBluffer(atob$1(b64), type);
|
|
}
|
|
|
|
//Can't find original post, but this is close
|
|
//http://stackoverflow.com/questions/6965107/ (continues on next line)
|
|
//converting-between-strings-and-arraybuffers
|
|
function arrayBufferToBinaryString(buffer) {
|
|
var binary = '';
|
|
var bytes = new Uint8Array(buffer);
|
|
var length = bytes.byteLength;
|
|
for (var i = 0; i < length; i++) {
|
|
binary += String.fromCharCode(bytes[i]);
|
|
}
|
|
return binary;
|
|
}
|
|
|
|
// shim for browsers that don't support it
|
|
function readAsBinaryString(blob, callback) {
|
|
if (typeof FileReader === 'undefined') {
|
|
// fix for Firefox in a web worker
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=901097
|
|
return callback(arrayBufferToBinaryString(
|
|
new FileReaderSync().readAsArrayBuffer(blob)));
|
|
}
|
|
|
|
var reader = new FileReader();
|
|
var hasBinaryString = typeof reader.readAsBinaryString === 'function';
|
|
reader.onloadend = function (e) {
|
|
var result = e.target.result || '';
|
|
if (hasBinaryString) {
|
|
return callback(result);
|
|
}
|
|
callback(arrayBufferToBinaryString(result));
|
|
};
|
|
if (hasBinaryString) {
|
|
reader.readAsBinaryString(blob);
|
|
} else {
|
|
reader.readAsArrayBuffer(blob);
|
|
}
|
|
}
|
|
|
|
function blobToBinaryString(blobOrBuffer, callback) {
|
|
readAsBinaryString(blobOrBuffer, function (bin) {
|
|
callback(bin);
|
|
});
|
|
}
|
|
|
|
function blobToBase64(blobOrBuffer, callback) {
|
|
blobToBinaryString(blobOrBuffer, function (base64) {
|
|
callback(btoa$1(base64));
|
|
});
|
|
}
|
|
|
|
// simplified API. universal browser support is assumed
|
|
function readAsArrayBuffer(blob, callback) {
|
|
if (typeof FileReader === 'undefined') {
|
|
// fix for Firefox in a web worker:
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=901097
|
|
return callback(new FileReaderSync().readAsArrayBuffer(blob));
|
|
}
|
|
|
|
var reader = new FileReader();
|
|
reader.onloadend = function (e) {
|
|
var result = e.target.result || new ArrayBuffer(0);
|
|
callback(result);
|
|
};
|
|
reader.readAsArrayBuffer(blob);
|
|
}
|
|
|
|
var setImmediateShim = global.setImmediate || global.setTimeout;
|
|
var MD5_CHUNK_SIZE = 32768;
|
|
|
|
function rawToBase64(raw) {
|
|
return btoa$1(raw);
|
|
}
|
|
|
|
function sliceBlob(blob, start, end) {
|
|
if (blob.webkitSlice) {
|
|
return blob.webkitSlice(start, end);
|
|
}
|
|
return blob.slice(start, end);
|
|
}
|
|
|
|
function appendBlob(buffer, blob, start, end, callback) {
|
|
if (start > 0 || end < blob.size) {
|
|
// only slice blob if we really need to
|
|
blob = sliceBlob(blob, start, end);
|
|
}
|
|
readAsArrayBuffer(blob, function (arrayBuffer) {
|
|
buffer.append(arrayBuffer);
|
|
callback();
|
|
});
|
|
}
|
|
|
|
function appendString(buffer, string, start, end, callback) {
|
|
if (start > 0 || end < string.length) {
|
|
// only create a substring if we really need to
|
|
string = string.substring(start, end);
|
|
}
|
|
buffer.appendBinary(string);
|
|
callback();
|
|
}
|
|
|
|
function binaryMd5(data, callback) {
|
|
var inputIsString = typeof data === 'string';
|
|
var len = inputIsString ? data.length : data.size;
|
|
var chunkSize = Math.min(MD5_CHUNK_SIZE, len);
|
|
var chunks = Math.ceil(len / chunkSize);
|
|
var currentChunk = 0;
|
|
var buffer = inputIsString ? new Md5() : new Md5.ArrayBuffer();
|
|
|
|
var append = inputIsString ? appendString : appendBlob;
|
|
|
|
function next() {
|
|
setImmediateShim(loadNextChunk);
|
|
}
|
|
|
|
function done() {
|
|
var raw = buffer.end(true);
|
|
var base64 = rawToBase64(raw);
|
|
callback(base64);
|
|
buffer.destroy();
|
|
}
|
|
|
|
function loadNextChunk() {
|
|
var start = currentChunk * chunkSize;
|
|
var end = start + chunkSize;
|
|
currentChunk++;
|
|
if (currentChunk < chunks) {
|
|
append(buffer, data, start, end, next);
|
|
} else {
|
|
append(buffer, data, start, end, done);
|
|
}
|
|
}
|
|
loadNextChunk();
|
|
}
|
|
|
|
function stringMd5(string) {
|
|
return Md5.hash(string);
|
|
}
|
|
|
|
function parseBase64(data) {
|
|
try {
|
|
return atob$1(data);
|
|
} catch (e) {
|
|
var err = createError(BAD_ARG,
|
|
'Attachment is not a valid base64 string');
|
|
return {error: err};
|
|
}
|
|
}
|
|
|
|
function preprocessString(att, blobType, callback) {
|
|
var asBinary = parseBase64(att.data);
|
|
if (asBinary.error) {
|
|
return callback(asBinary.error);
|
|
}
|
|
|
|
att.length = asBinary.length;
|
|
if (blobType === 'blob') {
|
|
att.data = binStringToBluffer(asBinary, att.content_type);
|
|
} else if (blobType === 'base64') {
|
|
att.data = btoa$1(asBinary);
|
|
} else { // binary
|
|
att.data = asBinary;
|
|
}
|
|
binaryMd5(asBinary, function (result) {
|
|
att.digest = 'md5-' + result;
|
|
callback();
|
|
});
|
|
}
|
|
|
|
function preprocessBlob(att, blobType, callback) {
|
|
binaryMd5(att.data, function (md5) {
|
|
att.digest = 'md5-' + md5;
|
|
// size is for blobs (browser), length is for buffers (node)
|
|
att.length = att.data.size || att.data.length || 0;
|
|
if (blobType === 'binary') {
|
|
blobToBinaryString(att.data, function (binString) {
|
|
att.data = binString;
|
|
callback();
|
|
});
|
|
} else if (blobType === 'base64') {
|
|
blobToBase64(att.data, function (b64) {
|
|
att.data = b64;
|
|
callback();
|
|
});
|
|
} else {
|
|
callback();
|
|
}
|
|
});
|
|
}
|
|
|
|
function preprocessAttachment(att, blobType, callback) {
|
|
if (att.stub) {
|
|
return callback();
|
|
}
|
|
if (typeof att.data === 'string') { // input is a base64 string
|
|
preprocessString(att, blobType, callback);
|
|
} else { // input is a blob
|
|
preprocessBlob(att, blobType, callback);
|
|
}
|
|
}
|
|
|
|
function preprocessAttachments(docInfos, blobType, callback) {
|
|
|
|
if (!docInfos.length) {
|
|
return callback();
|
|
}
|
|
|
|
var docv = 0;
|
|
var overallErr;
|
|
|
|
docInfos.forEach(function (docInfo) {
|
|
var attachments = docInfo.data && docInfo.data._attachments ?
|
|
Object.keys(docInfo.data._attachments) : [];
|
|
var recv = 0;
|
|
|
|
if (!attachments.length) {
|
|
return done();
|
|
}
|
|
|
|
function processedAttachment(err) {
|
|
overallErr = err;
|
|
recv++;
|
|
if (recv === attachments.length) {
|
|
done();
|
|
}
|
|
}
|
|
|
|
for (var key in docInfo.data._attachments) {
|
|
if (docInfo.data._attachments.hasOwnProperty(key)) {
|
|
preprocessAttachment(docInfo.data._attachments[key],
|
|
blobType, processedAttachment);
|
|
}
|
|
}
|
|
});
|
|
|
|
function done() {
|
|
docv++;
|
|
if (docInfos.length === docv) {
|
|
if (overallErr) {
|
|
callback(overallErr);
|
|
} else {
|
|
callback();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function updateDoc(revLimit, prev, docInfo, results,
|
|
i, cb, writeDoc, newEdits) {
|
|
|
|
if (revExists(prev.rev_tree, docInfo.metadata.rev)) {
|
|
results[i] = docInfo;
|
|
return cb();
|
|
}
|
|
|
|
// sometimes this is pre-calculated. historically not always
|
|
var previousWinningRev = prev.winningRev || winningRev(prev);
|
|
var previouslyDeleted = 'deleted' in prev ? prev.deleted :
|
|
isDeleted(prev, previousWinningRev);
|
|
var deleted = 'deleted' in docInfo.metadata ? docInfo.metadata.deleted :
|
|
isDeleted(docInfo.metadata);
|
|
var isRoot = /^1-/.test(docInfo.metadata.rev);
|
|
|
|
if (previouslyDeleted && !deleted && newEdits && isRoot) {
|
|
var newDoc = docInfo.data;
|
|
newDoc._rev = previousWinningRev;
|
|
newDoc._id = docInfo.metadata.id;
|
|
docInfo = parseDoc(newDoc, newEdits);
|
|
}
|
|
|
|
var merged = merge(prev.rev_tree, docInfo.metadata.rev_tree[0], revLimit);
|
|
|
|
var inConflict = newEdits && (((previouslyDeleted && deleted) ||
|
|
(!previouslyDeleted && merged.conflicts !== 'new_leaf') ||
|
|
(previouslyDeleted && !deleted && merged.conflicts === 'new_branch')));
|
|
|
|
if (inConflict) {
|
|
var err = createError(REV_CONFLICT);
|
|
results[i] = err;
|
|
return cb();
|
|
}
|
|
|
|
var newRev = docInfo.metadata.rev;
|
|
docInfo.metadata.rev_tree = merged.tree;
|
|
docInfo.stemmedRevs = merged.stemmedRevs || [];
|
|
/* istanbul ignore else */
|
|
if (prev.rev_map) {
|
|
docInfo.metadata.rev_map = prev.rev_map; // used only by leveldb
|
|
}
|
|
|
|
// recalculate
|
|
var winningRev$$ = winningRev(docInfo.metadata);
|
|
var winningRevIsDeleted = isDeleted(docInfo.metadata, winningRev$$);
|
|
|
|
// calculate the total number of documents that were added/removed,
|
|
// from the perspective of total_rows/doc_count
|
|
var delta = (previouslyDeleted === winningRevIsDeleted) ? 0 :
|
|
previouslyDeleted < winningRevIsDeleted ? -1 : 1;
|
|
|
|
var newRevIsDeleted;
|
|
if (newRev === winningRev$$) {
|
|
// if the new rev is the same as the winning rev, we can reuse that value
|
|
newRevIsDeleted = winningRevIsDeleted;
|
|
} else {
|
|
// if they're not the same, then we need to recalculate
|
|
newRevIsDeleted = isDeleted(docInfo.metadata, newRev);
|
|
}
|
|
|
|
writeDoc(docInfo, winningRev$$, winningRevIsDeleted, newRevIsDeleted,
|
|
true, delta, i, cb);
|
|
}
|
|
|
|
function rootIsMissing(docInfo) {
|
|
return docInfo.metadata.rev_tree[0].ids[1].status === 'missing';
|
|
}
|
|
|
|
function processDocs(revLimit, docInfos, api, fetchedDocs, tx, results,
|
|
writeDoc, opts, overallCallback) {
|
|
|
|
// Default to 1000 locally
|
|
revLimit = revLimit || 1000;
|
|
|
|
function insertDoc(docInfo, resultsIdx, callback) {
|
|
// Cant insert new deleted documents
|
|
var winningRev$$ = winningRev(docInfo.metadata);
|
|
var deleted = isDeleted(docInfo.metadata, winningRev$$);
|
|
if ('was_delete' in opts && deleted) {
|
|
results[resultsIdx] = createError(MISSING_DOC, 'deleted');
|
|
return callback();
|
|
}
|
|
|
|
// 4712 - detect whether a new document was inserted with a _rev
|
|
var inConflict = newEdits && rootIsMissing(docInfo);
|
|
|
|
if (inConflict) {
|
|
var err = createError(REV_CONFLICT);
|
|
results[resultsIdx] = err;
|
|
return callback();
|
|
}
|
|
|
|
var delta = deleted ? 0 : 1;
|
|
|
|
writeDoc(docInfo, winningRev$$, deleted, deleted, false,
|
|
delta, resultsIdx, callback);
|
|
}
|
|
|
|
var newEdits = opts.new_edits;
|
|
var idsToDocs = new pouchdbCollections.Map();
|
|
|
|
var docsDone = 0;
|
|
var docsToDo = docInfos.length;
|
|
|
|
function checkAllDocsDone() {
|
|
if (++docsDone === docsToDo && overallCallback) {
|
|
overallCallback();
|
|
}
|
|
}
|
|
|
|
docInfos.forEach(function (currentDoc, resultsIdx) {
|
|
|
|
if (currentDoc._id && isLocalId(currentDoc._id)) {
|
|
var fun = currentDoc._deleted ? '_removeLocal' : '_putLocal';
|
|
api[fun](currentDoc, {ctx: tx}, function (err, res) {
|
|
results[resultsIdx] = err || res;
|
|
checkAllDocsDone();
|
|
});
|
|
return;
|
|
}
|
|
|
|
var id = currentDoc.metadata.id;
|
|
if (idsToDocs.has(id)) {
|
|
docsToDo--; // duplicate
|
|
idsToDocs.get(id).push([currentDoc, resultsIdx]);
|
|
} else {
|
|
idsToDocs.set(id, [[currentDoc, resultsIdx]]);
|
|
}
|
|
});
|
|
|
|
// in the case of new_edits, the user can provide multiple docs
|
|
// with the same id. these need to be processed sequentially
|
|
idsToDocs.forEach(function (docs, id) {
|
|
var numDone = 0;
|
|
|
|
function docWritten() {
|
|
if (++numDone < docs.length) {
|
|
nextDoc();
|
|
} else {
|
|
checkAllDocsDone();
|
|
}
|
|
}
|
|
function nextDoc() {
|
|
var value = docs[numDone];
|
|
var currentDoc = value[0];
|
|
var resultsIdx = value[1];
|
|
|
|
if (fetchedDocs.has(id)) {
|
|
updateDoc(revLimit, fetchedDocs.get(id), currentDoc, results,
|
|
resultsIdx, docWritten, writeDoc, newEdits);
|
|
} else {
|
|
// Ensure stemming applies to new writes as well
|
|
var merged = merge([], currentDoc.metadata.rev_tree[0], revLimit);
|
|
currentDoc.metadata.rev_tree = merged.tree;
|
|
currentDoc.stemmedRevs = merged.stemmedRevs || [];
|
|
insertDoc(currentDoc, resultsIdx, docWritten);
|
|
}
|
|
}
|
|
nextDoc();
|
|
});
|
|
}
|
|
|
|
// IndexedDB requires a versioned database structure, so we use the
|
|
// version here to manage migrations.
|
|
var ADAPTER_VERSION = 5;
|
|
|
|
// The object stores created for each database
|
|
// DOC_STORE stores the document meta data, its revision history and state
|
|
// Keyed by document id
|
|
var DOC_STORE = 'document-store';
|
|
// BY_SEQ_STORE stores a particular version of a document, keyed by its
|
|
// sequence id
|
|
var BY_SEQ_STORE = 'by-sequence';
|
|
// Where we store attachments
|
|
var ATTACH_STORE = 'attach-store';
|
|
// Where we store many-to-many relations
|
|
// between attachment digests and seqs
|
|
var ATTACH_AND_SEQ_STORE = 'attach-seq-store';
|
|
|
|
// Where we store database-wide meta data in a single record
|
|
// keyed by id: META_STORE
|
|
var META_STORE = 'meta-store';
|
|
// Where we store local documents
|
|
var LOCAL_STORE = 'local-store';
|
|
// Where we detect blob support
|
|
var DETECT_BLOB_SUPPORT_STORE = 'detect-blob-support';
|
|
|
|
function slowJsonParse(str) {
|
|
try {
|
|
return JSON.parse(str);
|
|
} catch (e) {
|
|
/* istanbul ignore next */
|
|
return vuvuzela.parse(str);
|
|
}
|
|
}
|
|
|
|
function safeJsonParse(str) {
|
|
// try/catch is deoptimized in V8, leading to slower
|
|
// times than we'd like to have. Most documents are _not_
|
|
// huge, and do not require a slower code path just to parse them.
|
|
// We can be pretty sure that a document under 50000 characters
|
|
// will not be so deeply nested as to throw a stack overflow error
|
|
// (depends on the engine and available memory, though, so this is
|
|
// just a hunch). 50000 was chosen based on the average length
|
|
// of this string in our test suite, to try to find a number that covers
|
|
// most of our test cases (26 over this size, 26378 under it).
|
|
if (str.length < 50000) {
|
|
return JSON.parse(str);
|
|
}
|
|
return slowJsonParse(str);
|
|
}
|
|
|
|
function safeJsonStringify(json) {
|
|
try {
|
|
return JSON.stringify(json);
|
|
} catch (e) {
|
|
/* istanbul ignore next */
|
|
return vuvuzela.stringify(json);
|
|
}
|
|
}
|
|
|
|
function tryCode(fun, that, args, PouchDB) {
|
|
try {
|
|
fun.apply(that, args);
|
|
} catch (err) {
|
|
// Shouldn't happen, but in some odd cases
|
|
// IndexedDB implementations might throw a sync
|
|
// error, in which case this will at least log it.
|
|
PouchDB.emit('error', err);
|
|
}
|
|
}
|
|
|
|
var taskQueue = {
|
|
running: false,
|
|
queue: []
|
|
};
|
|
|
|
function applyNext(PouchDB) {
|
|
if (taskQueue.running || !taskQueue.queue.length) {
|
|
return;
|
|
}
|
|
taskQueue.running = true;
|
|
var item = taskQueue.queue.shift();
|
|
item.action(function (err, res) {
|
|
tryCode(item.callback, this, [err, res], PouchDB);
|
|
taskQueue.running = false;
|
|
process.nextTick(function () {
|
|
applyNext(PouchDB);
|
|
});
|
|
});
|
|
}
|
|
|
|
function idbError(callback) {
|
|
return function (evt) {
|
|
var message = 'unknown_error';
|
|
if (evt.target && evt.target.error) {
|
|
message = evt.target.error.name || evt.target.error.message;
|
|
}
|
|
callback(createError(IDB_ERROR, message, evt.type));
|
|
};
|
|
}
|
|
|
|
// Unfortunately, the metadata has to be stringified
|
|
// when it is put into the database, because otherwise
|
|
// IndexedDB can throw errors for deeply-nested objects.
|
|
// Originally we just used JSON.parse/JSON.stringify; now
|
|
// we use this custom vuvuzela library that avoids recursion.
|
|
// If we could do it all over again, we'd probably use a
|
|
// format for the revision trees other than JSON.
|
|
function encodeMetadata(metadata, winningRev, deleted) {
|
|
return {
|
|
data: safeJsonStringify(metadata),
|
|
winningRev: winningRev,
|
|
deletedOrLocal: deleted ? '1' : '0',
|
|
seq: metadata.seq, // highest seq for this doc
|
|
id: metadata.id
|
|
};
|
|
}
|
|
|
|
function decodeMetadata(storedObject) {
|
|
if (!storedObject) {
|
|
return null;
|
|
}
|
|
var metadata = safeJsonParse(storedObject.data);
|
|
metadata.winningRev = storedObject.winningRev;
|
|
metadata.deleted = storedObject.deletedOrLocal === '1';
|
|
metadata.seq = storedObject.seq;
|
|
return metadata;
|
|
}
|
|
|
|
// read the doc back out from the database. we don't store the
|
|
// _id or _rev because we already have _doc_id_rev.
|
|
function decodeDoc(doc) {
|
|
if (!doc) {
|
|
return doc;
|
|
}
|
|
var idx = doc._doc_id_rev.lastIndexOf(':');
|
|
doc._id = doc._doc_id_rev.substring(0, idx - 1);
|
|
doc._rev = doc._doc_id_rev.substring(idx + 1);
|
|
delete doc._doc_id_rev;
|
|
return doc;
|
|
}
|
|
|
|
// Read a blob from the database, encoding as necessary
|
|
// and translating from base64 if the IDB doesn't support
|
|
// native Blobs
|
|
function readBlobData(body, type, asBlob, callback) {
|
|
if (asBlob) {
|
|
if (!body) {
|
|
callback(createBlob([''], {type: type}));
|
|
} else if (typeof body !== 'string') { // we have blob support
|
|
callback(body);
|
|
} else { // no blob support
|
|
callback(b64ToBluffer(body, type));
|
|
}
|
|
} else { // as base64 string
|
|
if (!body) {
|
|
callback('');
|
|
} else if (typeof body !== 'string') { // we have blob support
|
|
readAsBinaryString(body, function (binary) {
|
|
callback(btoa$1(binary));
|
|
});
|
|
} else { // no blob support
|
|
callback(body);
|
|
}
|
|
}
|
|
}
|
|
|
|
function fetchAttachmentsIfNecessary(doc, opts, txn, cb) {
|
|
var attachments = Object.keys(doc._attachments || {});
|
|
if (!attachments.length) {
|
|
return cb && cb();
|
|
}
|
|
var numDone = 0;
|
|
|
|
function checkDone() {
|
|
if (++numDone === attachments.length && cb) {
|
|
cb();
|
|
}
|
|
}
|
|
|
|
function fetchAttachment(doc, att) {
|
|
var attObj = doc._attachments[att];
|
|
var digest = attObj.digest;
|
|
var req = txn.objectStore(ATTACH_STORE).get(digest);
|
|
req.onsuccess = function (e) {
|
|
attObj.body = e.target.result.body;
|
|
checkDone();
|
|
};
|
|
}
|
|
|
|
attachments.forEach(function (att) {
|
|
if (opts.attachments && opts.include_docs) {
|
|
fetchAttachment(doc, att);
|
|
} else {
|
|
doc._attachments[att].stub = true;
|
|
checkDone();
|
|
}
|
|
});
|
|
}
|
|
|
|
// IDB-specific postprocessing necessary because
|
|
// we don't know whether we stored a true Blob or
|
|
// a base64-encoded string, and if it's a Blob it
|
|
// needs to be read outside of the transaction context
|
|
function postProcessAttachments(results, asBlob) {
|
|
return PouchPromise.all(results.map(function (row) {
|
|
if (row.doc && row.doc._attachments) {
|
|
var attNames = Object.keys(row.doc._attachments);
|
|
return PouchPromise.all(attNames.map(function (att) {
|
|
var attObj = row.doc._attachments[att];
|
|
if (!('body' in attObj)) { // already processed
|
|
return;
|
|
}
|
|
var body = attObj.body;
|
|
var type = attObj.content_type;
|
|
return new PouchPromise(function (resolve) {
|
|
readBlobData(body, type, asBlob, function (data) {
|
|
row.doc._attachments[att] = jsExtend.extend(
|
|
pick(attObj, ['digest', 'content_type']),
|
|
{data: data}
|
|
);
|
|
resolve();
|
|
});
|
|
});
|
|
}));
|
|
}
|
|
}));
|
|
}
|
|
|
|
function compactRevs(revs, docId, txn) {
|
|
|
|
var possiblyOrphanedDigests = [];
|
|
var seqStore = txn.objectStore(BY_SEQ_STORE);
|
|
var attStore = txn.objectStore(ATTACH_STORE);
|
|
var attAndSeqStore = txn.objectStore(ATTACH_AND_SEQ_STORE);
|
|
var count = revs.length;
|
|
|
|
function checkDone() {
|
|
count--;
|
|
if (!count) { // done processing all revs
|
|
deleteOrphanedAttachments();
|
|
}
|
|
}
|
|
|
|
function deleteOrphanedAttachments() {
|
|
if (!possiblyOrphanedDigests.length) {
|
|
return;
|
|
}
|
|
possiblyOrphanedDigests.forEach(function (digest) {
|
|
var countReq = attAndSeqStore.index('digestSeq').count(
|
|
IDBKeyRange.bound(
|
|
digest + '::', digest + '::\uffff', false, false));
|
|
countReq.onsuccess = function (e) {
|
|
var count = e.target.result;
|
|
if (!count) {
|
|
// orphaned
|
|
attStore.delete(digest);
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
revs.forEach(function (rev) {
|
|
var index = seqStore.index('_doc_id_rev');
|
|
var key = docId + "::" + rev;
|
|
index.getKey(key).onsuccess = function (e) {
|
|
var seq = e.target.result;
|
|
if (typeof seq !== 'number') {
|
|
return checkDone();
|
|
}
|
|
seqStore.delete(seq);
|
|
|
|
var cursor = attAndSeqStore.index('seq')
|
|
.openCursor(IDBKeyRange.only(seq));
|
|
|
|
cursor.onsuccess = function (event) {
|
|
var cursor = event.target.result;
|
|
if (cursor) {
|
|
var digest = cursor.value.digestSeq.split('::')[0];
|
|
possiblyOrphanedDigests.push(digest);
|
|
attAndSeqStore.delete(cursor.primaryKey);
|
|
cursor.continue();
|
|
} else { // done
|
|
checkDone();
|
|
}
|
|
};
|
|
};
|
|
});
|
|
}
|
|
|
|
function openTransactionSafely(idb, stores, mode) {
|
|
try {
|
|
return {
|
|
txn: idb.transaction(stores, mode)
|
|
};
|
|
} catch (err) {
|
|
return {
|
|
error: err
|
|
};
|
|
}
|
|
}
|
|
|
|
function idbBulkDocs(dbOpts, req, opts, api, idb, idbChanges, callback) {
|
|
var docInfos = req.docs;
|
|
var txn;
|
|
var docStore;
|
|
var bySeqStore;
|
|
var attachStore;
|
|
var attachAndSeqStore;
|
|
var docInfoError;
|
|
var docCountDelta = 0;
|
|
|
|
for (var i = 0, len = docInfos.length; i < len; i++) {
|
|
var doc = docInfos[i];
|
|
if (doc._id && isLocalId(doc._id)) {
|
|
continue;
|
|
}
|
|
doc = docInfos[i] = parseDoc(doc, opts.new_edits);
|
|
if (doc.error && !docInfoError) {
|
|
docInfoError = doc;
|
|
}
|
|
}
|
|
|
|
if (docInfoError) {
|
|
return callback(docInfoError);
|
|
}
|
|
|
|
var results = new Array(docInfos.length);
|
|
var fetchedDocs = new pouchdbCollections.Map();
|
|
var preconditionErrored = false;
|
|
var blobType = api._meta.blobSupport ? 'blob' : 'base64';
|
|
|
|
preprocessAttachments(docInfos, blobType, function (err) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
startTransaction();
|
|
});
|
|
|
|
function startTransaction() {
|
|
|
|
var stores = [
|
|
DOC_STORE, BY_SEQ_STORE,
|
|
ATTACH_STORE,
|
|
LOCAL_STORE, ATTACH_AND_SEQ_STORE
|
|
];
|
|
var txnResult = openTransactionSafely(idb, stores, 'readwrite');
|
|
if (txnResult.error) {
|
|
return callback(txnResult.error);
|
|
}
|
|
txn = txnResult.txn;
|
|
txn.onabort = idbError(callback);
|
|
txn.ontimeout = idbError(callback);
|
|
txn.oncomplete = complete;
|
|
docStore = txn.objectStore(DOC_STORE);
|
|
bySeqStore = txn.objectStore(BY_SEQ_STORE);
|
|
attachStore = txn.objectStore(ATTACH_STORE);
|
|
attachAndSeqStore = txn.objectStore(ATTACH_AND_SEQ_STORE);
|
|
|
|
verifyAttachments(function (err) {
|
|
if (err) {
|
|
preconditionErrored = true;
|
|
return callback(err);
|
|
}
|
|
fetchExistingDocs();
|
|
});
|
|
}
|
|
|
|
function idbProcessDocs() {
|
|
processDocs(dbOpts.revs_limit, docInfos, api, fetchedDocs,
|
|
txn, results, writeDoc, opts);
|
|
}
|
|
|
|
function fetchExistingDocs() {
|
|
|
|
if (!docInfos.length) {
|
|
return;
|
|
}
|
|
|
|
var numFetched = 0;
|
|
|
|
function checkDone() {
|
|
if (++numFetched === docInfos.length) {
|
|
idbProcessDocs();
|
|
}
|
|
}
|
|
|
|
function readMetadata(event) {
|
|
var metadata = decodeMetadata(event.target.result);
|
|
|
|
if (metadata) {
|
|
fetchedDocs.set(metadata.id, metadata);
|
|
}
|
|
checkDone();
|
|
}
|
|
|
|
for (var i = 0, len = docInfos.length; i < len; i++) {
|
|
var docInfo = docInfos[i];
|
|
if (docInfo._id && isLocalId(docInfo._id)) {
|
|
checkDone(); // skip local docs
|
|
continue;
|
|
}
|
|
var req = docStore.get(docInfo.metadata.id);
|
|
req.onsuccess = readMetadata;
|
|
}
|
|
}
|
|
|
|
function complete() {
|
|
if (preconditionErrored) {
|
|
return;
|
|
}
|
|
|
|
idbChanges.notify(api._meta.name);
|
|
api._meta.docCount += docCountDelta;
|
|
callback(null, results);
|
|
}
|
|
|
|
function verifyAttachment(digest, callback) {
|
|
|
|
var req = attachStore.get(digest);
|
|
req.onsuccess = function (e) {
|
|
if (!e.target.result) {
|
|
var err = createError(MISSING_STUB,
|
|
'unknown stub attachment with digest ' +
|
|
digest);
|
|
err.status = 412;
|
|
callback(err);
|
|
} else {
|
|
callback();
|
|
}
|
|
};
|
|
}
|
|
|
|
function verifyAttachments(finish) {
|
|
|
|
|
|
var digests = [];
|
|
docInfos.forEach(function (docInfo) {
|
|
if (docInfo.data && docInfo.data._attachments) {
|
|
Object.keys(docInfo.data._attachments).forEach(function (filename) {
|
|
var att = docInfo.data._attachments[filename];
|
|
if (att.stub) {
|
|
digests.push(att.digest);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
if (!digests.length) {
|
|
return finish();
|
|
}
|
|
var numDone = 0;
|
|
var err;
|
|
|
|
function checkDone() {
|
|
if (++numDone === digests.length) {
|
|
finish(err);
|
|
}
|
|
}
|
|
digests.forEach(function (digest) {
|
|
verifyAttachment(digest, function (attErr) {
|
|
if (attErr && !err) {
|
|
err = attErr;
|
|
}
|
|
checkDone();
|
|
});
|
|
});
|
|
}
|
|
|
|
function writeDoc(docInfo, winningRev, winningRevIsDeleted, newRevIsDeleted,
|
|
isUpdate, delta, resultsIdx, callback) {
|
|
|
|
docCountDelta += delta;
|
|
|
|
docInfo.metadata.winningRev = winningRev;
|
|
docInfo.metadata.deleted = winningRevIsDeleted;
|
|
|
|
var doc = docInfo.data;
|
|
doc._id = docInfo.metadata.id;
|
|
doc._rev = docInfo.metadata.rev;
|
|
|
|
if (newRevIsDeleted) {
|
|
doc._deleted = true;
|
|
}
|
|
|
|
var hasAttachments = doc._attachments &&
|
|
Object.keys(doc._attachments).length;
|
|
if (hasAttachments) {
|
|
return writeAttachments(docInfo, winningRev, winningRevIsDeleted,
|
|
isUpdate, resultsIdx, callback);
|
|
}
|
|
|
|
finishDoc(docInfo, winningRev, winningRevIsDeleted,
|
|
isUpdate, resultsIdx, callback);
|
|
}
|
|
|
|
function finishDoc(docInfo, winningRev, winningRevIsDeleted,
|
|
isUpdate, resultsIdx, callback) {
|
|
|
|
var doc = docInfo.data;
|
|
var metadata = docInfo.metadata;
|
|
|
|
doc._doc_id_rev = metadata.id + '::' + metadata.rev;
|
|
delete doc._id;
|
|
delete doc._rev;
|
|
|
|
function afterPutDoc(e) {
|
|
var revsToDelete = docInfo.stemmedRevs || [];
|
|
|
|
if (isUpdate && api.auto_compaction) {
|
|
revsToDelete = revsToDelete.concat(compactTree(docInfo.metadata));
|
|
}
|
|
|
|
if (revsToDelete && revsToDelete.length) {
|
|
compactRevs(revsToDelete, docInfo.metadata.id, txn);
|
|
}
|
|
|
|
metadata.seq = e.target.result;
|
|
// Current _rev is calculated from _rev_tree on read
|
|
delete metadata.rev;
|
|
var metadataToStore = encodeMetadata(metadata, winningRev,
|
|
winningRevIsDeleted);
|
|
var metaDataReq = docStore.put(metadataToStore);
|
|
metaDataReq.onsuccess = afterPutMetadata;
|
|
}
|
|
|
|
function afterPutDocError(e) {
|
|
// ConstraintError, need to update, not put (see #1638 for details)
|
|
e.preventDefault(); // avoid transaction abort
|
|
e.stopPropagation(); // avoid transaction onerror
|
|
var index = bySeqStore.index('_doc_id_rev');
|
|
var getKeyReq = index.getKey(doc._doc_id_rev);
|
|
getKeyReq.onsuccess = function (e) {
|
|
var putReq = bySeqStore.put(doc, e.target.result);
|
|
putReq.onsuccess = afterPutDoc;
|
|
};
|
|
}
|
|
|
|
function afterPutMetadata() {
|
|
results[resultsIdx] = {
|
|
ok: true,
|
|
id: metadata.id,
|
|
rev: winningRev
|
|
};
|
|
fetchedDocs.set(docInfo.metadata.id, docInfo.metadata);
|
|
insertAttachmentMappings(docInfo, metadata.seq, callback);
|
|
}
|
|
|
|
var putReq = bySeqStore.put(doc);
|
|
|
|
putReq.onsuccess = afterPutDoc;
|
|
putReq.onerror = afterPutDocError;
|
|
}
|
|
|
|
function writeAttachments(docInfo, winningRev, winningRevIsDeleted,
|
|
isUpdate, resultsIdx, callback) {
|
|
|
|
|
|
var doc = docInfo.data;
|
|
|
|
var numDone = 0;
|
|
var attachments = Object.keys(doc._attachments);
|
|
|
|
function collectResults() {
|
|
if (numDone === attachments.length) {
|
|
finishDoc(docInfo, winningRev, winningRevIsDeleted,
|
|
isUpdate, resultsIdx, callback);
|
|
}
|
|
}
|
|
|
|
function attachmentSaved() {
|
|
numDone++;
|
|
collectResults();
|
|
}
|
|
|
|
attachments.forEach(function (key) {
|
|
var att = docInfo.data._attachments[key];
|
|
if (!att.stub) {
|
|
var data = att.data;
|
|
delete att.data;
|
|
att.revpos = parseInt(winningRev, 10);
|
|
var digest = att.digest;
|
|
saveAttachment(digest, data, attachmentSaved);
|
|
} else {
|
|
numDone++;
|
|
collectResults();
|
|
}
|
|
});
|
|
}
|
|
|
|
// map seqs to attachment digests, which
|
|
// we will need later during compaction
|
|
function insertAttachmentMappings(docInfo, seq, callback) {
|
|
|
|
var attsAdded = 0;
|
|
var attsToAdd = Object.keys(docInfo.data._attachments || {});
|
|
|
|
if (!attsToAdd.length) {
|
|
return callback();
|
|
}
|
|
|
|
function checkDone() {
|
|
if (++attsAdded === attsToAdd.length) {
|
|
callback();
|
|
}
|
|
}
|
|
|
|
function add(att) {
|
|
var digest = docInfo.data._attachments[att].digest;
|
|
var req = attachAndSeqStore.put({
|
|
seq: seq,
|
|
digestSeq: digest + '::' + seq
|
|
});
|
|
|
|
req.onsuccess = checkDone;
|
|
req.onerror = function (e) {
|
|
// this callback is for a constaint error, which we ignore
|
|
// because this docid/rev has already been associated with
|
|
// the digest (e.g. when new_edits == false)
|
|
e.preventDefault(); // avoid transaction abort
|
|
e.stopPropagation(); // avoid transaction onerror
|
|
checkDone();
|
|
};
|
|
}
|
|
for (var i = 0; i < attsToAdd.length; i++) {
|
|
add(attsToAdd[i]); // do in parallel
|
|
}
|
|
}
|
|
|
|
function saveAttachment(digest, data, callback) {
|
|
|
|
|
|
var getKeyReq = attachStore.count(digest);
|
|
getKeyReq.onsuccess = function (e) {
|
|
var count = e.target.result;
|
|
if (count) {
|
|
return callback(); // already exists
|
|
}
|
|
var newAtt = {
|
|
digest: digest,
|
|
body: data
|
|
};
|
|
var putReq = attachStore.put(newAtt);
|
|
putReq.onsuccess = callback;
|
|
};
|
|
}
|
|
}
|
|
|
|
function createKeyRange(start, end, inclusiveEnd, key, descending) {
|
|
try {
|
|
if (start && end) {
|
|
if (descending) {
|
|
return IDBKeyRange.bound(end, start, !inclusiveEnd, false);
|
|
} else {
|
|
return IDBKeyRange.bound(start, end, false, !inclusiveEnd);
|
|
}
|
|
} else if (start) {
|
|
if (descending) {
|
|
return IDBKeyRange.upperBound(start);
|
|
} else {
|
|
return IDBKeyRange.lowerBound(start);
|
|
}
|
|
} else if (end) {
|
|
if (descending) {
|
|
return IDBKeyRange.lowerBound(end, !inclusiveEnd);
|
|
} else {
|
|
return IDBKeyRange.upperBound(end, !inclusiveEnd);
|
|
}
|
|
} else if (key) {
|
|
return IDBKeyRange.only(key);
|
|
}
|
|
} catch (e) {
|
|
return {error: e};
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function handleKeyRangeError(api, opts, err, callback) {
|
|
if (err.name === "DataError" && err.code === 0) {
|
|
// data error, start is less than end
|
|
return callback(null, {
|
|
total_rows: api._meta.docCount,
|
|
offset: opts.skip,
|
|
rows: []
|
|
});
|
|
}
|
|
callback(createError(IDB_ERROR, err.name, err.message));
|
|
}
|
|
|
|
function idbAllDocs(opts, api, idb, callback) {
|
|
|
|
function allDocsQuery(opts, callback) {
|
|
var start = 'startkey' in opts ? opts.startkey : false;
|
|
var end = 'endkey' in opts ? opts.endkey : false;
|
|
var key = 'key' in opts ? opts.key : false;
|
|
var skip = opts.skip || 0;
|
|
var limit = typeof opts.limit === 'number' ? opts.limit : -1;
|
|
var inclusiveEnd = opts.inclusive_end !== false;
|
|
var descending = 'descending' in opts && opts.descending ? 'prev' : null;
|
|
|
|
var keyRange = createKeyRange(start, end, inclusiveEnd, key, descending);
|
|
if (keyRange && keyRange.error) {
|
|
return handleKeyRangeError(api, opts, keyRange.error, callback);
|
|
}
|
|
|
|
var stores = [DOC_STORE, BY_SEQ_STORE];
|
|
|
|
if (opts.attachments) {
|
|
stores.push(ATTACH_STORE);
|
|
}
|
|
var txnResult = openTransactionSafely(idb, stores, 'readonly');
|
|
if (txnResult.error) {
|
|
return callback(txnResult.error);
|
|
}
|
|
var txn = txnResult.txn;
|
|
var docStore = txn.objectStore(DOC_STORE);
|
|
var seqStore = txn.objectStore(BY_SEQ_STORE);
|
|
var cursor = descending ?
|
|
docStore.openCursor(keyRange, descending) :
|
|
docStore.openCursor(keyRange);
|
|
var docIdRevIndex = seqStore.index('_doc_id_rev');
|
|
var results = [];
|
|
var docCount = 0;
|
|
|
|
// if the user specifies include_docs=true, then we don't
|
|
// want to block the main cursor while we're fetching the doc
|
|
function fetchDocAsynchronously(metadata, row, winningRev) {
|
|
var key = metadata.id + "::" + winningRev;
|
|
docIdRevIndex.get(key).onsuccess = function onGetDoc(e) {
|
|
row.doc = decodeDoc(e.target.result);
|
|
if (opts.conflicts) {
|
|
row.doc._conflicts = collectConflicts(metadata);
|
|
}
|
|
fetchAttachmentsIfNecessary(row.doc, opts, txn);
|
|
};
|
|
}
|
|
|
|
function allDocsInner(cursor, winningRev, metadata) {
|
|
var row = {
|
|
id: metadata.id,
|
|
key: metadata.id,
|
|
value: {
|
|
rev: winningRev
|
|
}
|
|
};
|
|
var deleted = metadata.deleted;
|
|
if (opts.deleted === 'ok') {
|
|
results.push(row);
|
|
// deleted docs are okay with "keys" requests
|
|
if (deleted) {
|
|
row.value.deleted = true;
|
|
row.doc = null;
|
|
} else if (opts.include_docs) {
|
|
fetchDocAsynchronously(metadata, row, winningRev);
|
|
}
|
|
} else if (!deleted && skip-- <= 0) {
|
|
results.push(row);
|
|
if (opts.include_docs) {
|
|
fetchDocAsynchronously(metadata, row, winningRev);
|
|
}
|
|
if (--limit === 0) {
|
|
return;
|
|
}
|
|
}
|
|
cursor.continue();
|
|
}
|
|
|
|
function onGetCursor(e) {
|
|
docCount = api._meta.docCount; // do this within the txn for consistency
|
|
var cursor = e.target.result;
|
|
if (!cursor) {
|
|
return;
|
|
}
|
|
var metadata = decodeMetadata(cursor.value);
|
|
var winningRev = metadata.winningRev;
|
|
|
|
allDocsInner(cursor, winningRev, metadata);
|
|
}
|
|
|
|
function onResultsReady() {
|
|
callback(null, {
|
|
total_rows: docCount,
|
|
offset: opts.skip,
|
|
rows: results
|
|
});
|
|
}
|
|
|
|
function onTxnComplete() {
|
|
if (opts.attachments) {
|
|
postProcessAttachments(results, opts.binary).then(onResultsReady);
|
|
} else {
|
|
onResultsReady();
|
|
}
|
|
}
|
|
|
|
txn.oncomplete = onTxnComplete;
|
|
cursor.onsuccess = onGetCursor;
|
|
}
|
|
|
|
function allDocs(opts, callback) {
|
|
|
|
if (opts.limit === 0) {
|
|
return callback(null, {
|
|
total_rows: api._meta.docCount,
|
|
offset: opts.skip,
|
|
rows: []
|
|
});
|
|
}
|
|
allDocsQuery(opts, callback);
|
|
}
|
|
|
|
allDocs(opts, callback);
|
|
}
|
|
|
|
//
|
|
// Blobs are not supported in all versions of IndexedDB, notably
|
|
// Chrome <37 and Android <5. In those versions, storing a blob will throw.
|
|
//
|
|
// Various other blob bugs exist in Chrome v37-42 (inclusive).
|
|
// Detecting them is expensive and confusing to users, and Chrome 37-42
|
|
// is at very low usage worldwide, so we do a hacky userAgent check instead.
|
|
//
|
|
// content-type bug: https://code.google.com/p/chromium/issues/detail?id=408120
|
|
// 404 bug: https://code.google.com/p/chromium/issues/detail?id=447916
|
|
// FileReader bug: https://code.google.com/p/chromium/issues/detail?id=447836
|
|
//
|
|
function checkBlobSupport(txn) {
|
|
return new PouchPromise(function (resolve) {
|
|
var blob = createBlob(['']);
|
|
txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, 'key');
|
|
|
|
txn.onabort = function (e) {
|
|
// If the transaction aborts now its due to not being able to
|
|
// write to the database, likely due to the disk being full
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
resolve(false);
|
|
};
|
|
|
|
txn.oncomplete = function () {
|
|
var matchedChrome = navigator.userAgent.match(/Chrome\/(\d+)/);
|
|
var matchedEdge = navigator.userAgent.match(/Edge\//);
|
|
// MS Edge pretends to be Chrome 42:
|
|
// https://msdn.microsoft.com/en-us/library/hh869301%28v=vs.85%29.aspx
|
|
resolve(matchedEdge || !matchedChrome ||
|
|
parseInt(matchedChrome[1], 10) >= 43);
|
|
};
|
|
}).catch(function () {
|
|
return false; // error, so assume unsupported
|
|
});
|
|
}
|
|
|
|
var cachedDBs = new pouchdbCollections.Map();
|
|
var blobSupportPromise;
|
|
var idbChanges = new Changes$1();
|
|
var openReqList = new pouchdbCollections.Map();
|
|
|
|
function IdbPouch(opts, callback) {
|
|
var api = this;
|
|
|
|
taskQueue.queue.push({
|
|
action: function (thisCallback) {
|
|
init(api, opts, thisCallback);
|
|
},
|
|
callback: callback
|
|
});
|
|
applyNext(api.constructor);
|
|
}
|
|
|
|
function init(api, opts, callback) {
|
|
|
|
var dbName = opts.name;
|
|
|
|
var idb = null;
|
|
api._meta = null;
|
|
|
|
// called when creating a fresh new database
|
|
function createSchema(db) {
|
|
var docStore = db.createObjectStore(DOC_STORE, {keyPath : 'id'});
|
|
db.createObjectStore(BY_SEQ_STORE, {autoIncrement: true})
|
|
.createIndex('_doc_id_rev', '_doc_id_rev', {unique: true});
|
|
db.createObjectStore(ATTACH_STORE, {keyPath: 'digest'});
|
|
db.createObjectStore(META_STORE, {keyPath: 'id', autoIncrement: false});
|
|
db.createObjectStore(DETECT_BLOB_SUPPORT_STORE);
|
|
|
|
// added in v2
|
|
docStore.createIndex('deletedOrLocal', 'deletedOrLocal', {unique : false});
|
|
|
|
// added in v3
|
|
db.createObjectStore(LOCAL_STORE, {keyPath: '_id'});
|
|
|
|
// added in v4
|
|
var attAndSeqStore = db.createObjectStore(ATTACH_AND_SEQ_STORE,
|
|
{autoIncrement: true});
|
|
attAndSeqStore.createIndex('seq', 'seq');
|
|
attAndSeqStore.createIndex('digestSeq', 'digestSeq', {unique: true});
|
|
}
|
|
|
|
// migration to version 2
|
|
// unfortunately "deletedOrLocal" is a misnomer now that we no longer
|
|
// store local docs in the main doc-store, but whaddyagonnado
|
|
function addDeletedOrLocalIndex(txn, callback) {
|
|
var docStore = txn.objectStore(DOC_STORE);
|
|
docStore.createIndex('deletedOrLocal', 'deletedOrLocal', {unique : false});
|
|
|
|
docStore.openCursor().onsuccess = function (event) {
|
|
var cursor = event.target.result;
|
|
if (cursor) {
|
|
var metadata = cursor.value;
|
|
var deleted = isDeleted(metadata);
|
|
metadata.deletedOrLocal = deleted ? "1" : "0";
|
|
docStore.put(metadata);
|
|
cursor.continue();
|
|
} else {
|
|
callback();
|
|
}
|
|
};
|
|
}
|
|
|
|
// migration to version 3 (part 1)
|
|
function createLocalStoreSchema(db) {
|
|
db.createObjectStore(LOCAL_STORE, {keyPath: '_id'})
|
|
.createIndex('_doc_id_rev', '_doc_id_rev', {unique: true});
|
|
}
|
|
|
|
// migration to version 3 (part 2)
|
|
function migrateLocalStore(txn, cb) {
|
|
var localStore = txn.objectStore(LOCAL_STORE);
|
|
var docStore = txn.objectStore(DOC_STORE);
|
|
var seqStore = txn.objectStore(BY_SEQ_STORE);
|
|
|
|
var cursor = docStore.openCursor();
|
|
cursor.onsuccess = function (event) {
|
|
var cursor = event.target.result;
|
|
if (cursor) {
|
|
var metadata = cursor.value;
|
|
var docId = metadata.id;
|
|
var local = isLocalId(docId);
|
|
var rev = winningRev(metadata);
|
|
if (local) {
|
|
var docIdRev = docId + "::" + rev;
|
|
// remove all seq entries
|
|
// associated with this docId
|
|
var start = docId + "::";
|
|
var end = docId + "::~";
|
|
var index = seqStore.index('_doc_id_rev');
|
|
var range = IDBKeyRange.bound(start, end, false, false);
|
|
var seqCursor = index.openCursor(range);
|
|
seqCursor.onsuccess = function (e) {
|
|
seqCursor = e.target.result;
|
|
if (!seqCursor) {
|
|
// done
|
|
docStore.delete(cursor.primaryKey);
|
|
cursor.continue();
|
|
} else {
|
|
var data = seqCursor.value;
|
|
if (data._doc_id_rev === docIdRev) {
|
|
localStore.put(data);
|
|
}
|
|
seqStore.delete(seqCursor.primaryKey);
|
|
seqCursor.continue();
|
|
}
|
|
};
|
|
} else {
|
|
cursor.continue();
|
|
}
|
|
} else if (cb) {
|
|
cb();
|
|
}
|
|
};
|
|
}
|
|
|
|
// migration to version 4 (part 1)
|
|
function addAttachAndSeqStore(db) {
|
|
var attAndSeqStore = db.createObjectStore(ATTACH_AND_SEQ_STORE,
|
|
{autoIncrement: true});
|
|
attAndSeqStore.createIndex('seq', 'seq');
|
|
attAndSeqStore.createIndex('digestSeq', 'digestSeq', {unique: true});
|
|
}
|
|
|
|
// migration to version 4 (part 2)
|
|
function migrateAttsAndSeqs(txn, callback) {
|
|
var seqStore = txn.objectStore(BY_SEQ_STORE);
|
|
var attStore = txn.objectStore(ATTACH_STORE);
|
|
var attAndSeqStore = txn.objectStore(ATTACH_AND_SEQ_STORE);
|
|
|
|
// need to actually populate the table. this is the expensive part,
|
|
// so as an optimization, check first that this database even
|
|
// contains attachments
|
|
var req = attStore.count();
|
|
req.onsuccess = function (e) {
|
|
var count = e.target.result;
|
|
if (!count) {
|
|
return callback(); // done
|
|
}
|
|
|
|
seqStore.openCursor().onsuccess = function (e) {
|
|
var cursor = e.target.result;
|
|
if (!cursor) {
|
|
return callback(); // done
|
|
}
|
|
var doc = cursor.value;
|
|
var seq = cursor.primaryKey;
|
|
var atts = Object.keys(doc._attachments || {});
|
|
var digestMap = {};
|
|
for (var j = 0; j < atts.length; j++) {
|
|
var att = doc._attachments[atts[j]];
|
|
digestMap[att.digest] = true; // uniq digests, just in case
|
|
}
|
|
var digests = Object.keys(digestMap);
|
|
for (j = 0; j < digests.length; j++) {
|
|
var digest = digests[j];
|
|
attAndSeqStore.put({
|
|
seq: seq,
|
|
digestSeq: digest + '::' + seq
|
|
});
|
|
}
|
|
cursor.continue();
|
|
};
|
|
};
|
|
}
|
|
|
|
// migration to version 5
|
|
// Instead of relying on on-the-fly migration of metadata,
|
|
// this brings the doc-store to its modern form:
|
|
// - metadata.winningrev
|
|
// - metadata.seq
|
|
// - stringify the metadata when storing it
|
|
function migrateMetadata(txn) {
|
|
|
|
function decodeMetadataCompat(storedObject) {
|
|
if (!storedObject.data) {
|
|
// old format, when we didn't store it stringified
|
|
storedObject.deleted = storedObject.deletedOrLocal === '1';
|
|
return storedObject;
|
|
}
|
|
return decodeMetadata(storedObject);
|
|
}
|
|
|
|
// ensure that every metadata has a winningRev and seq,
|
|
// which was previously created on-the-fly but better to migrate
|
|
var bySeqStore = txn.objectStore(BY_SEQ_STORE);
|
|
var docStore = txn.objectStore(DOC_STORE);
|
|
var cursor = docStore.openCursor();
|
|
cursor.onsuccess = function (e) {
|
|
var cursor = e.target.result;
|
|
if (!cursor) {
|
|
return; // done
|
|
}
|
|
var metadata = decodeMetadataCompat(cursor.value);
|
|
|
|
metadata.winningRev = metadata.winningRev ||
|
|
winningRev(metadata);
|
|
|
|
function fetchMetadataSeq() {
|
|
// metadata.seq was added post-3.2.0, so if it's missing,
|
|
// we need to fetch it manually
|
|
var start = metadata.id + '::';
|
|
var end = metadata.id + '::\uffff';
|
|
var req = bySeqStore.index('_doc_id_rev').openCursor(
|
|
IDBKeyRange.bound(start, end));
|
|
|
|
var metadataSeq = 0;
|
|
req.onsuccess = function (e) {
|
|
var cursor = e.target.result;
|
|
if (!cursor) {
|
|
metadata.seq = metadataSeq;
|
|
return onGetMetadataSeq();
|
|
}
|
|
var seq = cursor.primaryKey;
|
|
if (seq > metadataSeq) {
|
|
metadataSeq = seq;
|
|
}
|
|
cursor.continue();
|
|
};
|
|
}
|
|
|
|
function onGetMetadataSeq() {
|
|
var metadataToStore = encodeMetadata(metadata,
|
|
metadata.winningRev, metadata.deleted);
|
|
|
|
var req = docStore.put(metadataToStore);
|
|
req.onsuccess = function () {
|
|
cursor.continue();
|
|
};
|
|
}
|
|
|
|
if (metadata.seq) {
|
|
return onGetMetadataSeq();
|
|
}
|
|
|
|
fetchMetadataSeq();
|
|
};
|
|
|
|
}
|
|
|
|
api.type = function () {
|
|
return 'idb';
|
|
};
|
|
|
|
api._id = toPromise(function (callback) {
|
|
callback(null, api._meta.instanceId);
|
|
});
|
|
|
|
api._bulkDocs = function idb_bulkDocs(req, reqOpts, callback) {
|
|
idbBulkDocs(opts, req, reqOpts, api, idb, idbChanges, callback);
|
|
};
|
|
|
|
// First we look up the metadata in the ids database, then we fetch the
|
|
// current revision(s) from the by sequence store
|
|
api._get = function idb_get(id, opts, callback) {
|
|
var doc;
|
|
var metadata;
|
|
var err;
|
|
var txn = opts.ctx;
|
|
if (!txn) {
|
|
var txnResult = openTransactionSafely(idb,
|
|
[DOC_STORE, BY_SEQ_STORE, ATTACH_STORE], 'readonly');
|
|
if (txnResult.error) {
|
|
return callback(txnResult.error);
|
|
}
|
|
txn = txnResult.txn;
|
|
}
|
|
|
|
function finish() {
|
|
callback(err, {doc: doc, metadata: metadata, ctx: txn});
|
|
}
|
|
|
|
txn.objectStore(DOC_STORE).get(id).onsuccess = function (e) {
|
|
metadata = decodeMetadata(e.target.result);
|
|
// we can determine the result here if:
|
|
// 1. there is no such document
|
|
// 2. the document is deleted and we don't ask about specific rev
|
|
// When we ask with opts.rev we expect the answer to be either
|
|
// doc (possibly with _deleted=true) or missing error
|
|
if (!metadata) {
|
|
err = createError(MISSING_DOC, 'missing');
|
|
return finish();
|
|
}
|
|
if (isDeleted(metadata) && !opts.rev) {
|
|
err = createError(MISSING_DOC, "deleted");
|
|
return finish();
|
|
}
|
|
var objectStore = txn.objectStore(BY_SEQ_STORE);
|
|
|
|
var rev = opts.rev || metadata.winningRev;
|
|
var key = metadata.id + '::' + rev;
|
|
|
|
objectStore.index('_doc_id_rev').get(key).onsuccess = function (e) {
|
|
doc = e.target.result;
|
|
if (doc) {
|
|
doc = decodeDoc(doc);
|
|
}
|
|
if (!doc) {
|
|
err = createError(MISSING_DOC, 'missing');
|
|
return finish();
|
|
}
|
|
finish();
|
|
};
|
|
};
|
|
};
|
|
|
|
api._getAttachment = function (docId, attachId, attachment, opts, callback) {
|
|
var txn;
|
|
if (opts.ctx) {
|
|
txn = opts.ctx;
|
|
} else {
|
|
var txnResult = openTransactionSafely(idb,
|
|
[DOC_STORE, BY_SEQ_STORE, ATTACH_STORE], 'readonly');
|
|
if (txnResult.error) {
|
|
return callback(txnResult.error);
|
|
}
|
|
txn = txnResult.txn;
|
|
}
|
|
var digest = attachment.digest;
|
|
var type = attachment.content_type;
|
|
|
|
txn.objectStore(ATTACH_STORE).get(digest).onsuccess = function (e) {
|
|
var body = e.target.result.body;
|
|
readBlobData(body, type, opts.binary, function (blobData) {
|
|
callback(null, blobData);
|
|
});
|
|
};
|
|
};
|
|
|
|
api._info = function idb_info(callback) {
|
|
|
|
if (idb === null || !cachedDBs.has(dbName)) {
|
|
var error = new Error('db isn\'t open');
|
|
error.id = 'idbNull';
|
|
return callback(error);
|
|
}
|
|
var updateSeq;
|
|
var docCount;
|
|
|
|
var txnResult = openTransactionSafely(idb, [BY_SEQ_STORE], 'readonly');
|
|
if (txnResult.error) {
|
|
return callback(txnResult.error);
|
|
}
|
|
var txn = txnResult.txn;
|
|
var cursor = txn.objectStore(BY_SEQ_STORE).openCursor(null, 'prev');
|
|
cursor.onsuccess = function (event) {
|
|
var cursor = event.target.result;
|
|
updateSeq = cursor ? cursor.key : 0;
|
|
// count within the same txn for consistency
|
|
docCount = api._meta.docCount;
|
|
};
|
|
|
|
txn.oncomplete = function () {
|
|
callback(null, {
|
|
doc_count: docCount,
|
|
update_seq: updateSeq,
|
|
// for debugging
|
|
idb_attachment_format: (api._meta.blobSupport ? 'binary' : 'base64')
|
|
});
|
|
};
|
|
};
|
|
|
|
api._allDocs = function idb_allDocs(opts, callback) {
|
|
idbAllDocs(opts, api, idb, callback);
|
|
};
|
|
|
|
api._changes = function (opts) {
|
|
opts = clone(opts);
|
|
|
|
if (opts.continuous) {
|
|
var id = dbName + ':' + uuid();
|
|
idbChanges.addListener(dbName, id, api, opts);
|
|
idbChanges.notify(dbName);
|
|
return {
|
|
cancel: function () {
|
|
idbChanges.removeListener(dbName, id);
|
|
}
|
|
};
|
|
}
|
|
|
|
var docIds = opts.doc_ids && new pouchdbCollections.Set(opts.doc_ids);
|
|
|
|
opts.since = opts.since || 0;
|
|
var lastSeq = opts.since;
|
|
|
|
var limit = 'limit' in opts ? opts.limit : -1;
|
|
if (limit === 0) {
|
|
limit = 1; // per CouchDB _changes spec
|
|
}
|
|
var returnDocs;
|
|
if ('return_docs' in opts) {
|
|
returnDocs = opts.return_docs;
|
|
} else if ('returnDocs' in opts) {
|
|
// TODO: Remove 'returnDocs' in favor of 'return_docs' in a future release
|
|
returnDocs = opts.returnDocs;
|
|
} else {
|
|
returnDocs = true;
|
|
}
|
|
|
|
var results = [];
|
|
var numResults = 0;
|
|
var filter = filterChange(opts);
|
|
var docIdsToMetadata = new pouchdbCollections.Map();
|
|
|
|
var txn;
|
|
var bySeqStore;
|
|
var docStore;
|
|
var docIdRevIndex;
|
|
|
|
function onGetCursor(cursor) {
|
|
|
|
var doc = decodeDoc(cursor.value);
|
|
var seq = cursor.key;
|
|
|
|
if (docIds && !docIds.has(doc._id)) {
|
|
return cursor.continue();
|
|
}
|
|
|
|
var metadata;
|
|
|
|
function onGetMetadata() {
|
|
if (metadata.seq !== seq) {
|
|
// some other seq is later
|
|
return cursor.continue();
|
|
}
|
|
|
|
lastSeq = seq;
|
|
|
|
if (metadata.winningRev === doc._rev) {
|
|
return onGetWinningDoc(doc);
|
|
}
|
|
|
|
fetchWinningDoc();
|
|
}
|
|
|
|
function fetchWinningDoc() {
|
|
var docIdRev = doc._id + '::' + metadata.winningRev;
|
|
var req = docIdRevIndex.get(docIdRev);
|
|
req.onsuccess = function (e) {
|
|
onGetWinningDoc(decodeDoc(e.target.result));
|
|
};
|
|
}
|
|
|
|
function onGetWinningDoc(winningDoc) {
|
|
|
|
var change = opts.processChange(winningDoc, metadata, opts);
|
|
change.seq = metadata.seq;
|
|
|
|
var filtered = filter(change);
|
|
if (typeof filtered === 'object') {
|
|
return opts.complete(filtered);
|
|
}
|
|
|
|
if (filtered) {
|
|
numResults++;
|
|
if (returnDocs) {
|
|
results.push(change);
|
|
}
|
|
// process the attachment immediately
|
|
// for the benefit of live listeners
|
|
if (opts.attachments && opts.include_docs) {
|
|
fetchAttachmentsIfNecessary(winningDoc, opts, txn, function () {
|
|
postProcessAttachments([change], opts.binary).then(function () {
|
|
opts.onChange(change);
|
|
});
|
|
});
|
|
} else {
|
|
opts.onChange(change);
|
|
}
|
|
}
|
|
if (numResults !== limit) {
|
|
cursor.continue();
|
|
}
|
|
}
|
|
|
|
metadata = docIdsToMetadata.get(doc._id);
|
|
if (metadata) { // cached
|
|
return onGetMetadata();
|
|
}
|
|
// metadata not cached, have to go fetch it
|
|
docStore.get(doc._id).onsuccess = function (event) {
|
|
metadata = decodeMetadata(event.target.result);
|
|
docIdsToMetadata.set(doc._id, metadata);
|
|
onGetMetadata();
|
|
};
|
|
}
|
|
|
|
function onsuccess(event) {
|
|
var cursor = event.target.result;
|
|
|
|
if (!cursor) {
|
|
return;
|
|
}
|
|
onGetCursor(cursor);
|
|
}
|
|
|
|
function fetchChanges() {
|
|
var objectStores = [DOC_STORE, BY_SEQ_STORE];
|
|
if (opts.attachments) {
|
|
objectStores.push(ATTACH_STORE);
|
|
}
|
|
var txnResult = openTransactionSafely(idb, objectStores, 'readonly');
|
|
if (txnResult.error) {
|
|
return opts.complete(txnResult.error);
|
|
}
|
|
txn = txnResult.txn;
|
|
txn.onabort = idbError(opts.complete);
|
|
txn.oncomplete = onTxnComplete;
|
|
|
|
bySeqStore = txn.objectStore(BY_SEQ_STORE);
|
|
docStore = txn.objectStore(DOC_STORE);
|
|
docIdRevIndex = bySeqStore.index('_doc_id_rev');
|
|
|
|
var req;
|
|
|
|
if (opts.descending) {
|
|
req = bySeqStore.openCursor(null, 'prev');
|
|
} else {
|
|
req = bySeqStore.openCursor(IDBKeyRange.lowerBound(opts.since, true));
|
|
}
|
|
|
|
req.onsuccess = onsuccess;
|
|
}
|
|
|
|
fetchChanges();
|
|
|
|
function onTxnComplete() {
|
|
|
|
function finish() {
|
|
opts.complete(null, {
|
|
results: results,
|
|
last_seq: lastSeq
|
|
});
|
|
}
|
|
|
|
if (!opts.continuous && opts.attachments) {
|
|
// cannot guarantee that postProcessing was already done,
|
|
// so do it again
|
|
postProcessAttachments(results).then(finish);
|
|
} else {
|
|
finish();
|
|
}
|
|
}
|
|
};
|
|
|
|
api._close = function (callback) {
|
|
if (idb === null) {
|
|
return callback(createError(NOT_OPEN));
|
|
}
|
|
|
|
// https://developer.mozilla.org/en-US/docs/IndexedDB/IDBDatabase#close
|
|
// "Returns immediately and closes the connection in a separate thread..."
|
|
idb.close();
|
|
cachedDBs.delete(dbName);
|
|
idb = null;
|
|
callback();
|
|
};
|
|
|
|
api._getRevisionTree = function (docId, callback) {
|
|
var txnResult = openTransactionSafely(idb, [DOC_STORE], 'readonly');
|
|
if (txnResult.error) {
|
|
return callback(txnResult.error);
|
|
}
|
|
var txn = txnResult.txn;
|
|
var req = txn.objectStore(DOC_STORE).get(docId);
|
|
req.onsuccess = function (event) {
|
|
var doc = decodeMetadata(event.target.result);
|
|
if (!doc) {
|
|
callback(createError(MISSING_DOC));
|
|
} else {
|
|
callback(null, doc.rev_tree);
|
|
}
|
|
};
|
|
};
|
|
|
|
// This function removes revisions of document docId
|
|
// which are listed in revs and sets this document
|
|
// revision to to rev_tree
|
|
api._doCompaction = function (docId, revs, callback) {
|
|
var stores = [
|
|
DOC_STORE,
|
|
BY_SEQ_STORE,
|
|
ATTACH_STORE,
|
|
ATTACH_AND_SEQ_STORE
|
|
];
|
|
var txnResult = openTransactionSafely(idb, stores, 'readwrite');
|
|
if (txnResult.error) {
|
|
return callback(txnResult.error);
|
|
}
|
|
var txn = txnResult.txn;
|
|
|
|
var docStore = txn.objectStore(DOC_STORE);
|
|
|
|
docStore.get(docId).onsuccess = function (event) {
|
|
var metadata = decodeMetadata(event.target.result);
|
|
traverseRevTree(metadata.rev_tree, function (isLeaf, pos,
|
|
revHash, ctx, opts) {
|
|
var rev = pos + '-' + revHash;
|
|
if (revs.indexOf(rev) !== -1) {
|
|
opts.status = 'missing';
|
|
}
|
|
});
|
|
compactRevs(revs, docId, txn);
|
|
var winningRev = metadata.winningRev;
|
|
var deleted = metadata.deleted;
|
|
txn.objectStore(DOC_STORE).put(
|
|
encodeMetadata(metadata, winningRev, deleted));
|
|
};
|
|
txn.onabort = idbError(callback);
|
|
txn.oncomplete = function () {
|
|
callback();
|
|
};
|
|
};
|
|
|
|
|
|
api._getLocal = function (id, callback) {
|
|
var txnResult = openTransactionSafely(idb, [LOCAL_STORE], 'readonly');
|
|
if (txnResult.error) {
|
|
return callback(txnResult.error);
|
|
}
|
|
var tx = txnResult.txn;
|
|
var req = tx.objectStore(LOCAL_STORE).get(id);
|
|
|
|
req.onerror = idbError(callback);
|
|
req.onsuccess = function (e) {
|
|
var doc = e.target.result;
|
|
if (!doc) {
|
|
callback(createError(MISSING_DOC));
|
|
} else {
|
|
delete doc['_doc_id_rev']; // for backwards compat
|
|
callback(null, doc);
|
|
}
|
|
};
|
|
};
|
|
|
|
api._putLocal = function (doc, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
delete doc._revisions; // ignore this, trust the rev
|
|
var oldRev = doc._rev;
|
|
var id = doc._id;
|
|
if (!oldRev) {
|
|
doc._rev = '0-1';
|
|
} else {
|
|
doc._rev = '0-' + (parseInt(oldRev.split('-')[1], 10) + 1);
|
|
}
|
|
|
|
var tx = opts.ctx;
|
|
var ret;
|
|
if (!tx) {
|
|
var txnResult = openTransactionSafely(idb, [LOCAL_STORE], 'readwrite');
|
|
if (txnResult.error) {
|
|
return callback(txnResult.error);
|
|
}
|
|
tx = txnResult.txn;
|
|
tx.onerror = idbError(callback);
|
|
tx.oncomplete = function () {
|
|
if (ret) {
|
|
callback(null, ret);
|
|
}
|
|
};
|
|
}
|
|
|
|
var oStore = tx.objectStore(LOCAL_STORE);
|
|
var req;
|
|
if (oldRev) {
|
|
req = oStore.get(id);
|
|
req.onsuccess = function (e) {
|
|
var oldDoc = e.target.result;
|
|
if (!oldDoc || oldDoc._rev !== oldRev) {
|
|
callback(createError(REV_CONFLICT));
|
|
} else { // update
|
|
var req = oStore.put(doc);
|
|
req.onsuccess = function () {
|
|
ret = {ok: true, id: doc._id, rev: doc._rev};
|
|
if (opts.ctx) { // return immediately
|
|
callback(null, ret);
|
|
}
|
|
};
|
|
}
|
|
};
|
|
} else { // new doc
|
|
req = oStore.add(doc);
|
|
req.onerror = function (e) {
|
|
// constraint error, already exists
|
|
callback(createError(REV_CONFLICT));
|
|
e.preventDefault(); // avoid transaction abort
|
|
e.stopPropagation(); // avoid transaction onerror
|
|
};
|
|
req.onsuccess = function () {
|
|
ret = {ok: true, id: doc._id, rev: doc._rev};
|
|
if (opts.ctx) { // return immediately
|
|
callback(null, ret);
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
api._removeLocal = function (doc, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
var tx = opts.ctx;
|
|
if (!tx) {
|
|
var txnResult = openTransactionSafely(idb, [LOCAL_STORE], 'readwrite');
|
|
if (txnResult.error) {
|
|
return callback(txnResult.error);
|
|
}
|
|
tx = txnResult.txn;
|
|
tx.oncomplete = function () {
|
|
if (ret) {
|
|
callback(null, ret);
|
|
}
|
|
};
|
|
}
|
|
var ret;
|
|
var id = doc._id;
|
|
var oStore = tx.objectStore(LOCAL_STORE);
|
|
var req = oStore.get(id);
|
|
|
|
req.onerror = idbError(callback);
|
|
req.onsuccess = function (e) {
|
|
var oldDoc = e.target.result;
|
|
if (!oldDoc || oldDoc._rev !== doc._rev) {
|
|
callback(createError(MISSING_DOC));
|
|
} else {
|
|
oStore.delete(id);
|
|
ret = {ok: true, id: id, rev: '0-0'};
|
|
if (opts.ctx) { // return immediately
|
|
callback(null, ret);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
api._destroy = function (opts, callback) {
|
|
idbChanges.removeAllListeners(dbName);
|
|
|
|
//Close open request for "dbName" database to fix ie delay.
|
|
var openReq = openReqList.get(dbName);
|
|
if (openReq && openReq.result) {
|
|
openReq.result.close();
|
|
cachedDBs.delete(dbName);
|
|
}
|
|
var req = indexedDB.deleteDatabase(dbName);
|
|
|
|
req.onsuccess = function () {
|
|
//Remove open request from the list.
|
|
openReqList.delete(dbName);
|
|
if (hasLocalStorage() && (dbName in localStorage)) {
|
|
delete localStorage[dbName];
|
|
}
|
|
callback(null, { 'ok': true });
|
|
};
|
|
|
|
req.onerror = idbError(callback);
|
|
};
|
|
|
|
var cached = cachedDBs.get(dbName);
|
|
|
|
if (cached) {
|
|
idb = cached.idb;
|
|
api._meta = cached.global;
|
|
process.nextTick(function () {
|
|
callback(null, api);
|
|
});
|
|
return;
|
|
}
|
|
|
|
var req;
|
|
if (opts.storage) {
|
|
req = tryStorageOption(dbName, opts.storage);
|
|
} else {
|
|
req = indexedDB.open(dbName, ADAPTER_VERSION);
|
|
}
|
|
|
|
openReqList.set(dbName, req);
|
|
|
|
req.onupgradeneeded = function (e) {
|
|
var db = e.target.result;
|
|
if (e.oldVersion < 1) {
|
|
return createSchema(db); // new db, initial schema
|
|
}
|
|
// do migrations
|
|
|
|
var txn = e.currentTarget.transaction;
|
|
// these migrations have to be done in this function, before
|
|
// control is returned to the event loop, because IndexedDB
|
|
|
|
if (e.oldVersion < 3) {
|
|
createLocalStoreSchema(db); // v2 -> v3
|
|
}
|
|
if (e.oldVersion < 4) {
|
|
addAttachAndSeqStore(db); // v3 -> v4
|
|
}
|
|
|
|
var migrations = [
|
|
addDeletedOrLocalIndex, // v1 -> v2
|
|
migrateLocalStore, // v2 -> v3
|
|
migrateAttsAndSeqs, // v3 -> v4
|
|
migrateMetadata // v4 -> v5
|
|
];
|
|
|
|
var i = e.oldVersion;
|
|
|
|
function next() {
|
|
var migration = migrations[i - 1];
|
|
i++;
|
|
if (migration) {
|
|
migration(txn, next);
|
|
}
|
|
}
|
|
|
|
next();
|
|
};
|
|
|
|
req.onsuccess = function (e) {
|
|
|
|
idb = e.target.result;
|
|
|
|
idb.onversionchange = function () {
|
|
idb.close();
|
|
cachedDBs.delete(dbName);
|
|
};
|
|
|
|
idb.onabort = function (e) {
|
|
guardedConsole('error', 'Database has a global failure', e.target.error);
|
|
idb.close();
|
|
cachedDBs.delete(dbName);
|
|
};
|
|
|
|
var txn = idb.transaction([
|
|
META_STORE,
|
|
DETECT_BLOB_SUPPORT_STORE,
|
|
DOC_STORE
|
|
], 'readwrite');
|
|
|
|
var req = txn.objectStore(META_STORE).get(META_STORE);
|
|
|
|
var blobSupport = null;
|
|
var docCount = null;
|
|
var instanceId = null;
|
|
|
|
req.onsuccess = function (e) {
|
|
|
|
var checkSetupComplete = function () {
|
|
if (blobSupport === null || docCount === null ||
|
|
instanceId === null) {
|
|
return;
|
|
} else {
|
|
api._meta = {
|
|
name: dbName,
|
|
instanceId: instanceId,
|
|
blobSupport: blobSupport,
|
|
docCount: docCount
|
|
};
|
|
|
|
cachedDBs.set(dbName, {
|
|
idb: idb,
|
|
global: api._meta
|
|
});
|
|
callback(null, api);
|
|
}
|
|
};
|
|
|
|
//
|
|
// fetch/store the id
|
|
//
|
|
|
|
var meta = e.target.result || {id: META_STORE};
|
|
if (dbName + '_id' in meta) {
|
|
instanceId = meta[dbName + '_id'];
|
|
checkSetupComplete();
|
|
} else {
|
|
instanceId = uuid();
|
|
meta[dbName + '_id'] = instanceId;
|
|
txn.objectStore(META_STORE).put(meta).onsuccess = function () {
|
|
checkSetupComplete();
|
|
};
|
|
}
|
|
|
|
//
|
|
// check blob support
|
|
//
|
|
|
|
if (!blobSupportPromise) {
|
|
// make sure blob support is only checked once
|
|
blobSupportPromise = checkBlobSupport(txn);
|
|
}
|
|
|
|
blobSupportPromise.then(function (val) {
|
|
blobSupport = val;
|
|
checkSetupComplete();
|
|
});
|
|
|
|
//
|
|
// count docs
|
|
//
|
|
|
|
var index = txn.objectStore(DOC_STORE).index('deletedOrLocal');
|
|
index.count(IDBKeyRange.only('0')).onsuccess = function (e) {
|
|
docCount = e.target.result;
|
|
checkSetupComplete();
|
|
};
|
|
|
|
};
|
|
};
|
|
|
|
req.onerror = function () {
|
|
var msg = 'Failed to open indexedDB, are you in private browsing mode?';
|
|
guardedConsole('error', msg);
|
|
callback(createError(IDB_ERROR, msg));
|
|
};
|
|
}
|
|
|
|
IdbPouch.valid = function () {
|
|
// Issue #2533, we finally gave up on doing bug
|
|
// detection instead of browser sniffing. Safari brought us
|
|
// to our knees.
|
|
var isSafari = typeof openDatabase !== 'undefined' &&
|
|
/(Safari|iPhone|iPad|iPod)/.test(navigator.userAgent) &&
|
|
!/Chrome/.test(navigator.userAgent) &&
|
|
!/BlackBerry/.test(navigator.platform);
|
|
|
|
// some outdated implementations of IDB that appear on Samsung
|
|
// and HTC Android devices <4.4 are missing IDBKeyRange
|
|
return !isSafari && typeof indexedDB !== 'undefined' &&
|
|
typeof IDBKeyRange !== 'undefined';
|
|
};
|
|
|
|
function tryStorageOption(dbName, storage) {
|
|
try { // option only available in Firefox 26+
|
|
return indexedDB.open(dbName, {
|
|
version: ADAPTER_VERSION,
|
|
storage: storage
|
|
});
|
|
} catch(err) {
|
|
return indexedDB.open(dbName, ADAPTER_VERSION);
|
|
}
|
|
}
|
|
|
|
function IDBPouch (PouchDB) {
|
|
PouchDB.adapter('idb', IdbPouch, true);
|
|
}
|
|
|
|
//
|
|
// Parsing hex strings. Yeah.
|
|
//
|
|
// So basically we need this because of a bug in WebSQL:
|
|
// https://code.google.com/p/chromium/issues/detail?id=422690
|
|
// https://bugs.webkit.org/show_bug.cgi?id=137637
|
|
//
|
|
// UTF-8 and UTF-16 are provided as separate functions
|
|
// for meager performance improvements
|
|
//
|
|
|
|
function decodeUtf8(str) {
|
|
return decodeURIComponent(escape(str));
|
|
}
|
|
|
|
function hexToInt(charCode) {
|
|
// '0'-'9' is 48-57
|
|
// 'A'-'F' is 65-70
|
|
// SQLite will only give us uppercase hex
|
|
return charCode < 65 ? (charCode - 48) : (charCode - 55);
|
|
}
|
|
|
|
|
|
// Example:
|
|
// pragma encoding=utf8;
|
|
// select hex('A');
|
|
// returns '41'
|
|
function parseHexUtf8(str, start, end) {
|
|
var result = '';
|
|
while (start < end) {
|
|
result += String.fromCharCode(
|
|
(hexToInt(str.charCodeAt(start++)) << 4) |
|
|
hexToInt(str.charCodeAt(start++)));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Example:
|
|
// pragma encoding=utf16;
|
|
// select hex('A');
|
|
// returns '4100'
|
|
// notice that the 00 comes after the 41 (i.e. it's swizzled)
|
|
function parseHexUtf16(str, start, end) {
|
|
var result = '';
|
|
while (start < end) {
|
|
// UTF-16, so swizzle the bytes
|
|
result += String.fromCharCode(
|
|
(hexToInt(str.charCodeAt(start + 2)) << 12) |
|
|
(hexToInt(str.charCodeAt(start + 3)) << 8) |
|
|
(hexToInt(str.charCodeAt(start)) << 4) |
|
|
hexToInt(str.charCodeAt(start + 1)));
|
|
start += 4;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function parseHexString(str, encoding) {
|
|
if (encoding === 'UTF-8') {
|
|
return decodeUtf8(parseHexUtf8(str, 0, str.length));
|
|
} else {
|
|
return parseHexUtf16(str, 0, str.length);
|
|
}
|
|
}
|
|
|
|
function quote(str) {
|
|
return "'" + str + "'";
|
|
}
|
|
|
|
var ADAPTER_VERSION$1 = 7; // used to manage migrations
|
|
|
|
// The object stores created for each database
|
|
// DOC_STORE stores the document meta data, its revision history and state
|
|
var DOC_STORE$1 = quote('document-store');
|
|
// BY_SEQ_STORE stores a particular version of a document, keyed by its
|
|
// sequence id
|
|
var BY_SEQ_STORE$1 = quote('by-sequence');
|
|
// Where we store attachments
|
|
var ATTACH_STORE$1 = quote('attach-store');
|
|
var LOCAL_STORE$1 = quote('local-store');
|
|
var META_STORE$1 = quote('metadata-store');
|
|
// where we store many-to-many relations between attachment
|
|
// digests and seqs
|
|
var ATTACH_AND_SEQ_STORE$1 = quote('attach-seq-store');
|
|
|
|
// escapeBlob and unescapeBlob are workarounds for a websql bug:
|
|
// https://code.google.com/p/chromium/issues/detail?id=422690
|
|
// https://bugs.webkit.org/show_bug.cgi?id=137637
|
|
// The goal is to never actually insert the \u0000 character
|
|
// in the database.
|
|
function escapeBlob(str) {
|
|
return str
|
|
.replace(/\u0002/g, '\u0002\u0002')
|
|
.replace(/\u0001/g, '\u0001\u0002')
|
|
.replace(/\u0000/g, '\u0001\u0001');
|
|
}
|
|
|
|
function unescapeBlob(str) {
|
|
return str
|
|
.replace(/\u0001\u0001/g, '\u0000')
|
|
.replace(/\u0001\u0002/g, '\u0001')
|
|
.replace(/\u0002\u0002/g, '\u0002');
|
|
}
|
|
|
|
function stringifyDoc(doc) {
|
|
// don't bother storing the id/rev. it uses lots of space,
|
|
// in persistent map/reduce especially
|
|
delete doc._id;
|
|
delete doc._rev;
|
|
return JSON.stringify(doc);
|
|
}
|
|
|
|
function unstringifyDoc(doc, id, rev) {
|
|
doc = JSON.parse(doc);
|
|
doc._id = id;
|
|
doc._rev = rev;
|
|
return doc;
|
|
}
|
|
|
|
// question mark groups IN queries, e.g. 3 -> '(?,?,?)'
|
|
function qMarks(num) {
|
|
var s = '(';
|
|
while (num--) {
|
|
s += '?';
|
|
if (num) {
|
|
s += ',';
|
|
}
|
|
}
|
|
return s + ')';
|
|
}
|
|
|
|
function select(selector, table, joiner, where, orderBy) {
|
|
return 'SELECT ' + selector + ' FROM ' +
|
|
(typeof table === 'string' ? table : table.join(' JOIN ')) +
|
|
(joiner ? (' ON ' + joiner) : '') +
|
|
(where ? (' WHERE ' +
|
|
(typeof where === 'string' ? where : where.join(' AND '))) : '') +
|
|
(orderBy ? (' ORDER BY ' + orderBy) : '');
|
|
}
|
|
|
|
function compactRevs$1(revs, docId, tx) {
|
|
|
|
if (!revs.length) {
|
|
return;
|
|
}
|
|
|
|
var numDone = 0;
|
|
var seqs = [];
|
|
|
|
function checkDone() {
|
|
if (++numDone === revs.length) { // done
|
|
deleteOrphans();
|
|
}
|
|
}
|
|
|
|
function deleteOrphans() {
|
|
// find orphaned attachment digests
|
|
|
|
if (!seqs.length) {
|
|
return;
|
|
}
|
|
|
|
var sql = 'SELECT DISTINCT digest AS digest FROM ' +
|
|
ATTACH_AND_SEQ_STORE$1 + ' WHERE seq IN ' + qMarks(seqs.length);
|
|
|
|
tx.executeSql(sql, seqs, function (tx, res) {
|
|
|
|
var digestsToCheck = [];
|
|
for (var i = 0; i < res.rows.length; i++) {
|
|
digestsToCheck.push(res.rows.item(i).digest);
|
|
}
|
|
if (!digestsToCheck.length) {
|
|
return;
|
|
}
|
|
|
|
var sql = 'DELETE FROM ' + ATTACH_AND_SEQ_STORE$1 +
|
|
' WHERE seq IN (' +
|
|
seqs.map(function () { return '?'; }).join(',') +
|
|
')';
|
|
tx.executeSql(sql, seqs, function (tx) {
|
|
|
|
var sql = 'SELECT digest FROM ' + ATTACH_AND_SEQ_STORE$1 +
|
|
' WHERE digest IN (' +
|
|
digestsToCheck.map(function () { return '?'; }).join(',') +
|
|
')';
|
|
tx.executeSql(sql, digestsToCheck, function (tx, res) {
|
|
var nonOrphanedDigests = new pouchdbCollections.Set();
|
|
for (var i = 0; i < res.rows.length; i++) {
|
|
nonOrphanedDigests.add(res.rows.item(i).digest);
|
|
}
|
|
digestsToCheck.forEach(function (digest) {
|
|
if (nonOrphanedDigests.has(digest)) {
|
|
return;
|
|
}
|
|
tx.executeSql(
|
|
'DELETE FROM ' + ATTACH_AND_SEQ_STORE$1 + ' WHERE digest=?',
|
|
[digest]);
|
|
tx.executeSql(
|
|
'DELETE FROM ' + ATTACH_STORE$1 + ' WHERE digest=?', [digest]);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// update by-seq and attach stores in parallel
|
|
revs.forEach(function (rev) {
|
|
var sql = 'SELECT seq FROM ' + BY_SEQ_STORE$1 +
|
|
' WHERE doc_id=? AND rev=?';
|
|
|
|
tx.executeSql(sql, [docId, rev], function (tx, res) {
|
|
if (!res.rows.length) { // already deleted
|
|
return checkDone();
|
|
}
|
|
var seq = res.rows.item(0).seq;
|
|
seqs.push(seq);
|
|
|
|
tx.executeSql(
|
|
'DELETE FROM ' + BY_SEQ_STORE$1 + ' WHERE seq=?', [seq], checkDone);
|
|
});
|
|
});
|
|
}
|
|
|
|
function websqlError(callback) {
|
|
return function (event) {
|
|
guardedConsole('error', 'WebSQL threw an error', event);
|
|
// event may actually be a SQLError object, so report is as such
|
|
var errorNameMatch = event && event.constructor.toString()
|
|
.match(/function ([^\(]+)/);
|
|
var errorName = (errorNameMatch && errorNameMatch[1]) || event.type;
|
|
var errorReason = event.target || event.message;
|
|
callback(createError(WSQ_ERROR, errorReason, errorName));
|
|
};
|
|
}
|
|
|
|
function getSize(opts) {
|
|
if ('size' in opts) {
|
|
// triggers immediate popup in iOS, fixes #2347
|
|
// e.g. 5000001 asks for 5 MB, 10000001 asks for 10 MB,
|
|
return opts.size * 1000000;
|
|
}
|
|
// In iOS, doesn't matter as long as it's <= 5000000.
|
|
// Except that if you request too much, our tests fail
|
|
// because of the native "do you accept?" popup.
|
|
// In Android <=4.3, this value is actually used as an
|
|
// honest-to-god ceiling for data, so we need to
|
|
// set it to a decently high number.
|
|
var isAndroid = typeof navigator !== 'undefined' &&
|
|
/Android/.test(navigator.userAgent);
|
|
return isAndroid ? 5000000 : 1; // in PhantomJS, if you use 0 it will crash
|
|
}
|
|
|
|
function websqlBulkDocs(dbOpts, req, opts, api, db, websqlChanges, callback) {
|
|
var newEdits = opts.new_edits;
|
|
var userDocs = req.docs;
|
|
|
|
// Parse the docs, give them a sequence number for the result
|
|
var docInfos = userDocs.map(function (doc) {
|
|
if (doc._id && isLocalId(doc._id)) {
|
|
return doc;
|
|
}
|
|
var newDoc = parseDoc(doc, newEdits);
|
|
return newDoc;
|
|
});
|
|
|
|
var docInfoErrors = docInfos.filter(function (docInfo) {
|
|
return docInfo.error;
|
|
});
|
|
if (docInfoErrors.length) {
|
|
return callback(docInfoErrors[0]);
|
|
}
|
|
|
|
var tx;
|
|
var results = new Array(docInfos.length);
|
|
var fetchedDocs = new pouchdbCollections.Map();
|
|
|
|
var preconditionErrored;
|
|
function complete() {
|
|
if (preconditionErrored) {
|
|
return callback(preconditionErrored);
|
|
}
|
|
websqlChanges.notify(api._name);
|
|
api._docCount = -1; // invalidate
|
|
callback(null, results);
|
|
}
|
|
|
|
function verifyAttachment(digest, callback) {
|
|
var sql = 'SELECT count(*) as cnt FROM ' + ATTACH_STORE$1 +
|
|
' WHERE digest=?';
|
|
tx.executeSql(sql, [digest], function (tx, result) {
|
|
if (result.rows.item(0).cnt === 0) {
|
|
var err = createError(MISSING_STUB,
|
|
'unknown stub attachment with digest ' +
|
|
digest);
|
|
callback(err);
|
|
} else {
|
|
callback();
|
|
}
|
|
});
|
|
}
|
|
|
|
function verifyAttachments(finish) {
|
|
var digests = [];
|
|
docInfos.forEach(function (docInfo) {
|
|
if (docInfo.data && docInfo.data._attachments) {
|
|
Object.keys(docInfo.data._attachments).forEach(function (filename) {
|
|
var att = docInfo.data._attachments[filename];
|
|
if (att.stub) {
|
|
digests.push(att.digest);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
if (!digests.length) {
|
|
return finish();
|
|
}
|
|
var numDone = 0;
|
|
var err;
|
|
|
|
function checkDone() {
|
|
if (++numDone === digests.length) {
|
|
finish(err);
|
|
}
|
|
}
|
|
digests.forEach(function (digest) {
|
|
verifyAttachment(digest, function (attErr) {
|
|
if (attErr && !err) {
|
|
err = attErr;
|
|
}
|
|
checkDone();
|
|
});
|
|
});
|
|
}
|
|
|
|
function writeDoc(docInfo, winningRev, winningRevIsDeleted, newRevIsDeleted,
|
|
isUpdate, delta, resultsIdx, callback) {
|
|
|
|
function finish() {
|
|
var data = docInfo.data;
|
|
var deletedInt = newRevIsDeleted ? 1 : 0;
|
|
|
|
var id = data._id;
|
|
var rev = data._rev;
|
|
var json = stringifyDoc(data);
|
|
var sql = 'INSERT INTO ' + BY_SEQ_STORE$1 +
|
|
' (doc_id, rev, json, deleted) VALUES (?, ?, ?, ?);';
|
|
var sqlArgs = [id, rev, json, deletedInt];
|
|
|
|
// map seqs to attachment digests, which
|
|
// we will need later during compaction
|
|
function insertAttachmentMappings(seq, callback) {
|
|
var attsAdded = 0;
|
|
var attsToAdd = Object.keys(data._attachments || {});
|
|
|
|
if (!attsToAdd.length) {
|
|
return callback();
|
|
}
|
|
function checkDone() {
|
|
if (++attsAdded === attsToAdd.length) {
|
|
callback();
|
|
}
|
|
return false; // ack handling a constraint error
|
|
}
|
|
function add(att) {
|
|
var sql = 'INSERT INTO ' + ATTACH_AND_SEQ_STORE$1 +
|
|
' (digest, seq) VALUES (?,?)';
|
|
var sqlArgs = [data._attachments[att].digest, seq];
|
|
tx.executeSql(sql, sqlArgs, checkDone, checkDone);
|
|
// second callback is for a constaint error, which we ignore
|
|
// because this docid/rev has already been associated with
|
|
// the digest (e.g. when new_edits == false)
|
|
}
|
|
for (var i = 0; i < attsToAdd.length; i++) {
|
|
add(attsToAdd[i]); // do in parallel
|
|
}
|
|
}
|
|
|
|
tx.executeSql(sql, sqlArgs, function (tx, result) {
|
|
var seq = result.insertId;
|
|
insertAttachmentMappings(seq, function () {
|
|
dataWritten(tx, seq);
|
|
});
|
|
}, function () {
|
|
// constraint error, recover by updating instead (see #1638)
|
|
var fetchSql = select('seq', BY_SEQ_STORE$1, null,
|
|
'doc_id=? AND rev=?');
|
|
tx.executeSql(fetchSql, [id, rev], function (tx, res) {
|
|
var seq = res.rows.item(0).seq;
|
|
var sql = 'UPDATE ' + BY_SEQ_STORE$1 +
|
|
' SET json=?, deleted=? WHERE doc_id=? AND rev=?;';
|
|
var sqlArgs = [json, deletedInt, id, rev];
|
|
tx.executeSql(sql, sqlArgs, function (tx) {
|
|
insertAttachmentMappings(seq, function () {
|
|
dataWritten(tx, seq);
|
|
});
|
|
});
|
|
});
|
|
return false; // ack that we've handled the error
|
|
});
|
|
}
|
|
|
|
function collectResults(attachmentErr) {
|
|
if (!err) {
|
|
if (attachmentErr) {
|
|
err = attachmentErr;
|
|
callback(err);
|
|
} else if (recv === attachments.length) {
|
|
finish();
|
|
}
|
|
}
|
|
}
|
|
|
|
var err = null;
|
|
var recv = 0;
|
|
|
|
docInfo.data._id = docInfo.metadata.id;
|
|
docInfo.data._rev = docInfo.metadata.rev;
|
|
var attachments = Object.keys(docInfo.data._attachments || {});
|
|
|
|
|
|
if (newRevIsDeleted) {
|
|
docInfo.data._deleted = true;
|
|
}
|
|
|
|
function attachmentSaved(err) {
|
|
recv++;
|
|
collectResults(err);
|
|
}
|
|
|
|
attachments.forEach(function (key) {
|
|
var att = docInfo.data._attachments[key];
|
|
if (!att.stub) {
|
|
var data = att.data;
|
|
delete att.data;
|
|
att.revpos = parseInt(winningRev, 10);
|
|
var digest = att.digest;
|
|
saveAttachment(digest, data, attachmentSaved);
|
|
} else {
|
|
recv++;
|
|
collectResults();
|
|
}
|
|
});
|
|
|
|
if (!attachments.length) {
|
|
finish();
|
|
}
|
|
|
|
function dataWritten(tx, seq) {
|
|
var id = docInfo.metadata.id;
|
|
|
|
var revsToCompact = docInfo.stemmedRevs || [];
|
|
if (isUpdate && api.auto_compaction) {
|
|
revsToCompact = compactTree(docInfo.metadata).concat(revsToCompact);
|
|
}
|
|
if (revsToCompact.length) {
|
|
compactRevs$1(revsToCompact, id, tx);
|
|
}
|
|
|
|
docInfo.metadata.seq = seq;
|
|
delete docInfo.metadata.rev;
|
|
|
|
var sql = isUpdate ?
|
|
'UPDATE ' + DOC_STORE$1 +
|
|
' SET json=?, max_seq=?, winningseq=' +
|
|
'(SELECT seq FROM ' + BY_SEQ_STORE$1 +
|
|
' WHERE doc_id=' + DOC_STORE$1 + '.id AND rev=?) WHERE id=?'
|
|
: 'INSERT INTO ' + DOC_STORE$1 +
|
|
' (id, winningseq, max_seq, json) VALUES (?,?,?,?);';
|
|
var metadataStr = safeJsonStringify(docInfo.metadata);
|
|
var params = isUpdate ?
|
|
[metadataStr, seq, winningRev, id] :
|
|
[id, seq, seq, metadataStr];
|
|
tx.executeSql(sql, params, function () {
|
|
results[resultsIdx] = {
|
|
ok: true,
|
|
id: docInfo.metadata.id,
|
|
rev: winningRev
|
|
};
|
|
fetchedDocs.set(id, docInfo.metadata);
|
|
callback();
|
|
});
|
|
}
|
|
}
|
|
|
|
function websqlProcessDocs() {
|
|
processDocs(dbOpts.revs_limit, docInfos, api, fetchedDocs, tx,
|
|
results, writeDoc, opts);
|
|
}
|
|
|
|
function fetchExistingDocs(callback) {
|
|
if (!docInfos.length) {
|
|
return callback();
|
|
}
|
|
|
|
var numFetched = 0;
|
|
|
|
function checkDone() {
|
|
if (++numFetched === docInfos.length) {
|
|
callback();
|
|
}
|
|
}
|
|
|
|
docInfos.forEach(function (docInfo) {
|
|
if (docInfo._id && isLocalId(docInfo._id)) {
|
|
return checkDone(); // skip local docs
|
|
}
|
|
var id = docInfo.metadata.id;
|
|
tx.executeSql('SELECT json FROM ' + DOC_STORE$1 +
|
|
' WHERE id = ?', [id], function (tx, result) {
|
|
if (result.rows.length) {
|
|
var metadata = safeJsonParse(result.rows.item(0).json);
|
|
fetchedDocs.set(id, metadata);
|
|
}
|
|
checkDone();
|
|
});
|
|
});
|
|
}
|
|
|
|
function saveAttachment(digest, data, callback) {
|
|
var sql = 'SELECT digest FROM ' + ATTACH_STORE$1 + ' WHERE digest=?';
|
|
tx.executeSql(sql, [digest], function (tx, result) {
|
|
if (result.rows.length) { // attachment already exists
|
|
return callback();
|
|
}
|
|
// we could just insert before selecting and catch the error,
|
|
// but my hunch is that it's cheaper not to serialize the blob
|
|
// from JS to C if we don't have to (TODO: confirm this)
|
|
sql = 'INSERT INTO ' + ATTACH_STORE$1 +
|
|
' (digest, body, escaped) VALUES (?,?,1)';
|
|
tx.executeSql(sql, [digest, escapeBlob(data)], function () {
|
|
callback();
|
|
}, function () {
|
|
// ignore constaint errors, means it already exists
|
|
callback();
|
|
return false; // ack we handled the error
|
|
});
|
|
});
|
|
}
|
|
|
|
preprocessAttachments(docInfos, 'binary', function (err) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
db.transaction(function (txn) {
|
|
tx = txn;
|
|
verifyAttachments(function (err) {
|
|
if (err) {
|
|
preconditionErrored = err;
|
|
} else {
|
|
fetchExistingDocs(websqlProcessDocs);
|
|
}
|
|
});
|
|
}, websqlError(callback), complete);
|
|
});
|
|
}
|
|
|
|
var cachedDatabases = new pouchdbCollections.Map();
|
|
|
|
// openDatabase passed in through opts (e.g. for node-websql)
|
|
function openDatabaseWithOpts(opts) {
|
|
return opts.websql(opts.name, opts.version, opts.description, opts.size);
|
|
}
|
|
|
|
function openDBSafely(opts) {
|
|
try {
|
|
return {
|
|
db: openDatabaseWithOpts(opts)
|
|
};
|
|
} catch (err) {
|
|
return {
|
|
error: err
|
|
};
|
|
}
|
|
}
|
|
|
|
function openDB(opts) {
|
|
var cachedResult = cachedDatabases.get(opts.name);
|
|
if (!cachedResult) {
|
|
cachedResult = openDBSafely(opts);
|
|
cachedDatabases.set(opts.name, cachedResult);
|
|
if (cachedResult.db) {
|
|
cachedResult.db._sqlitePlugin = typeof sqlitePlugin !== 'undefined';
|
|
}
|
|
}
|
|
return cachedResult;
|
|
}
|
|
|
|
var websqlChanges = new Changes$1();
|
|
|
|
function fetchAttachmentsIfNecessary$1(doc, opts, api, txn, cb) {
|
|
var attachments = Object.keys(doc._attachments || {});
|
|
if (!attachments.length) {
|
|
return cb && cb();
|
|
}
|
|
var numDone = 0;
|
|
|
|
function checkDone() {
|
|
if (++numDone === attachments.length && cb) {
|
|
cb();
|
|
}
|
|
}
|
|
|
|
function fetchAttachment(doc, att) {
|
|
var attObj = doc._attachments[att];
|
|
var attOpts = {binary: opts.binary, ctx: txn};
|
|
api._getAttachment(doc._id, att, attObj, attOpts, function (_, data) {
|
|
doc._attachments[att] = jsExtend.extend(
|
|
pick(attObj, ['digest', 'content_type']),
|
|
{ data: data }
|
|
);
|
|
checkDone();
|
|
});
|
|
}
|
|
|
|
attachments.forEach(function (att) {
|
|
if (opts.attachments && opts.include_docs) {
|
|
fetchAttachment(doc, att);
|
|
} else {
|
|
doc._attachments[att].stub = true;
|
|
checkDone();
|
|
}
|
|
});
|
|
}
|
|
|
|
var POUCH_VERSION = 1;
|
|
|
|
// these indexes cover the ground for most allDocs queries
|
|
var BY_SEQ_STORE_DELETED_INDEX_SQL =
|
|
'CREATE INDEX IF NOT EXISTS \'by-seq-deleted-idx\' ON ' +
|
|
BY_SEQ_STORE$1 + ' (seq, deleted)';
|
|
var BY_SEQ_STORE_DOC_ID_REV_INDEX_SQL =
|
|
'CREATE UNIQUE INDEX IF NOT EXISTS \'by-seq-doc-id-rev\' ON ' +
|
|
BY_SEQ_STORE$1 + ' (doc_id, rev)';
|
|
var DOC_STORE_WINNINGSEQ_INDEX_SQL =
|
|
'CREATE INDEX IF NOT EXISTS \'doc-winningseq-idx\' ON ' +
|
|
DOC_STORE$1 + ' (winningseq)';
|
|
var ATTACH_AND_SEQ_STORE_SEQ_INDEX_SQL =
|
|
'CREATE INDEX IF NOT EXISTS \'attach-seq-seq-idx\' ON ' +
|
|
ATTACH_AND_SEQ_STORE$1 + ' (seq)';
|
|
var ATTACH_AND_SEQ_STORE_ATTACH_INDEX_SQL =
|
|
'CREATE UNIQUE INDEX IF NOT EXISTS \'attach-seq-digest-idx\' ON ' +
|
|
ATTACH_AND_SEQ_STORE$1 + ' (digest, seq)';
|
|
|
|
var DOC_STORE_AND_BY_SEQ_JOINER = BY_SEQ_STORE$1 +
|
|
'.seq = ' + DOC_STORE$1 + '.winningseq';
|
|
|
|
var SELECT_DOCS = BY_SEQ_STORE$1 + '.seq AS seq, ' +
|
|
BY_SEQ_STORE$1 + '.deleted AS deleted, ' +
|
|
BY_SEQ_STORE$1 + '.json AS data, ' +
|
|
BY_SEQ_STORE$1 + '.rev AS rev, ' +
|
|
DOC_STORE$1 + '.json AS metadata';
|
|
|
|
function WebSqlPouch$1(opts, callback) {
|
|
var api = this;
|
|
var instanceId = null;
|
|
var size = getSize(opts);
|
|
var idRequests = [];
|
|
var encoding;
|
|
|
|
api._docCount = -1; // cache sqlite count(*) for performance
|
|
api._name = opts.name;
|
|
|
|
// extend the options here, because sqlite plugin has a ton of options
|
|
// and they are constantly changing, so it's more prudent to allow anything
|
|
var websqlOpts = jsExtend.extend({}, opts, {
|
|
version: POUCH_VERSION,
|
|
description: opts.name,
|
|
size: size
|
|
});
|
|
var openDBResult = openDB(websqlOpts);
|
|
if (openDBResult.error) {
|
|
return websqlError(callback)(openDBResult.error);
|
|
}
|
|
var db = openDBResult.db;
|
|
if (typeof db.readTransaction !== 'function') {
|
|
// doesn't exist in sqlite plugin
|
|
db.readTransaction = db.transaction;
|
|
}
|
|
|
|
function dbCreated() {
|
|
// note the db name in case the browser upgrades to idb
|
|
if (hasLocalStorage()) {
|
|
window.localStorage['_pouch__websqldb_' + api._name] = true;
|
|
}
|
|
callback(null, api);
|
|
}
|
|
|
|
// In this migration, we added the 'deleted' and 'local' columns to the
|
|
// by-seq and doc store tables.
|
|
// To preserve existing user data, we re-process all the existing JSON
|
|
// and add these values.
|
|
// Called migration2 because it corresponds to adapter version (db_version) #2
|
|
function runMigration2(tx, callback) {
|
|
// index used for the join in the allDocs query
|
|
tx.executeSql(DOC_STORE_WINNINGSEQ_INDEX_SQL);
|
|
|
|
tx.executeSql('ALTER TABLE ' + BY_SEQ_STORE$1 +
|
|
' ADD COLUMN deleted TINYINT(1) DEFAULT 0', [], function () {
|
|
tx.executeSql(BY_SEQ_STORE_DELETED_INDEX_SQL);
|
|
tx.executeSql('ALTER TABLE ' + DOC_STORE$1 +
|
|
' ADD COLUMN local TINYINT(1) DEFAULT 0', [], function () {
|
|
tx.executeSql('CREATE INDEX IF NOT EXISTS \'doc-store-local-idx\' ON ' +
|
|
DOC_STORE$1 + ' (local, id)');
|
|
|
|
var sql = 'SELECT ' + DOC_STORE$1 + '.winningseq AS seq, ' + DOC_STORE$1 +
|
|
'.json AS metadata FROM ' + BY_SEQ_STORE$1 + ' JOIN ' + DOC_STORE$1 +
|
|
' ON ' + BY_SEQ_STORE$1 + '.seq = ' + DOC_STORE$1 + '.winningseq';
|
|
|
|
tx.executeSql(sql, [], function (tx, result) {
|
|
|
|
var deleted = [];
|
|
var local = [];
|
|
|
|
for (var i = 0; i < result.rows.length; i++) {
|
|
var item = result.rows.item(i);
|
|
var seq = item.seq;
|
|
var metadata = JSON.parse(item.metadata);
|
|
if (isDeleted(metadata)) {
|
|
deleted.push(seq);
|
|
}
|
|
if (isLocalId(metadata.id)) {
|
|
local.push(metadata.id);
|
|
}
|
|
}
|
|
tx.executeSql('UPDATE ' + DOC_STORE$1 + 'SET local = 1 WHERE id IN ' +
|
|
qMarks(local.length), local, function () {
|
|
tx.executeSql('UPDATE ' + BY_SEQ_STORE$1 +
|
|
' SET deleted = 1 WHERE seq IN ' +
|
|
qMarks(deleted.length), deleted, callback);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// in this migration, we make all the local docs unversioned
|
|
function runMigration3(tx, callback) {
|
|
var local = 'CREATE TABLE IF NOT EXISTS ' + LOCAL_STORE$1 +
|
|
' (id UNIQUE, rev, json)';
|
|
tx.executeSql(local, [], function () {
|
|
var sql = 'SELECT ' + DOC_STORE$1 + '.id AS id, ' +
|
|
BY_SEQ_STORE$1 + '.json AS data ' +
|
|
'FROM ' + BY_SEQ_STORE$1 + ' JOIN ' +
|
|
DOC_STORE$1 + ' ON ' + BY_SEQ_STORE$1 + '.seq = ' +
|
|
DOC_STORE$1 + '.winningseq WHERE local = 1';
|
|
tx.executeSql(sql, [], function (tx, res) {
|
|
var rows = [];
|
|
for (var i = 0; i < res.rows.length; i++) {
|
|
rows.push(res.rows.item(i));
|
|
}
|
|
function doNext() {
|
|
if (!rows.length) {
|
|
return callback(tx);
|
|
}
|
|
var row = rows.shift();
|
|
var rev = JSON.parse(row.data)._rev;
|
|
tx.executeSql('INSERT INTO ' + LOCAL_STORE$1 +
|
|
' (id, rev, json) VALUES (?,?,?)',
|
|
[row.id, rev, row.data], function (tx) {
|
|
tx.executeSql('DELETE FROM ' + DOC_STORE$1 + ' WHERE id=?',
|
|
[row.id], function (tx) {
|
|
tx.executeSql('DELETE FROM ' + BY_SEQ_STORE$1 + ' WHERE seq=?',
|
|
[row.seq], function () {
|
|
doNext();
|
|
});
|
|
});
|
|
});
|
|
}
|
|
doNext();
|
|
});
|
|
});
|
|
}
|
|
|
|
// in this migration, we remove doc_id_rev and just use rev
|
|
function runMigration4(tx, callback) {
|
|
|
|
function updateRows(rows) {
|
|
function doNext() {
|
|
if (!rows.length) {
|
|
return callback(tx);
|
|
}
|
|
var row = rows.shift();
|
|
var doc_id_rev = parseHexString(row.hex, encoding);
|
|
var idx = doc_id_rev.lastIndexOf('::');
|
|
var doc_id = doc_id_rev.substring(0, idx);
|
|
var rev = doc_id_rev.substring(idx + 2);
|
|
var sql = 'UPDATE ' + BY_SEQ_STORE$1 +
|
|
' SET doc_id=?, rev=? WHERE doc_id_rev=?';
|
|
tx.executeSql(sql, [doc_id, rev, doc_id_rev], function () {
|
|
doNext();
|
|
});
|
|
}
|
|
doNext();
|
|
}
|
|
|
|
var sql = 'ALTER TABLE ' + BY_SEQ_STORE$1 + ' ADD COLUMN doc_id';
|
|
tx.executeSql(sql, [], function (tx) {
|
|
var sql = 'ALTER TABLE ' + BY_SEQ_STORE$1 + ' ADD COLUMN rev';
|
|
tx.executeSql(sql, [], function (tx) {
|
|
tx.executeSql(BY_SEQ_STORE_DOC_ID_REV_INDEX_SQL, [], function (tx) {
|
|
var sql = 'SELECT hex(doc_id_rev) as hex FROM ' + BY_SEQ_STORE$1;
|
|
tx.executeSql(sql, [], function (tx, res) {
|
|
var rows = [];
|
|
for (var i = 0; i < res.rows.length; i++) {
|
|
rows.push(res.rows.item(i));
|
|
}
|
|
updateRows(rows);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// in this migration, we add the attach_and_seq table
|
|
// for issue #2818
|
|
function runMigration5(tx, callback) {
|
|
|
|
function migrateAttsAndSeqs(tx) {
|
|
// need to actually populate the table. this is the expensive part,
|
|
// so as an optimization, check first that this database even
|
|
// contains attachments
|
|
var sql = 'SELECT COUNT(*) AS cnt FROM ' + ATTACH_STORE$1;
|
|
tx.executeSql(sql, [], function (tx, res) {
|
|
var count = res.rows.item(0).cnt;
|
|
if (!count) {
|
|
return callback(tx);
|
|
}
|
|
|
|
var offset = 0;
|
|
var pageSize = 10;
|
|
function nextPage() {
|
|
var sql = select(
|
|
SELECT_DOCS + ', ' + DOC_STORE$1 + '.id AS id',
|
|
[DOC_STORE$1, BY_SEQ_STORE$1],
|
|
DOC_STORE_AND_BY_SEQ_JOINER,
|
|
null,
|
|
DOC_STORE$1 + '.id '
|
|
);
|
|
sql += ' LIMIT ' + pageSize + ' OFFSET ' + offset;
|
|
offset += pageSize;
|
|
tx.executeSql(sql, [], function (tx, res) {
|
|
if (!res.rows.length) {
|
|
return callback(tx);
|
|
}
|
|
var digestSeqs = {};
|
|
function addDigestSeq(digest, seq) {
|
|
// uniq digest/seq pairs, just in case there are dups
|
|
var seqs = digestSeqs[digest] = (digestSeqs[digest] || []);
|
|
if (seqs.indexOf(seq) === -1) {
|
|
seqs.push(seq);
|
|
}
|
|
}
|
|
for (var i = 0; i < res.rows.length; i++) {
|
|
var row = res.rows.item(i);
|
|
var doc = unstringifyDoc(row.data, row.id, row.rev);
|
|
var atts = Object.keys(doc._attachments || {});
|
|
for (var j = 0; j < atts.length; j++) {
|
|
var att = doc._attachments[atts[j]];
|
|
addDigestSeq(att.digest, row.seq);
|
|
}
|
|
}
|
|
var digestSeqPairs = [];
|
|
Object.keys(digestSeqs).forEach(function (digest) {
|
|
var seqs = digestSeqs[digest];
|
|
seqs.forEach(function (seq) {
|
|
digestSeqPairs.push([digest, seq]);
|
|
});
|
|
});
|
|
if (!digestSeqPairs.length) {
|
|
return nextPage();
|
|
}
|
|
var numDone = 0;
|
|
digestSeqPairs.forEach(function (pair) {
|
|
var sql = 'INSERT INTO ' + ATTACH_AND_SEQ_STORE$1 +
|
|
' (digest, seq) VALUES (?,?)';
|
|
tx.executeSql(sql, pair, function () {
|
|
if (++numDone === digestSeqPairs.length) {
|
|
nextPage();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
nextPage();
|
|
});
|
|
}
|
|
|
|
var attachAndRev = 'CREATE TABLE IF NOT EXISTS ' +
|
|
ATTACH_AND_SEQ_STORE$1 + ' (digest, seq INTEGER)';
|
|
tx.executeSql(attachAndRev, [], function (tx) {
|
|
tx.executeSql(
|
|
ATTACH_AND_SEQ_STORE_ATTACH_INDEX_SQL, [], function (tx) {
|
|
tx.executeSql(
|
|
ATTACH_AND_SEQ_STORE_SEQ_INDEX_SQL, [],
|
|
migrateAttsAndSeqs);
|
|
});
|
|
});
|
|
}
|
|
|
|
// in this migration, we use escapeBlob() and unescapeBlob()
|
|
// instead of reading out the binary as HEX, which is slow
|
|
function runMigration6(tx, callback) {
|
|
var sql = 'ALTER TABLE ' + ATTACH_STORE$1 +
|
|
' ADD COLUMN escaped TINYINT(1) DEFAULT 0';
|
|
tx.executeSql(sql, [], callback);
|
|
}
|
|
|
|
// issue #3136, in this migration we need a "latest seq" as well
|
|
// as the "winning seq" in the doc store
|
|
function runMigration7(tx, callback) {
|
|
var sql = 'ALTER TABLE ' + DOC_STORE$1 +
|
|
' ADD COLUMN max_seq INTEGER';
|
|
tx.executeSql(sql, [], function (tx) {
|
|
var sql = 'UPDATE ' + DOC_STORE$1 + ' SET max_seq=(SELECT MAX(seq) FROM ' +
|
|
BY_SEQ_STORE$1 + ' WHERE doc_id=id)';
|
|
tx.executeSql(sql, [], function (tx) {
|
|
// add unique index after filling, else we'll get a constraint
|
|
// error when we do the ALTER TABLE
|
|
var sql =
|
|
'CREATE UNIQUE INDEX IF NOT EXISTS \'doc-max-seq-idx\' ON ' +
|
|
DOC_STORE$1 + ' (max_seq)';
|
|
tx.executeSql(sql, [], callback);
|
|
});
|
|
});
|
|
}
|
|
|
|
function checkEncoding(tx, cb) {
|
|
// UTF-8 on chrome/android, UTF-16 on safari < 7.1
|
|
tx.executeSql('SELECT HEX("a") AS hex', [], function (tx, res) {
|
|
var hex = res.rows.item(0).hex;
|
|
encoding = hex.length === 2 ? 'UTF-8' : 'UTF-16';
|
|
cb();
|
|
}
|
|
);
|
|
}
|
|
|
|
function onGetInstanceId() {
|
|
while (idRequests.length > 0) {
|
|
var idCallback = idRequests.pop();
|
|
idCallback(null, instanceId);
|
|
}
|
|
}
|
|
|
|
function onGetVersion(tx, dbVersion) {
|
|
if (dbVersion === 0) {
|
|
// initial schema
|
|
|
|
var meta = 'CREATE TABLE IF NOT EXISTS ' + META_STORE$1 +
|
|
' (dbid, db_version INTEGER)';
|
|
var attach = 'CREATE TABLE IF NOT EXISTS ' + ATTACH_STORE$1 +
|
|
' (digest UNIQUE, escaped TINYINT(1), body BLOB)';
|
|
var attachAndRev = 'CREATE TABLE IF NOT EXISTS ' +
|
|
ATTACH_AND_SEQ_STORE$1 + ' (digest, seq INTEGER)';
|
|
// TODO: migrate winningseq to INTEGER
|
|
var doc = 'CREATE TABLE IF NOT EXISTS ' + DOC_STORE$1 +
|
|
' (id unique, json, winningseq, max_seq INTEGER UNIQUE)';
|
|
var seq = 'CREATE TABLE IF NOT EXISTS ' + BY_SEQ_STORE$1 +
|
|
' (seq INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ' +
|
|
'json, deleted TINYINT(1), doc_id, rev)';
|
|
var local = 'CREATE TABLE IF NOT EXISTS ' + LOCAL_STORE$1 +
|
|
' (id UNIQUE, rev, json)';
|
|
|
|
// creates
|
|
tx.executeSql(attach);
|
|
tx.executeSql(local);
|
|
tx.executeSql(attachAndRev, [], function () {
|
|
tx.executeSql(ATTACH_AND_SEQ_STORE_SEQ_INDEX_SQL);
|
|
tx.executeSql(ATTACH_AND_SEQ_STORE_ATTACH_INDEX_SQL);
|
|
});
|
|
tx.executeSql(doc, [], function () {
|
|
tx.executeSql(DOC_STORE_WINNINGSEQ_INDEX_SQL);
|
|
tx.executeSql(seq, [], function () {
|
|
tx.executeSql(BY_SEQ_STORE_DELETED_INDEX_SQL);
|
|
tx.executeSql(BY_SEQ_STORE_DOC_ID_REV_INDEX_SQL);
|
|
tx.executeSql(meta, [], function () {
|
|
// mark the db version, and new dbid
|
|
var initSeq = 'INSERT INTO ' + META_STORE$1 +
|
|
' (db_version, dbid) VALUES (?,?)';
|
|
instanceId = uuid();
|
|
var initSeqArgs = [ADAPTER_VERSION$1, instanceId];
|
|
tx.executeSql(initSeq, initSeqArgs, function () {
|
|
onGetInstanceId();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
} else { // version > 0
|
|
|
|
var setupDone = function () {
|
|
var migrated = dbVersion < ADAPTER_VERSION$1;
|
|
if (migrated) {
|
|
// update the db version within this transaction
|
|
tx.executeSql('UPDATE ' + META_STORE$1 + ' SET db_version = ' +
|
|
ADAPTER_VERSION$1);
|
|
}
|
|
// notify db.id() callers
|
|
var sql = 'SELECT dbid FROM ' + META_STORE$1;
|
|
tx.executeSql(sql, [], function (tx, result) {
|
|
instanceId = result.rows.item(0).dbid;
|
|
onGetInstanceId();
|
|
});
|
|
};
|
|
|
|
// would love to use promises here, but then websql
|
|
// ends the transaction early
|
|
var tasks = [
|
|
runMigration2,
|
|
runMigration3,
|
|
runMigration4,
|
|
runMigration5,
|
|
runMigration6,
|
|
runMigration7,
|
|
setupDone
|
|
];
|
|
|
|
// run each migration sequentially
|
|
var i = dbVersion;
|
|
var nextMigration = function (tx) {
|
|
tasks[i - 1](tx, nextMigration);
|
|
i++;
|
|
};
|
|
nextMigration(tx);
|
|
}
|
|
}
|
|
|
|
function setup() {
|
|
db.transaction(function (tx) {
|
|
// first check the encoding
|
|
checkEncoding(tx, function () {
|
|
// then get the version
|
|
fetchVersion(tx);
|
|
});
|
|
}, websqlError(callback), dbCreated);
|
|
}
|
|
|
|
function fetchVersion(tx) {
|
|
var sql = 'SELECT sql FROM sqlite_master WHERE tbl_name = ' + META_STORE$1;
|
|
tx.executeSql(sql, [], function (tx, result) {
|
|
if (!result.rows.length) {
|
|
// database hasn't even been created yet (version 0)
|
|
onGetVersion(tx, 0);
|
|
} else if (!/db_version/.test(result.rows.item(0).sql)) {
|
|
// table was created, but without the new db_version column,
|
|
// so add it.
|
|
tx.executeSql('ALTER TABLE ' + META_STORE$1 +
|
|
' ADD COLUMN db_version INTEGER', [], function () {
|
|
// before version 2, this column didn't even exist
|
|
onGetVersion(tx, 1);
|
|
});
|
|
} else { // column exists, we can safely get it
|
|
tx.executeSql('SELECT db_version FROM ' + META_STORE$1,
|
|
[], function (tx, result) {
|
|
var dbVersion = result.rows.item(0).db_version;
|
|
onGetVersion(tx, dbVersion);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
setup();
|
|
|
|
api.type = function () {
|
|
return 'websql';
|
|
};
|
|
|
|
api._id = toPromise(function (callback) {
|
|
callback(null, instanceId);
|
|
});
|
|
|
|
api._info = function (callback) {
|
|
db.readTransaction(function (tx) {
|
|
countDocs(tx, function (docCount) {
|
|
var sql = 'SELECT MAX(seq) AS seq FROM ' + BY_SEQ_STORE$1;
|
|
tx.executeSql(sql, [], function (tx, res) {
|
|
var updateSeq = res.rows.item(0).seq || 0;
|
|
callback(null, {
|
|
doc_count: docCount,
|
|
update_seq: updateSeq,
|
|
// for debugging
|
|
sqlite_plugin: db._sqlitePlugin,
|
|
websql_encoding: encoding
|
|
});
|
|
});
|
|
});
|
|
}, websqlError(callback));
|
|
};
|
|
|
|
api._bulkDocs = function (req, reqOpts, callback) {
|
|
websqlBulkDocs(opts, req, reqOpts, api, db, websqlChanges, callback);
|
|
};
|
|
|
|
api._get = function (id, opts, callback) {
|
|
var doc;
|
|
var metadata;
|
|
var err;
|
|
var tx = opts.ctx;
|
|
if (!tx) {
|
|
return db.readTransaction(function (txn) {
|
|
api._get(id, jsExtend.extend({ctx: txn}, opts), callback);
|
|
});
|
|
}
|
|
|
|
function finish() {
|
|
callback(err, {doc: doc, metadata: metadata, ctx: tx});
|
|
}
|
|
|
|
var sql;
|
|
var sqlArgs;
|
|
if (opts.rev) {
|
|
sql = select(
|
|
SELECT_DOCS,
|
|
[DOC_STORE$1, BY_SEQ_STORE$1],
|
|
DOC_STORE$1 + '.id=' + BY_SEQ_STORE$1 + '.doc_id',
|
|
[BY_SEQ_STORE$1 + '.doc_id=?', BY_SEQ_STORE$1 + '.rev=?']);
|
|
sqlArgs = [id, opts.rev];
|
|
} else {
|
|
sql = select(
|
|
SELECT_DOCS,
|
|
[DOC_STORE$1, BY_SEQ_STORE$1],
|
|
DOC_STORE_AND_BY_SEQ_JOINER,
|
|
DOC_STORE$1 + '.id=?');
|
|
sqlArgs = [id];
|
|
}
|
|
tx.executeSql(sql, sqlArgs, function (a, results) {
|
|
if (!results.rows.length) {
|
|
err = createError(MISSING_DOC, 'missing');
|
|
return finish();
|
|
}
|
|
var item = results.rows.item(0);
|
|
metadata = safeJsonParse(item.metadata);
|
|
if (item.deleted && !opts.rev) {
|
|
err = createError(MISSING_DOC, 'deleted');
|
|
return finish();
|
|
}
|
|
doc = unstringifyDoc(item.data, metadata.id, item.rev);
|
|
finish();
|
|
});
|
|
};
|
|
|
|
function countDocs(tx, callback) {
|
|
|
|
if (api._docCount !== -1) {
|
|
return callback(api._docCount);
|
|
}
|
|
|
|
// count the total rows
|
|
var sql = select(
|
|
'COUNT(' + DOC_STORE$1 + '.id) AS \'num\'',
|
|
[DOC_STORE$1, BY_SEQ_STORE$1],
|
|
DOC_STORE_AND_BY_SEQ_JOINER,
|
|
BY_SEQ_STORE$1 + '.deleted=0');
|
|
|
|
tx.executeSql(sql, [], function (tx, result) {
|
|
api._docCount = result.rows.item(0).num;
|
|
callback(api._docCount);
|
|
});
|
|
}
|
|
|
|
api._allDocs = function (opts, callback) {
|
|
var results = [];
|
|
var totalRows;
|
|
|
|
var start = 'startkey' in opts ? opts.startkey : false;
|
|
var end = 'endkey' in opts ? opts.endkey : false;
|
|
var key = 'key' in opts ? opts.key : false;
|
|
var descending = 'descending' in opts ? opts.descending : false;
|
|
var limit = 'limit' in opts ? opts.limit : -1;
|
|
var offset = 'skip' in opts ? opts.skip : 0;
|
|
var inclusiveEnd = opts.inclusive_end !== false;
|
|
|
|
var sqlArgs = [];
|
|
var criteria = [];
|
|
|
|
if (key !== false) {
|
|
criteria.push(DOC_STORE$1 + '.id = ?');
|
|
sqlArgs.push(key);
|
|
} else if (start !== false || end !== false) {
|
|
if (start !== false) {
|
|
criteria.push(DOC_STORE$1 + '.id ' + (descending ? '<=' : '>=') + ' ?');
|
|
sqlArgs.push(start);
|
|
}
|
|
if (end !== false) {
|
|
var comparator = descending ? '>' : '<';
|
|
if (inclusiveEnd) {
|
|
comparator += '=';
|
|
}
|
|
criteria.push(DOC_STORE$1 + '.id ' + comparator + ' ?');
|
|
sqlArgs.push(end);
|
|
}
|
|
if (key !== false) {
|
|
criteria.push(DOC_STORE$1 + '.id = ?');
|
|
sqlArgs.push(key);
|
|
}
|
|
}
|
|
|
|
if (opts.deleted !== 'ok') {
|
|
// report deleted if keys are specified
|
|
criteria.push(BY_SEQ_STORE$1 + '.deleted = 0');
|
|
}
|
|
|
|
db.readTransaction(function (tx) {
|
|
|
|
// first count up the total rows
|
|
countDocs(tx, function (count) {
|
|
totalRows = count;
|
|
|
|
if (limit === 0) {
|
|
return;
|
|
}
|
|
|
|
// then actually fetch the documents
|
|
var sql = select(
|
|
SELECT_DOCS,
|
|
[DOC_STORE$1, BY_SEQ_STORE$1],
|
|
DOC_STORE_AND_BY_SEQ_JOINER,
|
|
criteria,
|
|
DOC_STORE$1 + '.id ' + (descending ? 'DESC' : 'ASC')
|
|
);
|
|
sql += ' LIMIT ' + limit + ' OFFSET ' + offset;
|
|
|
|
tx.executeSql(sql, sqlArgs, function (tx, result) {
|
|
for (var i = 0, l = result.rows.length; i < l; i++) {
|
|
var item = result.rows.item(i);
|
|
var metadata = safeJsonParse(item.metadata);
|
|
var id = metadata.id;
|
|
var data = unstringifyDoc(item.data, id, item.rev);
|
|
var winningRev = data._rev;
|
|
var doc = {
|
|
id: id,
|
|
key: id,
|
|
value: {rev: winningRev}
|
|
};
|
|
if (opts.include_docs) {
|
|
doc.doc = data;
|
|
doc.doc._rev = winningRev;
|
|
if (opts.conflicts) {
|
|
doc.doc._conflicts = collectConflicts(metadata);
|
|
}
|
|
fetchAttachmentsIfNecessary$1(doc.doc, opts, api, tx);
|
|
}
|
|
if (item.deleted) {
|
|
if (opts.deleted === 'ok') {
|
|
doc.value.deleted = true;
|
|
doc.doc = null;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
results.push(doc);
|
|
}
|
|
});
|
|
});
|
|
}, websqlError(callback), function () {
|
|
callback(null, {
|
|
total_rows: totalRows,
|
|
offset: opts.skip,
|
|
rows: results
|
|
});
|
|
});
|
|
};
|
|
|
|
api._changes = function (opts) {
|
|
opts = clone(opts);
|
|
|
|
if (opts.continuous) {
|
|
var id = api._name + ':' + uuid();
|
|
websqlChanges.addListener(api._name, id, api, opts);
|
|
websqlChanges.notify(api._name);
|
|
return {
|
|
cancel: function () {
|
|
websqlChanges.removeListener(api._name, id);
|
|
}
|
|
};
|
|
}
|
|
|
|
var descending = opts.descending;
|
|
|
|
// Ignore the `since` parameter when `descending` is true
|
|
opts.since = opts.since && !descending ? opts.since : 0;
|
|
|
|
var limit = 'limit' in opts ? opts.limit : -1;
|
|
if (limit === 0) {
|
|
limit = 1; // per CouchDB _changes spec
|
|
}
|
|
|
|
var returnDocs;
|
|
if ('return_docs' in opts) {
|
|
returnDocs = opts.return_docs;
|
|
} else if ('returnDocs' in opts) {
|
|
// TODO: Remove 'returnDocs' in favor of 'return_docs' in a future release
|
|
returnDocs = opts.returnDocs;
|
|
} else {
|
|
returnDocs = true;
|
|
}
|
|
var results = [];
|
|
var numResults = 0;
|
|
|
|
function fetchChanges() {
|
|
|
|
var selectStmt =
|
|
DOC_STORE$1 + '.json AS metadata, ' +
|
|
DOC_STORE$1 + '.max_seq AS maxSeq, ' +
|
|
BY_SEQ_STORE$1 + '.json AS winningDoc, ' +
|
|
BY_SEQ_STORE$1 + '.rev AS winningRev ';
|
|
|
|
var from = DOC_STORE$1 + ' JOIN ' + BY_SEQ_STORE$1;
|
|
|
|
var joiner = DOC_STORE$1 + '.id=' + BY_SEQ_STORE$1 + '.doc_id' +
|
|
' AND ' + DOC_STORE$1 + '.winningseq=' + BY_SEQ_STORE$1 + '.seq';
|
|
|
|
var criteria = ['maxSeq > ?'];
|
|
var sqlArgs = [opts.since];
|
|
|
|
if (opts.doc_ids) {
|
|
criteria.push(DOC_STORE$1 + '.id IN ' + qMarks(opts.doc_ids.length));
|
|
sqlArgs = sqlArgs.concat(opts.doc_ids);
|
|
}
|
|
|
|
var orderBy = 'maxSeq ' + (descending ? 'DESC' : 'ASC');
|
|
|
|
var sql = select(selectStmt, from, joiner, criteria, orderBy);
|
|
|
|
var filter = filterChange(opts);
|
|
if (!opts.view && !opts.filter) {
|
|
// we can just limit in the query
|
|
sql += ' LIMIT ' + limit;
|
|
}
|
|
|
|
var lastSeq = opts.since || 0;
|
|
db.readTransaction(function (tx) {
|
|
tx.executeSql(sql, sqlArgs, function (tx, result) {
|
|
function reportChange(change) {
|
|
return function () {
|
|
opts.onChange(change);
|
|
};
|
|
}
|
|
for (var i = 0, l = result.rows.length; i < l; i++) {
|
|
var item = result.rows.item(i);
|
|
var metadata = safeJsonParse(item.metadata);
|
|
lastSeq = item.maxSeq;
|
|
|
|
var doc = unstringifyDoc(item.winningDoc, metadata.id,
|
|
item.winningRev);
|
|
var change = opts.processChange(doc, metadata, opts);
|
|
change.seq = item.maxSeq;
|
|
|
|
var filtered = filter(change);
|
|
if (typeof filtered === 'object') {
|
|
return opts.complete(filtered);
|
|
}
|
|
|
|
if (filtered) {
|
|
numResults++;
|
|
if (returnDocs) {
|
|
results.push(change);
|
|
}
|
|
// process the attachment immediately
|
|
// for the benefit of live listeners
|
|
if (opts.attachments && opts.include_docs) {
|
|
fetchAttachmentsIfNecessary$1(doc, opts, api, tx,
|
|
reportChange(change));
|
|
} else {
|
|
reportChange(change)();
|
|
}
|
|
}
|
|
if (numResults === limit) {
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}, websqlError(opts.complete), function () {
|
|
if (!opts.continuous) {
|
|
opts.complete(null, {
|
|
results: results,
|
|
last_seq: lastSeq
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
fetchChanges();
|
|
};
|
|
|
|
api._close = function (callback) {
|
|
//WebSQL databases do not need to be closed
|
|
callback();
|
|
};
|
|
|
|
api._getAttachment = function (docId, attachId, attachment, opts, callback) {
|
|
var res;
|
|
var tx = opts.ctx;
|
|
var digest = attachment.digest;
|
|
var type = attachment.content_type;
|
|
var sql = 'SELECT escaped, ' +
|
|
'CASE WHEN escaped = 1 THEN body ELSE HEX(body) END AS body FROM ' +
|
|
ATTACH_STORE$1 + ' WHERE digest=?';
|
|
tx.executeSql(sql, [digest], function (tx, result) {
|
|
// websql has a bug where \u0000 causes early truncation in strings
|
|
// and blobs. to work around this, we used to use the hex() function,
|
|
// but that's not performant. after migration 6, we remove \u0000
|
|
// and add it back in afterwards
|
|
var item = result.rows.item(0);
|
|
var data = item.escaped ? unescapeBlob(item.body) :
|
|
parseHexString(item.body, encoding);
|
|
if (opts.binary) {
|
|
res = binStringToBluffer(data, type);
|
|
} else {
|
|
res = btoa$1(data);
|
|
}
|
|
callback(null, res);
|
|
});
|
|
};
|
|
|
|
api._getRevisionTree = function (docId, callback) {
|
|
db.readTransaction(function (tx) {
|
|
var sql = 'SELECT json AS metadata FROM ' + DOC_STORE$1 + ' WHERE id = ?';
|
|
tx.executeSql(sql, [docId], function (tx, result) {
|
|
if (!result.rows.length) {
|
|
callback(createError(MISSING_DOC));
|
|
} else {
|
|
var data = safeJsonParse(result.rows.item(0).metadata);
|
|
callback(null, data.rev_tree);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
api._doCompaction = function (docId, revs, callback) {
|
|
if (!revs.length) {
|
|
return callback();
|
|
}
|
|
db.transaction(function (tx) {
|
|
|
|
// update doc store
|
|
var sql = 'SELECT json AS metadata FROM ' + DOC_STORE$1 + ' WHERE id = ?';
|
|
tx.executeSql(sql, [docId], function (tx, result) {
|
|
var metadata = safeJsonParse(result.rows.item(0).metadata);
|
|
traverseRevTree(metadata.rev_tree, function (isLeaf, pos,
|
|
revHash, ctx, opts) {
|
|
var rev = pos + '-' + revHash;
|
|
if (revs.indexOf(rev) !== -1) {
|
|
opts.status = 'missing';
|
|
}
|
|
});
|
|
|
|
var sql = 'UPDATE ' + DOC_STORE$1 + ' SET json = ? WHERE id = ?';
|
|
tx.executeSql(sql, [safeJsonStringify(metadata), docId]);
|
|
});
|
|
|
|
compactRevs$1(revs, docId, tx);
|
|
}, websqlError(callback), function () {
|
|
callback();
|
|
});
|
|
};
|
|
|
|
api._getLocal = function (id, callback) {
|
|
db.readTransaction(function (tx) {
|
|
var sql = 'SELECT json, rev FROM ' + LOCAL_STORE$1 + ' WHERE id=?';
|
|
tx.executeSql(sql, [id], function (tx, res) {
|
|
if (res.rows.length) {
|
|
var item = res.rows.item(0);
|
|
var doc = unstringifyDoc(item.json, id, item.rev);
|
|
callback(null, doc);
|
|
} else {
|
|
callback(createError(MISSING_DOC));
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
api._putLocal = function (doc, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
delete doc._revisions; // ignore this, trust the rev
|
|
var oldRev = doc._rev;
|
|
var id = doc._id;
|
|
var newRev;
|
|
if (!oldRev) {
|
|
newRev = doc._rev = '0-1';
|
|
} else {
|
|
newRev = doc._rev = '0-' + (parseInt(oldRev.split('-')[1], 10) + 1);
|
|
}
|
|
var json = stringifyDoc(doc);
|
|
|
|
var ret;
|
|
function putLocal(tx) {
|
|
var sql;
|
|
var values;
|
|
if (oldRev) {
|
|
sql = 'UPDATE ' + LOCAL_STORE$1 + ' SET rev=?, json=? ' +
|
|
'WHERE id=? AND rev=?';
|
|
values = [newRev, json, id, oldRev];
|
|
} else {
|
|
sql = 'INSERT INTO ' + LOCAL_STORE$1 + ' (id, rev, json) VALUES (?,?,?)';
|
|
values = [id, newRev, json];
|
|
}
|
|
tx.executeSql(sql, values, function (tx, res) {
|
|
if (res.rowsAffected) {
|
|
ret = {ok: true, id: id, rev: newRev};
|
|
if (opts.ctx) { // return immediately
|
|
callback(null, ret);
|
|
}
|
|
} else {
|
|
callback(createError(REV_CONFLICT));
|
|
}
|
|
}, function () {
|
|
callback(createError(REV_CONFLICT));
|
|
return false; // ack that we handled the error
|
|
});
|
|
}
|
|
|
|
if (opts.ctx) {
|
|
putLocal(opts.ctx);
|
|
} else {
|
|
db.transaction(putLocal, websqlError(callback), function () {
|
|
if (ret) {
|
|
callback(null, ret);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
api._removeLocal = function (doc, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
var ret;
|
|
|
|
function removeLocal(tx) {
|
|
var sql = 'DELETE FROM ' + LOCAL_STORE$1 + ' WHERE id=? AND rev=?';
|
|
var params = [doc._id, doc._rev];
|
|
tx.executeSql(sql, params, function (tx, res) {
|
|
if (!res.rowsAffected) {
|
|
return callback(createError(MISSING_DOC));
|
|
}
|
|
ret = {ok: true, id: doc._id, rev: '0-0'};
|
|
if (opts.ctx) { // return immediately
|
|
callback(null, ret);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (opts.ctx) {
|
|
removeLocal(opts.ctx);
|
|
} else {
|
|
db.transaction(removeLocal, websqlError(callback), function () {
|
|
if (ret) {
|
|
callback(null, ret);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
api._destroy = function (opts, callback) {
|
|
websqlChanges.removeAllListeners(api._name);
|
|
db.transaction(function (tx) {
|
|
var stores = [DOC_STORE$1, BY_SEQ_STORE$1, ATTACH_STORE$1, META_STORE$1,
|
|
LOCAL_STORE$1, ATTACH_AND_SEQ_STORE$1];
|
|
stores.forEach(function (store) {
|
|
tx.executeSql('DROP TABLE IF EXISTS ' + store, []);
|
|
});
|
|
}, websqlError(callback), function () {
|
|
if (hasLocalStorage()) {
|
|
delete window.localStorage['_pouch__websqldb_' + api._name];
|
|
delete window.localStorage[api._name];
|
|
}
|
|
callback(null, {'ok': true});
|
|
});
|
|
};
|
|
}
|
|
|
|
function canOpenTestDB() {
|
|
try {
|
|
openDatabase('_pouch_validate_websql', 1, '', 1);
|
|
return true;
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// WKWebView had a bug where WebSQL would throw a DOM Exception 18
|
|
// (see https://bugs.webkit.org/show_bug.cgi?id=137760 and
|
|
// https://github.com/pouchdb/pouchdb/issues/5079)
|
|
// This has been fixed in latest WebKit, so we try to detect it here.
|
|
function isValidWebSQL() {
|
|
// WKWebView UA:
|
|
// Mozilla/5.0 (iPhone; CPU iPhone OS 9_2 like Mac OS X)
|
|
// AppleWebKit/601.1.46 (KHTML, like Gecko) Mobile/13C75
|
|
// Chrome for iOS UA:
|
|
// Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en)
|
|
// AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60
|
|
// Mobile/9B206 Safari/7534.48.3
|
|
// Firefox for iOS UA:
|
|
// Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4
|
|
// (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4
|
|
|
|
// indexedDB is null on some UIWebViews and undefined in others
|
|
// see: https://bugs.webkit.org/show_bug.cgi?id=137034
|
|
if (typeof indexedDB === 'undefined' || indexedDB === null ||
|
|
!/iP(hone|od|ad)/.test(navigator.userAgent)) {
|
|
// definitely not WKWebView, avoid creating an unnecessary database
|
|
return true;
|
|
}
|
|
// Cache the result in LocalStorage. Reason we do this is because if we
|
|
// call openDatabase() too many times, Safari craps out in SauceLabs and
|
|
// starts throwing DOM Exception 14s.
|
|
var hasLS = hasLocalStorage();
|
|
// Include user agent in the hash, so that if Safari is upgraded, we don't
|
|
// continually think it's broken.
|
|
var localStorageKey = '_pouch__websqldb_valid_' + navigator.userAgent;
|
|
if (hasLS && localStorage[localStorageKey]) {
|
|
return localStorage[localStorageKey] === '1';
|
|
}
|
|
var openedTestDB = canOpenTestDB();
|
|
if (hasLS) {
|
|
localStorage[localStorageKey] = openedTestDB ? '1' : '0';
|
|
}
|
|
return openedTestDB;
|
|
}
|
|
|
|
function validWithoutCheckingCordova() {
|
|
if (typeof openDatabase === 'undefined') {
|
|
return false;
|
|
}
|
|
if (typeof sqlitePlugin !== 'undefined') {
|
|
// Both sqlite-storage and SQLite Plugin 2 create this global object,
|
|
// which we can check for to determine validity. It should be defined
|
|
// after the 'deviceready' event.
|
|
return true;
|
|
}
|
|
return isValidWebSQL();
|
|
}
|
|
|
|
function valid() {
|
|
// The Cordova SQLite Plugin and SQLite Plugin 2 can be used in cordova apps,
|
|
// and we can't know whether or not the plugin was loaded until after the
|
|
// 'deviceready' event. Since it's impractical for us to wait for that event
|
|
// before returning true/false for valid(), we just return true here
|
|
// and notify the user that they may need a plugin.
|
|
if (typeof cordova !== 'undefined') {
|
|
return true;
|
|
}
|
|
return validWithoutCheckingCordova();
|
|
}
|
|
|
|
function createOpenDBFunction(opts) {
|
|
return function (name, version, description, size) {
|
|
if (typeof sqlitePlugin !== 'undefined') {
|
|
// The SQLite Plugin started deviating pretty heavily from the
|
|
// standard openDatabase() function, as they started adding more features.
|
|
// It's better to just use their "new" format and pass in a big ol'
|
|
// options object. Also there are many options here that may come from
|
|
// the PouchDB constructor, so we have to grab those.
|
|
var sqlitePluginOpts = jsExtend.extend({}, opts, {
|
|
name: name,
|
|
version: version,
|
|
description: description,
|
|
size: size
|
|
});
|
|
return sqlitePlugin.openDatabase(sqlitePluginOpts);
|
|
}
|
|
|
|
// Traditional WebSQL API
|
|
return openDatabase(name, version, description, size);
|
|
};
|
|
}
|
|
|
|
function WebSQLPouch(opts, callback) {
|
|
var websql = createOpenDBFunction(opts);
|
|
var _opts = jsExtend.extend({
|
|
websql: websql
|
|
}, opts);
|
|
|
|
if (typeof cordova !== 'undefined' && !validWithoutCheckingCordova()) {
|
|
guardedConsole('error',
|
|
'PouchDB error: you must install a SQLite plugin ' +
|
|
'in order for PouchDB to work on this platform. Options:' +
|
|
'\n - https://github.com/nolanlawson/cordova-plugin-sqlite-2' +
|
|
'\n - https://github.com/litehelpers/Cordova-sqlite-storage' +
|
|
'\n - https://github.com/Microsoft/cordova-plugin-websql');
|
|
}
|
|
|
|
WebSqlPouch$1.call(this, _opts, callback);
|
|
}
|
|
|
|
WebSQLPouch.valid = valid;
|
|
|
|
WebSQLPouch.use_prefix = true;
|
|
|
|
function WebSqlPouch (PouchDB) {
|
|
PouchDB.adapter('websql', WebSQLPouch, true);
|
|
}
|
|
|
|
function wrappedFetch() {
|
|
var wrappedPromise = {};
|
|
|
|
var promise = new PouchPromise(function (resolve, reject) {
|
|
wrappedPromise.resolve = resolve;
|
|
wrappedPromise.reject = reject;
|
|
});
|
|
|
|
var args = new Array(arguments.length);
|
|
|
|
for (var i = 0; i < args.length; i++) {
|
|
args[i] = arguments[i];
|
|
}
|
|
|
|
wrappedPromise.promise = promise;
|
|
|
|
PouchPromise.resolve().then(function () {
|
|
return fetch.apply(null, args);
|
|
}).then(function (response) {
|
|
wrappedPromise.resolve(response);
|
|
}).catch(function (error) {
|
|
wrappedPromise.reject(error);
|
|
});
|
|
|
|
return wrappedPromise;
|
|
}
|
|
|
|
function fetchRequest(options, callback) {
|
|
var wrappedPromise, timer, response;
|
|
|
|
var headers = new Headers();
|
|
|
|
var fetchOptions = {
|
|
method: options.method,
|
|
credentials: 'include',
|
|
headers: headers
|
|
};
|
|
|
|
if (options.json) {
|
|
headers.set('Accept', 'application/json');
|
|
headers.set('Content-Type', options.headers['Content-Type'] ||
|
|
'application/json');
|
|
}
|
|
|
|
if (options.body && (options.body instanceof Blob)) {
|
|
readAsArrayBuffer(options.body, function (arrayBuffer) {
|
|
fetchOptions.body = arrayBuffer;
|
|
});
|
|
} else if (options.body &&
|
|
options.processData &&
|
|
typeof options.body !== 'string') {
|
|
fetchOptions.body = JSON.stringify(options.body);
|
|
} else if ('body' in options) {
|
|
fetchOptions.body = options.body;
|
|
} else {
|
|
fetchOptions.body = null;
|
|
}
|
|
|
|
Object.keys(options.headers).forEach(function (key) {
|
|
if (options.headers.hasOwnProperty(key)) {
|
|
headers.set(key, options.headers[key]);
|
|
}
|
|
});
|
|
|
|
wrappedPromise = wrappedFetch(options.url, fetchOptions);
|
|
|
|
if (options.timeout > 0) {
|
|
timer = setTimeout(function () {
|
|
wrappedPromise.reject(new Error('Load timeout for resource: ' +
|
|
options.url));
|
|
}, options.timeout);
|
|
}
|
|
|
|
wrappedPromise.promise.then(function (fetchResponse) {
|
|
response = {
|
|
statusCode: fetchResponse.status
|
|
};
|
|
|
|
if (options.timeout > 0) {
|
|
clearTimeout(timer);
|
|
}
|
|
|
|
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
return options.binary ? fetchResponse.blob() : fetchResponse.text();
|
|
}
|
|
|
|
return fetchResponse.json();
|
|
}).then(function (result) {
|
|
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
callback(null, response, result);
|
|
} else {
|
|
callback(result, response);
|
|
}
|
|
}).catch(function (error) {
|
|
callback(error, response);
|
|
});
|
|
|
|
return {abort: wrappedPromise.reject};
|
|
}
|
|
|
|
function xhRequest(options, callback) {
|
|
|
|
var xhr, timer;
|
|
var timedout = false;
|
|
|
|
var abortReq = function () {
|
|
xhr.abort();
|
|
};
|
|
|
|
var timeoutReq = function () {
|
|
timedout = true;
|
|
xhr.abort();
|
|
};
|
|
|
|
if (options.xhr) {
|
|
xhr = new options.xhr();
|
|
} else {
|
|
xhr = new XMLHttpRequest();
|
|
}
|
|
|
|
try {
|
|
xhr.open(options.method, options.url);
|
|
} catch (exception) {
|
|
return callback(new Error(exception.name || 'Url is invalid'));
|
|
}
|
|
|
|
xhr.withCredentials = ('withCredentials' in options) ?
|
|
options.withCredentials : true;
|
|
|
|
if (options.method === 'GET') {
|
|
delete options.headers['Content-Type'];
|
|
} else if (options.json) {
|
|
options.headers.Accept = 'application/json';
|
|
options.headers['Content-Type'] = options.headers['Content-Type'] ||
|
|
'application/json';
|
|
if (options.body &&
|
|
options.processData &&
|
|
typeof options.body !== "string") {
|
|
options.body = JSON.stringify(options.body);
|
|
}
|
|
}
|
|
|
|
if (options.binary) {
|
|
xhr.responseType = 'arraybuffer';
|
|
}
|
|
|
|
if (!('body' in options)) {
|
|
options.body = null;
|
|
}
|
|
|
|
for (var key in options.headers) {
|
|
if (options.headers.hasOwnProperty(key)) {
|
|
xhr.setRequestHeader(key, options.headers[key]);
|
|
}
|
|
}
|
|
|
|
if (options.timeout > 0) {
|
|
timer = setTimeout(timeoutReq, options.timeout);
|
|
xhr.onprogress = function () {
|
|
clearTimeout(timer);
|
|
if(xhr.readyState !== 4) {
|
|
timer = setTimeout(timeoutReq, options.timeout);
|
|
}
|
|
};
|
|
if (typeof xhr.upload !== 'undefined') { // does not exist in ie9
|
|
xhr.upload.onprogress = xhr.onprogress;
|
|
}
|
|
}
|
|
|
|
xhr.onreadystatechange = function () {
|
|
if (xhr.readyState !== 4) {
|
|
return;
|
|
}
|
|
|
|
var response = {
|
|
statusCode: xhr.status
|
|
};
|
|
|
|
if (xhr.status >= 200 && xhr.status < 300) {
|
|
var data;
|
|
if (options.binary) {
|
|
data = createBlob([xhr.response || ''], {
|
|
type: xhr.getResponseHeader('Content-Type')
|
|
});
|
|
} else {
|
|
data = xhr.responseText;
|
|
}
|
|
callback(null, response, data);
|
|
} else {
|
|
var err = {};
|
|
if (timedout) {
|
|
err = new Error('ETIMEDOUT');
|
|
err.code = 'ETIMEDOUT';
|
|
} else {
|
|
try {
|
|
err = JSON.parse(xhr.response);
|
|
} catch(e) {}
|
|
}
|
|
err.status = xhr.status;
|
|
callback(err);
|
|
}
|
|
};
|
|
|
|
if (options.body && (options.body instanceof Blob)) {
|
|
readAsArrayBuffer(options.body, function (arrayBuffer) {
|
|
xhr.send(arrayBuffer);
|
|
});
|
|
} else {
|
|
xhr.send(options.body);
|
|
}
|
|
|
|
return {abort: abortReq};
|
|
}
|
|
|
|
function testXhr() {
|
|
try {
|
|
new XMLHttpRequest();
|
|
return true;
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var hasXhr = testXhr();
|
|
|
|
function ajax$1(options, callback) {
|
|
if (hasXhr || options.xhr) {
|
|
return xhRequest(options, callback);
|
|
} else {
|
|
return fetchRequest(options, callback);
|
|
}
|
|
}
|
|
|
|
// the blob already has a type; do nothing
|
|
var res$2 = function () {};
|
|
|
|
function defaultBody() {
|
|
return '';
|
|
}
|
|
|
|
function ajaxCore(options, callback) {
|
|
|
|
options = clone(options);
|
|
|
|
var defaultOptions = {
|
|
method : "GET",
|
|
headers: {},
|
|
json: true,
|
|
processData: true,
|
|
timeout: 10000,
|
|
cache: false
|
|
};
|
|
|
|
options = jsExtend.extend(defaultOptions, options);
|
|
|
|
function onSuccess(obj, resp, cb) {
|
|
if (!options.binary && options.json && typeof obj === 'string') {
|
|
/* istanbul ignore next */
|
|
try {
|
|
obj = JSON.parse(obj);
|
|
} catch (e) {
|
|
// Probably a malformed JSON from server
|
|
return cb(e);
|
|
}
|
|
}
|
|
if (Array.isArray(obj)) {
|
|
obj = obj.map(function (v) {
|
|
if (v.error || v.missing) {
|
|
return generateErrorFromResponse(v);
|
|
} else {
|
|
return v;
|
|
}
|
|
});
|
|
}
|
|
if (options.binary) {
|
|
res$2(obj, resp);
|
|
}
|
|
cb(null, obj, resp);
|
|
}
|
|
|
|
if (options.json) {
|
|
if (!options.binary) {
|
|
options.headers.Accept = 'application/json';
|
|
}
|
|
options.headers['Content-Type'] = options.headers['Content-Type'] ||
|
|
'application/json';
|
|
}
|
|
|
|
if (options.binary) {
|
|
options.encoding = null;
|
|
options.json = false;
|
|
}
|
|
|
|
if (!options.processData) {
|
|
options.json = false;
|
|
}
|
|
|
|
return ajax$1(options, function (err, response, body) {
|
|
|
|
if (err) {
|
|
return callback(generateErrorFromResponse(err));
|
|
}
|
|
|
|
var error;
|
|
var content_type = response.headers && response.headers['content-type'];
|
|
var data = body || defaultBody();
|
|
|
|
// CouchDB doesn't always return the right content-type for JSON data, so
|
|
// we check for ^{ and }$ (ignoring leading/trailing whitespace)
|
|
if (!options.binary && (options.json || !options.processData) &&
|
|
typeof data !== 'object' &&
|
|
(/json/.test(content_type) ||
|
|
(/^[\s]*\{/.test(data) && /\}[\s]*$/.test(data)))) {
|
|
try {
|
|
data = JSON.parse(data.toString());
|
|
} catch (e) {}
|
|
}
|
|
|
|
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
onSuccess(data, response, callback);
|
|
} else {
|
|
error = generateErrorFromResponse(data);
|
|
error.status = response.statusCode;
|
|
callback(error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function ajax(opts, callback) {
|
|
|
|
// cache-buster, specifically designed to work around IE's aggressive caching
|
|
// see http://www.dashbay.com/2011/05/internet-explorer-caches-ajax/
|
|
// Also Safari caches POSTs, so we need to cache-bust those too.
|
|
var ua = (navigator && navigator.userAgent) ?
|
|
navigator.userAgent.toLowerCase() : '';
|
|
|
|
var isSafari = ua.indexOf('safari') !== -1 && ua.indexOf('chrome') === -1;
|
|
var isIE = ua.indexOf('msie') !== -1;
|
|
var isEdge = ua.indexOf('edge') !== -1;
|
|
|
|
// it appears the new version of safari also caches GETs,
|
|
// see https://github.com/pouchdb/pouchdb/issues/5010
|
|
var shouldCacheBust = (isSafari ||
|
|
((isIE || isEdge) && opts.method === 'GET'));
|
|
|
|
var cache = 'cache' in opts ? opts.cache : true;
|
|
|
|
var isBlobUrl = /^blob:/.test(opts.url); // don't append nonces for blob URLs
|
|
|
|
if (!isBlobUrl && (shouldCacheBust || !cache)) {
|
|
var hasArgs = opts.url.indexOf('?') !== -1;
|
|
opts.url += (hasArgs ? '&' : '?') + '_nonce=' + Date.now();
|
|
}
|
|
|
|
return ajaxCore(opts, callback);
|
|
}
|
|
|
|
var CHANGES_BATCH_SIZE = 25;
|
|
var MAX_SIMULTANEOUS_REVS = 50;
|
|
|
|
var supportsBulkGetMap = {};
|
|
|
|
// according to http://stackoverflow.com/a/417184/680742,
|
|
// the de facto URL length limit is 2000 characters.
|
|
// but since most of our measurements don't take the full
|
|
// URL into account, we fudge it a bit.
|
|
// TODO: we could measure the full URL to enforce exactly 2000 chars
|
|
var MAX_URL_LENGTH = 1800;
|
|
|
|
var log$1 = debug('pouchdb:http');
|
|
|
|
function readAttachmentsAsBlobOrBuffer(row) {
|
|
var atts = row.doc && row.doc._attachments;
|
|
if (!atts) {
|
|
return;
|
|
}
|
|
Object.keys(atts).forEach(function (filename) {
|
|
var att = atts[filename];
|
|
att.data = b64ToBluffer(att.data, att.content_type);
|
|
});
|
|
}
|
|
|
|
function encodeDocId(id) {
|
|
if (/^_design/.test(id)) {
|
|
return '_design/' + encodeURIComponent(id.slice(8));
|
|
}
|
|
if (/^_local/.test(id)) {
|
|
return '_local/' + encodeURIComponent(id.slice(7));
|
|
}
|
|
return encodeURIComponent(id);
|
|
}
|
|
|
|
function preprocessAttachments$1(doc) {
|
|
if (!doc._attachments || !Object.keys(doc._attachments)) {
|
|
return PouchPromise.resolve();
|
|
}
|
|
|
|
return PouchPromise.all(Object.keys(doc._attachments).map(function (key) {
|
|
var attachment = doc._attachments[key];
|
|
if (attachment.data && typeof attachment.data !== 'string') {
|
|
return new PouchPromise(function (resolve) {
|
|
blobToBase64(attachment.data, resolve);
|
|
}).then(function (b64) {
|
|
attachment.data = b64;
|
|
});
|
|
}
|
|
}));
|
|
}
|
|
|
|
// Get all the information you possibly can about the URI given by name and
|
|
// return it as a suitable object.
|
|
function getHost(name) {
|
|
// Prase the URI into all its little bits
|
|
var uri = parseUri(name);
|
|
|
|
// Store the user and password as a separate auth object
|
|
if (uri.user || uri.password) {
|
|
uri.auth = {username: uri.user, password: uri.password};
|
|
}
|
|
|
|
// Split the path part of the URI into parts using '/' as the delimiter
|
|
// after removing any leading '/' and any trailing '/'
|
|
var parts = uri.path.replace(/(^\/|\/$)/g, '').split('/');
|
|
|
|
// Store the first part as the database name and remove it from the parts
|
|
// array
|
|
uri.db = parts.pop();
|
|
// Prevent double encoding of URI component
|
|
if (uri.db.indexOf('%') === -1) {
|
|
uri.db = encodeURIComponent(uri.db);
|
|
}
|
|
|
|
// Restore the path by joining all the remaining parts (all the parts
|
|
// except for the database name) with '/'s
|
|
uri.path = parts.join('/');
|
|
|
|
return uri;
|
|
}
|
|
|
|
// Generate a URL with the host data given by opts and the given path
|
|
function genDBUrl(opts, path) {
|
|
return genUrl(opts, opts.db + '/' + path);
|
|
}
|
|
|
|
// Generate a URL with the host data given by opts and the given path
|
|
function genUrl(opts, path) {
|
|
// If the host already has a path, then we need to have a path delimiter
|
|
// Otherwise, the path delimiter is the empty string
|
|
var pathDel = !opts.path ? '' : '/';
|
|
|
|
// If the host already has a path, then we need to have a path delimiter
|
|
// Otherwise, the path delimiter is the empty string
|
|
return opts.protocol + '://' + opts.host +
|
|
(opts.port ? (':' + opts.port) : '') +
|
|
'/' + opts.path + pathDel + path;
|
|
}
|
|
|
|
function paramsToStr(params) {
|
|
return '?' + Object.keys(params).map(function (k) {
|
|
return k + '=' + encodeURIComponent(params[k]);
|
|
}).join('&');
|
|
}
|
|
|
|
// Implements the PouchDB API for dealing with CouchDB instances over HTTP
|
|
function HttpPouch(opts, callback) {
|
|
// The functions that will be publicly available for HttpPouch
|
|
var api = this;
|
|
|
|
// Parse the URI given by opts.name into an easy-to-use object
|
|
var getHostFun = getHost;
|
|
|
|
// TODO: this seems to only be used by yarong for the Thali project.
|
|
// Verify whether or not it's still needed.
|
|
/* istanbul ignore if */
|
|
if (opts.getHost) {
|
|
getHostFun = opts.getHost;
|
|
}
|
|
|
|
var host = getHostFun(opts.name, opts);
|
|
var dbUrl = genDBUrl(host, '');
|
|
|
|
opts = clone(opts);
|
|
var ajaxOpts = opts.ajax || {};
|
|
|
|
api.getUrl = function () { return dbUrl; };
|
|
api.getHeaders = function () { return ajaxOpts.headers || {}; };
|
|
|
|
if (opts.auth || host.auth) {
|
|
var nAuth = opts.auth || host.auth;
|
|
var str = nAuth.username + ':' + nAuth.password;
|
|
var token = btoa$1(unescape(encodeURIComponent(str)));
|
|
ajaxOpts.headers = ajaxOpts.headers || {};
|
|
ajaxOpts.headers.Authorization = 'Basic ' + token;
|
|
}
|
|
|
|
// Not strictly necessary, but we do this because numerous tests
|
|
// rely on swapping ajax in and out.
|
|
api._ajax = ajax;
|
|
|
|
function ajax$$(userOpts, options, callback) {
|
|
var reqAjax = userOpts.ajax || {};
|
|
var reqOpts = jsExtend.extend(clone(ajaxOpts), reqAjax, options);
|
|
log$1(reqOpts.method + ' ' + reqOpts.url);
|
|
return api._ajax(reqOpts, callback);
|
|
}
|
|
|
|
function ajaxPromise(userOpts, opts) {
|
|
return new PouchPromise(function (resolve, reject) {
|
|
ajax$$(userOpts, opts, function (err, res) {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
resolve(res);
|
|
});
|
|
});
|
|
}
|
|
|
|
function adapterFun$$(name, fun) {
|
|
return adapterFun(name, getArguments(function (args) {
|
|
setup().then(function () {
|
|
return fun.apply(this, args);
|
|
}).catch(function (e) {
|
|
var callback = args.pop();
|
|
callback(e);
|
|
});
|
|
}));
|
|
}
|
|
|
|
var setupPromise;
|
|
|
|
function setup() {
|
|
// TODO: Remove `skipSetup` in favor of `skip_setup` in a future release
|
|
if (opts.skipSetup || opts.skip_setup) {
|
|
return PouchPromise.resolve();
|
|
}
|
|
|
|
// If there is a setup in process or previous successful setup
|
|
// done then we will use that
|
|
// If previous setups have been rejected we will try again
|
|
if (setupPromise) {
|
|
return setupPromise;
|
|
}
|
|
|
|
var checkExists = {method: 'GET', url: dbUrl};
|
|
setupPromise = ajaxPromise({}, checkExists).catch(function (err) {
|
|
if (err && err.status && err.status === 404) {
|
|
// Doesnt exist, create it
|
|
explainError(404, 'PouchDB is just detecting if the remote exists.');
|
|
return ajaxPromise({}, {method: 'PUT', url: dbUrl});
|
|
} else {
|
|
return PouchPromise.reject(err);
|
|
}
|
|
}).catch(function (err) {
|
|
// If we try to create a database that already exists, skipped in
|
|
// istanbul since its catching a race condition.
|
|
/* istanbul ignore if */
|
|
if (err && err.status && err.status === 412) {
|
|
return true;
|
|
}
|
|
return PouchPromise.reject(err);
|
|
});
|
|
|
|
setupPromise.catch(function () {
|
|
setupPromise = null;
|
|
});
|
|
|
|
return setupPromise;
|
|
}
|
|
|
|
setTimeout(function () {
|
|
callback(null, api);
|
|
});
|
|
|
|
api.type = function () {
|
|
return 'http';
|
|
};
|
|
|
|
api.id = adapterFun$$('id', function (callback) {
|
|
ajax$$({}, {method: 'GET', url: genUrl(host, '')}, function (err, result) {
|
|
var uuid = (result && result.uuid) ?
|
|
(result.uuid + host.db) : genDBUrl(host, '');
|
|
callback(null, uuid);
|
|
});
|
|
});
|
|
|
|
api.request = adapterFun$$('request', function (options, callback) {
|
|
options.url = genDBUrl(host, options.url);
|
|
ajax$$({}, options, callback);
|
|
});
|
|
|
|
// Sends a POST request to the host calling the couchdb _compact function
|
|
// version: The version of CouchDB it is running
|
|
api.compact = adapterFun$$('compact', function (opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
opts = clone(opts);
|
|
ajax$$(opts, {
|
|
url: genDBUrl(host, '_compact'),
|
|
method: 'POST'
|
|
}, function () {
|
|
function ping() {
|
|
api.info(function (err, res) {
|
|
if (res && !res.compact_running) {
|
|
callback(null, {ok: true});
|
|
} else {
|
|
setTimeout(ping, opts.interval || 200);
|
|
}
|
|
});
|
|
}
|
|
// Ping the http if it's finished compaction
|
|
ping();
|
|
});
|
|
});
|
|
|
|
api.bulkGet = adapterFun('bulkGet', function (opts, callback) {
|
|
var self = this;
|
|
|
|
function doBulkGet(cb) {
|
|
var params = {};
|
|
if (opts.revs) {
|
|
params.revs = true;
|
|
}
|
|
if (opts.attachments) {
|
|
/* istanbul ignore next */
|
|
params.attachments = true;
|
|
}
|
|
ajax$$({}, {
|
|
url: genDBUrl(host, '_bulk_get' + paramsToStr(params)),
|
|
method: 'POST',
|
|
body: { docs: opts.docs}
|
|
}, cb);
|
|
}
|
|
|
|
function doBulkGetShim() {
|
|
// avoid "url too long error" by splitting up into multiple requests
|
|
var batchSize = MAX_SIMULTANEOUS_REVS;
|
|
var numBatches = Math.ceil(opts.docs.length / batchSize);
|
|
var numDone = 0;
|
|
var results = new Array(numBatches);
|
|
|
|
function onResult(batchNum) {
|
|
return function (err, res) {
|
|
// err is impossible because shim returns a list of errs in that case
|
|
results[batchNum] = res.results;
|
|
if (++numDone === numBatches) {
|
|
callback(null, {results: flatten(results)});
|
|
}
|
|
};
|
|
}
|
|
|
|
for (var i = 0; i < numBatches; i++) {
|
|
var subOpts = pick(opts, ['revs', 'attachments']);
|
|
subOpts.ajax = ajaxOpts;
|
|
subOpts.docs = opts.docs.slice(i * batchSize,
|
|
Math.min(opts.docs.length, (i + 1) * batchSize));
|
|
bulkGet(self, subOpts, onResult(i));
|
|
}
|
|
}
|
|
|
|
// mark the whole database as either supporting or not supporting _bulk_get
|
|
var dbUrl = genUrl(host, '');
|
|
var supportsBulkGet = supportsBulkGetMap[dbUrl];
|
|
|
|
if (typeof supportsBulkGet !== 'boolean') {
|
|
// check if this database supports _bulk_get
|
|
doBulkGet(function (err, res) {
|
|
/* istanbul ignore else */
|
|
if (err) {
|
|
var status = Math.floor(err.status / 100);
|
|
/* istanbul ignore else */
|
|
if (status === 4 || status === 5) { // 40x or 50x
|
|
supportsBulkGetMap[dbUrl] = false;
|
|
explainError(
|
|
err.status,
|
|
'PouchDB is just detecting if the remote ' +
|
|
'supports the _bulk_get API.'
|
|
);
|
|
doBulkGetShim();
|
|
} else {
|
|
callback(err);
|
|
}
|
|
} else {
|
|
supportsBulkGetMap[dbUrl] = true;
|
|
callback(null, res);
|
|
}
|
|
});
|
|
} else if (supportsBulkGet) {
|
|
/* istanbul ignore next */
|
|
doBulkGet(callback);
|
|
} else {
|
|
doBulkGetShim();
|
|
}
|
|
});
|
|
|
|
// Calls GET on the host, which gets back a JSON string containing
|
|
// couchdb: A welcome string
|
|
// version: The version of CouchDB it is running
|
|
api._info = function (callback) {
|
|
setup().then(function () {
|
|
ajax$$({}, {
|
|
method: 'GET',
|
|
url: genDBUrl(host, '')
|
|
}, function (err, res) {
|
|
/* istanbul ignore next */
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
res.host = genDBUrl(host, '');
|
|
callback(null, res);
|
|
});
|
|
}).catch(callback);
|
|
};
|
|
|
|
// Get the document with the given id from the database given by host.
|
|
// The id could be solely the _id in the database, or it may be a
|
|
// _design/ID or _local/ID path
|
|
api.get = adapterFun$$('get', function (id, opts, callback) {
|
|
// If no options were given, set the callback to the second parameter
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
opts = clone(opts);
|
|
|
|
// List of parameters to add to the GET request
|
|
var params = {};
|
|
|
|
if (opts.revs) {
|
|
params.revs = true;
|
|
}
|
|
|
|
if (opts.revs_info) {
|
|
params.revs_info = true;
|
|
}
|
|
|
|
if (opts.open_revs) {
|
|
if (opts.open_revs !== "all") {
|
|
opts.open_revs = JSON.stringify(opts.open_revs);
|
|
}
|
|
params.open_revs = opts.open_revs;
|
|
}
|
|
|
|
if (opts.rev) {
|
|
params.rev = opts.rev;
|
|
}
|
|
|
|
if (opts.conflicts) {
|
|
params.conflicts = opts.conflicts;
|
|
}
|
|
|
|
id = encodeDocId(id);
|
|
|
|
// Set the options for the ajax call
|
|
var options = {
|
|
method: 'GET',
|
|
url: genDBUrl(host, id + paramsToStr(params))
|
|
};
|
|
|
|
function fetchAttachments(doc) {
|
|
var atts = doc._attachments;
|
|
var filenames = atts && Object.keys(atts);
|
|
if (!atts || !filenames.length) {
|
|
return;
|
|
}
|
|
// we fetch these manually in separate XHRs, because
|
|
// Sync Gateway would normally send it back as multipart/mixed,
|
|
// which we cannot parse. Also, this is more efficient than
|
|
// receiving attachments as base64-encoded strings.
|
|
function fetch() {
|
|
|
|
if (!filenames.length) {
|
|
return null;
|
|
}
|
|
|
|
var filename = filenames.pop();
|
|
var att = atts[filename];
|
|
var path = encodeDocId(doc._id) + '/' + encodeAttachmentId(filename) +
|
|
'?rev=' + doc._rev;
|
|
return ajaxPromise(opts, {
|
|
method: 'GET',
|
|
url: genDBUrl(host, path),
|
|
binary: true
|
|
}).then(function (blob) {
|
|
if (opts.binary) {
|
|
return blob;
|
|
}
|
|
return new PouchPromise(function (resolve) {
|
|
blobToBase64(blob, resolve);
|
|
});
|
|
}).then(function (data) {
|
|
delete att.stub;
|
|
delete att.length;
|
|
att.data = data;
|
|
});
|
|
}
|
|
|
|
// This limits the number of parallel xhr requests to 5 any time
|
|
// to avoid issues with maximum browser request limits
|
|
return new PromisePool(fetch, 5, {promise: PouchPromise}).start();
|
|
}
|
|
|
|
function fetchAllAttachments(docOrDocs) {
|
|
if (Array.isArray(docOrDocs)) {
|
|
return PouchPromise.all(docOrDocs.map(function (doc) {
|
|
if (doc.ok) {
|
|
return fetchAttachments(doc.ok);
|
|
}
|
|
}));
|
|
}
|
|
return fetchAttachments(docOrDocs);
|
|
}
|
|
|
|
ajaxPromise(opts, options).then(function (res) {
|
|
return PouchPromise.resolve().then(function () {
|
|
if (opts.attachments) {
|
|
return fetchAllAttachments(res);
|
|
}
|
|
}).then(function () {
|
|
callback(null, res);
|
|
});
|
|
}).catch(callback);
|
|
});
|
|
|
|
// Delete the document given by doc from the database given by host.
|
|
api.remove = adapterFun$$('remove',
|
|
function (docOrId, optsOrRev, opts, callback) {
|
|
var doc;
|
|
if (typeof optsOrRev === 'string') {
|
|
// id, rev, opts, callback style
|
|
doc = {
|
|
_id: docOrId,
|
|
_rev: optsOrRev
|
|
};
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
} else {
|
|
// doc, opts, callback style
|
|
doc = docOrId;
|
|
if (typeof optsOrRev === 'function') {
|
|
callback = optsOrRev;
|
|
opts = {};
|
|
} else {
|
|
callback = opts;
|
|
opts = optsOrRev;
|
|
}
|
|
}
|
|
|
|
var rev = (doc._rev || opts.rev);
|
|
|
|
// Delete the document
|
|
ajax$$(opts, {
|
|
method: 'DELETE',
|
|
url: genDBUrl(host, encodeDocId(doc._id)) + '?rev=' + rev
|
|
}, callback);
|
|
});
|
|
|
|
function encodeAttachmentId(attachmentId) {
|
|
return attachmentId.split("/").map(encodeURIComponent).join("/");
|
|
}
|
|
|
|
// Get the attachment
|
|
api.getAttachment =
|
|
adapterFun$$('getAttachment', function (docId, attachmentId, opts,
|
|
callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
var params = opts.rev ? ('?rev=' + opts.rev) : '';
|
|
var url = genDBUrl(host, encodeDocId(docId)) + '/' +
|
|
encodeAttachmentId(attachmentId) + params;
|
|
ajax$$(opts, {
|
|
method: 'GET',
|
|
url: url,
|
|
binary: true
|
|
}, callback);
|
|
});
|
|
|
|
// Remove the attachment given by the id and rev
|
|
api.removeAttachment =
|
|
adapterFun$$('removeAttachment', function (docId, attachmentId, rev,
|
|
callback) {
|
|
|
|
var url = genDBUrl(host, encodeDocId(docId) + '/' +
|
|
encodeAttachmentId(attachmentId)) + '?rev=' + rev;
|
|
|
|
ajax$$({}, {
|
|
method: 'DELETE',
|
|
url: url
|
|
}, callback);
|
|
});
|
|
|
|
// Add the attachment given by blob and its contentType property
|
|
// to the document with the given id, the revision given by rev, and
|
|
// add it to the database given by host.
|
|
api.putAttachment =
|
|
adapterFun$$('putAttachment', function (docId, attachmentId, rev, blob,
|
|
type, callback) {
|
|
if (typeof type === 'function') {
|
|
callback = type;
|
|
type = blob;
|
|
blob = rev;
|
|
rev = null;
|
|
}
|
|
var id = encodeDocId(docId) + '/' + encodeAttachmentId(attachmentId);
|
|
var url = genDBUrl(host, id);
|
|
if (rev) {
|
|
url += '?rev=' + rev;
|
|
}
|
|
|
|
if (typeof blob === 'string') {
|
|
// input is assumed to be a base64 string
|
|
var binary;
|
|
try {
|
|
binary = atob$1(blob);
|
|
} catch (err) {
|
|
return callback(createError(BAD_ARG,
|
|
'Attachment is not a valid base64 string'));
|
|
}
|
|
blob = binary ? binStringToBluffer(binary, type) : '';
|
|
}
|
|
|
|
var opts = {
|
|
headers: {'Content-Type': type},
|
|
method: 'PUT',
|
|
url: url,
|
|
processData: false,
|
|
body: blob,
|
|
timeout: ajaxOpts.timeout || 60000
|
|
};
|
|
// Add the attachment
|
|
ajax$$({}, opts, callback);
|
|
});
|
|
|
|
// Update/create multiple documents given by req in the database
|
|
// given by host.
|
|
api._bulkDocs = function (req, opts, callback) {
|
|
// If new_edits=false then it prevents the database from creating
|
|
// new revision numbers for the documents. Instead it just uses
|
|
// the old ones. This is used in database replication.
|
|
req.new_edits = opts.new_edits;
|
|
|
|
setup().then(function () {
|
|
return PouchPromise.all(req.docs.map(preprocessAttachments$1));
|
|
}).then(function () {
|
|
// Update/create the documents
|
|
ajax$$(opts, {
|
|
method: 'POST',
|
|
url: genDBUrl(host, '_bulk_docs'),
|
|
body: req
|
|
}, function (err, results) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
results.forEach(function (result) {
|
|
result.ok = true; // smooths out cloudant not adding this
|
|
});
|
|
callback(null, results);
|
|
});
|
|
}).catch(callback);
|
|
};
|
|
|
|
// Get a listing of the documents in the database given
|
|
// by host and ordered by increasing id.
|
|
api.allDocs = adapterFun$$('allDocs', function (opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
opts = clone(opts);
|
|
|
|
// List of parameters to add to the GET request
|
|
var params = {};
|
|
var body;
|
|
var method = 'GET';
|
|
|
|
if (opts.conflicts) {
|
|
params.conflicts = true;
|
|
}
|
|
|
|
if (opts.descending) {
|
|
params.descending = true;
|
|
}
|
|
|
|
if (opts.include_docs) {
|
|
params.include_docs = true;
|
|
}
|
|
|
|
// added in CouchDB 1.6.0
|
|
if (opts.attachments) {
|
|
params.attachments = true;
|
|
}
|
|
|
|
if (opts.key) {
|
|
params.key = JSON.stringify(opts.key);
|
|
}
|
|
|
|
if (opts.start_key) {
|
|
opts.startkey = opts.start_key;
|
|
}
|
|
|
|
if (opts.startkey) {
|
|
params.startkey = JSON.stringify(opts.startkey);
|
|
}
|
|
|
|
if (opts.end_key) {
|
|
opts.endkey = opts.end_key;
|
|
}
|
|
|
|
if (opts.endkey) {
|
|
params.endkey = JSON.stringify(opts.endkey);
|
|
}
|
|
|
|
if (typeof opts.inclusive_end !== 'undefined') {
|
|
params.inclusive_end = !!opts.inclusive_end;
|
|
}
|
|
|
|
if (typeof opts.limit !== 'undefined') {
|
|
params.limit = opts.limit;
|
|
}
|
|
|
|
if (typeof opts.skip !== 'undefined') {
|
|
params.skip = opts.skip;
|
|
}
|
|
|
|
var paramStr = paramsToStr(params);
|
|
|
|
if (typeof opts.keys !== 'undefined') {
|
|
|
|
var keysAsString =
|
|
'keys=' + encodeURIComponent(JSON.stringify(opts.keys));
|
|
if (keysAsString.length + paramStr.length + 1 <= MAX_URL_LENGTH) {
|
|
// If the keys are short enough, do a GET. we do this to work around
|
|
// Safari not understanding 304s on POSTs (see issue #1239)
|
|
paramStr += '&' + keysAsString;
|
|
} else {
|
|
// If keys are too long, issue a POST request to circumvent GET
|
|
// query string limits
|
|
// see http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options
|
|
method = 'POST';
|
|
body = {keys: opts.keys};
|
|
}
|
|
}
|
|
|
|
// Get the document listing
|
|
ajaxPromise(opts, {
|
|
method: method,
|
|
url: genDBUrl(host, '_all_docs' + paramStr),
|
|
body: body
|
|
}).then(function (res) {
|
|
if (opts.include_docs && opts.attachments && opts.binary) {
|
|
res.rows.forEach(readAttachmentsAsBlobOrBuffer);
|
|
}
|
|
callback(null, res);
|
|
}).catch(callback);
|
|
});
|
|
|
|
// Get a list of changes made to documents in the database given by host.
|
|
// TODO According to the README, there should be two other methods here,
|
|
// api.changes.addListener and api.changes.removeListener.
|
|
api._changes = function (opts) {
|
|
|
|
// We internally page the results of a changes request, this means
|
|
// if there is a large set of changes to be returned we can start
|
|
// processing them quicker instead of waiting on the entire
|
|
// set of changes to return and attempting to process them at once
|
|
var batchSize = 'batch_size' in opts ? opts.batch_size : CHANGES_BATCH_SIZE;
|
|
|
|
opts = clone(opts);
|
|
opts.timeout = ('timeout' in opts) ? opts.timeout :
|
|
('timeout' in ajaxOpts) ? ajaxOpts.timeout :
|
|
30 * 1000;
|
|
|
|
// We give a 5 second buffer for CouchDB changes to respond with
|
|
// an ok timeout (if a timeout it set)
|
|
var params = opts.timeout ? {timeout: opts.timeout - (5 * 1000)} : {};
|
|
var limit = (typeof opts.limit !== 'undefined') ? opts.limit : false;
|
|
var returnDocs;
|
|
if ('return_docs' in opts) {
|
|
returnDocs = opts.return_docs;
|
|
} else if ('returnDocs' in opts) {
|
|
// TODO: Remove 'returnDocs' in favor of 'return_docs' in a future release
|
|
returnDocs = opts.returnDocs;
|
|
} else {
|
|
returnDocs = true;
|
|
}
|
|
//
|
|
var leftToFetch = limit;
|
|
|
|
if (opts.style) {
|
|
params.style = opts.style;
|
|
}
|
|
|
|
if (opts.include_docs || opts.filter && typeof opts.filter === 'function') {
|
|
params.include_docs = true;
|
|
}
|
|
|
|
if (opts.attachments) {
|
|
params.attachments = true;
|
|
}
|
|
|
|
if (opts.continuous) {
|
|
params.feed = 'longpoll';
|
|
}
|
|
|
|
if (opts.conflicts) {
|
|
params.conflicts = true;
|
|
}
|
|
|
|
if (opts.descending) {
|
|
params.descending = true;
|
|
}
|
|
|
|
if ('heartbeat' in opts) {
|
|
// If the heartbeat value is false, it disables the default heartbeat
|
|
if (opts.heartbeat) {
|
|
params.heartbeat = opts.heartbeat;
|
|
}
|
|
} else {
|
|
// Default heartbeat to 10 seconds
|
|
params.heartbeat = 10000;
|
|
}
|
|
|
|
if (opts.filter && typeof opts.filter === 'string') {
|
|
params.filter = opts.filter;
|
|
}
|
|
|
|
if (opts.view && typeof opts.view === 'string') {
|
|
params.filter = '_view';
|
|
params.view = opts.view;
|
|
}
|
|
|
|
// If opts.query_params exists, pass it through to the changes request.
|
|
// These parameters may be used by the filter on the source database.
|
|
if (opts.query_params && typeof opts.query_params === 'object') {
|
|
for (var param_name in opts.query_params) {
|
|
/* istanbul ignore else */
|
|
if (opts.query_params.hasOwnProperty(param_name)) {
|
|
params[param_name] = opts.query_params[param_name];
|
|
}
|
|
}
|
|
}
|
|
|
|
var method = 'GET';
|
|
var body;
|
|
|
|
if (opts.doc_ids) {
|
|
// set this automagically for the user; it's annoying that couchdb
|
|
// requires both a "filter" and a "doc_ids" param.
|
|
params.filter = '_doc_ids';
|
|
|
|
var docIdsJson = JSON.stringify(opts.doc_ids);
|
|
|
|
if (docIdsJson.length < MAX_URL_LENGTH) {
|
|
params.doc_ids = docIdsJson;
|
|
} else {
|
|
// anything greater than ~2000 is unsafe for gets, so
|
|
// use POST instead
|
|
method = 'POST';
|
|
body = {doc_ids: opts.doc_ids };
|
|
}
|
|
}
|
|
|
|
var xhr;
|
|
var lastFetchedSeq;
|
|
|
|
// Get all the changes starting wtih the one immediately after the
|
|
// sequence number given by since.
|
|
var fetch = function (since, callback) {
|
|
if (opts.aborted) {
|
|
return;
|
|
}
|
|
params.since = since;
|
|
// "since" can be any kind of json object in Coudant/CouchDB 2.x
|
|
/* istanbul ignore next */
|
|
if (typeof params.since === "object") {
|
|
params.since = JSON.stringify(params.since);
|
|
}
|
|
|
|
if (opts.descending) {
|
|
if (limit) {
|
|
params.limit = leftToFetch;
|
|
}
|
|
} else {
|
|
params.limit = (!limit || leftToFetch > batchSize) ?
|
|
batchSize : leftToFetch;
|
|
}
|
|
|
|
// Set the options for the ajax call
|
|
var xhrOpts = {
|
|
method: method,
|
|
url: genDBUrl(host, '_changes' + paramsToStr(params)),
|
|
timeout: opts.timeout,
|
|
body: body
|
|
};
|
|
lastFetchedSeq = since;
|
|
|
|
/* istanbul ignore if */
|
|
if (opts.aborted) {
|
|
return;
|
|
}
|
|
|
|
// Get the changes
|
|
setup().then(function () {
|
|
xhr = ajax$$(opts, xhrOpts, callback);
|
|
}).catch(callback);
|
|
};
|
|
|
|
// If opts.since exists, get all the changes from the sequence
|
|
// number given by opts.since. Otherwise, get all the changes
|
|
// from the sequence number 0.
|
|
var results = {results: []};
|
|
|
|
var fetched = function (err, res) {
|
|
if (opts.aborted) {
|
|
return;
|
|
}
|
|
var raw_results_length = 0;
|
|
// If the result of the ajax call (res) contains changes (res.results)
|
|
if (res && res.results) {
|
|
raw_results_length = res.results.length;
|
|
results.last_seq = res.last_seq;
|
|
// For each change
|
|
var req = {};
|
|
req.query = opts.query_params;
|
|
res.results = res.results.filter(function (c) {
|
|
leftToFetch--;
|
|
var ret = filterChange(opts)(c);
|
|
if (ret) {
|
|
if (opts.include_docs && opts.attachments && opts.binary) {
|
|
readAttachmentsAsBlobOrBuffer(c);
|
|
}
|
|
if (returnDocs) {
|
|
results.results.push(c);
|
|
}
|
|
opts.onChange(c);
|
|
}
|
|
return ret;
|
|
});
|
|
} else if (err) {
|
|
// In case of an error, stop listening for changes and call
|
|
// opts.complete
|
|
opts.aborted = true;
|
|
opts.complete(err);
|
|
return;
|
|
}
|
|
|
|
// The changes feed may have timed out with no results
|
|
// if so reuse last update sequence
|
|
if (res && res.last_seq) {
|
|
lastFetchedSeq = res.last_seq;
|
|
}
|
|
|
|
var finished = (limit && leftToFetch <= 0) ||
|
|
(res && raw_results_length < batchSize) ||
|
|
(opts.descending);
|
|
|
|
if ((opts.continuous && !(limit && leftToFetch <= 0)) || !finished) {
|
|
// Queue a call to fetch again with the newest sequence number
|
|
setTimeout(function () { fetch(lastFetchedSeq, fetched); }, 0);
|
|
} else {
|
|
// We're done, call the callback
|
|
opts.complete(null, results);
|
|
}
|
|
};
|
|
|
|
fetch(opts.since || 0, fetched);
|
|
|
|
// Return a method to cancel this method from processing any more
|
|
return {
|
|
cancel: function () {
|
|
opts.aborted = true;
|
|
if (xhr) {
|
|
xhr.abort();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
// Given a set of document/revision IDs (given by req), tets the subset of
|
|
// those that do NOT correspond to revisions stored in the database.
|
|
// See http://wiki.apache.org/couchdb/HttpPostRevsDiff
|
|
api.revsDiff = adapterFun$$('revsDiff', function (req, opts, callback) {
|
|
// If no options were given, set the callback to be the second parameter
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
|
|
// Get the missing document/revision IDs
|
|
ajax$$(opts, {
|
|
method: 'POST',
|
|
url: genDBUrl(host, '_revs_diff'),
|
|
body: req
|
|
}, callback);
|
|
});
|
|
|
|
api._close = function (callback) {
|
|
callback();
|
|
};
|
|
|
|
api._destroy = function (options, callback) {
|
|
ajax$$(options, {
|
|
url: genDBUrl(host, ''),
|
|
method: 'DELETE'
|
|
}, function (err, resp) {
|
|
if (err && err.status && err.status !== 404) {
|
|
return callback(err);
|
|
}
|
|
callback(null, resp);
|
|
});
|
|
};
|
|
}
|
|
|
|
// HttpPouch is a valid adapter.
|
|
HttpPouch.valid = function () {
|
|
return true;
|
|
};
|
|
|
|
function HttpPouch$1 (PouchDB) {
|
|
PouchDB.adapter('http', HttpPouch, false);
|
|
PouchDB.adapter('https', HttpPouch, false);
|
|
}
|
|
|
|
function TaskQueue$1() {
|
|
this.promise = new PouchPromise(function (fulfill) {fulfill(); });
|
|
}
|
|
TaskQueue$1.prototype.add = function (promiseFactory) {
|
|
this.promise = this.promise.catch(function () {
|
|
// just recover
|
|
}).then(function () {
|
|
return promiseFactory();
|
|
});
|
|
return this.promise;
|
|
};
|
|
TaskQueue$1.prototype.finish = function () {
|
|
return this.promise;
|
|
};
|
|
|
|
function createView(opts) {
|
|
var sourceDB = opts.db;
|
|
var viewName = opts.viewName;
|
|
var mapFun = opts.map;
|
|
var reduceFun = opts.reduce;
|
|
var temporary = opts.temporary;
|
|
|
|
// the "undefined" part is for backwards compatibility
|
|
var viewSignature = mapFun.toString() + (reduceFun && reduceFun.toString()) +
|
|
'undefined';
|
|
|
|
var cachedViews;
|
|
if (!temporary) {
|
|
// cache this to ensure we don't try to update the same view twice
|
|
cachedViews = sourceDB._cachedViews = sourceDB._cachedViews || {};
|
|
if (cachedViews[viewSignature]) {
|
|
return cachedViews[viewSignature];
|
|
}
|
|
}
|
|
|
|
var promiseForView = sourceDB.info().then(function (info) {
|
|
|
|
var depDbName = info.db_name + '-mrview-' +
|
|
(temporary ? 'temp' : stringMd5(viewSignature));
|
|
|
|
// save the view name in the source db so it can be cleaned up if necessary
|
|
// (e.g. when the _design doc is deleted, remove all associated view data)
|
|
function diffFunction(doc) {
|
|
doc.views = doc.views || {};
|
|
var fullViewName = viewName;
|
|
if (fullViewName.indexOf('/') === -1) {
|
|
fullViewName = viewName + '/' + viewName;
|
|
}
|
|
var depDbs = doc.views[fullViewName] = doc.views[fullViewName] || {};
|
|
/* istanbul ignore if */
|
|
if (depDbs[depDbName]) {
|
|
return; // no update necessary
|
|
}
|
|
depDbs[depDbName] = true;
|
|
return doc;
|
|
}
|
|
return upsert(sourceDB, '_local/mrviews', diffFunction).then(function () {
|
|
return sourceDB.registerDependentDatabase(depDbName).then(function (res) {
|
|
var db = res.db;
|
|
db.auto_compaction = true;
|
|
var view = {
|
|
name: depDbName,
|
|
db: db,
|
|
sourceDB: sourceDB,
|
|
adapter: sourceDB.adapter,
|
|
mapFun: mapFun,
|
|
reduceFun: reduceFun
|
|
};
|
|
return view.db.get('_local/lastSeq').catch(function (err) {
|
|
/* istanbul ignore if */
|
|
if (err.status !== 404) {
|
|
throw err;
|
|
}
|
|
}).then(function (lastSeqDoc) {
|
|
view.seq = lastSeqDoc ? lastSeqDoc.seq : 0;
|
|
if (cachedViews) {
|
|
view.db.once('destroyed', function () {
|
|
delete cachedViews[viewSignature];
|
|
});
|
|
}
|
|
return view;
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
if (cachedViews) {
|
|
cachedViews[viewSignature] = promiseForView;
|
|
}
|
|
return promiseForView;
|
|
}
|
|
|
|
function evalfunc(func, emit, sum, log, isArray, toJSON) {
|
|
return scopedEval(
|
|
"return (" + func.replace(/;\s*$/, "") + ");",
|
|
{
|
|
emit: emit,
|
|
sum: sum,
|
|
log: log,
|
|
isArray: isArray,
|
|
toJSON: toJSON
|
|
}
|
|
);
|
|
}
|
|
|
|
var promisedCallback = function (promise, callback) {
|
|
if (callback) {
|
|
promise.then(function (res) {
|
|
process.nextTick(function () {
|
|
callback(null, res);
|
|
});
|
|
}, function (reason) {
|
|
process.nextTick(function () {
|
|
callback(reason);
|
|
});
|
|
});
|
|
}
|
|
return promise;
|
|
};
|
|
|
|
var callbackify = function (fun) {
|
|
return getArguments(function (args) {
|
|
var cb = args.pop();
|
|
var promise = fun.apply(this, args);
|
|
if (typeof cb === 'function') {
|
|
promisedCallback(promise, cb);
|
|
}
|
|
return promise;
|
|
});
|
|
};
|
|
|
|
// Promise finally util similar to Q.finally
|
|
var fin = function (promise, finalPromiseFactory) {
|
|
return promise.then(function (res) {
|
|
return finalPromiseFactory().then(function () {
|
|
return res;
|
|
});
|
|
}, function (reason) {
|
|
return finalPromiseFactory().then(function () {
|
|
throw reason;
|
|
});
|
|
});
|
|
};
|
|
|
|
var sequentialize = function (queue, promiseFactory) {
|
|
return function () {
|
|
var args = arguments;
|
|
var that = this;
|
|
return queue.add(function () {
|
|
return promiseFactory.apply(that, args);
|
|
});
|
|
};
|
|
};
|
|
|
|
// uniq an array of strings, order not guaranteed
|
|
// similar to underscore/lodash _.uniq
|
|
var uniq = function (arr) {
|
|
var map = {};
|
|
|
|
for (var i = 0, len = arr.length; i < len; i++) {
|
|
map['$' + arr[i]] = true;
|
|
}
|
|
|
|
var keys = Object.keys(map);
|
|
var output = new Array(keys.length);
|
|
|
|
for (i = 0, len = keys.length; i < len; i++) {
|
|
output[i] = keys[i].substring(1);
|
|
}
|
|
return output;
|
|
};
|
|
|
|
var persistentQueues = {};
|
|
var tempViewQueue = new TaskQueue$1();
|
|
var CHANGES_BATCH_SIZE$1 = 50;
|
|
|
|
var log$2 = guardedConsole.bind(null, 'log');
|
|
|
|
function parseViewName(name) {
|
|
// can be either 'ddocname/viewname' or just 'viewname'
|
|
// (where the ddoc name is the same)
|
|
return name.indexOf('/') === -1 ? [name, name] : name.split('/');
|
|
}
|
|
|
|
function isGenOne(changes) {
|
|
// only return true if the current change is 1-
|
|
// and there are no other leafs
|
|
return changes.length === 1 && /^1-/.test(changes[0].rev);
|
|
}
|
|
|
|
function emitError(db, e) {
|
|
try {
|
|
db.emit('error', e);
|
|
} catch (err) {
|
|
guardedConsole('error',
|
|
'The user\'s map/reduce function threw an uncaught error.\n' +
|
|
'You can debug this error by doing:\n' +
|
|
'myDatabase.on(\'error\', function (err) { debugger; });\n' +
|
|
'Please double-check your map/reduce function.');
|
|
guardedConsole('error', e);
|
|
}
|
|
}
|
|
|
|
function tryCode$1(db, fun, args) {
|
|
// emit an event if there was an error thrown by a map/reduce function.
|
|
// putting try/catches in a single function also avoids deoptimizations.
|
|
try {
|
|
return {
|
|
output : fun.apply(null, args)
|
|
};
|
|
} catch (e) {
|
|
emitError(db, e);
|
|
return {error: e};
|
|
}
|
|
}
|
|
|
|
function sortByKeyThenValue(x, y) {
|
|
var keyCompare = pouchdbCollate.collate(x.key, y.key);
|
|
return keyCompare !== 0 ? keyCompare : pouchdbCollate.collate(x.value, y.value);
|
|
}
|
|
|
|
function sliceResults(results, limit, skip) {
|
|
skip = skip || 0;
|
|
if (typeof limit === 'number') {
|
|
return results.slice(skip, limit + skip);
|
|
} else if (skip > 0) {
|
|
return results.slice(skip);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
function rowToDocId(row) {
|
|
var val = row.value;
|
|
// Users can explicitly specify a joined doc _id, or it
|
|
// defaults to the doc _id that emitted the key/value.
|
|
var docId = (val && typeof val === 'object' && val._id) || row.id;
|
|
return docId;
|
|
}
|
|
|
|
function readAttachmentsAsBlobOrBuffer$1(res) {
|
|
res.rows.forEach(function (row) {
|
|
var atts = row.doc && row.doc._attachments;
|
|
if (!atts) {
|
|
return;
|
|
}
|
|
Object.keys(atts).forEach(function (filename) {
|
|
var att = atts[filename];
|
|
atts[filename].data = b64ToBluffer(att.data, att.content_type);
|
|
});
|
|
});
|
|
}
|
|
|
|
function postprocessAttachments(opts) {
|
|
return function (res) {
|
|
if (opts.include_docs && opts.attachments && opts.binary) {
|
|
readAttachmentsAsBlobOrBuffer$1(res);
|
|
}
|
|
return res;
|
|
};
|
|
}
|
|
|
|
function createBuiltInError(name) {
|
|
var message = 'builtin ' + name +
|
|
' function requires map values to be numbers' +
|
|
' or number arrays';
|
|
return new BuiltInError(message);
|
|
}
|
|
|
|
function sum(values) {
|
|
var result = 0;
|
|
for (var i = 0, len = values.length; i < len; i++) {
|
|
var num = values[i];
|
|
if (typeof num !== 'number') {
|
|
if (Array.isArray(num)) {
|
|
// lists of numbers are also allowed, sum them separately
|
|
result = typeof result === 'number' ? [result] : result;
|
|
for (var j = 0, jLen = num.length; j < jLen; j++) {
|
|
var jNum = num[j];
|
|
if (typeof jNum !== 'number') {
|
|
throw createBuiltInError('_sum');
|
|
} else if (typeof result[j] === 'undefined') {
|
|
result.push(jNum);
|
|
} else {
|
|
result[j] += jNum;
|
|
}
|
|
}
|
|
} else { // not array/number
|
|
throw createBuiltInError('_sum');
|
|
}
|
|
} else if (typeof result === 'number') {
|
|
result += num;
|
|
} else { // add number to array
|
|
result[0] += num;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
var builtInReduce = {
|
|
_sum: function (keys, values) {
|
|
return sum(values);
|
|
},
|
|
|
|
_count: function (keys, values) {
|
|
return values.length;
|
|
},
|
|
|
|
_stats: function (keys, values) {
|
|
// no need to implement rereduce=true, because Pouch
|
|
// will never call it
|
|
function sumsqr(values) {
|
|
var _sumsqr = 0;
|
|
for (var i = 0, len = values.length; i < len; i++) {
|
|
var num = values[i];
|
|
_sumsqr += (num * num);
|
|
}
|
|
return _sumsqr;
|
|
}
|
|
return {
|
|
sum : sum(values),
|
|
min : Math.min.apply(null, values),
|
|
max : Math.max.apply(null, values),
|
|
count : values.length,
|
|
sumsqr : sumsqr(values)
|
|
};
|
|
}
|
|
};
|
|
|
|
function addHttpParam(paramName, opts, params, asJson) {
|
|
// add an http param from opts to params, optionally json-encoded
|
|
var val = opts[paramName];
|
|
if (typeof val !== 'undefined') {
|
|
if (asJson) {
|
|
val = encodeURIComponent(JSON.stringify(val));
|
|
}
|
|
params.push(paramName + '=' + val);
|
|
}
|
|
}
|
|
|
|
function coerceInteger(integerCandidate) {
|
|
if (typeof integerCandidate !== 'undefined') {
|
|
var asNumber = Number(integerCandidate);
|
|
// prevents e.g. '1foo' or '1.1' being coerced to 1
|
|
if (!isNaN(asNumber) && asNumber === parseInt(integerCandidate, 10)) {
|
|
return asNumber;
|
|
} else {
|
|
return integerCandidate;
|
|
}
|
|
}
|
|
}
|
|
|
|
function coerceOptions(opts) {
|
|
opts.group_level = coerceInteger(opts.group_level);
|
|
opts.limit = coerceInteger(opts.limit);
|
|
opts.skip = coerceInteger(opts.skip);
|
|
return opts;
|
|
}
|
|
|
|
function checkPositiveInteger(number) {
|
|
if (number) {
|
|
if (typeof number !== 'number') {
|
|
return new QueryParseError('Invalid value for integer: "' +
|
|
number + '"');
|
|
}
|
|
if (number < 0) {
|
|
return new QueryParseError('Invalid value for positive integer: ' +
|
|
'"' + number + '"');
|
|
}
|
|
}
|
|
}
|
|
|
|
function checkQueryParseError(options, fun) {
|
|
var startkeyName = options.descending ? 'endkey' : 'startkey';
|
|
var endkeyName = options.descending ? 'startkey' : 'endkey';
|
|
|
|
if (typeof options[startkeyName] !== 'undefined' &&
|
|
typeof options[endkeyName] !== 'undefined' &&
|
|
pouchdbCollate.collate(options[startkeyName], options[endkeyName]) > 0) {
|
|
throw new QueryParseError('No rows can match your key range, ' +
|
|
'reverse your start_key and end_key or set {descending : true}');
|
|
} else if (fun.reduce && options.reduce !== false) {
|
|
if (options.include_docs) {
|
|
throw new QueryParseError('{include_docs:true} is invalid for reduce');
|
|
} else if (options.keys && options.keys.length > 1 &&
|
|
!options.group && !options.group_level) {
|
|
throw new QueryParseError('Multi-key fetches for reduce views must use ' +
|
|
'{group: true}');
|
|
}
|
|
}
|
|
['group_level', 'limit', 'skip'].forEach(function (optionName) {
|
|
var error = checkPositiveInteger(options[optionName]);
|
|
if (error) {
|
|
throw error;
|
|
}
|
|
});
|
|
}
|
|
|
|
function httpQuery(db, fun, opts) {
|
|
// List of parameters to add to the PUT request
|
|
var params = [];
|
|
var body;
|
|
var method = 'GET';
|
|
|
|
// If opts.reduce exists and is defined, then add it to the list
|
|
// of parameters.
|
|
// If reduce=false then the results are that of only the map function
|
|
// not the final result of map and reduce.
|
|
addHttpParam('reduce', opts, params);
|
|
addHttpParam('include_docs', opts, params);
|
|
addHttpParam('attachments', opts, params);
|
|
addHttpParam('limit', opts, params);
|
|
addHttpParam('descending', opts, params);
|
|
addHttpParam('group', opts, params);
|
|
addHttpParam('group_level', opts, params);
|
|
addHttpParam('skip', opts, params);
|
|
addHttpParam('stale', opts, params);
|
|
addHttpParam('conflicts', opts, params);
|
|
addHttpParam('startkey', opts, params, true);
|
|
addHttpParam('start_key', opts, params, true);
|
|
addHttpParam('endkey', opts, params, true);
|
|
addHttpParam('end_key', opts, params, true);
|
|
addHttpParam('inclusive_end', opts, params);
|
|
addHttpParam('key', opts, params, true);
|
|
|
|
// Format the list of parameters into a valid URI query string
|
|
params = params.join('&');
|
|
params = params === '' ? '' : '?' + params;
|
|
|
|
// If keys are supplied, issue a POST to circumvent GET query string limits
|
|
// see http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options
|
|
if (typeof opts.keys !== 'undefined') {
|
|
var MAX_URL_LENGTH = 2000;
|
|
// according to http://stackoverflow.com/a/417184/680742,
|
|
// the de facto URL length limit is 2000 characters
|
|
|
|
var keysAsString =
|
|
'keys=' + encodeURIComponent(JSON.stringify(opts.keys));
|
|
if (keysAsString.length + params.length + 1 <= MAX_URL_LENGTH) {
|
|
// If the keys are short enough, do a GET. we do this to work around
|
|
// Safari not understanding 304s on POSTs (see pouchdb/pouchdb#1239)
|
|
params += (params[0] === '?' ? '&' : '?') + keysAsString;
|
|
} else {
|
|
method = 'POST';
|
|
if (typeof fun === 'string') {
|
|
body = {keys: opts.keys};
|
|
} else { // fun is {map : mapfun}, so append to this
|
|
fun.keys = opts.keys;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We are referencing a query defined in the design doc
|
|
if (typeof fun === 'string') {
|
|
var parts = parseViewName(fun);
|
|
return db.request({
|
|
method: method,
|
|
url: '_design/' + parts[0] + '/_view/' + parts[1] + params,
|
|
body: body
|
|
}).then(postprocessAttachments(opts));
|
|
}
|
|
|
|
// We are using a temporary view, terrible for performance, good for testing
|
|
body = body || {};
|
|
Object.keys(fun).forEach(function (key) {
|
|
if (Array.isArray(fun[key])) {
|
|
body[key] = fun[key];
|
|
} else {
|
|
body[key] = fun[key].toString();
|
|
}
|
|
});
|
|
return db.request({
|
|
method: 'POST',
|
|
url: '_temp_view' + params,
|
|
body: body
|
|
}).then(postprocessAttachments(opts));
|
|
}
|
|
|
|
// custom adapters can define their own api._query
|
|
// and override the default behavior
|
|
/* istanbul ignore next */
|
|
function customQuery(db, fun, opts) {
|
|
return new PouchPromise(function (resolve, reject) {
|
|
db._query(fun, opts, function (err, res) {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
resolve(res);
|
|
});
|
|
});
|
|
}
|
|
|
|
// custom adapters can define their own api._viewCleanup
|
|
// and override the default behavior
|
|
/* istanbul ignore next */
|
|
function customViewCleanup(db) {
|
|
return new PouchPromise(function (resolve, reject) {
|
|
db._viewCleanup(function (err, res) {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
resolve(res);
|
|
});
|
|
});
|
|
}
|
|
|
|
function defaultsTo(value) {
|
|
return function (reason) {
|
|
/* istanbul ignore else */
|
|
if (reason.status === 404) {
|
|
return value;
|
|
} else {
|
|
throw reason;
|
|
}
|
|
};
|
|
}
|
|
|
|
// returns a promise for a list of docs to update, based on the input docId.
|
|
// the order doesn't matter, because post-3.2.0, bulkDocs
|
|
// is an atomic operation in all three adapters.
|
|
function getDocsToPersist(docId, view, docIdsToChangesAndEmits) {
|
|
var metaDocId = '_local/doc_' + docId;
|
|
var defaultMetaDoc = {_id: metaDocId, keys: []};
|
|
var docData = docIdsToChangesAndEmits[docId];
|
|
var indexableKeysToKeyValues = docData.indexableKeysToKeyValues;
|
|
var changes = docData.changes;
|
|
|
|
function getMetaDoc() {
|
|
if (isGenOne(changes)) {
|
|
// generation 1, so we can safely assume initial state
|
|
// for performance reasons (avoids unnecessary GETs)
|
|
return PouchPromise.resolve(defaultMetaDoc);
|
|
}
|
|
return view.db.get(metaDocId).catch(defaultsTo(defaultMetaDoc));
|
|
}
|
|
|
|
function getKeyValueDocs(metaDoc) {
|
|
if (!metaDoc.keys.length) {
|
|
// no keys, no need for a lookup
|
|
return PouchPromise.resolve({rows: []});
|
|
}
|
|
return view.db.allDocs({
|
|
keys: metaDoc.keys,
|
|
include_docs: true
|
|
});
|
|
}
|
|
|
|
function processKvDocs(metaDoc, kvDocsRes) {
|
|
var kvDocs = [];
|
|
var oldKeysMap = {};
|
|
|
|
for (var i = 0, len = kvDocsRes.rows.length; i < len; i++) {
|
|
var row = kvDocsRes.rows[i];
|
|
var doc = row.doc;
|
|
if (!doc) { // deleted
|
|
continue;
|
|
}
|
|
kvDocs.push(doc);
|
|
oldKeysMap[doc._id] = true;
|
|
doc._deleted = !indexableKeysToKeyValues[doc._id];
|
|
if (!doc._deleted) {
|
|
var keyValue = indexableKeysToKeyValues[doc._id];
|
|
if ('value' in keyValue) {
|
|
doc.value = keyValue.value;
|
|
}
|
|
}
|
|
}
|
|
|
|
var newKeys = Object.keys(indexableKeysToKeyValues);
|
|
newKeys.forEach(function (key) {
|
|
if (!oldKeysMap[key]) {
|
|
// new doc
|
|
var kvDoc = {
|
|
_id: key
|
|
};
|
|
var keyValue = indexableKeysToKeyValues[key];
|
|
if ('value' in keyValue) {
|
|
kvDoc.value = keyValue.value;
|
|
}
|
|
kvDocs.push(kvDoc);
|
|
}
|
|
});
|
|
metaDoc.keys = uniq(newKeys.concat(metaDoc.keys));
|
|
kvDocs.push(metaDoc);
|
|
|
|
return kvDocs;
|
|
}
|
|
|
|
return getMetaDoc().then(function (metaDoc) {
|
|
return getKeyValueDocs(metaDoc).then(function (kvDocsRes) {
|
|
return processKvDocs(metaDoc, kvDocsRes);
|
|
});
|
|
});
|
|
}
|
|
|
|
// updates all emitted key/value docs and metaDocs in the mrview database
|
|
// for the given batch of documents from the source database
|
|
function saveKeyValues(view, docIdsToChangesAndEmits, seq) {
|
|
var seqDocId = '_local/lastSeq';
|
|
return view.db.get(seqDocId)
|
|
.catch(defaultsTo({_id: seqDocId, seq: 0}))
|
|
.then(function (lastSeqDoc) {
|
|
var docIds = Object.keys(docIdsToChangesAndEmits);
|
|
return PouchPromise.all(docIds.map(function (docId) {
|
|
return getDocsToPersist(docId, view, docIdsToChangesAndEmits);
|
|
})).then(function (listOfDocsToPersist) {
|
|
var docsToPersist = flatten(listOfDocsToPersist);
|
|
lastSeqDoc.seq = seq;
|
|
docsToPersist.push(lastSeqDoc);
|
|
// write all docs in a single operation, update the seq once
|
|
return view.db.bulkDocs({docs : docsToPersist});
|
|
});
|
|
});
|
|
}
|
|
|
|
function getQueue(view) {
|
|
var viewName = typeof view === 'string' ? view : view.name;
|
|
var queue = persistentQueues[viewName];
|
|
if (!queue) {
|
|
queue = persistentQueues[viewName] = new TaskQueue$1();
|
|
}
|
|
return queue;
|
|
}
|
|
|
|
function updateView(view) {
|
|
return sequentialize(getQueue(view), function () {
|
|
return updateViewInQueue(view);
|
|
})();
|
|
}
|
|
|
|
function updateViewInQueue(view) {
|
|
// bind the emit function once
|
|
var mapResults;
|
|
var doc;
|
|
|
|
function emit(key, value) {
|
|
var output = {id: doc._id, key: pouchdbCollate.normalizeKey(key)};
|
|
// Don't explicitly store the value unless it's defined and non-null.
|
|
// This saves on storage space, because often people don't use it.
|
|
if (typeof value !== 'undefined' && value !== null) {
|
|
output.value = pouchdbCollate.normalizeKey(value);
|
|
}
|
|
mapResults.push(output);
|
|
}
|
|
|
|
var mapFun;
|
|
// for temp_views one can use emit(doc, emit), see #38
|
|
if (typeof view.mapFun === "function" && view.mapFun.length === 2) {
|
|
var origMap = view.mapFun;
|
|
mapFun = function (doc) {
|
|
return origMap(doc, emit);
|
|
};
|
|
} else {
|
|
mapFun = evalfunc(view.mapFun.toString(), emit, sum, log$2, Array.isArray,
|
|
JSON.parse);
|
|
}
|
|
|
|
var currentSeq = view.seq || 0;
|
|
|
|
function processChange(docIdsToChangesAndEmits, seq) {
|
|
return function () {
|
|
return saveKeyValues(view, docIdsToChangesAndEmits, seq);
|
|
};
|
|
}
|
|
|
|
var queue = new TaskQueue$1();
|
|
// TODO(neojski): https://github.com/daleharvey/pouchdb/issues/1521
|
|
|
|
return new PouchPromise(function (resolve, reject) {
|
|
|
|
function complete() {
|
|
queue.finish().then(function () {
|
|
view.seq = currentSeq;
|
|
resolve();
|
|
});
|
|
}
|
|
|
|
function processNextBatch() {
|
|
view.sourceDB.changes({
|
|
conflicts: true,
|
|
include_docs: true,
|
|
style: 'all_docs',
|
|
since: currentSeq,
|
|
limit: CHANGES_BATCH_SIZE$1
|
|
}).on('complete', function (response) {
|
|
var results = response.results;
|
|
if (!results.length) {
|
|
return complete();
|
|
}
|
|
var docIdsToChangesAndEmits = {};
|
|
for (var i = 0, l = results.length; i < l; i++) {
|
|
var change = results[i];
|
|
if (change.doc._id[0] !== '_') {
|
|
mapResults = [];
|
|
doc = change.doc;
|
|
|
|
if (!doc._deleted) {
|
|
tryCode$1(view.sourceDB, mapFun, [doc]);
|
|
}
|
|
mapResults.sort(sortByKeyThenValue);
|
|
|
|
var indexableKeysToKeyValues = {};
|
|
var lastKey;
|
|
for (var j = 0, jl = mapResults.length; j < jl; j++) {
|
|
var obj = mapResults[j];
|
|
var complexKey = [obj.key, obj.id];
|
|
if (pouchdbCollate.collate(obj.key, lastKey) === 0) {
|
|
complexKey.push(j); // dup key+id, so make it unique
|
|
}
|
|
var indexableKey = pouchdbCollate.toIndexableString(complexKey);
|
|
indexableKeysToKeyValues[indexableKey] = obj;
|
|
lastKey = obj.key;
|
|
}
|
|
docIdsToChangesAndEmits[change.doc._id] = {
|
|
indexableKeysToKeyValues: indexableKeysToKeyValues,
|
|
changes: change.changes
|
|
};
|
|
}
|
|
currentSeq = change.seq;
|
|
}
|
|
queue.add(processChange(docIdsToChangesAndEmits, currentSeq));
|
|
if (results.length < CHANGES_BATCH_SIZE$1) {
|
|
return complete();
|
|
}
|
|
return processNextBatch();
|
|
}).on('error', onError);
|
|
/* istanbul ignore next */
|
|
function onError(err) {
|
|
reject(err);
|
|
}
|
|
}
|
|
|
|
processNextBatch();
|
|
});
|
|
}
|
|
|
|
function reduceView(view, results, options) {
|
|
if (options.group_level === 0) {
|
|
delete options.group_level;
|
|
}
|
|
|
|
var shouldGroup = options.group || options.group_level;
|
|
|
|
var reduceFun;
|
|
if (builtInReduce[view.reduceFun]) {
|
|
reduceFun = builtInReduce[view.reduceFun];
|
|
} else {
|
|
reduceFun = evalfunc(
|
|
view.reduceFun.toString(), null, sum, log$2, Array.isArray, JSON.parse);
|
|
}
|
|
|
|
var groups = [];
|
|
var lvl = isNaN(options.group_level) ? Number.POSITIVE_INFINITY :
|
|
options.group_level;
|
|
results.forEach(function (e) {
|
|
var last = groups[groups.length - 1];
|
|
var groupKey = shouldGroup ? e.key : null;
|
|
|
|
// only set group_level for array keys
|
|
if (shouldGroup && Array.isArray(groupKey)) {
|
|
groupKey = groupKey.slice(0, lvl);
|
|
}
|
|
|
|
if (last && pouchdbCollate.collate(last.groupKey, groupKey) === 0) {
|
|
last.keys.push([e.key, e.id]);
|
|
last.values.push(e.value);
|
|
return;
|
|
}
|
|
groups.push({
|
|
keys: [[e.key, e.id]],
|
|
values: [e.value],
|
|
groupKey: groupKey
|
|
});
|
|
});
|
|
results = [];
|
|
for (var i = 0, len = groups.length; i < len; i++) {
|
|
var e = groups[i];
|
|
var reduceTry = tryCode$1(view.sourceDB, reduceFun,
|
|
[e.keys, e.values, false]);
|
|
if (reduceTry.error && reduceTry.error instanceof BuiltInError) {
|
|
// CouchDB returns an error if a built-in errors out
|
|
throw reduceTry.error;
|
|
}
|
|
results.push({
|
|
// CouchDB just sets the value to null if a non-built-in errors out
|
|
value: reduceTry.error ? null : reduceTry.output,
|
|
key: e.groupKey
|
|
});
|
|
}
|
|
// no total_rows/offset when reducing
|
|
return {rows: sliceResults(results, options.limit, options.skip)};
|
|
}
|
|
|
|
function queryView(view, opts) {
|
|
return sequentialize(getQueue(view), function () {
|
|
return queryViewInQueue(view, opts);
|
|
})();
|
|
}
|
|
|
|
function queryViewInQueue(view, opts) {
|
|
var totalRows;
|
|
var shouldReduce = view.reduceFun && opts.reduce !== false;
|
|
var skip = opts.skip || 0;
|
|
if (typeof opts.keys !== 'undefined' && !opts.keys.length) {
|
|
// equivalent query
|
|
opts.limit = 0;
|
|
delete opts.keys;
|
|
}
|
|
|
|
function fetchFromView(viewOpts) {
|
|
viewOpts.include_docs = true;
|
|
return view.db.allDocs(viewOpts).then(function (res) {
|
|
totalRows = res.total_rows;
|
|
return res.rows.map(function (result) {
|
|
|
|
// implicit migration - in older versions of PouchDB,
|
|
// we explicitly stored the doc as {id: ..., key: ..., value: ...}
|
|
// this is tested in a migration test
|
|
/* istanbul ignore next */
|
|
if ('value' in result.doc && typeof result.doc.value === 'object' &&
|
|
result.doc.value !== null) {
|
|
var keys = Object.keys(result.doc.value).sort();
|
|
// this detection method is not perfect, but it's unlikely the user
|
|
// emitted a value which was an object with these 3 exact keys
|
|
var expectedKeys = ['id', 'key', 'value'];
|
|
if (!(keys < expectedKeys || keys > expectedKeys)) {
|
|
return result.doc.value;
|
|
}
|
|
}
|
|
|
|
var parsedKeyAndDocId = pouchdbCollate.parseIndexableString(result.doc._id);
|
|
return {
|
|
key: parsedKeyAndDocId[0],
|
|
id: parsedKeyAndDocId[1],
|
|
value: ('value' in result.doc ? result.doc.value : null)
|
|
};
|
|
});
|
|
});
|
|
}
|
|
|
|
function onMapResultsReady(rows) {
|
|
var finalResults;
|
|
if (shouldReduce) {
|
|
finalResults = reduceView(view, rows, opts);
|
|
} else {
|
|
finalResults = {
|
|
total_rows: totalRows,
|
|
offset: skip,
|
|
rows: rows
|
|
};
|
|
}
|
|
if (opts.include_docs) {
|
|
var docIds = uniq(rows.map(rowToDocId));
|
|
|
|
return view.sourceDB.allDocs({
|
|
keys: docIds,
|
|
include_docs: true,
|
|
conflicts: opts.conflicts,
|
|
attachments: opts.attachments,
|
|
binary: opts.binary
|
|
}).then(function (allDocsRes) {
|
|
var docIdsToDocs = {};
|
|
allDocsRes.rows.forEach(function (row) {
|
|
if (row.doc) {
|
|
docIdsToDocs['$' + row.id] = row.doc;
|
|
}
|
|
});
|
|
rows.forEach(function (row) {
|
|
var docId = rowToDocId(row);
|
|
var doc = docIdsToDocs['$' + docId];
|
|
if (doc) {
|
|
row.doc = doc;
|
|
}
|
|
});
|
|
return finalResults;
|
|
});
|
|
} else {
|
|
return finalResults;
|
|
}
|
|
}
|
|
|
|
if (typeof opts.keys !== 'undefined') {
|
|
var keys = opts.keys;
|
|
var fetchPromises = keys.map(function (key) {
|
|
var viewOpts = {
|
|
startkey : pouchdbCollate.toIndexableString([key]),
|
|
endkey : pouchdbCollate.toIndexableString([key, {}])
|
|
};
|
|
return fetchFromView(viewOpts);
|
|
});
|
|
return PouchPromise.all(fetchPromises).then(flatten).then(onMapResultsReady);
|
|
} else { // normal query, no 'keys'
|
|
var viewOpts = {
|
|
descending : opts.descending
|
|
};
|
|
if (opts.start_key) {
|
|
opts.startkey = opts.start_key;
|
|
}
|
|
if (opts.end_key) {
|
|
opts.endkey = opts.end_key;
|
|
}
|
|
if (typeof opts.startkey !== 'undefined') {
|
|
viewOpts.startkey = opts.descending ?
|
|
pouchdbCollate.toIndexableString([opts.startkey, {}]) :
|
|
pouchdbCollate.toIndexableString([opts.startkey]);
|
|
}
|
|
if (typeof opts.endkey !== 'undefined') {
|
|
var inclusiveEnd = opts.inclusive_end !== false;
|
|
if (opts.descending) {
|
|
inclusiveEnd = !inclusiveEnd;
|
|
}
|
|
|
|
viewOpts.endkey = pouchdbCollate.toIndexableString(
|
|
inclusiveEnd ? [opts.endkey, {}] : [opts.endkey]);
|
|
}
|
|
if (typeof opts.key !== 'undefined') {
|
|
var keyStart = pouchdbCollate.toIndexableString([opts.key]);
|
|
var keyEnd = pouchdbCollate.toIndexableString([opts.key, {}]);
|
|
if (viewOpts.descending) {
|
|
viewOpts.endkey = keyStart;
|
|
viewOpts.startkey = keyEnd;
|
|
} else {
|
|
viewOpts.startkey = keyStart;
|
|
viewOpts.endkey = keyEnd;
|
|
}
|
|
}
|
|
if (!shouldReduce) {
|
|
if (typeof opts.limit === 'number') {
|
|
viewOpts.limit = opts.limit;
|
|
}
|
|
viewOpts.skip = skip;
|
|
}
|
|
return fetchFromView(viewOpts).then(onMapResultsReady);
|
|
}
|
|
}
|
|
|
|
function httpViewCleanup(db) {
|
|
return db.request({
|
|
method: 'POST',
|
|
url: '_view_cleanup'
|
|
});
|
|
}
|
|
|
|
function localViewCleanup(db) {
|
|
return db.get('_local/mrviews').then(function (metaDoc) {
|
|
var docsToViews = {};
|
|
Object.keys(metaDoc.views).forEach(function (fullViewName) {
|
|
var parts = parseViewName(fullViewName);
|
|
var designDocName = '_design/' + parts[0];
|
|
var viewName = parts[1];
|
|
docsToViews[designDocName] = docsToViews[designDocName] || {};
|
|
docsToViews[designDocName][viewName] = true;
|
|
});
|
|
var opts = {
|
|
keys : Object.keys(docsToViews),
|
|
include_docs : true
|
|
};
|
|
return db.allDocs(opts).then(function (res) {
|
|
var viewsToStatus = {};
|
|
res.rows.forEach(function (row) {
|
|
var ddocName = row.key.substring(8);
|
|
Object.keys(docsToViews[row.key]).forEach(function (viewName) {
|
|
var fullViewName = ddocName + '/' + viewName;
|
|
/* istanbul ignore if */
|
|
if (!metaDoc.views[fullViewName]) {
|
|
// new format, without slashes, to support PouchDB 2.2.0
|
|
// migration test in pouchdb's browser.migration.js verifies this
|
|
fullViewName = viewName;
|
|
}
|
|
var viewDBNames = Object.keys(metaDoc.views[fullViewName]);
|
|
// design doc deleted, or view function nonexistent
|
|
var statusIsGood = row.doc && row.doc.views &&
|
|
row.doc.views[viewName];
|
|
viewDBNames.forEach(function (viewDBName) {
|
|
viewsToStatus[viewDBName] =
|
|
viewsToStatus[viewDBName] || statusIsGood;
|
|
});
|
|
});
|
|
});
|
|
var dbsToDelete = Object.keys(viewsToStatus).filter(
|
|
function (viewDBName) { return !viewsToStatus[viewDBName]; });
|
|
var destroyPromises = dbsToDelete.map(function (viewDBName) {
|
|
return sequentialize(getQueue(viewDBName), function () {
|
|
return new db.constructor(viewDBName, db.__opts).destroy();
|
|
})();
|
|
});
|
|
return PouchPromise.all(destroyPromises).then(function () {
|
|
return {ok: true};
|
|
});
|
|
});
|
|
}, defaultsTo({ok: true}));
|
|
}
|
|
|
|
var viewCleanup = callbackify(function () {
|
|
var db = this;
|
|
if (db.type() === 'http') {
|
|
return httpViewCleanup(db);
|
|
}
|
|
/* istanbul ignore next */
|
|
if (typeof db._viewCleanup === 'function') {
|
|
return customViewCleanup(db);
|
|
}
|
|
return localViewCleanup(db);
|
|
});
|
|
|
|
function queryPromised(db, fun, opts) {
|
|
if (db.type() === 'http') {
|
|
return httpQuery(db, fun, opts);
|
|
}
|
|
|
|
/* istanbul ignore next */
|
|
if (typeof db._query === 'function') {
|
|
return customQuery(db, fun, opts);
|
|
}
|
|
|
|
if (typeof fun !== 'string') {
|
|
// temp_view
|
|
checkQueryParseError(opts, fun);
|
|
|
|
var createViewOpts = {
|
|
db : db,
|
|
viewName : 'temp_view/temp_view',
|
|
map : fun.map,
|
|
reduce : fun.reduce,
|
|
temporary : true
|
|
};
|
|
tempViewQueue.add(function () {
|
|
return createView(createViewOpts).then(function (view) {
|
|
function cleanup() {
|
|
return view.db.destroy();
|
|
}
|
|
return fin(updateView(view).then(function () {
|
|
return queryView(view, opts);
|
|
}), cleanup);
|
|
});
|
|
});
|
|
return tempViewQueue.finish();
|
|
} else {
|
|
// persistent view
|
|
var fullViewName = fun;
|
|
var parts = parseViewName(fullViewName);
|
|
var designDocName = parts[0];
|
|
var viewName = parts[1];
|
|
return db.get('_design/' + designDocName).then(function (doc) {
|
|
var fun = doc.views && doc.views[viewName];
|
|
|
|
if (!fun || typeof fun.map !== 'string') {
|
|
throw new NotFoundError('ddoc ' + designDocName +
|
|
' has no view named ' + viewName);
|
|
}
|
|
checkQueryParseError(opts, fun);
|
|
|
|
var createViewOpts = {
|
|
db : db,
|
|
viewName : fullViewName,
|
|
map : fun.map,
|
|
reduce : fun.reduce
|
|
};
|
|
return createView(createViewOpts).then(function (view) {
|
|
if (opts.stale === 'ok' || opts.stale === 'update_after') {
|
|
if (opts.stale === 'update_after') {
|
|
process.nextTick(function () {
|
|
updateView(view);
|
|
});
|
|
}
|
|
return queryView(view, opts);
|
|
} else { // stale not ok
|
|
return updateView(view).then(function () {
|
|
return queryView(view, opts);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
var query = function (fun, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
opts = opts ? coerceOptions(opts) : {};
|
|
|
|
if (typeof fun === 'function') {
|
|
fun = {map : fun};
|
|
}
|
|
|
|
var db = this;
|
|
var promise = PouchPromise.resolve().then(function () {
|
|
return queryPromised(db, fun, opts);
|
|
});
|
|
promisedCallback(promise, callback);
|
|
return promise;
|
|
};
|
|
|
|
function QueryParseError(message) {
|
|
this.status = 400;
|
|
this.name = 'query_parse_error';
|
|
this.message = message;
|
|
this.error = true;
|
|
try {
|
|
Error.captureStackTrace(this, QueryParseError);
|
|
} catch (e) {}
|
|
}
|
|
|
|
inherits(QueryParseError, Error);
|
|
|
|
function NotFoundError(message) {
|
|
this.status = 404;
|
|
this.name = 'not_found';
|
|
this.message = message;
|
|
this.error = true;
|
|
try {
|
|
Error.captureStackTrace(this, NotFoundError);
|
|
} catch (e) {}
|
|
}
|
|
|
|
inherits(NotFoundError, Error);
|
|
|
|
function BuiltInError(message) {
|
|
this.status = 500;
|
|
this.name = 'invalid_value';
|
|
this.message = message;
|
|
this.error = true;
|
|
try {
|
|
Error.captureStackTrace(this, BuiltInError);
|
|
} catch (e) {}
|
|
}
|
|
|
|
inherits(BuiltInError, Error);
|
|
|
|
var mapreduce = {
|
|
query: query,
|
|
viewCleanup: viewCleanup
|
|
};
|
|
|
|
function isGenOne$1(rev) {
|
|
return /^1-/.test(rev);
|
|
}
|
|
|
|
function fileHasChanged(localDoc, remoteDoc, filename) {
|
|
return !localDoc._attachments ||
|
|
!localDoc._attachments[filename] ||
|
|
localDoc._attachments[filename].digest !== remoteDoc._attachments[filename].digest;
|
|
}
|
|
|
|
function getDocAttachments(db, doc) {
|
|
var filenames = Object.keys(doc._attachments);
|
|
return PouchPromise.all(filenames.map(function (filename) {
|
|
return db.getAttachment(doc._id, filename, {rev: doc._rev});
|
|
}));
|
|
}
|
|
|
|
function getDocAttachmentsFromTargetOrSource(target, src, doc) {
|
|
var doCheckForLocalAttachments = src.type() === 'http' && target.type() !== 'http';
|
|
var filenames = Object.keys(doc._attachments);
|
|
|
|
if (!doCheckForLocalAttachments) {
|
|
return getDocAttachments(src, doc);
|
|
}
|
|
|
|
return target.get(doc._id).then(function (localDoc) {
|
|
return PouchPromise.all(filenames.map(function (filename) {
|
|
if (fileHasChanged(localDoc, doc, filename)) {
|
|
return src.getAttachment(doc._id, filename);
|
|
}
|
|
|
|
return target.getAttachment(localDoc._id, filename);
|
|
}));
|
|
}).catch(function (error) {
|
|
/* istanbul ignore if */
|
|
if (error.status !== 404) {
|
|
throw error;
|
|
}
|
|
|
|
return getDocAttachments(src, doc);
|
|
});
|
|
}
|
|
|
|
function createBulkGetOpts(diffs) {
|
|
var requests = [];
|
|
Object.keys(diffs).forEach(function (id) {
|
|
var missingRevs = diffs[id].missing;
|
|
missingRevs.forEach(function (missingRev) {
|
|
requests.push({
|
|
id: id,
|
|
rev: missingRev
|
|
});
|
|
});
|
|
});
|
|
|
|
return {
|
|
docs: requests,
|
|
revs: true
|
|
};
|
|
}
|
|
|
|
//
|
|
// Fetch all the documents from the src as described in the "diffs",
|
|
// which is a mapping of docs IDs to revisions. If the state ever
|
|
// changes to "cancelled", then the returned promise will be rejected.
|
|
// Else it will be resolved with a list of fetched documents.
|
|
//
|
|
function getDocs(src, target, diffs, state) {
|
|
diffs = clone(diffs); // we do not need to modify this
|
|
|
|
var resultDocs = [],
|
|
ok = true;
|
|
|
|
function getAllDocs() {
|
|
|
|
var bulkGetOpts = createBulkGetOpts(diffs);
|
|
|
|
if (!bulkGetOpts.docs.length) { // optimization: skip empty requests
|
|
return;
|
|
}
|
|
|
|
return src.bulkGet(bulkGetOpts).then(function (bulkGetResponse) {
|
|
/* istanbul ignore if */
|
|
if (state.cancelled) {
|
|
throw new Error('cancelled');
|
|
}
|
|
return PouchPromise.all(bulkGetResponse.results.map(function (bulkGetInfo) {
|
|
return PouchPromise.all(bulkGetInfo.docs.map(function (doc) {
|
|
var remoteDoc = doc.ok;
|
|
|
|
if (doc.error) {
|
|
// when AUTO_COMPACTION is set, docs can be returned which look
|
|
// like this: {"missing":"1-7c3ac256b693c462af8442f992b83696"}
|
|
ok = false;
|
|
}
|
|
|
|
if (!remoteDoc || !remoteDoc._attachments) {
|
|
return remoteDoc;
|
|
}
|
|
|
|
return getDocAttachmentsFromTargetOrSource(target, src, remoteDoc).then(function (attachments) {
|
|
var filenames = Object.keys(remoteDoc._attachments);
|
|
attachments.forEach(function (attachment, i) {
|
|
var att = remoteDoc._attachments[filenames[i]];
|
|
delete att.stub;
|
|
delete att.length;
|
|
att.data = attachment;
|
|
});
|
|
|
|
return remoteDoc;
|
|
});
|
|
}));
|
|
}))
|
|
|
|
.then(function (results) {
|
|
resultDocs = resultDocs.concat(flatten(results).filter(Boolean));
|
|
});
|
|
});
|
|
}
|
|
|
|
function hasAttachments(doc) {
|
|
return doc._attachments && Object.keys(doc._attachments).length > 0;
|
|
}
|
|
|
|
function fetchRevisionOneDocs(ids) {
|
|
// Optimization: fetch gen-1 docs and attachments in
|
|
// a single request using _all_docs
|
|
return src.allDocs({
|
|
keys: ids,
|
|
include_docs: true
|
|
}).then(function (res) {
|
|
if (state.cancelled) {
|
|
throw new Error('cancelled');
|
|
}
|
|
res.rows.forEach(function (row) {
|
|
if (row.deleted || !row.doc || !isGenOne$1(row.value.rev) ||
|
|
hasAttachments(row.doc)) {
|
|
// if any of these conditions apply, we need to fetch using get()
|
|
return;
|
|
}
|
|
|
|
// the doc we got back from allDocs() is sufficient
|
|
resultDocs.push(row.doc);
|
|
delete diffs[row.id];
|
|
});
|
|
});
|
|
}
|
|
|
|
function getRevisionOneDocs() {
|
|
// filter out the generation 1 docs and get them
|
|
// leaving the non-generation one docs to be got otherwise
|
|
var ids = Object.keys(diffs).filter(function (id) {
|
|
var missing = diffs[id].missing;
|
|
return missing.length === 1 && isGenOne$1(missing[0]);
|
|
});
|
|
if (ids.length > 0) {
|
|
return fetchRevisionOneDocs(ids);
|
|
}
|
|
}
|
|
|
|
function returnResult() {
|
|
return { ok:ok, docs:resultDocs };
|
|
}
|
|
|
|
return PouchPromise.resolve()
|
|
.then(getRevisionOneDocs)
|
|
.then(getAllDocs)
|
|
.then(returnResult);
|
|
}
|
|
|
|
var CHECKPOINT_VERSION = 1;
|
|
var REPLICATOR = "pouchdb";
|
|
// This is an arbitrary number to limit the
|
|
// amount of replication history we save in the checkpoint.
|
|
// If we save too much, the checkpoing docs will become very big,
|
|
// if we save fewer, we'll run a greater risk of having to
|
|
// read all the changes from 0 when checkpoint PUTs fail
|
|
// CouchDB 2.0 has a more involved history pruning,
|
|
// but let's go for the simple version for now.
|
|
var CHECKPOINT_HISTORY_SIZE = 5;
|
|
var LOWEST_SEQ = 0;
|
|
|
|
function updateCheckpoint(db, id, checkpoint, session, returnValue) {
|
|
return db.get(id).catch(function (err) {
|
|
if (err.status === 404) {
|
|
if (db.type() === 'http') {
|
|
explainError(
|
|
404, 'PouchDB is just checking if a remote checkpoint exists.'
|
|
);
|
|
}
|
|
return {
|
|
session_id: session,
|
|
_id: id,
|
|
history: [],
|
|
replicator: REPLICATOR,
|
|
version: CHECKPOINT_VERSION
|
|
};
|
|
}
|
|
throw err;
|
|
}).then(function (doc) {
|
|
if (returnValue.cancelled) {
|
|
return;
|
|
}
|
|
// Filter out current entry for this replication
|
|
doc.history = (doc.history || []).filter(function (item) {
|
|
return item.session_id !== session;
|
|
});
|
|
|
|
// Add the latest checkpoint to history
|
|
doc.history.unshift({
|
|
last_seq: checkpoint,
|
|
session_id: session
|
|
});
|
|
|
|
// Just take the last pieces in history, to
|
|
// avoid really big checkpoint docs.
|
|
// see comment on history size above
|
|
doc.history = doc.history.slice(0, CHECKPOINT_HISTORY_SIZE);
|
|
|
|
doc.version = CHECKPOINT_VERSION;
|
|
doc.replicator = REPLICATOR;
|
|
|
|
doc.session_id = session;
|
|
doc.last_seq = checkpoint;
|
|
|
|
return db.put(doc).catch(function (err) {
|
|
if (err.status === 409) {
|
|
// retry; someone is trying to write a checkpoint simultaneously
|
|
return updateCheckpoint(db, id, checkpoint, session, returnValue);
|
|
}
|
|
throw err;
|
|
});
|
|
});
|
|
}
|
|
|
|
function Checkpointer(src, target, id, returnValue) {
|
|
this.src = src;
|
|
this.target = target;
|
|
this.id = id;
|
|
this.returnValue = returnValue;
|
|
}
|
|
|
|
Checkpointer.prototype.writeCheckpoint = function (checkpoint, session) {
|
|
var self = this;
|
|
return this.updateTarget(checkpoint, session).then(function () {
|
|
return self.updateSource(checkpoint, session);
|
|
});
|
|
};
|
|
|
|
Checkpointer.prototype.updateTarget = function (checkpoint, session) {
|
|
return updateCheckpoint(this.target, this.id, checkpoint,
|
|
session, this.returnValue);
|
|
};
|
|
|
|
Checkpointer.prototype.updateSource = function (checkpoint, session) {
|
|
var self = this;
|
|
if (this.readOnlySource) {
|
|
return PouchPromise.resolve(true);
|
|
}
|
|
return updateCheckpoint(this.src, this.id, checkpoint,
|
|
session, this.returnValue)
|
|
.catch(function (err) {
|
|
if (isForbiddenError(err)) {
|
|
self.readOnlySource = true;
|
|
return true;
|
|
}
|
|
throw err;
|
|
});
|
|
};
|
|
|
|
var comparisons = {
|
|
"undefined": function (targetDoc, sourceDoc) {
|
|
// This is the previous comparison function
|
|
if (pouchdbCollate.collate(targetDoc.last_seq, sourceDoc.last_seq) === 0) {
|
|
return sourceDoc.last_seq;
|
|
}
|
|
/* istanbul ignore next */
|
|
return 0;
|
|
},
|
|
"1": function (targetDoc, sourceDoc) {
|
|
// This is the comparison function ported from CouchDB
|
|
return compareReplicationLogs(sourceDoc, targetDoc).last_seq;
|
|
}
|
|
};
|
|
|
|
Checkpointer.prototype.getCheckpoint = function () {
|
|
var self = this;
|
|
return self.target.get(self.id).then(function (targetDoc) {
|
|
if (self.readOnlySource) {
|
|
return PouchPromise.resolve(targetDoc.last_seq);
|
|
}
|
|
|
|
return self.src.get(self.id).then(function (sourceDoc) {
|
|
// Since we can't migrate an old version doc to a new one
|
|
// (no session id), we just go with the lowest seq in this case
|
|
/* istanbul ignore if */
|
|
if (targetDoc.version !== sourceDoc.version) {
|
|
return LOWEST_SEQ;
|
|
}
|
|
|
|
var version;
|
|
if (targetDoc.version) {
|
|
version = targetDoc.version.toString();
|
|
} else {
|
|
version = "undefined";
|
|
}
|
|
|
|
if (version in comparisons) {
|
|
return comparisons[version](targetDoc, sourceDoc);
|
|
}
|
|
/* istanbul ignore next */
|
|
return LOWEST_SEQ;
|
|
}, function (err) {
|
|
if (err.status === 404 && targetDoc.last_seq) {
|
|
return self.src.put({
|
|
_id: self.id,
|
|
last_seq: LOWEST_SEQ
|
|
}).then(function () {
|
|
return LOWEST_SEQ;
|
|
}, function (err) {
|
|
if (isForbiddenError(err)) {
|
|
self.readOnlySource = true;
|
|
return targetDoc.last_seq;
|
|
}
|
|
/* istanbul ignore next */
|
|
return LOWEST_SEQ;
|
|
});
|
|
}
|
|
throw err;
|
|
});
|
|
}).catch(function (err) {
|
|
if (err.status !== 404) {
|
|
throw err;
|
|
}
|
|
return LOWEST_SEQ;
|
|
});
|
|
};
|
|
// This checkpoint comparison is ported from CouchDBs source
|
|
// they come from here:
|
|
// https://github.com/apache/couchdb-couch-replicator/blob/master/src/couch_replicator.erl#L863-L906
|
|
|
|
function compareReplicationLogs(srcDoc, tgtDoc) {
|
|
if (srcDoc.session_id === tgtDoc.session_id) {
|
|
return {
|
|
last_seq: srcDoc.last_seq,
|
|
history: srcDoc.history
|
|
};
|
|
}
|
|
|
|
return compareReplicationHistory(srcDoc.history, tgtDoc.history);
|
|
}
|
|
|
|
function compareReplicationHistory(sourceHistory, targetHistory) {
|
|
// the erlang loop via function arguments is not so easy to repeat in JS
|
|
// therefore, doing this as recursion
|
|
var S = sourceHistory[0];
|
|
var sourceRest = sourceHistory.slice(1);
|
|
var T = targetHistory[0];
|
|
var targetRest = targetHistory.slice(1);
|
|
|
|
if (!S || targetHistory.length === 0) {
|
|
return {
|
|
last_seq: LOWEST_SEQ,
|
|
history: []
|
|
};
|
|
}
|
|
|
|
var sourceId = S.session_id;
|
|
/* istanbul ignore if */
|
|
if (hasSessionId(sourceId, targetHistory)) {
|
|
return {
|
|
last_seq: S.last_seq,
|
|
history: sourceHistory
|
|
};
|
|
}
|
|
|
|
var targetId = T.session_id;
|
|
if (hasSessionId(targetId, sourceRest)) {
|
|
return {
|
|
last_seq: T.last_seq,
|
|
history: targetRest
|
|
};
|
|
}
|
|
|
|
return compareReplicationHistory(sourceRest, targetRest);
|
|
}
|
|
|
|
function hasSessionId(sessionId, history) {
|
|
var props = history[0];
|
|
var rest = history.slice(1);
|
|
|
|
if (!sessionId || history.length === 0) {
|
|
return false;
|
|
}
|
|
|
|
if (sessionId === props.session_id) {
|
|
return true;
|
|
}
|
|
|
|
return hasSessionId(sessionId, rest);
|
|
}
|
|
|
|
function isForbiddenError(err) {
|
|
return typeof err.status === 'number' && Math.floor(err.status / 100) === 4;
|
|
}
|
|
|
|
var STARTING_BACK_OFF = 0;
|
|
|
|
function backOff(opts, returnValue, error, callback) {
|
|
if (opts.retry === false) {
|
|
returnValue.emit('error', error);
|
|
returnValue.removeAllListeners();
|
|
return;
|
|
}
|
|
if (typeof opts.back_off_function !== 'function') {
|
|
opts.back_off_function = defaultBackOff;
|
|
}
|
|
returnValue.emit('requestError', error);
|
|
if (returnValue.state === 'active' || returnValue.state === 'pending') {
|
|
returnValue.emit('paused', error);
|
|
returnValue.state = 'stopped';
|
|
var backOffSet = function backoffTimeSet() {
|
|
opts.current_back_off = STARTING_BACK_OFF;
|
|
};
|
|
var removeBackOffSetter = function removeBackOffTimeSet() {
|
|
returnValue.removeListener('active', backOffSet);
|
|
};
|
|
returnValue.once('paused', removeBackOffSetter);
|
|
returnValue.once('active', backOffSet);
|
|
}
|
|
|
|
opts.current_back_off = opts.current_back_off || STARTING_BACK_OFF;
|
|
opts.current_back_off = opts.back_off_function(opts.current_back_off);
|
|
setTimeout(callback, opts.current_back_off);
|
|
}
|
|
|
|
function sortObjectPropertiesByKey(queryParams) {
|
|
return Object.keys(queryParams).sort(pouchdbCollate.collate).reduce(function (result, key) {
|
|
result[key] = queryParams[key];
|
|
return result;
|
|
}, {});
|
|
}
|
|
|
|
// Generate a unique id particular to this replication.
|
|
// Not guaranteed to align perfectly with CouchDB's rep ids.
|
|
function generateReplicationId(src, target, opts) {
|
|
var docIds = opts.doc_ids ? opts.doc_ids.sort(pouchdbCollate.collate) : '';
|
|
var filterFun = opts.filter ? opts.filter.toString() : '';
|
|
var queryParams = '';
|
|
var filterViewName = '';
|
|
|
|
if (opts.filter && opts.query_params) {
|
|
queryParams = JSON.stringify(sortObjectPropertiesByKey(opts.query_params));
|
|
}
|
|
|
|
if (opts.filter && opts.filter === '_view') {
|
|
filterViewName = opts.view.toString();
|
|
}
|
|
|
|
return PouchPromise.all([src.id(), target.id()]).then(function (res) {
|
|
var queryData = res[0] + res[1] + filterFun + filterViewName +
|
|
queryParams + docIds;
|
|
return new PouchPromise(function (resolve) {
|
|
binaryMd5(queryData, resolve);
|
|
});
|
|
}).then(function (md5sum) {
|
|
// can't use straight-up md5 alphabet, because
|
|
// the char '/' is interpreted as being for attachments,
|
|
// and + is also not url-safe
|
|
md5sum = md5sum.replace(/\//g, '.').replace(/\+/g, '_');
|
|
return '_local/' + md5sum;
|
|
});
|
|
}
|
|
|
|
function replicate$1(src, target, opts, returnValue, result) {
|
|
var batches = []; // list of batches to be processed
|
|
var currentBatch; // the batch currently being processed
|
|
var pendingBatch = {
|
|
seq: 0,
|
|
changes: [],
|
|
docs: []
|
|
}; // next batch, not yet ready to be processed
|
|
var writingCheckpoint = false; // true while checkpoint is being written
|
|
var changesCompleted = false; // true when all changes received
|
|
var replicationCompleted = false; // true when replication has completed
|
|
var last_seq = 0;
|
|
var continuous = opts.continuous || opts.live || false;
|
|
var batch_size = opts.batch_size || 100;
|
|
var batches_limit = opts.batches_limit || 10;
|
|
var changesPending = false; // true while src.changes is running
|
|
var doc_ids = opts.doc_ids;
|
|
var repId;
|
|
var checkpointer;
|
|
var allErrors = [];
|
|
var changedDocs = [];
|
|
// Like couchdb, every replication gets a unique session id
|
|
var session = uuid();
|
|
|
|
result = result || {
|
|
ok: true,
|
|
start_time: new Date(),
|
|
docs_read: 0,
|
|
docs_written: 0,
|
|
doc_write_failures: 0,
|
|
errors: []
|
|
};
|
|
|
|
var changesOpts = {};
|
|
returnValue.ready(src, target);
|
|
|
|
function initCheckpointer() {
|
|
if (checkpointer) {
|
|
return PouchPromise.resolve();
|
|
}
|
|
return generateReplicationId(src, target, opts).then(function (res) {
|
|
repId = res;
|
|
checkpointer = new Checkpointer(src, target, repId, returnValue);
|
|
});
|
|
}
|
|
|
|
function writeDocs() {
|
|
changedDocs = [];
|
|
|
|
if (currentBatch.docs.length === 0) {
|
|
return;
|
|
}
|
|
var docs = currentBatch.docs;
|
|
return target.bulkDocs({docs: docs, new_edits: false}).then(function (res) {
|
|
/* istanbul ignore if */
|
|
if (returnValue.cancelled) {
|
|
completeReplication();
|
|
throw new Error('cancelled');
|
|
}
|
|
var errors = [];
|
|
var errorsById = {};
|
|
res.forEach(function (res) {
|
|
if (res.error) {
|
|
result.doc_write_failures++;
|
|
errors.push(res);
|
|
errorsById[res.id] = res;
|
|
}
|
|
});
|
|
allErrors = allErrors.concat(errors);
|
|
result.docs_written += currentBatch.docs.length - errors.length;
|
|
var non403s = errors.filter(function (error) {
|
|
return error.name !== 'unauthorized' && error.name !== 'forbidden';
|
|
});
|
|
|
|
docs.forEach(function (doc) {
|
|
var error = errorsById[doc._id];
|
|
if (error) {
|
|
returnValue.emit('denied', clone(error));
|
|
} else {
|
|
changedDocs.push(doc);
|
|
}
|
|
});
|
|
|
|
if (non403s.length > 0) {
|
|
var error = new Error('bulkDocs error');
|
|
error.other_errors = errors;
|
|
abortReplication('target.bulkDocs failed to write docs', error);
|
|
throw new Error('bulkWrite partial failure');
|
|
}
|
|
}, function (err) {
|
|
result.doc_write_failures += docs.length;
|
|
throw err;
|
|
});
|
|
}
|
|
|
|
function finishBatch() {
|
|
if (currentBatch.error) {
|
|
throw new Error('There was a problem getting docs.');
|
|
}
|
|
result.last_seq = last_seq = currentBatch.seq;
|
|
var outResult = clone(result);
|
|
if (changedDocs.length) {
|
|
outResult.docs = changedDocs;
|
|
returnValue.emit('change', outResult);
|
|
}
|
|
writingCheckpoint = true;
|
|
return checkpointer.writeCheckpoint(currentBatch.seq,
|
|
session).then(function () {
|
|
writingCheckpoint = false;
|
|
/* istanbul ignore if */
|
|
if (returnValue.cancelled) {
|
|
completeReplication();
|
|
throw new Error('cancelled');
|
|
}
|
|
currentBatch = undefined;
|
|
getChanges();
|
|
}).catch(onCheckpointError);
|
|
}
|
|
|
|
function getDiffs() {
|
|
var diff = {};
|
|
currentBatch.changes.forEach(function (change) {
|
|
// Couchbase Sync Gateway emits these, but we can ignore them
|
|
/* istanbul ignore if */
|
|
if (change.id === "_user/") {
|
|
return;
|
|
}
|
|
diff[change.id] = change.changes.map(function (x) {
|
|
return x.rev;
|
|
});
|
|
});
|
|
return target.revsDiff(diff).then(function (diffs) {
|
|
/* istanbul ignore if */
|
|
if (returnValue.cancelled) {
|
|
completeReplication();
|
|
throw new Error('cancelled');
|
|
}
|
|
// currentBatch.diffs elements are deleted as the documents are written
|
|
currentBatch.diffs = diffs;
|
|
});
|
|
}
|
|
|
|
function getBatchDocs() {
|
|
return getDocs(src, target, currentBatch.diffs, returnValue).then(function (got) {
|
|
currentBatch.error = !got.ok;
|
|
got.docs.forEach(function (doc) {
|
|
delete currentBatch.diffs[doc._id];
|
|
result.docs_read++;
|
|
currentBatch.docs.push(doc);
|
|
});
|
|
});
|
|
}
|
|
|
|
function startNextBatch() {
|
|
if (returnValue.cancelled || currentBatch) {
|
|
return;
|
|
}
|
|
if (batches.length === 0) {
|
|
processPendingBatch(true);
|
|
return;
|
|
}
|
|
currentBatch = batches.shift();
|
|
getDiffs()
|
|
.then(getBatchDocs)
|
|
.then(writeDocs)
|
|
.then(finishBatch)
|
|
.then(startNextBatch)
|
|
.catch(function (err) {
|
|
abortReplication('batch processing terminated with error', err);
|
|
});
|
|
}
|
|
|
|
|
|
function processPendingBatch(immediate) {
|
|
if (pendingBatch.changes.length === 0) {
|
|
if (batches.length === 0 && !currentBatch) {
|
|
if ((continuous && changesOpts.live) || changesCompleted) {
|
|
returnValue.state = 'pending';
|
|
returnValue.emit('paused');
|
|
}
|
|
if (changesCompleted) {
|
|
completeReplication();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (
|
|
immediate ||
|
|
changesCompleted ||
|
|
pendingBatch.changes.length >= batch_size
|
|
) {
|
|
batches.push(pendingBatch);
|
|
pendingBatch = {
|
|
seq: 0,
|
|
changes: [],
|
|
docs: []
|
|
};
|
|
if (returnValue.state === 'pending' || returnValue.state === 'stopped') {
|
|
returnValue.state = 'active';
|
|
returnValue.emit('active');
|
|
}
|
|
startNextBatch();
|
|
}
|
|
}
|
|
|
|
|
|
function abortReplication(reason, err) {
|
|
if (replicationCompleted) {
|
|
return;
|
|
}
|
|
if (!err.message) {
|
|
err.message = reason;
|
|
}
|
|
result.ok = false;
|
|
result.status = 'aborting';
|
|
result.errors.push(err);
|
|
allErrors = allErrors.concat(err);
|
|
batches = [];
|
|
pendingBatch = {
|
|
seq: 0,
|
|
changes: [],
|
|
docs: []
|
|
};
|
|
completeReplication();
|
|
}
|
|
|
|
|
|
function completeReplication() {
|
|
if (replicationCompleted) {
|
|
return;
|
|
}
|
|
/* istanbul ignore if */
|
|
if (returnValue.cancelled) {
|
|
result.status = 'cancelled';
|
|
if (writingCheckpoint) {
|
|
return;
|
|
}
|
|
}
|
|
result.status = result.status || 'complete';
|
|
result.end_time = new Date();
|
|
result.last_seq = last_seq;
|
|
replicationCompleted = true;
|
|
var non403s = allErrors.filter(function (error) {
|
|
return error.name !== 'unauthorized' && error.name !== 'forbidden';
|
|
});
|
|
if (non403s.length > 0) {
|
|
var error = allErrors.pop();
|
|
if (allErrors.length > 0) {
|
|
error.other_errors = allErrors;
|
|
}
|
|
error.result = result;
|
|
backOff(opts, returnValue, error, function () {
|
|
replicate$1(src, target, opts, returnValue);
|
|
});
|
|
} else {
|
|
result.errors = allErrors;
|
|
returnValue.emit('complete', result);
|
|
returnValue.removeAllListeners();
|
|
}
|
|
}
|
|
|
|
|
|
function onChange(change) {
|
|
/* istanbul ignore if */
|
|
if (returnValue.cancelled) {
|
|
return completeReplication();
|
|
}
|
|
var filter = filterChange(opts)(change);
|
|
if (!filter) {
|
|
return;
|
|
}
|
|
pendingBatch.seq = change.seq;
|
|
pendingBatch.changes.push(change);
|
|
processPendingBatch(batches.length === 0 && changesOpts.live);
|
|
}
|
|
|
|
|
|
function onChangesComplete(changes) {
|
|
changesPending = false;
|
|
/* istanbul ignore if */
|
|
if (returnValue.cancelled) {
|
|
return completeReplication();
|
|
}
|
|
|
|
// if no results were returned then we're done,
|
|
// else fetch more
|
|
if (changes.results.length > 0) {
|
|
changesOpts.since = changes.last_seq;
|
|
getChanges();
|
|
processPendingBatch(true);
|
|
} else {
|
|
|
|
var complete = function () {
|
|
if (continuous) {
|
|
changesOpts.live = true;
|
|
getChanges();
|
|
} else {
|
|
changesCompleted = true;
|
|
}
|
|
processPendingBatch(true);
|
|
};
|
|
|
|
// update the checkpoint so we start from the right seq next time
|
|
if (!currentBatch && changes.results.length === 0) {
|
|
writingCheckpoint = true;
|
|
checkpointer.writeCheckpoint(changes.last_seq,
|
|
session).then(function () {
|
|
writingCheckpoint = false;
|
|
result.last_seq = last_seq = changes.last_seq;
|
|
complete();
|
|
})
|
|
.catch(onCheckpointError);
|
|
} else {
|
|
complete();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function onChangesError(err) {
|
|
changesPending = false;
|
|
/* istanbul ignore if */
|
|
if (returnValue.cancelled) {
|
|
return completeReplication();
|
|
}
|
|
abortReplication('changes rejected', err);
|
|
}
|
|
|
|
|
|
function getChanges() {
|
|
if (!(
|
|
!changesPending &&
|
|
!changesCompleted &&
|
|
batches.length < batches_limit
|
|
)) {
|
|
return;
|
|
}
|
|
changesPending = true;
|
|
function abortChanges() {
|
|
changes.cancel();
|
|
}
|
|
function removeListener() {
|
|
returnValue.removeListener('cancel', abortChanges);
|
|
}
|
|
|
|
if (returnValue._changes) { // remove old changes() and listeners
|
|
returnValue.removeListener('cancel', returnValue._abortChanges);
|
|
returnValue._changes.cancel();
|
|
}
|
|
returnValue.once('cancel', abortChanges);
|
|
|
|
var changes = src.changes(changesOpts)
|
|
.on('change', onChange);
|
|
changes.then(removeListener, removeListener);
|
|
changes.then(onChangesComplete)
|
|
.catch(onChangesError);
|
|
|
|
if (opts.retry) {
|
|
// save for later so we can cancel if necessary
|
|
returnValue._changes = changes;
|
|
returnValue._abortChanges = abortChanges;
|
|
}
|
|
}
|
|
|
|
|
|
function startChanges() {
|
|
initCheckpointer().then(function () {
|
|
/* istanbul ignore if */
|
|
if (returnValue.cancelled) {
|
|
completeReplication();
|
|
return;
|
|
}
|
|
return checkpointer.getCheckpoint().then(function (checkpoint) {
|
|
last_seq = checkpoint;
|
|
changesOpts = {
|
|
since: last_seq,
|
|
limit: batch_size,
|
|
batch_size: batch_size,
|
|
style: 'all_docs',
|
|
doc_ids: doc_ids,
|
|
return_docs: true // required so we know when we're done
|
|
};
|
|
if (opts.filter) {
|
|
if (typeof opts.filter !== 'string') {
|
|
// required for the client-side filter in onChange
|
|
changesOpts.include_docs = true;
|
|
} else { // ddoc filter
|
|
changesOpts.filter = opts.filter;
|
|
}
|
|
}
|
|
if ('heartbeat' in opts) {
|
|
changesOpts.heartbeat = opts.heartbeat;
|
|
}
|
|
if ('timeout' in opts) {
|
|
changesOpts.timeout = opts.timeout;
|
|
}
|
|
if (opts.query_params) {
|
|
changesOpts.query_params = opts.query_params;
|
|
}
|
|
if (opts.view) {
|
|
changesOpts.view = opts.view;
|
|
}
|
|
getChanges();
|
|
});
|
|
}).catch(function (err) {
|
|
abortReplication('getCheckpoint rejected with ', err);
|
|
});
|
|
}
|
|
|
|
/* istanbul ignore next */
|
|
function onCheckpointError(err) {
|
|
writingCheckpoint = false;
|
|
abortReplication('writeCheckpoint completed with error', err);
|
|
throw err;
|
|
}
|
|
|
|
/* istanbul ignore if */
|
|
if (returnValue.cancelled) { // cancelled immediately
|
|
completeReplication();
|
|
return;
|
|
}
|
|
|
|
if (!returnValue._addedListeners) {
|
|
returnValue.once('cancel', completeReplication);
|
|
|
|
if (typeof opts.complete === 'function') {
|
|
returnValue.once('error', opts.complete);
|
|
returnValue.once('complete', function (result) {
|
|
opts.complete(null, result);
|
|
});
|
|
}
|
|
returnValue._addedListeners = true;
|
|
}
|
|
|
|
if (typeof opts.since === 'undefined') {
|
|
startChanges();
|
|
} else {
|
|
initCheckpointer().then(function () {
|
|
writingCheckpoint = true;
|
|
return checkpointer.writeCheckpoint(opts.since, session);
|
|
}).then(function () {
|
|
writingCheckpoint = false;
|
|
/* istanbul ignore if */
|
|
if (returnValue.cancelled) {
|
|
completeReplication();
|
|
return;
|
|
}
|
|
last_seq = opts.since;
|
|
startChanges();
|
|
}).catch(onCheckpointError);
|
|
}
|
|
}
|
|
|
|
// We create a basic promise so the caller can cancel the replication possibly
|
|
// before we have actually started listening to changes etc
|
|
inherits(Replication, events.EventEmitter);
|
|
function Replication() {
|
|
events.EventEmitter.call(this);
|
|
this.cancelled = false;
|
|
this.state = 'pending';
|
|
var self = this;
|
|
var promise = new PouchPromise(function (fulfill, reject) {
|
|
self.once('complete', fulfill);
|
|
self.once('error', reject);
|
|
});
|
|
self.then = function (resolve, reject) {
|
|
return promise.then(resolve, reject);
|
|
};
|
|
self.catch = function (reject) {
|
|
return promise.catch(reject);
|
|
};
|
|
// As we allow error handling via "error" event as well,
|
|
// put a stub in here so that rejecting never throws UnhandledError.
|
|
self.catch(function () {});
|
|
}
|
|
|
|
Replication.prototype.cancel = function () {
|
|
this.cancelled = true;
|
|
this.state = 'cancelled';
|
|
this.emit('cancel');
|
|
};
|
|
|
|
Replication.prototype.ready = function (src, target) {
|
|
var self = this;
|
|
if (self._readyCalled) {
|
|
return;
|
|
}
|
|
self._readyCalled = true;
|
|
|
|
function onDestroy() {
|
|
self.cancel();
|
|
}
|
|
src.once('destroyed', onDestroy);
|
|
target.once('destroyed', onDestroy);
|
|
function cleanup() {
|
|
src.removeListener('destroyed', onDestroy);
|
|
target.removeListener('destroyed', onDestroy);
|
|
}
|
|
self.once('complete', cleanup);
|
|
};
|
|
|
|
function toPouch(db, opts) {
|
|
var PouchConstructor = opts.PouchConstructor;
|
|
if (typeof db === 'string') {
|
|
return new PouchConstructor(db, opts);
|
|
} else {
|
|
return db;
|
|
}
|
|
}
|
|
|
|
function replicate(src, target, opts, callback) {
|
|
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
if (typeof opts === 'undefined') {
|
|
opts = {};
|
|
}
|
|
|
|
if (opts.doc_ids && !Array.isArray(opts.doc_ids)) {
|
|
throw createError(BAD_REQUEST,
|
|
"`doc_ids` filter parameter is not a list.");
|
|
}
|
|
|
|
opts.complete = callback;
|
|
opts = clone(opts);
|
|
opts.continuous = opts.continuous || opts.live;
|
|
opts.retry = ('retry' in opts) ? opts.retry : false;
|
|
/*jshint validthis:true */
|
|
opts.PouchConstructor = opts.PouchConstructor || this;
|
|
var replicateRet = new Replication(opts);
|
|
var srcPouch = toPouch(src, opts);
|
|
var targetPouch = toPouch(target, opts);
|
|
replicate$1(srcPouch, targetPouch, opts, replicateRet);
|
|
return replicateRet;
|
|
}
|
|
|
|
inherits(Sync, events.EventEmitter);
|
|
function sync(src, target, opts, callback) {
|
|
if (typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
if (typeof opts === 'undefined') {
|
|
opts = {};
|
|
}
|
|
opts = clone(opts);
|
|
/*jshint validthis:true */
|
|
opts.PouchConstructor = opts.PouchConstructor || this;
|
|
src = toPouch(src, opts);
|
|
target = toPouch(target, opts);
|
|
return new Sync(src, target, opts, callback);
|
|
}
|
|
|
|
function Sync(src, target, opts, callback) {
|
|
var self = this;
|
|
this.canceled = false;
|
|
|
|
var optsPush = opts.push ? jsExtend.extend({}, opts, opts.push) : opts;
|
|
var optsPull = opts.pull ? jsExtend.extend({}, opts, opts.pull) : opts;
|
|
|
|
this.push = replicate(src, target, optsPush);
|
|
this.pull = replicate(target, src, optsPull);
|
|
|
|
this.pushPaused = true;
|
|
this.pullPaused = true;
|
|
|
|
function pullChange(change) {
|
|
self.emit('change', {
|
|
direction: 'pull',
|
|
change: change
|
|
});
|
|
}
|
|
function pushChange(change) {
|
|
self.emit('change', {
|
|
direction: 'push',
|
|
change: change
|
|
});
|
|
}
|
|
function pushDenied(doc) {
|
|
self.emit('denied', {
|
|
direction: 'push',
|
|
doc: doc
|
|
});
|
|
}
|
|
function pullDenied(doc) {
|
|
self.emit('denied', {
|
|
direction: 'pull',
|
|
doc: doc
|
|
});
|
|
}
|
|
function pushPaused() {
|
|
self.pushPaused = true;
|
|
/* istanbul ignore if */
|
|
if (self.pullPaused) {
|
|
self.emit('paused');
|
|
}
|
|
}
|
|
function pullPaused() {
|
|
self.pullPaused = true;
|
|
/* istanbul ignore if */
|
|
if (self.pushPaused) {
|
|
self.emit('paused');
|
|
}
|
|
}
|
|
function pushActive() {
|
|
self.pushPaused = false;
|
|
/* istanbul ignore if */
|
|
if (self.pullPaused) {
|
|
self.emit('active', {
|
|
direction: 'push'
|
|
});
|
|
}
|
|
}
|
|
function pullActive() {
|
|
self.pullPaused = false;
|
|
/* istanbul ignore if */
|
|
if (self.pushPaused) {
|
|
self.emit('active', {
|
|
direction: 'pull'
|
|
});
|
|
}
|
|
}
|
|
|
|
var removed = {};
|
|
|
|
function removeAll(type) { // type is 'push' or 'pull'
|
|
return function (event, func) {
|
|
var isChange = event === 'change' &&
|
|
(func === pullChange || func === pushChange);
|
|
var isDenied = event === 'denied' &&
|
|
(func === pullDenied || func === pushDenied);
|
|
var isPaused = event === 'paused' &&
|
|
(func === pullPaused || func === pushPaused);
|
|
var isActive = event === 'active' &&
|
|
(func === pullActive || func === pushActive);
|
|
|
|
if (isChange || isDenied || isPaused || isActive) {
|
|
if (!(event in removed)) {
|
|
removed[event] = {};
|
|
}
|
|
removed[event][type] = true;
|
|
if (Object.keys(removed[event]).length === 2) {
|
|
// both push and pull have asked to be removed
|
|
self.removeAllListeners(event);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
if (opts.live) {
|
|
this.push.on('complete', self.pull.cancel.bind(self.pull));
|
|
this.pull.on('complete', self.push.cancel.bind(self.push));
|
|
}
|
|
|
|
this.on('newListener', function (event) {
|
|
if (event === 'change') {
|
|
self.pull.on('change', pullChange);
|
|
self.push.on('change', pushChange);
|
|
} else if (event === 'denied') {
|
|
self.pull.on('denied', pullDenied);
|
|
self.push.on('denied', pushDenied);
|
|
} else if (event === 'active') {
|
|
self.pull.on('active', pullActive);
|
|
self.push.on('active', pushActive);
|
|
} else if (event === 'paused') {
|
|
self.pull.on('paused', pullPaused);
|
|
self.push.on('paused', pushPaused);
|
|
}
|
|
});
|
|
|
|
this.on('removeListener', function (event) {
|
|
if (event === 'change') {
|
|
self.pull.removeListener('change', pullChange);
|
|
self.push.removeListener('change', pushChange);
|
|
} else if (event === 'denied') {
|
|
self.pull.removeListener('denied', pullDenied);
|
|
self.push.removeListener('denied', pushDenied);
|
|
} else if (event === 'active') {
|
|
self.pull.removeListener('active', pullActive);
|
|
self.push.removeListener('active', pushActive);
|
|
} else if (event === 'paused') {
|
|
self.pull.removeListener('paused', pullPaused);
|
|
self.push.removeListener('paused', pushPaused);
|
|
}
|
|
});
|
|
|
|
this.pull.on('removeListener', removeAll('pull'));
|
|
this.push.on('removeListener', removeAll('push'));
|
|
|
|
var promise = PouchPromise.all([
|
|
this.push,
|
|
this.pull
|
|
]).then(function (resp) {
|
|
var out = {
|
|
push: resp[0],
|
|
pull: resp[1]
|
|
};
|
|
self.emit('complete', out);
|
|
if (callback) {
|
|
callback(null, out);
|
|
}
|
|
self.removeAllListeners();
|
|
return out;
|
|
}, function (err) {
|
|
self.cancel();
|
|
if (callback) {
|
|
// if there's a callback, then the callback can receive
|
|
// the error event
|
|
callback(err);
|
|
} else {
|
|
// if there's no callback, then we're safe to emit an error
|
|
// event, which would otherwise throw an unhandled error
|
|
// due to 'error' being a special event in EventEmitters
|
|
self.emit('error', err);
|
|
}
|
|
self.removeAllListeners();
|
|
if (callback) {
|
|
// no sense throwing if we're already emitting an 'error' event
|
|
throw err;
|
|
}
|
|
});
|
|
|
|
this.then = function (success, err) {
|
|
return promise.then(success, err);
|
|
};
|
|
|
|
this.catch = function (err) {
|
|
return promise.catch(err);
|
|
};
|
|
}
|
|
|
|
Sync.prototype.cancel = function () {
|
|
if (!this.canceled) {
|
|
this.canceled = true;
|
|
this.push.cancel();
|
|
this.pull.cancel();
|
|
}
|
|
};
|
|
|
|
function replication(PouchDB) {
|
|
PouchDB.replicate = replicate;
|
|
PouchDB.sync = sync;
|
|
}
|
|
|
|
PouchDB.plugin(IDBPouch)
|
|
.plugin(WebSqlPouch)
|
|
.plugin(HttpPouch$1)
|
|
.plugin(mapreduce)
|
|
.plugin(replication);
|
|
|
|
module.exports = PouchDB;
|
|
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{"_process":133,"argsarray":38,"debug":121,"es6-promise-pool":55,"events":56,"inherits":123,"js-extend":90,"lie":124,"pouchdb-collate":115,"pouchdb-collections":117,"scope-eval":276,"spark-md5":277,"vuvuzela":298}],121:[function(require,module,exports){
|
|
|
|
/**
|
|
* This is the web browser implementation of `debug()`.
|
|
*
|
|
* Expose `debug()` as the module.
|
|
*/
|
|
|
|
exports = module.exports = require('./debug');
|
|
exports.log = log;
|
|
exports.formatArgs = formatArgs;
|
|
exports.save = save;
|
|
exports.load = load;
|
|
exports.useColors = useColors;
|
|
exports.storage = 'undefined' != typeof chrome
|
|
&& 'undefined' != typeof chrome.storage
|
|
? chrome.storage.local
|
|
: localstorage();
|
|
|
|
/**
|
|
* Colors.
|
|
*/
|
|
|
|
exports.colors = [
|
|
'lightseagreen',
|
|
'forestgreen',
|
|
'goldenrod',
|
|
'dodgerblue',
|
|
'darkorchid',
|
|
'crimson'
|
|
];
|
|
|
|
/**
|
|
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
|
|
* and the Firebug extension (any Firefox version) are known
|
|
* to support "%c" CSS customizations.
|
|
*
|
|
* TODO: add a `localStorage` variable to explicitly enable/disable colors
|
|
*/
|
|
|
|
function useColors() {
|
|
// is webkit? http://stackoverflow.com/a/16459606/376773
|
|
return ('WebkitAppearance' in document.documentElement.style) ||
|
|
// is firebug? http://stackoverflow.com/a/398120/376773
|
|
(window.console && (console.firebug || (console.exception && console.table))) ||
|
|
// is firefox >= v31?
|
|
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
|
|
(navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31);
|
|
}
|
|
|
|
/**
|
|
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
|
|
*/
|
|
|
|
exports.formatters.j = function(v) {
|
|
return JSON.stringify(v);
|
|
};
|
|
|
|
|
|
/**
|
|
* Colorize log arguments if enabled.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
function formatArgs() {
|
|
var args = arguments;
|
|
var useColors = this.useColors;
|
|
|
|
args[0] = (useColors ? '%c' : '')
|
|
+ this.namespace
|
|
+ (useColors ? ' %c' : ' ')
|
|
+ args[0]
|
|
+ (useColors ? '%c ' : ' ')
|
|
+ '+' + exports.humanize(this.diff);
|
|
|
|
if (!useColors) return args;
|
|
|
|
var c = 'color: ' + this.color;
|
|
args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1));
|
|
|
|
// the final "%c" is somewhat tricky, because there could be other
|
|
// arguments passed either before or after the %c, so we need to
|
|
// figure out the correct index to insert the CSS into
|
|
var index = 0;
|
|
var lastC = 0;
|
|
args[0].replace(/%[a-z%]/g, function(match) {
|
|
if ('%%' === match) return;
|
|
index++;
|
|
if ('%c' === match) {
|
|
// we only are interested in the *last* %c
|
|
// (the user may have provided their own)
|
|
lastC = index;
|
|
}
|
|
});
|
|
|
|
args.splice(lastC, 0, c);
|
|
return args;
|
|
}
|
|
|
|
/**
|
|
* Invokes `console.log()` when available.
|
|
* No-op when `console.log` is not a "function".
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
function log() {
|
|
// this hackery is required for IE8/9, where
|
|
// the `console.log` function doesn't have 'apply'
|
|
return 'object' === typeof console
|
|
&& console.log
|
|
&& Function.prototype.apply.call(console.log, console, arguments);
|
|
}
|
|
|
|
/**
|
|
* Save `namespaces`.
|
|
*
|
|
* @param {String} namespaces
|
|
* @api private
|
|
*/
|
|
|
|
function save(namespaces) {
|
|
try {
|
|
if (null == namespaces) {
|
|
exports.storage.removeItem('debug');
|
|
} else {
|
|
exports.storage.debug = namespaces;
|
|
}
|
|
} catch(e) {}
|
|
}
|
|
|
|
/**
|
|
* Load `namespaces`.
|
|
*
|
|
* @return {String} returns the previously persisted debug modes
|
|
* @api private
|
|
*/
|
|
|
|
function load() {
|
|
var r;
|
|
try {
|
|
r = exports.storage.debug;
|
|
} catch(e) {}
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* Enable namespaces listed in `localStorage.debug` initially.
|
|
*/
|
|
|
|
exports.enable(load());
|
|
|
|
/**
|
|
* Localstorage attempts to return the localstorage.
|
|
*
|
|
* This is necessary because safari throws
|
|
* when a user disables cookies/localstorage
|
|
* and you attempt to access it.
|
|
*
|
|
* @return {LocalStorage}
|
|
* @api private
|
|
*/
|
|
|
|
function localstorage(){
|
|
try {
|
|
return window.localStorage;
|
|
} catch (e) {}
|
|
}
|
|
|
|
},{"./debug":122}],122:[function(require,module,exports){
|
|
|
|
/**
|
|
* This is the common logic for both the Node.js and web browser
|
|
* implementations of `debug()`.
|
|
*
|
|
* Expose `debug()` as the module.
|
|
*/
|
|
|
|
exports = module.exports = debug;
|
|
exports.coerce = coerce;
|
|
exports.disable = disable;
|
|
exports.enable = enable;
|
|
exports.enabled = enabled;
|
|
exports.humanize = require('ms');
|
|
|
|
/**
|
|
* The currently active debug mode names, and names to skip.
|
|
*/
|
|
|
|
exports.names = [];
|
|
exports.skips = [];
|
|
|
|
/**
|
|
* Map of special "%n" handling functions, for the debug "format" argument.
|
|
*
|
|
* Valid key names are a single, lowercased letter, i.e. "n".
|
|
*/
|
|
|
|
exports.formatters = {};
|
|
|
|
/**
|
|
* Previously assigned color.
|
|
*/
|
|
|
|
var prevColor = 0;
|
|
|
|
/**
|
|
* Previous log timestamp.
|
|
*/
|
|
|
|
var prevTime;
|
|
|
|
/**
|
|
* Select a color.
|
|
*
|
|
* @return {Number}
|
|
* @api private
|
|
*/
|
|
|
|
function selectColor() {
|
|
return exports.colors[prevColor++ % exports.colors.length];
|
|
}
|
|
|
|
/**
|
|
* Create a debugger with the given `namespace`.
|
|
*
|
|
* @param {String} namespace
|
|
* @return {Function}
|
|
* @api public
|
|
*/
|
|
|
|
function debug(namespace) {
|
|
|
|
// define the `disabled` version
|
|
function disabled() {
|
|
}
|
|
disabled.enabled = false;
|
|
|
|
// define the `enabled` version
|
|
function enabled() {
|
|
|
|
var self = enabled;
|
|
|
|
// set `diff` timestamp
|
|
var curr = +new Date();
|
|
var ms = curr - (prevTime || curr);
|
|
self.diff = ms;
|
|
self.prev = prevTime;
|
|
self.curr = curr;
|
|
prevTime = curr;
|
|
|
|
// add the `color` if not set
|
|
if (null == self.useColors) self.useColors = exports.useColors();
|
|
if (null == self.color && self.useColors) self.color = selectColor();
|
|
|
|
var args = Array.prototype.slice.call(arguments);
|
|
|
|
args[0] = exports.coerce(args[0]);
|
|
|
|
if ('string' !== typeof args[0]) {
|
|
// anything else let's inspect with %o
|
|
args = ['%o'].concat(args);
|
|
}
|
|
|
|
// apply any `formatters` transformations
|
|
var index = 0;
|
|
args[0] = args[0].replace(/%([a-z%])/g, function(match, format) {
|
|
// if we encounter an escaped % then don't increase the array index
|
|
if (match === '%%') return match;
|
|
index++;
|
|
var formatter = exports.formatters[format];
|
|
if ('function' === typeof formatter) {
|
|
var val = args[index];
|
|
match = formatter.call(self, val);
|
|
|
|
// now we need to remove `args[index]` since it's inlined in the `format`
|
|
args.splice(index, 1);
|
|
index--;
|
|
}
|
|
return match;
|
|
});
|
|
|
|
if ('function' === typeof exports.formatArgs) {
|
|
args = exports.formatArgs.apply(self, args);
|
|
}
|
|
var logFn = enabled.log || exports.log || console.log.bind(console);
|
|
logFn.apply(self, args);
|
|
}
|
|
enabled.enabled = true;
|
|
|
|
var fn = exports.enabled(namespace) ? enabled : disabled;
|
|
|
|
fn.namespace = namespace;
|
|
|
|
return fn;
|
|
}
|
|
|
|
/**
|
|
* Enables a debug mode by namespaces. This can include modes
|
|
* separated by a colon and wildcards.
|
|
*
|
|
* @param {String} namespaces
|
|
* @api public
|
|
*/
|
|
|
|
function enable(namespaces) {
|
|
exports.save(namespaces);
|
|
|
|
var split = (namespaces || '').split(/[\s,]+/);
|
|
var len = split.length;
|
|
|
|
for (var i = 0; i < len; i++) {
|
|
if (!split[i]) continue; // ignore empty strings
|
|
namespaces = split[i].replace(/\*/g, '.*?');
|
|
if (namespaces[0] === '-') {
|
|
exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
|
|
} else {
|
|
exports.names.push(new RegExp('^' + namespaces + '$'));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Disable debug output.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
function disable() {
|
|
exports.enable('');
|
|
}
|
|
|
|
/**
|
|
* Returns true if the given mode name is enabled, false otherwise.
|
|
*
|
|
* @param {String} name
|
|
* @return {Boolean}
|
|
* @api public
|
|
*/
|
|
|
|
function enabled(name) {
|
|
var i, len;
|
|
for (i = 0, len = exports.skips.length; i < len; i++) {
|
|
if (exports.skips[i].test(name)) {
|
|
return false;
|
|
}
|
|
}
|
|
for (i = 0, len = exports.names.length; i < len; i++) {
|
|
if (exports.names[i].test(name)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Coerce `val`.
|
|
*
|
|
* @param {Mixed} val
|
|
* @return {Mixed}
|
|
* @api private
|
|
*/
|
|
|
|
function coerce(val) {
|
|
if (val instanceof Error) return val.stack || val.message;
|
|
return val;
|
|
}
|
|
|
|
},{"ms":125}],123:[function(require,module,exports){
|
|
arguments[4][87][0].apply(exports,arguments)
|
|
},{"dup":87}],124:[function(require,module,exports){
|
|
'use strict';
|
|
var immediate = require('immediate');
|
|
|
|
/* istanbul ignore next */
|
|
function INTERNAL() {}
|
|
|
|
var handlers = {};
|
|
|
|
var REJECTED = ['REJECTED'];
|
|
var FULFILLED = ['FULFILLED'];
|
|
var PENDING = ['PENDING'];
|
|
|
|
module.exports = Promise;
|
|
|
|
function Promise(resolver) {
|
|
if (typeof resolver !== 'function') {
|
|
throw new TypeError('resolver must be a function');
|
|
}
|
|
this.state = PENDING;
|
|
this.queue = [];
|
|
this.outcome = void 0;
|
|
if (resolver !== INTERNAL) {
|
|
safelyResolveThenable(this, resolver);
|
|
}
|
|
}
|
|
|
|
Promise.prototype["catch"] = function (onRejected) {
|
|
return this.then(null, onRejected);
|
|
};
|
|
Promise.prototype.then = function (onFulfilled, onRejected) {
|
|
if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
|
|
typeof onRejected !== 'function' && this.state === REJECTED) {
|
|
return this;
|
|
}
|
|
var promise = new this.constructor(INTERNAL);
|
|
if (this.state !== PENDING) {
|
|
var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
|
|
unwrap(promise, resolver, this.outcome);
|
|
} else {
|
|
this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
|
|
}
|
|
|
|
return promise;
|
|
};
|
|
function QueueItem(promise, onFulfilled, onRejected) {
|
|
this.promise = promise;
|
|
if (typeof onFulfilled === 'function') {
|
|
this.onFulfilled = onFulfilled;
|
|
this.callFulfilled = this.otherCallFulfilled;
|
|
}
|
|
if (typeof onRejected === 'function') {
|
|
this.onRejected = onRejected;
|
|
this.callRejected = this.otherCallRejected;
|
|
}
|
|
}
|
|
QueueItem.prototype.callFulfilled = function (value) {
|
|
handlers.resolve(this.promise, value);
|
|
};
|
|
QueueItem.prototype.otherCallFulfilled = function (value) {
|
|
unwrap(this.promise, this.onFulfilled, value);
|
|
};
|
|
QueueItem.prototype.callRejected = function (value) {
|
|
handlers.reject(this.promise, value);
|
|
};
|
|
QueueItem.prototype.otherCallRejected = function (value) {
|
|
unwrap(this.promise, this.onRejected, value);
|
|
};
|
|
|
|
function unwrap(promise, func, value) {
|
|
immediate(function () {
|
|
var returnValue;
|
|
try {
|
|
returnValue = func(value);
|
|
} catch (e) {
|
|
return handlers.reject(promise, e);
|
|
}
|
|
if (returnValue === promise) {
|
|
handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));
|
|
} else {
|
|
handlers.resolve(promise, returnValue);
|
|
}
|
|
});
|
|
}
|
|
|
|
handlers.resolve = function (self, value) {
|
|
var result = tryCatch(getThen, value);
|
|
if (result.status === 'error') {
|
|
return handlers.reject(self, result.value);
|
|
}
|
|
var thenable = result.value;
|
|
|
|
if (thenable) {
|
|
safelyResolveThenable(self, thenable);
|
|
} else {
|
|
self.state = FULFILLED;
|
|
self.outcome = value;
|
|
var i = -1;
|
|
var len = self.queue.length;
|
|
while (++i < len) {
|
|
self.queue[i].callFulfilled(value);
|
|
}
|
|
}
|
|
return self;
|
|
};
|
|
handlers.reject = function (self, error) {
|
|
self.state = REJECTED;
|
|
self.outcome = error;
|
|
var i = -1;
|
|
var len = self.queue.length;
|
|
while (++i < len) {
|
|
self.queue[i].callRejected(error);
|
|
}
|
|
return self;
|
|
};
|
|
|
|
function getThen(obj) {
|
|
// Make sure we only access the accessor once as required by the spec
|
|
var then = obj && obj.then;
|
|
if (obj && typeof obj === 'object' && typeof then === 'function') {
|
|
return function appyThen() {
|
|
then.apply(obj, arguments);
|
|
};
|
|
}
|
|
}
|
|
|
|
function safelyResolveThenable(self, thenable) {
|
|
// Either fulfill, reject or reject with error
|
|
var called = false;
|
|
function onError(value) {
|
|
if (called) {
|
|
return;
|
|
}
|
|
called = true;
|
|
handlers.reject(self, value);
|
|
}
|
|
|
|
function onSuccess(value) {
|
|
if (called) {
|
|
return;
|
|
}
|
|
called = true;
|
|
handlers.resolve(self, value);
|
|
}
|
|
|
|
function tryToUnwrap() {
|
|
thenable(onSuccess, onError);
|
|
}
|
|
|
|
var result = tryCatch(tryToUnwrap);
|
|
if (result.status === 'error') {
|
|
onError(result.value);
|
|
}
|
|
}
|
|
|
|
function tryCatch(func, value) {
|
|
var out = {};
|
|
try {
|
|
out.value = func(value);
|
|
out.status = 'success';
|
|
} catch (e) {
|
|
out.status = 'error';
|
|
out.value = e;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
Promise.resolve = resolve;
|
|
function resolve(value) {
|
|
if (value instanceof this) {
|
|
return value;
|
|
}
|
|
return handlers.resolve(new this(INTERNAL), value);
|
|
}
|
|
|
|
Promise.reject = reject;
|
|
function reject(reason) {
|
|
var promise = new this(INTERNAL);
|
|
return handlers.reject(promise, reason);
|
|
}
|
|
|
|
Promise.all = all;
|
|
function all(iterable) {
|
|
var self = this;
|
|
if (Object.prototype.toString.call(iterable) !== '[object Array]') {
|
|
return this.reject(new TypeError('must be an array'));
|
|
}
|
|
|
|
var len = iterable.length;
|
|
var called = false;
|
|
if (!len) {
|
|
return this.resolve([]);
|
|
}
|
|
|
|
var values = new Array(len);
|
|
var resolved = 0;
|
|
var i = -1;
|
|
var promise = new this(INTERNAL);
|
|
|
|
while (++i < len) {
|
|
allResolver(iterable[i], i);
|
|
}
|
|
return promise;
|
|
function allResolver(value, i) {
|
|
self.resolve(value).then(resolveFromAll, function (error) {
|
|
if (!called) {
|
|
called = true;
|
|
handlers.reject(promise, error);
|
|
}
|
|
});
|
|
function resolveFromAll(outValue) {
|
|
values[i] = outValue;
|
|
if (++resolved === len && !called) {
|
|
called = true;
|
|
handlers.resolve(promise, values);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Promise.race = race;
|
|
function race(iterable) {
|
|
var self = this;
|
|
if (Object.prototype.toString.call(iterable) !== '[object Array]') {
|
|
return this.reject(new TypeError('must be an array'));
|
|
}
|
|
|
|
var len = iterable.length;
|
|
var called = false;
|
|
if (!len) {
|
|
return this.resolve([]);
|
|
}
|
|
|
|
var i = -1;
|
|
var promise = new this(INTERNAL);
|
|
|
|
while (++i < len) {
|
|
resolver(iterable[i]);
|
|
}
|
|
return promise;
|
|
function resolver(value) {
|
|
self.resolve(value).then(function (response) {
|
|
if (!called) {
|
|
called = true;
|
|
handlers.resolve(promise, response);
|
|
}
|
|
}, function (error) {
|
|
if (!called) {
|
|
called = true;
|
|
handlers.reject(promise, error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
},{"immediate":86}],125:[function(require,module,exports){
|
|
/**
|
|
* Helpers.
|
|
*/
|
|
|
|
var s = 1000;
|
|
var m = s * 60;
|
|
var h = m * 60;
|
|
var d = h * 24;
|
|
var y = d * 365.25;
|
|
|
|
/**
|
|
* Parse or format the given `val`.
|
|
*
|
|
* Options:
|
|
*
|
|
* - `long` verbose formatting [false]
|
|
*
|
|
* @param {String|Number} val
|
|
* @param {Object} options
|
|
* @return {String|Number}
|
|
* @api public
|
|
*/
|
|
|
|
module.exports = function(val, options){
|
|
options = options || {};
|
|
if ('string' == typeof val) return parse(val);
|
|
return options.long
|
|
? long(val)
|
|
: short(val);
|
|
};
|
|
|
|
/**
|
|
* Parse the given `str` and return milliseconds.
|
|
*
|
|
* @param {String} str
|
|
* @return {Number}
|
|
* @api private
|
|
*/
|
|
|
|
function parse(str) {
|
|
str = '' + str;
|
|
if (str.length > 10000) return;
|
|
var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str);
|
|
if (!match) return;
|
|
var n = parseFloat(match[1]);
|
|
var type = (match[2] || 'ms').toLowerCase();
|
|
switch (type) {
|
|
case 'years':
|
|
case 'year':
|
|
case 'yrs':
|
|
case 'yr':
|
|
case 'y':
|
|
return n * y;
|
|
case 'days':
|
|
case 'day':
|
|
case 'd':
|
|
return n * d;
|
|
case 'hours':
|
|
case 'hour':
|
|
case 'hrs':
|
|
case 'hr':
|
|
case 'h':
|
|
return n * h;
|
|
case 'minutes':
|
|
case 'minute':
|
|
case 'mins':
|
|
case 'min':
|
|
case 'm':
|
|
return n * m;
|
|
case 'seconds':
|
|
case 'second':
|
|
case 'secs':
|
|
case 'sec':
|
|
case 's':
|
|
return n * s;
|
|
case 'milliseconds':
|
|
case 'millisecond':
|
|
case 'msecs':
|
|
case 'msec':
|
|
case 'ms':
|
|
return n;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Short format for `ms`.
|
|
*
|
|
* @param {Number} ms
|
|
* @return {String}
|
|
* @api private
|
|
*/
|
|
|
|
function short(ms) {
|
|
if (ms >= d) return Math.round(ms / d) + 'd';
|
|
if (ms >= h) return Math.round(ms / h) + 'h';
|
|
if (ms >= m) return Math.round(ms / m) + 'm';
|
|
if (ms >= s) return Math.round(ms / s) + 's';
|
|
return ms + 'ms';
|
|
}
|
|
|
|
/**
|
|
* Long format for `ms`.
|
|
*
|
|
* @param {Number} ms
|
|
* @return {String}
|
|
* @api private
|
|
*/
|
|
|
|
function long(ms) {
|
|
return plural(ms, d, 'day')
|
|
|| plural(ms, h, 'hour')
|
|
|| plural(ms, m, 'minute')
|
|
|| plural(ms, s, 'second')
|
|
|| ms + ' ms';
|
|
}
|
|
|
|
/**
|
|
* Pluralization helper.
|
|
*/
|
|
|
|
function plural(ms, n, name) {
|
|
if (ms < n) return;
|
|
if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name;
|
|
return Math.ceil(ms / n) + ' ' + name + 's';
|
|
}
|
|
|
|
},{}],126:[function(require,module,exports){
|
|
// a duplex stream is just a stream that is both readable and writable.
|
|
// Since JS doesn't have multiple prototypal inheritance, this class
|
|
// prototypally inherits from Readable, and then parasitically from
|
|
// Writable.
|
|
|
|
'use strict';
|
|
|
|
/*<replacement>*/
|
|
|
|
var objectKeys = Object.keys || function (obj) {
|
|
var keys = [];
|
|
for (var key in obj) {
|
|
keys.push(key);
|
|
}return keys;
|
|
};
|
|
/*</replacement>*/
|
|
|
|
module.exports = Duplex;
|
|
|
|
/*<replacement>*/
|
|
var processNextTick = require('process-nextick-args');
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
var Readable = require('./_stream_readable');
|
|
var Writable = require('./_stream_writable');
|
|
|
|
util.inherits(Duplex, Readable);
|
|
|
|
var keys = objectKeys(Writable.prototype);
|
|
for (var v = 0; v < keys.length; v++) {
|
|
var method = keys[v];
|
|
if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method];
|
|
}
|
|
|
|
function Duplex(options) {
|
|
if (!(this instanceof Duplex)) return new Duplex(options);
|
|
|
|
Readable.call(this, options);
|
|
Writable.call(this, options);
|
|
|
|
if (options && options.readable === false) this.readable = false;
|
|
|
|
if (options && options.writable === false) this.writable = false;
|
|
|
|
this.allowHalfOpen = true;
|
|
if (options && options.allowHalfOpen === false) this.allowHalfOpen = false;
|
|
|
|
this.once('end', onend);
|
|
}
|
|
|
|
// the no-half-open enforcer
|
|
function onend() {
|
|
// if we allow half-open state, or if the writable side ended,
|
|
// then we're ok.
|
|
if (this.allowHalfOpen || this._writableState.ended) return;
|
|
|
|
// no more data can be written.
|
|
// But allow more writes to happen in this tick.
|
|
processNextTick(onEndNT, this);
|
|
}
|
|
|
|
function onEndNT(self) {
|
|
self.end();
|
|
}
|
|
|
|
function forEach(xs, f) {
|
|
for (var i = 0, l = xs.length; i < l; i++) {
|
|
f(xs[i], i);
|
|
}
|
|
}
|
|
},{"./_stream_readable":127,"./_stream_writable":129,"core-util-is":44,"inherits":123,"process-nextick-args":132}],127:[function(require,module,exports){
|
|
(function (process){
|
|
'use strict';
|
|
|
|
module.exports = Readable;
|
|
|
|
/*<replacement>*/
|
|
var processNextTick = require('process-nextick-args');
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var isArray = require('isarray');
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var Buffer = require('buffer').Buffer;
|
|
/*</replacement>*/
|
|
|
|
Readable.ReadableState = ReadableState;
|
|
|
|
var EE = require('events');
|
|
|
|
/*<replacement>*/
|
|
var EElistenerCount = function (emitter, type) {
|
|
return emitter.listeners(type).length;
|
|
};
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var Stream;
|
|
(function () {
|
|
try {
|
|
Stream = require('st' + 'ream');
|
|
} catch (_) {} finally {
|
|
if (!Stream) Stream = require('events').EventEmitter;
|
|
}
|
|
})();
|
|
/*</replacement>*/
|
|
|
|
var Buffer = require('buffer').Buffer;
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var debugUtil = require('util');
|
|
var debug = undefined;
|
|
if (debugUtil && debugUtil.debuglog) {
|
|
debug = debugUtil.debuglog('stream');
|
|
} else {
|
|
debug = function () {};
|
|
}
|
|
/*</replacement>*/
|
|
|
|
var StringDecoder;
|
|
|
|
util.inherits(Readable, Stream);
|
|
|
|
var Duplex;
|
|
function ReadableState(options, stream) {
|
|
Duplex = Duplex || require('./_stream_duplex');
|
|
|
|
options = options || {};
|
|
|
|
// object stream flag. Used to make read(n) ignore n and to
|
|
// make all the buffer merging and length checks go away
|
|
this.objectMode = !!options.objectMode;
|
|
|
|
if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.readableObjectMode;
|
|
|
|
// the point at which it stops calling _read() to fill the buffer
|
|
// Note: 0 is a valid value, means "don't call _read preemptively ever"
|
|
var hwm = options.highWaterMark;
|
|
var defaultHwm = this.objectMode ? 16 : 16 * 1024;
|
|
this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm;
|
|
|
|
// cast to ints.
|
|
this.highWaterMark = ~ ~this.highWaterMark;
|
|
|
|
this.buffer = [];
|
|
this.length = 0;
|
|
this.pipes = null;
|
|
this.pipesCount = 0;
|
|
this.flowing = null;
|
|
this.ended = false;
|
|
this.endEmitted = false;
|
|
this.reading = false;
|
|
|
|
// a flag to be able to tell if the onwrite cb is called immediately,
|
|
// or on a later tick. We set this to true at first, because any
|
|
// actions that shouldn't happen until "later" should generally also
|
|
// not happen before the first write call.
|
|
this.sync = true;
|
|
|
|
// whenever we return null, then we set a flag to say
|
|
// that we're awaiting a 'readable' event emission.
|
|
this.needReadable = false;
|
|
this.emittedReadable = false;
|
|
this.readableListening = false;
|
|
this.resumeScheduled = false;
|
|
|
|
// Crypto is kind of old and crusty. Historically, its default string
|
|
// encoding is 'binary' so we have to make this configurable.
|
|
// Everything else in the universe uses 'utf8', though.
|
|
this.defaultEncoding = options.defaultEncoding || 'utf8';
|
|
|
|
// when piping, we only care about 'readable' events that happen
|
|
// after read()ing all the bytes and not getting any pushback.
|
|
this.ranOut = false;
|
|
|
|
// the number of writers that are awaiting a drain event in .pipe()s
|
|
this.awaitDrain = 0;
|
|
|
|
// if true, a maybeReadMore has been scheduled
|
|
this.readingMore = false;
|
|
|
|
this.decoder = null;
|
|
this.encoding = null;
|
|
if (options.encoding) {
|
|
if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder;
|
|
this.decoder = new StringDecoder(options.encoding);
|
|
this.encoding = options.encoding;
|
|
}
|
|
}
|
|
|
|
var Duplex;
|
|
function Readable(options) {
|
|
Duplex = Duplex || require('./_stream_duplex');
|
|
|
|
if (!(this instanceof Readable)) return new Readable(options);
|
|
|
|
this._readableState = new ReadableState(options, this);
|
|
|
|
// legacy
|
|
this.readable = true;
|
|
|
|
if (options && typeof options.read === 'function') this._read = options.read;
|
|
|
|
Stream.call(this);
|
|
}
|
|
|
|
// Manually shove something into the read() buffer.
|
|
// This returns true if the highWaterMark has not been hit yet,
|
|
// similar to how Writable.write() returns true if you should
|
|
// write() some more.
|
|
Readable.prototype.push = function (chunk, encoding) {
|
|
var state = this._readableState;
|
|
|
|
if (!state.objectMode && typeof chunk === 'string') {
|
|
encoding = encoding || state.defaultEncoding;
|
|
if (encoding !== state.encoding) {
|
|
chunk = new Buffer(chunk, encoding);
|
|
encoding = '';
|
|
}
|
|
}
|
|
|
|
return readableAddChunk(this, state, chunk, encoding, false);
|
|
};
|
|
|
|
// Unshift should *always* be something directly out of read()
|
|
Readable.prototype.unshift = function (chunk) {
|
|
var state = this._readableState;
|
|
return readableAddChunk(this, state, chunk, '', true);
|
|
};
|
|
|
|
Readable.prototype.isPaused = function () {
|
|
return this._readableState.flowing === false;
|
|
};
|
|
|
|
function readableAddChunk(stream, state, chunk, encoding, addToFront) {
|
|
var er = chunkInvalid(state, chunk);
|
|
if (er) {
|
|
stream.emit('error', er);
|
|
} else if (chunk === null) {
|
|
state.reading = false;
|
|
onEofChunk(stream, state);
|
|
} else if (state.objectMode || chunk && chunk.length > 0) {
|
|
if (state.ended && !addToFront) {
|
|
var e = new Error('stream.push() after EOF');
|
|
stream.emit('error', e);
|
|
} else if (state.endEmitted && addToFront) {
|
|
var e = new Error('stream.unshift() after end event');
|
|
stream.emit('error', e);
|
|
} else {
|
|
var skipAdd;
|
|
if (state.decoder && !addToFront && !encoding) {
|
|
chunk = state.decoder.write(chunk);
|
|
skipAdd = !state.objectMode && chunk.length === 0;
|
|
}
|
|
|
|
if (!addToFront) state.reading = false;
|
|
|
|
// Don't add to the buffer if we've decoded to an empty string chunk and
|
|
// we're not in object mode
|
|
if (!skipAdd) {
|
|
// if we want the data now, just emit it.
|
|
if (state.flowing && state.length === 0 && !state.sync) {
|
|
stream.emit('data', chunk);
|
|
stream.read(0);
|
|
} else {
|
|
// update the buffer info.
|
|
state.length += state.objectMode ? 1 : chunk.length;
|
|
if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk);
|
|
|
|
if (state.needReadable) emitReadable(stream);
|
|
}
|
|
}
|
|
|
|
maybeReadMore(stream, state);
|
|
}
|
|
} else if (!addToFront) {
|
|
state.reading = false;
|
|
}
|
|
|
|
return needMoreData(state);
|
|
}
|
|
|
|
// if it's past the high water mark, we can push in some more.
|
|
// Also, if we have no data yet, we can stand some
|
|
// more bytes. This is to work around cases where hwm=0,
|
|
// such as the repl. Also, if the push() triggered a
|
|
// readable event, and the user called read(largeNumber) such that
|
|
// needReadable was set, then we ought to push more, so that another
|
|
// 'readable' event will be triggered.
|
|
function needMoreData(state) {
|
|
return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0);
|
|
}
|
|
|
|
// backwards compatibility.
|
|
Readable.prototype.setEncoding = function (enc) {
|
|
if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder;
|
|
this._readableState.decoder = new StringDecoder(enc);
|
|
this._readableState.encoding = enc;
|
|
return this;
|
|
};
|
|
|
|
// Don't raise the hwm > 8MB
|
|
var MAX_HWM = 0x800000;
|
|
function computeNewHighWaterMark(n) {
|
|
if (n >= MAX_HWM) {
|
|
n = MAX_HWM;
|
|
} else {
|
|
// Get the next highest power of 2
|
|
n--;
|
|
n |= n >>> 1;
|
|
n |= n >>> 2;
|
|
n |= n >>> 4;
|
|
n |= n >>> 8;
|
|
n |= n >>> 16;
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
function howMuchToRead(n, state) {
|
|
if (state.length === 0 && state.ended) return 0;
|
|
|
|
if (state.objectMode) return n === 0 ? 0 : 1;
|
|
|
|
if (n === null || isNaN(n)) {
|
|
// only flow one buffer at a time
|
|
if (state.flowing && state.buffer.length) return state.buffer[0].length;else return state.length;
|
|
}
|
|
|
|
if (n <= 0) return 0;
|
|
|
|
// If we're asking for more than the target buffer level,
|
|
// then raise the water mark. Bump up to the next highest
|
|
// power of 2, to prevent increasing it excessively in tiny
|
|
// amounts.
|
|
if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n);
|
|
|
|
// don't have that much. return null, unless we've ended.
|
|
if (n > state.length) {
|
|
if (!state.ended) {
|
|
state.needReadable = true;
|
|
return 0;
|
|
} else {
|
|
return state.length;
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
// you can override either this method, or the async _read(n) below.
|
|
Readable.prototype.read = function (n) {
|
|
debug('read', n);
|
|
var state = this._readableState;
|
|
var nOrig = n;
|
|
|
|
if (typeof n !== 'number' || n > 0) state.emittedReadable = false;
|
|
|
|
// if we're doing read(0) to trigger a readable event, but we
|
|
// already have a bunch of data in the buffer, then just trigger
|
|
// the 'readable' event and move on.
|
|
if (n === 0 && state.needReadable && (state.length >= state.highWaterMark || state.ended)) {
|
|
debug('read: emitReadable', state.length, state.ended);
|
|
if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this);
|
|
return null;
|
|
}
|
|
|
|
n = howMuchToRead(n, state);
|
|
|
|
// if we've ended, and we're now clear, then finish it up.
|
|
if (n === 0 && state.ended) {
|
|
if (state.length === 0) endReadable(this);
|
|
return null;
|
|
}
|
|
|
|
// All the actual chunk generation logic needs to be
|
|
// *below* the call to _read. The reason is that in certain
|
|
// synthetic stream cases, such as passthrough streams, _read
|
|
// may be a completely synchronous operation which may change
|
|
// the state of the read buffer, providing enough data when
|
|
// before there was *not* enough.
|
|
//
|
|
// So, the steps are:
|
|
// 1. Figure out what the state of things will be after we do
|
|
// a read from the buffer.
|
|
//
|
|
// 2. If that resulting state will trigger a _read, then call _read.
|
|
// Note that this may be asynchronous, or synchronous. Yes, it is
|
|
// deeply ugly to write APIs this way, but that still doesn't mean
|
|
// that the Readable class should behave improperly, as streams are
|
|
// designed to be sync/async agnostic.
|
|
// Take note if the _read call is sync or async (ie, if the read call
|
|
// has returned yet), so that we know whether or not it's safe to emit
|
|
// 'readable' etc.
|
|
//
|
|
// 3. Actually pull the requested chunks out of the buffer and return.
|
|
|
|
// if we need a readable event, then we need to do some reading.
|
|
var doRead = state.needReadable;
|
|
debug('need readable', doRead);
|
|
|
|
// if we currently have less than the highWaterMark, then also read some
|
|
if (state.length === 0 || state.length - n < state.highWaterMark) {
|
|
doRead = true;
|
|
debug('length less than watermark', doRead);
|
|
}
|
|
|
|
// however, if we've ended, then there's no point, and if we're already
|
|
// reading, then it's unnecessary.
|
|
if (state.ended || state.reading) {
|
|
doRead = false;
|
|
debug('reading or ended', doRead);
|
|
}
|
|
|
|
if (doRead) {
|
|
debug('do read');
|
|
state.reading = true;
|
|
state.sync = true;
|
|
// if the length is currently zero, then we *need* a readable event.
|
|
if (state.length === 0) state.needReadable = true;
|
|
// call internal read method
|
|
this._read(state.highWaterMark);
|
|
state.sync = false;
|
|
}
|
|
|
|
// If _read pushed data synchronously, then `reading` will be false,
|
|
// and we need to re-evaluate how much data we can return to the user.
|
|
if (doRead && !state.reading) n = howMuchToRead(nOrig, state);
|
|
|
|
var ret;
|
|
if (n > 0) ret = fromList(n, state);else ret = null;
|
|
|
|
if (ret === null) {
|
|
state.needReadable = true;
|
|
n = 0;
|
|
}
|
|
|
|
state.length -= n;
|
|
|
|
// If we have nothing in the buffer, then we want to know
|
|
// as soon as we *do* get something into the buffer.
|
|
if (state.length === 0 && !state.ended) state.needReadable = true;
|
|
|
|
// If we tried to read() past the EOF, then emit end on the next tick.
|
|
if (nOrig !== n && state.ended && state.length === 0) endReadable(this);
|
|
|
|
if (ret !== null) this.emit('data', ret);
|
|
|
|
return ret;
|
|
};
|
|
|
|
function chunkInvalid(state, chunk) {
|
|
var er = null;
|
|
if (!Buffer.isBuffer(chunk) && typeof chunk !== 'string' && chunk !== null && chunk !== undefined && !state.objectMode) {
|
|
er = new TypeError('Invalid non-string/buffer chunk');
|
|
}
|
|
return er;
|
|
}
|
|
|
|
function onEofChunk(stream, state) {
|
|
if (state.ended) return;
|
|
if (state.decoder) {
|
|
var chunk = state.decoder.end();
|
|
if (chunk && chunk.length) {
|
|
state.buffer.push(chunk);
|
|
state.length += state.objectMode ? 1 : chunk.length;
|
|
}
|
|
}
|
|
state.ended = true;
|
|
|
|
// emit 'readable' now to make sure it gets picked up.
|
|
emitReadable(stream);
|
|
}
|
|
|
|
// Don't emit readable right away in sync mode, because this can trigger
|
|
// another read() call => stack overflow. This way, it might trigger
|
|
// a nextTick recursion warning, but that's not so bad.
|
|
function emitReadable(stream) {
|
|
var state = stream._readableState;
|
|
state.needReadable = false;
|
|
if (!state.emittedReadable) {
|
|
debug('emitReadable', state.flowing);
|
|
state.emittedReadable = true;
|
|
if (state.sync) processNextTick(emitReadable_, stream);else emitReadable_(stream);
|
|
}
|
|
}
|
|
|
|
function emitReadable_(stream) {
|
|
debug('emit readable');
|
|
stream.emit('readable');
|
|
flow(stream);
|
|
}
|
|
|
|
// at this point, the user has presumably seen the 'readable' event,
|
|
// and called read() to consume some data. that may have triggered
|
|
// in turn another _read(n) call, in which case reading = true if
|
|
// it's in progress.
|
|
// However, if we're not ended, or reading, and the length < hwm,
|
|
// then go ahead and try to read some more preemptively.
|
|
function maybeReadMore(stream, state) {
|
|
if (!state.readingMore) {
|
|
state.readingMore = true;
|
|
processNextTick(maybeReadMore_, stream, state);
|
|
}
|
|
}
|
|
|
|
function maybeReadMore_(stream, state) {
|
|
var len = state.length;
|
|
while (!state.reading && !state.flowing && !state.ended && state.length < state.highWaterMark) {
|
|
debug('maybeReadMore read 0');
|
|
stream.read(0);
|
|
if (len === state.length)
|
|
// didn't get any data, stop spinning.
|
|
break;else len = state.length;
|
|
}
|
|
state.readingMore = false;
|
|
}
|
|
|
|
// abstract method. to be overridden in specific implementation classes.
|
|
// call cb(er, data) where data is <= n in length.
|
|
// for virtual (non-string, non-buffer) streams, "length" is somewhat
|
|
// arbitrary, and perhaps not very meaningful.
|
|
Readable.prototype._read = function (n) {
|
|
this.emit('error', new Error('not implemented'));
|
|
};
|
|
|
|
Readable.prototype.pipe = function (dest, pipeOpts) {
|
|
var src = this;
|
|
var state = this._readableState;
|
|
|
|
switch (state.pipesCount) {
|
|
case 0:
|
|
state.pipes = dest;
|
|
break;
|
|
case 1:
|
|
state.pipes = [state.pipes, dest];
|
|
break;
|
|
default:
|
|
state.pipes.push(dest);
|
|
break;
|
|
}
|
|
state.pipesCount += 1;
|
|
debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts);
|
|
|
|
var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr;
|
|
|
|
var endFn = doEnd ? onend : cleanup;
|
|
if (state.endEmitted) processNextTick(endFn);else src.once('end', endFn);
|
|
|
|
dest.on('unpipe', onunpipe);
|
|
function onunpipe(readable) {
|
|
debug('onunpipe');
|
|
if (readable === src) {
|
|
cleanup();
|
|
}
|
|
}
|
|
|
|
function onend() {
|
|
debug('onend');
|
|
dest.end();
|
|
}
|
|
|
|
// when the dest drains, it reduces the awaitDrain counter
|
|
// on the source. This would be more elegant with a .once()
|
|
// handler in flow(), but adding and removing repeatedly is
|
|
// too slow.
|
|
var ondrain = pipeOnDrain(src);
|
|
dest.on('drain', ondrain);
|
|
|
|
var cleanedUp = false;
|
|
function cleanup() {
|
|
debug('cleanup');
|
|
// cleanup event handlers once the pipe is broken
|
|
dest.removeListener('close', onclose);
|
|
dest.removeListener('finish', onfinish);
|
|
dest.removeListener('drain', ondrain);
|
|
dest.removeListener('error', onerror);
|
|
dest.removeListener('unpipe', onunpipe);
|
|
src.removeListener('end', onend);
|
|
src.removeListener('end', cleanup);
|
|
src.removeListener('data', ondata);
|
|
|
|
cleanedUp = true;
|
|
|
|
// if the reader is waiting for a drain event from this
|
|
// specific writer, then it would cause it to never start
|
|
// flowing again.
|
|
// So, if this is awaiting a drain, then we just call it now.
|
|
// If we don't know, then assume that we are waiting for one.
|
|
if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain();
|
|
}
|
|
|
|
src.on('data', ondata);
|
|
function ondata(chunk) {
|
|
debug('ondata');
|
|
var ret = dest.write(chunk);
|
|
if (false === ret) {
|
|
// If the user unpiped during `dest.write()`, it is possible
|
|
// to get stuck in a permanently paused state if that write
|
|
// also returned false.
|
|
if (state.pipesCount === 1 && state.pipes[0] === dest && src.listenerCount('data') === 1 && !cleanedUp) {
|
|
debug('false write response, pause', src._readableState.awaitDrain);
|
|
src._readableState.awaitDrain++;
|
|
}
|
|
src.pause();
|
|
}
|
|
}
|
|
|
|
// if the dest has an error, then stop piping into it.
|
|
// however, don't suppress the throwing behavior for this.
|
|
function onerror(er) {
|
|
debug('onerror', er);
|
|
unpipe();
|
|
dest.removeListener('error', onerror);
|
|
if (EElistenerCount(dest, 'error') === 0) dest.emit('error', er);
|
|
}
|
|
// This is a brutally ugly hack to make sure that our error handler
|
|
// is attached before any userland ones. NEVER DO THIS.
|
|
if (!dest._events || !dest._events.error) dest.on('error', onerror);else if (isArray(dest._events.error)) dest._events.error.unshift(onerror);else dest._events.error = [onerror, dest._events.error];
|
|
|
|
// Both close and finish should trigger unpipe, but only once.
|
|
function onclose() {
|
|
dest.removeListener('finish', onfinish);
|
|
unpipe();
|
|
}
|
|
dest.once('close', onclose);
|
|
function onfinish() {
|
|
debug('onfinish');
|
|
dest.removeListener('close', onclose);
|
|
unpipe();
|
|
}
|
|
dest.once('finish', onfinish);
|
|
|
|
function unpipe() {
|
|
debug('unpipe');
|
|
src.unpipe(dest);
|
|
}
|
|
|
|
// tell the dest that it's being piped to
|
|
dest.emit('pipe', src);
|
|
|
|
// start the flow if it hasn't been started already.
|
|
if (!state.flowing) {
|
|
debug('pipe resume');
|
|
src.resume();
|
|
}
|
|
|
|
return dest;
|
|
};
|
|
|
|
function pipeOnDrain(src) {
|
|
return function () {
|
|
var state = src._readableState;
|
|
debug('pipeOnDrain', state.awaitDrain);
|
|
if (state.awaitDrain) state.awaitDrain--;
|
|
if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) {
|
|
state.flowing = true;
|
|
flow(src);
|
|
}
|
|
};
|
|
}
|
|
|
|
Readable.prototype.unpipe = function (dest) {
|
|
var state = this._readableState;
|
|
|
|
// if we're not piping anywhere, then do nothing.
|
|
if (state.pipesCount === 0) return this;
|
|
|
|
// just one destination. most common case.
|
|
if (state.pipesCount === 1) {
|
|
// passed in one, but it's not the right one.
|
|
if (dest && dest !== state.pipes) return this;
|
|
|
|
if (!dest) dest = state.pipes;
|
|
|
|
// got a match.
|
|
state.pipes = null;
|
|
state.pipesCount = 0;
|
|
state.flowing = false;
|
|
if (dest) dest.emit('unpipe', this);
|
|
return this;
|
|
}
|
|
|
|
// slow case. multiple pipe destinations.
|
|
|
|
if (!dest) {
|
|
// remove all.
|
|
var dests = state.pipes;
|
|
var len = state.pipesCount;
|
|
state.pipes = null;
|
|
state.pipesCount = 0;
|
|
state.flowing = false;
|
|
|
|
for (var _i = 0; _i < len; _i++) {
|
|
dests[_i].emit('unpipe', this);
|
|
}return this;
|
|
}
|
|
|
|
// try to find the right one.
|
|
var i = indexOf(state.pipes, dest);
|
|
if (i === -1) return this;
|
|
|
|
state.pipes.splice(i, 1);
|
|
state.pipesCount -= 1;
|
|
if (state.pipesCount === 1) state.pipes = state.pipes[0];
|
|
|
|
dest.emit('unpipe', this);
|
|
|
|
return this;
|
|
};
|
|
|
|
// set up data events if they are asked for
|
|
// Ensure readable listeners eventually get something
|
|
Readable.prototype.on = function (ev, fn) {
|
|
var res = Stream.prototype.on.call(this, ev, fn);
|
|
|
|
// If listening to data, and it has not explicitly been paused,
|
|
// then call resume to start the flow of data on the next tick.
|
|
if (ev === 'data' && false !== this._readableState.flowing) {
|
|
this.resume();
|
|
}
|
|
|
|
if (ev === 'readable' && !this._readableState.endEmitted) {
|
|
var state = this._readableState;
|
|
if (!state.readableListening) {
|
|
state.readableListening = true;
|
|
state.emittedReadable = false;
|
|
state.needReadable = true;
|
|
if (!state.reading) {
|
|
processNextTick(nReadingNextTick, this);
|
|
} else if (state.length) {
|
|
emitReadable(this, state);
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
};
|
|
Readable.prototype.addListener = Readable.prototype.on;
|
|
|
|
function nReadingNextTick(self) {
|
|
debug('readable nexttick read 0');
|
|
self.read(0);
|
|
}
|
|
|
|
// pause() and resume() are remnants of the legacy readable stream API
|
|
// If the user uses them, then switch into old mode.
|
|
Readable.prototype.resume = function () {
|
|
var state = this._readableState;
|
|
if (!state.flowing) {
|
|
debug('resume');
|
|
state.flowing = true;
|
|
resume(this, state);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
function resume(stream, state) {
|
|
if (!state.resumeScheduled) {
|
|
state.resumeScheduled = true;
|
|
processNextTick(resume_, stream, state);
|
|
}
|
|
}
|
|
|
|
function resume_(stream, state) {
|
|
if (!state.reading) {
|
|
debug('resume read 0');
|
|
stream.read(0);
|
|
}
|
|
|
|
state.resumeScheduled = false;
|
|
stream.emit('resume');
|
|
flow(stream);
|
|
if (state.flowing && !state.reading) stream.read(0);
|
|
}
|
|
|
|
Readable.prototype.pause = function () {
|
|
debug('call pause flowing=%j', this._readableState.flowing);
|
|
if (false !== this._readableState.flowing) {
|
|
debug('pause');
|
|
this._readableState.flowing = false;
|
|
this.emit('pause');
|
|
}
|
|
return this;
|
|
};
|
|
|
|
function flow(stream) {
|
|
var state = stream._readableState;
|
|
debug('flow', state.flowing);
|
|
if (state.flowing) {
|
|
do {
|
|
var chunk = stream.read();
|
|
} while (null !== chunk && state.flowing);
|
|
}
|
|
}
|
|
|
|
// wrap an old-style stream as the async data source.
|
|
// This is *not* part of the readable stream interface.
|
|
// It is an ugly unfortunate mess of history.
|
|
Readable.prototype.wrap = function (stream) {
|
|
var state = this._readableState;
|
|
var paused = false;
|
|
|
|
var self = this;
|
|
stream.on('end', function () {
|
|
debug('wrapped end');
|
|
if (state.decoder && !state.ended) {
|
|
var chunk = state.decoder.end();
|
|
if (chunk && chunk.length) self.push(chunk);
|
|
}
|
|
|
|
self.push(null);
|
|
});
|
|
|
|
stream.on('data', function (chunk) {
|
|
debug('wrapped data');
|
|
if (state.decoder) chunk = state.decoder.write(chunk);
|
|
|
|
// don't skip over falsy values in objectMode
|
|
if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return;
|
|
|
|
var ret = self.push(chunk);
|
|
if (!ret) {
|
|
paused = true;
|
|
stream.pause();
|
|
}
|
|
});
|
|
|
|
// proxy all the other methods.
|
|
// important when wrapping filters and duplexes.
|
|
for (var i in stream) {
|
|
if (this[i] === undefined && typeof stream[i] === 'function') {
|
|
this[i] = function (method) {
|
|
return function () {
|
|
return stream[method].apply(stream, arguments);
|
|
};
|
|
}(i);
|
|
}
|
|
}
|
|
|
|
// proxy certain important events.
|
|
var events = ['error', 'close', 'destroy', 'pause', 'resume'];
|
|
forEach(events, function (ev) {
|
|
stream.on(ev, self.emit.bind(self, ev));
|
|
});
|
|
|
|
// when we try to consume some more bytes, simply unpause the
|
|
// underlying stream.
|
|
self._read = function (n) {
|
|
debug('wrapped _read', n);
|
|
if (paused) {
|
|
paused = false;
|
|
stream.resume();
|
|
}
|
|
};
|
|
|
|
return self;
|
|
};
|
|
|
|
// exposed for testing purposes only.
|
|
Readable._fromList = fromList;
|
|
|
|
// Pluck off n bytes from an array of buffers.
|
|
// Length is the combined lengths of all the buffers in the list.
|
|
function fromList(n, state) {
|
|
var list = state.buffer;
|
|
var length = state.length;
|
|
var stringMode = !!state.decoder;
|
|
var objectMode = !!state.objectMode;
|
|
var ret;
|
|
|
|
// nothing in the list, definitely empty.
|
|
if (list.length === 0) return null;
|
|
|
|
if (length === 0) ret = null;else if (objectMode) ret = list.shift();else if (!n || n >= length) {
|
|
// read it all, truncate the array.
|
|
if (stringMode) ret = list.join('');else if (list.length === 1) ret = list[0];else ret = Buffer.concat(list, length);
|
|
list.length = 0;
|
|
} else {
|
|
// read just some of it.
|
|
if (n < list[0].length) {
|
|
// just take a part of the first list item.
|
|
// slice is the same for buffers and strings.
|
|
var buf = list[0];
|
|
ret = buf.slice(0, n);
|
|
list[0] = buf.slice(n);
|
|
} else if (n === list[0].length) {
|
|
// first list is a perfect match
|
|
ret = list.shift();
|
|
} else {
|
|
// complex case.
|
|
// we have enough to cover it, but it spans past the first buffer.
|
|
if (stringMode) ret = '';else ret = new Buffer(n);
|
|
|
|
var c = 0;
|
|
for (var i = 0, l = list.length; i < l && c < n; i++) {
|
|
var buf = list[0];
|
|
var cpy = Math.min(n - c, buf.length);
|
|
|
|
if (stringMode) ret += buf.slice(0, cpy);else buf.copy(ret, c, 0, cpy);
|
|
|
|
if (cpy < buf.length) list[0] = buf.slice(cpy);else list.shift();
|
|
|
|
c += cpy;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
function endReadable(stream) {
|
|
var state = stream._readableState;
|
|
|
|
// If we get here before consuming all the bytes, then that is a
|
|
// bug in node. Should never happen.
|
|
if (state.length > 0) throw new Error('endReadable called on non-empty stream');
|
|
|
|
if (!state.endEmitted) {
|
|
state.ended = true;
|
|
processNextTick(endReadableNT, state, stream);
|
|
}
|
|
}
|
|
|
|
function endReadableNT(state, stream) {
|
|
// Check that we didn't get one last unshift.
|
|
if (!state.endEmitted && state.length === 0) {
|
|
state.endEmitted = true;
|
|
stream.readable = false;
|
|
stream.emit('end');
|
|
}
|
|
}
|
|
|
|
function forEach(xs, f) {
|
|
for (var i = 0, l = xs.length; i < l; i++) {
|
|
f(xs[i], i);
|
|
}
|
|
}
|
|
|
|
function indexOf(xs, x) {
|
|
for (var i = 0, l = xs.length; i < l; i++) {
|
|
if (xs[i] === x) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
}).call(this,require('_process'))
|
|
},{"./_stream_duplex":126,"_process":133,"buffer":43,"core-util-is":44,"events":56,"inherits":123,"isarray":89,"process-nextick-args":132,"string_decoder/":279,"util":41}],128:[function(require,module,exports){
|
|
// a transform stream is a readable/writable stream where you do
|
|
// something with the data. Sometimes it's called a "filter",
|
|
// but that's not a great name for it, since that implies a thing where
|
|
// some bits pass through, and others are simply ignored. (That would
|
|
// be a valid example of a transform, of course.)
|
|
//
|
|
// While the output is causally related to the input, it's not a
|
|
// necessarily symmetric or synchronous transformation. For example,
|
|
// a zlib stream might take multiple plain-text writes(), and then
|
|
// emit a single compressed chunk some time in the future.
|
|
//
|
|
// Here's how this works:
|
|
//
|
|
// The Transform stream has all the aspects of the readable and writable
|
|
// stream classes. When you write(chunk), that calls _write(chunk,cb)
|
|
// internally, and returns false if there's a lot of pending writes
|
|
// buffered up. When you call read(), that calls _read(n) until
|
|
// there's enough pending readable data buffered up.
|
|
//
|
|
// In a transform stream, the written data is placed in a buffer. When
|
|
// _read(n) is called, it transforms the queued up data, calling the
|
|
// buffered _write cb's as it consumes chunks. If consuming a single
|
|
// written chunk would result in multiple output chunks, then the first
|
|
// outputted bit calls the readcb, and subsequent chunks just go into
|
|
// the read buffer, and will cause it to emit 'readable' if necessary.
|
|
//
|
|
// This way, back-pressure is actually determined by the reading side,
|
|
// since _read has to be called to start processing a new chunk. However,
|
|
// a pathological inflate type of transform can cause excessive buffering
|
|
// here. For example, imagine a stream where every byte of input is
|
|
// interpreted as an integer from 0-255, and then results in that many
|
|
// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
|
|
// 1kb of data being output. In this case, you could write a very small
|
|
// amount of input, and end up with a very large amount of output. In
|
|
// such a pathological inflating mechanism, there'd be no way to tell
|
|
// the system to stop doing the transform. A single 4MB write could
|
|
// cause the system to run out of memory.
|
|
//
|
|
// However, even in such a pathological case, only a single written chunk
|
|
// would be consumed, and then the rest would wait (un-transformed) until
|
|
// the results of the previous transformed chunk were consumed.
|
|
|
|
'use strict';
|
|
|
|
module.exports = Transform;
|
|
|
|
var Duplex = require('./_stream_duplex');
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
util.inherits(Transform, Duplex);
|
|
|
|
function TransformState(stream) {
|
|
this.afterTransform = function (er, data) {
|
|
return afterTransform(stream, er, data);
|
|
};
|
|
|
|
this.needTransform = false;
|
|
this.transforming = false;
|
|
this.writecb = null;
|
|
this.writechunk = null;
|
|
this.writeencoding = null;
|
|
}
|
|
|
|
function afterTransform(stream, er, data) {
|
|
var ts = stream._transformState;
|
|
ts.transforming = false;
|
|
|
|
var cb = ts.writecb;
|
|
|
|
if (!cb) return stream.emit('error', new Error('no writecb in Transform class'));
|
|
|
|
ts.writechunk = null;
|
|
ts.writecb = null;
|
|
|
|
if (data !== null && data !== undefined) stream.push(data);
|
|
|
|
cb(er);
|
|
|
|
var rs = stream._readableState;
|
|
rs.reading = false;
|
|
if (rs.needReadable || rs.length < rs.highWaterMark) {
|
|
stream._read(rs.highWaterMark);
|
|
}
|
|
}
|
|
|
|
function Transform(options) {
|
|
if (!(this instanceof Transform)) return new Transform(options);
|
|
|
|
Duplex.call(this, options);
|
|
|
|
this._transformState = new TransformState(this);
|
|
|
|
// when the writable side finishes, then flush out anything remaining.
|
|
var stream = this;
|
|
|
|
// start out asking for a readable event once data is transformed.
|
|
this._readableState.needReadable = true;
|
|
|
|
// we have implemented the _read method, and done the other things
|
|
// that Readable wants before the first _read call, so unset the
|
|
// sync guard flag.
|
|
this._readableState.sync = false;
|
|
|
|
if (options) {
|
|
if (typeof options.transform === 'function') this._transform = options.transform;
|
|
|
|
if (typeof options.flush === 'function') this._flush = options.flush;
|
|
}
|
|
|
|
this.once('prefinish', function () {
|
|
if (typeof this._flush === 'function') this._flush(function (er) {
|
|
done(stream, er);
|
|
});else done(stream);
|
|
});
|
|
}
|
|
|
|
Transform.prototype.push = function (chunk, encoding) {
|
|
this._transformState.needTransform = false;
|
|
return Duplex.prototype.push.call(this, chunk, encoding);
|
|
};
|
|
|
|
// This is the part where you do stuff!
|
|
// override this function in implementation classes.
|
|
// 'chunk' is an input chunk.
|
|
//
|
|
// Call `push(newChunk)` to pass along transformed output
|
|
// to the readable side. You may call 'push' zero or more times.
|
|
//
|
|
// Call `cb(err)` when you are done with this chunk. If you pass
|
|
// an error, then that'll put the hurt on the whole operation. If you
|
|
// never call cb(), then you'll never get another chunk.
|
|
Transform.prototype._transform = function (chunk, encoding, cb) {
|
|
throw new Error('not implemented');
|
|
};
|
|
|
|
Transform.prototype._write = function (chunk, encoding, cb) {
|
|
var ts = this._transformState;
|
|
ts.writecb = cb;
|
|
ts.writechunk = chunk;
|
|
ts.writeencoding = encoding;
|
|
if (!ts.transforming) {
|
|
var rs = this._readableState;
|
|
if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark);
|
|
}
|
|
};
|
|
|
|
// Doesn't matter what the args are here.
|
|
// _transform does all the work.
|
|
// That we got here means that the readable side wants more data.
|
|
Transform.prototype._read = function (n) {
|
|
var ts = this._transformState;
|
|
|
|
if (ts.writechunk !== null && ts.writecb && !ts.transforming) {
|
|
ts.transforming = true;
|
|
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
|
|
} else {
|
|
// mark that we need a transform, so that any data that comes in
|
|
// will get processed, now that we've asked for it.
|
|
ts.needTransform = true;
|
|
}
|
|
};
|
|
|
|
function done(stream, er) {
|
|
if (er) return stream.emit('error', er);
|
|
|
|
// if there's nothing in the write buffer, then that means
|
|
// that nothing more will ever be provided
|
|
var ws = stream._writableState;
|
|
var ts = stream._transformState;
|
|
|
|
if (ws.length) throw new Error('calling transform done when ws.length != 0');
|
|
|
|
if (ts.transforming) throw new Error('calling transform done when still transforming');
|
|
|
|
return stream.push(null);
|
|
}
|
|
},{"./_stream_duplex":126,"core-util-is":44,"inherits":123}],129:[function(require,module,exports){
|
|
(function (process){
|
|
// A bit simpler than readable streams.
|
|
// Implement an async ._write(chunk, encoding, cb), and it'll handle all
|
|
// the drain event emission and buffering.
|
|
|
|
'use strict';
|
|
|
|
module.exports = Writable;
|
|
|
|
/*<replacement>*/
|
|
var processNextTick = require('process-nextick-args');
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : processNextTick;
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var Buffer = require('buffer').Buffer;
|
|
/*</replacement>*/
|
|
|
|
Writable.WritableState = WritableState;
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var internalUtil = {
|
|
deprecate: require('util-deprecate')
|
|
};
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var Stream;
|
|
(function () {
|
|
try {
|
|
Stream = require('st' + 'ream');
|
|
} catch (_) {} finally {
|
|
if (!Stream) Stream = require('events').EventEmitter;
|
|
}
|
|
})();
|
|
/*</replacement>*/
|
|
|
|
var Buffer = require('buffer').Buffer;
|
|
|
|
util.inherits(Writable, Stream);
|
|
|
|
function nop() {}
|
|
|
|
function WriteReq(chunk, encoding, cb) {
|
|
this.chunk = chunk;
|
|
this.encoding = encoding;
|
|
this.callback = cb;
|
|
this.next = null;
|
|
}
|
|
|
|
var Duplex;
|
|
function WritableState(options, stream) {
|
|
Duplex = Duplex || require('./_stream_duplex');
|
|
|
|
options = options || {};
|
|
|
|
// object stream flag to indicate whether or not this stream
|
|
// contains buffers or objects.
|
|
this.objectMode = !!options.objectMode;
|
|
|
|
if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.writableObjectMode;
|
|
|
|
// the point at which write() starts returning false
|
|
// Note: 0 is a valid value, means that we always return false if
|
|
// the entire buffer is not flushed immediately on write()
|
|
var hwm = options.highWaterMark;
|
|
var defaultHwm = this.objectMode ? 16 : 16 * 1024;
|
|
this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm;
|
|
|
|
// cast to ints.
|
|
this.highWaterMark = ~ ~this.highWaterMark;
|
|
|
|
this.needDrain = false;
|
|
// at the start of calling end()
|
|
this.ending = false;
|
|
// when end() has been called, and returned
|
|
this.ended = false;
|
|
// when 'finish' is emitted
|
|
this.finished = false;
|
|
|
|
// should we decode strings into buffers before passing to _write?
|
|
// this is here so that some node-core streams can optimize string
|
|
// handling at a lower level.
|
|
var noDecode = options.decodeStrings === false;
|
|
this.decodeStrings = !noDecode;
|
|
|
|
// Crypto is kind of old and crusty. Historically, its default string
|
|
// encoding is 'binary' so we have to make this configurable.
|
|
// Everything else in the universe uses 'utf8', though.
|
|
this.defaultEncoding = options.defaultEncoding || 'utf8';
|
|
|
|
// not an actual buffer we keep track of, but a measurement
|
|
// of how much we're waiting to get pushed to some underlying
|
|
// socket or file.
|
|
this.length = 0;
|
|
|
|
// a flag to see when we're in the middle of a write.
|
|
this.writing = false;
|
|
|
|
// when true all writes will be buffered until .uncork() call
|
|
this.corked = 0;
|
|
|
|
// a flag to be able to tell if the onwrite cb is called immediately,
|
|
// or on a later tick. We set this to true at first, because any
|
|
// actions that shouldn't happen until "later" should generally also
|
|
// not happen before the first write call.
|
|
this.sync = true;
|
|
|
|
// a flag to know if we're processing previously buffered items, which
|
|
// may call the _write() callback in the same tick, so that we don't
|
|
// end up in an overlapped onwrite situation.
|
|
this.bufferProcessing = false;
|
|
|
|
// the callback that's passed to _write(chunk,cb)
|
|
this.onwrite = function (er) {
|
|
onwrite(stream, er);
|
|
};
|
|
|
|
// the callback that the user supplies to write(chunk,encoding,cb)
|
|
this.writecb = null;
|
|
|
|
// the amount that is being written when _write is called.
|
|
this.writelen = 0;
|
|
|
|
this.bufferedRequest = null;
|
|
this.lastBufferedRequest = null;
|
|
|
|
// number of pending user-supplied write callbacks
|
|
// this must be 0 before 'finish' can be emitted
|
|
this.pendingcb = 0;
|
|
|
|
// emit prefinish if the only thing we're waiting for is _write cbs
|
|
// This is relevant for synchronous Transform streams
|
|
this.prefinished = false;
|
|
|
|
// True if the error was already emitted and should not be thrown again
|
|
this.errorEmitted = false;
|
|
|
|
// count buffered requests
|
|
this.bufferedRequestCount = 0;
|
|
|
|
// create the two objects needed to store the corked requests
|
|
// they are not a linked list, as no new elements are inserted in there
|
|
this.corkedRequestsFree = new CorkedRequest(this);
|
|
this.corkedRequestsFree.next = new CorkedRequest(this);
|
|
}
|
|
|
|
WritableState.prototype.getBuffer = function writableStateGetBuffer() {
|
|
var current = this.bufferedRequest;
|
|
var out = [];
|
|
while (current) {
|
|
out.push(current);
|
|
current = current.next;
|
|
}
|
|
return out;
|
|
};
|
|
|
|
(function () {
|
|
try {
|
|
Object.defineProperty(WritableState.prototype, 'buffer', {
|
|
get: internalUtil.deprecate(function () {
|
|
return this.getBuffer();
|
|
}, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.')
|
|
});
|
|
} catch (_) {}
|
|
})();
|
|
|
|
var Duplex;
|
|
function Writable(options) {
|
|
Duplex = Duplex || require('./_stream_duplex');
|
|
|
|
// Writable ctor is applied to Duplexes, though they're not
|
|
// instanceof Writable, they're instanceof Readable.
|
|
if (!(this instanceof Writable) && !(this instanceof Duplex)) return new Writable(options);
|
|
|
|
this._writableState = new WritableState(options, this);
|
|
|
|
// legacy.
|
|
this.writable = true;
|
|
|
|
if (options) {
|
|
if (typeof options.write === 'function') this._write = options.write;
|
|
|
|
if (typeof options.writev === 'function') this._writev = options.writev;
|
|
}
|
|
|
|
Stream.call(this);
|
|
}
|
|
|
|
// Otherwise people can pipe Writable streams, which is just wrong.
|
|
Writable.prototype.pipe = function () {
|
|
this.emit('error', new Error('Cannot pipe. Not readable.'));
|
|
};
|
|
|
|
function writeAfterEnd(stream, cb) {
|
|
var er = new Error('write after end');
|
|
// TODO: defer error events consistently everywhere, not just the cb
|
|
stream.emit('error', er);
|
|
processNextTick(cb, er);
|
|
}
|
|
|
|
// If we get something that is not a buffer, string, null, or undefined,
|
|
// and we're not in objectMode, then that's an error.
|
|
// Otherwise stream chunks are all considered to be of length=1, and the
|
|
// watermarks determine how many objects to keep in the buffer, rather than
|
|
// how many bytes or characters.
|
|
function validChunk(stream, state, chunk, cb) {
|
|
var valid = true;
|
|
|
|
if (!Buffer.isBuffer(chunk) && typeof chunk !== 'string' && chunk !== null && chunk !== undefined && !state.objectMode) {
|
|
var er = new TypeError('Invalid non-string/buffer chunk');
|
|
stream.emit('error', er);
|
|
processNextTick(cb, er);
|
|
valid = false;
|
|
}
|
|
return valid;
|
|
}
|
|
|
|
Writable.prototype.write = function (chunk, encoding, cb) {
|
|
var state = this._writableState;
|
|
var ret = false;
|
|
|
|
if (typeof encoding === 'function') {
|
|
cb = encoding;
|
|
encoding = null;
|
|
}
|
|
|
|
if (Buffer.isBuffer(chunk)) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding;
|
|
|
|
if (typeof cb !== 'function') cb = nop;
|
|
|
|
if (state.ended) writeAfterEnd(this, cb);else if (validChunk(this, state, chunk, cb)) {
|
|
state.pendingcb++;
|
|
ret = writeOrBuffer(this, state, chunk, encoding, cb);
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
Writable.prototype.cork = function () {
|
|
var state = this._writableState;
|
|
|
|
state.corked++;
|
|
};
|
|
|
|
Writable.prototype.uncork = function () {
|
|
var state = this._writableState;
|
|
|
|
if (state.corked) {
|
|
state.corked--;
|
|
|
|
if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state);
|
|
}
|
|
};
|
|
|
|
Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {
|
|
// node::ParseEncoding() requires lower case.
|
|
if (typeof encoding === 'string') encoding = encoding.toLowerCase();
|
|
if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding);
|
|
this._writableState.defaultEncoding = encoding;
|
|
};
|
|
|
|
function decodeChunk(state, chunk, encoding) {
|
|
if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') {
|
|
chunk = new Buffer(chunk, encoding);
|
|
}
|
|
return chunk;
|
|
}
|
|
|
|
// if we're already writing something, then just put this
|
|
// in the queue, and wait our turn. Otherwise, call _write
|
|
// If we return false, then we need a drain event, so set that flag.
|
|
function writeOrBuffer(stream, state, chunk, encoding, cb) {
|
|
chunk = decodeChunk(state, chunk, encoding);
|
|
|
|
if (Buffer.isBuffer(chunk)) encoding = 'buffer';
|
|
var len = state.objectMode ? 1 : chunk.length;
|
|
|
|
state.length += len;
|
|
|
|
var ret = state.length < state.highWaterMark;
|
|
// we must ensure that previous needDrain will not be reset to false.
|
|
if (!ret) state.needDrain = true;
|
|
|
|
if (state.writing || state.corked) {
|
|
var last = state.lastBufferedRequest;
|
|
state.lastBufferedRequest = new WriteReq(chunk, encoding, cb);
|
|
if (last) {
|
|
last.next = state.lastBufferedRequest;
|
|
} else {
|
|
state.bufferedRequest = state.lastBufferedRequest;
|
|
}
|
|
state.bufferedRequestCount += 1;
|
|
} else {
|
|
doWrite(stream, state, false, len, chunk, encoding, cb);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
function doWrite(stream, state, writev, len, chunk, encoding, cb) {
|
|
state.writelen = len;
|
|
state.writecb = cb;
|
|
state.writing = true;
|
|
state.sync = true;
|
|
if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite);
|
|
state.sync = false;
|
|
}
|
|
|
|
function onwriteError(stream, state, sync, er, cb) {
|
|
--state.pendingcb;
|
|
if (sync) processNextTick(cb, er);else cb(er);
|
|
|
|
stream._writableState.errorEmitted = true;
|
|
stream.emit('error', er);
|
|
}
|
|
|
|
function onwriteStateUpdate(state) {
|
|
state.writing = false;
|
|
state.writecb = null;
|
|
state.length -= state.writelen;
|
|
state.writelen = 0;
|
|
}
|
|
|
|
function onwrite(stream, er) {
|
|
var state = stream._writableState;
|
|
var sync = state.sync;
|
|
var cb = state.writecb;
|
|
|
|
onwriteStateUpdate(state);
|
|
|
|
if (er) onwriteError(stream, state, sync, er, cb);else {
|
|
// Check if we're actually ready to finish, but don't emit yet
|
|
var finished = needFinish(state);
|
|
|
|
if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) {
|
|
clearBuffer(stream, state);
|
|
}
|
|
|
|
if (sync) {
|
|
/*<replacement>*/
|
|
asyncWrite(afterWrite, stream, state, finished, cb);
|
|
/*</replacement>*/
|
|
} else {
|
|
afterWrite(stream, state, finished, cb);
|
|
}
|
|
}
|
|
}
|
|
|
|
function afterWrite(stream, state, finished, cb) {
|
|
if (!finished) onwriteDrain(stream, state);
|
|
state.pendingcb--;
|
|
cb();
|
|
finishMaybe(stream, state);
|
|
}
|
|
|
|
// Must force callback to be called on nextTick, so that we don't
|
|
// emit 'drain' before the write() consumer gets the 'false' return
|
|
// value, and has a chance to attach a 'drain' listener.
|
|
function onwriteDrain(stream, state) {
|
|
if (state.length === 0 && state.needDrain) {
|
|
state.needDrain = false;
|
|
stream.emit('drain');
|
|
}
|
|
}
|
|
|
|
// if there's something in the buffer waiting, then process it
|
|
function clearBuffer(stream, state) {
|
|
state.bufferProcessing = true;
|
|
var entry = state.bufferedRequest;
|
|
|
|
if (stream._writev && entry && entry.next) {
|
|
// Fast case, write everything using _writev()
|
|
var l = state.bufferedRequestCount;
|
|
var buffer = new Array(l);
|
|
var holder = state.corkedRequestsFree;
|
|
holder.entry = entry;
|
|
|
|
var count = 0;
|
|
while (entry) {
|
|
buffer[count] = entry;
|
|
entry = entry.next;
|
|
count += 1;
|
|
}
|
|
|
|
doWrite(stream, state, true, state.length, buffer, '', holder.finish);
|
|
|
|
// doWrite is always async, defer these to save a bit of time
|
|
// as the hot path ends with doWrite
|
|
state.pendingcb++;
|
|
state.lastBufferedRequest = null;
|
|
state.corkedRequestsFree = holder.next;
|
|
holder.next = null;
|
|
} else {
|
|
// Slow case, write chunks one-by-one
|
|
while (entry) {
|
|
var chunk = entry.chunk;
|
|
var encoding = entry.encoding;
|
|
var cb = entry.callback;
|
|
var len = state.objectMode ? 1 : chunk.length;
|
|
|
|
doWrite(stream, state, false, len, chunk, encoding, cb);
|
|
entry = entry.next;
|
|
// if we didn't call the onwrite immediately, then
|
|
// it means that we need to wait until it does.
|
|
// also, that means that the chunk and cb are currently
|
|
// being processed, so move the buffer counter past them.
|
|
if (state.writing) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (entry === null) state.lastBufferedRequest = null;
|
|
}
|
|
|
|
state.bufferedRequestCount = 0;
|
|
state.bufferedRequest = entry;
|
|
state.bufferProcessing = false;
|
|
}
|
|
|
|
Writable.prototype._write = function (chunk, encoding, cb) {
|
|
cb(new Error('not implemented'));
|
|
};
|
|
|
|
Writable.prototype._writev = null;
|
|
|
|
Writable.prototype.end = function (chunk, encoding, cb) {
|
|
var state = this._writableState;
|
|
|
|
if (typeof chunk === 'function') {
|
|
cb = chunk;
|
|
chunk = null;
|
|
encoding = null;
|
|
} else if (typeof encoding === 'function') {
|
|
cb = encoding;
|
|
encoding = null;
|
|
}
|
|
|
|
if (chunk !== null && chunk !== undefined) this.write(chunk, encoding);
|
|
|
|
// .end() fully uncorks
|
|
if (state.corked) {
|
|
state.corked = 1;
|
|
this.uncork();
|
|
}
|
|
|
|
// ignore unnecessary end() calls.
|
|
if (!state.ending && !state.finished) endWritable(this, state, cb);
|
|
};
|
|
|
|
function needFinish(state) {
|
|
return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing;
|
|
}
|
|
|
|
function prefinish(stream, state) {
|
|
if (!state.prefinished) {
|
|
state.prefinished = true;
|
|
stream.emit('prefinish');
|
|
}
|
|
}
|
|
|
|
function finishMaybe(stream, state) {
|
|
var need = needFinish(state);
|
|
if (need) {
|
|
if (state.pendingcb === 0) {
|
|
prefinish(stream, state);
|
|
state.finished = true;
|
|
stream.emit('finish');
|
|
} else {
|
|
prefinish(stream, state);
|
|
}
|
|
}
|
|
return need;
|
|
}
|
|
|
|
function endWritable(stream, state, cb) {
|
|
state.ending = true;
|
|
finishMaybe(stream, state);
|
|
if (cb) {
|
|
if (state.finished) processNextTick(cb);else stream.once('finish', cb);
|
|
}
|
|
state.ended = true;
|
|
stream.writable = false;
|
|
}
|
|
|
|
// It seems a linked list but it is not
|
|
// there will be only 2 of these for each stream
|
|
function CorkedRequest(state) {
|
|
var _this = this;
|
|
|
|
this.next = null;
|
|
this.entry = null;
|
|
|
|
this.finish = function (err) {
|
|
var entry = _this.entry;
|
|
_this.entry = null;
|
|
while (entry) {
|
|
var cb = entry.callback;
|
|
state.pendingcb--;
|
|
cb(err);
|
|
entry = entry.next;
|
|
}
|
|
if (state.corkedRequestsFree) {
|
|
state.corkedRequestsFree.next = _this;
|
|
} else {
|
|
state.corkedRequestsFree = _this;
|
|
}
|
|
};
|
|
}
|
|
}).call(this,require('_process'))
|
|
},{"./_stream_duplex":126,"_process":133,"buffer":43,"core-util-is":44,"events":56,"inherits":123,"process-nextick-args":132,"util-deprecate":294}],130:[function(require,module,exports){
|
|
module.exports = require("./lib/_stream_transform.js")
|
|
|
|
},{"./lib/_stream_transform.js":128}],131:[function(require,module,exports){
|
|
(function (process){
|
|
var Transform = require('readable-stream/transform')
|
|
, inherits = require('util').inherits
|
|
, xtend = require('xtend')
|
|
|
|
function DestroyableTransform(opts) {
|
|
Transform.call(this, opts)
|
|
this._destroyed = false
|
|
}
|
|
|
|
inherits(DestroyableTransform, Transform)
|
|
|
|
DestroyableTransform.prototype.destroy = function(err) {
|
|
if (this._destroyed) return
|
|
this._destroyed = true
|
|
|
|
var self = this
|
|
process.nextTick(function() {
|
|
if (err)
|
|
self.emit('error', err)
|
|
self.emit('close')
|
|
})
|
|
}
|
|
|
|
// a noop _transform function
|
|
function noop (chunk, enc, callback) {
|
|
callback(null, chunk)
|
|
}
|
|
|
|
|
|
// create a new export function, used by both the main export and
|
|
// the .ctor export, contains common logic for dealing with arguments
|
|
function through2 (construct) {
|
|
return function (options, transform, flush) {
|
|
if (typeof options == 'function') {
|
|
flush = transform
|
|
transform = options
|
|
options = {}
|
|
}
|
|
|
|
if (typeof transform != 'function')
|
|
transform = noop
|
|
|
|
if (typeof flush != 'function')
|
|
flush = null
|
|
|
|
return construct(options, transform, flush)
|
|
}
|
|
}
|
|
|
|
|
|
// main export, just make me a transform stream!
|
|
module.exports = through2(function (options, transform, flush) {
|
|
var t2 = new DestroyableTransform(options)
|
|
|
|
t2._transform = transform
|
|
|
|
if (flush)
|
|
t2._flush = flush
|
|
|
|
return t2
|
|
})
|
|
|
|
|
|
// make me a reusable prototype that I can `new`, or implicitly `new`
|
|
// with a constructor call
|
|
module.exports.ctor = through2(function (options, transform, flush) {
|
|
function Through2 (override) {
|
|
if (!(this instanceof Through2))
|
|
return new Through2(override)
|
|
|
|
this.options = xtend(options, override)
|
|
|
|
DestroyableTransform.call(this, this.options)
|
|
}
|
|
|
|
inherits(Through2, DestroyableTransform)
|
|
|
|
Through2.prototype._transform = transform
|
|
|
|
if (flush)
|
|
Through2.prototype._flush = flush
|
|
|
|
return Through2
|
|
})
|
|
|
|
|
|
module.exports.obj = through2(function (options, transform, flush) {
|
|
var t2 = new DestroyableTransform(xtend({ objectMode: true, highWaterMark: 16 }, options))
|
|
|
|
t2._transform = transform
|
|
|
|
if (flush)
|
|
t2._flush = flush
|
|
|
|
return t2
|
|
})
|
|
|
|
}).call(this,require('_process'))
|
|
},{"_process":133,"readable-stream/transform":130,"util":297,"xtend":299}],132:[function(require,module,exports){
|
|
(function (process){
|
|
'use strict';
|
|
|
|
if (!process.version ||
|
|
process.version.indexOf('v0.') === 0 ||
|
|
process.version.indexOf('v1.') === 0 && process.version.indexOf('v1.8.') !== 0) {
|
|
module.exports = nextTick;
|
|
} else {
|
|
module.exports = process.nextTick;
|
|
}
|
|
|
|
function nextTick(fn, arg1, arg2, arg3) {
|
|
if (typeof fn !== 'function') {
|
|
throw new TypeError('"callback" argument must be a function');
|
|
}
|
|
var len = arguments.length;
|
|
var args, i;
|
|
switch (len) {
|
|
case 0:
|
|
case 1:
|
|
return process.nextTick(fn);
|
|
case 2:
|
|
return process.nextTick(function afterTickOne() {
|
|
fn.call(null, arg1);
|
|
});
|
|
case 3:
|
|
return process.nextTick(function afterTickTwo() {
|
|
fn.call(null, arg1, arg2);
|
|
});
|
|
case 4:
|
|
return process.nextTick(function afterTickThree() {
|
|
fn.call(null, arg1, arg2, arg3);
|
|
});
|
|
default:
|
|
args = new Array(len - 1);
|
|
i = 0;
|
|
while (i < args.length) {
|
|
args[i++] = arguments[i];
|
|
}
|
|
return process.nextTick(function afterTick() {
|
|
fn.apply(null, args);
|
|
});
|
|
}
|
|
}
|
|
|
|
}).call(this,require('_process'))
|
|
},{"_process":133}],133:[function(require,module,exports){
|
|
// shim for using process in browser
|
|
var process = module.exports = {};
|
|
|
|
// cached from whatever global is present so that test runners that stub it
|
|
// don't break things. But we need to wrap it in a try catch in case it is
|
|
// wrapped in strict mode code which doesn't define any globals. It's inside a
|
|
// function because try/catches deoptimize in certain engines.
|
|
|
|
var cachedSetTimeout;
|
|
var cachedClearTimeout;
|
|
|
|
function defaultSetTimout() {
|
|
throw new Error('setTimeout has not been defined');
|
|
}
|
|
function defaultClearTimeout () {
|
|
throw new Error('clearTimeout has not been defined');
|
|
}
|
|
(function () {
|
|
try {
|
|
if (typeof setTimeout === 'function') {
|
|
cachedSetTimeout = setTimeout;
|
|
} else {
|
|
cachedSetTimeout = defaultSetTimout;
|
|
}
|
|
} catch (e) {
|
|
cachedSetTimeout = defaultSetTimout;
|
|
}
|
|
try {
|
|
if (typeof clearTimeout === 'function') {
|
|
cachedClearTimeout = clearTimeout;
|
|
} else {
|
|
cachedClearTimeout = defaultClearTimeout;
|
|
}
|
|
} catch (e) {
|
|
cachedClearTimeout = defaultClearTimeout;
|
|
}
|
|
} ())
|
|
function runTimeout(fun) {
|
|
if (cachedSetTimeout === setTimeout) {
|
|
//normal enviroments in sane situations
|
|
return setTimeout(fun, 0);
|
|
}
|
|
// if setTimeout wasn't available but was latter defined
|
|
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
|
|
cachedSetTimeout = setTimeout;
|
|
return setTimeout(fun, 0);
|
|
}
|
|
try {
|
|
// when when somebody has screwed with setTimeout but no I.E. maddness
|
|
return cachedSetTimeout(fun, 0);
|
|
} catch(e){
|
|
try {
|
|
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
|
return cachedSetTimeout.call(null, fun, 0);
|
|
} catch(e){
|
|
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
|
|
return cachedSetTimeout.call(this, fun, 0);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
function runClearTimeout(marker) {
|
|
if (cachedClearTimeout === clearTimeout) {
|
|
//normal enviroments in sane situations
|
|
return clearTimeout(marker);
|
|
}
|
|
// if clearTimeout wasn't available but was latter defined
|
|
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
|
|
cachedClearTimeout = clearTimeout;
|
|
return clearTimeout(marker);
|
|
}
|
|
try {
|
|
// when when somebody has screwed with setTimeout but no I.E. maddness
|
|
return cachedClearTimeout(marker);
|
|
} catch (e){
|
|
try {
|
|
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
|
return cachedClearTimeout.call(null, marker);
|
|
} catch (e){
|
|
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
|
|
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
|
|
return cachedClearTimeout.call(this, marker);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
var queue = [];
|
|
var draining = false;
|
|
var currentQueue;
|
|
var queueIndex = -1;
|
|
|
|
function cleanUpNextTick() {
|
|
if (!draining || !currentQueue) {
|
|
return;
|
|
}
|
|
draining = false;
|
|
if (currentQueue.length) {
|
|
queue = currentQueue.concat(queue);
|
|
} else {
|
|
queueIndex = -1;
|
|
}
|
|
if (queue.length) {
|
|
drainQueue();
|
|
}
|
|
}
|
|
|
|
function drainQueue() {
|
|
if (draining) {
|
|
return;
|
|
}
|
|
var timeout = runTimeout(cleanUpNextTick);
|
|
draining = true;
|
|
|
|
var len = queue.length;
|
|
while(len) {
|
|
currentQueue = queue;
|
|
queue = [];
|
|
while (++queueIndex < len) {
|
|
if (currentQueue) {
|
|
currentQueue[queueIndex].run();
|
|
}
|
|
}
|
|
queueIndex = -1;
|
|
len = queue.length;
|
|
}
|
|
currentQueue = null;
|
|
draining = false;
|
|
runClearTimeout(timeout);
|
|
}
|
|
|
|
process.nextTick = function (fun) {
|
|
var args = new Array(arguments.length - 1);
|
|
if (arguments.length > 1) {
|
|
for (var i = 1; i < arguments.length; i++) {
|
|
args[i - 1] = arguments[i];
|
|
}
|
|
}
|
|
queue.push(new Item(fun, args));
|
|
if (queue.length === 1 && !draining) {
|
|
runTimeout(drainQueue);
|
|
}
|
|
};
|
|
|
|
// v8 likes predictible objects
|
|
function Item(fun, array) {
|
|
this.fun = fun;
|
|
this.array = array;
|
|
}
|
|
Item.prototype.run = function () {
|
|
this.fun.apply(null, this.array);
|
|
};
|
|
process.title = 'browser';
|
|
process.browser = true;
|
|
process.env = {};
|
|
process.argv = [];
|
|
process.version = ''; // empty string to avoid regexp issues
|
|
process.versions = {};
|
|
|
|
function noop() {}
|
|
|
|
process.on = noop;
|
|
process.addListener = noop;
|
|
process.once = noop;
|
|
process.off = noop;
|
|
process.removeListener = noop;
|
|
process.removeAllListeners = noop;
|
|
process.emit = noop;
|
|
|
|
process.binding = function (name) {
|
|
throw new Error('process.binding is not supported');
|
|
};
|
|
|
|
process.cwd = function () { return '/' };
|
|
process.chdir = function (dir) {
|
|
throw new Error('process.chdir is not supported');
|
|
};
|
|
process.umask = function() { return 0; };
|
|
|
|
},{}],134:[function(require,module,exports){
|
|
arguments[4][105][0].apply(exports,arguments)
|
|
},{"dup":105}],135:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
module.exports = require('react/lib/ReactDOM');
|
|
|
|
},{"react/lib/ReactDOM":170}],136:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule AutoFocusUtils
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactMount = require('./ReactMount');
|
|
|
|
var findDOMNode = require('./findDOMNode');
|
|
var focusNode = require('fbjs/lib/focusNode');
|
|
|
|
var Mixin = {
|
|
componentDidMount: function () {
|
|
if (this.props.autoFocus) {
|
|
focusNode(findDOMNode(this));
|
|
}
|
|
}
|
|
};
|
|
|
|
var AutoFocusUtils = {
|
|
Mixin: Mixin,
|
|
|
|
focusDOMComponent: function () {
|
|
focusNode(ReactMount.getNode(this._rootNodeID));
|
|
}
|
|
};
|
|
|
|
module.exports = AutoFocusUtils;
|
|
},{"./ReactMount":200,"./findDOMNode":243,"fbjs/lib/focusNode":66}],137:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015 Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule BeforeInputEventPlugin
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var EventConstants = require('./EventConstants');
|
|
var EventPropagators = require('./EventPropagators');
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
var FallbackCompositionState = require('./FallbackCompositionState');
|
|
var SyntheticCompositionEvent = require('./SyntheticCompositionEvent');
|
|
var SyntheticInputEvent = require('./SyntheticInputEvent');
|
|
|
|
var keyOf = require('fbjs/lib/keyOf');
|
|
|
|
var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
|
|
var START_KEYCODE = 229;
|
|
|
|
var canUseCompositionEvent = ExecutionEnvironment.canUseDOM && 'CompositionEvent' in window;
|
|
|
|
var documentMode = null;
|
|
if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) {
|
|
documentMode = document.documentMode;
|
|
}
|
|
|
|
// Webkit offers a very useful `textInput` event that can be used to
|
|
// directly represent `beforeInput`. The IE `textinput` event is not as
|
|
// useful, so we don't use it.
|
|
var canUseTextInputEvent = ExecutionEnvironment.canUseDOM && 'TextEvent' in window && !documentMode && !isPresto();
|
|
|
|
// In IE9+, we have access to composition events, but the data supplied
|
|
// by the native compositionend event may be incorrect. Japanese ideographic
|
|
// spaces, for instance (\u3000) are not recorded correctly.
|
|
var useFallbackCompositionData = ExecutionEnvironment.canUseDOM && (!canUseCompositionEvent || documentMode && documentMode > 8 && documentMode <= 11);
|
|
|
|
/**
|
|
* Opera <= 12 includes TextEvent in window, but does not fire
|
|
* text input events. Rely on keypress instead.
|
|
*/
|
|
function isPresto() {
|
|
var opera = window.opera;
|
|
return typeof opera === 'object' && typeof opera.version === 'function' && parseInt(opera.version(), 10) <= 12;
|
|
}
|
|
|
|
var SPACEBAR_CODE = 32;
|
|
var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
|
|
|
|
var topLevelTypes = EventConstants.topLevelTypes;
|
|
|
|
// Events and their corresponding property names.
|
|
var eventTypes = {
|
|
beforeInput: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onBeforeInput: null }),
|
|
captured: keyOf({ onBeforeInputCapture: null })
|
|
},
|
|
dependencies: [topLevelTypes.topCompositionEnd, topLevelTypes.topKeyPress, topLevelTypes.topTextInput, topLevelTypes.topPaste]
|
|
},
|
|
compositionEnd: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onCompositionEnd: null }),
|
|
captured: keyOf({ onCompositionEndCapture: null })
|
|
},
|
|
dependencies: [topLevelTypes.topBlur, topLevelTypes.topCompositionEnd, topLevelTypes.topKeyDown, topLevelTypes.topKeyPress, topLevelTypes.topKeyUp, topLevelTypes.topMouseDown]
|
|
},
|
|
compositionStart: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onCompositionStart: null }),
|
|
captured: keyOf({ onCompositionStartCapture: null })
|
|
},
|
|
dependencies: [topLevelTypes.topBlur, topLevelTypes.topCompositionStart, topLevelTypes.topKeyDown, topLevelTypes.topKeyPress, topLevelTypes.topKeyUp, topLevelTypes.topMouseDown]
|
|
},
|
|
compositionUpdate: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onCompositionUpdate: null }),
|
|
captured: keyOf({ onCompositionUpdateCapture: null })
|
|
},
|
|
dependencies: [topLevelTypes.topBlur, topLevelTypes.topCompositionUpdate, topLevelTypes.topKeyDown, topLevelTypes.topKeyPress, topLevelTypes.topKeyUp, topLevelTypes.topMouseDown]
|
|
}
|
|
};
|
|
|
|
// Track whether we've ever handled a keypress on the space key.
|
|
var hasSpaceKeypress = false;
|
|
|
|
/**
|
|
* Return whether a native keypress event is assumed to be a command.
|
|
* This is required because Firefox fires `keypress` events for key commands
|
|
* (cut, copy, select-all, etc.) even though no character is inserted.
|
|
*/
|
|
function isKeypressCommand(nativeEvent) {
|
|
return (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) &&
|
|
// ctrlKey && altKey is equivalent to AltGr, and is not a command.
|
|
!(nativeEvent.ctrlKey && nativeEvent.altKey);
|
|
}
|
|
|
|
/**
|
|
* Translate native top level events into event types.
|
|
*
|
|
* @param {string} topLevelType
|
|
* @return {object}
|
|
*/
|
|
function getCompositionEventType(topLevelType) {
|
|
switch (topLevelType) {
|
|
case topLevelTypes.topCompositionStart:
|
|
return eventTypes.compositionStart;
|
|
case topLevelTypes.topCompositionEnd:
|
|
return eventTypes.compositionEnd;
|
|
case topLevelTypes.topCompositionUpdate:
|
|
return eventTypes.compositionUpdate;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Does our fallback best-guess model think this event signifies that
|
|
* composition has begun?
|
|
*
|
|
* @param {string} topLevelType
|
|
* @param {object} nativeEvent
|
|
* @return {boolean}
|
|
*/
|
|
function isFallbackCompositionStart(topLevelType, nativeEvent) {
|
|
return topLevelType === topLevelTypes.topKeyDown && nativeEvent.keyCode === START_KEYCODE;
|
|
}
|
|
|
|
/**
|
|
* Does our fallback mode think that this event is the end of composition?
|
|
*
|
|
* @param {string} topLevelType
|
|
* @param {object} nativeEvent
|
|
* @return {boolean}
|
|
*/
|
|
function isFallbackCompositionEnd(topLevelType, nativeEvent) {
|
|
switch (topLevelType) {
|
|
case topLevelTypes.topKeyUp:
|
|
// Command keys insert or clear IME input.
|
|
return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
|
|
case topLevelTypes.topKeyDown:
|
|
// Expect IME keyCode on each keydown. If we get any other
|
|
// code we must have exited earlier.
|
|
return nativeEvent.keyCode !== START_KEYCODE;
|
|
case topLevelTypes.topKeyPress:
|
|
case topLevelTypes.topMouseDown:
|
|
case topLevelTypes.topBlur:
|
|
// Events are not possible without cancelling IME.
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Google Input Tools provides composition data via a CustomEvent,
|
|
* with the `data` property populated in the `detail` object. If this
|
|
* is available on the event object, use it. If not, this is a plain
|
|
* composition event and we have nothing special to extract.
|
|
*
|
|
* @param {object} nativeEvent
|
|
* @return {?string}
|
|
*/
|
|
function getDataFromCustomEvent(nativeEvent) {
|
|
var detail = nativeEvent.detail;
|
|
if (typeof detail === 'object' && 'data' in detail) {
|
|
return detail.data;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Track the current IME composition fallback object, if any.
|
|
var currentComposition = null;
|
|
|
|
/**
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {DOMEventTarget} topLevelTarget The listening component root node.
|
|
* @param {string} topLevelTargetID ID of `topLevelTarget`.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {?object} A SyntheticCompositionEvent.
|
|
*/
|
|
function extractCompositionEvent(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget) {
|
|
var eventType;
|
|
var fallbackData;
|
|
|
|
if (canUseCompositionEvent) {
|
|
eventType = getCompositionEventType(topLevelType);
|
|
} else if (!currentComposition) {
|
|
if (isFallbackCompositionStart(topLevelType, nativeEvent)) {
|
|
eventType = eventTypes.compositionStart;
|
|
}
|
|
} else if (isFallbackCompositionEnd(topLevelType, nativeEvent)) {
|
|
eventType = eventTypes.compositionEnd;
|
|
}
|
|
|
|
if (!eventType) {
|
|
return null;
|
|
}
|
|
|
|
if (useFallbackCompositionData) {
|
|
// The current composition is stored statically and must not be
|
|
// overwritten while composition continues.
|
|
if (!currentComposition && eventType === eventTypes.compositionStart) {
|
|
currentComposition = FallbackCompositionState.getPooled(topLevelTarget);
|
|
} else if (eventType === eventTypes.compositionEnd) {
|
|
if (currentComposition) {
|
|
fallbackData = currentComposition.getData();
|
|
}
|
|
}
|
|
}
|
|
|
|
var event = SyntheticCompositionEvent.getPooled(eventType, topLevelTargetID, nativeEvent, nativeEventTarget);
|
|
|
|
if (fallbackData) {
|
|
// Inject data generated from fallback path into the synthetic event.
|
|
// This matches the property of native CompositionEventInterface.
|
|
event.data = fallbackData;
|
|
} else {
|
|
var customData = getDataFromCustomEvent(nativeEvent);
|
|
if (customData !== null) {
|
|
event.data = customData;
|
|
}
|
|
}
|
|
|
|
EventPropagators.accumulateTwoPhaseDispatches(event);
|
|
return event;
|
|
}
|
|
|
|
/**
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {?string} The string corresponding to this `beforeInput` event.
|
|
*/
|
|
function getNativeBeforeInputChars(topLevelType, nativeEvent) {
|
|
switch (topLevelType) {
|
|
case topLevelTypes.topCompositionEnd:
|
|
return getDataFromCustomEvent(nativeEvent);
|
|
case topLevelTypes.topKeyPress:
|
|
/**
|
|
* If native `textInput` events are available, our goal is to make
|
|
* use of them. However, there is a special case: the spacebar key.
|
|
* In Webkit, preventing default on a spacebar `textInput` event
|
|
* cancels character insertion, but it *also* causes the browser
|
|
* to fall back to its default spacebar behavior of scrolling the
|
|
* page.
|
|
*
|
|
* Tracking at:
|
|
* https://code.google.com/p/chromium/issues/detail?id=355103
|
|
*
|
|
* To avoid this issue, use the keypress event as if no `textInput`
|
|
* event is available.
|
|
*/
|
|
var which = nativeEvent.which;
|
|
if (which !== SPACEBAR_CODE) {
|
|
return null;
|
|
}
|
|
|
|
hasSpaceKeypress = true;
|
|
return SPACEBAR_CHAR;
|
|
|
|
case topLevelTypes.topTextInput:
|
|
// Record the characters to be added to the DOM.
|
|
var chars = nativeEvent.data;
|
|
|
|
// If it's a spacebar character, assume that we have already handled
|
|
// it at the keypress level and bail immediately. Android Chrome
|
|
// doesn't give us keycodes, so we need to blacklist it.
|
|
if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
|
|
return null;
|
|
}
|
|
|
|
return chars;
|
|
|
|
default:
|
|
// For other native event types, do nothing.
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* For browsers that do not provide the `textInput` event, extract the
|
|
* appropriate string to use for SyntheticInputEvent.
|
|
*
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {?string} The fallback string for this `beforeInput` event.
|
|
*/
|
|
function getFallbackBeforeInputChars(topLevelType, nativeEvent) {
|
|
// If we are currently composing (IME) and using a fallback to do so,
|
|
// try to extract the composed characters from the fallback object.
|
|
if (currentComposition) {
|
|
if (topLevelType === topLevelTypes.topCompositionEnd || isFallbackCompositionEnd(topLevelType, nativeEvent)) {
|
|
var chars = currentComposition.getData();
|
|
FallbackCompositionState.release(currentComposition);
|
|
currentComposition = null;
|
|
return chars;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
switch (topLevelType) {
|
|
case topLevelTypes.topPaste:
|
|
// If a paste event occurs after a keypress, throw out the input
|
|
// chars. Paste events should not lead to BeforeInput events.
|
|
return null;
|
|
case topLevelTypes.topKeyPress:
|
|
/**
|
|
* As of v27, Firefox may fire keypress events even when no character
|
|
* will be inserted. A few possibilities:
|
|
*
|
|
* - `which` is `0`. Arrow keys, Esc key, etc.
|
|
*
|
|
* - `which` is the pressed key code, but no char is available.
|
|
* Ex: 'AltGr + d` in Polish. There is no modified character for
|
|
* this key combination and no character is inserted into the
|
|
* document, but FF fires the keypress for char code `100` anyway.
|
|
* No `input` event will occur.
|
|
*
|
|
* - `which` is the pressed key code, but a command combination is
|
|
* being used. Ex: `Cmd+C`. No character is inserted, and no
|
|
* `input` event will occur.
|
|
*/
|
|
if (nativeEvent.which && !isKeypressCommand(nativeEvent)) {
|
|
return String.fromCharCode(nativeEvent.which);
|
|
}
|
|
return null;
|
|
case topLevelTypes.topCompositionEnd:
|
|
return useFallbackCompositionData ? null : nativeEvent.data;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract a SyntheticInputEvent for `beforeInput`, based on either native
|
|
* `textInput` or fallback behavior.
|
|
*
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {DOMEventTarget} topLevelTarget The listening component root node.
|
|
* @param {string} topLevelTargetID ID of `topLevelTarget`.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {?object} A SyntheticInputEvent.
|
|
*/
|
|
function extractBeforeInputEvent(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget) {
|
|
var chars;
|
|
|
|
if (canUseTextInputEvent) {
|
|
chars = getNativeBeforeInputChars(topLevelType, nativeEvent);
|
|
} else {
|
|
chars = getFallbackBeforeInputChars(topLevelType, nativeEvent);
|
|
}
|
|
|
|
// If no characters are being inserted, no BeforeInput event should
|
|
// be fired.
|
|
if (!chars) {
|
|
return null;
|
|
}
|
|
|
|
var event = SyntheticInputEvent.getPooled(eventTypes.beforeInput, topLevelTargetID, nativeEvent, nativeEventTarget);
|
|
|
|
event.data = chars;
|
|
EventPropagators.accumulateTwoPhaseDispatches(event);
|
|
return event;
|
|
}
|
|
|
|
/**
|
|
* Create an `onBeforeInput` event to match
|
|
* http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
|
|
*
|
|
* This event plugin is based on the native `textInput` event
|
|
* available in Chrome, Safari, Opera, and IE. This event fires after
|
|
* `onKeyPress` and `onCompositionEnd`, but before `onInput`.
|
|
*
|
|
* `beforeInput` is spec'd but not implemented in any browsers, and
|
|
* the `input` event does not provide any useful information about what has
|
|
* actually been added, contrary to the spec. Thus, `textInput` is the best
|
|
* available event to identify the characters that have actually been inserted
|
|
* into the target node.
|
|
*
|
|
* This plugin is also responsible for emitting `composition` events, thus
|
|
* allowing us to share composition fallback code for both `beforeInput` and
|
|
* `composition` event types.
|
|
*/
|
|
var BeforeInputEventPlugin = {
|
|
|
|
eventTypes: eventTypes,
|
|
|
|
/**
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {DOMEventTarget} topLevelTarget The listening component root node.
|
|
* @param {string} topLevelTargetID ID of `topLevelTarget`.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {*} An accumulation of synthetic events.
|
|
* @see {EventPluginHub.extractEvents}
|
|
*/
|
|
extractEvents: function (topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget) {
|
|
return [extractCompositionEvent(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget), extractBeforeInputEvent(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget)];
|
|
}
|
|
};
|
|
|
|
module.exports = BeforeInputEventPlugin;
|
|
},{"./EventConstants":149,"./EventPropagators":153,"./FallbackCompositionState":154,"./SyntheticCompositionEvent":225,"./SyntheticInputEvent":229,"fbjs/lib/ExecutionEnvironment":58,"fbjs/lib/keyOf":76}],138:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule CSSProperty
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* CSS properties which accept numbers but are not in units of "px".
|
|
*/
|
|
var isUnitlessNumber = {
|
|
animationIterationCount: true,
|
|
boxFlex: true,
|
|
boxFlexGroup: true,
|
|
boxOrdinalGroup: true,
|
|
columnCount: true,
|
|
flex: true,
|
|
flexGrow: true,
|
|
flexPositive: true,
|
|
flexShrink: true,
|
|
flexNegative: true,
|
|
flexOrder: true,
|
|
fontWeight: true,
|
|
lineClamp: true,
|
|
lineHeight: true,
|
|
opacity: true,
|
|
order: true,
|
|
orphans: true,
|
|
tabSize: true,
|
|
widows: true,
|
|
zIndex: true,
|
|
zoom: true,
|
|
|
|
// SVG-related properties
|
|
fillOpacity: true,
|
|
stopOpacity: true,
|
|
strokeDashoffset: true,
|
|
strokeOpacity: true,
|
|
strokeWidth: true
|
|
};
|
|
|
|
/**
|
|
* @param {string} prefix vendor-specific prefix, eg: Webkit
|
|
* @param {string} key style name, eg: transitionDuration
|
|
* @return {string} style name prefixed with `prefix`, properly camelCased, eg:
|
|
* WebkitTransitionDuration
|
|
*/
|
|
function prefixKey(prefix, key) {
|
|
return prefix + key.charAt(0).toUpperCase() + key.substring(1);
|
|
}
|
|
|
|
/**
|
|
* Support style names that may come passed in prefixed by adding permutations
|
|
* of vendor prefixes.
|
|
*/
|
|
var prefixes = ['Webkit', 'ms', 'Moz', 'O'];
|
|
|
|
// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
|
|
// infinite loop, because it iterates over the newly added props too.
|
|
Object.keys(isUnitlessNumber).forEach(function (prop) {
|
|
prefixes.forEach(function (prefix) {
|
|
isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Most style properties can be unset by doing .style[prop] = '' but IE8
|
|
* doesn't like doing that with shorthand properties so for the properties that
|
|
* IE8 breaks on, which are listed here, we instead unset each of the
|
|
* individual properties. See http://bugs.jquery.com/ticket/12385.
|
|
* The 4-value 'clock' properties like margin, padding, border-width seem to
|
|
* behave without any problems. Curiously, list-style works too without any
|
|
* special prodding.
|
|
*/
|
|
var shorthandPropertyExpansions = {
|
|
background: {
|
|
backgroundAttachment: true,
|
|
backgroundColor: true,
|
|
backgroundImage: true,
|
|
backgroundPositionX: true,
|
|
backgroundPositionY: true,
|
|
backgroundRepeat: true
|
|
},
|
|
backgroundPosition: {
|
|
backgroundPositionX: true,
|
|
backgroundPositionY: true
|
|
},
|
|
border: {
|
|
borderWidth: true,
|
|
borderStyle: true,
|
|
borderColor: true
|
|
},
|
|
borderBottom: {
|
|
borderBottomWidth: true,
|
|
borderBottomStyle: true,
|
|
borderBottomColor: true
|
|
},
|
|
borderLeft: {
|
|
borderLeftWidth: true,
|
|
borderLeftStyle: true,
|
|
borderLeftColor: true
|
|
},
|
|
borderRight: {
|
|
borderRightWidth: true,
|
|
borderRightStyle: true,
|
|
borderRightColor: true
|
|
},
|
|
borderTop: {
|
|
borderTopWidth: true,
|
|
borderTopStyle: true,
|
|
borderTopColor: true
|
|
},
|
|
font: {
|
|
fontStyle: true,
|
|
fontVariant: true,
|
|
fontWeight: true,
|
|
fontSize: true,
|
|
lineHeight: true,
|
|
fontFamily: true
|
|
},
|
|
outline: {
|
|
outlineWidth: true,
|
|
outlineStyle: true,
|
|
outlineColor: true
|
|
}
|
|
};
|
|
|
|
var CSSProperty = {
|
|
isUnitlessNumber: isUnitlessNumber,
|
|
shorthandPropertyExpansions: shorthandPropertyExpansions
|
|
};
|
|
|
|
module.exports = CSSProperty;
|
|
},{}],139:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule CSSPropertyOperations
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var CSSProperty = require('./CSSProperty');
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
var ReactPerf = require('./ReactPerf');
|
|
|
|
var camelizeStyleName = require('fbjs/lib/camelizeStyleName');
|
|
var dangerousStyleValue = require('./dangerousStyleValue');
|
|
var hyphenateStyleName = require('fbjs/lib/hyphenateStyleName');
|
|
var memoizeStringOnly = require('fbjs/lib/memoizeStringOnly');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
var processStyleName = memoizeStringOnly(function (styleName) {
|
|
return hyphenateStyleName(styleName);
|
|
});
|
|
|
|
var hasShorthandPropertyBug = false;
|
|
var styleFloatAccessor = 'cssFloat';
|
|
if (ExecutionEnvironment.canUseDOM) {
|
|
var tempStyle = document.createElement('div').style;
|
|
try {
|
|
// IE8 throws "Invalid argument." if resetting shorthand style properties.
|
|
tempStyle.font = '';
|
|
} catch (e) {
|
|
hasShorthandPropertyBug = true;
|
|
}
|
|
// IE8 only supports accessing cssFloat (standard) as styleFloat
|
|
if (document.documentElement.style.cssFloat === undefined) {
|
|
styleFloatAccessor = 'styleFloat';
|
|
}
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// 'msTransform' is correct, but the other prefixes should be capitalized
|
|
var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/;
|
|
|
|
// style values shouldn't contain a semicolon
|
|
var badStyleValueWithSemicolonPattern = /;\s*$/;
|
|
|
|
var warnedStyleNames = {};
|
|
var warnedStyleValues = {};
|
|
|
|
var warnHyphenatedStyleName = function (name) {
|
|
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
|
|
return;
|
|
}
|
|
|
|
warnedStyleNames[name] = true;
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Unsupported style property %s. Did you mean %s?', name, camelizeStyleName(name)) : undefined;
|
|
};
|
|
|
|
var warnBadVendoredStyleName = function (name) {
|
|
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
|
|
return;
|
|
}
|
|
|
|
warnedStyleNames[name] = true;
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Unsupported vendor-prefixed style property %s. Did you mean %s?', name, name.charAt(0).toUpperCase() + name.slice(1)) : undefined;
|
|
};
|
|
|
|
var warnStyleValueWithSemicolon = function (name, value) {
|
|
if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) {
|
|
return;
|
|
}
|
|
|
|
warnedStyleValues[value] = true;
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Style property values shouldn\'t contain a semicolon. ' + 'Try "%s: %s" instead.', name, value.replace(badStyleValueWithSemicolonPattern, '')) : undefined;
|
|
};
|
|
|
|
/**
|
|
* @param {string} name
|
|
* @param {*} value
|
|
*/
|
|
var warnValidStyle = function (name, value) {
|
|
if (name.indexOf('-') > -1) {
|
|
warnHyphenatedStyleName(name);
|
|
} else if (badVendoredStyleNamePattern.test(name)) {
|
|
warnBadVendoredStyleName(name);
|
|
} else if (badStyleValueWithSemicolonPattern.test(value)) {
|
|
warnStyleValueWithSemicolon(name, value);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Operations for dealing with CSS properties.
|
|
*/
|
|
var CSSPropertyOperations = {
|
|
|
|
/**
|
|
* Serializes a mapping of style properties for use as inline styles:
|
|
*
|
|
* > createMarkupForStyles({width: '200px', height: 0})
|
|
* "width:200px;height:0;"
|
|
*
|
|
* Undefined values are ignored so that declarative programming is easier.
|
|
* The result should be HTML-escaped before insertion into the DOM.
|
|
*
|
|
* @param {object} styles
|
|
* @return {?string}
|
|
*/
|
|
createMarkupForStyles: function (styles) {
|
|
var serialized = '';
|
|
for (var styleName in styles) {
|
|
if (!styles.hasOwnProperty(styleName)) {
|
|
continue;
|
|
}
|
|
var styleValue = styles[styleName];
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
warnValidStyle(styleName, styleValue);
|
|
}
|
|
if (styleValue != null) {
|
|
serialized += processStyleName(styleName) + ':';
|
|
serialized += dangerousStyleValue(styleName, styleValue) + ';';
|
|
}
|
|
}
|
|
return serialized || null;
|
|
},
|
|
|
|
/**
|
|
* Sets the value for multiple styles on a node. If a value is specified as
|
|
* '' (empty string), the corresponding style property will be unset.
|
|
*
|
|
* @param {DOMElement} node
|
|
* @param {object} styles
|
|
*/
|
|
setValueForStyles: function (node, styles) {
|
|
var style = node.style;
|
|
for (var styleName in styles) {
|
|
if (!styles.hasOwnProperty(styleName)) {
|
|
continue;
|
|
}
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
warnValidStyle(styleName, styles[styleName]);
|
|
}
|
|
var styleValue = dangerousStyleValue(styleName, styles[styleName]);
|
|
if (styleName === 'float') {
|
|
styleName = styleFloatAccessor;
|
|
}
|
|
if (styleValue) {
|
|
style[styleName] = styleValue;
|
|
} else {
|
|
var expansion = hasShorthandPropertyBug && CSSProperty.shorthandPropertyExpansions[styleName];
|
|
if (expansion) {
|
|
// Shorthand property that IE8 won't like unsetting, so unset each
|
|
// component to placate it
|
|
for (var individualStyleName in expansion) {
|
|
style[individualStyleName] = '';
|
|
}
|
|
} else {
|
|
style[styleName] = '';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
ReactPerf.measureMethods(CSSPropertyOperations, 'CSSPropertyOperations', {
|
|
setValueForStyles: 'setValueForStyles'
|
|
});
|
|
|
|
module.exports = CSSPropertyOperations;
|
|
}).call(this,require('_process'))
|
|
},{"./CSSProperty":138,"./ReactPerf":206,"./dangerousStyleValue":240,"_process":133,"fbjs/lib/ExecutionEnvironment":58,"fbjs/lib/camelizeStyleName":60,"fbjs/lib/hyphenateStyleName":71,"fbjs/lib/memoizeStringOnly":78,"fbjs/lib/warning":83}],140:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule CallbackQueue
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var PooledClass = require('./PooledClass');
|
|
|
|
var assign = require('./Object.assign');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
/**
|
|
* A specialized pseudo-event module to help keep track of components waiting to
|
|
* be notified when their DOM representations are available for use.
|
|
*
|
|
* This implements `PooledClass`, so you should never need to instantiate this.
|
|
* Instead, use `CallbackQueue.getPooled()`.
|
|
*
|
|
* @class ReactMountReady
|
|
* @implements PooledClass
|
|
* @internal
|
|
*/
|
|
function CallbackQueue() {
|
|
this._callbacks = null;
|
|
this._contexts = null;
|
|
}
|
|
|
|
assign(CallbackQueue.prototype, {
|
|
|
|
/**
|
|
* Enqueues a callback to be invoked when `notifyAll` is invoked.
|
|
*
|
|
* @param {function} callback Invoked when `notifyAll` is invoked.
|
|
* @param {?object} context Context to call `callback` with.
|
|
* @internal
|
|
*/
|
|
enqueue: function (callback, context) {
|
|
this._callbacks = this._callbacks || [];
|
|
this._contexts = this._contexts || [];
|
|
this._callbacks.push(callback);
|
|
this._contexts.push(context);
|
|
},
|
|
|
|
/**
|
|
* Invokes all enqueued callbacks and clears the queue. This is invoked after
|
|
* the DOM representation of a component has been created or updated.
|
|
*
|
|
* @internal
|
|
*/
|
|
notifyAll: function () {
|
|
var callbacks = this._callbacks;
|
|
var contexts = this._contexts;
|
|
if (callbacks) {
|
|
!(callbacks.length === contexts.length) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Mismatched list of contexts in callback queue') : invariant(false) : undefined;
|
|
this._callbacks = null;
|
|
this._contexts = null;
|
|
for (var i = 0; i < callbacks.length; i++) {
|
|
callbacks[i].call(contexts[i]);
|
|
}
|
|
callbacks.length = 0;
|
|
contexts.length = 0;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Resets the internal queue.
|
|
*
|
|
* @internal
|
|
*/
|
|
reset: function () {
|
|
this._callbacks = null;
|
|
this._contexts = null;
|
|
},
|
|
|
|
/**
|
|
* `PooledClass` looks for this.
|
|
*/
|
|
destructor: function () {
|
|
this.reset();
|
|
}
|
|
|
|
});
|
|
|
|
PooledClass.addPoolingTo(CallbackQueue);
|
|
|
|
module.exports = CallbackQueue;
|
|
}).call(this,require('_process'))
|
|
},{"./Object.assign":157,"./PooledClass":158,"_process":133,"fbjs/lib/invariant":72}],141:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ChangeEventPlugin
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var EventConstants = require('./EventConstants');
|
|
var EventPluginHub = require('./EventPluginHub');
|
|
var EventPropagators = require('./EventPropagators');
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
var ReactUpdates = require('./ReactUpdates');
|
|
var SyntheticEvent = require('./SyntheticEvent');
|
|
|
|
var getEventTarget = require('./getEventTarget');
|
|
var isEventSupported = require('./isEventSupported');
|
|
var isTextInputElement = require('./isTextInputElement');
|
|
var keyOf = require('fbjs/lib/keyOf');
|
|
|
|
var topLevelTypes = EventConstants.topLevelTypes;
|
|
|
|
var eventTypes = {
|
|
change: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onChange: null }),
|
|
captured: keyOf({ onChangeCapture: null })
|
|
},
|
|
dependencies: [topLevelTypes.topBlur, topLevelTypes.topChange, topLevelTypes.topClick, topLevelTypes.topFocus, topLevelTypes.topInput, topLevelTypes.topKeyDown, topLevelTypes.topKeyUp, topLevelTypes.topSelectionChange]
|
|
}
|
|
};
|
|
|
|
/**
|
|
* For IE shims
|
|
*/
|
|
var activeElement = null;
|
|
var activeElementID = null;
|
|
var activeElementValue = null;
|
|
var activeElementValueProp = null;
|
|
|
|
/**
|
|
* SECTION: handle `change` event
|
|
*/
|
|
function shouldUseChangeEvent(elem) {
|
|
var nodeName = elem.nodeName && elem.nodeName.toLowerCase();
|
|
return nodeName === 'select' || nodeName === 'input' && elem.type === 'file';
|
|
}
|
|
|
|
var doesChangeEventBubble = false;
|
|
if (ExecutionEnvironment.canUseDOM) {
|
|
// See `handleChange` comment below
|
|
doesChangeEventBubble = isEventSupported('change') && (!('documentMode' in document) || document.documentMode > 8);
|
|
}
|
|
|
|
function manualDispatchChangeEvent(nativeEvent) {
|
|
var event = SyntheticEvent.getPooled(eventTypes.change, activeElementID, nativeEvent, getEventTarget(nativeEvent));
|
|
EventPropagators.accumulateTwoPhaseDispatches(event);
|
|
|
|
// If change and propertychange bubbled, we'd just bind to it like all the
|
|
// other events and have it go through ReactBrowserEventEmitter. Since it
|
|
// doesn't, we manually listen for the events and so we have to enqueue and
|
|
// process the abstract event manually.
|
|
//
|
|
// Batching is necessary here in order to ensure that all event handlers run
|
|
// before the next rerender (including event handlers attached to ancestor
|
|
// elements instead of directly on the input). Without this, controlled
|
|
// components don't work properly in conjunction with event bubbling because
|
|
// the component is rerendered and the value reverted before all the event
|
|
// handlers can run. See https://github.com/facebook/react/issues/708.
|
|
ReactUpdates.batchedUpdates(runEventInBatch, event);
|
|
}
|
|
|
|
function runEventInBatch(event) {
|
|
EventPluginHub.enqueueEvents(event);
|
|
EventPluginHub.processEventQueue(false);
|
|
}
|
|
|
|
function startWatchingForChangeEventIE8(target, targetID) {
|
|
activeElement = target;
|
|
activeElementID = targetID;
|
|
activeElement.attachEvent('onchange', manualDispatchChangeEvent);
|
|
}
|
|
|
|
function stopWatchingForChangeEventIE8() {
|
|
if (!activeElement) {
|
|
return;
|
|
}
|
|
activeElement.detachEvent('onchange', manualDispatchChangeEvent);
|
|
activeElement = null;
|
|
activeElementID = null;
|
|
}
|
|
|
|
function getTargetIDForChangeEvent(topLevelType, topLevelTarget, topLevelTargetID) {
|
|
if (topLevelType === topLevelTypes.topChange) {
|
|
return topLevelTargetID;
|
|
}
|
|
}
|
|
function handleEventsForChangeEventIE8(topLevelType, topLevelTarget, topLevelTargetID) {
|
|
if (topLevelType === topLevelTypes.topFocus) {
|
|
// stopWatching() should be a noop here but we call it just in case we
|
|
// missed a blur event somehow.
|
|
stopWatchingForChangeEventIE8();
|
|
startWatchingForChangeEventIE8(topLevelTarget, topLevelTargetID);
|
|
} else if (topLevelType === topLevelTypes.topBlur) {
|
|
stopWatchingForChangeEventIE8();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SECTION: handle `input` event
|
|
*/
|
|
var isInputEventSupported = false;
|
|
if (ExecutionEnvironment.canUseDOM) {
|
|
// IE9 claims to support the input event but fails to trigger it when
|
|
// deleting text, so we ignore its input events
|
|
isInputEventSupported = isEventSupported('input') && (!('documentMode' in document) || document.documentMode > 9);
|
|
}
|
|
|
|
/**
|
|
* (For old IE.) Replacement getter/setter for the `value` property that gets
|
|
* set on the active element.
|
|
*/
|
|
var newValueProp = {
|
|
get: function () {
|
|
return activeElementValueProp.get.call(this);
|
|
},
|
|
set: function (val) {
|
|
// Cast to a string so we can do equality checks.
|
|
activeElementValue = '' + val;
|
|
activeElementValueProp.set.call(this, val);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* (For old IE.) Starts tracking propertychange events on the passed-in element
|
|
* and override the value property so that we can distinguish user events from
|
|
* value changes in JS.
|
|
*/
|
|
function startWatchingForValueChange(target, targetID) {
|
|
activeElement = target;
|
|
activeElementID = targetID;
|
|
activeElementValue = target.value;
|
|
activeElementValueProp = Object.getOwnPropertyDescriptor(target.constructor.prototype, 'value');
|
|
|
|
// Not guarded in a canDefineProperty check: IE8 supports defineProperty only
|
|
// on DOM elements
|
|
Object.defineProperty(activeElement, 'value', newValueProp);
|
|
activeElement.attachEvent('onpropertychange', handlePropertyChange);
|
|
}
|
|
|
|
/**
|
|
* (For old IE.) Removes the event listeners from the currently-tracked element,
|
|
* if any exists.
|
|
*/
|
|
function stopWatchingForValueChange() {
|
|
if (!activeElement) {
|
|
return;
|
|
}
|
|
|
|
// delete restores the original property definition
|
|
delete activeElement.value;
|
|
activeElement.detachEvent('onpropertychange', handlePropertyChange);
|
|
|
|
activeElement = null;
|
|
activeElementID = null;
|
|
activeElementValue = null;
|
|
activeElementValueProp = null;
|
|
}
|
|
|
|
/**
|
|
* (For old IE.) Handles a propertychange event, sending a `change` event if
|
|
* the value of the active element has changed.
|
|
*/
|
|
function handlePropertyChange(nativeEvent) {
|
|
if (nativeEvent.propertyName !== 'value') {
|
|
return;
|
|
}
|
|
var value = nativeEvent.srcElement.value;
|
|
if (value === activeElementValue) {
|
|
return;
|
|
}
|
|
activeElementValue = value;
|
|
|
|
manualDispatchChangeEvent(nativeEvent);
|
|
}
|
|
|
|
/**
|
|
* If a `change` event should be fired, returns the target's ID.
|
|
*/
|
|
function getTargetIDForInputEvent(topLevelType, topLevelTarget, topLevelTargetID) {
|
|
if (topLevelType === topLevelTypes.topInput) {
|
|
// In modern browsers (i.e., not IE8 or IE9), the input event is exactly
|
|
// what we want so fall through here and trigger an abstract event
|
|
return topLevelTargetID;
|
|
}
|
|
}
|
|
|
|
// For IE8 and IE9.
|
|
function handleEventsForInputEventIE(topLevelType, topLevelTarget, topLevelTargetID) {
|
|
if (topLevelType === topLevelTypes.topFocus) {
|
|
// In IE8, we can capture almost all .value changes by adding a
|
|
// propertychange handler and looking for events with propertyName
|
|
// equal to 'value'
|
|
// In IE9, propertychange fires for most input events but is buggy and
|
|
// doesn't fire when text is deleted, but conveniently, selectionchange
|
|
// appears to fire in all of the remaining cases so we catch those and
|
|
// forward the event if the value has changed
|
|
// In either case, we don't want to call the event handler if the value
|
|
// is changed from JS so we redefine a setter for `.value` that updates
|
|
// our activeElementValue variable, allowing us to ignore those changes
|
|
//
|
|
// stopWatching() should be a noop here but we call it just in case we
|
|
// missed a blur event somehow.
|
|
stopWatchingForValueChange();
|
|
startWatchingForValueChange(topLevelTarget, topLevelTargetID);
|
|
} else if (topLevelType === topLevelTypes.topBlur) {
|
|
stopWatchingForValueChange();
|
|
}
|
|
}
|
|
|
|
// For IE8 and IE9.
|
|
function getTargetIDForInputEventIE(topLevelType, topLevelTarget, topLevelTargetID) {
|
|
if (topLevelType === topLevelTypes.topSelectionChange || topLevelType === topLevelTypes.topKeyUp || topLevelType === topLevelTypes.topKeyDown) {
|
|
// On the selectionchange event, the target is just document which isn't
|
|
// helpful for us so just check activeElement instead.
|
|
//
|
|
// 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire
|
|
// propertychange on the first input event after setting `value` from a
|
|
// script and fires only keydown, keypress, keyup. Catching keyup usually
|
|
// gets it and catching keydown lets us fire an event for the first
|
|
// keystroke if user does a key repeat (it'll be a little delayed: right
|
|
// before the second keystroke). Other input methods (e.g., paste) seem to
|
|
// fire selectionchange normally.
|
|
if (activeElement && activeElement.value !== activeElementValue) {
|
|
activeElementValue = activeElement.value;
|
|
return activeElementID;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SECTION: handle `click` event
|
|
*/
|
|
function shouldUseClickEvent(elem) {
|
|
// Use the `click` event to detect changes to checkbox and radio inputs.
|
|
// This approach works across all browsers, whereas `change` does not fire
|
|
// until `blur` in IE8.
|
|
return elem.nodeName && elem.nodeName.toLowerCase() === 'input' && (elem.type === 'checkbox' || elem.type === 'radio');
|
|
}
|
|
|
|
function getTargetIDForClickEvent(topLevelType, topLevelTarget, topLevelTargetID) {
|
|
if (topLevelType === topLevelTypes.topClick) {
|
|
return topLevelTargetID;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This plugin creates an `onChange` event that normalizes change events
|
|
* across form elements. This event fires at a time when it's possible to
|
|
* change the element's value without seeing a flicker.
|
|
*
|
|
* Supported elements are:
|
|
* - input (see `isTextInputElement`)
|
|
* - textarea
|
|
* - select
|
|
*/
|
|
var ChangeEventPlugin = {
|
|
|
|
eventTypes: eventTypes,
|
|
|
|
/**
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {DOMEventTarget} topLevelTarget The listening component root node.
|
|
* @param {string} topLevelTargetID ID of `topLevelTarget`.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {*} An accumulation of synthetic events.
|
|
* @see {EventPluginHub.extractEvents}
|
|
*/
|
|
extractEvents: function (topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget) {
|
|
|
|
var getTargetIDFunc, handleEventFunc;
|
|
if (shouldUseChangeEvent(topLevelTarget)) {
|
|
if (doesChangeEventBubble) {
|
|
getTargetIDFunc = getTargetIDForChangeEvent;
|
|
} else {
|
|
handleEventFunc = handleEventsForChangeEventIE8;
|
|
}
|
|
} else if (isTextInputElement(topLevelTarget)) {
|
|
if (isInputEventSupported) {
|
|
getTargetIDFunc = getTargetIDForInputEvent;
|
|
} else {
|
|
getTargetIDFunc = getTargetIDForInputEventIE;
|
|
handleEventFunc = handleEventsForInputEventIE;
|
|
}
|
|
} else if (shouldUseClickEvent(topLevelTarget)) {
|
|
getTargetIDFunc = getTargetIDForClickEvent;
|
|
}
|
|
|
|
if (getTargetIDFunc) {
|
|
var targetID = getTargetIDFunc(topLevelType, topLevelTarget, topLevelTargetID);
|
|
if (targetID) {
|
|
var event = SyntheticEvent.getPooled(eventTypes.change, targetID, nativeEvent, nativeEventTarget);
|
|
event.type = 'change';
|
|
EventPropagators.accumulateTwoPhaseDispatches(event);
|
|
return event;
|
|
}
|
|
}
|
|
|
|
if (handleEventFunc) {
|
|
handleEventFunc(topLevelType, topLevelTarget, topLevelTargetID);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ChangeEventPlugin;
|
|
},{"./EventConstants":149,"./EventPluginHub":150,"./EventPropagators":153,"./ReactUpdates":218,"./SyntheticEvent":227,"./getEventTarget":249,"./isEventSupported":254,"./isTextInputElement":255,"fbjs/lib/ExecutionEnvironment":58,"fbjs/lib/keyOf":76}],142:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ClientReactRootIndex
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var nextReactRootIndex = 0;
|
|
|
|
var ClientReactRootIndex = {
|
|
createReactRootIndex: function () {
|
|
return nextReactRootIndex++;
|
|
}
|
|
};
|
|
|
|
module.exports = ClientReactRootIndex;
|
|
},{}],143:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule DOMChildrenOperations
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var Danger = require('./Danger');
|
|
var ReactMultiChildUpdateTypes = require('./ReactMultiChildUpdateTypes');
|
|
var ReactPerf = require('./ReactPerf');
|
|
|
|
var setInnerHTML = require('./setInnerHTML');
|
|
var setTextContent = require('./setTextContent');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
/**
|
|
* Inserts `childNode` as a child of `parentNode` at the `index`.
|
|
*
|
|
* @param {DOMElement} parentNode Parent node in which to insert.
|
|
* @param {DOMElement} childNode Child node to insert.
|
|
* @param {number} index Index at which to insert the child.
|
|
* @internal
|
|
*/
|
|
function insertChildAt(parentNode, childNode, index) {
|
|
// By exploiting arrays returning `undefined` for an undefined index, we can
|
|
// rely exclusively on `insertBefore(node, null)` instead of also using
|
|
// `appendChild(node)`. However, using `undefined` is not allowed by all
|
|
// browsers so we must replace it with `null`.
|
|
|
|
// fix render order error in safari
|
|
// IE8 will throw error when index out of list size.
|
|
var beforeChild = index >= parentNode.childNodes.length ? null : parentNode.childNodes.item(index);
|
|
|
|
parentNode.insertBefore(childNode, beforeChild);
|
|
}
|
|
|
|
/**
|
|
* Operations for updating with DOM children.
|
|
*/
|
|
var DOMChildrenOperations = {
|
|
|
|
dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
|
|
|
|
updateTextContent: setTextContent,
|
|
|
|
/**
|
|
* Updates a component's children by processing a series of updates. The
|
|
* update configurations are each expected to have a `parentNode` property.
|
|
*
|
|
* @param {array<object>} updates List of update configurations.
|
|
* @param {array<string>} markupList List of markup strings.
|
|
* @internal
|
|
*/
|
|
processUpdates: function (updates, markupList) {
|
|
var update;
|
|
// Mapping from parent IDs to initial child orderings.
|
|
var initialChildren = null;
|
|
// List of children that will be moved or removed.
|
|
var updatedChildren = null;
|
|
|
|
for (var i = 0; i < updates.length; i++) {
|
|
update = updates[i];
|
|
if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING || update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) {
|
|
var updatedIndex = update.fromIndex;
|
|
var updatedChild = update.parentNode.childNodes[updatedIndex];
|
|
var parentID = update.parentID;
|
|
|
|
!updatedChild ? process.env.NODE_ENV !== 'production' ? invariant(false, 'processUpdates(): Unable to find child %s of element. This ' + 'probably means the DOM was unexpectedly mutated (e.g., by the ' + 'browser), usually due to forgetting a <tbody> when using tables, ' + 'nesting tags like <form>, <p>, or <a>, or using non-SVG elements ' + 'in an <svg> parent. Try inspecting the child nodes of the element ' + 'with React ID `%s`.', updatedIndex, parentID) : invariant(false) : undefined;
|
|
|
|
initialChildren = initialChildren || {};
|
|
initialChildren[parentID] = initialChildren[parentID] || [];
|
|
initialChildren[parentID][updatedIndex] = updatedChild;
|
|
|
|
updatedChildren = updatedChildren || [];
|
|
updatedChildren.push(updatedChild);
|
|
}
|
|
}
|
|
|
|
var renderedMarkup;
|
|
// markupList is either a list of markup or just a list of elements
|
|
if (markupList.length && typeof markupList[0] === 'string') {
|
|
renderedMarkup = Danger.dangerouslyRenderMarkup(markupList);
|
|
} else {
|
|
renderedMarkup = markupList;
|
|
}
|
|
|
|
// Remove updated children first so that `toIndex` is consistent.
|
|
if (updatedChildren) {
|
|
for (var j = 0; j < updatedChildren.length; j++) {
|
|
updatedChildren[j].parentNode.removeChild(updatedChildren[j]);
|
|
}
|
|
}
|
|
|
|
for (var k = 0; k < updates.length; k++) {
|
|
update = updates[k];
|
|
switch (update.type) {
|
|
case ReactMultiChildUpdateTypes.INSERT_MARKUP:
|
|
insertChildAt(update.parentNode, renderedMarkup[update.markupIndex], update.toIndex);
|
|
break;
|
|
case ReactMultiChildUpdateTypes.MOVE_EXISTING:
|
|
insertChildAt(update.parentNode, initialChildren[update.parentID][update.fromIndex], update.toIndex);
|
|
break;
|
|
case ReactMultiChildUpdateTypes.SET_MARKUP:
|
|
setInnerHTML(update.parentNode, update.content);
|
|
break;
|
|
case ReactMultiChildUpdateTypes.TEXT_CONTENT:
|
|
setTextContent(update.parentNode, update.content);
|
|
break;
|
|
case ReactMultiChildUpdateTypes.REMOVE_NODE:
|
|
// Already removed by the for-loop above.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
ReactPerf.measureMethods(DOMChildrenOperations, 'DOMChildrenOperations', {
|
|
updateTextContent: 'updateTextContent'
|
|
});
|
|
|
|
module.exports = DOMChildrenOperations;
|
|
}).call(this,require('_process'))
|
|
},{"./Danger":146,"./ReactMultiChildUpdateTypes":202,"./ReactPerf":206,"./setInnerHTML":259,"./setTextContent":260,"_process":133,"fbjs/lib/invariant":72}],144:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule DOMProperty
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
function checkMask(value, bitmask) {
|
|
return (value & bitmask) === bitmask;
|
|
}
|
|
|
|
var DOMPropertyInjection = {
|
|
/**
|
|
* Mapping from normalized, camelcased property names to a configuration that
|
|
* specifies how the associated DOM property should be accessed or rendered.
|
|
*/
|
|
MUST_USE_ATTRIBUTE: 0x1,
|
|
MUST_USE_PROPERTY: 0x2,
|
|
HAS_SIDE_EFFECTS: 0x4,
|
|
HAS_BOOLEAN_VALUE: 0x8,
|
|
HAS_NUMERIC_VALUE: 0x10,
|
|
HAS_POSITIVE_NUMERIC_VALUE: 0x20 | 0x10,
|
|
HAS_OVERLOADED_BOOLEAN_VALUE: 0x40,
|
|
|
|
/**
|
|
* Inject some specialized knowledge about the DOM. This takes a config object
|
|
* with the following properties:
|
|
*
|
|
* isCustomAttribute: function that given an attribute name will return true
|
|
* if it can be inserted into the DOM verbatim. Useful for data-* or aria-*
|
|
* attributes where it's impossible to enumerate all of the possible
|
|
* attribute names,
|
|
*
|
|
* Properties: object mapping DOM property name to one of the
|
|
* DOMPropertyInjection constants or null. If your attribute isn't in here,
|
|
* it won't get written to the DOM.
|
|
*
|
|
* DOMAttributeNames: object mapping React attribute name to the DOM
|
|
* attribute name. Attribute names not specified use the **lowercase**
|
|
* normalized name.
|
|
*
|
|
* DOMAttributeNamespaces: object mapping React attribute name to the DOM
|
|
* attribute namespace URL. (Attribute names not specified use no namespace.)
|
|
*
|
|
* DOMPropertyNames: similar to DOMAttributeNames but for DOM properties.
|
|
* Property names not specified use the normalized name.
|
|
*
|
|
* DOMMutationMethods: Properties that require special mutation methods. If
|
|
* `value` is undefined, the mutation method should unset the property.
|
|
*
|
|
* @param {object} domPropertyConfig the config as described above.
|
|
*/
|
|
injectDOMPropertyConfig: function (domPropertyConfig) {
|
|
var Injection = DOMPropertyInjection;
|
|
var Properties = domPropertyConfig.Properties || {};
|
|
var DOMAttributeNamespaces = domPropertyConfig.DOMAttributeNamespaces || {};
|
|
var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {};
|
|
var DOMPropertyNames = domPropertyConfig.DOMPropertyNames || {};
|
|
var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {};
|
|
|
|
if (domPropertyConfig.isCustomAttribute) {
|
|
DOMProperty._isCustomAttributeFunctions.push(domPropertyConfig.isCustomAttribute);
|
|
}
|
|
|
|
for (var propName in Properties) {
|
|
!!DOMProperty.properties.hasOwnProperty(propName) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'injectDOMPropertyConfig(...): You\'re trying to inject DOM property ' + '\'%s\' which has already been injected. You may be accidentally ' + 'injecting the same DOM property config twice, or you may be ' + 'injecting two configs that have conflicting property names.', propName) : invariant(false) : undefined;
|
|
|
|
var lowerCased = propName.toLowerCase();
|
|
var propConfig = Properties[propName];
|
|
|
|
var propertyInfo = {
|
|
attributeName: lowerCased,
|
|
attributeNamespace: null,
|
|
propertyName: propName,
|
|
mutationMethod: null,
|
|
|
|
mustUseAttribute: checkMask(propConfig, Injection.MUST_USE_ATTRIBUTE),
|
|
mustUseProperty: checkMask(propConfig, Injection.MUST_USE_PROPERTY),
|
|
hasSideEffects: checkMask(propConfig, Injection.HAS_SIDE_EFFECTS),
|
|
hasBooleanValue: checkMask(propConfig, Injection.HAS_BOOLEAN_VALUE),
|
|
hasNumericValue: checkMask(propConfig, Injection.HAS_NUMERIC_VALUE),
|
|
hasPositiveNumericValue: checkMask(propConfig, Injection.HAS_POSITIVE_NUMERIC_VALUE),
|
|
hasOverloadedBooleanValue: checkMask(propConfig, Injection.HAS_OVERLOADED_BOOLEAN_VALUE)
|
|
};
|
|
|
|
!(!propertyInfo.mustUseAttribute || !propertyInfo.mustUseProperty) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'DOMProperty: Cannot require using both attribute and property: %s', propName) : invariant(false) : undefined;
|
|
!(propertyInfo.mustUseProperty || !propertyInfo.hasSideEffects) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'DOMProperty: Properties that have side effects must use property: %s', propName) : invariant(false) : undefined;
|
|
!(propertyInfo.hasBooleanValue + propertyInfo.hasNumericValue + propertyInfo.hasOverloadedBooleanValue <= 1) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'DOMProperty: Value can be one of boolean, overloaded boolean, or ' + 'numeric value, but not a combination: %s', propName) : invariant(false) : undefined;
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
DOMProperty.getPossibleStandardName[lowerCased] = propName;
|
|
}
|
|
|
|
if (DOMAttributeNames.hasOwnProperty(propName)) {
|
|
var attributeName = DOMAttributeNames[propName];
|
|
propertyInfo.attributeName = attributeName;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
DOMProperty.getPossibleStandardName[attributeName] = propName;
|
|
}
|
|
}
|
|
|
|
if (DOMAttributeNamespaces.hasOwnProperty(propName)) {
|
|
propertyInfo.attributeNamespace = DOMAttributeNamespaces[propName];
|
|
}
|
|
|
|
if (DOMPropertyNames.hasOwnProperty(propName)) {
|
|
propertyInfo.propertyName = DOMPropertyNames[propName];
|
|
}
|
|
|
|
if (DOMMutationMethods.hasOwnProperty(propName)) {
|
|
propertyInfo.mutationMethod = DOMMutationMethods[propName];
|
|
}
|
|
|
|
DOMProperty.properties[propName] = propertyInfo;
|
|
}
|
|
}
|
|
};
|
|
var defaultValueCache = {};
|
|
|
|
/**
|
|
* DOMProperty exports lookup objects that can be used like functions:
|
|
*
|
|
* > DOMProperty.isValid['id']
|
|
* true
|
|
* > DOMProperty.isValid['foobar']
|
|
* undefined
|
|
*
|
|
* Although this may be confusing, it performs better in general.
|
|
*
|
|
* @see http://jsperf.com/key-exists
|
|
* @see http://jsperf.com/key-missing
|
|
*/
|
|
var DOMProperty = {
|
|
|
|
ID_ATTRIBUTE_NAME: 'data-reactid',
|
|
|
|
/**
|
|
* Map from property "standard name" to an object with info about how to set
|
|
* the property in the DOM. Each object contains:
|
|
*
|
|
* attributeName:
|
|
* Used when rendering markup or with `*Attribute()`.
|
|
* attributeNamespace
|
|
* propertyName:
|
|
* Used on DOM node instances. (This includes properties that mutate due to
|
|
* external factors.)
|
|
* mutationMethod:
|
|
* If non-null, used instead of the property or `setAttribute()` after
|
|
* initial render.
|
|
* mustUseAttribute:
|
|
* Whether the property must be accessed and mutated using `*Attribute()`.
|
|
* (This includes anything that fails `<propName> in <element>`.)
|
|
* mustUseProperty:
|
|
* Whether the property must be accessed and mutated as an object property.
|
|
* hasSideEffects:
|
|
* Whether or not setting a value causes side effects such as triggering
|
|
* resources to be loaded or text selection changes. If true, we read from
|
|
* the DOM before updating to ensure that the value is only set if it has
|
|
* changed.
|
|
* hasBooleanValue:
|
|
* Whether the property should be removed when set to a falsey value.
|
|
* hasNumericValue:
|
|
* Whether the property must be numeric or parse as a numeric and should be
|
|
* removed when set to a falsey value.
|
|
* hasPositiveNumericValue:
|
|
* Whether the property must be positive numeric or parse as a positive
|
|
* numeric and should be removed when set to a falsey value.
|
|
* hasOverloadedBooleanValue:
|
|
* Whether the property can be used as a flag as well as with a value.
|
|
* Removed when strictly equal to false; present without a value when
|
|
* strictly equal to true; present with a value otherwise.
|
|
*/
|
|
properties: {},
|
|
|
|
/**
|
|
* Mapping from lowercase property names to the properly cased version, used
|
|
* to warn in the case of missing properties. Available only in __DEV__.
|
|
* @type {Object}
|
|
*/
|
|
getPossibleStandardName: process.env.NODE_ENV !== 'production' ? {} : null,
|
|
|
|
/**
|
|
* All of the isCustomAttribute() functions that have been injected.
|
|
*/
|
|
_isCustomAttributeFunctions: [],
|
|
|
|
/**
|
|
* Checks whether a property name is a custom attribute.
|
|
* @method
|
|
*/
|
|
isCustomAttribute: function (attributeName) {
|
|
for (var i = 0; i < DOMProperty._isCustomAttributeFunctions.length; i++) {
|
|
var isCustomAttributeFn = DOMProperty._isCustomAttributeFunctions[i];
|
|
if (isCustomAttributeFn(attributeName)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Returns the default property value for a DOM property (i.e., not an
|
|
* attribute). Most default values are '' or false, but not all. Worse yet,
|
|
* some (in particular, `type`) vary depending on the type of element.
|
|
*
|
|
* TODO: Is it better to grab all the possible properties when creating an
|
|
* element to avoid having to create the same element twice?
|
|
*/
|
|
getDefaultValueForProperty: function (nodeName, prop) {
|
|
var nodeDefaults = defaultValueCache[nodeName];
|
|
var testElement;
|
|
if (!nodeDefaults) {
|
|
defaultValueCache[nodeName] = nodeDefaults = {};
|
|
}
|
|
if (!(prop in nodeDefaults)) {
|
|
testElement = document.createElement(nodeName);
|
|
nodeDefaults[prop] = testElement[prop];
|
|
}
|
|
return nodeDefaults[prop];
|
|
},
|
|
|
|
injection: DOMPropertyInjection
|
|
};
|
|
|
|
module.exports = DOMProperty;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133,"fbjs/lib/invariant":72}],145:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule DOMPropertyOperations
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var DOMProperty = require('./DOMProperty');
|
|
var ReactPerf = require('./ReactPerf');
|
|
|
|
var quoteAttributeValueForBrowser = require('./quoteAttributeValueForBrowser');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
// Simplified subset
|
|
var VALID_ATTRIBUTE_NAME_REGEX = /^[a-zA-Z_][\w\.\-]*$/;
|
|
var illegalAttributeNameCache = {};
|
|
var validatedAttributeNameCache = {};
|
|
|
|
function isAttributeNameSafe(attributeName) {
|
|
if (validatedAttributeNameCache.hasOwnProperty(attributeName)) {
|
|
return true;
|
|
}
|
|
if (illegalAttributeNameCache.hasOwnProperty(attributeName)) {
|
|
return false;
|
|
}
|
|
if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) {
|
|
validatedAttributeNameCache[attributeName] = true;
|
|
return true;
|
|
}
|
|
illegalAttributeNameCache[attributeName] = true;
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Invalid attribute name: `%s`', attributeName) : undefined;
|
|
return false;
|
|
}
|
|
|
|
function shouldIgnoreValue(propertyInfo, value) {
|
|
return value == null || propertyInfo.hasBooleanValue && !value || propertyInfo.hasNumericValue && isNaN(value) || propertyInfo.hasPositiveNumericValue && value < 1 || propertyInfo.hasOverloadedBooleanValue && value === false;
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var reactProps = {
|
|
children: true,
|
|
dangerouslySetInnerHTML: true,
|
|
key: true,
|
|
ref: true
|
|
};
|
|
var warnedProperties = {};
|
|
|
|
var warnUnknownProperty = function (name) {
|
|
if (reactProps.hasOwnProperty(name) && reactProps[name] || warnedProperties.hasOwnProperty(name) && warnedProperties[name]) {
|
|
return;
|
|
}
|
|
|
|
warnedProperties[name] = true;
|
|
var lowerCasedName = name.toLowerCase();
|
|
|
|
// data-* attributes should be lowercase; suggest the lowercase version
|
|
var standardName = DOMProperty.isCustomAttribute(lowerCasedName) ? lowerCasedName : DOMProperty.getPossibleStandardName.hasOwnProperty(lowerCasedName) ? DOMProperty.getPossibleStandardName[lowerCasedName] : null;
|
|
|
|
// For now, only warn when we have a suggested correction. This prevents
|
|
// logging too much when using transferPropsTo.
|
|
process.env.NODE_ENV !== 'production' ? warning(standardName == null, 'Unknown DOM property %s. Did you mean %s?', name, standardName) : undefined;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Operations for dealing with DOM properties.
|
|
*/
|
|
var DOMPropertyOperations = {
|
|
|
|
/**
|
|
* Creates markup for the ID property.
|
|
*
|
|
* @param {string} id Unescaped ID.
|
|
* @return {string} Markup string.
|
|
*/
|
|
createMarkupForID: function (id) {
|
|
return DOMProperty.ID_ATTRIBUTE_NAME + '=' + quoteAttributeValueForBrowser(id);
|
|
},
|
|
|
|
setAttributeForID: function (node, id) {
|
|
node.setAttribute(DOMProperty.ID_ATTRIBUTE_NAME, id);
|
|
},
|
|
|
|
/**
|
|
* Creates markup for a property.
|
|
*
|
|
* @param {string} name
|
|
* @param {*} value
|
|
* @return {?string} Markup string, or null if the property was invalid.
|
|
*/
|
|
createMarkupForProperty: function (name, value) {
|
|
var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null;
|
|
if (propertyInfo) {
|
|
if (shouldIgnoreValue(propertyInfo, value)) {
|
|
return '';
|
|
}
|
|
var attributeName = propertyInfo.attributeName;
|
|
if (propertyInfo.hasBooleanValue || propertyInfo.hasOverloadedBooleanValue && value === true) {
|
|
return attributeName + '=""';
|
|
}
|
|
return attributeName + '=' + quoteAttributeValueForBrowser(value);
|
|
} else if (DOMProperty.isCustomAttribute(name)) {
|
|
if (value == null) {
|
|
return '';
|
|
}
|
|
return name + '=' + quoteAttributeValueForBrowser(value);
|
|
} else if (process.env.NODE_ENV !== 'production') {
|
|
warnUnknownProperty(name);
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
* Creates markup for a custom property.
|
|
*
|
|
* @param {string} name
|
|
* @param {*} value
|
|
* @return {string} Markup string, or empty string if the property was invalid.
|
|
*/
|
|
createMarkupForCustomAttribute: function (name, value) {
|
|
if (!isAttributeNameSafe(name) || value == null) {
|
|
return '';
|
|
}
|
|
return name + '=' + quoteAttributeValueForBrowser(value);
|
|
},
|
|
|
|
/**
|
|
* Sets the value for a property on a node.
|
|
*
|
|
* @param {DOMElement} node
|
|
* @param {string} name
|
|
* @param {*} value
|
|
*/
|
|
setValueForProperty: function (node, name, value) {
|
|
var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null;
|
|
if (propertyInfo) {
|
|
var mutationMethod = propertyInfo.mutationMethod;
|
|
if (mutationMethod) {
|
|
mutationMethod(node, value);
|
|
} else if (shouldIgnoreValue(propertyInfo, value)) {
|
|
this.deleteValueForProperty(node, name);
|
|
} else if (propertyInfo.mustUseAttribute) {
|
|
var attributeName = propertyInfo.attributeName;
|
|
var namespace = propertyInfo.attributeNamespace;
|
|
// `setAttribute` with objects becomes only `[object]` in IE8/9,
|
|
// ('' + value) makes it output the correct toString()-value.
|
|
if (namespace) {
|
|
node.setAttributeNS(namespace, attributeName, '' + value);
|
|
} else if (propertyInfo.hasBooleanValue || propertyInfo.hasOverloadedBooleanValue && value === true) {
|
|
node.setAttribute(attributeName, '');
|
|
} else {
|
|
node.setAttribute(attributeName, '' + value);
|
|
}
|
|
} else {
|
|
var propName = propertyInfo.propertyName;
|
|
// Must explicitly cast values for HAS_SIDE_EFFECTS-properties to the
|
|
// property type before comparing; only `value` does and is string.
|
|
if (!propertyInfo.hasSideEffects || '' + node[propName] !== '' + value) {
|
|
// Contrary to `setAttribute`, object properties are properly
|
|
// `toString`ed by IE8/9.
|
|
node[propName] = value;
|
|
}
|
|
}
|
|
} else if (DOMProperty.isCustomAttribute(name)) {
|
|
DOMPropertyOperations.setValueForAttribute(node, name, value);
|
|
} else if (process.env.NODE_ENV !== 'production') {
|
|
warnUnknownProperty(name);
|
|
}
|
|
},
|
|
|
|
setValueForAttribute: function (node, name, value) {
|
|
if (!isAttributeNameSafe(name)) {
|
|
return;
|
|
}
|
|
if (value == null) {
|
|
node.removeAttribute(name);
|
|
} else {
|
|
node.setAttribute(name, '' + value);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Deletes the value for a property on a node.
|
|
*
|
|
* @param {DOMElement} node
|
|
* @param {string} name
|
|
*/
|
|
deleteValueForProperty: function (node, name) {
|
|
var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null;
|
|
if (propertyInfo) {
|
|
var mutationMethod = propertyInfo.mutationMethod;
|
|
if (mutationMethod) {
|
|
mutationMethod(node, undefined);
|
|
} else if (propertyInfo.mustUseAttribute) {
|
|
node.removeAttribute(propertyInfo.attributeName);
|
|
} else {
|
|
var propName = propertyInfo.propertyName;
|
|
var defaultValue = DOMProperty.getDefaultValueForProperty(node.nodeName, propName);
|
|
if (!propertyInfo.hasSideEffects || '' + node[propName] !== defaultValue) {
|
|
node[propName] = defaultValue;
|
|
}
|
|
}
|
|
} else if (DOMProperty.isCustomAttribute(name)) {
|
|
node.removeAttribute(name);
|
|
} else if (process.env.NODE_ENV !== 'production') {
|
|
warnUnknownProperty(name);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
ReactPerf.measureMethods(DOMPropertyOperations, 'DOMPropertyOperations', {
|
|
setValueForProperty: 'setValueForProperty',
|
|
setValueForAttribute: 'setValueForAttribute',
|
|
deleteValueForProperty: 'deleteValueForProperty'
|
|
});
|
|
|
|
module.exports = DOMPropertyOperations;
|
|
}).call(this,require('_process'))
|
|
},{"./DOMProperty":144,"./ReactPerf":206,"./quoteAttributeValueForBrowser":257,"_process":133,"fbjs/lib/warning":83}],146:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule Danger
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
|
|
var createNodesFromMarkup = require('fbjs/lib/createNodesFromMarkup');
|
|
var emptyFunction = require('fbjs/lib/emptyFunction');
|
|
var getMarkupWrap = require('fbjs/lib/getMarkupWrap');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
var OPEN_TAG_NAME_EXP = /^(<[^ \/>]+)/;
|
|
var RESULT_INDEX_ATTR = 'data-danger-index';
|
|
|
|
/**
|
|
* Extracts the `nodeName` from a string of markup.
|
|
*
|
|
* NOTE: Extracting the `nodeName` does not require a regular expression match
|
|
* because we make assumptions about React-generated markup (i.e. there are no
|
|
* spaces surrounding the opening tag and there is at least one attribute).
|
|
*
|
|
* @param {string} markup String of markup.
|
|
* @return {string} Node name of the supplied markup.
|
|
* @see http://jsperf.com/extract-nodename
|
|
*/
|
|
function getNodeName(markup) {
|
|
return markup.substring(1, markup.indexOf(' '));
|
|
}
|
|
|
|
var Danger = {
|
|
|
|
/**
|
|
* Renders markup into an array of nodes. The markup is expected to render
|
|
* into a list of root nodes. Also, the length of `resultList` and
|
|
* `markupList` should be the same.
|
|
*
|
|
* @param {array<string>} markupList List of markup strings to render.
|
|
* @return {array<DOMElement>} List of rendered nodes.
|
|
* @internal
|
|
*/
|
|
dangerouslyRenderMarkup: function (markupList) {
|
|
!ExecutionEnvironment.canUseDOM ? process.env.NODE_ENV !== 'production' ? invariant(false, 'dangerouslyRenderMarkup(...): Cannot render markup in a worker ' + 'thread. Make sure `window` and `document` are available globally ' + 'before requiring React when unit testing or use ' + 'ReactDOMServer.renderToString for server rendering.') : invariant(false) : undefined;
|
|
var nodeName;
|
|
var markupByNodeName = {};
|
|
// Group markup by `nodeName` if a wrap is necessary, else by '*'.
|
|
for (var i = 0; i < markupList.length; i++) {
|
|
!markupList[i] ? process.env.NODE_ENV !== 'production' ? invariant(false, 'dangerouslyRenderMarkup(...): Missing markup.') : invariant(false) : undefined;
|
|
nodeName = getNodeName(markupList[i]);
|
|
nodeName = getMarkupWrap(nodeName) ? nodeName : '*';
|
|
markupByNodeName[nodeName] = markupByNodeName[nodeName] || [];
|
|
markupByNodeName[nodeName][i] = markupList[i];
|
|
}
|
|
var resultList = [];
|
|
var resultListAssignmentCount = 0;
|
|
for (nodeName in markupByNodeName) {
|
|
if (!markupByNodeName.hasOwnProperty(nodeName)) {
|
|
continue;
|
|
}
|
|
var markupListByNodeName = markupByNodeName[nodeName];
|
|
|
|
// This for-in loop skips the holes of the sparse array. The order of
|
|
// iteration should follow the order of assignment, which happens to match
|
|
// numerical index order, but we don't rely on that.
|
|
var resultIndex;
|
|
for (resultIndex in markupListByNodeName) {
|
|
if (markupListByNodeName.hasOwnProperty(resultIndex)) {
|
|
var markup = markupListByNodeName[resultIndex];
|
|
|
|
// Push the requested markup with an additional RESULT_INDEX_ATTR
|
|
// attribute. If the markup does not start with a < character, it
|
|
// will be discarded below (with an appropriate console.error).
|
|
markupListByNodeName[resultIndex] = markup.replace(OPEN_TAG_NAME_EXP,
|
|
// This index will be parsed back out below.
|
|
'$1 ' + RESULT_INDEX_ATTR + '="' + resultIndex + '" ');
|
|
}
|
|
}
|
|
|
|
// Render each group of markup with similar wrapping `nodeName`.
|
|
var renderNodes = createNodesFromMarkup(markupListByNodeName.join(''), emptyFunction // Do nothing special with <script> tags.
|
|
);
|
|
|
|
for (var j = 0; j < renderNodes.length; ++j) {
|
|
var renderNode = renderNodes[j];
|
|
if (renderNode.hasAttribute && renderNode.hasAttribute(RESULT_INDEX_ATTR)) {
|
|
|
|
resultIndex = +renderNode.getAttribute(RESULT_INDEX_ATTR);
|
|
renderNode.removeAttribute(RESULT_INDEX_ATTR);
|
|
|
|
!!resultList.hasOwnProperty(resultIndex) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Danger: Assigning to an already-occupied result index.') : invariant(false) : undefined;
|
|
|
|
resultList[resultIndex] = renderNode;
|
|
|
|
// This should match resultList.length and markupList.length when
|
|
// we're done.
|
|
resultListAssignmentCount += 1;
|
|
} else if (process.env.NODE_ENV !== 'production') {
|
|
console.error('Danger: Discarding unexpected node:', renderNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Although resultList was populated out of order, it should now be a dense
|
|
// array.
|
|
!(resultListAssignmentCount === resultList.length) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Danger: Did not assign to every index of resultList.') : invariant(false) : undefined;
|
|
|
|
!(resultList.length === markupList.length) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Danger: Expected markup to render %s nodes, but rendered %s.', markupList.length, resultList.length) : invariant(false) : undefined;
|
|
|
|
return resultList;
|
|
},
|
|
|
|
/**
|
|
* Replaces a node with a string of markup at its current position within its
|
|
* parent. The markup must render into a single root node.
|
|
*
|
|
* @param {DOMElement} oldChild Child node to replace.
|
|
* @param {string} markup Markup to render in place of the child node.
|
|
* @internal
|
|
*/
|
|
dangerouslyReplaceNodeWithMarkup: function (oldChild, markup) {
|
|
!ExecutionEnvironment.canUseDOM ? process.env.NODE_ENV !== 'production' ? invariant(false, 'dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a ' + 'worker thread. Make sure `window` and `document` are available ' + 'globally before requiring React when unit testing or use ' + 'ReactDOMServer.renderToString() for server rendering.') : invariant(false) : undefined;
|
|
!markup ? process.env.NODE_ENV !== 'production' ? invariant(false, 'dangerouslyReplaceNodeWithMarkup(...): Missing markup.') : invariant(false) : undefined;
|
|
!(oldChild.tagName.toLowerCase() !== 'html') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'dangerouslyReplaceNodeWithMarkup(...): Cannot replace markup of the ' + '<html> node. This is because browser quirks make this unreliable ' + 'and/or slow. If you want to render to the root you must use ' + 'server rendering. See ReactDOMServer.renderToString().') : invariant(false) : undefined;
|
|
|
|
var newChild;
|
|
if (typeof markup === 'string') {
|
|
newChild = createNodesFromMarkup(markup, emptyFunction)[0];
|
|
} else {
|
|
newChild = markup;
|
|
}
|
|
oldChild.parentNode.replaceChild(newChild, oldChild);
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = Danger;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133,"fbjs/lib/ExecutionEnvironment":58,"fbjs/lib/createNodesFromMarkup":63,"fbjs/lib/emptyFunction":64,"fbjs/lib/getMarkupWrap":68,"fbjs/lib/invariant":72}],147:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule DefaultEventPluginOrder
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var keyOf = require('fbjs/lib/keyOf');
|
|
|
|
/**
|
|
* Module that is injectable into `EventPluginHub`, that specifies a
|
|
* deterministic ordering of `EventPlugin`s. A convenient way to reason about
|
|
* plugins, without having to package every one of them. This is better than
|
|
* having plugins be ordered in the same order that they are injected because
|
|
* that ordering would be influenced by the packaging order.
|
|
* `ResponderEventPlugin` must occur before `SimpleEventPlugin` so that
|
|
* preventing default on events is convenient in `SimpleEventPlugin` handlers.
|
|
*/
|
|
var DefaultEventPluginOrder = [keyOf({ ResponderEventPlugin: null }), keyOf({ SimpleEventPlugin: null }), keyOf({ TapEventPlugin: null }), keyOf({ EnterLeaveEventPlugin: null }), keyOf({ ChangeEventPlugin: null }), keyOf({ SelectEventPlugin: null }), keyOf({ BeforeInputEventPlugin: null })];
|
|
|
|
module.exports = DefaultEventPluginOrder;
|
|
},{"fbjs/lib/keyOf":76}],148:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule EnterLeaveEventPlugin
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var EventConstants = require('./EventConstants');
|
|
var EventPropagators = require('./EventPropagators');
|
|
var SyntheticMouseEvent = require('./SyntheticMouseEvent');
|
|
|
|
var ReactMount = require('./ReactMount');
|
|
var keyOf = require('fbjs/lib/keyOf');
|
|
|
|
var topLevelTypes = EventConstants.topLevelTypes;
|
|
var getFirstReactDOM = ReactMount.getFirstReactDOM;
|
|
|
|
var eventTypes = {
|
|
mouseEnter: {
|
|
registrationName: keyOf({ onMouseEnter: null }),
|
|
dependencies: [topLevelTypes.topMouseOut, topLevelTypes.topMouseOver]
|
|
},
|
|
mouseLeave: {
|
|
registrationName: keyOf({ onMouseLeave: null }),
|
|
dependencies: [topLevelTypes.topMouseOut, topLevelTypes.topMouseOver]
|
|
}
|
|
};
|
|
|
|
var extractedEvents = [null, null];
|
|
|
|
var EnterLeaveEventPlugin = {
|
|
|
|
eventTypes: eventTypes,
|
|
|
|
/**
|
|
* For almost every interaction we care about, there will be both a top-level
|
|
* `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that
|
|
* we do not extract duplicate events. However, moving the mouse into the
|
|
* browser from outside will not fire a `mouseout` event. In this case, we use
|
|
* the `mouseover` top-level event.
|
|
*
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {DOMEventTarget} topLevelTarget The listening component root node.
|
|
* @param {string} topLevelTargetID ID of `topLevelTarget`.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {*} An accumulation of synthetic events.
|
|
* @see {EventPluginHub.extractEvents}
|
|
*/
|
|
extractEvents: function (topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget) {
|
|
if (topLevelType === topLevelTypes.topMouseOver && (nativeEvent.relatedTarget || nativeEvent.fromElement)) {
|
|
return null;
|
|
}
|
|
if (topLevelType !== topLevelTypes.topMouseOut && topLevelType !== topLevelTypes.topMouseOver) {
|
|
// Must not be a mouse in or mouse out - ignoring.
|
|
return null;
|
|
}
|
|
|
|
var win;
|
|
if (topLevelTarget.window === topLevelTarget) {
|
|
// `topLevelTarget` is probably a window object.
|
|
win = topLevelTarget;
|
|
} else {
|
|
// TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
|
|
var doc = topLevelTarget.ownerDocument;
|
|
if (doc) {
|
|
win = doc.defaultView || doc.parentWindow;
|
|
} else {
|
|
win = window;
|
|
}
|
|
}
|
|
|
|
var from;
|
|
var to;
|
|
var fromID = '';
|
|
var toID = '';
|
|
if (topLevelType === topLevelTypes.topMouseOut) {
|
|
from = topLevelTarget;
|
|
fromID = topLevelTargetID;
|
|
to = getFirstReactDOM(nativeEvent.relatedTarget || nativeEvent.toElement);
|
|
if (to) {
|
|
toID = ReactMount.getID(to);
|
|
} else {
|
|
to = win;
|
|
}
|
|
to = to || win;
|
|
} else {
|
|
from = win;
|
|
to = topLevelTarget;
|
|
toID = topLevelTargetID;
|
|
}
|
|
|
|
if (from === to) {
|
|
// Nothing pertains to our managed components.
|
|
return null;
|
|
}
|
|
|
|
var leave = SyntheticMouseEvent.getPooled(eventTypes.mouseLeave, fromID, nativeEvent, nativeEventTarget);
|
|
leave.type = 'mouseleave';
|
|
leave.target = from;
|
|
leave.relatedTarget = to;
|
|
|
|
var enter = SyntheticMouseEvent.getPooled(eventTypes.mouseEnter, toID, nativeEvent, nativeEventTarget);
|
|
enter.type = 'mouseenter';
|
|
enter.target = to;
|
|
enter.relatedTarget = from;
|
|
|
|
EventPropagators.accumulateEnterLeaveDispatches(leave, enter, fromID, toID);
|
|
|
|
extractedEvents[0] = leave;
|
|
extractedEvents[1] = enter;
|
|
|
|
return extractedEvents;
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = EnterLeaveEventPlugin;
|
|
},{"./EventConstants":149,"./EventPropagators":153,"./ReactMount":200,"./SyntheticMouseEvent":231,"fbjs/lib/keyOf":76}],149:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule EventConstants
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var keyMirror = require('fbjs/lib/keyMirror');
|
|
|
|
var PropagationPhases = keyMirror({ bubbled: null, captured: null });
|
|
|
|
/**
|
|
* Types of raw signals from the browser caught at the top level.
|
|
*/
|
|
var topLevelTypes = keyMirror({
|
|
topAbort: null,
|
|
topBlur: null,
|
|
topCanPlay: null,
|
|
topCanPlayThrough: null,
|
|
topChange: null,
|
|
topClick: null,
|
|
topCompositionEnd: null,
|
|
topCompositionStart: null,
|
|
topCompositionUpdate: null,
|
|
topContextMenu: null,
|
|
topCopy: null,
|
|
topCut: null,
|
|
topDoubleClick: null,
|
|
topDrag: null,
|
|
topDragEnd: null,
|
|
topDragEnter: null,
|
|
topDragExit: null,
|
|
topDragLeave: null,
|
|
topDragOver: null,
|
|
topDragStart: null,
|
|
topDrop: null,
|
|
topDurationChange: null,
|
|
topEmptied: null,
|
|
topEncrypted: null,
|
|
topEnded: null,
|
|
topError: null,
|
|
topFocus: null,
|
|
topInput: null,
|
|
topKeyDown: null,
|
|
topKeyPress: null,
|
|
topKeyUp: null,
|
|
topLoad: null,
|
|
topLoadedData: null,
|
|
topLoadedMetadata: null,
|
|
topLoadStart: null,
|
|
topMouseDown: null,
|
|
topMouseMove: null,
|
|
topMouseOut: null,
|
|
topMouseOver: null,
|
|
topMouseUp: null,
|
|
topPaste: null,
|
|
topPause: null,
|
|
topPlay: null,
|
|
topPlaying: null,
|
|
topProgress: null,
|
|
topRateChange: null,
|
|
topReset: null,
|
|
topScroll: null,
|
|
topSeeked: null,
|
|
topSeeking: null,
|
|
topSelectionChange: null,
|
|
topStalled: null,
|
|
topSubmit: null,
|
|
topSuspend: null,
|
|
topTextInput: null,
|
|
topTimeUpdate: null,
|
|
topTouchCancel: null,
|
|
topTouchEnd: null,
|
|
topTouchMove: null,
|
|
topTouchStart: null,
|
|
topVolumeChange: null,
|
|
topWaiting: null,
|
|
topWheel: null
|
|
});
|
|
|
|
var EventConstants = {
|
|
topLevelTypes: topLevelTypes,
|
|
PropagationPhases: PropagationPhases
|
|
};
|
|
|
|
module.exports = EventConstants;
|
|
},{"fbjs/lib/keyMirror":75}],150:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule EventPluginHub
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var EventPluginRegistry = require('./EventPluginRegistry');
|
|
var EventPluginUtils = require('./EventPluginUtils');
|
|
var ReactErrorUtils = require('./ReactErrorUtils');
|
|
|
|
var accumulateInto = require('./accumulateInto');
|
|
var forEachAccumulated = require('./forEachAccumulated');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
/**
|
|
* Internal store for event listeners
|
|
*/
|
|
var listenerBank = {};
|
|
|
|
/**
|
|
* Internal queue of events that have accumulated their dispatches and are
|
|
* waiting to have their dispatches executed.
|
|
*/
|
|
var eventQueue = null;
|
|
|
|
/**
|
|
* Dispatches an event and releases it back into the pool, unless persistent.
|
|
*
|
|
* @param {?object} event Synthetic event to be dispatched.
|
|
* @param {boolean} simulated If the event is simulated (changes exn behavior)
|
|
* @private
|
|
*/
|
|
var executeDispatchesAndRelease = function (event, simulated) {
|
|
if (event) {
|
|
EventPluginUtils.executeDispatchesInOrder(event, simulated);
|
|
|
|
if (!event.isPersistent()) {
|
|
event.constructor.release(event);
|
|
}
|
|
}
|
|
};
|
|
var executeDispatchesAndReleaseSimulated = function (e) {
|
|
return executeDispatchesAndRelease(e, true);
|
|
};
|
|
var executeDispatchesAndReleaseTopLevel = function (e) {
|
|
return executeDispatchesAndRelease(e, false);
|
|
};
|
|
|
|
/**
|
|
* - `InstanceHandle`: [required] Module that performs logical traversals of DOM
|
|
* hierarchy given ids of the logical DOM elements involved.
|
|
*/
|
|
var InstanceHandle = null;
|
|
|
|
function validateInstanceHandle() {
|
|
var valid = InstanceHandle && InstanceHandle.traverseTwoPhase && InstanceHandle.traverseEnterLeave;
|
|
process.env.NODE_ENV !== 'production' ? warning(valid, 'InstanceHandle not injected before use!') : undefined;
|
|
}
|
|
|
|
/**
|
|
* This is a unified interface for event plugins to be installed and configured.
|
|
*
|
|
* Event plugins can implement the following properties:
|
|
*
|
|
* `extractEvents` {function(string, DOMEventTarget, string, object): *}
|
|
* Required. When a top-level event is fired, this method is expected to
|
|
* extract synthetic events that will in turn be queued and dispatched.
|
|
*
|
|
* `eventTypes` {object}
|
|
* Optional, plugins that fire events must publish a mapping of registration
|
|
* names that are used to register listeners. Values of this mapping must
|
|
* be objects that contain `registrationName` or `phasedRegistrationNames`.
|
|
*
|
|
* `executeDispatch` {function(object, function, string)}
|
|
* Optional, allows plugins to override how an event gets dispatched. By
|
|
* default, the listener is simply invoked.
|
|
*
|
|
* Each plugin that is injected into `EventsPluginHub` is immediately operable.
|
|
*
|
|
* @public
|
|
*/
|
|
var EventPluginHub = {
|
|
|
|
/**
|
|
* Methods for injecting dependencies.
|
|
*/
|
|
injection: {
|
|
|
|
/**
|
|
* @param {object} InjectedMount
|
|
* @public
|
|
*/
|
|
injectMount: EventPluginUtils.injection.injectMount,
|
|
|
|
/**
|
|
* @param {object} InjectedInstanceHandle
|
|
* @public
|
|
*/
|
|
injectInstanceHandle: function (InjectedInstanceHandle) {
|
|
InstanceHandle = InjectedInstanceHandle;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
validateInstanceHandle();
|
|
}
|
|
},
|
|
|
|
getInstanceHandle: function () {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
validateInstanceHandle();
|
|
}
|
|
return InstanceHandle;
|
|
},
|
|
|
|
/**
|
|
* @param {array} InjectedEventPluginOrder
|
|
* @public
|
|
*/
|
|
injectEventPluginOrder: EventPluginRegistry.injectEventPluginOrder,
|
|
|
|
/**
|
|
* @param {object} injectedNamesToPlugins Map from names to plugin modules.
|
|
*/
|
|
injectEventPluginsByName: EventPluginRegistry.injectEventPluginsByName
|
|
|
|
},
|
|
|
|
eventNameDispatchConfigs: EventPluginRegistry.eventNameDispatchConfigs,
|
|
|
|
registrationNameModules: EventPluginRegistry.registrationNameModules,
|
|
|
|
/**
|
|
* Stores `listener` at `listenerBank[registrationName][id]`. Is idempotent.
|
|
*
|
|
* @param {string} id ID of the DOM element.
|
|
* @param {string} registrationName Name of listener (e.g. `onClick`).
|
|
* @param {?function} listener The callback to store.
|
|
*/
|
|
putListener: function (id, registrationName, listener) {
|
|
!(typeof listener === 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Expected %s listener to be a function, instead got type %s', registrationName, typeof listener) : invariant(false) : undefined;
|
|
|
|
var bankForRegistrationName = listenerBank[registrationName] || (listenerBank[registrationName] = {});
|
|
bankForRegistrationName[id] = listener;
|
|
|
|
var PluginModule = EventPluginRegistry.registrationNameModules[registrationName];
|
|
if (PluginModule && PluginModule.didPutListener) {
|
|
PluginModule.didPutListener(id, registrationName, listener);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param {string} id ID of the DOM element.
|
|
* @param {string} registrationName Name of listener (e.g. `onClick`).
|
|
* @return {?function} The stored callback.
|
|
*/
|
|
getListener: function (id, registrationName) {
|
|
var bankForRegistrationName = listenerBank[registrationName];
|
|
return bankForRegistrationName && bankForRegistrationName[id];
|
|
},
|
|
|
|
/**
|
|
* Deletes a listener from the registration bank.
|
|
*
|
|
* @param {string} id ID of the DOM element.
|
|
* @param {string} registrationName Name of listener (e.g. `onClick`).
|
|
*/
|
|
deleteListener: function (id, registrationName) {
|
|
var PluginModule = EventPluginRegistry.registrationNameModules[registrationName];
|
|
if (PluginModule && PluginModule.willDeleteListener) {
|
|
PluginModule.willDeleteListener(id, registrationName);
|
|
}
|
|
|
|
var bankForRegistrationName = listenerBank[registrationName];
|
|
// TODO: This should never be null -- when is it?
|
|
if (bankForRegistrationName) {
|
|
delete bankForRegistrationName[id];
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Deletes all listeners for the DOM element with the supplied ID.
|
|
*
|
|
* @param {string} id ID of the DOM element.
|
|
*/
|
|
deleteAllListeners: function (id) {
|
|
for (var registrationName in listenerBank) {
|
|
if (!listenerBank[registrationName][id]) {
|
|
continue;
|
|
}
|
|
|
|
var PluginModule = EventPluginRegistry.registrationNameModules[registrationName];
|
|
if (PluginModule && PluginModule.willDeleteListener) {
|
|
PluginModule.willDeleteListener(id, registrationName);
|
|
}
|
|
|
|
delete listenerBank[registrationName][id];
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Allows registered plugins an opportunity to extract events from top-level
|
|
* native browser events.
|
|
*
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {DOMEventTarget} topLevelTarget The listening component root node.
|
|
* @param {string} topLevelTargetID ID of `topLevelTarget`.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {*} An accumulation of synthetic events.
|
|
* @internal
|
|
*/
|
|
extractEvents: function (topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget) {
|
|
var events;
|
|
var plugins = EventPluginRegistry.plugins;
|
|
for (var i = 0; i < plugins.length; i++) {
|
|
// Not every plugin in the ordering may be loaded at runtime.
|
|
var possiblePlugin = plugins[i];
|
|
if (possiblePlugin) {
|
|
var extractedEvents = possiblePlugin.extractEvents(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget);
|
|
if (extractedEvents) {
|
|
events = accumulateInto(events, extractedEvents);
|
|
}
|
|
}
|
|
}
|
|
return events;
|
|
},
|
|
|
|
/**
|
|
* Enqueues a synthetic event that should be dispatched when
|
|
* `processEventQueue` is invoked.
|
|
*
|
|
* @param {*} events An accumulation of synthetic events.
|
|
* @internal
|
|
*/
|
|
enqueueEvents: function (events) {
|
|
if (events) {
|
|
eventQueue = accumulateInto(eventQueue, events);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Dispatches all synthetic events on the event queue.
|
|
*
|
|
* @internal
|
|
*/
|
|
processEventQueue: function (simulated) {
|
|
// Set `eventQueue` to null before processing it so that we can tell if more
|
|
// events get enqueued while processing.
|
|
var processingEventQueue = eventQueue;
|
|
eventQueue = null;
|
|
if (simulated) {
|
|
forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseSimulated);
|
|
} else {
|
|
forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
|
|
}
|
|
!!eventQueue ? process.env.NODE_ENV !== 'production' ? invariant(false, 'processEventQueue(): Additional events were enqueued while processing ' + 'an event queue. Support for this has not yet been implemented.') : invariant(false) : undefined;
|
|
// This would be a good time to rethrow if any of the event handlers threw.
|
|
ReactErrorUtils.rethrowCaughtError();
|
|
},
|
|
|
|
/**
|
|
* These are needed for tests only. Do not use!
|
|
*/
|
|
__purge: function () {
|
|
listenerBank = {};
|
|
},
|
|
|
|
__getListenerBank: function () {
|
|
return listenerBank;
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = EventPluginHub;
|
|
}).call(this,require('_process'))
|
|
},{"./EventPluginRegistry":151,"./EventPluginUtils":152,"./ReactErrorUtils":191,"./accumulateInto":237,"./forEachAccumulated":245,"_process":133,"fbjs/lib/invariant":72,"fbjs/lib/warning":83}],151:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule EventPluginRegistry
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
/**
|
|
* Injectable ordering of event plugins.
|
|
*/
|
|
var EventPluginOrder = null;
|
|
|
|
/**
|
|
* Injectable mapping from names to event plugin modules.
|
|
*/
|
|
var namesToPlugins = {};
|
|
|
|
/**
|
|
* Recomputes the plugin list using the injected plugins and plugin ordering.
|
|
*
|
|
* @private
|
|
*/
|
|
function recomputePluginOrdering() {
|
|
if (!EventPluginOrder) {
|
|
// Wait until an `EventPluginOrder` is injected.
|
|
return;
|
|
}
|
|
for (var pluginName in namesToPlugins) {
|
|
var PluginModule = namesToPlugins[pluginName];
|
|
var pluginIndex = EventPluginOrder.indexOf(pluginName);
|
|
!(pluginIndex > -1) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'EventPluginRegistry: Cannot inject event plugins that do not exist in ' + 'the plugin ordering, `%s`.', pluginName) : invariant(false) : undefined;
|
|
if (EventPluginRegistry.plugins[pluginIndex]) {
|
|
continue;
|
|
}
|
|
!PluginModule.extractEvents ? process.env.NODE_ENV !== 'production' ? invariant(false, 'EventPluginRegistry: Event plugins must implement an `extractEvents` ' + 'method, but `%s` does not.', pluginName) : invariant(false) : undefined;
|
|
EventPluginRegistry.plugins[pluginIndex] = PluginModule;
|
|
var publishedEvents = PluginModule.eventTypes;
|
|
for (var eventName in publishedEvents) {
|
|
!publishEventForPlugin(publishedEvents[eventName], PluginModule, eventName) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.', eventName, pluginName) : invariant(false) : undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Publishes an event so that it can be dispatched by the supplied plugin.
|
|
*
|
|
* @param {object} dispatchConfig Dispatch configuration for the event.
|
|
* @param {object} PluginModule Plugin publishing the event.
|
|
* @return {boolean} True if the event was successfully published.
|
|
* @private
|
|
*/
|
|
function publishEventForPlugin(dispatchConfig, PluginModule, eventName) {
|
|
!!EventPluginRegistry.eventNameDispatchConfigs.hasOwnProperty(eventName) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'EventPluginHub: More than one plugin attempted to publish the same ' + 'event name, `%s`.', eventName) : invariant(false) : undefined;
|
|
EventPluginRegistry.eventNameDispatchConfigs[eventName] = dispatchConfig;
|
|
|
|
var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
|
|
if (phasedRegistrationNames) {
|
|
for (var phaseName in phasedRegistrationNames) {
|
|
if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
|
|
var phasedRegistrationName = phasedRegistrationNames[phaseName];
|
|
publishRegistrationName(phasedRegistrationName, PluginModule, eventName);
|
|
}
|
|
}
|
|
return true;
|
|
} else if (dispatchConfig.registrationName) {
|
|
publishRegistrationName(dispatchConfig.registrationName, PluginModule, eventName);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Publishes a registration name that is used to identify dispatched events and
|
|
* can be used with `EventPluginHub.putListener` to register listeners.
|
|
*
|
|
* @param {string} registrationName Registration name to add.
|
|
* @param {object} PluginModule Plugin publishing the event.
|
|
* @private
|
|
*/
|
|
function publishRegistrationName(registrationName, PluginModule, eventName) {
|
|
!!EventPluginRegistry.registrationNameModules[registrationName] ? process.env.NODE_ENV !== 'production' ? invariant(false, 'EventPluginHub: More than one plugin attempted to publish the same ' + 'registration name, `%s`.', registrationName) : invariant(false) : undefined;
|
|
EventPluginRegistry.registrationNameModules[registrationName] = PluginModule;
|
|
EventPluginRegistry.registrationNameDependencies[registrationName] = PluginModule.eventTypes[eventName].dependencies;
|
|
}
|
|
|
|
/**
|
|
* Registers plugins so that they can extract and dispatch events.
|
|
*
|
|
* @see {EventPluginHub}
|
|
*/
|
|
var EventPluginRegistry = {
|
|
|
|
/**
|
|
* Ordered list of injected plugins.
|
|
*/
|
|
plugins: [],
|
|
|
|
/**
|
|
* Mapping from event name to dispatch config
|
|
*/
|
|
eventNameDispatchConfigs: {},
|
|
|
|
/**
|
|
* Mapping from registration name to plugin module
|
|
*/
|
|
registrationNameModules: {},
|
|
|
|
/**
|
|
* Mapping from registration name to event name
|
|
*/
|
|
registrationNameDependencies: {},
|
|
|
|
/**
|
|
* Injects an ordering of plugins (by plugin name). This allows the ordering
|
|
* to be decoupled from injection of the actual plugins so that ordering is
|
|
* always deterministic regardless of packaging, on-the-fly injection, etc.
|
|
*
|
|
* @param {array} InjectedEventPluginOrder
|
|
* @internal
|
|
* @see {EventPluginHub.injection.injectEventPluginOrder}
|
|
*/
|
|
injectEventPluginOrder: function (InjectedEventPluginOrder) {
|
|
!!EventPluginOrder ? process.env.NODE_ENV !== 'production' ? invariant(false, 'EventPluginRegistry: Cannot inject event plugin ordering more than ' + 'once. You are likely trying to load more than one copy of React.') : invariant(false) : undefined;
|
|
// Clone the ordering so it cannot be dynamically mutated.
|
|
EventPluginOrder = Array.prototype.slice.call(InjectedEventPluginOrder);
|
|
recomputePluginOrdering();
|
|
},
|
|
|
|
/**
|
|
* Injects plugins to be used by `EventPluginHub`. The plugin names must be
|
|
* in the ordering injected by `injectEventPluginOrder`.
|
|
*
|
|
* Plugins can be injected as part of page initialization or on-the-fly.
|
|
*
|
|
* @param {object} injectedNamesToPlugins Map from names to plugin modules.
|
|
* @internal
|
|
* @see {EventPluginHub.injection.injectEventPluginsByName}
|
|
*/
|
|
injectEventPluginsByName: function (injectedNamesToPlugins) {
|
|
var isOrderingDirty = false;
|
|
for (var pluginName in injectedNamesToPlugins) {
|
|
if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {
|
|
continue;
|
|
}
|
|
var PluginModule = injectedNamesToPlugins[pluginName];
|
|
if (!namesToPlugins.hasOwnProperty(pluginName) || namesToPlugins[pluginName] !== PluginModule) {
|
|
!!namesToPlugins[pluginName] ? process.env.NODE_ENV !== 'production' ? invariant(false, 'EventPluginRegistry: Cannot inject two different event plugins ' + 'using the same name, `%s`.', pluginName) : invariant(false) : undefined;
|
|
namesToPlugins[pluginName] = PluginModule;
|
|
isOrderingDirty = true;
|
|
}
|
|
}
|
|
if (isOrderingDirty) {
|
|
recomputePluginOrdering();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Looks up the plugin for the supplied event.
|
|
*
|
|
* @param {object} event A synthetic event.
|
|
* @return {?object} The plugin that created the supplied event.
|
|
* @internal
|
|
*/
|
|
getPluginModuleForEvent: function (event) {
|
|
var dispatchConfig = event.dispatchConfig;
|
|
if (dispatchConfig.registrationName) {
|
|
return EventPluginRegistry.registrationNameModules[dispatchConfig.registrationName] || null;
|
|
}
|
|
for (var phase in dispatchConfig.phasedRegistrationNames) {
|
|
if (!dispatchConfig.phasedRegistrationNames.hasOwnProperty(phase)) {
|
|
continue;
|
|
}
|
|
var PluginModule = EventPluginRegistry.registrationNameModules[dispatchConfig.phasedRegistrationNames[phase]];
|
|
if (PluginModule) {
|
|
return PluginModule;
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
* Exposed for unit testing.
|
|
* @private
|
|
*/
|
|
_resetEventPlugins: function () {
|
|
EventPluginOrder = null;
|
|
for (var pluginName in namesToPlugins) {
|
|
if (namesToPlugins.hasOwnProperty(pluginName)) {
|
|
delete namesToPlugins[pluginName];
|
|
}
|
|
}
|
|
EventPluginRegistry.plugins.length = 0;
|
|
|
|
var eventNameDispatchConfigs = EventPluginRegistry.eventNameDispatchConfigs;
|
|
for (var eventName in eventNameDispatchConfigs) {
|
|
if (eventNameDispatchConfigs.hasOwnProperty(eventName)) {
|
|
delete eventNameDispatchConfigs[eventName];
|
|
}
|
|
}
|
|
|
|
var registrationNameModules = EventPluginRegistry.registrationNameModules;
|
|
for (var registrationName in registrationNameModules) {
|
|
if (registrationNameModules.hasOwnProperty(registrationName)) {
|
|
delete registrationNameModules[registrationName];
|
|
}
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = EventPluginRegistry;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133,"fbjs/lib/invariant":72}],152:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule EventPluginUtils
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var EventConstants = require('./EventConstants');
|
|
var ReactErrorUtils = require('./ReactErrorUtils');
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
/**
|
|
* Injected dependencies:
|
|
*/
|
|
|
|
/**
|
|
* - `Mount`: [required] Module that can convert between React dom IDs and
|
|
* actual node references.
|
|
*/
|
|
var injection = {
|
|
Mount: null,
|
|
injectMount: function (InjectedMount) {
|
|
injection.Mount = InjectedMount;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(InjectedMount && InjectedMount.getNode && InjectedMount.getID, 'EventPluginUtils.injection.injectMount(...): Injected Mount ' + 'module is missing getNode or getID.') : undefined;
|
|
}
|
|
}
|
|
};
|
|
|
|
var topLevelTypes = EventConstants.topLevelTypes;
|
|
|
|
function isEndish(topLevelType) {
|
|
return topLevelType === topLevelTypes.topMouseUp || topLevelType === topLevelTypes.topTouchEnd || topLevelType === topLevelTypes.topTouchCancel;
|
|
}
|
|
|
|
function isMoveish(topLevelType) {
|
|
return topLevelType === topLevelTypes.topMouseMove || topLevelType === topLevelTypes.topTouchMove;
|
|
}
|
|
function isStartish(topLevelType) {
|
|
return topLevelType === topLevelTypes.topMouseDown || topLevelType === topLevelTypes.topTouchStart;
|
|
}
|
|
|
|
var validateEventDispatches;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
validateEventDispatches = function (event) {
|
|
var dispatchListeners = event._dispatchListeners;
|
|
var dispatchIDs = event._dispatchIDs;
|
|
|
|
var listenersIsArr = Array.isArray(dispatchListeners);
|
|
var idsIsArr = Array.isArray(dispatchIDs);
|
|
var IDsLen = idsIsArr ? dispatchIDs.length : dispatchIDs ? 1 : 0;
|
|
var listenersLen = listenersIsArr ? dispatchListeners.length : dispatchListeners ? 1 : 0;
|
|
|
|
process.env.NODE_ENV !== 'production' ? warning(idsIsArr === listenersIsArr && IDsLen === listenersLen, 'EventPluginUtils: Invalid `event`.') : undefined;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Dispatch the event to the listener.
|
|
* @param {SyntheticEvent} event SyntheticEvent to handle
|
|
* @param {boolean} simulated If the event is simulated (changes exn behavior)
|
|
* @param {function} listener Application-level callback
|
|
* @param {string} domID DOM id to pass to the callback.
|
|
*/
|
|
function executeDispatch(event, simulated, listener, domID) {
|
|
var type = event.type || 'unknown-event';
|
|
event.currentTarget = injection.Mount.getNode(domID);
|
|
if (simulated) {
|
|
ReactErrorUtils.invokeGuardedCallbackWithCatch(type, listener, event, domID);
|
|
} else {
|
|
ReactErrorUtils.invokeGuardedCallback(type, listener, event, domID);
|
|
}
|
|
event.currentTarget = null;
|
|
}
|
|
|
|
/**
|
|
* Standard/simple iteration through an event's collected dispatches.
|
|
*/
|
|
function executeDispatchesInOrder(event, simulated) {
|
|
var dispatchListeners = event._dispatchListeners;
|
|
var dispatchIDs = event._dispatchIDs;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
validateEventDispatches(event);
|
|
}
|
|
if (Array.isArray(dispatchListeners)) {
|
|
for (var i = 0; i < dispatchListeners.length; i++) {
|
|
if (event.isPropagationStopped()) {
|
|
break;
|
|
}
|
|
// Listeners and IDs are two parallel arrays that are always in sync.
|
|
executeDispatch(event, simulated, dispatchListeners[i], dispatchIDs[i]);
|
|
}
|
|
} else if (dispatchListeners) {
|
|
executeDispatch(event, simulated, dispatchListeners, dispatchIDs);
|
|
}
|
|
event._dispatchListeners = null;
|
|
event._dispatchIDs = null;
|
|
}
|
|
|
|
/**
|
|
* Standard/simple iteration through an event's collected dispatches, but stops
|
|
* at the first dispatch execution returning true, and returns that id.
|
|
*
|
|
* @return {?string} id of the first dispatch execution who's listener returns
|
|
* true, or null if no listener returned true.
|
|
*/
|
|
function executeDispatchesInOrderStopAtTrueImpl(event) {
|
|
var dispatchListeners = event._dispatchListeners;
|
|
var dispatchIDs = event._dispatchIDs;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
validateEventDispatches(event);
|
|
}
|
|
if (Array.isArray(dispatchListeners)) {
|
|
for (var i = 0; i < dispatchListeners.length; i++) {
|
|
if (event.isPropagationStopped()) {
|
|
break;
|
|
}
|
|
// Listeners and IDs are two parallel arrays that are always in sync.
|
|
if (dispatchListeners[i](event, dispatchIDs[i])) {
|
|
return dispatchIDs[i];
|
|
}
|
|
}
|
|
} else if (dispatchListeners) {
|
|
if (dispatchListeners(event, dispatchIDs)) {
|
|
return dispatchIDs;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @see executeDispatchesInOrderStopAtTrueImpl
|
|
*/
|
|
function executeDispatchesInOrderStopAtTrue(event) {
|
|
var ret = executeDispatchesInOrderStopAtTrueImpl(event);
|
|
event._dispatchIDs = null;
|
|
event._dispatchListeners = null;
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Execution of a "direct" dispatch - there must be at most one dispatch
|
|
* accumulated on the event or it is considered an error. It doesn't really make
|
|
* sense for an event with multiple dispatches (bubbled) to keep track of the
|
|
* return values at each dispatch execution, but it does tend to make sense when
|
|
* dealing with "direct" dispatches.
|
|
*
|
|
* @return {*} The return value of executing the single dispatch.
|
|
*/
|
|
function executeDirectDispatch(event) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
validateEventDispatches(event);
|
|
}
|
|
var dispatchListener = event._dispatchListeners;
|
|
var dispatchID = event._dispatchIDs;
|
|
!!Array.isArray(dispatchListener) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'executeDirectDispatch(...): Invalid `event`.') : invariant(false) : undefined;
|
|
var res = dispatchListener ? dispatchListener(event, dispatchID) : null;
|
|
event._dispatchListeners = null;
|
|
event._dispatchIDs = null;
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* @param {SyntheticEvent} event
|
|
* @return {boolean} True iff number of dispatches accumulated is greater than 0.
|
|
*/
|
|
function hasDispatches(event) {
|
|
return !!event._dispatchListeners;
|
|
}
|
|
|
|
/**
|
|
* General utilities that are useful in creating custom Event Plugins.
|
|
*/
|
|
var EventPluginUtils = {
|
|
isEndish: isEndish,
|
|
isMoveish: isMoveish,
|
|
isStartish: isStartish,
|
|
|
|
executeDirectDispatch: executeDirectDispatch,
|
|
executeDispatchesInOrder: executeDispatchesInOrder,
|
|
executeDispatchesInOrderStopAtTrue: executeDispatchesInOrderStopAtTrue,
|
|
hasDispatches: hasDispatches,
|
|
|
|
getNode: function (id) {
|
|
return injection.Mount.getNode(id);
|
|
},
|
|
getID: function (node) {
|
|
return injection.Mount.getID(node);
|
|
},
|
|
|
|
injection: injection
|
|
};
|
|
|
|
module.exports = EventPluginUtils;
|
|
}).call(this,require('_process'))
|
|
},{"./EventConstants":149,"./ReactErrorUtils":191,"_process":133,"fbjs/lib/invariant":72,"fbjs/lib/warning":83}],153:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule EventPropagators
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var EventConstants = require('./EventConstants');
|
|
var EventPluginHub = require('./EventPluginHub');
|
|
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
var accumulateInto = require('./accumulateInto');
|
|
var forEachAccumulated = require('./forEachAccumulated');
|
|
|
|
var PropagationPhases = EventConstants.PropagationPhases;
|
|
var getListener = EventPluginHub.getListener;
|
|
|
|
/**
|
|
* Some event types have a notion of different registration names for different
|
|
* "phases" of propagation. This finds listeners by a given phase.
|
|
*/
|
|
function listenerAtPhase(id, event, propagationPhase) {
|
|
var registrationName = event.dispatchConfig.phasedRegistrationNames[propagationPhase];
|
|
return getListener(id, registrationName);
|
|
}
|
|
|
|
/**
|
|
* Tags a `SyntheticEvent` with dispatched listeners. Creating this function
|
|
* here, allows us to not have to bind or create functions for each event.
|
|
* Mutating the event's members allows us to not have to create a wrapping
|
|
* "dispatch" object that pairs the event with the listener.
|
|
*/
|
|
function accumulateDirectionalDispatches(domID, upwards, event) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(domID, 'Dispatching id must not be null') : undefined;
|
|
}
|
|
var phase = upwards ? PropagationPhases.bubbled : PropagationPhases.captured;
|
|
var listener = listenerAtPhase(domID, event, phase);
|
|
if (listener) {
|
|
event._dispatchListeners = accumulateInto(event._dispatchListeners, listener);
|
|
event._dispatchIDs = accumulateInto(event._dispatchIDs, domID);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Collect dispatches (must be entirely collected before dispatching - see unit
|
|
* tests). Lazily allocate the array to conserve memory. We must loop through
|
|
* each event and perform the traversal for each one. We cannot perform a
|
|
* single traversal for the entire collection of events because each event may
|
|
* have a different target.
|
|
*/
|
|
function accumulateTwoPhaseDispatchesSingle(event) {
|
|
if (event && event.dispatchConfig.phasedRegistrationNames) {
|
|
EventPluginHub.injection.getInstanceHandle().traverseTwoPhase(event.dispatchMarker, accumulateDirectionalDispatches, event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Same as `accumulateTwoPhaseDispatchesSingle`, but skips over the targetID.
|
|
*/
|
|
function accumulateTwoPhaseDispatchesSingleSkipTarget(event) {
|
|
if (event && event.dispatchConfig.phasedRegistrationNames) {
|
|
EventPluginHub.injection.getInstanceHandle().traverseTwoPhaseSkipTarget(event.dispatchMarker, accumulateDirectionalDispatches, event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Accumulates without regard to direction, does not look for phased
|
|
* registration names. Same as `accumulateDirectDispatchesSingle` but without
|
|
* requiring that the `dispatchMarker` be the same as the dispatched ID.
|
|
*/
|
|
function accumulateDispatches(id, ignoredDirection, event) {
|
|
if (event && event.dispatchConfig.registrationName) {
|
|
var registrationName = event.dispatchConfig.registrationName;
|
|
var listener = getListener(id, registrationName);
|
|
if (listener) {
|
|
event._dispatchListeners = accumulateInto(event._dispatchListeners, listener);
|
|
event._dispatchIDs = accumulateInto(event._dispatchIDs, id);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Accumulates dispatches on an `SyntheticEvent`, but only for the
|
|
* `dispatchMarker`.
|
|
* @param {SyntheticEvent} event
|
|
*/
|
|
function accumulateDirectDispatchesSingle(event) {
|
|
if (event && event.dispatchConfig.registrationName) {
|
|
accumulateDispatches(event.dispatchMarker, null, event);
|
|
}
|
|
}
|
|
|
|
function accumulateTwoPhaseDispatches(events) {
|
|
forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
|
|
}
|
|
|
|
function accumulateTwoPhaseDispatchesSkipTarget(events) {
|
|
forEachAccumulated(events, accumulateTwoPhaseDispatchesSingleSkipTarget);
|
|
}
|
|
|
|
function accumulateEnterLeaveDispatches(leave, enter, fromID, toID) {
|
|
EventPluginHub.injection.getInstanceHandle().traverseEnterLeave(fromID, toID, accumulateDispatches, leave, enter);
|
|
}
|
|
|
|
function accumulateDirectDispatches(events) {
|
|
forEachAccumulated(events, accumulateDirectDispatchesSingle);
|
|
}
|
|
|
|
/**
|
|
* A small set of propagation patterns, each of which will accept a small amount
|
|
* of information, and generate a set of "dispatch ready event objects" - which
|
|
* are sets of events that have already been annotated with a set of dispatched
|
|
* listener functions/ids. The API is designed this way to discourage these
|
|
* propagation strategies from actually executing the dispatches, since we
|
|
* always want to collect the entire set of dispatches before executing event a
|
|
* single one.
|
|
*
|
|
* @constructor EventPropagators
|
|
*/
|
|
var EventPropagators = {
|
|
accumulateTwoPhaseDispatches: accumulateTwoPhaseDispatches,
|
|
accumulateTwoPhaseDispatchesSkipTarget: accumulateTwoPhaseDispatchesSkipTarget,
|
|
accumulateDirectDispatches: accumulateDirectDispatches,
|
|
accumulateEnterLeaveDispatches: accumulateEnterLeaveDispatches
|
|
};
|
|
|
|
module.exports = EventPropagators;
|
|
}).call(this,require('_process'))
|
|
},{"./EventConstants":149,"./EventPluginHub":150,"./accumulateInto":237,"./forEachAccumulated":245,"_process":133,"fbjs/lib/warning":83}],154:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule FallbackCompositionState
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var PooledClass = require('./PooledClass');
|
|
|
|
var assign = require('./Object.assign');
|
|
var getTextContentAccessor = require('./getTextContentAccessor');
|
|
|
|
/**
|
|
* This helper class stores information about text content of a target node,
|
|
* allowing comparison of content before and after a given event.
|
|
*
|
|
* Identify the node where selection currently begins, then observe
|
|
* both its text content and its current position in the DOM. Since the
|
|
* browser may natively replace the target node during composition, we can
|
|
* use its position to find its replacement.
|
|
*
|
|
* @param {DOMEventTarget} root
|
|
*/
|
|
function FallbackCompositionState(root) {
|
|
this._root = root;
|
|
this._startText = this.getText();
|
|
this._fallbackText = null;
|
|
}
|
|
|
|
assign(FallbackCompositionState.prototype, {
|
|
destructor: function () {
|
|
this._root = null;
|
|
this._startText = null;
|
|
this._fallbackText = null;
|
|
},
|
|
|
|
/**
|
|
* Get current text of input.
|
|
*
|
|
* @return {string}
|
|
*/
|
|
getText: function () {
|
|
if ('value' in this._root) {
|
|
return this._root.value;
|
|
}
|
|
return this._root[getTextContentAccessor()];
|
|
},
|
|
|
|
/**
|
|
* Determine the differing substring between the initially stored
|
|
* text content and the current content.
|
|
*
|
|
* @return {string}
|
|
*/
|
|
getData: function () {
|
|
if (this._fallbackText) {
|
|
return this._fallbackText;
|
|
}
|
|
|
|
var start;
|
|
var startValue = this._startText;
|
|
var startLength = startValue.length;
|
|
var end;
|
|
var endValue = this.getText();
|
|
var endLength = endValue.length;
|
|
|
|
for (start = 0; start < startLength; start++) {
|
|
if (startValue[start] !== endValue[start]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
var minEnd = startLength - start;
|
|
for (end = 1; end <= minEnd; end++) {
|
|
if (startValue[startLength - end] !== endValue[endLength - end]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
var sliceTail = end > 1 ? 1 - end : undefined;
|
|
this._fallbackText = endValue.slice(start, sliceTail);
|
|
return this._fallbackText;
|
|
}
|
|
});
|
|
|
|
PooledClass.addPoolingTo(FallbackCompositionState);
|
|
|
|
module.exports = FallbackCompositionState;
|
|
},{"./Object.assign":157,"./PooledClass":158,"./getTextContentAccessor":252}],155:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule HTMLDOMPropertyConfig
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var DOMProperty = require('./DOMProperty');
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
|
|
var MUST_USE_ATTRIBUTE = DOMProperty.injection.MUST_USE_ATTRIBUTE;
|
|
var MUST_USE_PROPERTY = DOMProperty.injection.MUST_USE_PROPERTY;
|
|
var HAS_BOOLEAN_VALUE = DOMProperty.injection.HAS_BOOLEAN_VALUE;
|
|
var HAS_SIDE_EFFECTS = DOMProperty.injection.HAS_SIDE_EFFECTS;
|
|
var HAS_NUMERIC_VALUE = DOMProperty.injection.HAS_NUMERIC_VALUE;
|
|
var HAS_POSITIVE_NUMERIC_VALUE = DOMProperty.injection.HAS_POSITIVE_NUMERIC_VALUE;
|
|
var HAS_OVERLOADED_BOOLEAN_VALUE = DOMProperty.injection.HAS_OVERLOADED_BOOLEAN_VALUE;
|
|
|
|
var hasSVG;
|
|
if (ExecutionEnvironment.canUseDOM) {
|
|
var implementation = document.implementation;
|
|
hasSVG = implementation && implementation.hasFeature && implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#BasicStructure', '1.1');
|
|
}
|
|
|
|
var HTMLDOMPropertyConfig = {
|
|
isCustomAttribute: RegExp.prototype.test.bind(/^(data|aria)-[a-z_][a-z\d_.\-]*$/),
|
|
Properties: {
|
|
/**
|
|
* Standard Properties
|
|
*/
|
|
accept: null,
|
|
acceptCharset: null,
|
|
accessKey: null,
|
|
action: null,
|
|
allowFullScreen: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
|
allowTransparency: MUST_USE_ATTRIBUTE,
|
|
alt: null,
|
|
async: HAS_BOOLEAN_VALUE,
|
|
autoComplete: null,
|
|
// autoFocus is polyfilled/normalized by AutoFocusUtils
|
|
// autoFocus: HAS_BOOLEAN_VALUE,
|
|
autoPlay: HAS_BOOLEAN_VALUE,
|
|
capture: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
|
cellPadding: null,
|
|
cellSpacing: null,
|
|
charSet: MUST_USE_ATTRIBUTE,
|
|
challenge: MUST_USE_ATTRIBUTE,
|
|
checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
classID: MUST_USE_ATTRIBUTE,
|
|
// To set className on SVG elements, it's necessary to use .setAttribute;
|
|
// this works on HTML elements too in all browsers except IE8. Conveniently,
|
|
// IE8 doesn't support SVG and so we can simply use the attribute in
|
|
// browsers that support SVG and the property in browsers that don't,
|
|
// regardless of whether the element is HTML or SVG.
|
|
className: hasSVG ? MUST_USE_ATTRIBUTE : MUST_USE_PROPERTY,
|
|
cols: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE,
|
|
colSpan: null,
|
|
content: null,
|
|
contentEditable: null,
|
|
contextMenu: MUST_USE_ATTRIBUTE,
|
|
controls: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
coords: null,
|
|
crossOrigin: null,
|
|
data: null, // For `<object />` acts as `src`.
|
|
dateTime: MUST_USE_ATTRIBUTE,
|
|
'default': HAS_BOOLEAN_VALUE,
|
|
defer: HAS_BOOLEAN_VALUE,
|
|
dir: null,
|
|
disabled: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
|
download: HAS_OVERLOADED_BOOLEAN_VALUE,
|
|
draggable: null,
|
|
encType: null,
|
|
form: MUST_USE_ATTRIBUTE,
|
|
formAction: MUST_USE_ATTRIBUTE,
|
|
formEncType: MUST_USE_ATTRIBUTE,
|
|
formMethod: MUST_USE_ATTRIBUTE,
|
|
formNoValidate: HAS_BOOLEAN_VALUE,
|
|
formTarget: MUST_USE_ATTRIBUTE,
|
|
frameBorder: MUST_USE_ATTRIBUTE,
|
|
headers: null,
|
|
height: MUST_USE_ATTRIBUTE,
|
|
hidden: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
|
high: null,
|
|
href: null,
|
|
hrefLang: null,
|
|
htmlFor: null,
|
|
httpEquiv: null,
|
|
icon: null,
|
|
id: MUST_USE_PROPERTY,
|
|
inputMode: MUST_USE_ATTRIBUTE,
|
|
integrity: null,
|
|
is: MUST_USE_ATTRIBUTE,
|
|
keyParams: MUST_USE_ATTRIBUTE,
|
|
keyType: MUST_USE_ATTRIBUTE,
|
|
kind: null,
|
|
label: null,
|
|
lang: null,
|
|
list: MUST_USE_ATTRIBUTE,
|
|
loop: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
low: null,
|
|
manifest: MUST_USE_ATTRIBUTE,
|
|
marginHeight: null,
|
|
marginWidth: null,
|
|
max: null,
|
|
maxLength: MUST_USE_ATTRIBUTE,
|
|
media: MUST_USE_ATTRIBUTE,
|
|
mediaGroup: null,
|
|
method: null,
|
|
min: null,
|
|
minLength: MUST_USE_ATTRIBUTE,
|
|
multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
muted: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
name: null,
|
|
nonce: MUST_USE_ATTRIBUTE,
|
|
noValidate: HAS_BOOLEAN_VALUE,
|
|
open: HAS_BOOLEAN_VALUE,
|
|
optimum: null,
|
|
pattern: null,
|
|
placeholder: null,
|
|
poster: null,
|
|
preload: null,
|
|
radioGroup: null,
|
|
readOnly: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
rel: null,
|
|
required: HAS_BOOLEAN_VALUE,
|
|
reversed: HAS_BOOLEAN_VALUE,
|
|
role: MUST_USE_ATTRIBUTE,
|
|
rows: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE,
|
|
rowSpan: null,
|
|
sandbox: null,
|
|
scope: null,
|
|
scoped: HAS_BOOLEAN_VALUE,
|
|
scrolling: null,
|
|
seamless: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
|
selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
|
shape: null,
|
|
size: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE,
|
|
sizes: MUST_USE_ATTRIBUTE,
|
|
span: HAS_POSITIVE_NUMERIC_VALUE,
|
|
spellCheck: null,
|
|
src: null,
|
|
srcDoc: MUST_USE_PROPERTY,
|
|
srcLang: null,
|
|
srcSet: MUST_USE_ATTRIBUTE,
|
|
start: HAS_NUMERIC_VALUE,
|
|
step: null,
|
|
style: null,
|
|
summary: null,
|
|
tabIndex: null,
|
|
target: null,
|
|
title: null,
|
|
type: null,
|
|
useMap: null,
|
|
value: MUST_USE_PROPERTY | HAS_SIDE_EFFECTS,
|
|
width: MUST_USE_ATTRIBUTE,
|
|
wmode: MUST_USE_ATTRIBUTE,
|
|
wrap: null,
|
|
|
|
/**
|
|
* RDFa Properties
|
|
*/
|
|
about: MUST_USE_ATTRIBUTE,
|
|
datatype: MUST_USE_ATTRIBUTE,
|
|
inlist: MUST_USE_ATTRIBUTE,
|
|
prefix: MUST_USE_ATTRIBUTE,
|
|
// property is also supported for OpenGraph in meta tags.
|
|
property: MUST_USE_ATTRIBUTE,
|
|
resource: MUST_USE_ATTRIBUTE,
|
|
'typeof': MUST_USE_ATTRIBUTE,
|
|
vocab: MUST_USE_ATTRIBUTE,
|
|
|
|
/**
|
|
* Non-standard Properties
|
|
*/
|
|
// autoCapitalize and autoCorrect are supported in Mobile Safari for
|
|
// keyboard hints.
|
|
autoCapitalize: MUST_USE_ATTRIBUTE,
|
|
autoCorrect: MUST_USE_ATTRIBUTE,
|
|
// autoSave allows WebKit/Blink to persist values of input fields on page reloads
|
|
autoSave: null,
|
|
// color is for Safari mask-icon link
|
|
color: null,
|
|
// itemProp, itemScope, itemType are for
|
|
// Microdata support. See http://schema.org/docs/gs.html
|
|
itemProp: MUST_USE_ATTRIBUTE,
|
|
itemScope: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
|
itemType: MUST_USE_ATTRIBUTE,
|
|
// itemID and itemRef are for Microdata support as well but
|
|
// only specified in the the WHATWG spec document. See
|
|
// https://html.spec.whatwg.org/multipage/microdata.html#microdata-dom-api
|
|
itemID: MUST_USE_ATTRIBUTE,
|
|
itemRef: MUST_USE_ATTRIBUTE,
|
|
// results show looking glass icon and recent searches on input
|
|
// search fields in WebKit/Blink
|
|
results: null,
|
|
// IE-only attribute that specifies security restrictions on an iframe
|
|
// as an alternative to the sandbox attribute on IE<10
|
|
security: MUST_USE_ATTRIBUTE,
|
|
// IE-only attribute that controls focus behavior
|
|
unselectable: MUST_USE_ATTRIBUTE
|
|
},
|
|
DOMAttributeNames: {
|
|
acceptCharset: 'accept-charset',
|
|
className: 'class',
|
|
htmlFor: 'for',
|
|
httpEquiv: 'http-equiv'
|
|
},
|
|
DOMPropertyNames: {
|
|
autoComplete: 'autocomplete',
|
|
autoFocus: 'autofocus',
|
|
autoPlay: 'autoplay',
|
|
autoSave: 'autosave',
|
|
// `encoding` is equivalent to `enctype`, IE8 lacks an `enctype` setter.
|
|
// http://www.w3.org/TR/html5/forms.html#dom-fs-encoding
|
|
encType: 'encoding',
|
|
hrefLang: 'hreflang',
|
|
radioGroup: 'radiogroup',
|
|
spellCheck: 'spellcheck',
|
|
srcDoc: 'srcdoc',
|
|
srcSet: 'srcset'
|
|
}
|
|
};
|
|
|
|
module.exports = HTMLDOMPropertyConfig;
|
|
},{"./DOMProperty":144,"fbjs/lib/ExecutionEnvironment":58}],156:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule LinkedValueUtils
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactPropTypes = require('./ReactPropTypes');
|
|
var ReactPropTypeLocations = require('./ReactPropTypeLocations');
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
var hasReadOnlyValue = {
|
|
'button': true,
|
|
'checkbox': true,
|
|
'image': true,
|
|
'hidden': true,
|
|
'radio': true,
|
|
'reset': true,
|
|
'submit': true
|
|
};
|
|
|
|
function _assertSingleLink(inputProps) {
|
|
!(inputProps.checkedLink == null || inputProps.valueLink == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Cannot provide a checkedLink and a valueLink. If you want to use ' + 'checkedLink, you probably don\'t want to use valueLink and vice versa.') : invariant(false) : undefined;
|
|
}
|
|
function _assertValueLink(inputProps) {
|
|
_assertSingleLink(inputProps);
|
|
!(inputProps.value == null && inputProps.onChange == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Cannot provide a valueLink and a value or onChange event. If you want ' + 'to use value or onChange, you probably don\'t want to use valueLink.') : invariant(false) : undefined;
|
|
}
|
|
|
|
function _assertCheckedLink(inputProps) {
|
|
_assertSingleLink(inputProps);
|
|
!(inputProps.checked == null && inputProps.onChange == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Cannot provide a checkedLink and a checked property or onChange event. ' + 'If you want to use checked or onChange, you probably don\'t want to ' + 'use checkedLink') : invariant(false) : undefined;
|
|
}
|
|
|
|
var propTypes = {
|
|
value: function (props, propName, componentName) {
|
|
if (!props[propName] || hasReadOnlyValue[props.type] || props.onChange || props.readOnly || props.disabled) {
|
|
return null;
|
|
}
|
|
return new Error('You provided a `value` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultValue`. Otherwise, ' + 'set either `onChange` or `readOnly`.');
|
|
},
|
|
checked: function (props, propName, componentName) {
|
|
if (!props[propName] || props.onChange || props.readOnly || props.disabled) {
|
|
return null;
|
|
}
|
|
return new Error('You provided a `checked` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultChecked`. Otherwise, ' + 'set either `onChange` or `readOnly`.');
|
|
},
|
|
onChange: ReactPropTypes.func
|
|
};
|
|
|
|
var loggedTypeFailures = {};
|
|
function getDeclarationErrorAddendum(owner) {
|
|
if (owner) {
|
|
var name = owner.getName();
|
|
if (name) {
|
|
return ' Check the render method of `' + name + '`.';
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Provide a linked `value` attribute for controlled forms. You should not use
|
|
* this outside of the ReactDOM controlled form components.
|
|
*/
|
|
var LinkedValueUtils = {
|
|
checkPropTypes: function (tagName, props, owner) {
|
|
for (var propName in propTypes) {
|
|
if (propTypes.hasOwnProperty(propName)) {
|
|
var error = propTypes[propName](props, propName, tagName, ReactPropTypeLocations.prop);
|
|
}
|
|
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
|
|
// Only monitor this failure once because there tends to be a lot of the
|
|
// same error.
|
|
loggedTypeFailures[error.message] = true;
|
|
|
|
var addendum = getDeclarationErrorAddendum(owner);
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Failed form propType: %s%s', error.message, addendum) : undefined;
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param {object} inputProps Props for form component
|
|
* @return {*} current value of the input either from value prop or link.
|
|
*/
|
|
getValue: function (inputProps) {
|
|
if (inputProps.valueLink) {
|
|
_assertValueLink(inputProps);
|
|
return inputProps.valueLink.value;
|
|
}
|
|
return inputProps.value;
|
|
},
|
|
|
|
/**
|
|
* @param {object} inputProps Props for form component
|
|
* @return {*} current checked status of the input either from checked prop
|
|
* or link.
|
|
*/
|
|
getChecked: function (inputProps) {
|
|
if (inputProps.checkedLink) {
|
|
_assertCheckedLink(inputProps);
|
|
return inputProps.checkedLink.value;
|
|
}
|
|
return inputProps.checked;
|
|
},
|
|
|
|
/**
|
|
* @param {object} inputProps Props for form component
|
|
* @param {SyntheticEvent} event change event to handle
|
|
*/
|
|
executeOnChange: function (inputProps, event) {
|
|
if (inputProps.valueLink) {
|
|
_assertValueLink(inputProps);
|
|
return inputProps.valueLink.requestChange(event.target.value);
|
|
} else if (inputProps.checkedLink) {
|
|
_assertCheckedLink(inputProps);
|
|
return inputProps.checkedLink.requestChange(event.target.checked);
|
|
} else if (inputProps.onChange) {
|
|
return inputProps.onChange.call(undefined, event);
|
|
}
|
|
}
|
|
};
|
|
|
|
module.exports = LinkedValueUtils;
|
|
}).call(this,require('_process'))
|
|
},{"./ReactPropTypeLocations":208,"./ReactPropTypes":209,"_process":133,"fbjs/lib/invariant":72,"fbjs/lib/warning":83}],157:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2014-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule Object.assign
|
|
*/
|
|
|
|
// https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign
|
|
|
|
'use strict';
|
|
|
|
function assign(target, sources) {
|
|
if (target == null) {
|
|
throw new TypeError('Object.assign target cannot be null or undefined');
|
|
}
|
|
|
|
var to = Object(target);
|
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
|
|
for (var nextIndex = 1; nextIndex < arguments.length; nextIndex++) {
|
|
var nextSource = arguments[nextIndex];
|
|
if (nextSource == null) {
|
|
continue;
|
|
}
|
|
|
|
var from = Object(nextSource);
|
|
|
|
// We don't currently support accessors nor proxies. Therefore this
|
|
// copy cannot throw. If we ever supported this then we must handle
|
|
// exceptions and side-effects. We don't support symbols so they won't
|
|
// be transferred.
|
|
|
|
for (var key in from) {
|
|
if (hasOwnProperty.call(from, key)) {
|
|
to[key] = from[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
return to;
|
|
}
|
|
|
|
module.exports = assign;
|
|
},{}],158:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule PooledClass
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
/**
|
|
* Static poolers. Several custom versions for each potential number of
|
|
* arguments. A completely generic pooler is easy to implement, but would
|
|
* require accessing the `arguments` object. In each of these, `this` refers to
|
|
* the Class itself, not an instance. If any others are needed, simply add them
|
|
* here, or in their own files.
|
|
*/
|
|
var oneArgumentPooler = function (copyFieldsFrom) {
|
|
var Klass = this;
|
|
if (Klass.instancePool.length) {
|
|
var instance = Klass.instancePool.pop();
|
|
Klass.call(instance, copyFieldsFrom);
|
|
return instance;
|
|
} else {
|
|
return new Klass(copyFieldsFrom);
|
|
}
|
|
};
|
|
|
|
var twoArgumentPooler = function (a1, a2) {
|
|
var Klass = this;
|
|
if (Klass.instancePool.length) {
|
|
var instance = Klass.instancePool.pop();
|
|
Klass.call(instance, a1, a2);
|
|
return instance;
|
|
} else {
|
|
return new Klass(a1, a2);
|
|
}
|
|
};
|
|
|
|
var threeArgumentPooler = function (a1, a2, a3) {
|
|
var Klass = this;
|
|
if (Klass.instancePool.length) {
|
|
var instance = Klass.instancePool.pop();
|
|
Klass.call(instance, a1, a2, a3);
|
|
return instance;
|
|
} else {
|
|
return new Klass(a1, a2, a3);
|
|
}
|
|
};
|
|
|
|
var fourArgumentPooler = function (a1, a2, a3, a4) {
|
|
var Klass = this;
|
|
if (Klass.instancePool.length) {
|
|
var instance = Klass.instancePool.pop();
|
|
Klass.call(instance, a1, a2, a3, a4);
|
|
return instance;
|
|
} else {
|
|
return new Klass(a1, a2, a3, a4);
|
|
}
|
|
};
|
|
|
|
var fiveArgumentPooler = function (a1, a2, a3, a4, a5) {
|
|
var Klass = this;
|
|
if (Klass.instancePool.length) {
|
|
var instance = Klass.instancePool.pop();
|
|
Klass.call(instance, a1, a2, a3, a4, a5);
|
|
return instance;
|
|
} else {
|
|
return new Klass(a1, a2, a3, a4, a5);
|
|
}
|
|
};
|
|
|
|
var standardReleaser = function (instance) {
|
|
var Klass = this;
|
|
!(instance instanceof Klass) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Trying to release an instance into a pool of a different type.') : invariant(false) : undefined;
|
|
instance.destructor();
|
|
if (Klass.instancePool.length < Klass.poolSize) {
|
|
Klass.instancePool.push(instance);
|
|
}
|
|
};
|
|
|
|
var DEFAULT_POOL_SIZE = 10;
|
|
var DEFAULT_POOLER = oneArgumentPooler;
|
|
|
|
/**
|
|
* Augments `CopyConstructor` to be a poolable class, augmenting only the class
|
|
* itself (statically) not adding any prototypical fields. Any CopyConstructor
|
|
* you give this may have a `poolSize` property, and will look for a
|
|
* prototypical `destructor` on instances (optional).
|
|
*
|
|
* @param {Function} CopyConstructor Constructor that can be used to reset.
|
|
* @param {Function} pooler Customizable pooler.
|
|
*/
|
|
var addPoolingTo = function (CopyConstructor, pooler) {
|
|
var NewKlass = CopyConstructor;
|
|
NewKlass.instancePool = [];
|
|
NewKlass.getPooled = pooler || DEFAULT_POOLER;
|
|
if (!NewKlass.poolSize) {
|
|
NewKlass.poolSize = DEFAULT_POOL_SIZE;
|
|
}
|
|
NewKlass.release = standardReleaser;
|
|
return NewKlass;
|
|
};
|
|
|
|
var PooledClass = {
|
|
addPoolingTo: addPoolingTo,
|
|
oneArgumentPooler: oneArgumentPooler,
|
|
twoArgumentPooler: twoArgumentPooler,
|
|
threeArgumentPooler: threeArgumentPooler,
|
|
fourArgumentPooler: fourArgumentPooler,
|
|
fiveArgumentPooler: fiveArgumentPooler
|
|
};
|
|
|
|
module.exports = PooledClass;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133,"fbjs/lib/invariant":72}],159:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule React
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactDOM = require('./ReactDOM');
|
|
var ReactDOMServer = require('./ReactDOMServer');
|
|
var ReactIsomorphic = require('./ReactIsomorphic');
|
|
|
|
var assign = require('./Object.assign');
|
|
var deprecated = require('./deprecated');
|
|
|
|
// `version` will be added here by ReactIsomorphic.
|
|
var React = {};
|
|
|
|
assign(React, ReactIsomorphic);
|
|
|
|
assign(React, {
|
|
// ReactDOM
|
|
findDOMNode: deprecated('findDOMNode', 'ReactDOM', 'react-dom', ReactDOM, ReactDOM.findDOMNode),
|
|
render: deprecated('render', 'ReactDOM', 'react-dom', ReactDOM, ReactDOM.render),
|
|
unmountComponentAtNode: deprecated('unmountComponentAtNode', 'ReactDOM', 'react-dom', ReactDOM, ReactDOM.unmountComponentAtNode),
|
|
|
|
// ReactDOMServer
|
|
renderToString: deprecated('renderToString', 'ReactDOMServer', 'react-dom/server', ReactDOMServer, ReactDOMServer.renderToString),
|
|
renderToStaticMarkup: deprecated('renderToStaticMarkup', 'ReactDOMServer', 'react-dom/server', ReactDOMServer, ReactDOMServer.renderToStaticMarkup)
|
|
});
|
|
|
|
React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactDOM;
|
|
React.__SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactDOMServer;
|
|
|
|
module.exports = React;
|
|
},{"./Object.assign":157,"./ReactDOM":170,"./ReactDOMServer":180,"./ReactIsomorphic":198,"./deprecated":241}],160:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactBrowserComponentMixin
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactInstanceMap = require('./ReactInstanceMap');
|
|
|
|
var findDOMNode = require('./findDOMNode');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
var didWarnKey = '_getDOMNodeDidWarn';
|
|
|
|
var ReactBrowserComponentMixin = {
|
|
/**
|
|
* Returns the DOM node rendered by this component.
|
|
*
|
|
* @return {DOMElement} The root node of this component.
|
|
* @final
|
|
* @protected
|
|
*/
|
|
getDOMNode: function () {
|
|
process.env.NODE_ENV !== 'production' ? warning(this.constructor[didWarnKey], '%s.getDOMNode(...) is deprecated. Please use ' + 'ReactDOM.findDOMNode(instance) instead.', ReactInstanceMap.get(this).getName() || this.tagName || 'Unknown') : undefined;
|
|
this.constructor[didWarnKey] = true;
|
|
return findDOMNode(this);
|
|
}
|
|
};
|
|
|
|
module.exports = ReactBrowserComponentMixin;
|
|
}).call(this,require('_process'))
|
|
},{"./ReactInstanceMap":197,"./findDOMNode":243,"_process":133,"fbjs/lib/warning":83}],161:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactBrowserEventEmitter
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var EventConstants = require('./EventConstants');
|
|
var EventPluginHub = require('./EventPluginHub');
|
|
var EventPluginRegistry = require('./EventPluginRegistry');
|
|
var ReactEventEmitterMixin = require('./ReactEventEmitterMixin');
|
|
var ReactPerf = require('./ReactPerf');
|
|
var ViewportMetrics = require('./ViewportMetrics');
|
|
|
|
var assign = require('./Object.assign');
|
|
var isEventSupported = require('./isEventSupported');
|
|
|
|
/**
|
|
* Summary of `ReactBrowserEventEmitter` event handling:
|
|
*
|
|
* - Top-level delegation is used to trap most native browser events. This
|
|
* may only occur in the main thread and is the responsibility of
|
|
* ReactEventListener, which is injected and can therefore support pluggable
|
|
* event sources. This is the only work that occurs in the main thread.
|
|
*
|
|
* - We normalize and de-duplicate events to account for browser quirks. This
|
|
* may be done in the worker thread.
|
|
*
|
|
* - Forward these native events (with the associated top-level type used to
|
|
* trap it) to `EventPluginHub`, which in turn will ask plugins if they want
|
|
* to extract any synthetic events.
|
|
*
|
|
* - The `EventPluginHub` will then process each event by annotating them with
|
|
* "dispatches", a sequence of listeners and IDs that care about that event.
|
|
*
|
|
* - The `EventPluginHub` then dispatches the events.
|
|
*
|
|
* Overview of React and the event system:
|
|
*
|
|
* +------------+ .
|
|
* | DOM | .
|
|
* +------------+ .
|
|
* | .
|
|
* v .
|
|
* +------------+ .
|
|
* | ReactEvent | .
|
|
* | Listener | .
|
|
* +------------+ . +-----------+
|
|
* | . +--------+|SimpleEvent|
|
|
* | . | |Plugin |
|
|
* +-----|------+ . v +-----------+
|
|
* | | | . +--------------+ +------------+
|
|
* | +-----------.--->|EventPluginHub| | Event |
|
|
* | | . | | +-----------+ | Propagators|
|
|
* | ReactEvent | . | | |TapEvent | |------------|
|
|
* | Emitter | . | |<---+|Plugin | |other plugin|
|
|
* | | . | | +-----------+ | utilities |
|
|
* | +-----------.--->| | +------------+
|
|
* | | | . +--------------+
|
|
* +-----|------+ . ^ +-----------+
|
|
* | . | |Enter/Leave|
|
|
* + . +-------+|Plugin |
|
|
* +-------------+ . +-----------+
|
|
* | application | .
|
|
* |-------------| .
|
|
* | | .
|
|
* | | .
|
|
* +-------------+ .
|
|
* .
|
|
* React Core . General Purpose Event Plugin System
|
|
*/
|
|
|
|
var alreadyListeningTo = {};
|
|
var isMonitoringScrollValue = false;
|
|
var reactTopListenersCounter = 0;
|
|
|
|
// For events like 'submit' which don't consistently bubble (which we trap at a
|
|
// lower node than `document`), binding at `document` would cause duplicate
|
|
// events so we don't include them here
|
|
var topEventMapping = {
|
|
topAbort: 'abort',
|
|
topBlur: 'blur',
|
|
topCanPlay: 'canplay',
|
|
topCanPlayThrough: 'canplaythrough',
|
|
topChange: 'change',
|
|
topClick: 'click',
|
|
topCompositionEnd: 'compositionend',
|
|
topCompositionStart: 'compositionstart',
|
|
topCompositionUpdate: 'compositionupdate',
|
|
topContextMenu: 'contextmenu',
|
|
topCopy: 'copy',
|
|
topCut: 'cut',
|
|
topDoubleClick: 'dblclick',
|
|
topDrag: 'drag',
|
|
topDragEnd: 'dragend',
|
|
topDragEnter: 'dragenter',
|
|
topDragExit: 'dragexit',
|
|
topDragLeave: 'dragleave',
|
|
topDragOver: 'dragover',
|
|
topDragStart: 'dragstart',
|
|
topDrop: 'drop',
|
|
topDurationChange: 'durationchange',
|
|
topEmptied: 'emptied',
|
|
topEncrypted: 'encrypted',
|
|
topEnded: 'ended',
|
|
topError: 'error',
|
|
topFocus: 'focus',
|
|
topInput: 'input',
|
|
topKeyDown: 'keydown',
|
|
topKeyPress: 'keypress',
|
|
topKeyUp: 'keyup',
|
|
topLoadedData: 'loadeddata',
|
|
topLoadedMetadata: 'loadedmetadata',
|
|
topLoadStart: 'loadstart',
|
|
topMouseDown: 'mousedown',
|
|
topMouseMove: 'mousemove',
|
|
topMouseOut: 'mouseout',
|
|
topMouseOver: 'mouseover',
|
|
topMouseUp: 'mouseup',
|
|
topPaste: 'paste',
|
|
topPause: 'pause',
|
|
topPlay: 'play',
|
|
topPlaying: 'playing',
|
|
topProgress: 'progress',
|
|
topRateChange: 'ratechange',
|
|
topScroll: 'scroll',
|
|
topSeeked: 'seeked',
|
|
topSeeking: 'seeking',
|
|
topSelectionChange: 'selectionchange',
|
|
topStalled: 'stalled',
|
|
topSuspend: 'suspend',
|
|
topTextInput: 'textInput',
|
|
topTimeUpdate: 'timeupdate',
|
|
topTouchCancel: 'touchcancel',
|
|
topTouchEnd: 'touchend',
|
|
topTouchMove: 'touchmove',
|
|
topTouchStart: 'touchstart',
|
|
topVolumeChange: 'volumechange',
|
|
topWaiting: 'waiting',
|
|
topWheel: 'wheel'
|
|
};
|
|
|
|
/**
|
|
* To ensure no conflicts with other potential React instances on the page
|
|
*/
|
|
var topListenersIDKey = '_reactListenersID' + String(Math.random()).slice(2);
|
|
|
|
function getListeningForDocument(mountAt) {
|
|
// In IE8, `mountAt` is a host object and doesn't have `hasOwnProperty`
|
|
// directly.
|
|
if (!Object.prototype.hasOwnProperty.call(mountAt, topListenersIDKey)) {
|
|
mountAt[topListenersIDKey] = reactTopListenersCounter++;
|
|
alreadyListeningTo[mountAt[topListenersIDKey]] = {};
|
|
}
|
|
return alreadyListeningTo[mountAt[topListenersIDKey]];
|
|
}
|
|
|
|
/**
|
|
* `ReactBrowserEventEmitter` is used to attach top-level event listeners. For
|
|
* example:
|
|
*
|
|
* ReactBrowserEventEmitter.putListener('myID', 'onClick', myFunction);
|
|
*
|
|
* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
|
|
*
|
|
* @internal
|
|
*/
|
|
var ReactBrowserEventEmitter = assign({}, ReactEventEmitterMixin, {
|
|
|
|
/**
|
|
* Injectable event backend
|
|
*/
|
|
ReactEventListener: null,
|
|
|
|
injection: {
|
|
/**
|
|
* @param {object} ReactEventListener
|
|
*/
|
|
injectReactEventListener: function (ReactEventListener) {
|
|
ReactEventListener.setHandleTopLevel(ReactBrowserEventEmitter.handleTopLevel);
|
|
ReactBrowserEventEmitter.ReactEventListener = ReactEventListener;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets whether or not any created callbacks should be enabled.
|
|
*
|
|
* @param {boolean} enabled True if callbacks should be enabled.
|
|
*/
|
|
setEnabled: function (enabled) {
|
|
if (ReactBrowserEventEmitter.ReactEventListener) {
|
|
ReactBrowserEventEmitter.ReactEventListener.setEnabled(enabled);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @return {boolean} True if callbacks are enabled.
|
|
*/
|
|
isEnabled: function () {
|
|
return !!(ReactBrowserEventEmitter.ReactEventListener && ReactBrowserEventEmitter.ReactEventListener.isEnabled());
|
|
},
|
|
|
|
/**
|
|
* We listen for bubbled touch events on the document object.
|
|
*
|
|
* Firefox v8.01 (and possibly others) exhibited strange behavior when
|
|
* mounting `onmousemove` events at some node that was not the document
|
|
* element. The symptoms were that if your mouse is not moving over something
|
|
* contained within that mount point (for example on the background) the
|
|
* top-level listeners for `onmousemove` won't be called. However, if you
|
|
* register the `mousemove` on the document object, then it will of course
|
|
* catch all `mousemove`s. This along with iOS quirks, justifies restricting
|
|
* top-level listeners to the document object only, at least for these
|
|
* movement types of events and possibly all events.
|
|
*
|
|
* @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
|
|
*
|
|
* Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but
|
|
* they bubble to document.
|
|
*
|
|
* @param {string} registrationName Name of listener (e.g. `onClick`).
|
|
* @param {object} contentDocumentHandle Document which owns the container
|
|
*/
|
|
listenTo: function (registrationName, contentDocumentHandle) {
|
|
var mountAt = contentDocumentHandle;
|
|
var isListening = getListeningForDocument(mountAt);
|
|
var dependencies = EventPluginRegistry.registrationNameDependencies[registrationName];
|
|
|
|
var topLevelTypes = EventConstants.topLevelTypes;
|
|
for (var i = 0; i < dependencies.length; i++) {
|
|
var dependency = dependencies[i];
|
|
if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
|
|
if (dependency === topLevelTypes.topWheel) {
|
|
if (isEventSupported('wheel')) {
|
|
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelTypes.topWheel, 'wheel', mountAt);
|
|
} else if (isEventSupported('mousewheel')) {
|
|
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelTypes.topWheel, 'mousewheel', mountAt);
|
|
} else {
|
|
// Firefox needs to capture a different mouse scroll event.
|
|
// @see http://www.quirksmode.org/dom/events/tests/scroll.html
|
|
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelTypes.topWheel, 'DOMMouseScroll', mountAt);
|
|
}
|
|
} else if (dependency === topLevelTypes.topScroll) {
|
|
|
|
if (isEventSupported('scroll', true)) {
|
|
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(topLevelTypes.topScroll, 'scroll', mountAt);
|
|
} else {
|
|
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelTypes.topScroll, 'scroll', ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE);
|
|
}
|
|
} else if (dependency === topLevelTypes.topFocus || dependency === topLevelTypes.topBlur) {
|
|
|
|
if (isEventSupported('focus', true)) {
|
|
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(topLevelTypes.topFocus, 'focus', mountAt);
|
|
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(topLevelTypes.topBlur, 'blur', mountAt);
|
|
} else if (isEventSupported('focusin')) {
|
|
// IE has `focusin` and `focusout` events which bubble.
|
|
// @see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
|
|
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelTypes.topFocus, 'focusin', mountAt);
|
|
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelTypes.topBlur, 'focusout', mountAt);
|
|
}
|
|
|
|
// to make sure blur and focus event listeners are only attached once
|
|
isListening[topLevelTypes.topBlur] = true;
|
|
isListening[topLevelTypes.topFocus] = true;
|
|
} else if (topEventMapping.hasOwnProperty(dependency)) {
|
|
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(dependency, topEventMapping[dependency], mountAt);
|
|
}
|
|
|
|
isListening[dependency] = true;
|
|
}
|
|
}
|
|
},
|
|
|
|
trapBubbledEvent: function (topLevelType, handlerBaseName, handle) {
|
|
return ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelType, handlerBaseName, handle);
|
|
},
|
|
|
|
trapCapturedEvent: function (topLevelType, handlerBaseName, handle) {
|
|
return ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(topLevelType, handlerBaseName, handle);
|
|
},
|
|
|
|
/**
|
|
* Listens to window scroll and resize events. We cache scroll values so that
|
|
* application code can access them without triggering reflows.
|
|
*
|
|
* NOTE: Scroll events do not bubble.
|
|
*
|
|
* @see http://www.quirksmode.org/dom/events/scroll.html
|
|
*/
|
|
ensureScrollValueMonitoring: function () {
|
|
if (!isMonitoringScrollValue) {
|
|
var refresh = ViewportMetrics.refreshScrollValues;
|
|
ReactBrowserEventEmitter.ReactEventListener.monitorScrollValue(refresh);
|
|
isMonitoringScrollValue = true;
|
|
}
|
|
},
|
|
|
|
eventNameDispatchConfigs: EventPluginHub.eventNameDispatchConfigs,
|
|
|
|
registrationNameModules: EventPluginHub.registrationNameModules,
|
|
|
|
putListener: EventPluginHub.putListener,
|
|
|
|
getListener: EventPluginHub.getListener,
|
|
|
|
deleteListener: EventPluginHub.deleteListener,
|
|
|
|
deleteAllListeners: EventPluginHub.deleteAllListeners
|
|
|
|
});
|
|
|
|
ReactPerf.measureMethods(ReactBrowserEventEmitter, 'ReactBrowserEventEmitter', {
|
|
putListener: 'putListener',
|
|
deleteListener: 'deleteListener'
|
|
});
|
|
|
|
module.exports = ReactBrowserEventEmitter;
|
|
},{"./EventConstants":149,"./EventPluginHub":150,"./EventPluginRegistry":151,"./Object.assign":157,"./ReactEventEmitterMixin":192,"./ReactPerf":206,"./ViewportMetrics":236,"./isEventSupported":254}],162:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2014-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactChildReconciler
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactReconciler = require('./ReactReconciler');
|
|
|
|
var instantiateReactComponent = require('./instantiateReactComponent');
|
|
var shouldUpdateReactComponent = require('./shouldUpdateReactComponent');
|
|
var traverseAllChildren = require('./traverseAllChildren');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
function instantiateChild(childInstances, child, name) {
|
|
// We found a component instance.
|
|
var keyUnique = childInstances[name] === undefined;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(keyUnique, 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.', name) : undefined;
|
|
}
|
|
if (child != null && keyUnique) {
|
|
childInstances[name] = instantiateReactComponent(child, null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ReactChildReconciler provides helpers for initializing or updating a set of
|
|
* children. Its output is suitable for passing it onto ReactMultiChild which
|
|
* does diffed reordering and insertion.
|
|
*/
|
|
var ReactChildReconciler = {
|
|
/**
|
|
* Generates a "mount image" for each of the supplied children. In the case
|
|
* of `ReactDOMComponent`, a mount image is a string of markup.
|
|
*
|
|
* @param {?object} nestedChildNodes Nested child maps.
|
|
* @return {?object} A set of child instances.
|
|
* @internal
|
|
*/
|
|
instantiateChildren: function (nestedChildNodes, transaction, context) {
|
|
if (nestedChildNodes == null) {
|
|
return null;
|
|
}
|
|
var childInstances = {};
|
|
traverseAllChildren(nestedChildNodes, instantiateChild, childInstances);
|
|
return childInstances;
|
|
},
|
|
|
|
/**
|
|
* Updates the rendered children and returns a new set of children.
|
|
*
|
|
* @param {?object} prevChildren Previously initialized set of children.
|
|
* @param {?object} nextChildren Flat child element maps.
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @param {object} context
|
|
* @return {?object} A new set of child instances.
|
|
* @internal
|
|
*/
|
|
updateChildren: function (prevChildren, nextChildren, transaction, context) {
|
|
// We currently don't have a way to track moves here but if we use iterators
|
|
// instead of for..in we can zip the iterators and check if an item has
|
|
// moved.
|
|
// TODO: If nothing has changed, return the prevChildren object so that we
|
|
// can quickly bailout if nothing has changed.
|
|
if (!nextChildren && !prevChildren) {
|
|
return null;
|
|
}
|
|
var name;
|
|
for (name in nextChildren) {
|
|
if (!nextChildren.hasOwnProperty(name)) {
|
|
continue;
|
|
}
|
|
var prevChild = prevChildren && prevChildren[name];
|
|
var prevElement = prevChild && prevChild._currentElement;
|
|
var nextElement = nextChildren[name];
|
|
if (prevChild != null && shouldUpdateReactComponent(prevElement, nextElement)) {
|
|
ReactReconciler.receiveComponent(prevChild, nextElement, transaction, context);
|
|
nextChildren[name] = prevChild;
|
|
} else {
|
|
if (prevChild) {
|
|
ReactReconciler.unmountComponent(prevChild, name);
|
|
}
|
|
// The child must be instantiated before it's mounted.
|
|
var nextChildInstance = instantiateReactComponent(nextElement, null);
|
|
nextChildren[name] = nextChildInstance;
|
|
}
|
|
}
|
|
// Unmount children that are no longer present.
|
|
for (name in prevChildren) {
|
|
if (prevChildren.hasOwnProperty(name) && !(nextChildren && nextChildren.hasOwnProperty(name))) {
|
|
ReactReconciler.unmountComponent(prevChildren[name]);
|
|
}
|
|
}
|
|
return nextChildren;
|
|
},
|
|
|
|
/**
|
|
* Unmounts all rendered children. This should be used to clean up children
|
|
* when this component is unmounted.
|
|
*
|
|
* @param {?object} renderedChildren Previously initialized set of children.
|
|
* @internal
|
|
*/
|
|
unmountChildren: function (renderedChildren) {
|
|
for (var name in renderedChildren) {
|
|
if (renderedChildren.hasOwnProperty(name)) {
|
|
var renderedChild = renderedChildren[name];
|
|
ReactReconciler.unmountComponent(renderedChild);
|
|
}
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactChildReconciler;
|
|
}).call(this,require('_process'))
|
|
},{"./ReactReconciler":211,"./instantiateReactComponent":253,"./shouldUpdateReactComponent":261,"./traverseAllChildren":262,"_process":133,"fbjs/lib/warning":83}],163:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactChildren
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var PooledClass = require('./PooledClass');
|
|
var ReactElement = require('./ReactElement');
|
|
|
|
var emptyFunction = require('fbjs/lib/emptyFunction');
|
|
var traverseAllChildren = require('./traverseAllChildren');
|
|
|
|
var twoArgumentPooler = PooledClass.twoArgumentPooler;
|
|
var fourArgumentPooler = PooledClass.fourArgumentPooler;
|
|
|
|
var userProvidedKeyEscapeRegex = /\/(?!\/)/g;
|
|
function escapeUserProvidedKey(text) {
|
|
return ('' + text).replace(userProvidedKeyEscapeRegex, '//');
|
|
}
|
|
|
|
/**
|
|
* PooledClass representing the bookkeeping associated with performing a child
|
|
* traversal. Allows avoiding binding callbacks.
|
|
*
|
|
* @constructor ForEachBookKeeping
|
|
* @param {!function} forEachFunction Function to perform traversal with.
|
|
* @param {?*} forEachContext Context to perform context with.
|
|
*/
|
|
function ForEachBookKeeping(forEachFunction, forEachContext) {
|
|
this.func = forEachFunction;
|
|
this.context = forEachContext;
|
|
this.count = 0;
|
|
}
|
|
ForEachBookKeeping.prototype.destructor = function () {
|
|
this.func = null;
|
|
this.context = null;
|
|
this.count = 0;
|
|
};
|
|
PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler);
|
|
|
|
function forEachSingleChild(bookKeeping, child, name) {
|
|
var func = bookKeeping.func;
|
|
var context = bookKeeping.context;
|
|
|
|
func.call(context, child, bookKeeping.count++);
|
|
}
|
|
|
|
/**
|
|
* Iterates through children that are typically specified as `props.children`.
|
|
*
|
|
* The provided forEachFunc(child, index) will be called for each
|
|
* leaf child.
|
|
*
|
|
* @param {?*} children Children tree container.
|
|
* @param {function(*, int)} forEachFunc
|
|
* @param {*} forEachContext Context for forEachContext.
|
|
*/
|
|
function forEachChildren(children, forEachFunc, forEachContext) {
|
|
if (children == null) {
|
|
return children;
|
|
}
|
|
var traverseContext = ForEachBookKeeping.getPooled(forEachFunc, forEachContext);
|
|
traverseAllChildren(children, forEachSingleChild, traverseContext);
|
|
ForEachBookKeeping.release(traverseContext);
|
|
}
|
|
|
|
/**
|
|
* PooledClass representing the bookkeeping associated with performing a child
|
|
* mapping. Allows avoiding binding callbacks.
|
|
*
|
|
* @constructor MapBookKeeping
|
|
* @param {!*} mapResult Object containing the ordered map of results.
|
|
* @param {!function} mapFunction Function to perform mapping with.
|
|
* @param {?*} mapContext Context to perform mapping with.
|
|
*/
|
|
function MapBookKeeping(mapResult, keyPrefix, mapFunction, mapContext) {
|
|
this.result = mapResult;
|
|
this.keyPrefix = keyPrefix;
|
|
this.func = mapFunction;
|
|
this.context = mapContext;
|
|
this.count = 0;
|
|
}
|
|
MapBookKeeping.prototype.destructor = function () {
|
|
this.result = null;
|
|
this.keyPrefix = null;
|
|
this.func = null;
|
|
this.context = null;
|
|
this.count = 0;
|
|
};
|
|
PooledClass.addPoolingTo(MapBookKeeping, fourArgumentPooler);
|
|
|
|
function mapSingleChildIntoContext(bookKeeping, child, childKey) {
|
|
var result = bookKeeping.result;
|
|
var keyPrefix = bookKeeping.keyPrefix;
|
|
var func = bookKeeping.func;
|
|
var context = bookKeeping.context;
|
|
|
|
var mappedChild = func.call(context, child, bookKeeping.count++);
|
|
if (Array.isArray(mappedChild)) {
|
|
mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, emptyFunction.thatReturnsArgument);
|
|
} else if (mappedChild != null) {
|
|
if (ReactElement.isValidElement(mappedChild)) {
|
|
mappedChild = ReactElement.cloneAndReplaceKey(mappedChild,
|
|
// Keep both the (mapped) and old keys if they differ, just as
|
|
// traverseAllChildren used to do for objects as children
|
|
keyPrefix + (mappedChild !== child ? escapeUserProvidedKey(mappedChild.key || '') + '/' : '') + childKey);
|
|
}
|
|
result.push(mappedChild);
|
|
}
|
|
}
|
|
|
|
function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
|
|
var escapedPrefix = '';
|
|
if (prefix != null) {
|
|
escapedPrefix = escapeUserProvidedKey(prefix) + '/';
|
|
}
|
|
var traverseContext = MapBookKeeping.getPooled(array, escapedPrefix, func, context);
|
|
traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
|
|
MapBookKeeping.release(traverseContext);
|
|
}
|
|
|
|
/**
|
|
* Maps children that are typically specified as `props.children`.
|
|
*
|
|
* The provided mapFunction(child, key, index) will be called for each
|
|
* leaf child.
|
|
*
|
|
* @param {?*} children Children tree container.
|
|
* @param {function(*, int)} func The map function.
|
|
* @param {*} context Context for mapFunction.
|
|
* @return {object} Object containing the ordered map of results.
|
|
*/
|
|
function mapChildren(children, func, context) {
|
|
if (children == null) {
|
|
return children;
|
|
}
|
|
var result = [];
|
|
mapIntoWithKeyPrefixInternal(children, result, null, func, context);
|
|
return result;
|
|
}
|
|
|
|
function forEachSingleChildDummy(traverseContext, child, name) {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Count the number of children that are typically specified as
|
|
* `props.children`.
|
|
*
|
|
* @param {?*} children Children tree container.
|
|
* @return {number} The number of children.
|
|
*/
|
|
function countChildren(children, context) {
|
|
return traverseAllChildren(children, forEachSingleChildDummy, null);
|
|
}
|
|
|
|
/**
|
|
* Flatten a children object (typically specified as `props.children`) and
|
|
* return an array with appropriately re-keyed children.
|
|
*/
|
|
function toArray(children) {
|
|
var result = [];
|
|
mapIntoWithKeyPrefixInternal(children, result, null, emptyFunction.thatReturnsArgument);
|
|
return result;
|
|
}
|
|
|
|
var ReactChildren = {
|
|
forEach: forEachChildren,
|
|
map: mapChildren,
|
|
mapIntoWithKeyPrefixInternal: mapIntoWithKeyPrefixInternal,
|
|
count: countChildren,
|
|
toArray: toArray
|
|
};
|
|
|
|
module.exports = ReactChildren;
|
|
},{"./PooledClass":158,"./ReactElement":187,"./traverseAllChildren":262,"fbjs/lib/emptyFunction":64}],164:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactClass
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactComponent = require('./ReactComponent');
|
|
var ReactElement = require('./ReactElement');
|
|
var ReactPropTypeLocations = require('./ReactPropTypeLocations');
|
|
var ReactPropTypeLocationNames = require('./ReactPropTypeLocationNames');
|
|
var ReactNoopUpdateQueue = require('./ReactNoopUpdateQueue');
|
|
|
|
var assign = require('./Object.assign');
|
|
var emptyObject = require('fbjs/lib/emptyObject');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var keyMirror = require('fbjs/lib/keyMirror');
|
|
var keyOf = require('fbjs/lib/keyOf');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
var MIXINS_KEY = keyOf({ mixins: null });
|
|
|
|
/**
|
|
* Policies that describe methods in `ReactClassInterface`.
|
|
*/
|
|
var SpecPolicy = keyMirror({
|
|
/**
|
|
* These methods may be defined only once by the class specification or mixin.
|
|
*/
|
|
DEFINE_ONCE: null,
|
|
/**
|
|
* These methods may be defined by both the class specification and mixins.
|
|
* Subsequent definitions will be chained. These methods must return void.
|
|
*/
|
|
DEFINE_MANY: null,
|
|
/**
|
|
* These methods are overriding the base class.
|
|
*/
|
|
OVERRIDE_BASE: null,
|
|
/**
|
|
* These methods are similar to DEFINE_MANY, except we assume they return
|
|
* objects. We try to merge the keys of the return values of all the mixed in
|
|
* functions. If there is a key conflict we throw.
|
|
*/
|
|
DEFINE_MANY_MERGED: null
|
|
});
|
|
|
|
var injectedMixins = [];
|
|
|
|
var warnedSetProps = false;
|
|
function warnSetProps() {
|
|
if (!warnedSetProps) {
|
|
warnedSetProps = true;
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'setProps(...) and replaceProps(...) are deprecated. ' + 'Instead, call render again at the top level.') : undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Composite components are higher-level components that compose other composite
|
|
* or native components.
|
|
*
|
|
* To create a new type of `ReactClass`, pass a specification of
|
|
* your new class to `React.createClass`. The only requirement of your class
|
|
* specification is that you implement a `render` method.
|
|
*
|
|
* var MyComponent = React.createClass({
|
|
* render: function() {
|
|
* return <div>Hello World</div>;
|
|
* }
|
|
* });
|
|
*
|
|
* The class specification supports a specific protocol of methods that have
|
|
* special meaning (e.g. `render`). See `ReactClassInterface` for
|
|
* more the comprehensive protocol. Any other properties and methods in the
|
|
* class specification will be available on the prototype.
|
|
*
|
|
* @interface ReactClassInterface
|
|
* @internal
|
|
*/
|
|
var ReactClassInterface = {
|
|
|
|
/**
|
|
* An array of Mixin objects to include when defining your component.
|
|
*
|
|
* @type {array}
|
|
* @optional
|
|
*/
|
|
mixins: SpecPolicy.DEFINE_MANY,
|
|
|
|
/**
|
|
* An object containing properties and methods that should be defined on
|
|
* the component's constructor instead of its prototype (static methods).
|
|
*
|
|
* @type {object}
|
|
* @optional
|
|
*/
|
|
statics: SpecPolicy.DEFINE_MANY,
|
|
|
|
/**
|
|
* Definition of prop types for this component.
|
|
*
|
|
* @type {object}
|
|
* @optional
|
|
*/
|
|
propTypes: SpecPolicy.DEFINE_MANY,
|
|
|
|
/**
|
|
* Definition of context types for this component.
|
|
*
|
|
* @type {object}
|
|
* @optional
|
|
*/
|
|
contextTypes: SpecPolicy.DEFINE_MANY,
|
|
|
|
/**
|
|
* Definition of context types this component sets for its children.
|
|
*
|
|
* @type {object}
|
|
* @optional
|
|
*/
|
|
childContextTypes: SpecPolicy.DEFINE_MANY,
|
|
|
|
// ==== Definition methods ====
|
|
|
|
/**
|
|
* Invoked when the component is mounted. Values in the mapping will be set on
|
|
* `this.props` if that prop is not specified (i.e. using an `in` check).
|
|
*
|
|
* This method is invoked before `getInitialState` and therefore cannot rely
|
|
* on `this.state` or use `this.setState`.
|
|
*
|
|
* @return {object}
|
|
* @optional
|
|
*/
|
|
getDefaultProps: SpecPolicy.DEFINE_MANY_MERGED,
|
|
|
|
/**
|
|
* Invoked once before the component is mounted. The return value will be used
|
|
* as the initial value of `this.state`.
|
|
*
|
|
* getInitialState: function() {
|
|
* return {
|
|
* isOn: false,
|
|
* fooBaz: new BazFoo()
|
|
* }
|
|
* }
|
|
*
|
|
* @return {object}
|
|
* @optional
|
|
*/
|
|
getInitialState: SpecPolicy.DEFINE_MANY_MERGED,
|
|
|
|
/**
|
|
* @return {object}
|
|
* @optional
|
|
*/
|
|
getChildContext: SpecPolicy.DEFINE_MANY_MERGED,
|
|
|
|
/**
|
|
* Uses props from `this.props` and state from `this.state` to render the
|
|
* structure of the component.
|
|
*
|
|
* No guarantees are made about when or how often this method is invoked, so
|
|
* it must not have side effects.
|
|
*
|
|
* render: function() {
|
|
* var name = this.props.name;
|
|
* return <div>Hello, {name}!</div>;
|
|
* }
|
|
*
|
|
* @return {ReactComponent}
|
|
* @nosideeffects
|
|
* @required
|
|
*/
|
|
render: SpecPolicy.DEFINE_ONCE,
|
|
|
|
// ==== Delegate methods ====
|
|
|
|
/**
|
|
* Invoked when the component is initially created and about to be mounted.
|
|
* This may have side effects, but any external subscriptions or data created
|
|
* by this method must be cleaned up in `componentWillUnmount`.
|
|
*
|
|
* @optional
|
|
*/
|
|
componentWillMount: SpecPolicy.DEFINE_MANY,
|
|
|
|
/**
|
|
* Invoked when the component has been mounted and has a DOM representation.
|
|
* However, there is no guarantee that the DOM node is in the document.
|
|
*
|
|
* Use this as an opportunity to operate on the DOM when the component has
|
|
* been mounted (initialized and rendered) for the first time.
|
|
*
|
|
* @param {DOMElement} rootNode DOM element representing the component.
|
|
* @optional
|
|
*/
|
|
componentDidMount: SpecPolicy.DEFINE_MANY,
|
|
|
|
/**
|
|
* Invoked before the component receives new props.
|
|
*
|
|
* Use this as an opportunity to react to a prop transition by updating the
|
|
* state using `this.setState`. Current props are accessed via `this.props`.
|
|
*
|
|
* componentWillReceiveProps: function(nextProps, nextContext) {
|
|
* this.setState({
|
|
* likesIncreasing: nextProps.likeCount > this.props.likeCount
|
|
* });
|
|
* }
|
|
*
|
|
* NOTE: There is no equivalent `componentWillReceiveState`. An incoming prop
|
|
* transition may cause a state change, but the opposite is not true. If you
|
|
* need it, you are probably looking for `componentWillUpdate`.
|
|
*
|
|
* @param {object} nextProps
|
|
* @optional
|
|
*/
|
|
componentWillReceiveProps: SpecPolicy.DEFINE_MANY,
|
|
|
|
/**
|
|
* Invoked while deciding if the component should be updated as a result of
|
|
* receiving new props, state and/or context.
|
|
*
|
|
* Use this as an opportunity to `return false` when you're certain that the
|
|
* transition to the new props/state/context will not require a component
|
|
* update.
|
|
*
|
|
* shouldComponentUpdate: function(nextProps, nextState, nextContext) {
|
|
* return !equal(nextProps, this.props) ||
|
|
* !equal(nextState, this.state) ||
|
|
* !equal(nextContext, this.context);
|
|
* }
|
|
*
|
|
* @param {object} nextProps
|
|
* @param {?object} nextState
|
|
* @param {?object} nextContext
|
|
* @return {boolean} True if the component should update.
|
|
* @optional
|
|
*/
|
|
shouldComponentUpdate: SpecPolicy.DEFINE_ONCE,
|
|
|
|
/**
|
|
* Invoked when the component is about to update due to a transition from
|
|
* `this.props`, `this.state` and `this.context` to `nextProps`, `nextState`
|
|
* and `nextContext`.
|
|
*
|
|
* Use this as an opportunity to perform preparation before an update occurs.
|
|
*
|
|
* NOTE: You **cannot** use `this.setState()` in this method.
|
|
*
|
|
* @param {object} nextProps
|
|
* @param {?object} nextState
|
|
* @param {?object} nextContext
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @optional
|
|
*/
|
|
componentWillUpdate: SpecPolicy.DEFINE_MANY,
|
|
|
|
/**
|
|
* Invoked when the component's DOM representation has been updated.
|
|
*
|
|
* Use this as an opportunity to operate on the DOM when the component has
|
|
* been updated.
|
|
*
|
|
* @param {object} prevProps
|
|
* @param {?object} prevState
|
|
* @param {?object} prevContext
|
|
* @param {DOMElement} rootNode DOM element representing the component.
|
|
* @optional
|
|
*/
|
|
componentDidUpdate: SpecPolicy.DEFINE_MANY,
|
|
|
|
/**
|
|
* Invoked when the component is about to be removed from its parent and have
|
|
* its DOM representation destroyed.
|
|
*
|
|
* Use this as an opportunity to deallocate any external resources.
|
|
*
|
|
* NOTE: There is no `componentDidUnmount` since your component will have been
|
|
* destroyed by that point.
|
|
*
|
|
* @optional
|
|
*/
|
|
componentWillUnmount: SpecPolicy.DEFINE_MANY,
|
|
|
|
// ==== Advanced methods ====
|
|
|
|
/**
|
|
* Updates the component's currently mounted DOM representation.
|
|
*
|
|
* By default, this implements React's rendering and reconciliation algorithm.
|
|
* Sophisticated clients may wish to override this.
|
|
*
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @internal
|
|
* @overridable
|
|
*/
|
|
updateComponent: SpecPolicy.OVERRIDE_BASE
|
|
|
|
};
|
|
|
|
/**
|
|
* Mapping from class specification keys to special processing functions.
|
|
*
|
|
* Although these are declared like instance properties in the specification
|
|
* when defining classes using `React.createClass`, they are actually static
|
|
* and are accessible on the constructor instead of the prototype. Despite
|
|
* being static, they must be defined outside of the "statics" key under
|
|
* which all other static methods are defined.
|
|
*/
|
|
var RESERVED_SPEC_KEYS = {
|
|
displayName: function (Constructor, displayName) {
|
|
Constructor.displayName = displayName;
|
|
},
|
|
mixins: function (Constructor, mixins) {
|
|
if (mixins) {
|
|
for (var i = 0; i < mixins.length; i++) {
|
|
mixSpecIntoComponent(Constructor, mixins[i]);
|
|
}
|
|
}
|
|
},
|
|
childContextTypes: function (Constructor, childContextTypes) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
validateTypeDef(Constructor, childContextTypes, ReactPropTypeLocations.childContext);
|
|
}
|
|
Constructor.childContextTypes = assign({}, Constructor.childContextTypes, childContextTypes);
|
|
},
|
|
contextTypes: function (Constructor, contextTypes) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
validateTypeDef(Constructor, contextTypes, ReactPropTypeLocations.context);
|
|
}
|
|
Constructor.contextTypes = assign({}, Constructor.contextTypes, contextTypes);
|
|
},
|
|
/**
|
|
* Special case getDefaultProps which should move into statics but requires
|
|
* automatic merging.
|
|
*/
|
|
getDefaultProps: function (Constructor, getDefaultProps) {
|
|
if (Constructor.getDefaultProps) {
|
|
Constructor.getDefaultProps = createMergedResultFunction(Constructor.getDefaultProps, getDefaultProps);
|
|
} else {
|
|
Constructor.getDefaultProps = getDefaultProps;
|
|
}
|
|
},
|
|
propTypes: function (Constructor, propTypes) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
validateTypeDef(Constructor, propTypes, ReactPropTypeLocations.prop);
|
|
}
|
|
Constructor.propTypes = assign({}, Constructor.propTypes, propTypes);
|
|
},
|
|
statics: function (Constructor, statics) {
|
|
mixStaticSpecIntoComponent(Constructor, statics);
|
|
},
|
|
autobind: function () {} };
|
|
|
|
// noop
|
|
function validateTypeDef(Constructor, typeDef, location) {
|
|
for (var propName in typeDef) {
|
|
if (typeDef.hasOwnProperty(propName)) {
|
|
// use a warning instead of an invariant so components
|
|
// don't show up in prod but not in __DEV__
|
|
process.env.NODE_ENV !== 'production' ? warning(typeof typeDef[propName] === 'function', '%s: %s type `%s` is invalid; it must be a function, usually from ' + 'React.PropTypes.', Constructor.displayName || 'ReactClass', ReactPropTypeLocationNames[location], propName) : undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
function validateMethodOverride(proto, name) {
|
|
var specPolicy = ReactClassInterface.hasOwnProperty(name) ? ReactClassInterface[name] : null;
|
|
|
|
// Disallow overriding of base class methods unless explicitly allowed.
|
|
if (ReactClassMixin.hasOwnProperty(name)) {
|
|
!(specPolicy === SpecPolicy.OVERRIDE_BASE) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactClassInterface: You are attempting to override ' + '`%s` from your class specification. Ensure that your method names ' + 'do not overlap with React methods.', name) : invariant(false) : undefined;
|
|
}
|
|
|
|
// Disallow defining methods more than once unless explicitly allowed.
|
|
if (proto.hasOwnProperty(name)) {
|
|
!(specPolicy === SpecPolicy.DEFINE_MANY || specPolicy === SpecPolicy.DEFINE_MANY_MERGED) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactClassInterface: You are attempting to define ' + '`%s` on your component more than once. This conflict may be due ' + 'to a mixin.', name) : invariant(false) : undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mixin helper which handles policy validation and reserved
|
|
* specification keys when building React classses.
|
|
*/
|
|
function mixSpecIntoComponent(Constructor, spec) {
|
|
if (!spec) {
|
|
return;
|
|
}
|
|
|
|
!(typeof spec !== 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactClass: You\'re attempting to ' + 'use a component class as a mixin. Instead, just use a regular object.') : invariant(false) : undefined;
|
|
!!ReactElement.isValidElement(spec) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactClass: You\'re attempting to ' + 'use a component as a mixin. Instead, just use a regular object.') : invariant(false) : undefined;
|
|
|
|
var proto = Constructor.prototype;
|
|
|
|
// By handling mixins before any other properties, we ensure the same
|
|
// chaining order is applied to methods with DEFINE_MANY policy, whether
|
|
// mixins are listed before or after these methods in the spec.
|
|
if (spec.hasOwnProperty(MIXINS_KEY)) {
|
|
RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins);
|
|
}
|
|
|
|
for (var name in spec) {
|
|
if (!spec.hasOwnProperty(name)) {
|
|
continue;
|
|
}
|
|
|
|
if (name === MIXINS_KEY) {
|
|
// We have already handled mixins in a special case above.
|
|
continue;
|
|
}
|
|
|
|
var property = spec[name];
|
|
validateMethodOverride(proto, name);
|
|
|
|
if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) {
|
|
RESERVED_SPEC_KEYS[name](Constructor, property);
|
|
} else {
|
|
// Setup methods on prototype:
|
|
// The following member methods should not be automatically bound:
|
|
// 1. Expected ReactClass methods (in the "interface").
|
|
// 2. Overridden methods (that were mixed in).
|
|
var isReactClassMethod = ReactClassInterface.hasOwnProperty(name);
|
|
var isAlreadyDefined = proto.hasOwnProperty(name);
|
|
var isFunction = typeof property === 'function';
|
|
var shouldAutoBind = isFunction && !isReactClassMethod && !isAlreadyDefined && spec.autobind !== false;
|
|
|
|
if (shouldAutoBind) {
|
|
if (!proto.__reactAutoBindMap) {
|
|
proto.__reactAutoBindMap = {};
|
|
}
|
|
proto.__reactAutoBindMap[name] = property;
|
|
proto[name] = property;
|
|
} else {
|
|
if (isAlreadyDefined) {
|
|
var specPolicy = ReactClassInterface[name];
|
|
|
|
// These cases should already be caught by validateMethodOverride.
|
|
!(isReactClassMethod && (specPolicy === SpecPolicy.DEFINE_MANY_MERGED || specPolicy === SpecPolicy.DEFINE_MANY)) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactClass: Unexpected spec policy %s for key %s ' + 'when mixing in component specs.', specPolicy, name) : invariant(false) : undefined;
|
|
|
|
// For methods which are defined more than once, call the existing
|
|
// methods before calling the new property, merging if appropriate.
|
|
if (specPolicy === SpecPolicy.DEFINE_MANY_MERGED) {
|
|
proto[name] = createMergedResultFunction(proto[name], property);
|
|
} else if (specPolicy === SpecPolicy.DEFINE_MANY) {
|
|
proto[name] = createChainedFunction(proto[name], property);
|
|
}
|
|
} else {
|
|
proto[name] = property;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// Add verbose displayName to the function, which helps when looking
|
|
// at profiling tools.
|
|
if (typeof property === 'function' && spec.displayName) {
|
|
proto[name].displayName = spec.displayName + '_' + name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function mixStaticSpecIntoComponent(Constructor, statics) {
|
|
if (!statics) {
|
|
return;
|
|
}
|
|
for (var name in statics) {
|
|
var property = statics[name];
|
|
if (!statics.hasOwnProperty(name)) {
|
|
continue;
|
|
}
|
|
|
|
var isReserved = (name in RESERVED_SPEC_KEYS);
|
|
!!isReserved ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactClass: You are attempting to define a reserved ' + 'property, `%s`, that shouldn\'t be on the "statics" key. Define it ' + 'as an instance property instead; it will still be accessible on the ' + 'constructor.', name) : invariant(false) : undefined;
|
|
|
|
var isInherited = (name in Constructor);
|
|
!!isInherited ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactClass: You are attempting to define ' + '`%s` on your component more than once. This conflict may be ' + 'due to a mixin.', name) : invariant(false) : undefined;
|
|
Constructor[name] = property;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Merge two objects, but throw if both contain the same key.
|
|
*
|
|
* @param {object} one The first object, which is mutated.
|
|
* @param {object} two The second object
|
|
* @return {object} one after it has been mutated to contain everything in two.
|
|
*/
|
|
function mergeIntoWithNoDuplicateKeys(one, two) {
|
|
!(one && two && typeof one === 'object' && typeof two === 'object') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.') : invariant(false) : undefined;
|
|
|
|
for (var key in two) {
|
|
if (two.hasOwnProperty(key)) {
|
|
!(one[key] === undefined) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'mergeIntoWithNoDuplicateKeys(): ' + 'Tried to merge two objects with the same key: `%s`. This conflict ' + 'may be due to a mixin; in particular, this may be caused by two ' + 'getInitialState() or getDefaultProps() methods returning objects ' + 'with clashing keys.', key) : invariant(false) : undefined;
|
|
one[key] = two[key];
|
|
}
|
|
}
|
|
return one;
|
|
}
|
|
|
|
/**
|
|
* Creates a function that invokes two functions and merges their return values.
|
|
*
|
|
* @param {function} one Function to invoke first.
|
|
* @param {function} two Function to invoke second.
|
|
* @return {function} Function that invokes the two argument functions.
|
|
* @private
|
|
*/
|
|
function createMergedResultFunction(one, two) {
|
|
return function mergedResult() {
|
|
var a = one.apply(this, arguments);
|
|
var b = two.apply(this, arguments);
|
|
if (a == null) {
|
|
return b;
|
|
} else if (b == null) {
|
|
return a;
|
|
}
|
|
var c = {};
|
|
mergeIntoWithNoDuplicateKeys(c, a);
|
|
mergeIntoWithNoDuplicateKeys(c, b);
|
|
return c;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates a function that invokes two functions and ignores their return vales.
|
|
*
|
|
* @param {function} one Function to invoke first.
|
|
* @param {function} two Function to invoke second.
|
|
* @return {function} Function that invokes the two argument functions.
|
|
* @private
|
|
*/
|
|
function createChainedFunction(one, two) {
|
|
return function chainedFunction() {
|
|
one.apply(this, arguments);
|
|
two.apply(this, arguments);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Binds a method to the component.
|
|
*
|
|
* @param {object} component Component whose method is going to be bound.
|
|
* @param {function} method Method to be bound.
|
|
* @return {function} The bound method.
|
|
*/
|
|
function bindAutoBindMethod(component, method) {
|
|
var boundMethod = method.bind(component);
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
boundMethod.__reactBoundContext = component;
|
|
boundMethod.__reactBoundMethod = method;
|
|
boundMethod.__reactBoundArguments = null;
|
|
var componentName = component.constructor.displayName;
|
|
var _bind = boundMethod.bind;
|
|
/* eslint-disable block-scoped-var, no-undef */
|
|
boundMethod.bind = function (newThis) {
|
|
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
args[_key - 1] = arguments[_key];
|
|
}
|
|
|
|
// User is trying to bind() an autobound method; we effectively will
|
|
// ignore the value of "this" that the user is trying to use, so
|
|
// let's warn.
|
|
if (newThis !== component && newThis !== null) {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'bind(): React component methods may only be bound to the ' + 'component instance. See %s', componentName) : undefined;
|
|
} else if (!args.length) {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'bind(): You are binding a component method to the component. ' + 'React does this for you automatically in a high-performance ' + 'way, so you can safely remove this call. See %s', componentName) : undefined;
|
|
return boundMethod;
|
|
}
|
|
var reboundMethod = _bind.apply(boundMethod, arguments);
|
|
reboundMethod.__reactBoundContext = component;
|
|
reboundMethod.__reactBoundMethod = method;
|
|
reboundMethod.__reactBoundArguments = args;
|
|
return reboundMethod;
|
|
/* eslint-enable */
|
|
};
|
|
}
|
|
return boundMethod;
|
|
}
|
|
|
|
/**
|
|
* Binds all auto-bound methods in a component.
|
|
*
|
|
* @param {object} component Component whose method is going to be bound.
|
|
*/
|
|
function bindAutoBindMethods(component) {
|
|
for (var autoBindKey in component.__reactAutoBindMap) {
|
|
if (component.__reactAutoBindMap.hasOwnProperty(autoBindKey)) {
|
|
var method = component.__reactAutoBindMap[autoBindKey];
|
|
component[autoBindKey] = bindAutoBindMethod(component, method);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add more to the ReactClass base class. These are all legacy features and
|
|
* therefore not already part of the modern ReactComponent.
|
|
*/
|
|
var ReactClassMixin = {
|
|
|
|
/**
|
|
* TODO: This will be deprecated because state should always keep a consistent
|
|
* type signature and the only use case for this, is to avoid that.
|
|
*/
|
|
replaceState: function (newState, callback) {
|
|
this.updater.enqueueReplaceState(this, newState);
|
|
if (callback) {
|
|
this.updater.enqueueCallback(this, callback);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Checks whether or not this composite component is mounted.
|
|
* @return {boolean} True if mounted, false otherwise.
|
|
* @protected
|
|
* @final
|
|
*/
|
|
isMounted: function () {
|
|
return this.updater.isMounted(this);
|
|
},
|
|
|
|
/**
|
|
* Sets a subset of the props.
|
|
*
|
|
* @param {object} partialProps Subset of the next props.
|
|
* @param {?function} callback Called after props are updated.
|
|
* @final
|
|
* @public
|
|
* @deprecated
|
|
*/
|
|
setProps: function (partialProps, callback) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
warnSetProps();
|
|
}
|
|
this.updater.enqueueSetProps(this, partialProps);
|
|
if (callback) {
|
|
this.updater.enqueueCallback(this, callback);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Replace all the props.
|
|
*
|
|
* @param {object} newProps Subset of the next props.
|
|
* @param {?function} callback Called after props are updated.
|
|
* @final
|
|
* @public
|
|
* @deprecated
|
|
*/
|
|
replaceProps: function (newProps, callback) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
warnSetProps();
|
|
}
|
|
this.updater.enqueueReplaceProps(this, newProps);
|
|
if (callback) {
|
|
this.updater.enqueueCallback(this, callback);
|
|
}
|
|
}
|
|
};
|
|
|
|
var ReactClassComponent = function () {};
|
|
assign(ReactClassComponent.prototype, ReactComponent.prototype, ReactClassMixin);
|
|
|
|
/**
|
|
* Module for creating composite components.
|
|
*
|
|
* @class ReactClass
|
|
*/
|
|
var ReactClass = {
|
|
|
|
/**
|
|
* Creates a composite component class given a class specification.
|
|
*
|
|
* @param {object} spec Class specification (which must define `render`).
|
|
* @return {function} Component constructor function.
|
|
* @public
|
|
*/
|
|
createClass: function (spec) {
|
|
var Constructor = function (props, context, updater) {
|
|
// This constructor is overridden by mocks. The argument is used
|
|
// by mocks to assert on what gets mounted.
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(this instanceof Constructor, 'Something is calling a React component directly. Use a factory or ' + 'JSX instead. See: https://fb.me/react-legacyfactory') : undefined;
|
|
}
|
|
|
|
// Wire up auto-binding
|
|
if (this.__reactAutoBindMap) {
|
|
bindAutoBindMethods(this);
|
|
}
|
|
|
|
this.props = props;
|
|
this.context = context;
|
|
this.refs = emptyObject;
|
|
this.updater = updater || ReactNoopUpdateQueue;
|
|
|
|
this.state = null;
|
|
|
|
// ReactClasses doesn't have constructors. Instead, they use the
|
|
// getInitialState and componentWillMount methods for initialization.
|
|
|
|
var initialState = this.getInitialState ? this.getInitialState() : null;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// We allow auto-mocks to proceed as if they're returning null.
|
|
if (typeof initialState === 'undefined' && this.getInitialState._isMockFunction) {
|
|
// This is probably bad practice. Consider warning here and
|
|
// deprecating this convenience.
|
|
initialState = null;
|
|
}
|
|
}
|
|
!(typeof initialState === 'object' && !Array.isArray(initialState)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.getInitialState(): must return an object or null', Constructor.displayName || 'ReactCompositeComponent') : invariant(false) : undefined;
|
|
|
|
this.state = initialState;
|
|
};
|
|
Constructor.prototype = new ReactClassComponent();
|
|
Constructor.prototype.constructor = Constructor;
|
|
|
|
injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor));
|
|
|
|
mixSpecIntoComponent(Constructor, spec);
|
|
|
|
// Initialize the defaultProps property after all mixins have been merged.
|
|
if (Constructor.getDefaultProps) {
|
|
Constructor.defaultProps = Constructor.getDefaultProps();
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// This is a tag to indicate that the use of these method names is ok,
|
|
// since it's used with createClass. If it's not, then it's likely a
|
|
// mistake so we'll warn you to use the static property, property
|
|
// initializer or constructor respectively.
|
|
if (Constructor.getDefaultProps) {
|
|
Constructor.getDefaultProps.isReactClassApproved = {};
|
|
}
|
|
if (Constructor.prototype.getInitialState) {
|
|
Constructor.prototype.getInitialState.isReactClassApproved = {};
|
|
}
|
|
}
|
|
|
|
!Constructor.prototype.render ? process.env.NODE_ENV !== 'production' ? invariant(false, 'createClass(...): Class specification must implement a `render` method.') : invariant(false) : undefined;
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(!Constructor.prototype.componentShouldUpdate, '%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', spec.displayName || 'A component') : undefined;
|
|
process.env.NODE_ENV !== 'production' ? warning(!Constructor.prototype.componentWillRecieveProps, '%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', spec.displayName || 'A component') : undefined;
|
|
}
|
|
|
|
// Reduce time spent doing lookups by setting these on the prototype.
|
|
for (var methodName in ReactClassInterface) {
|
|
if (!Constructor.prototype[methodName]) {
|
|
Constructor.prototype[methodName] = null;
|
|
}
|
|
}
|
|
|
|
return Constructor;
|
|
},
|
|
|
|
injection: {
|
|
injectMixin: function (mixin) {
|
|
injectedMixins.push(mixin);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactClass;
|
|
}).call(this,require('_process'))
|
|
},{"./Object.assign":157,"./ReactComponent":165,"./ReactElement":187,"./ReactNoopUpdateQueue":204,"./ReactPropTypeLocationNames":207,"./ReactPropTypeLocations":208,"_process":133,"fbjs/lib/emptyObject":65,"fbjs/lib/invariant":72,"fbjs/lib/keyMirror":75,"fbjs/lib/keyOf":76,"fbjs/lib/warning":83}],165:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactComponent
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactNoopUpdateQueue = require('./ReactNoopUpdateQueue');
|
|
|
|
var canDefineProperty = require('./canDefineProperty');
|
|
var emptyObject = require('fbjs/lib/emptyObject');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
/**
|
|
* Base class helpers for the updating state of a component.
|
|
*/
|
|
function ReactComponent(props, context, updater) {
|
|
this.props = props;
|
|
this.context = context;
|
|
this.refs = emptyObject;
|
|
// We initialize the default updater but the real one gets injected by the
|
|
// renderer.
|
|
this.updater = updater || ReactNoopUpdateQueue;
|
|
}
|
|
|
|
ReactComponent.prototype.isReactComponent = {};
|
|
|
|
/**
|
|
* Sets a subset of the state. Always use this to mutate
|
|
* state. You should treat `this.state` as immutable.
|
|
*
|
|
* There is no guarantee that `this.state` will be immediately updated, so
|
|
* accessing `this.state` after calling this method may return the old value.
|
|
*
|
|
* There is no guarantee that calls to `setState` will run synchronously,
|
|
* as they may eventually be batched together. You can provide an optional
|
|
* callback that will be executed when the call to setState is actually
|
|
* completed.
|
|
*
|
|
* When a function is provided to setState, it will be called at some point in
|
|
* the future (not synchronously). It will be called with the up to date
|
|
* component arguments (state, props, context). These values can be different
|
|
* from this.* because your function may be called after receiveProps but before
|
|
* shouldComponentUpdate, and this new state, props, and context will not yet be
|
|
* assigned to this.
|
|
*
|
|
* @param {object|function} partialState Next partial state or function to
|
|
* produce next partial state to be merged with current state.
|
|
* @param {?function} callback Called after state is updated.
|
|
* @final
|
|
* @protected
|
|
*/
|
|
ReactComponent.prototype.setState = function (partialState, callback) {
|
|
!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'setState(...): takes an object of state variables to update or a ' + 'function which returns an object of state variables.') : invariant(false) : undefined;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(partialState != null, 'setState(...): You passed an undefined or null state object; ' + 'instead, use forceUpdate().') : undefined;
|
|
}
|
|
this.updater.enqueueSetState(this, partialState);
|
|
if (callback) {
|
|
this.updater.enqueueCallback(this, callback);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Forces an update. This should only be invoked when it is known with
|
|
* certainty that we are **not** in a DOM transaction.
|
|
*
|
|
* You may want to call this when you know that some deeper aspect of the
|
|
* component's state has changed but `setState` was not called.
|
|
*
|
|
* This will not invoke `shouldComponentUpdate`, but it will invoke
|
|
* `componentWillUpdate` and `componentDidUpdate`.
|
|
*
|
|
* @param {?function} callback Called after update is complete.
|
|
* @final
|
|
* @protected
|
|
*/
|
|
ReactComponent.prototype.forceUpdate = function (callback) {
|
|
this.updater.enqueueForceUpdate(this);
|
|
if (callback) {
|
|
this.updater.enqueueCallback(this, callback);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Deprecated APIs. These APIs used to exist on classic React classes but since
|
|
* we would like to deprecate them, we're not going to move them over to this
|
|
* modern base class. Instead, we define a getter that warns if it's accessed.
|
|
*/
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var deprecatedAPIs = {
|
|
getDOMNode: ['getDOMNode', 'Use ReactDOM.findDOMNode(component) instead.'],
|
|
isMounted: ['isMounted', 'Instead, make sure to clean up subscriptions and pending requests in ' + 'componentWillUnmount to prevent memory leaks.'],
|
|
replaceProps: ['replaceProps', 'Instead, call render again at the top level.'],
|
|
replaceState: ['replaceState', 'Refactor your code to use setState instead (see ' + 'https://github.com/facebook/react/issues/3236).'],
|
|
setProps: ['setProps', 'Instead, call render again at the top level.']
|
|
};
|
|
var defineDeprecationWarning = function (methodName, info) {
|
|
if (canDefineProperty) {
|
|
Object.defineProperty(ReactComponent.prototype, methodName, {
|
|
get: function () {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, '%s(...) is deprecated in plain JavaScript React classes. %s', info[0], info[1]) : undefined;
|
|
return undefined;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
for (var fnName in deprecatedAPIs) {
|
|
if (deprecatedAPIs.hasOwnProperty(fnName)) {
|
|
defineDeprecationWarning(fnName, deprecatedAPIs[fnName]);
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = ReactComponent;
|
|
}).call(this,require('_process'))
|
|
},{"./ReactNoopUpdateQueue":204,"./canDefineProperty":239,"_process":133,"fbjs/lib/emptyObject":65,"fbjs/lib/invariant":72,"fbjs/lib/warning":83}],166:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactComponentBrowserEnvironment
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactDOMIDOperations = require('./ReactDOMIDOperations');
|
|
var ReactMount = require('./ReactMount');
|
|
|
|
/**
|
|
* Abstracts away all functionality of the reconciler that requires knowledge of
|
|
* the browser context. TODO: These callers should be refactored to avoid the
|
|
* need for this injection.
|
|
*/
|
|
var ReactComponentBrowserEnvironment = {
|
|
|
|
processChildrenUpdates: ReactDOMIDOperations.dangerouslyProcessChildrenUpdates,
|
|
|
|
replaceNodeWithMarkupByID: ReactDOMIDOperations.dangerouslyReplaceNodeWithMarkupByID,
|
|
|
|
/**
|
|
* If a particular environment requires that some resources be cleaned up,
|
|
* specify this in the injected Mixin. In the DOM, we would likely want to
|
|
* purge any cached node ID lookups.
|
|
*
|
|
* @private
|
|
*/
|
|
unmountIDFromEnvironment: function (rootNodeID) {
|
|
ReactMount.purgeID(rootNodeID);
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactComponentBrowserEnvironment;
|
|
},{"./ReactDOMIDOperations":175,"./ReactMount":200}],167:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2014-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactComponentEnvironment
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
var injected = false;
|
|
|
|
var ReactComponentEnvironment = {
|
|
|
|
/**
|
|
* Optionally injectable environment dependent cleanup hook. (server vs.
|
|
* browser etc). Example: A browser system caches DOM nodes based on component
|
|
* ID and must remove that cache entry when this instance is unmounted.
|
|
*/
|
|
unmountIDFromEnvironment: null,
|
|
|
|
/**
|
|
* Optionally injectable hook for swapping out mount images in the middle of
|
|
* the tree.
|
|
*/
|
|
replaceNodeWithMarkupByID: null,
|
|
|
|
/**
|
|
* Optionally injectable hook for processing a queue of child updates. Will
|
|
* later move into MultiChildComponents.
|
|
*/
|
|
processChildrenUpdates: null,
|
|
|
|
injection: {
|
|
injectEnvironment: function (environment) {
|
|
!!injected ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactCompositeComponent: injectEnvironment() can only be called once.') : invariant(false) : undefined;
|
|
ReactComponentEnvironment.unmountIDFromEnvironment = environment.unmountIDFromEnvironment;
|
|
ReactComponentEnvironment.replaceNodeWithMarkupByID = environment.replaceNodeWithMarkupByID;
|
|
ReactComponentEnvironment.processChildrenUpdates = environment.processChildrenUpdates;
|
|
injected = true;
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactComponentEnvironment;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133,"fbjs/lib/invariant":72}],168:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactCompositeComponent
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactComponentEnvironment = require('./ReactComponentEnvironment');
|
|
var ReactCurrentOwner = require('./ReactCurrentOwner');
|
|
var ReactElement = require('./ReactElement');
|
|
var ReactInstanceMap = require('./ReactInstanceMap');
|
|
var ReactPerf = require('./ReactPerf');
|
|
var ReactPropTypeLocations = require('./ReactPropTypeLocations');
|
|
var ReactPropTypeLocationNames = require('./ReactPropTypeLocationNames');
|
|
var ReactReconciler = require('./ReactReconciler');
|
|
var ReactUpdateQueue = require('./ReactUpdateQueue');
|
|
|
|
var assign = require('./Object.assign');
|
|
var emptyObject = require('fbjs/lib/emptyObject');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var shouldUpdateReactComponent = require('./shouldUpdateReactComponent');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
function getDeclarationErrorAddendum(component) {
|
|
var owner = component._currentElement._owner || null;
|
|
if (owner) {
|
|
var name = owner.getName();
|
|
if (name) {
|
|
return ' Check the render method of `' + name + '`.';
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function StatelessComponent(Component) {}
|
|
StatelessComponent.prototype.render = function () {
|
|
var Component = ReactInstanceMap.get(this)._currentElement.type;
|
|
return Component(this.props, this.context, this.updater);
|
|
};
|
|
|
|
/**
|
|
* ------------------ The Life-Cycle of a Composite Component ------------------
|
|
*
|
|
* - constructor: Initialization of state. The instance is now retained.
|
|
* - componentWillMount
|
|
* - render
|
|
* - [children's constructors]
|
|
* - [children's componentWillMount and render]
|
|
* - [children's componentDidMount]
|
|
* - componentDidMount
|
|
*
|
|
* Update Phases:
|
|
* - componentWillReceiveProps (only called if parent updated)
|
|
* - shouldComponentUpdate
|
|
* - componentWillUpdate
|
|
* - render
|
|
* - [children's constructors or receive props phases]
|
|
* - componentDidUpdate
|
|
*
|
|
* - componentWillUnmount
|
|
* - [children's componentWillUnmount]
|
|
* - [children destroyed]
|
|
* - (destroyed): The instance is now blank, released by React and ready for GC.
|
|
*
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* An incrementing ID assigned to each component when it is mounted. This is
|
|
* used to enforce the order in which `ReactUpdates` updates dirty components.
|
|
*
|
|
* @private
|
|
*/
|
|
var nextMountID = 1;
|
|
|
|
/**
|
|
* @lends {ReactCompositeComponent.prototype}
|
|
*/
|
|
var ReactCompositeComponentMixin = {
|
|
|
|
/**
|
|
* Base constructor for all composite component.
|
|
*
|
|
* @param {ReactElement} element
|
|
* @final
|
|
* @internal
|
|
*/
|
|
construct: function (element) {
|
|
this._currentElement = element;
|
|
this._rootNodeID = null;
|
|
this._instance = null;
|
|
|
|
// See ReactUpdateQueue
|
|
this._pendingElement = null;
|
|
this._pendingStateQueue = null;
|
|
this._pendingReplaceState = false;
|
|
this._pendingForceUpdate = false;
|
|
|
|
this._renderedComponent = null;
|
|
|
|
this._context = null;
|
|
this._mountOrder = 0;
|
|
this._topLevelWrapper = null;
|
|
|
|
// See ReactUpdates and ReactUpdateQueue.
|
|
this._pendingCallbacks = null;
|
|
},
|
|
|
|
/**
|
|
* Initializes the component, renders markup, and registers event listeners.
|
|
*
|
|
* @param {string} rootID DOM ID of the root node.
|
|
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
|
* @return {?string} Rendered markup to be inserted into the DOM.
|
|
* @final
|
|
* @internal
|
|
*/
|
|
mountComponent: function (rootID, transaction, context) {
|
|
this._context = context;
|
|
this._mountOrder = nextMountID++;
|
|
this._rootNodeID = rootID;
|
|
|
|
var publicProps = this._processProps(this._currentElement.props);
|
|
var publicContext = this._processContext(context);
|
|
|
|
var Component = this._currentElement.type;
|
|
|
|
// Initialize the public class
|
|
var inst;
|
|
var renderedElement;
|
|
|
|
// This is a way to detect if Component is a stateless arrow function
|
|
// component, which is not newable. It might not be 100% reliable but is
|
|
// something we can do until we start detecting that Component extends
|
|
// React.Component. We already assume that typeof Component === 'function'.
|
|
var canInstantiate = ('prototype' in Component);
|
|
|
|
if (canInstantiate) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
ReactCurrentOwner.current = this;
|
|
try {
|
|
inst = new Component(publicProps, publicContext, ReactUpdateQueue);
|
|
} finally {
|
|
ReactCurrentOwner.current = null;
|
|
}
|
|
} else {
|
|
inst = new Component(publicProps, publicContext, ReactUpdateQueue);
|
|
}
|
|
}
|
|
|
|
if (!canInstantiate || inst === null || inst === false || ReactElement.isValidElement(inst)) {
|
|
renderedElement = inst;
|
|
inst = new StatelessComponent(Component);
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// This will throw later in _renderValidatedComponent, but add an early
|
|
// warning now to help debugging
|
|
if (inst.render == null) {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, '%s(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render`, returned ' + 'null/false from a stateless component, or tried to render an ' + 'element whose type is a function that isn\'t a React component.', Component.displayName || Component.name || 'Component') : undefined;
|
|
} else {
|
|
// We support ES6 inheriting from React.Component, the module pattern,
|
|
// and stateless components, but not ES6 classes that don't extend
|
|
process.env.NODE_ENV !== 'production' ? warning(Component.prototype && Component.prototype.isReactComponent || !canInstantiate || !(inst instanceof Component), '%s(...): React component classes must extend React.Component.', Component.displayName || Component.name || 'Component') : undefined;
|
|
}
|
|
}
|
|
|
|
// These should be set up in the constructor, but as a convenience for
|
|
// simpler class abstractions, we set them up after the fact.
|
|
inst.props = publicProps;
|
|
inst.context = publicContext;
|
|
inst.refs = emptyObject;
|
|
inst.updater = ReactUpdateQueue;
|
|
|
|
this._instance = inst;
|
|
|
|
// Store a reference from the instance back to the internal representation
|
|
ReactInstanceMap.set(inst, this);
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// Since plain JS classes are defined without any special initialization
|
|
// logic, we can not catch common errors early. Therefore, we have to
|
|
// catch them here, at initialization time, instead.
|
|
process.env.NODE_ENV !== 'production' ? warning(!inst.getInitialState || inst.getInitialState.isReactClassApproved, 'getInitialState was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Did you mean to define a state property instead?', this.getName() || 'a component') : undefined;
|
|
process.env.NODE_ENV !== 'production' ? warning(!inst.getDefaultProps || inst.getDefaultProps.isReactClassApproved, 'getDefaultProps was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Use a static property to define defaultProps instead.', this.getName() || 'a component') : undefined;
|
|
process.env.NODE_ENV !== 'production' ? warning(!inst.propTypes, 'propTypes was defined as an instance property on %s. Use a static ' + 'property to define propTypes instead.', this.getName() || 'a component') : undefined;
|
|
process.env.NODE_ENV !== 'production' ? warning(!inst.contextTypes, 'contextTypes was defined as an instance property on %s. Use a ' + 'static property to define contextTypes instead.', this.getName() || 'a component') : undefined;
|
|
process.env.NODE_ENV !== 'production' ? warning(typeof inst.componentShouldUpdate !== 'function', '%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', this.getName() || 'A component') : undefined;
|
|
process.env.NODE_ENV !== 'production' ? warning(typeof inst.componentDidUnmount !== 'function', '%s has a method called ' + 'componentDidUnmount(). But there is no such lifecycle method. ' + 'Did you mean componentWillUnmount()?', this.getName() || 'A component') : undefined;
|
|
process.env.NODE_ENV !== 'production' ? warning(typeof inst.componentWillRecieveProps !== 'function', '%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', this.getName() || 'A component') : undefined;
|
|
}
|
|
|
|
var initialState = inst.state;
|
|
if (initialState === undefined) {
|
|
inst.state = initialState = null;
|
|
}
|
|
!(typeof initialState === 'object' && !Array.isArray(initialState)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.state: must be set to an object or null', this.getName() || 'ReactCompositeComponent') : invariant(false) : undefined;
|
|
|
|
this._pendingStateQueue = null;
|
|
this._pendingReplaceState = false;
|
|
this._pendingForceUpdate = false;
|
|
|
|
if (inst.componentWillMount) {
|
|
inst.componentWillMount();
|
|
// When mounting, calls to `setState` by `componentWillMount` will set
|
|
// `this._pendingStateQueue` without triggering a re-render.
|
|
if (this._pendingStateQueue) {
|
|
inst.state = this._processPendingState(inst.props, inst.context);
|
|
}
|
|
}
|
|
|
|
// If not a stateless component, we now render
|
|
if (renderedElement === undefined) {
|
|
renderedElement = this._renderValidatedComponent();
|
|
}
|
|
|
|
this._renderedComponent = this._instantiateReactComponent(renderedElement);
|
|
|
|
var markup = ReactReconciler.mountComponent(this._renderedComponent, rootID, transaction, this._processChildContext(context));
|
|
if (inst.componentDidMount) {
|
|
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
|
|
}
|
|
|
|
return markup;
|
|
},
|
|
|
|
/**
|
|
* Releases any resources allocated by `mountComponent`.
|
|
*
|
|
* @final
|
|
* @internal
|
|
*/
|
|
unmountComponent: function () {
|
|
var inst = this._instance;
|
|
|
|
if (inst.componentWillUnmount) {
|
|
inst.componentWillUnmount();
|
|
}
|
|
|
|
ReactReconciler.unmountComponent(this._renderedComponent);
|
|
this._renderedComponent = null;
|
|
this._instance = null;
|
|
|
|
// Reset pending fields
|
|
// Even if this component is scheduled for another update in ReactUpdates,
|
|
// it would still be ignored because these fields are reset.
|
|
this._pendingStateQueue = null;
|
|
this._pendingReplaceState = false;
|
|
this._pendingForceUpdate = false;
|
|
this._pendingCallbacks = null;
|
|
this._pendingElement = null;
|
|
|
|
// These fields do not really need to be reset since this object is no
|
|
// longer accessible.
|
|
this._context = null;
|
|
this._rootNodeID = null;
|
|
this._topLevelWrapper = null;
|
|
|
|
// Delete the reference from the instance to this internal representation
|
|
// which allow the internals to be properly cleaned up even if the user
|
|
// leaks a reference to the public instance.
|
|
ReactInstanceMap.remove(inst);
|
|
|
|
// Some existing components rely on inst.props even after they've been
|
|
// destroyed (in event handlers).
|
|
// TODO: inst.props = null;
|
|
// TODO: inst.state = null;
|
|
// TODO: inst.context = null;
|
|
},
|
|
|
|
/**
|
|
* Filters the context object to only contain keys specified in
|
|
* `contextTypes`
|
|
*
|
|
* @param {object} context
|
|
* @return {?object}
|
|
* @private
|
|
*/
|
|
_maskContext: function (context) {
|
|
var maskedContext = null;
|
|
var Component = this._currentElement.type;
|
|
var contextTypes = Component.contextTypes;
|
|
if (!contextTypes) {
|
|
return emptyObject;
|
|
}
|
|
maskedContext = {};
|
|
for (var contextName in contextTypes) {
|
|
maskedContext[contextName] = context[contextName];
|
|
}
|
|
return maskedContext;
|
|
},
|
|
|
|
/**
|
|
* Filters the context object to only contain keys specified in
|
|
* `contextTypes`, and asserts that they are valid.
|
|
*
|
|
* @param {object} context
|
|
* @return {?object}
|
|
* @private
|
|
*/
|
|
_processContext: function (context) {
|
|
var maskedContext = this._maskContext(context);
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var Component = this._currentElement.type;
|
|
if (Component.contextTypes) {
|
|
this._checkPropTypes(Component.contextTypes, maskedContext, ReactPropTypeLocations.context);
|
|
}
|
|
}
|
|
return maskedContext;
|
|
},
|
|
|
|
/**
|
|
* @param {object} currentContext
|
|
* @return {object}
|
|
* @private
|
|
*/
|
|
_processChildContext: function (currentContext) {
|
|
var Component = this._currentElement.type;
|
|
var inst = this._instance;
|
|
var childContext = inst.getChildContext && inst.getChildContext();
|
|
if (childContext) {
|
|
!(typeof Component.childContextTypes === 'object') ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.getChildContext(): childContextTypes must be defined in order to ' + 'use getChildContext().', this.getName() || 'ReactCompositeComponent') : invariant(false) : undefined;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
this._checkPropTypes(Component.childContextTypes, childContext, ReactPropTypeLocations.childContext);
|
|
}
|
|
for (var name in childContext) {
|
|
!(name in Component.childContextTypes) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.getChildContext(): key "%s" is not defined in childContextTypes.', this.getName() || 'ReactCompositeComponent', name) : invariant(false) : undefined;
|
|
}
|
|
return assign({}, currentContext, childContext);
|
|
}
|
|
return currentContext;
|
|
},
|
|
|
|
/**
|
|
* Processes props by setting default values for unspecified props and
|
|
* asserting that the props are valid. Does not mutate its argument; returns
|
|
* a new props object with defaults merged in.
|
|
*
|
|
* @param {object} newProps
|
|
* @return {object}
|
|
* @private
|
|
*/
|
|
_processProps: function (newProps) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var Component = this._currentElement.type;
|
|
if (Component.propTypes) {
|
|
this._checkPropTypes(Component.propTypes, newProps, ReactPropTypeLocations.prop);
|
|
}
|
|
}
|
|
return newProps;
|
|
},
|
|
|
|
/**
|
|
* Assert that the props are valid
|
|
*
|
|
* @param {object} propTypes Map of prop name to a ReactPropType
|
|
* @param {object} props
|
|
* @param {string} location e.g. "prop", "context", "child context"
|
|
* @private
|
|
*/
|
|
_checkPropTypes: function (propTypes, props, location) {
|
|
// TODO: Stop validating prop types here and only use the element
|
|
// validation.
|
|
var componentName = this.getName();
|
|
for (var propName in propTypes) {
|
|
if (propTypes.hasOwnProperty(propName)) {
|
|
var error;
|
|
try {
|
|
// This is intentionally an invariant that gets caught. It's the same
|
|
// behavior as without this statement except with a better message.
|
|
!(typeof propTypes[propName] === 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s: %s type `%s` is invalid; it must be a function, usually ' + 'from React.PropTypes.', componentName || 'React class', ReactPropTypeLocationNames[location], propName) : invariant(false) : undefined;
|
|
error = propTypes[propName](props, propName, componentName, location);
|
|
} catch (ex) {
|
|
error = ex;
|
|
}
|
|
if (error instanceof Error) {
|
|
// We may want to extend this logic for similar errors in
|
|
// top-level render calls, so I'm abstracting it away into
|
|
// a function to minimize refactoring in the future
|
|
var addendum = getDeclarationErrorAddendum(this);
|
|
|
|
if (location === ReactPropTypeLocations.prop) {
|
|
// Preface gives us something to blacklist in warning module
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Failed Composite propType: %s%s', error.message, addendum) : undefined;
|
|
} else {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Failed Context Types: %s%s', error.message, addendum) : undefined;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
receiveComponent: function (nextElement, transaction, nextContext) {
|
|
var prevElement = this._currentElement;
|
|
var prevContext = this._context;
|
|
|
|
this._pendingElement = null;
|
|
|
|
this.updateComponent(transaction, prevElement, nextElement, prevContext, nextContext);
|
|
},
|
|
|
|
/**
|
|
* If any of `_pendingElement`, `_pendingStateQueue`, or `_pendingForceUpdate`
|
|
* is set, update the component.
|
|
*
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @internal
|
|
*/
|
|
performUpdateIfNecessary: function (transaction) {
|
|
if (this._pendingElement != null) {
|
|
ReactReconciler.receiveComponent(this, this._pendingElement || this._currentElement, transaction, this._context);
|
|
}
|
|
|
|
if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
|
|
this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Perform an update to a mounted component. The componentWillReceiveProps and
|
|
* shouldComponentUpdate methods are called, then (assuming the update isn't
|
|
* skipped) the remaining update lifecycle methods are called and the DOM
|
|
* representation is updated.
|
|
*
|
|
* By default, this implements React's rendering and reconciliation algorithm.
|
|
* Sophisticated clients may wish to override this.
|
|
*
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @param {ReactElement} prevParentElement
|
|
* @param {ReactElement} nextParentElement
|
|
* @internal
|
|
* @overridable
|
|
*/
|
|
updateComponent: function (transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext) {
|
|
var inst = this._instance;
|
|
|
|
var nextContext = this._context === nextUnmaskedContext ? inst.context : this._processContext(nextUnmaskedContext);
|
|
var nextProps;
|
|
|
|
// Distinguish between a props update versus a simple state update
|
|
if (prevParentElement === nextParentElement) {
|
|
// Skip checking prop types again -- we don't read inst.props to avoid
|
|
// warning for DOM component props in this upgrade
|
|
nextProps = nextParentElement.props;
|
|
} else {
|
|
nextProps = this._processProps(nextParentElement.props);
|
|
// An update here will schedule an update but immediately set
|
|
// _pendingStateQueue which will ensure that any state updates gets
|
|
// immediately reconciled instead of waiting for the next batch.
|
|
|
|
if (inst.componentWillReceiveProps) {
|
|
inst.componentWillReceiveProps(nextProps, nextContext);
|
|
}
|
|
}
|
|
|
|
var nextState = this._processPendingState(nextProps, nextContext);
|
|
|
|
var shouldUpdate = this._pendingForceUpdate || !inst.shouldComponentUpdate || inst.shouldComponentUpdate(nextProps, nextState, nextContext);
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(typeof shouldUpdate !== 'undefined', '%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', this.getName() || 'ReactCompositeComponent') : undefined;
|
|
}
|
|
|
|
if (shouldUpdate) {
|
|
this._pendingForceUpdate = false;
|
|
// Will set `this.props`, `this.state` and `this.context`.
|
|
this._performComponentUpdate(nextParentElement, nextProps, nextState, nextContext, transaction, nextUnmaskedContext);
|
|
} else {
|
|
// If it's determined that a component should not update, we still want
|
|
// to set props and state but we shortcut the rest of the update.
|
|
this._currentElement = nextParentElement;
|
|
this._context = nextUnmaskedContext;
|
|
inst.props = nextProps;
|
|
inst.state = nextState;
|
|
inst.context = nextContext;
|
|
}
|
|
},
|
|
|
|
_processPendingState: function (props, context) {
|
|
var inst = this._instance;
|
|
var queue = this._pendingStateQueue;
|
|
var replace = this._pendingReplaceState;
|
|
this._pendingReplaceState = false;
|
|
this._pendingStateQueue = null;
|
|
|
|
if (!queue) {
|
|
return inst.state;
|
|
}
|
|
|
|
if (replace && queue.length === 1) {
|
|
return queue[0];
|
|
}
|
|
|
|
var nextState = assign({}, replace ? queue[0] : inst.state);
|
|
for (var i = replace ? 1 : 0; i < queue.length; i++) {
|
|
var partial = queue[i];
|
|
assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);
|
|
}
|
|
|
|
return nextState;
|
|
},
|
|
|
|
/**
|
|
* Merges new props and state, notifies delegate methods of update and
|
|
* performs update.
|
|
*
|
|
* @param {ReactElement} nextElement Next element
|
|
* @param {object} nextProps Next public object to set as properties.
|
|
* @param {?object} nextState Next object to set as state.
|
|
* @param {?object} nextContext Next public object to set as context.
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @param {?object} unmaskedContext
|
|
* @private
|
|
*/
|
|
_performComponentUpdate: function (nextElement, nextProps, nextState, nextContext, transaction, unmaskedContext) {
|
|
var inst = this._instance;
|
|
|
|
var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
|
|
var prevProps;
|
|
var prevState;
|
|
var prevContext;
|
|
if (hasComponentDidUpdate) {
|
|
prevProps = inst.props;
|
|
prevState = inst.state;
|
|
prevContext = inst.context;
|
|
}
|
|
|
|
if (inst.componentWillUpdate) {
|
|
inst.componentWillUpdate(nextProps, nextState, nextContext);
|
|
}
|
|
|
|
this._currentElement = nextElement;
|
|
this._context = unmaskedContext;
|
|
inst.props = nextProps;
|
|
inst.state = nextState;
|
|
inst.context = nextContext;
|
|
|
|
this._updateRenderedComponent(transaction, unmaskedContext);
|
|
|
|
if (hasComponentDidUpdate) {
|
|
transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), inst);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Call the component's `render` method and update the DOM accordingly.
|
|
*
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @internal
|
|
*/
|
|
_updateRenderedComponent: function (transaction, context) {
|
|
var prevComponentInstance = this._renderedComponent;
|
|
var prevRenderedElement = prevComponentInstance._currentElement;
|
|
var nextRenderedElement = this._renderValidatedComponent();
|
|
if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
|
|
ReactReconciler.receiveComponent(prevComponentInstance, nextRenderedElement, transaction, this._processChildContext(context));
|
|
} else {
|
|
// These two IDs are actually the same! But nothing should rely on that.
|
|
var thisID = this._rootNodeID;
|
|
var prevComponentID = prevComponentInstance._rootNodeID;
|
|
ReactReconciler.unmountComponent(prevComponentInstance);
|
|
|
|
this._renderedComponent = this._instantiateReactComponent(nextRenderedElement);
|
|
var nextMarkup = ReactReconciler.mountComponent(this._renderedComponent, thisID, transaction, this._processChildContext(context));
|
|
this._replaceNodeWithMarkupByID(prevComponentID, nextMarkup);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @protected
|
|
*/
|
|
_replaceNodeWithMarkupByID: function (prevComponentID, nextMarkup) {
|
|
ReactComponentEnvironment.replaceNodeWithMarkupByID(prevComponentID, nextMarkup);
|
|
},
|
|
|
|
/**
|
|
* @protected
|
|
*/
|
|
_renderValidatedComponentWithoutOwnerOrContext: function () {
|
|
var inst = this._instance;
|
|
var renderedComponent = inst.render();
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// We allow auto-mocks to proceed as if they're returning null.
|
|
if (typeof renderedComponent === 'undefined' && inst.render._isMockFunction) {
|
|
// This is probably bad practice. Consider warning here and
|
|
// deprecating this convenience.
|
|
renderedComponent = null;
|
|
}
|
|
}
|
|
|
|
return renderedComponent;
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
_renderValidatedComponent: function () {
|
|
var renderedComponent;
|
|
ReactCurrentOwner.current = this;
|
|
try {
|
|
renderedComponent = this._renderValidatedComponentWithoutOwnerOrContext();
|
|
} finally {
|
|
ReactCurrentOwner.current = null;
|
|
}
|
|
!(
|
|
// TODO: An `isValidNode` function would probably be more appropriate
|
|
renderedComponent === null || renderedComponent === false || ReactElement.isValidElement(renderedComponent)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.render(): A valid ReactComponent must be returned. You may have ' + 'returned undefined, an array or some other invalid object.', this.getName() || 'ReactCompositeComponent') : invariant(false) : undefined;
|
|
return renderedComponent;
|
|
},
|
|
|
|
/**
|
|
* Lazily allocates the refs object and stores `component` as `ref`.
|
|
*
|
|
* @param {string} ref Reference name.
|
|
* @param {component} component Component to store as `ref`.
|
|
* @final
|
|
* @private
|
|
*/
|
|
attachRef: function (ref, component) {
|
|
var inst = this.getPublicInstance();
|
|
!(inst != null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Stateless function components cannot have refs.') : invariant(false) : undefined;
|
|
var publicComponentInstance = component.getPublicInstance();
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var componentName = component && component.getName ? component.getName() : 'a component';
|
|
process.env.NODE_ENV !== 'production' ? warning(publicComponentInstance != null, 'Stateless function components cannot be given refs ' + '(See ref "%s" in %s created by %s). ' + 'Attempts to access this ref will fail.', ref, componentName, this.getName()) : undefined;
|
|
}
|
|
var refs = inst.refs === emptyObject ? inst.refs = {} : inst.refs;
|
|
refs[ref] = publicComponentInstance;
|
|
},
|
|
|
|
/**
|
|
* Detaches a reference name.
|
|
*
|
|
* @param {string} ref Name to dereference.
|
|
* @final
|
|
* @private
|
|
*/
|
|
detachRef: function (ref) {
|
|
var refs = this.getPublicInstance().refs;
|
|
delete refs[ref];
|
|
},
|
|
|
|
/**
|
|
* Get a text description of the component that can be used to identify it
|
|
* in error messages.
|
|
* @return {string} The name or null.
|
|
* @internal
|
|
*/
|
|
getName: function () {
|
|
var type = this._currentElement.type;
|
|
var constructor = this._instance && this._instance.constructor;
|
|
return type.displayName || constructor && constructor.displayName || type.name || constructor && constructor.name || null;
|
|
},
|
|
|
|
/**
|
|
* Get the publicly accessible representation of this component - i.e. what
|
|
* is exposed by refs and returned by render. Can be null for stateless
|
|
* components.
|
|
*
|
|
* @return {ReactComponent} the public component instance.
|
|
* @internal
|
|
*/
|
|
getPublicInstance: function () {
|
|
var inst = this._instance;
|
|
if (inst instanceof StatelessComponent) {
|
|
return null;
|
|
}
|
|
return inst;
|
|
},
|
|
|
|
// Stub
|
|
_instantiateReactComponent: null
|
|
|
|
};
|
|
|
|
ReactPerf.measureMethods(ReactCompositeComponentMixin, 'ReactCompositeComponent', {
|
|
mountComponent: 'mountComponent',
|
|
updateComponent: 'updateComponent',
|
|
_renderValidatedComponent: '_renderValidatedComponent'
|
|
});
|
|
|
|
var ReactCompositeComponent = {
|
|
|
|
Mixin: ReactCompositeComponentMixin
|
|
|
|
};
|
|
|
|
module.exports = ReactCompositeComponent;
|
|
}).call(this,require('_process'))
|
|
},{"./Object.assign":157,"./ReactComponentEnvironment":167,"./ReactCurrentOwner":169,"./ReactElement":187,"./ReactInstanceMap":197,"./ReactPerf":206,"./ReactPropTypeLocationNames":207,"./ReactPropTypeLocations":208,"./ReactReconciler":211,"./ReactUpdateQueue":217,"./shouldUpdateReactComponent":261,"_process":133,"fbjs/lib/emptyObject":65,"fbjs/lib/invariant":72,"fbjs/lib/warning":83}],169:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactCurrentOwner
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Keeps track of the current owner.
|
|
*
|
|
* The current owner is the component who should own any components that are
|
|
* currently being constructed.
|
|
*/
|
|
var ReactCurrentOwner = {
|
|
|
|
/**
|
|
* @internal
|
|
* @type {ReactComponent}
|
|
*/
|
|
current: null
|
|
|
|
};
|
|
|
|
module.exports = ReactCurrentOwner;
|
|
},{}],170:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOM
|
|
*/
|
|
|
|
/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__*/
|
|
|
|
'use strict';
|
|
|
|
var ReactCurrentOwner = require('./ReactCurrentOwner');
|
|
var ReactDOMTextComponent = require('./ReactDOMTextComponent');
|
|
var ReactDefaultInjection = require('./ReactDefaultInjection');
|
|
var ReactInstanceHandles = require('./ReactInstanceHandles');
|
|
var ReactMount = require('./ReactMount');
|
|
var ReactPerf = require('./ReactPerf');
|
|
var ReactReconciler = require('./ReactReconciler');
|
|
var ReactUpdates = require('./ReactUpdates');
|
|
var ReactVersion = require('./ReactVersion');
|
|
|
|
var findDOMNode = require('./findDOMNode');
|
|
var renderSubtreeIntoContainer = require('./renderSubtreeIntoContainer');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
ReactDefaultInjection.inject();
|
|
|
|
var render = ReactPerf.measure('React', 'render', ReactMount.render);
|
|
|
|
var React = {
|
|
findDOMNode: findDOMNode,
|
|
render: render,
|
|
unmountComponentAtNode: ReactMount.unmountComponentAtNode,
|
|
version: ReactVersion,
|
|
|
|
/* eslint-disable camelcase */
|
|
unstable_batchedUpdates: ReactUpdates.batchedUpdates,
|
|
unstable_renderSubtreeIntoContainer: renderSubtreeIntoContainer
|
|
};
|
|
|
|
// Inject the runtime into a devtools global hook regardless of browser.
|
|
// Allows for debugging when the hook is injected on the page.
|
|
/* eslint-enable camelcase */
|
|
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') {
|
|
__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
|
|
CurrentOwner: ReactCurrentOwner,
|
|
InstanceHandles: ReactInstanceHandles,
|
|
Mount: ReactMount,
|
|
Reconciler: ReactReconciler,
|
|
TextComponent: ReactDOMTextComponent
|
|
});
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
if (ExecutionEnvironment.canUseDOM && window.top === window.self) {
|
|
|
|
// First check if devtools is not installed
|
|
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
|
|
// If we're in Chrome or Firefox, provide a download link if not installed.
|
|
if (navigator.userAgent.indexOf('Chrome') > -1 && navigator.userAgent.indexOf('Edge') === -1 || navigator.userAgent.indexOf('Firefox') > -1) {
|
|
console.debug('Download the React DevTools for a better development experience: ' + 'https://fb.me/react-devtools');
|
|
}
|
|
}
|
|
|
|
// If we're in IE8, check to see if we are in compatibility mode and provide
|
|
// information on preventing compatibility mode
|
|
var ieCompatibilityMode = document.documentMode && document.documentMode < 8;
|
|
|
|
process.env.NODE_ENV !== 'production' ? warning(!ieCompatibilityMode, 'Internet Explorer is running in compatibility mode; please add the ' + 'following tag to your HTML to prevent this from happening: ' + '<meta http-equiv="X-UA-Compatible" content="IE=edge" />') : undefined;
|
|
|
|
var expectedFeatures = [
|
|
// shims
|
|
Array.isArray, Array.prototype.every, Array.prototype.forEach, Array.prototype.indexOf, Array.prototype.map, Date.now, Function.prototype.bind, Object.keys, String.prototype.split, String.prototype.trim,
|
|
|
|
// shams
|
|
Object.create, Object.freeze];
|
|
|
|
for (var i = 0; i < expectedFeatures.length; i++) {
|
|
if (!expectedFeatures[i]) {
|
|
console.error('One or more ES5 shim/shams expected by React are not available: ' + 'https://fb.me/react-warning-polyfills');
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = React;
|
|
}).call(this,require('_process'))
|
|
},{"./ReactCurrentOwner":169,"./ReactDOMTextComponent":181,"./ReactDefaultInjection":184,"./ReactInstanceHandles":196,"./ReactMount":200,"./ReactPerf":206,"./ReactReconciler":211,"./ReactUpdates":218,"./ReactVersion":219,"./findDOMNode":243,"./renderSubtreeIntoContainer":258,"_process":133,"fbjs/lib/ExecutionEnvironment":58,"fbjs/lib/warning":83}],171:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOMButton
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var mouseListenerNames = {
|
|
onClick: true,
|
|
onDoubleClick: true,
|
|
onMouseDown: true,
|
|
onMouseMove: true,
|
|
onMouseUp: true,
|
|
|
|
onClickCapture: true,
|
|
onDoubleClickCapture: true,
|
|
onMouseDownCapture: true,
|
|
onMouseMoveCapture: true,
|
|
onMouseUpCapture: true
|
|
};
|
|
|
|
/**
|
|
* Implements a <button> native component that does not receive mouse events
|
|
* when `disabled` is set.
|
|
*/
|
|
var ReactDOMButton = {
|
|
getNativeProps: function (inst, props, context) {
|
|
if (!props.disabled) {
|
|
return props;
|
|
}
|
|
|
|
// Copy the props, except the mouse listeners
|
|
var nativeProps = {};
|
|
for (var key in props) {
|
|
if (props.hasOwnProperty(key) && !mouseListenerNames[key]) {
|
|
nativeProps[key] = props[key];
|
|
}
|
|
}
|
|
|
|
return nativeProps;
|
|
}
|
|
};
|
|
|
|
module.exports = ReactDOMButton;
|
|
},{}],172:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOMComponent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
/* global hasOwnProperty:true */
|
|
|
|
'use strict';
|
|
|
|
var AutoFocusUtils = require('./AutoFocusUtils');
|
|
var CSSPropertyOperations = require('./CSSPropertyOperations');
|
|
var DOMProperty = require('./DOMProperty');
|
|
var DOMPropertyOperations = require('./DOMPropertyOperations');
|
|
var EventConstants = require('./EventConstants');
|
|
var ReactBrowserEventEmitter = require('./ReactBrowserEventEmitter');
|
|
var ReactComponentBrowserEnvironment = require('./ReactComponentBrowserEnvironment');
|
|
var ReactDOMButton = require('./ReactDOMButton');
|
|
var ReactDOMInput = require('./ReactDOMInput');
|
|
var ReactDOMOption = require('./ReactDOMOption');
|
|
var ReactDOMSelect = require('./ReactDOMSelect');
|
|
var ReactDOMTextarea = require('./ReactDOMTextarea');
|
|
var ReactMount = require('./ReactMount');
|
|
var ReactMultiChild = require('./ReactMultiChild');
|
|
var ReactPerf = require('./ReactPerf');
|
|
var ReactUpdateQueue = require('./ReactUpdateQueue');
|
|
|
|
var assign = require('./Object.assign');
|
|
var canDefineProperty = require('./canDefineProperty');
|
|
var escapeTextContentForBrowser = require('./escapeTextContentForBrowser');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var isEventSupported = require('./isEventSupported');
|
|
var keyOf = require('fbjs/lib/keyOf');
|
|
var setInnerHTML = require('./setInnerHTML');
|
|
var setTextContent = require('./setTextContent');
|
|
var shallowEqual = require('fbjs/lib/shallowEqual');
|
|
var validateDOMNesting = require('./validateDOMNesting');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
var deleteListener = ReactBrowserEventEmitter.deleteListener;
|
|
var listenTo = ReactBrowserEventEmitter.listenTo;
|
|
var registrationNameModules = ReactBrowserEventEmitter.registrationNameModules;
|
|
|
|
// For quickly matching children type, to test if can be treated as content.
|
|
var CONTENT_TYPES = { 'string': true, 'number': true };
|
|
|
|
var CHILDREN = keyOf({ children: null });
|
|
var STYLE = keyOf({ style: null });
|
|
var HTML = keyOf({ __html: null });
|
|
|
|
var ELEMENT_NODE_TYPE = 1;
|
|
|
|
function getDeclarationErrorAddendum(internalInstance) {
|
|
if (internalInstance) {
|
|
var owner = internalInstance._currentElement._owner || null;
|
|
if (owner) {
|
|
var name = owner.getName();
|
|
if (name) {
|
|
return ' This DOM node was rendered by `' + name + '`.';
|
|
}
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
var legacyPropsDescriptor;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
legacyPropsDescriptor = {
|
|
props: {
|
|
enumerable: false,
|
|
get: function () {
|
|
var component = this._reactInternalComponent;
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'ReactDOMComponent: Do not access .props of a DOM node; instead, ' + 'recreate the props as `render` did originally or read the DOM ' + 'properties/attributes directly from this node (e.g., ' + 'this.refs.box.className).%s', getDeclarationErrorAddendum(component)) : undefined;
|
|
return component._currentElement.props;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function legacyGetDOMNode() {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var component = this._reactInternalComponent;
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'ReactDOMComponent: Do not access .getDOMNode() of a DOM node; ' + 'instead, use the node directly.%s', getDeclarationErrorAddendum(component)) : undefined;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
function legacyIsMounted() {
|
|
var component = this._reactInternalComponent;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'ReactDOMComponent: Do not access .isMounted() of a DOM node.%s', getDeclarationErrorAddendum(component)) : undefined;
|
|
}
|
|
return !!component;
|
|
}
|
|
|
|
function legacySetStateEtc() {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var component = this._reactInternalComponent;
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'ReactDOMComponent: Do not access .setState(), .replaceState(), or ' + '.forceUpdate() of a DOM node. This is a no-op.%s', getDeclarationErrorAddendum(component)) : undefined;
|
|
}
|
|
}
|
|
|
|
function legacySetProps(partialProps, callback) {
|
|
var component = this._reactInternalComponent;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'ReactDOMComponent: Do not access .setProps() of a DOM node. ' + 'Instead, call ReactDOM.render again at the top level.%s', getDeclarationErrorAddendum(component)) : undefined;
|
|
}
|
|
if (!component) {
|
|
return;
|
|
}
|
|
ReactUpdateQueue.enqueueSetPropsInternal(component, partialProps);
|
|
if (callback) {
|
|
ReactUpdateQueue.enqueueCallbackInternal(component, callback);
|
|
}
|
|
}
|
|
|
|
function legacyReplaceProps(partialProps, callback) {
|
|
var component = this._reactInternalComponent;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'ReactDOMComponent: Do not access .replaceProps() of a DOM node. ' + 'Instead, call ReactDOM.render again at the top level.%s', getDeclarationErrorAddendum(component)) : undefined;
|
|
}
|
|
if (!component) {
|
|
return;
|
|
}
|
|
ReactUpdateQueue.enqueueReplacePropsInternal(component, partialProps);
|
|
if (callback) {
|
|
ReactUpdateQueue.enqueueCallbackInternal(component, callback);
|
|
}
|
|
}
|
|
|
|
function friendlyStringify(obj) {
|
|
if (typeof obj === 'object') {
|
|
if (Array.isArray(obj)) {
|
|
return '[' + obj.map(friendlyStringify).join(', ') + ']';
|
|
} else {
|
|
var pairs = [];
|
|
for (var key in obj) {
|
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
var keyEscaped = /^[a-z$_][\w$_]*$/i.test(key) ? key : JSON.stringify(key);
|
|
pairs.push(keyEscaped + ': ' + friendlyStringify(obj[key]));
|
|
}
|
|
}
|
|
return '{' + pairs.join(', ') + '}';
|
|
}
|
|
} else if (typeof obj === 'string') {
|
|
return JSON.stringify(obj);
|
|
} else if (typeof obj === 'function') {
|
|
return '[function object]';
|
|
}
|
|
// Differs from JSON.stringify in that undefined becauses undefined and that
|
|
// inf and nan don't become null
|
|
return String(obj);
|
|
}
|
|
|
|
var styleMutationWarning = {};
|
|
|
|
function checkAndWarnForMutatedStyle(style1, style2, component) {
|
|
if (style1 == null || style2 == null) {
|
|
return;
|
|
}
|
|
if (shallowEqual(style1, style2)) {
|
|
return;
|
|
}
|
|
|
|
var componentName = component._tag;
|
|
var owner = component._currentElement._owner;
|
|
var ownerName;
|
|
if (owner) {
|
|
ownerName = owner.getName();
|
|
}
|
|
|
|
var hash = ownerName + '|' + componentName;
|
|
|
|
if (styleMutationWarning.hasOwnProperty(hash)) {
|
|
return;
|
|
}
|
|
|
|
styleMutationWarning[hash] = true;
|
|
|
|
process.env.NODE_ENV !== 'production' ? warning(false, '`%s` was passed a style object that has previously been mutated. ' + 'Mutating `style` is deprecated. Consider cloning it beforehand. Check ' + 'the `render` %s. Previous style: %s. Mutated style: %s.', componentName, owner ? 'of `' + ownerName + '`' : 'using <' + componentName + '>', friendlyStringify(style1), friendlyStringify(style2)) : undefined;
|
|
}
|
|
|
|
/**
|
|
* @param {object} component
|
|
* @param {?object} props
|
|
*/
|
|
function assertValidProps(component, props) {
|
|
if (!props) {
|
|
return;
|
|
}
|
|
// Note the use of `==` which checks for null or undefined.
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
if (voidElementTags[component._tag]) {
|
|
process.env.NODE_ENV !== 'production' ? warning(props.children == null && props.dangerouslySetInnerHTML == null, '%s is a void element tag and must not have `children` or ' + 'use `props.dangerouslySetInnerHTML`.%s', component._tag, component._currentElement._owner ? ' Check the render method of ' + component._currentElement._owner.getName() + '.' : '') : undefined;
|
|
}
|
|
}
|
|
if (props.dangerouslySetInnerHTML != null) {
|
|
!(props.children == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.') : invariant(false) : undefined;
|
|
!(typeof props.dangerouslySetInnerHTML === 'object' && HTML in props.dangerouslySetInnerHTML) ? process.env.NODE_ENV !== 'production' ? invariant(false, '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' + 'Please visit https://fb.me/react-invariant-dangerously-set-inner-html ' + 'for more information.') : invariant(false) : undefined;
|
|
}
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(props.innerHTML == null, 'Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.') : undefined;
|
|
process.env.NODE_ENV !== 'production' ? warning(!props.contentEditable || props.children == null, 'A component is `contentEditable` and contains `children` managed by ' + 'React. It is now your responsibility to guarantee that none of ' + 'those nodes are unexpectedly modified or duplicated. This is ' + 'probably not intentional.') : undefined;
|
|
}
|
|
!(props.style == null || typeof props.style === 'object') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'The `style` prop expects a mapping from style properties to values, ' + 'not a string. For example, style={{marginRight: spacing + \'em\'}} when ' + 'using JSX.%s', getDeclarationErrorAddendum(component)) : invariant(false) : undefined;
|
|
}
|
|
|
|
function enqueuePutListener(id, registrationName, listener, transaction) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// IE8 has no API for event capturing and the `onScroll` event doesn't
|
|
// bubble.
|
|
process.env.NODE_ENV !== 'production' ? warning(registrationName !== 'onScroll' || isEventSupported('scroll', true), 'This browser doesn\'t support the `onScroll` event') : undefined;
|
|
}
|
|
var container = ReactMount.findReactContainerForID(id);
|
|
if (container) {
|
|
var doc = container.nodeType === ELEMENT_NODE_TYPE ? container.ownerDocument : container;
|
|
listenTo(registrationName, doc);
|
|
}
|
|
transaction.getReactMountReady().enqueue(putListener, {
|
|
id: id,
|
|
registrationName: registrationName,
|
|
listener: listener
|
|
});
|
|
}
|
|
|
|
function putListener() {
|
|
var listenerToPut = this;
|
|
ReactBrowserEventEmitter.putListener(listenerToPut.id, listenerToPut.registrationName, listenerToPut.listener);
|
|
}
|
|
|
|
// There are so many media events, it makes sense to just
|
|
// maintain a list rather than create a `trapBubbledEvent` for each
|
|
var mediaEvents = {
|
|
topAbort: 'abort',
|
|
topCanPlay: 'canplay',
|
|
topCanPlayThrough: 'canplaythrough',
|
|
topDurationChange: 'durationchange',
|
|
topEmptied: 'emptied',
|
|
topEncrypted: 'encrypted',
|
|
topEnded: 'ended',
|
|
topError: 'error',
|
|
topLoadedData: 'loadeddata',
|
|
topLoadedMetadata: 'loadedmetadata',
|
|
topLoadStart: 'loadstart',
|
|
topPause: 'pause',
|
|
topPlay: 'play',
|
|
topPlaying: 'playing',
|
|
topProgress: 'progress',
|
|
topRateChange: 'ratechange',
|
|
topSeeked: 'seeked',
|
|
topSeeking: 'seeking',
|
|
topStalled: 'stalled',
|
|
topSuspend: 'suspend',
|
|
topTimeUpdate: 'timeupdate',
|
|
topVolumeChange: 'volumechange',
|
|
topWaiting: 'waiting'
|
|
};
|
|
|
|
function trapBubbledEventsLocal() {
|
|
var inst = this;
|
|
// If a component renders to null or if another component fatals and causes
|
|
// the state of the tree to be corrupted, `node` here can be null.
|
|
!inst._rootNodeID ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Must be mounted to trap events') : invariant(false) : undefined;
|
|
var node = ReactMount.getNode(inst._rootNodeID);
|
|
!node ? process.env.NODE_ENV !== 'production' ? invariant(false, 'trapBubbledEvent(...): Requires node to be rendered.') : invariant(false) : undefined;
|
|
|
|
switch (inst._tag) {
|
|
case 'iframe':
|
|
inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes.topLoad, 'load', node)];
|
|
break;
|
|
case 'video':
|
|
case 'audio':
|
|
|
|
inst._wrapperState.listeners = [];
|
|
// create listener for each media event
|
|
for (var event in mediaEvents) {
|
|
if (mediaEvents.hasOwnProperty(event)) {
|
|
inst._wrapperState.listeners.push(ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes[event], mediaEvents[event], node));
|
|
}
|
|
}
|
|
|
|
break;
|
|
case 'img':
|
|
inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes.topError, 'error', node), ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes.topLoad, 'load', node)];
|
|
break;
|
|
case 'form':
|
|
inst._wrapperState.listeners = [ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes.topReset, 'reset', node), ReactBrowserEventEmitter.trapBubbledEvent(EventConstants.topLevelTypes.topSubmit, 'submit', node)];
|
|
break;
|
|
}
|
|
}
|
|
|
|
function mountReadyInputWrapper() {
|
|
ReactDOMInput.mountReadyWrapper(this);
|
|
}
|
|
|
|
function postUpdateSelectWrapper() {
|
|
ReactDOMSelect.postUpdateWrapper(this);
|
|
}
|
|
|
|
// For HTML, certain tags should omit their close tag. We keep a whitelist for
|
|
// those special cased tags.
|
|
|
|
var omittedCloseTags = {
|
|
'area': true,
|
|
'base': true,
|
|
'br': true,
|
|
'col': true,
|
|
'embed': true,
|
|
'hr': true,
|
|
'img': true,
|
|
'input': true,
|
|
'keygen': true,
|
|
'link': true,
|
|
'meta': true,
|
|
'param': true,
|
|
'source': true,
|
|
'track': true,
|
|
'wbr': true
|
|
};
|
|
|
|
// NOTE: menuitem's close tag should be omitted, but that causes problems.
|
|
var newlineEatingTags = {
|
|
'listing': true,
|
|
'pre': true,
|
|
'textarea': true
|
|
};
|
|
|
|
// For HTML, certain tags cannot have children. This has the same purpose as
|
|
// `omittedCloseTags` except that `menuitem` should still have its closing tag.
|
|
|
|
var voidElementTags = assign({
|
|
'menuitem': true
|
|
}, omittedCloseTags);
|
|
|
|
// We accept any tag to be rendered but since this gets injected into arbitrary
|
|
// HTML, we want to make sure that it's a safe tag.
|
|
// http://www.w3.org/TR/REC-xml/#NT-Name
|
|
|
|
var VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/; // Simplified subset
|
|
var validatedTagCache = {};
|
|
var hasOwnProperty = ({}).hasOwnProperty;
|
|
|
|
function validateDangerousTag(tag) {
|
|
if (!hasOwnProperty.call(validatedTagCache, tag)) {
|
|
!VALID_TAG_REGEX.test(tag) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Invalid tag: %s', tag) : invariant(false) : undefined;
|
|
validatedTagCache[tag] = true;
|
|
}
|
|
}
|
|
|
|
function processChildContextDev(context, inst) {
|
|
// Pass down our tag name to child components for validation purposes
|
|
context = assign({}, context);
|
|
var info = context[validateDOMNesting.ancestorInfoContextKey];
|
|
context[validateDOMNesting.ancestorInfoContextKey] = validateDOMNesting.updatedAncestorInfo(info, inst._tag, inst);
|
|
return context;
|
|
}
|
|
|
|
function isCustomComponent(tagName, props) {
|
|
return tagName.indexOf('-') >= 0 || props.is != null;
|
|
}
|
|
|
|
/**
|
|
* Creates a new React class that is idempotent and capable of containing other
|
|
* React components. It accepts event listeners and DOM properties that are
|
|
* valid according to `DOMProperty`.
|
|
*
|
|
* - Event listeners: `onClick`, `onMouseDown`, etc.
|
|
* - DOM properties: `className`, `name`, `title`, etc.
|
|
*
|
|
* The `style` property functions differently from the DOM API. It accepts an
|
|
* object mapping of style properties to values.
|
|
*
|
|
* @constructor ReactDOMComponent
|
|
* @extends ReactMultiChild
|
|
*/
|
|
function ReactDOMComponent(tag) {
|
|
validateDangerousTag(tag);
|
|
this._tag = tag.toLowerCase();
|
|
this._renderedChildren = null;
|
|
this._previousStyle = null;
|
|
this._previousStyleCopy = null;
|
|
this._rootNodeID = null;
|
|
this._wrapperState = null;
|
|
this._topLevelWrapper = null;
|
|
this._nodeWithLegacyProperties = null;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
this._unprocessedContextDev = null;
|
|
this._processedContextDev = null;
|
|
}
|
|
}
|
|
|
|
ReactDOMComponent.displayName = 'ReactDOMComponent';
|
|
|
|
ReactDOMComponent.Mixin = {
|
|
|
|
construct: function (element) {
|
|
this._currentElement = element;
|
|
},
|
|
|
|
/**
|
|
* Generates root tag markup then recurses. This method has side effects and
|
|
* is not idempotent.
|
|
*
|
|
* @internal
|
|
* @param {string} rootID The root DOM ID for this node.
|
|
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
|
* @param {object} context
|
|
* @return {string} The computed markup.
|
|
*/
|
|
mountComponent: function (rootID, transaction, context) {
|
|
this._rootNodeID = rootID;
|
|
|
|
var props = this._currentElement.props;
|
|
|
|
switch (this._tag) {
|
|
case 'iframe':
|
|
case 'img':
|
|
case 'form':
|
|
case 'video':
|
|
case 'audio':
|
|
this._wrapperState = {
|
|
listeners: null
|
|
};
|
|
transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
|
|
break;
|
|
case 'button':
|
|
props = ReactDOMButton.getNativeProps(this, props, context);
|
|
break;
|
|
case 'input':
|
|
ReactDOMInput.mountWrapper(this, props, context);
|
|
props = ReactDOMInput.getNativeProps(this, props, context);
|
|
break;
|
|
case 'option':
|
|
ReactDOMOption.mountWrapper(this, props, context);
|
|
props = ReactDOMOption.getNativeProps(this, props, context);
|
|
break;
|
|
case 'select':
|
|
ReactDOMSelect.mountWrapper(this, props, context);
|
|
props = ReactDOMSelect.getNativeProps(this, props, context);
|
|
context = ReactDOMSelect.processChildContext(this, props, context);
|
|
break;
|
|
case 'textarea':
|
|
ReactDOMTextarea.mountWrapper(this, props, context);
|
|
props = ReactDOMTextarea.getNativeProps(this, props, context);
|
|
break;
|
|
}
|
|
|
|
assertValidProps(this, props);
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
if (context[validateDOMNesting.ancestorInfoContextKey]) {
|
|
validateDOMNesting(this._tag, this, context[validateDOMNesting.ancestorInfoContextKey]);
|
|
}
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
this._unprocessedContextDev = context;
|
|
this._processedContextDev = processChildContextDev(context, this);
|
|
context = this._processedContextDev;
|
|
}
|
|
|
|
var mountImage;
|
|
if (transaction.useCreateElement) {
|
|
var ownerDocument = context[ReactMount.ownerDocumentContextKey];
|
|
var el = ownerDocument.createElement(this._currentElement.type);
|
|
DOMPropertyOperations.setAttributeForID(el, this._rootNodeID);
|
|
// Populate node cache
|
|
ReactMount.getID(el);
|
|
this._updateDOMProperties({}, props, transaction, el);
|
|
this._createInitialChildren(transaction, props, context, el);
|
|
mountImage = el;
|
|
} else {
|
|
var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction, props);
|
|
var tagContent = this._createContentMarkup(transaction, props, context);
|
|
if (!tagContent && omittedCloseTags[this._tag]) {
|
|
mountImage = tagOpen + '/>';
|
|
} else {
|
|
mountImage = tagOpen + '>' + tagContent + '</' + this._currentElement.type + '>';
|
|
}
|
|
}
|
|
|
|
switch (this._tag) {
|
|
case 'input':
|
|
transaction.getReactMountReady().enqueue(mountReadyInputWrapper, this);
|
|
// falls through
|
|
case 'button':
|
|
case 'select':
|
|
case 'textarea':
|
|
if (props.autoFocus) {
|
|
transaction.getReactMountReady().enqueue(AutoFocusUtils.focusDOMComponent, this);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return mountImage;
|
|
},
|
|
|
|
/**
|
|
* Creates markup for the open tag and all attributes.
|
|
*
|
|
* This method has side effects because events get registered.
|
|
*
|
|
* Iterating over object properties is faster than iterating over arrays.
|
|
* @see http://jsperf.com/obj-vs-arr-iteration
|
|
*
|
|
* @private
|
|
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
|
* @param {object} props
|
|
* @return {string} Markup of opening tag.
|
|
*/
|
|
_createOpenTagMarkupAndPutListeners: function (transaction, props) {
|
|
var ret = '<' + this._currentElement.type;
|
|
|
|
for (var propKey in props) {
|
|
if (!props.hasOwnProperty(propKey)) {
|
|
continue;
|
|
}
|
|
var propValue = props[propKey];
|
|
if (propValue == null) {
|
|
continue;
|
|
}
|
|
if (registrationNameModules.hasOwnProperty(propKey)) {
|
|
if (propValue) {
|
|
enqueuePutListener(this._rootNodeID, propKey, propValue, transaction);
|
|
}
|
|
} else {
|
|
if (propKey === STYLE) {
|
|
if (propValue) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// See `_updateDOMProperties`. style block
|
|
this._previousStyle = propValue;
|
|
}
|
|
propValue = this._previousStyleCopy = assign({}, props.style);
|
|
}
|
|
propValue = CSSPropertyOperations.createMarkupForStyles(propValue);
|
|
}
|
|
var markup = null;
|
|
if (this._tag != null && isCustomComponent(this._tag, props)) {
|
|
if (propKey !== CHILDREN) {
|
|
markup = DOMPropertyOperations.createMarkupForCustomAttribute(propKey, propValue);
|
|
}
|
|
} else {
|
|
markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue);
|
|
}
|
|
if (markup) {
|
|
ret += ' ' + markup;
|
|
}
|
|
}
|
|
}
|
|
|
|
// For static pages, no need to put React ID and checksum. Saves lots of
|
|
// bytes.
|
|
if (transaction.renderToStaticMarkup) {
|
|
return ret;
|
|
}
|
|
|
|
var markupForID = DOMPropertyOperations.createMarkupForID(this._rootNodeID);
|
|
return ret + ' ' + markupForID;
|
|
},
|
|
|
|
/**
|
|
* Creates markup for the content between the tags.
|
|
*
|
|
* @private
|
|
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
|
* @param {object} props
|
|
* @param {object} context
|
|
* @return {string} Content markup.
|
|
*/
|
|
_createContentMarkup: function (transaction, props, context) {
|
|
var ret = '';
|
|
|
|
// Intentional use of != to avoid catching zero/false.
|
|
var innerHTML = props.dangerouslySetInnerHTML;
|
|
if (innerHTML != null) {
|
|
if (innerHTML.__html != null) {
|
|
ret = innerHTML.__html;
|
|
}
|
|
} else {
|
|
var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null;
|
|
var childrenToUse = contentToUse != null ? null : props.children;
|
|
if (contentToUse != null) {
|
|
// TODO: Validate that text is allowed as a child of this node
|
|
ret = escapeTextContentForBrowser(contentToUse);
|
|
} else if (childrenToUse != null) {
|
|
var mountImages = this.mountChildren(childrenToUse, transaction, context);
|
|
ret = mountImages.join('');
|
|
}
|
|
}
|
|
if (newlineEatingTags[this._tag] && ret.charAt(0) === '\n') {
|
|
// text/html ignores the first character in these tags if it's a newline
|
|
// Prefer to break application/xml over text/html (for now) by adding
|
|
// a newline specifically to get eaten by the parser. (Alternately for
|
|
// textareas, replacing "^\n" with "\r\n" doesn't get eaten, and the first
|
|
// \r is normalized out by HTMLTextAreaElement#value.)
|
|
// See: <http://www.w3.org/TR/html-polyglot/#newlines-in-textarea-and-pre>
|
|
// See: <http://www.w3.org/TR/html5/syntax.html#element-restrictions>
|
|
// See: <http://www.w3.org/TR/html5/syntax.html#newlines>
|
|
// See: Parsing of "textarea" "listing" and "pre" elements
|
|
// from <http://www.w3.org/TR/html5/syntax.html#parsing-main-inbody>
|
|
return '\n' + ret;
|
|
} else {
|
|
return ret;
|
|
}
|
|
},
|
|
|
|
_createInitialChildren: function (transaction, props, context, el) {
|
|
// Intentional use of != to avoid catching zero/false.
|
|
var innerHTML = props.dangerouslySetInnerHTML;
|
|
if (innerHTML != null) {
|
|
if (innerHTML.__html != null) {
|
|
setInnerHTML(el, innerHTML.__html);
|
|
}
|
|
} else {
|
|
var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null;
|
|
var childrenToUse = contentToUse != null ? null : props.children;
|
|
if (contentToUse != null) {
|
|
// TODO: Validate that text is allowed as a child of this node
|
|
setTextContent(el, contentToUse);
|
|
} else if (childrenToUse != null) {
|
|
var mountImages = this.mountChildren(childrenToUse, transaction, context);
|
|
for (var i = 0; i < mountImages.length; i++) {
|
|
el.appendChild(mountImages[i]);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Receives a next element and updates the component.
|
|
*
|
|
* @internal
|
|
* @param {ReactElement} nextElement
|
|
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
|
* @param {object} context
|
|
*/
|
|
receiveComponent: function (nextElement, transaction, context) {
|
|
var prevElement = this._currentElement;
|
|
this._currentElement = nextElement;
|
|
this.updateComponent(transaction, prevElement, nextElement, context);
|
|
},
|
|
|
|
/**
|
|
* Updates a native DOM component after it has already been allocated and
|
|
* attached to the DOM. Reconciles the root DOM node, then recurses.
|
|
*
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @param {ReactElement} prevElement
|
|
* @param {ReactElement} nextElement
|
|
* @internal
|
|
* @overridable
|
|
*/
|
|
updateComponent: function (transaction, prevElement, nextElement, context) {
|
|
var lastProps = prevElement.props;
|
|
var nextProps = this._currentElement.props;
|
|
|
|
switch (this._tag) {
|
|
case 'button':
|
|
lastProps = ReactDOMButton.getNativeProps(this, lastProps);
|
|
nextProps = ReactDOMButton.getNativeProps(this, nextProps);
|
|
break;
|
|
case 'input':
|
|
ReactDOMInput.updateWrapper(this);
|
|
lastProps = ReactDOMInput.getNativeProps(this, lastProps);
|
|
nextProps = ReactDOMInput.getNativeProps(this, nextProps);
|
|
break;
|
|
case 'option':
|
|
lastProps = ReactDOMOption.getNativeProps(this, lastProps);
|
|
nextProps = ReactDOMOption.getNativeProps(this, nextProps);
|
|
break;
|
|
case 'select':
|
|
lastProps = ReactDOMSelect.getNativeProps(this, lastProps);
|
|
nextProps = ReactDOMSelect.getNativeProps(this, nextProps);
|
|
break;
|
|
case 'textarea':
|
|
ReactDOMTextarea.updateWrapper(this);
|
|
lastProps = ReactDOMTextarea.getNativeProps(this, lastProps);
|
|
nextProps = ReactDOMTextarea.getNativeProps(this, nextProps);
|
|
break;
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// If the context is reference-equal to the old one, pass down the same
|
|
// processed object so the update bailout in ReactReconciler behaves
|
|
// correctly (and identically in dev and prod). See #5005.
|
|
if (this._unprocessedContextDev !== context) {
|
|
this._unprocessedContextDev = context;
|
|
this._processedContextDev = processChildContextDev(context, this);
|
|
}
|
|
context = this._processedContextDev;
|
|
}
|
|
|
|
assertValidProps(this, nextProps);
|
|
this._updateDOMProperties(lastProps, nextProps, transaction, null);
|
|
this._updateDOMChildren(lastProps, nextProps, transaction, context);
|
|
|
|
if (!canDefineProperty && this._nodeWithLegacyProperties) {
|
|
this._nodeWithLegacyProperties.props = nextProps;
|
|
}
|
|
|
|
if (this._tag === 'select') {
|
|
// <select> value update needs to occur after <option> children
|
|
// reconciliation
|
|
transaction.getReactMountReady().enqueue(postUpdateSelectWrapper, this);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Reconciles the properties by detecting differences in property values and
|
|
* updating the DOM as necessary. This function is probably the single most
|
|
* critical path for performance optimization.
|
|
*
|
|
* TODO: Benchmark whether checking for changed values in memory actually
|
|
* improves performance (especially statically positioned elements).
|
|
* TODO: Benchmark the effects of putting this at the top since 99% of props
|
|
* do not change for a given reconciliation.
|
|
* TODO: Benchmark areas that can be improved with caching.
|
|
*
|
|
* @private
|
|
* @param {object} lastProps
|
|
* @param {object} nextProps
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @param {?DOMElement} node
|
|
*/
|
|
_updateDOMProperties: function (lastProps, nextProps, transaction, node) {
|
|
var propKey;
|
|
var styleName;
|
|
var styleUpdates;
|
|
for (propKey in lastProps) {
|
|
if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey)) {
|
|
continue;
|
|
}
|
|
if (propKey === STYLE) {
|
|
var lastStyle = this._previousStyleCopy;
|
|
for (styleName in lastStyle) {
|
|
if (lastStyle.hasOwnProperty(styleName)) {
|
|
styleUpdates = styleUpdates || {};
|
|
styleUpdates[styleName] = '';
|
|
}
|
|
}
|
|
this._previousStyleCopy = null;
|
|
} else if (registrationNameModules.hasOwnProperty(propKey)) {
|
|
if (lastProps[propKey]) {
|
|
// Only call deleteListener if there was a listener previously or
|
|
// else willDeleteListener gets called when there wasn't actually a
|
|
// listener (e.g., onClick={null})
|
|
deleteListener(this._rootNodeID, propKey);
|
|
}
|
|
} else if (DOMProperty.properties[propKey] || DOMProperty.isCustomAttribute(propKey)) {
|
|
if (!node) {
|
|
node = ReactMount.getNode(this._rootNodeID);
|
|
}
|
|
DOMPropertyOperations.deleteValueForProperty(node, propKey);
|
|
}
|
|
}
|
|
for (propKey in nextProps) {
|
|
var nextProp = nextProps[propKey];
|
|
var lastProp = propKey === STYLE ? this._previousStyleCopy : lastProps[propKey];
|
|
if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp) {
|
|
continue;
|
|
}
|
|
if (propKey === STYLE) {
|
|
if (nextProp) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
checkAndWarnForMutatedStyle(this._previousStyleCopy, this._previousStyle, this);
|
|
this._previousStyle = nextProp;
|
|
}
|
|
nextProp = this._previousStyleCopy = assign({}, nextProp);
|
|
} else {
|
|
this._previousStyleCopy = null;
|
|
}
|
|
if (lastProp) {
|
|
// Unset styles on `lastProp` but not on `nextProp`.
|
|
for (styleName in lastProp) {
|
|
if (lastProp.hasOwnProperty(styleName) && (!nextProp || !nextProp.hasOwnProperty(styleName))) {
|
|
styleUpdates = styleUpdates || {};
|
|
styleUpdates[styleName] = '';
|
|
}
|
|
}
|
|
// Update styles that changed since `lastProp`.
|
|
for (styleName in nextProp) {
|
|
if (nextProp.hasOwnProperty(styleName) && lastProp[styleName] !== nextProp[styleName]) {
|
|
styleUpdates = styleUpdates || {};
|
|
styleUpdates[styleName] = nextProp[styleName];
|
|
}
|
|
}
|
|
} else {
|
|
// Relies on `updateStylesByID` not mutating `styleUpdates`.
|
|
styleUpdates = nextProp;
|
|
}
|
|
} else if (registrationNameModules.hasOwnProperty(propKey)) {
|
|
if (nextProp) {
|
|
enqueuePutListener(this._rootNodeID, propKey, nextProp, transaction);
|
|
} else if (lastProp) {
|
|
deleteListener(this._rootNodeID, propKey);
|
|
}
|
|
} else if (isCustomComponent(this._tag, nextProps)) {
|
|
if (!node) {
|
|
node = ReactMount.getNode(this._rootNodeID);
|
|
}
|
|
if (propKey === CHILDREN) {
|
|
nextProp = null;
|
|
}
|
|
DOMPropertyOperations.setValueForAttribute(node, propKey, nextProp);
|
|
} else if (DOMProperty.properties[propKey] || DOMProperty.isCustomAttribute(propKey)) {
|
|
if (!node) {
|
|
node = ReactMount.getNode(this._rootNodeID);
|
|
}
|
|
// If we're updating to null or undefined, we should remove the property
|
|
// from the DOM node instead of inadvertantly setting to a string. This
|
|
// brings us in line with the same behavior we have on initial render.
|
|
if (nextProp != null) {
|
|
DOMPropertyOperations.setValueForProperty(node, propKey, nextProp);
|
|
} else {
|
|
DOMPropertyOperations.deleteValueForProperty(node, propKey);
|
|
}
|
|
}
|
|
}
|
|
if (styleUpdates) {
|
|
if (!node) {
|
|
node = ReactMount.getNode(this._rootNodeID);
|
|
}
|
|
CSSPropertyOperations.setValueForStyles(node, styleUpdates);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Reconciles the children with the various properties that affect the
|
|
* children content.
|
|
*
|
|
* @param {object} lastProps
|
|
* @param {object} nextProps
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @param {object} context
|
|
*/
|
|
_updateDOMChildren: function (lastProps, nextProps, transaction, context) {
|
|
var lastContent = CONTENT_TYPES[typeof lastProps.children] ? lastProps.children : null;
|
|
var nextContent = CONTENT_TYPES[typeof nextProps.children] ? nextProps.children : null;
|
|
|
|
var lastHtml = lastProps.dangerouslySetInnerHTML && lastProps.dangerouslySetInnerHTML.__html;
|
|
var nextHtml = nextProps.dangerouslySetInnerHTML && nextProps.dangerouslySetInnerHTML.__html;
|
|
|
|
// Note the use of `!=` which checks for null or undefined.
|
|
var lastChildren = lastContent != null ? null : lastProps.children;
|
|
var nextChildren = nextContent != null ? null : nextProps.children;
|
|
|
|
// If we're switching from children to content/html or vice versa, remove
|
|
// the old content
|
|
var lastHasContentOrHtml = lastContent != null || lastHtml != null;
|
|
var nextHasContentOrHtml = nextContent != null || nextHtml != null;
|
|
if (lastChildren != null && nextChildren == null) {
|
|
this.updateChildren(null, transaction, context);
|
|
} else if (lastHasContentOrHtml && !nextHasContentOrHtml) {
|
|
this.updateTextContent('');
|
|
}
|
|
|
|
if (nextContent != null) {
|
|
if (lastContent !== nextContent) {
|
|
this.updateTextContent('' + nextContent);
|
|
}
|
|
} else if (nextHtml != null) {
|
|
if (lastHtml !== nextHtml) {
|
|
this.updateMarkup('' + nextHtml);
|
|
}
|
|
} else if (nextChildren != null) {
|
|
this.updateChildren(nextChildren, transaction, context);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Destroys all event registrations for this instance. Does not remove from
|
|
* the DOM. That must be done by the parent.
|
|
*
|
|
* @internal
|
|
*/
|
|
unmountComponent: function () {
|
|
switch (this._tag) {
|
|
case 'iframe':
|
|
case 'img':
|
|
case 'form':
|
|
case 'video':
|
|
case 'audio':
|
|
var listeners = this._wrapperState.listeners;
|
|
if (listeners) {
|
|
for (var i = 0; i < listeners.length; i++) {
|
|
listeners[i].remove();
|
|
}
|
|
}
|
|
break;
|
|
case 'input':
|
|
ReactDOMInput.unmountWrapper(this);
|
|
break;
|
|
case 'html':
|
|
case 'head':
|
|
case 'body':
|
|
/**
|
|
* Components like <html> <head> and <body> can't be removed or added
|
|
* easily in a cross-browser way, however it's valuable to be able to
|
|
* take advantage of React's reconciliation for styling and <title>
|
|
* management. So we just document it and throw in dangerous cases.
|
|
*/
|
|
!false ? process.env.NODE_ENV !== 'production' ? invariant(false, '<%s> tried to unmount. Because of cross-browser quirks it is ' + 'impossible to unmount some top-level components (eg <html>, ' + '<head>, and <body>) reliably and efficiently. To fix this, have a ' + 'single top-level component that never unmounts render these ' + 'elements.', this._tag) : invariant(false) : undefined;
|
|
break;
|
|
}
|
|
|
|
this.unmountChildren();
|
|
ReactBrowserEventEmitter.deleteAllListeners(this._rootNodeID);
|
|
ReactComponentBrowserEnvironment.unmountIDFromEnvironment(this._rootNodeID);
|
|
this._rootNodeID = null;
|
|
this._wrapperState = null;
|
|
if (this._nodeWithLegacyProperties) {
|
|
var node = this._nodeWithLegacyProperties;
|
|
node._reactInternalComponent = null;
|
|
this._nodeWithLegacyProperties = null;
|
|
}
|
|
},
|
|
|
|
getPublicInstance: function () {
|
|
if (!this._nodeWithLegacyProperties) {
|
|
var node = ReactMount.getNode(this._rootNodeID);
|
|
|
|
node._reactInternalComponent = this;
|
|
node.getDOMNode = legacyGetDOMNode;
|
|
node.isMounted = legacyIsMounted;
|
|
node.setState = legacySetStateEtc;
|
|
node.replaceState = legacySetStateEtc;
|
|
node.forceUpdate = legacySetStateEtc;
|
|
node.setProps = legacySetProps;
|
|
node.replaceProps = legacyReplaceProps;
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
if (canDefineProperty) {
|
|
Object.defineProperties(node, legacyPropsDescriptor);
|
|
} else {
|
|
// updateComponent will update this property on subsequent renders
|
|
node.props = this._currentElement.props;
|
|
}
|
|
} else {
|
|
// updateComponent will update this property on subsequent renders
|
|
node.props = this._currentElement.props;
|
|
}
|
|
|
|
this._nodeWithLegacyProperties = node;
|
|
}
|
|
return this._nodeWithLegacyProperties;
|
|
}
|
|
|
|
};
|
|
|
|
ReactPerf.measureMethods(ReactDOMComponent, 'ReactDOMComponent', {
|
|
mountComponent: 'mountComponent',
|
|
updateComponent: 'updateComponent'
|
|
});
|
|
|
|
assign(ReactDOMComponent.prototype, ReactDOMComponent.Mixin, ReactMultiChild.Mixin);
|
|
|
|
module.exports = ReactDOMComponent;
|
|
}).call(this,require('_process'))
|
|
},{"./AutoFocusUtils":136,"./CSSPropertyOperations":139,"./DOMProperty":144,"./DOMPropertyOperations":145,"./EventConstants":149,"./Object.assign":157,"./ReactBrowserEventEmitter":161,"./ReactComponentBrowserEnvironment":166,"./ReactDOMButton":171,"./ReactDOMInput":176,"./ReactDOMOption":177,"./ReactDOMSelect":178,"./ReactDOMTextarea":182,"./ReactMount":200,"./ReactMultiChild":201,"./ReactPerf":206,"./ReactUpdateQueue":217,"./canDefineProperty":239,"./escapeTextContentForBrowser":242,"./isEventSupported":254,"./setInnerHTML":259,"./setTextContent":260,"./validateDOMNesting":263,"_process":133,"fbjs/lib/invariant":72,"fbjs/lib/keyOf":76,"fbjs/lib/shallowEqual":81,"fbjs/lib/warning":83}],173:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOMFactories
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactElement = require('./ReactElement');
|
|
var ReactElementValidator = require('./ReactElementValidator');
|
|
|
|
var mapObject = require('fbjs/lib/mapObject');
|
|
|
|
/**
|
|
* Create a factory that creates HTML tag elements.
|
|
*
|
|
* @param {string} tag Tag name (e.g. `div`).
|
|
* @private
|
|
*/
|
|
function createDOMFactory(tag) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
return ReactElementValidator.createFactory(tag);
|
|
}
|
|
return ReactElement.createFactory(tag);
|
|
}
|
|
|
|
/**
|
|
* Creates a mapping from supported HTML tags to `ReactDOMComponent` classes.
|
|
* This is also accessible via `React.DOM`.
|
|
*
|
|
* @public
|
|
*/
|
|
var ReactDOMFactories = mapObject({
|
|
a: 'a',
|
|
abbr: 'abbr',
|
|
address: 'address',
|
|
area: 'area',
|
|
article: 'article',
|
|
aside: 'aside',
|
|
audio: 'audio',
|
|
b: 'b',
|
|
base: 'base',
|
|
bdi: 'bdi',
|
|
bdo: 'bdo',
|
|
big: 'big',
|
|
blockquote: 'blockquote',
|
|
body: 'body',
|
|
br: 'br',
|
|
button: 'button',
|
|
canvas: 'canvas',
|
|
caption: 'caption',
|
|
cite: 'cite',
|
|
code: 'code',
|
|
col: 'col',
|
|
colgroup: 'colgroup',
|
|
data: 'data',
|
|
datalist: 'datalist',
|
|
dd: 'dd',
|
|
del: 'del',
|
|
details: 'details',
|
|
dfn: 'dfn',
|
|
dialog: 'dialog',
|
|
div: 'div',
|
|
dl: 'dl',
|
|
dt: 'dt',
|
|
em: 'em',
|
|
embed: 'embed',
|
|
fieldset: 'fieldset',
|
|
figcaption: 'figcaption',
|
|
figure: 'figure',
|
|
footer: 'footer',
|
|
form: 'form',
|
|
h1: 'h1',
|
|
h2: 'h2',
|
|
h3: 'h3',
|
|
h4: 'h4',
|
|
h5: 'h5',
|
|
h6: 'h6',
|
|
head: 'head',
|
|
header: 'header',
|
|
hgroup: 'hgroup',
|
|
hr: 'hr',
|
|
html: 'html',
|
|
i: 'i',
|
|
iframe: 'iframe',
|
|
img: 'img',
|
|
input: 'input',
|
|
ins: 'ins',
|
|
kbd: 'kbd',
|
|
keygen: 'keygen',
|
|
label: 'label',
|
|
legend: 'legend',
|
|
li: 'li',
|
|
link: 'link',
|
|
main: 'main',
|
|
map: 'map',
|
|
mark: 'mark',
|
|
menu: 'menu',
|
|
menuitem: 'menuitem',
|
|
meta: 'meta',
|
|
meter: 'meter',
|
|
nav: 'nav',
|
|
noscript: 'noscript',
|
|
object: 'object',
|
|
ol: 'ol',
|
|
optgroup: 'optgroup',
|
|
option: 'option',
|
|
output: 'output',
|
|
p: 'p',
|
|
param: 'param',
|
|
picture: 'picture',
|
|
pre: 'pre',
|
|
progress: 'progress',
|
|
q: 'q',
|
|
rp: 'rp',
|
|
rt: 'rt',
|
|
ruby: 'ruby',
|
|
s: 's',
|
|
samp: 'samp',
|
|
script: 'script',
|
|
section: 'section',
|
|
select: 'select',
|
|
small: 'small',
|
|
source: 'source',
|
|
span: 'span',
|
|
strong: 'strong',
|
|
style: 'style',
|
|
sub: 'sub',
|
|
summary: 'summary',
|
|
sup: 'sup',
|
|
table: 'table',
|
|
tbody: 'tbody',
|
|
td: 'td',
|
|
textarea: 'textarea',
|
|
tfoot: 'tfoot',
|
|
th: 'th',
|
|
thead: 'thead',
|
|
time: 'time',
|
|
title: 'title',
|
|
tr: 'tr',
|
|
track: 'track',
|
|
u: 'u',
|
|
ul: 'ul',
|
|
'var': 'var',
|
|
video: 'video',
|
|
wbr: 'wbr',
|
|
|
|
// SVG
|
|
circle: 'circle',
|
|
clipPath: 'clipPath',
|
|
defs: 'defs',
|
|
ellipse: 'ellipse',
|
|
g: 'g',
|
|
image: 'image',
|
|
line: 'line',
|
|
linearGradient: 'linearGradient',
|
|
mask: 'mask',
|
|
path: 'path',
|
|
pattern: 'pattern',
|
|
polygon: 'polygon',
|
|
polyline: 'polyline',
|
|
radialGradient: 'radialGradient',
|
|
rect: 'rect',
|
|
stop: 'stop',
|
|
svg: 'svg',
|
|
text: 'text',
|
|
tspan: 'tspan'
|
|
|
|
}, createDOMFactory);
|
|
|
|
module.exports = ReactDOMFactories;
|
|
}).call(this,require('_process'))
|
|
},{"./ReactElement":187,"./ReactElementValidator":188,"_process":133,"fbjs/lib/mapObject":77}],174:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOMFeatureFlags
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactDOMFeatureFlags = {
|
|
useCreateElement: false
|
|
};
|
|
|
|
module.exports = ReactDOMFeatureFlags;
|
|
},{}],175:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOMIDOperations
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var DOMChildrenOperations = require('./DOMChildrenOperations');
|
|
var DOMPropertyOperations = require('./DOMPropertyOperations');
|
|
var ReactMount = require('./ReactMount');
|
|
var ReactPerf = require('./ReactPerf');
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
/**
|
|
* Errors for properties that should not be updated with `updatePropertyByID()`.
|
|
*
|
|
* @type {object}
|
|
* @private
|
|
*/
|
|
var INVALID_PROPERTY_ERRORS = {
|
|
dangerouslySetInnerHTML: '`dangerouslySetInnerHTML` must be set using `updateInnerHTMLByID()`.',
|
|
style: '`style` must be set using `updateStylesByID()`.'
|
|
};
|
|
|
|
/**
|
|
* Operations used to process updates to DOM nodes.
|
|
*/
|
|
var ReactDOMIDOperations = {
|
|
|
|
/**
|
|
* Updates a DOM node with new property values. This should only be used to
|
|
* update DOM properties in `DOMProperty`.
|
|
*
|
|
* @param {string} id ID of the node to update.
|
|
* @param {string} name A valid property name, see `DOMProperty`.
|
|
* @param {*} value New value of the property.
|
|
* @internal
|
|
*/
|
|
updatePropertyByID: function (id, name, value) {
|
|
var node = ReactMount.getNode(id);
|
|
!!INVALID_PROPERTY_ERRORS.hasOwnProperty(name) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'updatePropertyByID(...): %s', INVALID_PROPERTY_ERRORS[name]) : invariant(false) : undefined;
|
|
|
|
// If we're updating to null or undefined, we should remove the property
|
|
// from the DOM node instead of inadvertantly setting to a string. This
|
|
// brings us in line with the same behavior we have on initial render.
|
|
if (value != null) {
|
|
DOMPropertyOperations.setValueForProperty(node, name, value);
|
|
} else {
|
|
DOMPropertyOperations.deleteValueForProperty(node, name);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Replaces a DOM node that exists in the document with markup.
|
|
*
|
|
* @param {string} id ID of child to be replaced.
|
|
* @param {string} markup Dangerous markup to inject in place of child.
|
|
* @internal
|
|
* @see {Danger.dangerouslyReplaceNodeWithMarkup}
|
|
*/
|
|
dangerouslyReplaceNodeWithMarkupByID: function (id, markup) {
|
|
var node = ReactMount.getNode(id);
|
|
DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup(node, markup);
|
|
},
|
|
|
|
/**
|
|
* Updates a component's children by processing a series of updates.
|
|
*
|
|
* @param {array<object>} updates List of update configurations.
|
|
* @param {array<string>} markup List of markup strings.
|
|
* @internal
|
|
*/
|
|
dangerouslyProcessChildrenUpdates: function (updates, markup) {
|
|
for (var i = 0; i < updates.length; i++) {
|
|
updates[i].parentNode = ReactMount.getNode(updates[i].parentID);
|
|
}
|
|
DOMChildrenOperations.processUpdates(updates, markup);
|
|
}
|
|
};
|
|
|
|
ReactPerf.measureMethods(ReactDOMIDOperations, 'ReactDOMIDOperations', {
|
|
dangerouslyReplaceNodeWithMarkupByID: 'dangerouslyReplaceNodeWithMarkupByID',
|
|
dangerouslyProcessChildrenUpdates: 'dangerouslyProcessChildrenUpdates'
|
|
});
|
|
|
|
module.exports = ReactDOMIDOperations;
|
|
}).call(this,require('_process'))
|
|
},{"./DOMChildrenOperations":143,"./DOMPropertyOperations":145,"./ReactMount":200,"./ReactPerf":206,"_process":133,"fbjs/lib/invariant":72}],176:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOMInput
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactDOMIDOperations = require('./ReactDOMIDOperations');
|
|
var LinkedValueUtils = require('./LinkedValueUtils');
|
|
var ReactMount = require('./ReactMount');
|
|
var ReactUpdates = require('./ReactUpdates');
|
|
|
|
var assign = require('./Object.assign');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
var instancesByReactID = {};
|
|
|
|
function forceUpdateIfMounted() {
|
|
if (this._rootNodeID) {
|
|
// DOM component is still mounted; update
|
|
ReactDOMInput.updateWrapper(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements an <input> native component that allows setting these optional
|
|
* props: `checked`, `value`, `defaultChecked`, and `defaultValue`.
|
|
*
|
|
* If `checked` or `value` are not supplied (or null/undefined), user actions
|
|
* that affect the checked state or value will trigger updates to the element.
|
|
*
|
|
* If they are supplied (and not null/undefined), the rendered element will not
|
|
* trigger updates to the element. Instead, the props must change in order for
|
|
* the rendered element to be updated.
|
|
*
|
|
* The rendered element will be initialized as unchecked (or `defaultChecked`)
|
|
* with an empty value (or `defaultValue`).
|
|
*
|
|
* @see http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
|
|
*/
|
|
var ReactDOMInput = {
|
|
getNativeProps: function (inst, props, context) {
|
|
var value = LinkedValueUtils.getValue(props);
|
|
var checked = LinkedValueUtils.getChecked(props);
|
|
|
|
var nativeProps = assign({}, props, {
|
|
defaultChecked: undefined,
|
|
defaultValue: undefined,
|
|
value: value != null ? value : inst._wrapperState.initialValue,
|
|
checked: checked != null ? checked : inst._wrapperState.initialChecked,
|
|
onChange: inst._wrapperState.onChange
|
|
});
|
|
|
|
return nativeProps;
|
|
},
|
|
|
|
mountWrapper: function (inst, props) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
LinkedValueUtils.checkPropTypes('input', props, inst._currentElement._owner);
|
|
}
|
|
|
|
var defaultValue = props.defaultValue;
|
|
inst._wrapperState = {
|
|
initialChecked: props.defaultChecked || false,
|
|
initialValue: defaultValue != null ? defaultValue : null,
|
|
onChange: _handleChange.bind(inst)
|
|
};
|
|
},
|
|
|
|
mountReadyWrapper: function (inst) {
|
|
// Can't be in mountWrapper or else server rendering leaks.
|
|
instancesByReactID[inst._rootNodeID] = inst;
|
|
},
|
|
|
|
unmountWrapper: function (inst) {
|
|
delete instancesByReactID[inst._rootNodeID];
|
|
},
|
|
|
|
updateWrapper: function (inst) {
|
|
var props = inst._currentElement.props;
|
|
|
|
// TODO: Shouldn't this be getChecked(props)?
|
|
var checked = props.checked;
|
|
if (checked != null) {
|
|
ReactDOMIDOperations.updatePropertyByID(inst._rootNodeID, 'checked', checked || false);
|
|
}
|
|
|
|
var value = LinkedValueUtils.getValue(props);
|
|
if (value != null) {
|
|
// Cast `value` to a string to ensure the value is set correctly. While
|
|
// browsers typically do this as necessary, jsdom doesn't.
|
|
ReactDOMIDOperations.updatePropertyByID(inst._rootNodeID, 'value', '' + value);
|
|
}
|
|
}
|
|
};
|
|
|
|
function _handleChange(event) {
|
|
var props = this._currentElement.props;
|
|
|
|
var returnValue = LinkedValueUtils.executeOnChange(props, event);
|
|
|
|
// Here we use asap to wait until all updates have propagated, which
|
|
// is important when using controlled components within layers:
|
|
// https://github.com/facebook/react/issues/1698
|
|
ReactUpdates.asap(forceUpdateIfMounted, this);
|
|
|
|
var name = props.name;
|
|
if (props.type === 'radio' && name != null) {
|
|
var rootNode = ReactMount.getNode(this._rootNodeID);
|
|
var queryRoot = rootNode;
|
|
|
|
while (queryRoot.parentNode) {
|
|
queryRoot = queryRoot.parentNode;
|
|
}
|
|
|
|
// If `rootNode.form` was non-null, then we could try `form.elements`,
|
|
// but that sometimes behaves strangely in IE8. We could also try using
|
|
// `form.getElementsByName`, but that will only return direct children
|
|
// and won't include inputs that use the HTML5 `form=` attribute. Since
|
|
// the input might not even be in a form, let's just use the global
|
|
// `querySelectorAll` to ensure we don't miss anything.
|
|
var group = queryRoot.querySelectorAll('input[name=' + JSON.stringify('' + name) + '][type="radio"]');
|
|
|
|
for (var i = 0; i < group.length; i++) {
|
|
var otherNode = group[i];
|
|
if (otherNode === rootNode || otherNode.form !== rootNode.form) {
|
|
continue;
|
|
}
|
|
// This will throw if radio buttons rendered by different copies of React
|
|
// and the same name are rendered into the same form (same as #1939).
|
|
// That's probably okay; we don't support it just as we don't support
|
|
// mixing React with non-React.
|
|
var otherID = ReactMount.getID(otherNode);
|
|
!otherID ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactDOMInput: Mixing React and non-React radio inputs with the ' + 'same `name` is not supported.') : invariant(false) : undefined;
|
|
var otherInstance = instancesByReactID[otherID];
|
|
!otherInstance ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactDOMInput: Unknown radio button ID %s.', otherID) : invariant(false) : undefined;
|
|
// If this is a controlled radio button group, forcing the input that
|
|
// was previously checked to update will cause it to be come re-checked
|
|
// as appropriate.
|
|
ReactUpdates.asap(forceUpdateIfMounted, otherInstance);
|
|
}
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
module.exports = ReactDOMInput;
|
|
}).call(this,require('_process'))
|
|
},{"./LinkedValueUtils":156,"./Object.assign":157,"./ReactDOMIDOperations":175,"./ReactMount":200,"./ReactUpdates":218,"_process":133,"fbjs/lib/invariant":72}],177:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOMOption
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactChildren = require('./ReactChildren');
|
|
var ReactDOMSelect = require('./ReactDOMSelect');
|
|
|
|
var assign = require('./Object.assign');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
var valueContextKey = ReactDOMSelect.valueContextKey;
|
|
|
|
/**
|
|
* Implements an <option> native component that warns when `selected` is set.
|
|
*/
|
|
var ReactDOMOption = {
|
|
mountWrapper: function (inst, props, context) {
|
|
// TODO (yungsters): Remove support for `selected` in <option>.
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(props.selected == null, 'Use the `defaultValue` or `value` props on <select> instead of ' + 'setting `selected` on <option>.') : undefined;
|
|
}
|
|
|
|
// Look up whether this option is 'selected' via context
|
|
var selectValue = context[valueContextKey];
|
|
|
|
// If context key is null (e.g., no specified value or after initial mount)
|
|
// or missing (e.g., for <datalist>), we don't change props.selected
|
|
var selected = null;
|
|
if (selectValue != null) {
|
|
selected = false;
|
|
if (Array.isArray(selectValue)) {
|
|
// multiple
|
|
for (var i = 0; i < selectValue.length; i++) {
|
|
if ('' + selectValue[i] === '' + props.value) {
|
|
selected = true;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
selected = '' + selectValue === '' + props.value;
|
|
}
|
|
}
|
|
|
|
inst._wrapperState = { selected: selected };
|
|
},
|
|
|
|
getNativeProps: function (inst, props, context) {
|
|
var nativeProps = assign({ selected: undefined, children: undefined }, props);
|
|
|
|
// Read state only from initial mount because <select> updates value
|
|
// manually; we need the initial state only for server rendering
|
|
if (inst._wrapperState.selected != null) {
|
|
nativeProps.selected = inst._wrapperState.selected;
|
|
}
|
|
|
|
var content = '';
|
|
|
|
// Flatten children and warn if they aren't strings or numbers;
|
|
// invalid types are ignored.
|
|
ReactChildren.forEach(props.children, function (child) {
|
|
if (child == null) {
|
|
return;
|
|
}
|
|
if (typeof child === 'string' || typeof child === 'number') {
|
|
content += child;
|
|
} else {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Only strings and numbers are supported as <option> children.') : undefined;
|
|
}
|
|
});
|
|
|
|
if (content) {
|
|
nativeProps.children = content;
|
|
}
|
|
|
|
return nativeProps;
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactDOMOption;
|
|
}).call(this,require('_process'))
|
|
},{"./Object.assign":157,"./ReactChildren":163,"./ReactDOMSelect":178,"_process":133,"fbjs/lib/warning":83}],178:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOMSelect
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var LinkedValueUtils = require('./LinkedValueUtils');
|
|
var ReactMount = require('./ReactMount');
|
|
var ReactUpdates = require('./ReactUpdates');
|
|
|
|
var assign = require('./Object.assign');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
var valueContextKey = '__ReactDOMSelect_value$' + Math.random().toString(36).slice(2);
|
|
|
|
function updateOptionsIfPendingUpdateAndMounted() {
|
|
if (this._rootNodeID && this._wrapperState.pendingUpdate) {
|
|
this._wrapperState.pendingUpdate = false;
|
|
|
|
var props = this._currentElement.props;
|
|
var value = LinkedValueUtils.getValue(props);
|
|
|
|
if (value != null) {
|
|
updateOptions(this, Boolean(props.multiple), value);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getDeclarationErrorAddendum(owner) {
|
|
if (owner) {
|
|
var name = owner.getName();
|
|
if (name) {
|
|
return ' Check the render method of `' + name + '`.';
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
var valuePropNames = ['value', 'defaultValue'];
|
|
|
|
/**
|
|
* Validation function for `value` and `defaultValue`.
|
|
* @private
|
|
*/
|
|
function checkSelectPropTypes(inst, props) {
|
|
var owner = inst._currentElement._owner;
|
|
LinkedValueUtils.checkPropTypes('select', props, owner);
|
|
|
|
for (var i = 0; i < valuePropNames.length; i++) {
|
|
var propName = valuePropNames[i];
|
|
if (props[propName] == null) {
|
|
continue;
|
|
}
|
|
if (props.multiple) {
|
|
process.env.NODE_ENV !== 'production' ? warning(Array.isArray(props[propName]), 'The `%s` prop supplied to <select> must be an array if ' + '`multiple` is true.%s', propName, getDeclarationErrorAddendum(owner)) : undefined;
|
|
} else {
|
|
process.env.NODE_ENV !== 'production' ? warning(!Array.isArray(props[propName]), 'The `%s` prop supplied to <select> must be a scalar ' + 'value if `multiple` is false.%s', propName, getDeclarationErrorAddendum(owner)) : undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {ReactDOMComponent} inst
|
|
* @param {boolean} multiple
|
|
* @param {*} propValue A stringable (with `multiple`, a list of stringables).
|
|
* @private
|
|
*/
|
|
function updateOptions(inst, multiple, propValue) {
|
|
var selectedValue, i;
|
|
var options = ReactMount.getNode(inst._rootNodeID).options;
|
|
|
|
if (multiple) {
|
|
selectedValue = {};
|
|
for (i = 0; i < propValue.length; i++) {
|
|
selectedValue['' + propValue[i]] = true;
|
|
}
|
|
for (i = 0; i < options.length; i++) {
|
|
var selected = selectedValue.hasOwnProperty(options[i].value);
|
|
if (options[i].selected !== selected) {
|
|
options[i].selected = selected;
|
|
}
|
|
}
|
|
} else {
|
|
// Do not set `select.value` as exact behavior isn't consistent across all
|
|
// browsers for all cases.
|
|
selectedValue = '' + propValue;
|
|
for (i = 0; i < options.length; i++) {
|
|
if (options[i].value === selectedValue) {
|
|
options[i].selected = true;
|
|
return;
|
|
}
|
|
}
|
|
if (options.length) {
|
|
options[0].selected = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements a <select> native component that allows optionally setting the
|
|
* props `value` and `defaultValue`. If `multiple` is false, the prop must be a
|
|
* stringable. If `multiple` is true, the prop must be an array of stringables.
|
|
*
|
|
* If `value` is not supplied (or null/undefined), user actions that change the
|
|
* selected option will trigger updates to the rendered options.
|
|
*
|
|
* If it is supplied (and not null/undefined), the rendered options will not
|
|
* update in response to user actions. Instead, the `value` prop must change in
|
|
* order for the rendered options to update.
|
|
*
|
|
* If `defaultValue` is provided, any options with the supplied values will be
|
|
* selected.
|
|
*/
|
|
var ReactDOMSelect = {
|
|
valueContextKey: valueContextKey,
|
|
|
|
getNativeProps: function (inst, props, context) {
|
|
return assign({}, props, {
|
|
onChange: inst._wrapperState.onChange,
|
|
value: undefined
|
|
});
|
|
},
|
|
|
|
mountWrapper: function (inst, props) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
checkSelectPropTypes(inst, props);
|
|
}
|
|
|
|
var value = LinkedValueUtils.getValue(props);
|
|
inst._wrapperState = {
|
|
pendingUpdate: false,
|
|
initialValue: value != null ? value : props.defaultValue,
|
|
onChange: _handleChange.bind(inst),
|
|
wasMultiple: Boolean(props.multiple)
|
|
};
|
|
},
|
|
|
|
processChildContext: function (inst, props, context) {
|
|
// Pass down initial value so initial generated markup has correct
|
|
// `selected` attributes
|
|
var childContext = assign({}, context);
|
|
childContext[valueContextKey] = inst._wrapperState.initialValue;
|
|
return childContext;
|
|
},
|
|
|
|
postUpdateWrapper: function (inst) {
|
|
var props = inst._currentElement.props;
|
|
|
|
// After the initial mount, we control selected-ness manually so don't pass
|
|
// the context value down
|
|
inst._wrapperState.initialValue = undefined;
|
|
|
|
var wasMultiple = inst._wrapperState.wasMultiple;
|
|
inst._wrapperState.wasMultiple = Boolean(props.multiple);
|
|
|
|
var value = LinkedValueUtils.getValue(props);
|
|
if (value != null) {
|
|
inst._wrapperState.pendingUpdate = false;
|
|
updateOptions(inst, Boolean(props.multiple), value);
|
|
} else if (wasMultiple !== Boolean(props.multiple)) {
|
|
// For simplicity, reapply `defaultValue` if `multiple` is toggled.
|
|
if (props.defaultValue != null) {
|
|
updateOptions(inst, Boolean(props.multiple), props.defaultValue);
|
|
} else {
|
|
// Revert the select back to its default unselected state.
|
|
updateOptions(inst, Boolean(props.multiple), props.multiple ? [] : '');
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
function _handleChange(event) {
|
|
var props = this._currentElement.props;
|
|
var returnValue = LinkedValueUtils.executeOnChange(props, event);
|
|
|
|
this._wrapperState.pendingUpdate = true;
|
|
ReactUpdates.asap(updateOptionsIfPendingUpdateAndMounted, this);
|
|
return returnValue;
|
|
}
|
|
|
|
module.exports = ReactDOMSelect;
|
|
}).call(this,require('_process'))
|
|
},{"./LinkedValueUtils":156,"./Object.assign":157,"./ReactMount":200,"./ReactUpdates":218,"_process":133,"fbjs/lib/warning":83}],179:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOMSelection
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
|
|
var getNodeForCharacterOffset = require('./getNodeForCharacterOffset');
|
|
var getTextContentAccessor = require('./getTextContentAccessor');
|
|
|
|
/**
|
|
* While `isCollapsed` is available on the Selection object and `collapsed`
|
|
* is available on the Range object, IE11 sometimes gets them wrong.
|
|
* If the anchor/focus nodes and offsets are the same, the range is collapsed.
|
|
*/
|
|
function isCollapsed(anchorNode, anchorOffset, focusNode, focusOffset) {
|
|
return anchorNode === focusNode && anchorOffset === focusOffset;
|
|
}
|
|
|
|
/**
|
|
* Get the appropriate anchor and focus node/offset pairs for IE.
|
|
*
|
|
* The catch here is that IE's selection API doesn't provide information
|
|
* about whether the selection is forward or backward, so we have to
|
|
* behave as though it's always forward.
|
|
*
|
|
* IE text differs from modern selection in that it behaves as though
|
|
* block elements end with a new line. This means character offsets will
|
|
* differ between the two APIs.
|
|
*
|
|
* @param {DOMElement} node
|
|
* @return {object}
|
|
*/
|
|
function getIEOffsets(node) {
|
|
var selection = document.selection;
|
|
var selectedRange = selection.createRange();
|
|
var selectedLength = selectedRange.text.length;
|
|
|
|
// Duplicate selection so we can move range without breaking user selection.
|
|
var fromStart = selectedRange.duplicate();
|
|
fromStart.moveToElementText(node);
|
|
fromStart.setEndPoint('EndToStart', selectedRange);
|
|
|
|
var startOffset = fromStart.text.length;
|
|
var endOffset = startOffset + selectedLength;
|
|
|
|
return {
|
|
start: startOffset,
|
|
end: endOffset
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {DOMElement} node
|
|
* @return {?object}
|
|
*/
|
|
function getModernOffsets(node) {
|
|
var selection = window.getSelection && window.getSelection();
|
|
|
|
if (!selection || selection.rangeCount === 0) {
|
|
return null;
|
|
}
|
|
|
|
var anchorNode = selection.anchorNode;
|
|
var anchorOffset = selection.anchorOffset;
|
|
var focusNode = selection.focusNode;
|
|
var focusOffset = selection.focusOffset;
|
|
|
|
var currentRange = selection.getRangeAt(0);
|
|
|
|
// In Firefox, range.startContainer and range.endContainer can be "anonymous
|
|
// divs", e.g. the up/down buttons on an <input type="number">. Anonymous
|
|
// divs do not seem to expose properties, triggering a "Permission denied
|
|
// error" if any of its properties are accessed. The only seemingly possible
|
|
// way to avoid erroring is to access a property that typically works for
|
|
// non-anonymous divs and catch any error that may otherwise arise. See
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=208427
|
|
try {
|
|
/* eslint-disable no-unused-expressions */
|
|
currentRange.startContainer.nodeType;
|
|
currentRange.endContainer.nodeType;
|
|
/* eslint-enable no-unused-expressions */
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
|
|
// If the node and offset values are the same, the selection is collapsed.
|
|
// `Selection.isCollapsed` is available natively, but IE sometimes gets
|
|
// this value wrong.
|
|
var isSelectionCollapsed = isCollapsed(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset);
|
|
|
|
var rangeLength = isSelectionCollapsed ? 0 : currentRange.toString().length;
|
|
|
|
var tempRange = currentRange.cloneRange();
|
|
tempRange.selectNodeContents(node);
|
|
tempRange.setEnd(currentRange.startContainer, currentRange.startOffset);
|
|
|
|
var isTempRangeCollapsed = isCollapsed(tempRange.startContainer, tempRange.startOffset, tempRange.endContainer, tempRange.endOffset);
|
|
|
|
var start = isTempRangeCollapsed ? 0 : tempRange.toString().length;
|
|
var end = start + rangeLength;
|
|
|
|
// Detect whether the selection is backward.
|
|
var detectionRange = document.createRange();
|
|
detectionRange.setStart(anchorNode, anchorOffset);
|
|
detectionRange.setEnd(focusNode, focusOffset);
|
|
var isBackward = detectionRange.collapsed;
|
|
|
|
return {
|
|
start: isBackward ? end : start,
|
|
end: isBackward ? start : end
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {DOMElement|DOMTextNode} node
|
|
* @param {object} offsets
|
|
*/
|
|
function setIEOffsets(node, offsets) {
|
|
var range = document.selection.createRange().duplicate();
|
|
var start, end;
|
|
|
|
if (typeof offsets.end === 'undefined') {
|
|
start = offsets.start;
|
|
end = start;
|
|
} else if (offsets.start > offsets.end) {
|
|
start = offsets.end;
|
|
end = offsets.start;
|
|
} else {
|
|
start = offsets.start;
|
|
end = offsets.end;
|
|
}
|
|
|
|
range.moveToElementText(node);
|
|
range.moveStart('character', start);
|
|
range.setEndPoint('EndToStart', range);
|
|
range.moveEnd('character', end - start);
|
|
range.select();
|
|
}
|
|
|
|
/**
|
|
* In modern non-IE browsers, we can support both forward and backward
|
|
* selections.
|
|
*
|
|
* Note: IE10+ supports the Selection object, but it does not support
|
|
* the `extend` method, which means that even in modern IE, it's not possible
|
|
* to programatically create a backward selection. Thus, for all IE
|
|
* versions, we use the old IE API to create our selections.
|
|
*
|
|
* @param {DOMElement|DOMTextNode} node
|
|
* @param {object} offsets
|
|
*/
|
|
function setModernOffsets(node, offsets) {
|
|
if (!window.getSelection) {
|
|
return;
|
|
}
|
|
|
|
var selection = window.getSelection();
|
|
var length = node[getTextContentAccessor()].length;
|
|
var start = Math.min(offsets.start, length);
|
|
var end = typeof offsets.end === 'undefined' ? start : Math.min(offsets.end, length);
|
|
|
|
// IE 11 uses modern selection, but doesn't support the extend method.
|
|
// Flip backward selections, so we can set with a single range.
|
|
if (!selection.extend && start > end) {
|
|
var temp = end;
|
|
end = start;
|
|
start = temp;
|
|
}
|
|
|
|
var startMarker = getNodeForCharacterOffset(node, start);
|
|
var endMarker = getNodeForCharacterOffset(node, end);
|
|
|
|
if (startMarker && endMarker) {
|
|
var range = document.createRange();
|
|
range.setStart(startMarker.node, startMarker.offset);
|
|
selection.removeAllRanges();
|
|
|
|
if (start > end) {
|
|
selection.addRange(range);
|
|
selection.extend(endMarker.node, endMarker.offset);
|
|
} else {
|
|
range.setEnd(endMarker.node, endMarker.offset);
|
|
selection.addRange(range);
|
|
}
|
|
}
|
|
}
|
|
|
|
var useIEOffsets = ExecutionEnvironment.canUseDOM && 'selection' in document && !('getSelection' in window);
|
|
|
|
var ReactDOMSelection = {
|
|
/**
|
|
* @param {DOMElement} node
|
|
*/
|
|
getOffsets: useIEOffsets ? getIEOffsets : getModernOffsets,
|
|
|
|
/**
|
|
* @param {DOMElement|DOMTextNode} node
|
|
* @param {object} offsets
|
|
*/
|
|
setOffsets: useIEOffsets ? setIEOffsets : setModernOffsets
|
|
};
|
|
|
|
module.exports = ReactDOMSelection;
|
|
},{"./getNodeForCharacterOffset":251,"./getTextContentAccessor":252,"fbjs/lib/ExecutionEnvironment":58}],180:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOMServer
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactDefaultInjection = require('./ReactDefaultInjection');
|
|
var ReactServerRendering = require('./ReactServerRendering');
|
|
var ReactVersion = require('./ReactVersion');
|
|
|
|
ReactDefaultInjection.inject();
|
|
|
|
var ReactDOMServer = {
|
|
renderToString: ReactServerRendering.renderToString,
|
|
renderToStaticMarkup: ReactServerRendering.renderToStaticMarkup,
|
|
version: ReactVersion
|
|
};
|
|
|
|
module.exports = ReactDOMServer;
|
|
},{"./ReactDefaultInjection":184,"./ReactServerRendering":215,"./ReactVersion":219}],181:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOMTextComponent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var DOMChildrenOperations = require('./DOMChildrenOperations');
|
|
var DOMPropertyOperations = require('./DOMPropertyOperations');
|
|
var ReactComponentBrowserEnvironment = require('./ReactComponentBrowserEnvironment');
|
|
var ReactMount = require('./ReactMount');
|
|
|
|
var assign = require('./Object.assign');
|
|
var escapeTextContentForBrowser = require('./escapeTextContentForBrowser');
|
|
var setTextContent = require('./setTextContent');
|
|
var validateDOMNesting = require('./validateDOMNesting');
|
|
|
|
/**
|
|
* Text nodes violate a couple assumptions that React makes about components:
|
|
*
|
|
* - When mounting text into the DOM, adjacent text nodes are merged.
|
|
* - Text nodes cannot be assigned a React root ID.
|
|
*
|
|
* This component is used to wrap strings in elements so that they can undergo
|
|
* the same reconciliation that is applied to elements.
|
|
*
|
|
* TODO: Investigate representing React components in the DOM with text nodes.
|
|
*
|
|
* @class ReactDOMTextComponent
|
|
* @extends ReactComponent
|
|
* @internal
|
|
*/
|
|
var ReactDOMTextComponent = function (props) {
|
|
// This constructor and its argument is currently used by mocks.
|
|
};
|
|
|
|
assign(ReactDOMTextComponent.prototype, {
|
|
|
|
/**
|
|
* @param {ReactText} text
|
|
* @internal
|
|
*/
|
|
construct: function (text) {
|
|
// TODO: This is really a ReactText (ReactNode), not a ReactElement
|
|
this._currentElement = text;
|
|
this._stringText = '' + text;
|
|
|
|
// Properties
|
|
this._rootNodeID = null;
|
|
this._mountIndex = 0;
|
|
},
|
|
|
|
/**
|
|
* Creates the markup for this text node. This node is not intended to have
|
|
* any features besides containing text content.
|
|
*
|
|
* @param {string} rootID DOM ID of the root node.
|
|
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
|
* @return {string} Markup for this text node.
|
|
* @internal
|
|
*/
|
|
mountComponent: function (rootID, transaction, context) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
if (context[validateDOMNesting.ancestorInfoContextKey]) {
|
|
validateDOMNesting('span', null, context[validateDOMNesting.ancestorInfoContextKey]);
|
|
}
|
|
}
|
|
|
|
this._rootNodeID = rootID;
|
|
if (transaction.useCreateElement) {
|
|
var ownerDocument = context[ReactMount.ownerDocumentContextKey];
|
|
var el = ownerDocument.createElement('span');
|
|
DOMPropertyOperations.setAttributeForID(el, rootID);
|
|
// Populate node cache
|
|
ReactMount.getID(el);
|
|
setTextContent(el, this._stringText);
|
|
return el;
|
|
} else {
|
|
var escapedText = escapeTextContentForBrowser(this._stringText);
|
|
|
|
if (transaction.renderToStaticMarkup) {
|
|
// Normally we'd wrap this in a `span` for the reasons stated above, but
|
|
// since this is a situation where React won't take over (static pages),
|
|
// we can simply return the text as it is.
|
|
return escapedText;
|
|
}
|
|
|
|
return '<span ' + DOMPropertyOperations.createMarkupForID(rootID) + '>' + escapedText + '</span>';
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Updates this component by updating the text content.
|
|
*
|
|
* @param {ReactText} nextText The next text content
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @internal
|
|
*/
|
|
receiveComponent: function (nextText, transaction) {
|
|
if (nextText !== this._currentElement) {
|
|
this._currentElement = nextText;
|
|
var nextStringText = '' + nextText;
|
|
if (nextStringText !== this._stringText) {
|
|
// TODO: Save this as pending props and use performUpdateIfNecessary
|
|
// and/or updateComponent to do the actual update for consistency with
|
|
// other component types?
|
|
this._stringText = nextStringText;
|
|
var node = ReactMount.getNode(this._rootNodeID);
|
|
DOMChildrenOperations.updateTextContent(node, nextStringText);
|
|
}
|
|
}
|
|
},
|
|
|
|
unmountComponent: function () {
|
|
ReactComponentBrowserEnvironment.unmountIDFromEnvironment(this._rootNodeID);
|
|
}
|
|
|
|
});
|
|
|
|
module.exports = ReactDOMTextComponent;
|
|
}).call(this,require('_process'))
|
|
},{"./DOMChildrenOperations":143,"./DOMPropertyOperations":145,"./Object.assign":157,"./ReactComponentBrowserEnvironment":166,"./ReactMount":200,"./escapeTextContentForBrowser":242,"./setTextContent":260,"./validateDOMNesting":263,"_process":133}],182:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDOMTextarea
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var LinkedValueUtils = require('./LinkedValueUtils');
|
|
var ReactDOMIDOperations = require('./ReactDOMIDOperations');
|
|
var ReactUpdates = require('./ReactUpdates');
|
|
|
|
var assign = require('./Object.assign');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
function forceUpdateIfMounted() {
|
|
if (this._rootNodeID) {
|
|
// DOM component is still mounted; update
|
|
ReactDOMTextarea.updateWrapper(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements a <textarea> native component that allows setting `value`, and
|
|
* `defaultValue`. This differs from the traditional DOM API because value is
|
|
* usually set as PCDATA children.
|
|
*
|
|
* If `value` is not supplied (or null/undefined), user actions that affect the
|
|
* value will trigger updates to the element.
|
|
*
|
|
* If `value` is supplied (and not null/undefined), the rendered element will
|
|
* not trigger updates to the element. Instead, the `value` prop must change in
|
|
* order for the rendered element to be updated.
|
|
*
|
|
* The rendered element will be initialized with an empty value, the prop
|
|
* `defaultValue` if specified, or the children content (deprecated).
|
|
*/
|
|
var ReactDOMTextarea = {
|
|
getNativeProps: function (inst, props, context) {
|
|
!(props.dangerouslySetInnerHTML == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, '`dangerouslySetInnerHTML` does not make sense on <textarea>.') : invariant(false) : undefined;
|
|
|
|
// Always set children to the same thing. In IE9, the selection range will
|
|
// get reset if `textContent` is mutated.
|
|
var nativeProps = assign({}, props, {
|
|
defaultValue: undefined,
|
|
value: undefined,
|
|
children: inst._wrapperState.initialValue,
|
|
onChange: inst._wrapperState.onChange
|
|
});
|
|
|
|
return nativeProps;
|
|
},
|
|
|
|
mountWrapper: function (inst, props) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
LinkedValueUtils.checkPropTypes('textarea', props, inst._currentElement._owner);
|
|
}
|
|
|
|
var defaultValue = props.defaultValue;
|
|
// TODO (yungsters): Remove support for children content in <textarea>.
|
|
var children = props.children;
|
|
if (children != null) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Use the `defaultValue` or `value` props instead of setting ' + 'children on <textarea>.') : undefined;
|
|
}
|
|
!(defaultValue == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'If you supply `defaultValue` on a <textarea>, do not pass children.') : invariant(false) : undefined;
|
|
if (Array.isArray(children)) {
|
|
!(children.length <= 1) ? process.env.NODE_ENV !== 'production' ? invariant(false, '<textarea> can only have at most one child.') : invariant(false) : undefined;
|
|
children = children[0];
|
|
}
|
|
|
|
defaultValue = '' + children;
|
|
}
|
|
if (defaultValue == null) {
|
|
defaultValue = '';
|
|
}
|
|
var value = LinkedValueUtils.getValue(props);
|
|
|
|
inst._wrapperState = {
|
|
// We save the initial value so that `ReactDOMComponent` doesn't update
|
|
// `textContent` (unnecessary since we update value).
|
|
// The initial value can be a boolean or object so that's why it's
|
|
// forced to be a string.
|
|
initialValue: '' + (value != null ? value : defaultValue),
|
|
onChange: _handleChange.bind(inst)
|
|
};
|
|
},
|
|
|
|
updateWrapper: function (inst) {
|
|
var props = inst._currentElement.props;
|
|
var value = LinkedValueUtils.getValue(props);
|
|
if (value != null) {
|
|
// Cast `value` to a string to ensure the value is set correctly. While
|
|
// browsers typically do this as necessary, jsdom doesn't.
|
|
ReactDOMIDOperations.updatePropertyByID(inst._rootNodeID, 'value', '' + value);
|
|
}
|
|
}
|
|
};
|
|
|
|
function _handleChange(event) {
|
|
var props = this._currentElement.props;
|
|
var returnValue = LinkedValueUtils.executeOnChange(props, event);
|
|
ReactUpdates.asap(forceUpdateIfMounted, this);
|
|
return returnValue;
|
|
}
|
|
|
|
module.exports = ReactDOMTextarea;
|
|
}).call(this,require('_process'))
|
|
},{"./LinkedValueUtils":156,"./Object.assign":157,"./ReactDOMIDOperations":175,"./ReactUpdates":218,"_process":133,"fbjs/lib/invariant":72,"fbjs/lib/warning":83}],183:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDefaultBatchingStrategy
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactUpdates = require('./ReactUpdates');
|
|
var Transaction = require('./Transaction');
|
|
|
|
var assign = require('./Object.assign');
|
|
var emptyFunction = require('fbjs/lib/emptyFunction');
|
|
|
|
var RESET_BATCHED_UPDATES = {
|
|
initialize: emptyFunction,
|
|
close: function () {
|
|
ReactDefaultBatchingStrategy.isBatchingUpdates = false;
|
|
}
|
|
};
|
|
|
|
var FLUSH_BATCHED_UPDATES = {
|
|
initialize: emptyFunction,
|
|
close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
|
|
};
|
|
|
|
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
|
|
|
|
function ReactDefaultBatchingStrategyTransaction() {
|
|
this.reinitializeTransaction();
|
|
}
|
|
|
|
assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction.Mixin, {
|
|
getTransactionWrappers: function () {
|
|
return TRANSACTION_WRAPPERS;
|
|
}
|
|
});
|
|
|
|
var transaction = new ReactDefaultBatchingStrategyTransaction();
|
|
|
|
var ReactDefaultBatchingStrategy = {
|
|
isBatchingUpdates: false,
|
|
|
|
/**
|
|
* Call the provided function in a context within which calls to `setState`
|
|
* and friends are batched such that components aren't updated unnecessarily.
|
|
*/
|
|
batchedUpdates: function (callback, a, b, c, d, e) {
|
|
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
|
|
|
|
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
|
|
|
|
// The code is written this way to avoid extra allocations
|
|
if (alreadyBatchingUpdates) {
|
|
callback(a, b, c, d, e);
|
|
} else {
|
|
transaction.perform(callback, null, a, b, c, d, e);
|
|
}
|
|
}
|
|
};
|
|
|
|
module.exports = ReactDefaultBatchingStrategy;
|
|
},{"./Object.assign":157,"./ReactUpdates":218,"./Transaction":235,"fbjs/lib/emptyFunction":64}],184:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDefaultInjection
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var BeforeInputEventPlugin = require('./BeforeInputEventPlugin');
|
|
var ChangeEventPlugin = require('./ChangeEventPlugin');
|
|
var ClientReactRootIndex = require('./ClientReactRootIndex');
|
|
var DefaultEventPluginOrder = require('./DefaultEventPluginOrder');
|
|
var EnterLeaveEventPlugin = require('./EnterLeaveEventPlugin');
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
var HTMLDOMPropertyConfig = require('./HTMLDOMPropertyConfig');
|
|
var ReactBrowserComponentMixin = require('./ReactBrowserComponentMixin');
|
|
var ReactComponentBrowserEnvironment = require('./ReactComponentBrowserEnvironment');
|
|
var ReactDefaultBatchingStrategy = require('./ReactDefaultBatchingStrategy');
|
|
var ReactDOMComponent = require('./ReactDOMComponent');
|
|
var ReactDOMTextComponent = require('./ReactDOMTextComponent');
|
|
var ReactEventListener = require('./ReactEventListener');
|
|
var ReactInjection = require('./ReactInjection');
|
|
var ReactInstanceHandles = require('./ReactInstanceHandles');
|
|
var ReactMount = require('./ReactMount');
|
|
var ReactReconcileTransaction = require('./ReactReconcileTransaction');
|
|
var SelectEventPlugin = require('./SelectEventPlugin');
|
|
var ServerReactRootIndex = require('./ServerReactRootIndex');
|
|
var SimpleEventPlugin = require('./SimpleEventPlugin');
|
|
var SVGDOMPropertyConfig = require('./SVGDOMPropertyConfig');
|
|
|
|
var alreadyInjected = false;
|
|
|
|
function inject() {
|
|
if (alreadyInjected) {
|
|
// TODO: This is currently true because these injections are shared between
|
|
// the client and the server package. They should be built independently
|
|
// and not share any injection state. Then this problem will be solved.
|
|
return;
|
|
}
|
|
alreadyInjected = true;
|
|
|
|
ReactInjection.EventEmitter.injectReactEventListener(ReactEventListener);
|
|
|
|
/**
|
|
* Inject modules for resolving DOM hierarchy and plugin ordering.
|
|
*/
|
|
ReactInjection.EventPluginHub.injectEventPluginOrder(DefaultEventPluginOrder);
|
|
ReactInjection.EventPluginHub.injectInstanceHandle(ReactInstanceHandles);
|
|
ReactInjection.EventPluginHub.injectMount(ReactMount);
|
|
|
|
/**
|
|
* Some important event plugins included by default (without having to require
|
|
* them).
|
|
*/
|
|
ReactInjection.EventPluginHub.injectEventPluginsByName({
|
|
SimpleEventPlugin: SimpleEventPlugin,
|
|
EnterLeaveEventPlugin: EnterLeaveEventPlugin,
|
|
ChangeEventPlugin: ChangeEventPlugin,
|
|
SelectEventPlugin: SelectEventPlugin,
|
|
BeforeInputEventPlugin: BeforeInputEventPlugin
|
|
});
|
|
|
|
ReactInjection.NativeComponent.injectGenericComponentClass(ReactDOMComponent);
|
|
|
|
ReactInjection.NativeComponent.injectTextComponentClass(ReactDOMTextComponent);
|
|
|
|
ReactInjection.Class.injectMixin(ReactBrowserComponentMixin);
|
|
|
|
ReactInjection.DOMProperty.injectDOMPropertyConfig(HTMLDOMPropertyConfig);
|
|
ReactInjection.DOMProperty.injectDOMPropertyConfig(SVGDOMPropertyConfig);
|
|
|
|
ReactInjection.EmptyComponent.injectEmptyComponent('noscript');
|
|
|
|
ReactInjection.Updates.injectReconcileTransaction(ReactReconcileTransaction);
|
|
ReactInjection.Updates.injectBatchingStrategy(ReactDefaultBatchingStrategy);
|
|
|
|
ReactInjection.RootIndex.injectCreateReactRootIndex(ExecutionEnvironment.canUseDOM ? ClientReactRootIndex.createReactRootIndex : ServerReactRootIndex.createReactRootIndex);
|
|
|
|
ReactInjection.Component.injectEnvironment(ReactComponentBrowserEnvironment);
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var url = ExecutionEnvironment.canUseDOM && window.location.href || '';
|
|
if (/[?&]react_perf\b/.test(url)) {
|
|
var ReactDefaultPerf = require('./ReactDefaultPerf');
|
|
ReactDefaultPerf.start();
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
inject: inject
|
|
};
|
|
}).call(this,require('_process'))
|
|
},{"./BeforeInputEventPlugin":137,"./ChangeEventPlugin":141,"./ClientReactRootIndex":142,"./DefaultEventPluginOrder":147,"./EnterLeaveEventPlugin":148,"./HTMLDOMPropertyConfig":155,"./ReactBrowserComponentMixin":160,"./ReactComponentBrowserEnvironment":166,"./ReactDOMComponent":172,"./ReactDOMTextComponent":181,"./ReactDefaultBatchingStrategy":183,"./ReactDefaultPerf":185,"./ReactEventListener":193,"./ReactInjection":194,"./ReactInstanceHandles":196,"./ReactMount":200,"./ReactReconcileTransaction":210,"./SVGDOMPropertyConfig":220,"./SelectEventPlugin":221,"./ServerReactRootIndex":222,"./SimpleEventPlugin":223,"_process":133,"fbjs/lib/ExecutionEnvironment":58}],185:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDefaultPerf
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var DOMProperty = require('./DOMProperty');
|
|
var ReactDefaultPerfAnalysis = require('./ReactDefaultPerfAnalysis');
|
|
var ReactMount = require('./ReactMount');
|
|
var ReactPerf = require('./ReactPerf');
|
|
|
|
var performanceNow = require('fbjs/lib/performanceNow');
|
|
|
|
function roundFloat(val) {
|
|
return Math.floor(val * 100) / 100;
|
|
}
|
|
|
|
function addValue(obj, key, val) {
|
|
obj[key] = (obj[key] || 0) + val;
|
|
}
|
|
|
|
var ReactDefaultPerf = {
|
|
_allMeasurements: [], // last item in the list is the current one
|
|
_mountStack: [0],
|
|
_injected: false,
|
|
|
|
start: function () {
|
|
if (!ReactDefaultPerf._injected) {
|
|
ReactPerf.injection.injectMeasure(ReactDefaultPerf.measure);
|
|
}
|
|
|
|
ReactDefaultPerf._allMeasurements.length = 0;
|
|
ReactPerf.enableMeasure = true;
|
|
},
|
|
|
|
stop: function () {
|
|
ReactPerf.enableMeasure = false;
|
|
},
|
|
|
|
getLastMeasurements: function () {
|
|
return ReactDefaultPerf._allMeasurements;
|
|
},
|
|
|
|
printExclusive: function (measurements) {
|
|
measurements = measurements || ReactDefaultPerf._allMeasurements;
|
|
var summary = ReactDefaultPerfAnalysis.getExclusiveSummary(measurements);
|
|
console.table(summary.map(function (item) {
|
|
return {
|
|
'Component class name': item.componentName,
|
|
'Total inclusive time (ms)': roundFloat(item.inclusive),
|
|
'Exclusive mount time (ms)': roundFloat(item.exclusive),
|
|
'Exclusive render time (ms)': roundFloat(item.render),
|
|
'Mount time per instance (ms)': roundFloat(item.exclusive / item.count),
|
|
'Render time per instance (ms)': roundFloat(item.render / item.count),
|
|
'Instances': item.count
|
|
};
|
|
}));
|
|
// TODO: ReactDefaultPerfAnalysis.getTotalTime() does not return the correct
|
|
// number.
|
|
},
|
|
|
|
printInclusive: function (measurements) {
|
|
measurements = measurements || ReactDefaultPerf._allMeasurements;
|
|
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(measurements);
|
|
console.table(summary.map(function (item) {
|
|
return {
|
|
'Owner > component': item.componentName,
|
|
'Inclusive time (ms)': roundFloat(item.time),
|
|
'Instances': item.count
|
|
};
|
|
}));
|
|
console.log('Total time:', ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms');
|
|
},
|
|
|
|
getMeasurementsSummaryMap: function (measurements) {
|
|
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(measurements, true);
|
|
return summary.map(function (item) {
|
|
return {
|
|
'Owner > component': item.componentName,
|
|
'Wasted time (ms)': item.time,
|
|
'Instances': item.count
|
|
};
|
|
});
|
|
},
|
|
|
|
printWasted: function (measurements) {
|
|
measurements = measurements || ReactDefaultPerf._allMeasurements;
|
|
console.table(ReactDefaultPerf.getMeasurementsSummaryMap(measurements));
|
|
console.log('Total time:', ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms');
|
|
},
|
|
|
|
printDOM: function (measurements) {
|
|
measurements = measurements || ReactDefaultPerf._allMeasurements;
|
|
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
|
|
console.table(summary.map(function (item) {
|
|
var result = {};
|
|
result[DOMProperty.ID_ATTRIBUTE_NAME] = item.id;
|
|
result.type = item.type;
|
|
result.args = JSON.stringify(item.args);
|
|
return result;
|
|
}));
|
|
console.log('Total time:', ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms');
|
|
},
|
|
|
|
_recordWrite: function (id, fnName, totalTime, args) {
|
|
// TODO: totalTime isn't that useful since it doesn't count paints/reflows
|
|
var writes = ReactDefaultPerf._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1].writes;
|
|
writes[id] = writes[id] || [];
|
|
writes[id].push({
|
|
type: fnName,
|
|
time: totalTime,
|
|
args: args
|
|
});
|
|
},
|
|
|
|
measure: function (moduleName, fnName, func) {
|
|
return function () {
|
|
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
|
|
args[_key] = arguments[_key];
|
|
}
|
|
|
|
var totalTime;
|
|
var rv;
|
|
var start;
|
|
|
|
if (fnName === '_renderNewRootComponent' || fnName === 'flushBatchedUpdates') {
|
|
// A "measurement" is a set of metrics recorded for each flush. We want
|
|
// to group the metrics for a given flush together so we can look at the
|
|
// components that rendered and the DOM operations that actually
|
|
// happened to determine the amount of "wasted work" performed.
|
|
ReactDefaultPerf._allMeasurements.push({
|
|
exclusive: {},
|
|
inclusive: {},
|
|
render: {},
|
|
counts: {},
|
|
writes: {},
|
|
displayNames: {},
|
|
totalTime: 0,
|
|
created: {}
|
|
});
|
|
start = performanceNow();
|
|
rv = func.apply(this, args);
|
|
ReactDefaultPerf._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1].totalTime = performanceNow() - start;
|
|
return rv;
|
|
} else if (fnName === '_mountImageIntoNode' || moduleName === 'ReactBrowserEventEmitter' || moduleName === 'ReactDOMIDOperations' || moduleName === 'CSSPropertyOperations' || moduleName === 'DOMChildrenOperations' || moduleName === 'DOMPropertyOperations') {
|
|
start = performanceNow();
|
|
rv = func.apply(this, args);
|
|
totalTime = performanceNow() - start;
|
|
|
|
if (fnName === '_mountImageIntoNode') {
|
|
var mountID = ReactMount.getID(args[1]);
|
|
ReactDefaultPerf._recordWrite(mountID, fnName, totalTime, args[0]);
|
|
} else if (fnName === 'dangerouslyProcessChildrenUpdates') {
|
|
// special format
|
|
args[0].forEach(function (update) {
|
|
var writeArgs = {};
|
|
if (update.fromIndex !== null) {
|
|
writeArgs.fromIndex = update.fromIndex;
|
|
}
|
|
if (update.toIndex !== null) {
|
|
writeArgs.toIndex = update.toIndex;
|
|
}
|
|
if (update.textContent !== null) {
|
|
writeArgs.textContent = update.textContent;
|
|
}
|
|
if (update.markupIndex !== null) {
|
|
writeArgs.markup = args[1][update.markupIndex];
|
|
}
|
|
ReactDefaultPerf._recordWrite(update.parentID, update.type, totalTime, writeArgs);
|
|
});
|
|
} else {
|
|
// basic format
|
|
var id = args[0];
|
|
if (typeof id === 'object') {
|
|
id = ReactMount.getID(args[0]);
|
|
}
|
|
ReactDefaultPerf._recordWrite(id, fnName, totalTime, Array.prototype.slice.call(args, 1));
|
|
}
|
|
return rv;
|
|
} else if (moduleName === 'ReactCompositeComponent' && (fnName === 'mountComponent' || fnName === 'updateComponent' || // TODO: receiveComponent()?
|
|
fnName === '_renderValidatedComponent')) {
|
|
|
|
if (this._currentElement.type === ReactMount.TopLevelWrapper) {
|
|
return func.apply(this, args);
|
|
}
|
|
|
|
var rootNodeID = fnName === 'mountComponent' ? args[0] : this._rootNodeID;
|
|
var isRender = fnName === '_renderValidatedComponent';
|
|
var isMount = fnName === 'mountComponent';
|
|
|
|
var mountStack = ReactDefaultPerf._mountStack;
|
|
var entry = ReactDefaultPerf._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1];
|
|
|
|
if (isRender) {
|
|
addValue(entry.counts, rootNodeID, 1);
|
|
} else if (isMount) {
|
|
entry.created[rootNodeID] = true;
|
|
mountStack.push(0);
|
|
}
|
|
|
|
start = performanceNow();
|
|
rv = func.apply(this, args);
|
|
totalTime = performanceNow() - start;
|
|
|
|
if (isRender) {
|
|
addValue(entry.render, rootNodeID, totalTime);
|
|
} else if (isMount) {
|
|
var subMountTime = mountStack.pop();
|
|
mountStack[mountStack.length - 1] += totalTime;
|
|
addValue(entry.exclusive, rootNodeID, totalTime - subMountTime);
|
|
addValue(entry.inclusive, rootNodeID, totalTime);
|
|
} else {
|
|
addValue(entry.inclusive, rootNodeID, totalTime);
|
|
}
|
|
|
|
entry.displayNames[rootNodeID] = {
|
|
current: this.getName(),
|
|
owner: this._currentElement._owner ? this._currentElement._owner.getName() : '<root>'
|
|
};
|
|
|
|
return rv;
|
|
} else {
|
|
return func.apply(this, args);
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
module.exports = ReactDefaultPerf;
|
|
},{"./DOMProperty":144,"./ReactDefaultPerfAnalysis":186,"./ReactMount":200,"./ReactPerf":206,"fbjs/lib/performanceNow":80}],186:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactDefaultPerfAnalysis
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var assign = require('./Object.assign');
|
|
|
|
// Don't try to save users less than 1.2ms (a number I made up)
|
|
var DONT_CARE_THRESHOLD = 1.2;
|
|
var DOM_OPERATION_TYPES = {
|
|
'_mountImageIntoNode': 'set innerHTML',
|
|
INSERT_MARKUP: 'set innerHTML',
|
|
MOVE_EXISTING: 'move',
|
|
REMOVE_NODE: 'remove',
|
|
SET_MARKUP: 'set innerHTML',
|
|
TEXT_CONTENT: 'set textContent',
|
|
'setValueForProperty': 'update attribute',
|
|
'setValueForAttribute': 'update attribute',
|
|
'deleteValueForProperty': 'remove attribute',
|
|
'setValueForStyles': 'update styles',
|
|
'replaceNodeWithMarkup': 'replace',
|
|
'updateTextContent': 'set textContent'
|
|
};
|
|
|
|
function getTotalTime(measurements) {
|
|
// TODO: return number of DOM ops? could be misleading.
|
|
// TODO: measure dropped frames after reconcile?
|
|
// TODO: log total time of each reconcile and the top-level component
|
|
// class that triggered it.
|
|
var totalTime = 0;
|
|
for (var i = 0; i < measurements.length; i++) {
|
|
var measurement = measurements[i];
|
|
totalTime += measurement.totalTime;
|
|
}
|
|
return totalTime;
|
|
}
|
|
|
|
function getDOMSummary(measurements) {
|
|
var items = [];
|
|
measurements.forEach(function (measurement) {
|
|
Object.keys(measurement.writes).forEach(function (id) {
|
|
measurement.writes[id].forEach(function (write) {
|
|
items.push({
|
|
id: id,
|
|
type: DOM_OPERATION_TYPES[write.type] || write.type,
|
|
args: write.args
|
|
});
|
|
});
|
|
});
|
|
});
|
|
return items;
|
|
}
|
|
|
|
function getExclusiveSummary(measurements) {
|
|
var candidates = {};
|
|
var displayName;
|
|
|
|
for (var i = 0; i < measurements.length; i++) {
|
|
var measurement = measurements[i];
|
|
var allIDs = assign({}, measurement.exclusive, measurement.inclusive);
|
|
|
|
for (var id in allIDs) {
|
|
displayName = measurement.displayNames[id].current;
|
|
|
|
candidates[displayName] = candidates[displayName] || {
|
|
componentName: displayName,
|
|
inclusive: 0,
|
|
exclusive: 0,
|
|
render: 0,
|
|
count: 0
|
|
};
|
|
if (measurement.render[id]) {
|
|
candidates[displayName].render += measurement.render[id];
|
|
}
|
|
if (measurement.exclusive[id]) {
|
|
candidates[displayName].exclusive += measurement.exclusive[id];
|
|
}
|
|
if (measurement.inclusive[id]) {
|
|
candidates[displayName].inclusive += measurement.inclusive[id];
|
|
}
|
|
if (measurement.counts[id]) {
|
|
candidates[displayName].count += measurement.counts[id];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now make a sorted array with the results.
|
|
var arr = [];
|
|
for (displayName in candidates) {
|
|
if (candidates[displayName].exclusive >= DONT_CARE_THRESHOLD) {
|
|
arr.push(candidates[displayName]);
|
|
}
|
|
}
|
|
|
|
arr.sort(function (a, b) {
|
|
return b.exclusive - a.exclusive;
|
|
});
|
|
|
|
return arr;
|
|
}
|
|
|
|
function getInclusiveSummary(measurements, onlyClean) {
|
|
var candidates = {};
|
|
var inclusiveKey;
|
|
|
|
for (var i = 0; i < measurements.length; i++) {
|
|
var measurement = measurements[i];
|
|
var allIDs = assign({}, measurement.exclusive, measurement.inclusive);
|
|
var cleanComponents;
|
|
|
|
if (onlyClean) {
|
|
cleanComponents = getUnchangedComponents(measurement);
|
|
}
|
|
|
|
for (var id in allIDs) {
|
|
if (onlyClean && !cleanComponents[id]) {
|
|
continue;
|
|
}
|
|
|
|
var displayName = measurement.displayNames[id];
|
|
|
|
// Inclusive time is not useful for many components without knowing where
|
|
// they are instantiated. So we aggregate inclusive time with both the
|
|
// owner and current displayName as the key.
|
|
inclusiveKey = displayName.owner + ' > ' + displayName.current;
|
|
|
|
candidates[inclusiveKey] = candidates[inclusiveKey] || {
|
|
componentName: inclusiveKey,
|
|
time: 0,
|
|
count: 0
|
|
};
|
|
|
|
if (measurement.inclusive[id]) {
|
|
candidates[inclusiveKey].time += measurement.inclusive[id];
|
|
}
|
|
if (measurement.counts[id]) {
|
|
candidates[inclusiveKey].count += measurement.counts[id];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now make a sorted array with the results.
|
|
var arr = [];
|
|
for (inclusiveKey in candidates) {
|
|
if (candidates[inclusiveKey].time >= DONT_CARE_THRESHOLD) {
|
|
arr.push(candidates[inclusiveKey]);
|
|
}
|
|
}
|
|
|
|
arr.sort(function (a, b) {
|
|
return b.time - a.time;
|
|
});
|
|
|
|
return arr;
|
|
}
|
|
|
|
function getUnchangedComponents(measurement) {
|
|
// For a given reconcile, look at which components did not actually
|
|
// render anything to the DOM and return a mapping of their ID to
|
|
// the amount of time it took to render the entire subtree.
|
|
var cleanComponents = {};
|
|
var dirtyLeafIDs = Object.keys(measurement.writes);
|
|
var allIDs = assign({}, measurement.exclusive, measurement.inclusive);
|
|
|
|
for (var id in allIDs) {
|
|
var isDirty = false;
|
|
// For each component that rendered, see if a component that triggered
|
|
// a DOM op is in its subtree.
|
|
for (var i = 0; i < dirtyLeafIDs.length; i++) {
|
|
if (dirtyLeafIDs[i].indexOf(id) === 0) {
|
|
isDirty = true;
|
|
break;
|
|
}
|
|
}
|
|
// check if component newly created
|
|
if (measurement.created[id]) {
|
|
isDirty = true;
|
|
}
|
|
if (!isDirty && measurement.counts[id] > 0) {
|
|
cleanComponents[id] = true;
|
|
}
|
|
}
|
|
return cleanComponents;
|
|
}
|
|
|
|
var ReactDefaultPerfAnalysis = {
|
|
getExclusiveSummary: getExclusiveSummary,
|
|
getInclusiveSummary: getInclusiveSummary,
|
|
getDOMSummary: getDOMSummary,
|
|
getTotalTime: getTotalTime
|
|
};
|
|
|
|
module.exports = ReactDefaultPerfAnalysis;
|
|
},{"./Object.assign":157}],187:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2014-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactElement
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactCurrentOwner = require('./ReactCurrentOwner');
|
|
|
|
var assign = require('./Object.assign');
|
|
var canDefineProperty = require('./canDefineProperty');
|
|
|
|
// The Symbol used to tag the ReactElement type. If there is no native Symbol
|
|
// nor polyfill, then a plain number is used for performance.
|
|
var REACT_ELEMENT_TYPE = typeof Symbol === 'function' && Symbol['for'] && Symbol['for']('react.element') || 0xeac7;
|
|
|
|
var RESERVED_PROPS = {
|
|
key: true,
|
|
ref: true,
|
|
__self: true,
|
|
__source: true
|
|
};
|
|
|
|
/**
|
|
* Base constructor for all React elements. This is only used to make this
|
|
* work with a dynamic instanceof check. Nothing should live on this prototype.
|
|
*
|
|
* @param {*} type
|
|
* @param {*} key
|
|
* @param {string|object} ref
|
|
* @param {*} self A *temporary* helper to detect places where `this` is
|
|
* different from the `owner` when React.createElement is called, so that we
|
|
* can warn. We want to get rid of owner and replace string `ref`s with arrow
|
|
* functions, and as long as `this` and owner are the same, there will be no
|
|
* change in behavior.
|
|
* @param {*} source An annotation object (added by a transpiler or otherwise)
|
|
* indicating filename, line number, and/or other information.
|
|
* @param {*} owner
|
|
* @param {*} props
|
|
* @internal
|
|
*/
|
|
var ReactElement = function (type, key, ref, self, source, owner, props) {
|
|
var element = {
|
|
// This tag allow us to uniquely identify this as a React Element
|
|
$$typeof: REACT_ELEMENT_TYPE,
|
|
|
|
// Built-in properties that belong on the element
|
|
type: type,
|
|
key: key,
|
|
ref: ref,
|
|
props: props,
|
|
|
|
// Record the component responsible for creating this element.
|
|
_owner: owner
|
|
};
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// The validation flag is currently mutative. We put it on
|
|
// an external backing store so that we can freeze the whole object.
|
|
// This can be replaced with a WeakMap once they are implemented in
|
|
// commonly used development environments.
|
|
element._store = {};
|
|
|
|
// To make comparing ReactElements easier for testing purposes, we make
|
|
// the validation flag non-enumerable (where possible, which should
|
|
// include every environment we run tests in), so the test framework
|
|
// ignores it.
|
|
if (canDefineProperty) {
|
|
Object.defineProperty(element._store, 'validated', {
|
|
configurable: false,
|
|
enumerable: false,
|
|
writable: true,
|
|
value: false
|
|
});
|
|
// self and source are DEV only properties.
|
|
Object.defineProperty(element, '_self', {
|
|
configurable: false,
|
|
enumerable: false,
|
|
writable: false,
|
|
value: self
|
|
});
|
|
// Two elements created in two different places should be considered
|
|
// equal for testing purposes and therefore we hide it from enumeration.
|
|
Object.defineProperty(element, '_source', {
|
|
configurable: false,
|
|
enumerable: false,
|
|
writable: false,
|
|
value: source
|
|
});
|
|
} else {
|
|
element._store.validated = false;
|
|
element._self = self;
|
|
element._source = source;
|
|
}
|
|
Object.freeze(element.props);
|
|
Object.freeze(element);
|
|
}
|
|
|
|
return element;
|
|
};
|
|
|
|
ReactElement.createElement = function (type, config, children) {
|
|
var propName;
|
|
|
|
// Reserved names are extracted
|
|
var props = {};
|
|
|
|
var key = null;
|
|
var ref = null;
|
|
var self = null;
|
|
var source = null;
|
|
|
|
if (config != null) {
|
|
ref = config.ref === undefined ? null : config.ref;
|
|
key = config.key === undefined ? null : '' + config.key;
|
|
self = config.__self === undefined ? null : config.__self;
|
|
source = config.__source === undefined ? null : config.__source;
|
|
// Remaining properties are added to a new props object
|
|
for (propName in config) {
|
|
if (config.hasOwnProperty(propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
|
|
props[propName] = config[propName];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Children can be more than one argument, and those are transferred onto
|
|
// the newly allocated props object.
|
|
var childrenLength = arguments.length - 2;
|
|
if (childrenLength === 1) {
|
|
props.children = children;
|
|
} else if (childrenLength > 1) {
|
|
var childArray = Array(childrenLength);
|
|
for (var i = 0; i < childrenLength; i++) {
|
|
childArray[i] = arguments[i + 2];
|
|
}
|
|
props.children = childArray;
|
|
}
|
|
|
|
// Resolve default props
|
|
if (type && type.defaultProps) {
|
|
var defaultProps = type.defaultProps;
|
|
for (propName in defaultProps) {
|
|
if (typeof props[propName] === 'undefined') {
|
|
props[propName] = defaultProps[propName];
|
|
}
|
|
}
|
|
}
|
|
|
|
return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
|
|
};
|
|
|
|
ReactElement.createFactory = function (type) {
|
|
var factory = ReactElement.createElement.bind(null, type);
|
|
// Expose the type on the factory and the prototype so that it can be
|
|
// easily accessed on elements. E.g. `<Foo />.type === Foo`.
|
|
// This should not be named `constructor` since this may not be the function
|
|
// that created the element, and it may not even be a constructor.
|
|
// Legacy hook TODO: Warn if this is accessed
|
|
factory.type = type;
|
|
return factory;
|
|
};
|
|
|
|
ReactElement.cloneAndReplaceKey = function (oldElement, newKey) {
|
|
var newElement = ReactElement(oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props);
|
|
|
|
return newElement;
|
|
};
|
|
|
|
ReactElement.cloneAndReplaceProps = function (oldElement, newProps) {
|
|
var newElement = ReactElement(oldElement.type, oldElement.key, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, newProps);
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// If the key on the original is valid, then the clone is valid
|
|
newElement._store.validated = oldElement._store.validated;
|
|
}
|
|
|
|
return newElement;
|
|
};
|
|
|
|
ReactElement.cloneElement = function (element, config, children) {
|
|
var propName;
|
|
|
|
// Original props are copied
|
|
var props = assign({}, element.props);
|
|
|
|
// Reserved names are extracted
|
|
var key = element.key;
|
|
var ref = element.ref;
|
|
// Self is preserved since the owner is preserved.
|
|
var self = element._self;
|
|
// Source is preserved since cloneElement is unlikely to be targeted by a
|
|
// transpiler, and the original source is probably a better indicator of the
|
|
// true owner.
|
|
var source = element._source;
|
|
|
|
// Owner will be preserved, unless ref is overridden
|
|
var owner = element._owner;
|
|
|
|
if (config != null) {
|
|
if (config.ref !== undefined) {
|
|
// Silently steal the ref from the parent.
|
|
ref = config.ref;
|
|
owner = ReactCurrentOwner.current;
|
|
}
|
|
if (config.key !== undefined) {
|
|
key = '' + config.key;
|
|
}
|
|
// Remaining properties override existing props
|
|
for (propName in config) {
|
|
if (config.hasOwnProperty(propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
|
|
props[propName] = config[propName];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Children can be more than one argument, and those are transferred onto
|
|
// the newly allocated props object.
|
|
var childrenLength = arguments.length - 2;
|
|
if (childrenLength === 1) {
|
|
props.children = children;
|
|
} else if (childrenLength > 1) {
|
|
var childArray = Array(childrenLength);
|
|
for (var i = 0; i < childrenLength; i++) {
|
|
childArray[i] = arguments[i + 2];
|
|
}
|
|
props.children = childArray;
|
|
}
|
|
|
|
return ReactElement(element.type, key, ref, self, source, owner, props);
|
|
};
|
|
|
|
/**
|
|
* @param {?object} object
|
|
* @return {boolean} True if `object` is a valid component.
|
|
* @final
|
|
*/
|
|
ReactElement.isValidElement = function (object) {
|
|
return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
|
|
};
|
|
|
|
module.exports = ReactElement;
|
|
}).call(this,require('_process'))
|
|
},{"./Object.assign":157,"./ReactCurrentOwner":169,"./canDefineProperty":239,"_process":133}],188:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2014-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactElementValidator
|
|
*/
|
|
|
|
/**
|
|
* ReactElementValidator provides a wrapper around a element factory
|
|
* which validates the props passed to the element. This is intended to be
|
|
* used only in DEV and could be replaced by a static type checker for languages
|
|
* that support it.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactElement = require('./ReactElement');
|
|
var ReactPropTypeLocations = require('./ReactPropTypeLocations');
|
|
var ReactPropTypeLocationNames = require('./ReactPropTypeLocationNames');
|
|
var ReactCurrentOwner = require('./ReactCurrentOwner');
|
|
|
|
var canDefineProperty = require('./canDefineProperty');
|
|
var getIteratorFn = require('./getIteratorFn');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
function getDeclarationErrorAddendum() {
|
|
if (ReactCurrentOwner.current) {
|
|
var name = ReactCurrentOwner.current.getName();
|
|
if (name) {
|
|
return ' Check the render method of `' + name + '`.';
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Warn if there's no key explicitly set on dynamic arrays of children or
|
|
* object keys are not valid. This allows us to keep track of children between
|
|
* updates.
|
|
*/
|
|
var ownerHasKeyUseWarning = {};
|
|
|
|
var loggedTypeFailures = {};
|
|
|
|
/**
|
|
* Warn if the element doesn't have an explicit key assigned to it.
|
|
* This element is in an array. The array could grow and shrink or be
|
|
* reordered. All children that haven't already been validated are required to
|
|
* have a "key" property assigned to it.
|
|
*
|
|
* @internal
|
|
* @param {ReactElement} element Element that requires a key.
|
|
* @param {*} parentType element's parent's type.
|
|
*/
|
|
function validateExplicitKey(element, parentType) {
|
|
if (!element._store || element._store.validated || element.key != null) {
|
|
return;
|
|
}
|
|
element._store.validated = true;
|
|
|
|
var addenda = getAddendaForKeyUse('uniqueKey', element, parentType);
|
|
if (addenda === null) {
|
|
// we already showed the warning
|
|
return;
|
|
}
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Each child in an array or iterator should have a unique "key" prop.' + '%s%s%s', addenda.parentOrOwner || '', addenda.childOwner || '', addenda.url || '') : undefined;
|
|
}
|
|
|
|
/**
|
|
* Shared warning and monitoring code for the key warnings.
|
|
*
|
|
* @internal
|
|
* @param {string} messageType A key used for de-duping warnings.
|
|
* @param {ReactElement} element Component that requires a key.
|
|
* @param {*} parentType element's parent's type.
|
|
* @returns {?object} A set of addenda to use in the warning message, or null
|
|
* if the warning has already been shown before (and shouldn't be shown again).
|
|
*/
|
|
function getAddendaForKeyUse(messageType, element, parentType) {
|
|
var addendum = getDeclarationErrorAddendum();
|
|
if (!addendum) {
|
|
var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name;
|
|
if (parentName) {
|
|
addendum = ' Check the top-level render call using <' + parentName + '>.';
|
|
}
|
|
}
|
|
|
|
var memoizer = ownerHasKeyUseWarning[messageType] || (ownerHasKeyUseWarning[messageType] = {});
|
|
if (memoizer[addendum]) {
|
|
return null;
|
|
}
|
|
memoizer[addendum] = true;
|
|
|
|
var addenda = {
|
|
parentOrOwner: addendum,
|
|
url: ' See https://fb.me/react-warning-keys for more information.',
|
|
childOwner: null
|
|
};
|
|
|
|
// Usually the current owner is the offender, but if it accepts children as a
|
|
// property, it may be the creator of the child that's responsible for
|
|
// assigning it a key.
|
|
if (element && element._owner && element._owner !== ReactCurrentOwner.current) {
|
|
// Give the component that originally created this child.
|
|
addenda.childOwner = ' It was passed a child from ' + element._owner.getName() + '.';
|
|
}
|
|
|
|
return addenda;
|
|
}
|
|
|
|
/**
|
|
* Ensure that every element either is passed in a static location, in an
|
|
* array with an explicit keys property defined, or in an object literal
|
|
* with valid key property.
|
|
*
|
|
* @internal
|
|
* @param {ReactNode} node Statically passed child of any type.
|
|
* @param {*} parentType node's parent's type.
|
|
*/
|
|
function validateChildKeys(node, parentType) {
|
|
if (typeof node !== 'object') {
|
|
return;
|
|
}
|
|
if (Array.isArray(node)) {
|
|
for (var i = 0; i < node.length; i++) {
|
|
var child = node[i];
|
|
if (ReactElement.isValidElement(child)) {
|
|
validateExplicitKey(child, parentType);
|
|
}
|
|
}
|
|
} else if (ReactElement.isValidElement(node)) {
|
|
// This element was passed in a valid location.
|
|
if (node._store) {
|
|
node._store.validated = true;
|
|
}
|
|
} else if (node) {
|
|
var iteratorFn = getIteratorFn(node);
|
|
// Entry iterators provide implicit keys.
|
|
if (iteratorFn) {
|
|
if (iteratorFn !== node.entries) {
|
|
var iterator = iteratorFn.call(node);
|
|
var step;
|
|
while (!(step = iterator.next()).done) {
|
|
if (ReactElement.isValidElement(step.value)) {
|
|
validateExplicitKey(step.value, parentType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Assert that the props are valid
|
|
*
|
|
* @param {string} componentName Name of the component for error messages.
|
|
* @param {object} propTypes Map of prop name to a ReactPropType
|
|
* @param {object} props
|
|
* @param {string} location e.g. "prop", "context", "child context"
|
|
* @private
|
|
*/
|
|
function checkPropTypes(componentName, propTypes, props, location) {
|
|
for (var propName in propTypes) {
|
|
if (propTypes.hasOwnProperty(propName)) {
|
|
var error;
|
|
// Prop type validation may throw. In case they do, we don't want to
|
|
// fail the render phase where it didn't fail before. So we log it.
|
|
// After these have been cleaned up, we'll let them throw.
|
|
try {
|
|
// This is intentionally an invariant that gets caught. It's the same
|
|
// behavior as without this statement except with a better message.
|
|
!(typeof propTypes[propName] === 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s: %s type `%s` is invalid; it must be a function, usually from ' + 'React.PropTypes.', componentName || 'React class', ReactPropTypeLocationNames[location], propName) : invariant(false) : undefined;
|
|
error = propTypes[propName](props, propName, componentName, location);
|
|
} catch (ex) {
|
|
error = ex;
|
|
}
|
|
process.env.NODE_ENV !== 'production' ? warning(!error || error instanceof Error, '%s: type specification of %s `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', ReactPropTypeLocationNames[location], propName, typeof error) : undefined;
|
|
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
|
|
// Only monitor this failure once because there tends to be a lot of the
|
|
// same error.
|
|
loggedTypeFailures[error.message] = true;
|
|
|
|
var addendum = getDeclarationErrorAddendum();
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Failed propType: %s%s', error.message, addendum) : undefined;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given an element, validate that its props follow the propTypes definition,
|
|
* provided by the type.
|
|
*
|
|
* @param {ReactElement} element
|
|
*/
|
|
function validatePropTypes(element) {
|
|
var componentClass = element.type;
|
|
if (typeof componentClass !== 'function') {
|
|
return;
|
|
}
|
|
var name = componentClass.displayName || componentClass.name;
|
|
if (componentClass.propTypes) {
|
|
checkPropTypes(name, componentClass.propTypes, element.props, ReactPropTypeLocations.prop);
|
|
}
|
|
if (typeof componentClass.getDefaultProps === 'function') {
|
|
process.env.NODE_ENV !== 'production' ? warning(componentClass.getDefaultProps.isReactClassApproved, 'getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.') : undefined;
|
|
}
|
|
}
|
|
|
|
var ReactElementValidator = {
|
|
|
|
createElement: function (type, props, children) {
|
|
var validType = typeof type === 'string' || typeof type === 'function';
|
|
// We warn in this case but don't throw. We expect the element creation to
|
|
// succeed and there will likely be errors in render.
|
|
process.env.NODE_ENV !== 'production' ? warning(validType, 'React.createElement: type should not be null, undefined, boolean, or ' + 'number. It should be a string (for DOM elements) or a ReactClass ' + '(for composite components).%s', getDeclarationErrorAddendum()) : undefined;
|
|
|
|
var element = ReactElement.createElement.apply(this, arguments);
|
|
|
|
// The result can be nullish if a mock or a custom function is used.
|
|
// TODO: Drop this when these are no longer allowed as the type argument.
|
|
if (element == null) {
|
|
return element;
|
|
}
|
|
|
|
// Skip key warning if the type isn't valid since our key validation logic
|
|
// doesn't expect a non-string/function type and can throw confusing errors.
|
|
// We don't want exception behavior to differ between dev and prod.
|
|
// (Rendering will throw with a helpful message and as soon as the type is
|
|
// fixed, the key warnings will appear.)
|
|
if (validType) {
|
|
for (var i = 2; i < arguments.length; i++) {
|
|
validateChildKeys(arguments[i], type);
|
|
}
|
|
}
|
|
|
|
validatePropTypes(element);
|
|
|
|
return element;
|
|
},
|
|
|
|
createFactory: function (type) {
|
|
var validatedFactory = ReactElementValidator.createElement.bind(null, type);
|
|
// Legacy hook TODO: Warn if this is accessed
|
|
validatedFactory.type = type;
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
if (canDefineProperty) {
|
|
Object.defineProperty(validatedFactory, 'type', {
|
|
enumerable: false,
|
|
get: function () {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Factory.type is deprecated. Access the class directly ' + 'before passing it to createFactory.') : undefined;
|
|
Object.defineProperty(this, 'type', {
|
|
value: type
|
|
});
|
|
return type;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
return validatedFactory;
|
|
},
|
|
|
|
cloneElement: function (element, props, children) {
|
|
var newElement = ReactElement.cloneElement.apply(this, arguments);
|
|
for (var i = 2; i < arguments.length; i++) {
|
|
validateChildKeys(arguments[i], newElement.type);
|
|
}
|
|
validatePropTypes(newElement);
|
|
return newElement;
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactElementValidator;
|
|
}).call(this,require('_process'))
|
|
},{"./ReactCurrentOwner":169,"./ReactElement":187,"./ReactPropTypeLocationNames":207,"./ReactPropTypeLocations":208,"./canDefineProperty":239,"./getIteratorFn":250,"_process":133,"fbjs/lib/invariant":72,"fbjs/lib/warning":83}],189:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2014-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactEmptyComponent
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactElement = require('./ReactElement');
|
|
var ReactEmptyComponentRegistry = require('./ReactEmptyComponentRegistry');
|
|
var ReactReconciler = require('./ReactReconciler');
|
|
|
|
var assign = require('./Object.assign');
|
|
|
|
var placeholderElement;
|
|
|
|
var ReactEmptyComponentInjection = {
|
|
injectEmptyComponent: function (component) {
|
|
placeholderElement = ReactElement.createElement(component);
|
|
}
|
|
};
|
|
|
|
function registerNullComponentID() {
|
|
ReactEmptyComponentRegistry.registerNullComponentID(this._rootNodeID);
|
|
}
|
|
|
|
var ReactEmptyComponent = function (instantiate) {
|
|
this._currentElement = null;
|
|
this._rootNodeID = null;
|
|
this._renderedComponent = instantiate(placeholderElement);
|
|
};
|
|
assign(ReactEmptyComponent.prototype, {
|
|
construct: function (element) {},
|
|
mountComponent: function (rootID, transaction, context) {
|
|
transaction.getReactMountReady().enqueue(registerNullComponentID, this);
|
|
this._rootNodeID = rootID;
|
|
return ReactReconciler.mountComponent(this._renderedComponent, rootID, transaction, context);
|
|
},
|
|
receiveComponent: function () {},
|
|
unmountComponent: function (rootID, transaction, context) {
|
|
ReactReconciler.unmountComponent(this._renderedComponent);
|
|
ReactEmptyComponentRegistry.deregisterNullComponentID(this._rootNodeID);
|
|
this._rootNodeID = null;
|
|
this._renderedComponent = null;
|
|
}
|
|
});
|
|
|
|
ReactEmptyComponent.injection = ReactEmptyComponentInjection;
|
|
|
|
module.exports = ReactEmptyComponent;
|
|
},{"./Object.assign":157,"./ReactElement":187,"./ReactEmptyComponentRegistry":190,"./ReactReconciler":211}],190:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2014-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactEmptyComponentRegistry
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
// This registry keeps track of the React IDs of the components that rendered to
|
|
// `null` (in reality a placeholder such as `noscript`)
|
|
var nullComponentIDsRegistry = {};
|
|
|
|
/**
|
|
* @param {string} id Component's `_rootNodeID`.
|
|
* @return {boolean} True if the component is rendered to null.
|
|
*/
|
|
function isNullComponentID(id) {
|
|
return !!nullComponentIDsRegistry[id];
|
|
}
|
|
|
|
/**
|
|
* Mark the component as having rendered to null.
|
|
* @param {string} id Component's `_rootNodeID`.
|
|
*/
|
|
function registerNullComponentID(id) {
|
|
nullComponentIDsRegistry[id] = true;
|
|
}
|
|
|
|
/**
|
|
* Unmark the component as having rendered to null: it renders to something now.
|
|
* @param {string} id Component's `_rootNodeID`.
|
|
*/
|
|
function deregisterNullComponentID(id) {
|
|
delete nullComponentIDsRegistry[id];
|
|
}
|
|
|
|
var ReactEmptyComponentRegistry = {
|
|
isNullComponentID: isNullComponentID,
|
|
registerNullComponentID: registerNullComponentID,
|
|
deregisterNullComponentID: deregisterNullComponentID
|
|
};
|
|
|
|
module.exports = ReactEmptyComponentRegistry;
|
|
},{}],191:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactErrorUtils
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var caughtError = null;
|
|
|
|
/**
|
|
* Call a function while guarding against errors that happens within it.
|
|
*
|
|
* @param {?String} name of the guard to use for logging or debugging
|
|
* @param {Function} func The function to invoke
|
|
* @param {*} a First argument
|
|
* @param {*} b Second argument
|
|
*/
|
|
function invokeGuardedCallback(name, func, a, b) {
|
|
try {
|
|
return func(a, b);
|
|
} catch (x) {
|
|
if (caughtError === null) {
|
|
caughtError = x;
|
|
}
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
var ReactErrorUtils = {
|
|
invokeGuardedCallback: invokeGuardedCallback,
|
|
|
|
/**
|
|
* Invoked by ReactTestUtils.Simulate so that any errors thrown by the event
|
|
* handler are sure to be rethrown by rethrowCaughtError.
|
|
*/
|
|
invokeGuardedCallbackWithCatch: invokeGuardedCallback,
|
|
|
|
/**
|
|
* During execution of guarded functions we will capture the first error which
|
|
* we will rethrow to be handled by the top level error handler.
|
|
*/
|
|
rethrowCaughtError: function () {
|
|
if (caughtError) {
|
|
var error = caughtError;
|
|
caughtError = null;
|
|
throw error;
|
|
}
|
|
}
|
|
};
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
/**
|
|
* To help development we can get better devtools integration by simulating a
|
|
* real browser event.
|
|
*/
|
|
if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function' && typeof document !== 'undefined' && typeof document.createEvent === 'function') {
|
|
var fakeNode = document.createElement('react');
|
|
ReactErrorUtils.invokeGuardedCallback = function (name, func, a, b) {
|
|
var boundFunc = func.bind(null, a, b);
|
|
var evtType = 'react-' + name;
|
|
fakeNode.addEventListener(evtType, boundFunc, false);
|
|
var evt = document.createEvent('Event');
|
|
evt.initEvent(evtType, false, false);
|
|
fakeNode.dispatchEvent(evt);
|
|
fakeNode.removeEventListener(evtType, boundFunc, false);
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = ReactErrorUtils;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133}],192:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactEventEmitterMixin
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var EventPluginHub = require('./EventPluginHub');
|
|
|
|
function runEventQueueInBatch(events) {
|
|
EventPluginHub.enqueueEvents(events);
|
|
EventPluginHub.processEventQueue(false);
|
|
}
|
|
|
|
var ReactEventEmitterMixin = {
|
|
|
|
/**
|
|
* Streams a fired top-level event to `EventPluginHub` where plugins have the
|
|
* opportunity to create `ReactEvent`s to be dispatched.
|
|
*
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {object} topLevelTarget The listening component root node.
|
|
* @param {string} topLevelTargetID ID of `topLevelTarget`.
|
|
* @param {object} nativeEvent Native environment event.
|
|
*/
|
|
handleTopLevel: function (topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget) {
|
|
var events = EventPluginHub.extractEvents(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget);
|
|
runEventQueueInBatch(events);
|
|
}
|
|
};
|
|
|
|
module.exports = ReactEventEmitterMixin;
|
|
},{"./EventPluginHub":150}],193:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactEventListener
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var EventListener = require('fbjs/lib/EventListener');
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
var PooledClass = require('./PooledClass');
|
|
var ReactInstanceHandles = require('./ReactInstanceHandles');
|
|
var ReactMount = require('./ReactMount');
|
|
var ReactUpdates = require('./ReactUpdates');
|
|
|
|
var assign = require('./Object.assign');
|
|
var getEventTarget = require('./getEventTarget');
|
|
var getUnboundedScrollPosition = require('fbjs/lib/getUnboundedScrollPosition');
|
|
|
|
var DOCUMENT_FRAGMENT_NODE_TYPE = 11;
|
|
|
|
/**
|
|
* Finds the parent React component of `node`.
|
|
*
|
|
* @param {*} node
|
|
* @return {?DOMEventTarget} Parent container, or `null` if the specified node
|
|
* is not nested.
|
|
*/
|
|
function findParent(node) {
|
|
// TODO: It may be a good idea to cache this to prevent unnecessary DOM
|
|
// traversal, but caching is difficult to do correctly without using a
|
|
// mutation observer to listen for all DOM changes.
|
|
var nodeID = ReactMount.getID(node);
|
|
var rootID = ReactInstanceHandles.getReactRootIDFromNodeID(nodeID);
|
|
var container = ReactMount.findReactContainerForID(rootID);
|
|
var parent = ReactMount.getFirstReactDOM(container);
|
|
return parent;
|
|
}
|
|
|
|
// Used to store ancestor hierarchy in top level callback
|
|
function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) {
|
|
this.topLevelType = topLevelType;
|
|
this.nativeEvent = nativeEvent;
|
|
this.ancestors = [];
|
|
}
|
|
assign(TopLevelCallbackBookKeeping.prototype, {
|
|
destructor: function () {
|
|
this.topLevelType = null;
|
|
this.nativeEvent = null;
|
|
this.ancestors.length = 0;
|
|
}
|
|
});
|
|
PooledClass.addPoolingTo(TopLevelCallbackBookKeeping, PooledClass.twoArgumentPooler);
|
|
|
|
function handleTopLevelImpl(bookKeeping) {
|
|
// TODO: Re-enable event.path handling
|
|
//
|
|
// if (bookKeeping.nativeEvent.path && bookKeeping.nativeEvent.path.length > 1) {
|
|
// // New browsers have a path attribute on native events
|
|
// handleTopLevelWithPath(bookKeeping);
|
|
// } else {
|
|
// // Legacy browsers don't have a path attribute on native events
|
|
// handleTopLevelWithoutPath(bookKeeping);
|
|
// }
|
|
|
|
void handleTopLevelWithPath; // temporarily unused
|
|
handleTopLevelWithoutPath(bookKeeping);
|
|
}
|
|
|
|
// Legacy browsers don't have a path attribute on native events
|
|
function handleTopLevelWithoutPath(bookKeeping) {
|
|
var topLevelTarget = ReactMount.getFirstReactDOM(getEventTarget(bookKeeping.nativeEvent)) || window;
|
|
|
|
// Loop through the hierarchy, in case there's any nested components.
|
|
// It's important that we build the array of ancestors before calling any
|
|
// event handlers, because event handlers can modify the DOM, leading to
|
|
// inconsistencies with ReactMount's node cache. See #1105.
|
|
var ancestor = topLevelTarget;
|
|
while (ancestor) {
|
|
bookKeeping.ancestors.push(ancestor);
|
|
ancestor = findParent(ancestor);
|
|
}
|
|
|
|
for (var i = 0; i < bookKeeping.ancestors.length; i++) {
|
|
topLevelTarget = bookKeeping.ancestors[i];
|
|
var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
|
|
ReactEventListener._handleTopLevel(bookKeeping.topLevelType, topLevelTarget, topLevelTargetID, bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent));
|
|
}
|
|
}
|
|
|
|
// New browsers have a path attribute on native events
|
|
function handleTopLevelWithPath(bookKeeping) {
|
|
var path = bookKeeping.nativeEvent.path;
|
|
var currentNativeTarget = path[0];
|
|
var eventsFired = 0;
|
|
for (var i = 0; i < path.length; i++) {
|
|
var currentPathElement = path[i];
|
|
if (currentPathElement.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE) {
|
|
currentNativeTarget = path[i + 1];
|
|
}
|
|
// TODO: slow
|
|
var reactParent = ReactMount.getFirstReactDOM(currentPathElement);
|
|
if (reactParent === currentPathElement) {
|
|
var currentPathElementID = ReactMount.getID(currentPathElement);
|
|
var newRootID = ReactInstanceHandles.getReactRootIDFromNodeID(currentPathElementID);
|
|
bookKeeping.ancestors.push(currentPathElement);
|
|
|
|
var topLevelTargetID = ReactMount.getID(currentPathElement) || '';
|
|
eventsFired++;
|
|
ReactEventListener._handleTopLevel(bookKeeping.topLevelType, currentPathElement, topLevelTargetID, bookKeeping.nativeEvent, currentNativeTarget);
|
|
|
|
// Jump to the root of this React render tree
|
|
while (currentPathElementID !== newRootID) {
|
|
i++;
|
|
currentPathElement = path[i];
|
|
currentPathElementID = ReactMount.getID(currentPathElement);
|
|
}
|
|
}
|
|
}
|
|
if (eventsFired === 0) {
|
|
ReactEventListener._handleTopLevel(bookKeeping.topLevelType, window, '', bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent));
|
|
}
|
|
}
|
|
|
|
function scrollValueMonitor(cb) {
|
|
var scrollPosition = getUnboundedScrollPosition(window);
|
|
cb(scrollPosition);
|
|
}
|
|
|
|
var ReactEventListener = {
|
|
_enabled: true,
|
|
_handleTopLevel: null,
|
|
|
|
WINDOW_HANDLE: ExecutionEnvironment.canUseDOM ? window : null,
|
|
|
|
setHandleTopLevel: function (handleTopLevel) {
|
|
ReactEventListener._handleTopLevel = handleTopLevel;
|
|
},
|
|
|
|
setEnabled: function (enabled) {
|
|
ReactEventListener._enabled = !!enabled;
|
|
},
|
|
|
|
isEnabled: function () {
|
|
return ReactEventListener._enabled;
|
|
},
|
|
|
|
/**
|
|
* Traps top-level events by using event bubbling.
|
|
*
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {string} handlerBaseName Event name (e.g. "click").
|
|
* @param {object} handle Element on which to attach listener.
|
|
* @return {?object} An object with a remove function which will forcefully
|
|
* remove the listener.
|
|
* @internal
|
|
*/
|
|
trapBubbledEvent: function (topLevelType, handlerBaseName, handle) {
|
|
var element = handle;
|
|
if (!element) {
|
|
return null;
|
|
}
|
|
return EventListener.listen(element, handlerBaseName, ReactEventListener.dispatchEvent.bind(null, topLevelType));
|
|
},
|
|
|
|
/**
|
|
* Traps a top-level event by using event capturing.
|
|
*
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {string} handlerBaseName Event name (e.g. "click").
|
|
* @param {object} handle Element on which to attach listener.
|
|
* @return {?object} An object with a remove function which will forcefully
|
|
* remove the listener.
|
|
* @internal
|
|
*/
|
|
trapCapturedEvent: function (topLevelType, handlerBaseName, handle) {
|
|
var element = handle;
|
|
if (!element) {
|
|
return null;
|
|
}
|
|
return EventListener.capture(element, handlerBaseName, ReactEventListener.dispatchEvent.bind(null, topLevelType));
|
|
},
|
|
|
|
monitorScrollValue: function (refresh) {
|
|
var callback = scrollValueMonitor.bind(null, refresh);
|
|
EventListener.listen(window, 'scroll', callback);
|
|
},
|
|
|
|
dispatchEvent: function (topLevelType, nativeEvent) {
|
|
if (!ReactEventListener._enabled) {
|
|
return;
|
|
}
|
|
|
|
var bookKeeping = TopLevelCallbackBookKeeping.getPooled(topLevelType, nativeEvent);
|
|
try {
|
|
// Event queue being processed in the same cycle allows
|
|
// `preventDefault`.
|
|
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
|
|
} finally {
|
|
TopLevelCallbackBookKeeping.release(bookKeeping);
|
|
}
|
|
}
|
|
};
|
|
|
|
module.exports = ReactEventListener;
|
|
},{"./Object.assign":157,"./PooledClass":158,"./ReactInstanceHandles":196,"./ReactMount":200,"./ReactUpdates":218,"./getEventTarget":249,"fbjs/lib/EventListener":57,"fbjs/lib/ExecutionEnvironment":58,"fbjs/lib/getUnboundedScrollPosition":69}],194:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactInjection
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var DOMProperty = require('./DOMProperty');
|
|
var EventPluginHub = require('./EventPluginHub');
|
|
var ReactComponentEnvironment = require('./ReactComponentEnvironment');
|
|
var ReactClass = require('./ReactClass');
|
|
var ReactEmptyComponent = require('./ReactEmptyComponent');
|
|
var ReactBrowserEventEmitter = require('./ReactBrowserEventEmitter');
|
|
var ReactNativeComponent = require('./ReactNativeComponent');
|
|
var ReactPerf = require('./ReactPerf');
|
|
var ReactRootIndex = require('./ReactRootIndex');
|
|
var ReactUpdates = require('./ReactUpdates');
|
|
|
|
var ReactInjection = {
|
|
Component: ReactComponentEnvironment.injection,
|
|
Class: ReactClass.injection,
|
|
DOMProperty: DOMProperty.injection,
|
|
EmptyComponent: ReactEmptyComponent.injection,
|
|
EventPluginHub: EventPluginHub.injection,
|
|
EventEmitter: ReactBrowserEventEmitter.injection,
|
|
NativeComponent: ReactNativeComponent.injection,
|
|
Perf: ReactPerf.injection,
|
|
RootIndex: ReactRootIndex.injection,
|
|
Updates: ReactUpdates.injection
|
|
};
|
|
|
|
module.exports = ReactInjection;
|
|
},{"./DOMProperty":144,"./EventPluginHub":150,"./ReactBrowserEventEmitter":161,"./ReactClass":164,"./ReactComponentEnvironment":167,"./ReactEmptyComponent":189,"./ReactNativeComponent":203,"./ReactPerf":206,"./ReactRootIndex":213,"./ReactUpdates":218}],195:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactInputSelection
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactDOMSelection = require('./ReactDOMSelection');
|
|
|
|
var containsNode = require('fbjs/lib/containsNode');
|
|
var focusNode = require('fbjs/lib/focusNode');
|
|
var getActiveElement = require('fbjs/lib/getActiveElement');
|
|
|
|
function isInDocument(node) {
|
|
return containsNode(document.documentElement, node);
|
|
}
|
|
|
|
/**
|
|
* @ReactInputSelection: React input selection module. Based on Selection.js,
|
|
* but modified to be suitable for react and has a couple of bug fixes (doesn't
|
|
* assume buttons have range selections allowed).
|
|
* Input selection module for React.
|
|
*/
|
|
var ReactInputSelection = {
|
|
|
|
hasSelectionCapabilities: function (elem) {
|
|
var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
|
|
return nodeName && (nodeName === 'input' && elem.type === 'text' || nodeName === 'textarea' || elem.contentEditable === 'true');
|
|
},
|
|
|
|
getSelectionInformation: function () {
|
|
var focusedElem = getActiveElement();
|
|
return {
|
|
focusedElem: focusedElem,
|
|
selectionRange: ReactInputSelection.hasSelectionCapabilities(focusedElem) ? ReactInputSelection.getSelection(focusedElem) : null
|
|
};
|
|
},
|
|
|
|
/**
|
|
* @restoreSelection: If any selection information was potentially lost,
|
|
* restore it. This is useful when performing operations that could remove dom
|
|
* nodes and place them back in, resulting in focus being lost.
|
|
*/
|
|
restoreSelection: function (priorSelectionInformation) {
|
|
var curFocusedElem = getActiveElement();
|
|
var priorFocusedElem = priorSelectionInformation.focusedElem;
|
|
var priorSelectionRange = priorSelectionInformation.selectionRange;
|
|
if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
|
|
if (ReactInputSelection.hasSelectionCapabilities(priorFocusedElem)) {
|
|
ReactInputSelection.setSelection(priorFocusedElem, priorSelectionRange);
|
|
}
|
|
focusNode(priorFocusedElem);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @getSelection: Gets the selection bounds of a focused textarea, input or
|
|
* contentEditable node.
|
|
* -@input: Look up selection bounds of this input
|
|
* -@return {start: selectionStart, end: selectionEnd}
|
|
*/
|
|
getSelection: function (input) {
|
|
var selection;
|
|
|
|
if ('selectionStart' in input) {
|
|
// Modern browser with input or textarea.
|
|
selection = {
|
|
start: input.selectionStart,
|
|
end: input.selectionEnd
|
|
};
|
|
} else if (document.selection && (input.nodeName && input.nodeName.toLowerCase() === 'input')) {
|
|
// IE8 input.
|
|
var range = document.selection.createRange();
|
|
// There can only be one selection per document in IE, so it must
|
|
// be in our element.
|
|
if (range.parentElement() === input) {
|
|
selection = {
|
|
start: -range.moveStart('character', -input.value.length),
|
|
end: -range.moveEnd('character', -input.value.length)
|
|
};
|
|
}
|
|
} else {
|
|
// Content editable or old IE textarea.
|
|
selection = ReactDOMSelection.getOffsets(input);
|
|
}
|
|
|
|
return selection || { start: 0, end: 0 };
|
|
},
|
|
|
|
/**
|
|
* @setSelection: Sets the selection bounds of a textarea or input and focuses
|
|
* the input.
|
|
* -@input Set selection bounds of this input or textarea
|
|
* -@offsets Object of same form that is returned from get*
|
|
*/
|
|
setSelection: function (input, offsets) {
|
|
var start = offsets.start;
|
|
var end = offsets.end;
|
|
if (typeof end === 'undefined') {
|
|
end = start;
|
|
}
|
|
|
|
if ('selectionStart' in input) {
|
|
input.selectionStart = start;
|
|
input.selectionEnd = Math.min(end, input.value.length);
|
|
} else if (document.selection && (input.nodeName && input.nodeName.toLowerCase() === 'input')) {
|
|
var range = input.createTextRange();
|
|
range.collapse(true);
|
|
range.moveStart('character', start);
|
|
range.moveEnd('character', end - start);
|
|
range.select();
|
|
} else {
|
|
ReactDOMSelection.setOffsets(input, offsets);
|
|
}
|
|
}
|
|
};
|
|
|
|
module.exports = ReactInputSelection;
|
|
},{"./ReactDOMSelection":179,"fbjs/lib/containsNode":61,"fbjs/lib/focusNode":66,"fbjs/lib/getActiveElement":67}],196:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactInstanceHandles
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactRootIndex = require('./ReactRootIndex');
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
var SEPARATOR = '.';
|
|
var SEPARATOR_LENGTH = SEPARATOR.length;
|
|
|
|
/**
|
|
* Maximum depth of traversals before we consider the possibility of a bad ID.
|
|
*/
|
|
var MAX_TREE_DEPTH = 10000;
|
|
|
|
/**
|
|
* Creates a DOM ID prefix to use when mounting React components.
|
|
*
|
|
* @param {number} index A unique integer
|
|
* @return {string} React root ID.
|
|
* @internal
|
|
*/
|
|
function getReactRootIDString(index) {
|
|
return SEPARATOR + index.toString(36);
|
|
}
|
|
|
|
/**
|
|
* Checks if a character in the supplied ID is a separator or the end.
|
|
*
|
|
* @param {string} id A React DOM ID.
|
|
* @param {number} index Index of the character to check.
|
|
* @return {boolean} True if the character is a separator or end of the ID.
|
|
* @private
|
|
*/
|
|
function isBoundary(id, index) {
|
|
return id.charAt(index) === SEPARATOR || index === id.length;
|
|
}
|
|
|
|
/**
|
|
* Checks if the supplied string is a valid React DOM ID.
|
|
*
|
|
* @param {string} id A React DOM ID, maybe.
|
|
* @return {boolean} True if the string is a valid React DOM ID.
|
|
* @private
|
|
*/
|
|
function isValidID(id) {
|
|
return id === '' || id.charAt(0) === SEPARATOR && id.charAt(id.length - 1) !== SEPARATOR;
|
|
}
|
|
|
|
/**
|
|
* Checks if the first ID is an ancestor of or equal to the second ID.
|
|
*
|
|
* @param {string} ancestorID
|
|
* @param {string} descendantID
|
|
* @return {boolean} True if `ancestorID` is an ancestor of `descendantID`.
|
|
* @internal
|
|
*/
|
|
function isAncestorIDOf(ancestorID, descendantID) {
|
|
return descendantID.indexOf(ancestorID) === 0 && isBoundary(descendantID, ancestorID.length);
|
|
}
|
|
|
|
/**
|
|
* Gets the parent ID of the supplied React DOM ID, `id`.
|
|
*
|
|
* @param {string} id ID of a component.
|
|
* @return {string} ID of the parent, or an empty string.
|
|
* @private
|
|
*/
|
|
function getParentID(id) {
|
|
return id ? id.substr(0, id.lastIndexOf(SEPARATOR)) : '';
|
|
}
|
|
|
|
/**
|
|
* Gets the next DOM ID on the tree path from the supplied `ancestorID` to the
|
|
* supplied `destinationID`. If they are equal, the ID is returned.
|
|
*
|
|
* @param {string} ancestorID ID of an ancestor node of `destinationID`.
|
|
* @param {string} destinationID ID of the destination node.
|
|
* @return {string} Next ID on the path from `ancestorID` to `destinationID`.
|
|
* @private
|
|
*/
|
|
function getNextDescendantID(ancestorID, destinationID) {
|
|
!(isValidID(ancestorID) && isValidID(destinationID)) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'getNextDescendantID(%s, %s): Received an invalid React DOM ID.', ancestorID, destinationID) : invariant(false) : undefined;
|
|
!isAncestorIDOf(ancestorID, destinationID) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'getNextDescendantID(...): React has made an invalid assumption about ' + 'the DOM hierarchy. Expected `%s` to be an ancestor of `%s`.', ancestorID, destinationID) : invariant(false) : undefined;
|
|
if (ancestorID === destinationID) {
|
|
return ancestorID;
|
|
}
|
|
// Skip over the ancestor and the immediate separator. Traverse until we hit
|
|
// another separator or we reach the end of `destinationID`.
|
|
var start = ancestorID.length + SEPARATOR_LENGTH;
|
|
var i;
|
|
for (i = start; i < destinationID.length; i++) {
|
|
if (isBoundary(destinationID, i)) {
|
|
break;
|
|
}
|
|
}
|
|
return destinationID.substr(0, i);
|
|
}
|
|
|
|
/**
|
|
* Gets the nearest common ancestor ID of two IDs.
|
|
*
|
|
* Using this ID scheme, the nearest common ancestor ID is the longest common
|
|
* prefix of the two IDs that immediately preceded a "marker" in both strings.
|
|
*
|
|
* @param {string} oneID
|
|
* @param {string} twoID
|
|
* @return {string} Nearest common ancestor ID, or the empty string if none.
|
|
* @private
|
|
*/
|
|
function getFirstCommonAncestorID(oneID, twoID) {
|
|
var minLength = Math.min(oneID.length, twoID.length);
|
|
if (minLength === 0) {
|
|
return '';
|
|
}
|
|
var lastCommonMarkerIndex = 0;
|
|
// Use `<=` to traverse until the "EOL" of the shorter string.
|
|
for (var i = 0; i <= minLength; i++) {
|
|
if (isBoundary(oneID, i) && isBoundary(twoID, i)) {
|
|
lastCommonMarkerIndex = i;
|
|
} else if (oneID.charAt(i) !== twoID.charAt(i)) {
|
|
break;
|
|
}
|
|
}
|
|
var longestCommonID = oneID.substr(0, lastCommonMarkerIndex);
|
|
!isValidID(longestCommonID) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'getFirstCommonAncestorID(%s, %s): Expected a valid React DOM ID: %s', oneID, twoID, longestCommonID) : invariant(false) : undefined;
|
|
return longestCommonID;
|
|
}
|
|
|
|
/**
|
|
* Traverses the parent path between two IDs (either up or down). The IDs must
|
|
* not be the same, and there must exist a parent path between them. If the
|
|
* callback returns `false`, traversal is stopped.
|
|
*
|
|
* @param {?string} start ID at which to start traversal.
|
|
* @param {?string} stop ID at which to end traversal.
|
|
* @param {function} cb Callback to invoke each ID with.
|
|
* @param {*} arg Argument to invoke the callback with.
|
|
* @param {?boolean} skipFirst Whether or not to skip the first node.
|
|
* @param {?boolean} skipLast Whether or not to skip the last node.
|
|
* @private
|
|
*/
|
|
function traverseParentPath(start, stop, cb, arg, skipFirst, skipLast) {
|
|
start = start || '';
|
|
stop = stop || '';
|
|
!(start !== stop) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'traverseParentPath(...): Cannot traverse from and to the same ID, `%s`.', start) : invariant(false) : undefined;
|
|
var traverseUp = isAncestorIDOf(stop, start);
|
|
!(traverseUp || isAncestorIDOf(start, stop)) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'traverseParentPath(%s, %s, ...): Cannot traverse from two IDs that do ' + 'not have a parent path.', start, stop) : invariant(false) : undefined;
|
|
// Traverse from `start` to `stop` one depth at a time.
|
|
var depth = 0;
|
|
var traverse = traverseUp ? getParentID : getNextDescendantID;
|
|
for (var id = start;; /* until break */id = traverse(id, stop)) {
|
|
var ret;
|
|
if ((!skipFirst || id !== start) && (!skipLast || id !== stop)) {
|
|
ret = cb(id, traverseUp, arg);
|
|
}
|
|
if (ret === false || id === stop) {
|
|
// Only break //after// visiting `stop`.
|
|
break;
|
|
}
|
|
!(depth++ < MAX_TREE_DEPTH) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'traverseParentPath(%s, %s, ...): Detected an infinite loop while ' + 'traversing the React DOM ID tree. This may be due to malformed IDs: %s', start, stop, id) : invariant(false) : undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Manages the IDs assigned to DOM representations of React components. This
|
|
* uses a specific scheme in order to traverse the DOM efficiently (e.g. in
|
|
* order to simulate events).
|
|
*
|
|
* @internal
|
|
*/
|
|
var ReactInstanceHandles = {
|
|
|
|
/**
|
|
* Constructs a React root ID
|
|
* @return {string} A React root ID.
|
|
*/
|
|
createReactRootID: function () {
|
|
return getReactRootIDString(ReactRootIndex.createReactRootIndex());
|
|
},
|
|
|
|
/**
|
|
* Constructs a React ID by joining a root ID with a name.
|
|
*
|
|
* @param {string} rootID Root ID of a parent component.
|
|
* @param {string} name A component's name (as flattened children).
|
|
* @return {string} A React ID.
|
|
* @internal
|
|
*/
|
|
createReactID: function (rootID, name) {
|
|
return rootID + name;
|
|
},
|
|
|
|
/**
|
|
* Gets the DOM ID of the React component that is the root of the tree that
|
|
* contains the React component with the supplied DOM ID.
|
|
*
|
|
* @param {string} id DOM ID of a React component.
|
|
* @return {?string} DOM ID of the React component that is the root.
|
|
* @internal
|
|
*/
|
|
getReactRootIDFromNodeID: function (id) {
|
|
if (id && id.charAt(0) === SEPARATOR && id.length > 1) {
|
|
var index = id.indexOf(SEPARATOR, 1);
|
|
return index > -1 ? id.substr(0, index) : id;
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
* Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
|
|
* should would receive a `mouseEnter` or `mouseLeave` event.
|
|
*
|
|
* NOTE: Does not invoke the callback on the nearest common ancestor because
|
|
* nothing "entered" or "left" that element.
|
|
*
|
|
* @param {string} leaveID ID being left.
|
|
* @param {string} enterID ID being entered.
|
|
* @param {function} cb Callback to invoke on each entered/left ID.
|
|
* @param {*} upArg Argument to invoke the callback with on left IDs.
|
|
* @param {*} downArg Argument to invoke the callback with on entered IDs.
|
|
* @internal
|
|
*/
|
|
traverseEnterLeave: function (leaveID, enterID, cb, upArg, downArg) {
|
|
var ancestorID = getFirstCommonAncestorID(leaveID, enterID);
|
|
if (ancestorID !== leaveID) {
|
|
traverseParentPath(leaveID, ancestorID, cb, upArg, false, true);
|
|
}
|
|
if (ancestorID !== enterID) {
|
|
traverseParentPath(ancestorID, enterID, cb, downArg, true, false);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Simulates the traversal of a two-phase, capture/bubble event dispatch.
|
|
*
|
|
* NOTE: This traversal happens on IDs without touching the DOM.
|
|
*
|
|
* @param {string} targetID ID of the target node.
|
|
* @param {function} cb Callback to invoke.
|
|
* @param {*} arg Argument to invoke the callback with.
|
|
* @internal
|
|
*/
|
|
traverseTwoPhase: function (targetID, cb, arg) {
|
|
if (targetID) {
|
|
traverseParentPath('', targetID, cb, arg, true, false);
|
|
traverseParentPath(targetID, '', cb, arg, false, true);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Same as `traverseTwoPhase` but skips the `targetID`.
|
|
*/
|
|
traverseTwoPhaseSkipTarget: function (targetID, cb, arg) {
|
|
if (targetID) {
|
|
traverseParentPath('', targetID, cb, arg, true, true);
|
|
traverseParentPath(targetID, '', cb, arg, true, true);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Traverse a node ID, calling the supplied `cb` for each ancestor ID. For
|
|
* example, passing `.0.$row-0.1` would result in `cb` getting called
|
|
* with `.0`, `.0.$row-0`, and `.0.$row-0.1`.
|
|
*
|
|
* NOTE: This traversal happens on IDs without touching the DOM.
|
|
*
|
|
* @param {string} targetID ID of the target node.
|
|
* @param {function} cb Callback to invoke.
|
|
* @param {*} arg Argument to invoke the callback with.
|
|
* @internal
|
|
*/
|
|
traverseAncestors: function (targetID, cb, arg) {
|
|
traverseParentPath('', targetID, cb, arg, true, false);
|
|
},
|
|
|
|
getFirstCommonAncestorID: getFirstCommonAncestorID,
|
|
|
|
/**
|
|
* Exposed for unit testing.
|
|
* @private
|
|
*/
|
|
_getNextDescendantID: getNextDescendantID,
|
|
|
|
isAncestorIDOf: isAncestorIDOf,
|
|
|
|
SEPARATOR: SEPARATOR
|
|
|
|
};
|
|
|
|
module.exports = ReactInstanceHandles;
|
|
}).call(this,require('_process'))
|
|
},{"./ReactRootIndex":213,"_process":133,"fbjs/lib/invariant":72}],197:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactInstanceMap
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* `ReactInstanceMap` maintains a mapping from a public facing stateful
|
|
* instance (key) and the internal representation (value). This allows public
|
|
* methods to accept the user facing instance as an argument and map them back
|
|
* to internal methods.
|
|
*/
|
|
|
|
// TODO: Replace this with ES6: var ReactInstanceMap = new Map();
|
|
var ReactInstanceMap = {
|
|
|
|
/**
|
|
* This API should be called `delete` but we'd have to make sure to always
|
|
* transform these to strings for IE support. When this transform is fully
|
|
* supported we can rename it.
|
|
*/
|
|
remove: function (key) {
|
|
key._reactInternalInstance = undefined;
|
|
},
|
|
|
|
get: function (key) {
|
|
return key._reactInternalInstance;
|
|
},
|
|
|
|
has: function (key) {
|
|
return key._reactInternalInstance !== undefined;
|
|
},
|
|
|
|
set: function (key, value) {
|
|
key._reactInternalInstance = value;
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactInstanceMap;
|
|
},{}],198:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactIsomorphic
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactChildren = require('./ReactChildren');
|
|
var ReactComponent = require('./ReactComponent');
|
|
var ReactClass = require('./ReactClass');
|
|
var ReactDOMFactories = require('./ReactDOMFactories');
|
|
var ReactElement = require('./ReactElement');
|
|
var ReactElementValidator = require('./ReactElementValidator');
|
|
var ReactPropTypes = require('./ReactPropTypes');
|
|
var ReactVersion = require('./ReactVersion');
|
|
|
|
var assign = require('./Object.assign');
|
|
var onlyChild = require('./onlyChild');
|
|
|
|
var createElement = ReactElement.createElement;
|
|
var createFactory = ReactElement.createFactory;
|
|
var cloneElement = ReactElement.cloneElement;
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
createElement = ReactElementValidator.createElement;
|
|
createFactory = ReactElementValidator.createFactory;
|
|
cloneElement = ReactElementValidator.cloneElement;
|
|
}
|
|
|
|
var React = {
|
|
|
|
// Modern
|
|
|
|
Children: {
|
|
map: ReactChildren.map,
|
|
forEach: ReactChildren.forEach,
|
|
count: ReactChildren.count,
|
|
toArray: ReactChildren.toArray,
|
|
only: onlyChild
|
|
},
|
|
|
|
Component: ReactComponent,
|
|
|
|
createElement: createElement,
|
|
cloneElement: cloneElement,
|
|
isValidElement: ReactElement.isValidElement,
|
|
|
|
// Classic
|
|
|
|
PropTypes: ReactPropTypes,
|
|
createClass: ReactClass.createClass,
|
|
createFactory: createFactory,
|
|
createMixin: function (mixin) {
|
|
// Currently a noop. Will be used to validate and trace mixins.
|
|
return mixin;
|
|
},
|
|
|
|
// This looks DOM specific but these are actually isomorphic helpers
|
|
// since they are just generating DOM strings.
|
|
DOM: ReactDOMFactories,
|
|
|
|
version: ReactVersion,
|
|
|
|
// Hook for JSX spread, don't use this for anything else.
|
|
__spread: assign
|
|
};
|
|
|
|
module.exports = React;
|
|
}).call(this,require('_process'))
|
|
},{"./Object.assign":157,"./ReactChildren":163,"./ReactClass":164,"./ReactComponent":165,"./ReactDOMFactories":173,"./ReactElement":187,"./ReactElementValidator":188,"./ReactPropTypes":209,"./ReactVersion":219,"./onlyChild":256,"_process":133}],199:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactMarkupChecksum
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var adler32 = require('./adler32');
|
|
|
|
var TAG_END = /\/?>/;
|
|
|
|
var ReactMarkupChecksum = {
|
|
CHECKSUM_ATTR_NAME: 'data-react-checksum',
|
|
|
|
/**
|
|
* @param {string} markup Markup string
|
|
* @return {string} Markup string with checksum attribute attached
|
|
*/
|
|
addChecksumToMarkup: function (markup) {
|
|
var checksum = adler32(markup);
|
|
|
|
// Add checksum (handle both parent tags and self-closing tags)
|
|
return markup.replace(TAG_END, ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="' + checksum + '"$&');
|
|
},
|
|
|
|
/**
|
|
* @param {string} markup to use
|
|
* @param {DOMElement} element root React element
|
|
* @returns {boolean} whether or not the markup is the same
|
|
*/
|
|
canReuseMarkup: function (markup, element) {
|
|
var existingChecksum = element.getAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
|
|
existingChecksum = existingChecksum && parseInt(existingChecksum, 10);
|
|
var markupChecksum = adler32(markup);
|
|
return markupChecksum === existingChecksum;
|
|
}
|
|
};
|
|
|
|
module.exports = ReactMarkupChecksum;
|
|
},{"./adler32":238}],200:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactMount
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var DOMProperty = require('./DOMProperty');
|
|
var ReactBrowserEventEmitter = require('./ReactBrowserEventEmitter');
|
|
var ReactCurrentOwner = require('./ReactCurrentOwner');
|
|
var ReactDOMFeatureFlags = require('./ReactDOMFeatureFlags');
|
|
var ReactElement = require('./ReactElement');
|
|
var ReactEmptyComponentRegistry = require('./ReactEmptyComponentRegistry');
|
|
var ReactInstanceHandles = require('./ReactInstanceHandles');
|
|
var ReactInstanceMap = require('./ReactInstanceMap');
|
|
var ReactMarkupChecksum = require('./ReactMarkupChecksum');
|
|
var ReactPerf = require('./ReactPerf');
|
|
var ReactReconciler = require('./ReactReconciler');
|
|
var ReactUpdateQueue = require('./ReactUpdateQueue');
|
|
var ReactUpdates = require('./ReactUpdates');
|
|
|
|
var assign = require('./Object.assign');
|
|
var emptyObject = require('fbjs/lib/emptyObject');
|
|
var containsNode = require('fbjs/lib/containsNode');
|
|
var instantiateReactComponent = require('./instantiateReactComponent');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var setInnerHTML = require('./setInnerHTML');
|
|
var shouldUpdateReactComponent = require('./shouldUpdateReactComponent');
|
|
var validateDOMNesting = require('./validateDOMNesting');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
|
|
var nodeCache = {};
|
|
|
|
var ELEMENT_NODE_TYPE = 1;
|
|
var DOC_NODE_TYPE = 9;
|
|
var DOCUMENT_FRAGMENT_NODE_TYPE = 11;
|
|
|
|
var ownerDocumentContextKey = '__ReactMount_ownerDocument$' + Math.random().toString(36).slice(2);
|
|
|
|
/** Mapping from reactRootID to React component instance. */
|
|
var instancesByReactRootID = {};
|
|
|
|
/** Mapping from reactRootID to `container` nodes. */
|
|
var containersByReactRootID = {};
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
/** __DEV__-only mapping from reactRootID to root elements. */
|
|
var rootElementsByReactRootID = {};
|
|
}
|
|
|
|
// Used to store breadth-first search state in findComponentRoot.
|
|
var findComponentRootReusableArray = [];
|
|
|
|
/**
|
|
* Finds the index of the first character
|
|
* that's not common between the two given strings.
|
|
*
|
|
* @return {number} the index of the character where the strings diverge
|
|
*/
|
|
function firstDifferenceIndex(string1, string2) {
|
|
var minLen = Math.min(string1.length, string2.length);
|
|
for (var i = 0; i < minLen; i++) {
|
|
if (string1.charAt(i) !== string2.charAt(i)) {
|
|
return i;
|
|
}
|
|
}
|
|
return string1.length === string2.length ? -1 : minLen;
|
|
}
|
|
|
|
/**
|
|
* @param {DOMElement|DOMDocument} container DOM element that may contain
|
|
* a React component
|
|
* @return {?*} DOM element that may have the reactRoot ID, or null.
|
|
*/
|
|
function getReactRootElementInContainer(container) {
|
|
if (!container) {
|
|
return null;
|
|
}
|
|
|
|
if (container.nodeType === DOC_NODE_TYPE) {
|
|
return container.documentElement;
|
|
} else {
|
|
return container.firstChild;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {DOMElement} container DOM element that may contain a React component.
|
|
* @return {?string} A "reactRoot" ID, if a React component is rendered.
|
|
*/
|
|
function getReactRootID(container) {
|
|
var rootElement = getReactRootElementInContainer(container);
|
|
return rootElement && ReactMount.getID(rootElement);
|
|
}
|
|
|
|
/**
|
|
* Accessing node[ATTR_NAME] or calling getAttribute(ATTR_NAME) on a form
|
|
* element can return its control whose name or ID equals ATTR_NAME. All
|
|
* DOM nodes support `getAttributeNode` but this can also get called on
|
|
* other objects so just return '' if we're given something other than a
|
|
* DOM node (such as window).
|
|
*
|
|
* @param {?DOMElement|DOMWindow|DOMDocument|DOMTextNode} node DOM node.
|
|
* @return {string} ID of the supplied `domNode`.
|
|
*/
|
|
function getID(node) {
|
|
var id = internalGetID(node);
|
|
if (id) {
|
|
if (nodeCache.hasOwnProperty(id)) {
|
|
var cached = nodeCache[id];
|
|
if (cached !== node) {
|
|
!!isValid(cached, id) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactMount: Two valid but unequal nodes with the same `%s`: %s', ATTR_NAME, id) : invariant(false) : undefined;
|
|
|
|
nodeCache[id] = node;
|
|
}
|
|
} else {
|
|
nodeCache[id] = node;
|
|
}
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
function internalGetID(node) {
|
|
// If node is something like a window, document, or text node, none of
|
|
// which support attributes or a .getAttribute method, gracefully return
|
|
// the empty string, as if the attribute were missing.
|
|
return node && node.getAttribute && node.getAttribute(ATTR_NAME) || '';
|
|
}
|
|
|
|
/**
|
|
* Sets the React-specific ID of the given node.
|
|
*
|
|
* @param {DOMElement} node The DOM node whose ID will be set.
|
|
* @param {string} id The value of the ID attribute.
|
|
*/
|
|
function setID(node, id) {
|
|
var oldID = internalGetID(node);
|
|
if (oldID !== id) {
|
|
delete nodeCache[oldID];
|
|
}
|
|
node.setAttribute(ATTR_NAME, id);
|
|
nodeCache[id] = node;
|
|
}
|
|
|
|
/**
|
|
* Finds the node with the supplied React-generated DOM ID.
|
|
*
|
|
* @param {string} id A React-generated DOM ID.
|
|
* @return {DOMElement} DOM node with the suppled `id`.
|
|
* @internal
|
|
*/
|
|
function getNode(id) {
|
|
if (!nodeCache.hasOwnProperty(id) || !isValid(nodeCache[id], id)) {
|
|
nodeCache[id] = ReactMount.findReactNodeByID(id);
|
|
}
|
|
return nodeCache[id];
|
|
}
|
|
|
|
/**
|
|
* Finds the node with the supplied public React instance.
|
|
*
|
|
* @param {*} instance A public React instance.
|
|
* @return {?DOMElement} DOM node with the suppled `id`.
|
|
* @internal
|
|
*/
|
|
function getNodeFromInstance(instance) {
|
|
var id = ReactInstanceMap.get(instance)._rootNodeID;
|
|
if (ReactEmptyComponentRegistry.isNullComponentID(id)) {
|
|
return null;
|
|
}
|
|
if (!nodeCache.hasOwnProperty(id) || !isValid(nodeCache[id], id)) {
|
|
nodeCache[id] = ReactMount.findReactNodeByID(id);
|
|
}
|
|
return nodeCache[id];
|
|
}
|
|
|
|
/**
|
|
* A node is "valid" if it is contained by a currently mounted container.
|
|
*
|
|
* This means that the node does not have to be contained by a document in
|
|
* order to be considered valid.
|
|
*
|
|
* @param {?DOMElement} node The candidate DOM node.
|
|
* @param {string} id The expected ID of the node.
|
|
* @return {boolean} Whether the node is contained by a mounted container.
|
|
*/
|
|
function isValid(node, id) {
|
|
if (node) {
|
|
!(internalGetID(node) === id) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactMount: Unexpected modification of `%s`', ATTR_NAME) : invariant(false) : undefined;
|
|
|
|
var container = ReactMount.findReactContainerForID(id);
|
|
if (container && containsNode(container, node)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Causes the cache to forget about one React-specific ID.
|
|
*
|
|
* @param {string} id The ID to forget.
|
|
*/
|
|
function purgeID(id) {
|
|
delete nodeCache[id];
|
|
}
|
|
|
|
var deepestNodeSoFar = null;
|
|
function findDeepestCachedAncestorImpl(ancestorID) {
|
|
var ancestor = nodeCache[ancestorID];
|
|
if (ancestor && isValid(ancestor, ancestorID)) {
|
|
deepestNodeSoFar = ancestor;
|
|
} else {
|
|
// This node isn't populated in the cache, so presumably none of its
|
|
// descendants are. Break out of the loop.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the deepest cached node whose ID is a prefix of `targetID`.
|
|
*/
|
|
function findDeepestCachedAncestor(targetID) {
|
|
deepestNodeSoFar = null;
|
|
ReactInstanceHandles.traverseAncestors(targetID, findDeepestCachedAncestorImpl);
|
|
|
|
var foundNode = deepestNodeSoFar;
|
|
deepestNodeSoFar = null;
|
|
return foundNode;
|
|
}
|
|
|
|
/**
|
|
* Mounts this component and inserts it into the DOM.
|
|
*
|
|
* @param {ReactComponent} componentInstance The instance to mount.
|
|
* @param {string} rootID DOM ID of the root node.
|
|
* @param {DOMElement} container DOM element to mount into.
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @param {boolean} shouldReuseMarkup If true, do not insert markup
|
|
*/
|
|
function mountComponentIntoNode(componentInstance, rootID, container, transaction, shouldReuseMarkup, context) {
|
|
if (ReactDOMFeatureFlags.useCreateElement) {
|
|
context = assign({}, context);
|
|
if (container.nodeType === DOC_NODE_TYPE) {
|
|
context[ownerDocumentContextKey] = container;
|
|
} else {
|
|
context[ownerDocumentContextKey] = container.ownerDocument;
|
|
}
|
|
}
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
if (context === emptyObject) {
|
|
context = {};
|
|
}
|
|
var tag = container.nodeName.toLowerCase();
|
|
context[validateDOMNesting.ancestorInfoContextKey] = validateDOMNesting.updatedAncestorInfo(null, tag, null);
|
|
}
|
|
var markup = ReactReconciler.mountComponent(componentInstance, rootID, transaction, context);
|
|
componentInstance._renderedComponent._topLevelWrapper = componentInstance;
|
|
ReactMount._mountImageIntoNode(markup, container, shouldReuseMarkup, transaction);
|
|
}
|
|
|
|
/**
|
|
* Batched mount.
|
|
*
|
|
* @param {ReactComponent} componentInstance The instance to mount.
|
|
* @param {string} rootID DOM ID of the root node.
|
|
* @param {DOMElement} container DOM element to mount into.
|
|
* @param {boolean} shouldReuseMarkup If true, do not insert markup
|
|
*/
|
|
function batchedMountComponentIntoNode(componentInstance, rootID, container, shouldReuseMarkup, context) {
|
|
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
|
|
/* forceHTML */shouldReuseMarkup);
|
|
transaction.perform(mountComponentIntoNode, null, componentInstance, rootID, container, transaction, shouldReuseMarkup, context);
|
|
ReactUpdates.ReactReconcileTransaction.release(transaction);
|
|
}
|
|
|
|
/**
|
|
* Unmounts a component and removes it from the DOM.
|
|
*
|
|
* @param {ReactComponent} instance React component instance.
|
|
* @param {DOMElement} container DOM element to unmount from.
|
|
* @final
|
|
* @internal
|
|
* @see {ReactMount.unmountComponentAtNode}
|
|
*/
|
|
function unmountComponentFromNode(instance, container) {
|
|
ReactReconciler.unmountComponent(instance);
|
|
|
|
if (container.nodeType === DOC_NODE_TYPE) {
|
|
container = container.documentElement;
|
|
}
|
|
|
|
// http://jsperf.com/emptying-a-node
|
|
while (container.lastChild) {
|
|
container.removeChild(container.lastChild);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* True if the supplied DOM node has a direct React-rendered child that is
|
|
* not a React root element. Useful for warning in `render`,
|
|
* `unmountComponentAtNode`, etc.
|
|
*
|
|
* @param {?DOMElement} node The candidate DOM node.
|
|
* @return {boolean} True if the DOM element contains a direct child that was
|
|
* rendered by React but is not a root element.
|
|
* @internal
|
|
*/
|
|
function hasNonRootReactChild(node) {
|
|
var reactRootID = getReactRootID(node);
|
|
return reactRootID ? reactRootID !== ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID) : false;
|
|
}
|
|
|
|
/**
|
|
* Returns the first (deepest) ancestor of a node which is rendered by this copy
|
|
* of React.
|
|
*/
|
|
function findFirstReactDOMImpl(node) {
|
|
// This node might be from another React instance, so we make sure not to
|
|
// examine the node cache here
|
|
for (; node && node.parentNode !== node; node = node.parentNode) {
|
|
if (node.nodeType !== 1) {
|
|
// Not a DOMElement, therefore not a React component
|
|
continue;
|
|
}
|
|
var nodeID = internalGetID(node);
|
|
if (!nodeID) {
|
|
continue;
|
|
}
|
|
var reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(nodeID);
|
|
|
|
// If containersByReactRootID contains the container we find by crawling up
|
|
// the tree, we know that this instance of React rendered the node.
|
|
// nb. isValid's strategy (with containsNode) does not work because render
|
|
// trees may be nested and we don't want a false positive in that case.
|
|
var current = node;
|
|
var lastID;
|
|
do {
|
|
lastID = internalGetID(current);
|
|
current = current.parentNode;
|
|
if (current == null) {
|
|
// The passed-in node has been detached from the container it was
|
|
// originally rendered into.
|
|
return null;
|
|
}
|
|
} while (lastID !== reactRootID);
|
|
|
|
if (current === containersByReactRootID[reactRootID]) {
|
|
return node;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Temporary (?) hack so that we can store all top-level pending updates on
|
|
* composites instead of having to worry about different types of components
|
|
* here.
|
|
*/
|
|
var TopLevelWrapper = function () {};
|
|
TopLevelWrapper.prototype.isReactComponent = {};
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
TopLevelWrapper.displayName = 'TopLevelWrapper';
|
|
}
|
|
TopLevelWrapper.prototype.render = function () {
|
|
// this.props is actually a ReactElement
|
|
return this.props;
|
|
};
|
|
|
|
/**
|
|
* Mounting is the process of initializing a React component by creating its
|
|
* representative DOM elements and inserting them into a supplied `container`.
|
|
* Any prior content inside `container` is destroyed in the process.
|
|
*
|
|
* ReactMount.render(
|
|
* component,
|
|
* document.getElementById('container')
|
|
* );
|
|
*
|
|
* <div id="container"> <-- Supplied `container`.
|
|
* <div data-reactid=".3"> <-- Rendered reactRoot of React
|
|
* // ... component.
|
|
* </div>
|
|
* </div>
|
|
*
|
|
* Inside of `container`, the first element rendered is the "reactRoot".
|
|
*/
|
|
var ReactMount = {
|
|
|
|
TopLevelWrapper: TopLevelWrapper,
|
|
|
|
/** Exposed for debugging purposes **/
|
|
_instancesByReactRootID: instancesByReactRootID,
|
|
|
|
/**
|
|
* This is a hook provided to support rendering React components while
|
|
* ensuring that the apparent scroll position of its `container` does not
|
|
* change.
|
|
*
|
|
* @param {DOMElement} container The `container` being rendered into.
|
|
* @param {function} renderCallback This must be called once to do the render.
|
|
*/
|
|
scrollMonitor: function (container, renderCallback) {
|
|
renderCallback();
|
|
},
|
|
|
|
/**
|
|
* Take a component that's already mounted into the DOM and replace its props
|
|
* @param {ReactComponent} prevComponent component instance already in the DOM
|
|
* @param {ReactElement} nextElement component instance to render
|
|
* @param {DOMElement} container container to render into
|
|
* @param {?function} callback function triggered on completion
|
|
*/
|
|
_updateRootComponent: function (prevComponent, nextElement, container, callback) {
|
|
ReactMount.scrollMonitor(container, function () {
|
|
ReactUpdateQueue.enqueueElementInternal(prevComponent, nextElement);
|
|
if (callback) {
|
|
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
|
|
}
|
|
});
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// Record the root element in case it later gets transplanted.
|
|
rootElementsByReactRootID[getReactRootID(container)] = getReactRootElementInContainer(container);
|
|
}
|
|
|
|
return prevComponent;
|
|
},
|
|
|
|
/**
|
|
* Register a component into the instance map and starts scroll value
|
|
* monitoring
|
|
* @param {ReactComponent} nextComponent component instance to render
|
|
* @param {DOMElement} container container to render into
|
|
* @return {string} reactRoot ID prefix
|
|
*/
|
|
_registerComponent: function (nextComponent, container) {
|
|
!(container && (container.nodeType === ELEMENT_NODE_TYPE || container.nodeType === DOC_NODE_TYPE || container.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '_registerComponent(...): Target container is not a DOM element.') : invariant(false) : undefined;
|
|
|
|
ReactBrowserEventEmitter.ensureScrollValueMonitoring();
|
|
|
|
var reactRootID = ReactMount.registerContainer(container);
|
|
instancesByReactRootID[reactRootID] = nextComponent;
|
|
return reactRootID;
|
|
},
|
|
|
|
/**
|
|
* Render a new component into the DOM.
|
|
* @param {ReactElement} nextElement element to render
|
|
* @param {DOMElement} container container to render into
|
|
* @param {boolean} shouldReuseMarkup if we should skip the markup insertion
|
|
* @return {ReactComponent} nextComponent
|
|
*/
|
|
_renderNewRootComponent: function (nextElement, container, shouldReuseMarkup, context) {
|
|
// Various parts of our code (such as ReactCompositeComponent's
|
|
// _renderValidatedComponent) assume that calls to render aren't nested;
|
|
// verify that that's the case.
|
|
process.env.NODE_ENV !== 'production' ? warning(ReactCurrentOwner.current == null, '_renderNewRootComponent(): Render methods should be a pure function ' + 'of props and state; triggering nested component updates from ' + 'render is not allowed. If necessary, trigger nested updates in ' + 'componentDidUpdate. Check the render method of %s.', ReactCurrentOwner.current && ReactCurrentOwner.current.getName() || 'ReactCompositeComponent') : undefined;
|
|
|
|
var componentInstance = instantiateReactComponent(nextElement, null);
|
|
var reactRootID = ReactMount._registerComponent(componentInstance, container);
|
|
|
|
// The initial render is synchronous but any updates that happen during
|
|
// rendering, in componentWillMount or componentDidMount, will be batched
|
|
// according to the current batching strategy.
|
|
|
|
ReactUpdates.batchedUpdates(batchedMountComponentIntoNode, componentInstance, reactRootID, container, shouldReuseMarkup, context);
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// Record the root element in case it later gets transplanted.
|
|
rootElementsByReactRootID[reactRootID] = getReactRootElementInContainer(container);
|
|
}
|
|
|
|
return componentInstance;
|
|
},
|
|
|
|
/**
|
|
* Renders a React component into the DOM in the supplied `container`.
|
|
*
|
|
* If the React component was previously rendered into `container`, this will
|
|
* perform an update on it and only mutate the DOM as necessary to reflect the
|
|
* latest React component.
|
|
*
|
|
* @param {ReactComponent} parentComponent The conceptual parent of this render tree.
|
|
* @param {ReactElement} nextElement Component element to render.
|
|
* @param {DOMElement} container DOM element to render into.
|
|
* @param {?function} callback function triggered on completion
|
|
* @return {ReactComponent} Component instance rendered in `container`.
|
|
*/
|
|
renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback) {
|
|
!(parentComponent != null && parentComponent._reactInternalInstance != null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'parentComponent must be a valid React Component') : invariant(false) : undefined;
|
|
return ReactMount._renderSubtreeIntoContainer(parentComponent, nextElement, container, callback);
|
|
},
|
|
|
|
_renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback) {
|
|
!ReactElement.isValidElement(nextElement) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactDOM.render(): Invalid component element.%s', typeof nextElement === 'string' ? ' Instead of passing an element string, make sure to instantiate ' + 'it by passing it to React.createElement.' : typeof nextElement === 'function' ? ' Instead of passing a component class, make sure to instantiate ' + 'it by passing it to React.createElement.' :
|
|
// Check if it quacks like an element
|
|
nextElement != null && nextElement.props !== undefined ? ' This may be caused by unintentionally loading two independent ' + 'copies of React.' : '') : invariant(false) : undefined;
|
|
|
|
process.env.NODE_ENV !== 'production' ? warning(!container || !container.tagName || container.tagName.toUpperCase() !== 'BODY', 'render(): Rendering components directly into document.body is ' + 'discouraged, since its children are often manipulated by third-party ' + 'scripts and browser extensions. This may lead to subtle ' + 'reconciliation issues. Try rendering into a container element created ' + 'for your app.') : undefined;
|
|
|
|
var nextWrappedElement = new ReactElement(TopLevelWrapper, null, null, null, null, null, nextElement);
|
|
|
|
var prevComponent = instancesByReactRootID[getReactRootID(container)];
|
|
|
|
if (prevComponent) {
|
|
var prevWrappedElement = prevComponent._currentElement;
|
|
var prevElement = prevWrappedElement.props;
|
|
if (shouldUpdateReactComponent(prevElement, nextElement)) {
|
|
var publicInst = prevComponent._renderedComponent.getPublicInstance();
|
|
var updatedCallback = callback && function () {
|
|
callback.call(publicInst);
|
|
};
|
|
ReactMount._updateRootComponent(prevComponent, nextWrappedElement, container, updatedCallback);
|
|
return publicInst;
|
|
} else {
|
|
ReactMount.unmountComponentAtNode(container);
|
|
}
|
|
}
|
|
|
|
var reactRootElement = getReactRootElementInContainer(container);
|
|
var containerHasReactMarkup = reactRootElement && !!internalGetID(reactRootElement);
|
|
var containerHasNonRootReactChild = hasNonRootReactChild(container);
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(!containerHasNonRootReactChild, 'render(...): Replacing React-rendered children with a new root ' + 'component. If you intended to update the children of this node, ' + 'you should instead have the existing children update their state ' + 'and render the new components instead of calling ReactDOM.render.') : undefined;
|
|
|
|
if (!containerHasReactMarkup || reactRootElement.nextSibling) {
|
|
var rootElementSibling = reactRootElement;
|
|
while (rootElementSibling) {
|
|
if (internalGetID(rootElementSibling)) {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'render(): Target node has markup rendered by React, but there ' + 'are unrelated nodes as well. This is most commonly caused by ' + 'white-space inserted around server-rendered markup.') : undefined;
|
|
break;
|
|
}
|
|
rootElementSibling = rootElementSibling.nextSibling;
|
|
}
|
|
}
|
|
}
|
|
|
|
var shouldReuseMarkup = containerHasReactMarkup && !prevComponent && !containerHasNonRootReactChild;
|
|
var component = ReactMount._renderNewRootComponent(nextWrappedElement, container, shouldReuseMarkup, parentComponent != null ? parentComponent._reactInternalInstance._processChildContext(parentComponent._reactInternalInstance._context) : emptyObject)._renderedComponent.getPublicInstance();
|
|
if (callback) {
|
|
callback.call(component);
|
|
}
|
|
return component;
|
|
},
|
|
|
|
/**
|
|
* Renders a React component into the DOM in the supplied `container`.
|
|
*
|
|
* If the React component was previously rendered into `container`, this will
|
|
* perform an update on it and only mutate the DOM as necessary to reflect the
|
|
* latest React component.
|
|
*
|
|
* @param {ReactElement} nextElement Component element to render.
|
|
* @param {DOMElement} container DOM element to render into.
|
|
* @param {?function} callback function triggered on completion
|
|
* @return {ReactComponent} Component instance rendered in `container`.
|
|
*/
|
|
render: function (nextElement, container, callback) {
|
|
return ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback);
|
|
},
|
|
|
|
/**
|
|
* Registers a container node into which React components will be rendered.
|
|
* This also creates the "reactRoot" ID that will be assigned to the element
|
|
* rendered within.
|
|
*
|
|
* @param {DOMElement} container DOM element to register as a container.
|
|
* @return {string} The "reactRoot" ID of elements rendered within.
|
|
*/
|
|
registerContainer: function (container) {
|
|
var reactRootID = getReactRootID(container);
|
|
if (reactRootID) {
|
|
// If one exists, make sure it is a valid "reactRoot" ID.
|
|
reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID);
|
|
}
|
|
if (!reactRootID) {
|
|
// No valid "reactRoot" ID found, create one.
|
|
reactRootID = ReactInstanceHandles.createReactRootID();
|
|
}
|
|
containersByReactRootID[reactRootID] = container;
|
|
return reactRootID;
|
|
},
|
|
|
|
/**
|
|
* Unmounts and destroys the React component rendered in the `container`.
|
|
*
|
|
* @param {DOMElement} container DOM element containing a React component.
|
|
* @return {boolean} True if a component was found in and unmounted from
|
|
* `container`
|
|
*/
|
|
unmountComponentAtNode: function (container) {
|
|
// Various parts of our code (such as ReactCompositeComponent's
|
|
// _renderValidatedComponent) assume that calls to render aren't nested;
|
|
// verify that that's the case. (Strictly speaking, unmounting won't cause a
|
|
// render but we still don't expect to be in a render call here.)
|
|
process.env.NODE_ENV !== 'production' ? warning(ReactCurrentOwner.current == null, 'unmountComponentAtNode(): Render methods should be a pure function ' + 'of props and state; triggering nested component updates from render ' + 'is not allowed. If necessary, trigger nested updates in ' + 'componentDidUpdate. Check the render method of %s.', ReactCurrentOwner.current && ReactCurrentOwner.current.getName() || 'ReactCompositeComponent') : undefined;
|
|
|
|
!(container && (container.nodeType === ELEMENT_NODE_TYPE || container.nodeType === DOC_NODE_TYPE || container.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE)) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'unmountComponentAtNode(...): Target container is not a DOM element.') : invariant(false) : undefined;
|
|
|
|
var reactRootID = getReactRootID(container);
|
|
var component = instancesByReactRootID[reactRootID];
|
|
if (!component) {
|
|
// Check if the node being unmounted was rendered by React, but isn't a
|
|
// root node.
|
|
var containerHasNonRootReactChild = hasNonRootReactChild(container);
|
|
|
|
// Check if the container itself is a React root node.
|
|
var containerID = internalGetID(container);
|
|
var isContainerReactRoot = containerID && containerID === ReactInstanceHandles.getReactRootIDFromNodeID(containerID);
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(!containerHasNonRootReactChild, 'unmountComponentAtNode(): The node you\'re attempting to unmount ' + 'was rendered by React and is not a top-level container. %s', isContainerReactRoot ? 'You may have accidentally passed in a React root node instead ' + 'of its container.' : 'Instead, have the parent component update its state and ' + 'rerender in order to remove this component.') : undefined;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
ReactUpdates.batchedUpdates(unmountComponentFromNode, component, container);
|
|
delete instancesByReactRootID[reactRootID];
|
|
delete containersByReactRootID[reactRootID];
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
delete rootElementsByReactRootID[reactRootID];
|
|
}
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Finds the container DOM element that contains React component to which the
|
|
* supplied DOM `id` belongs.
|
|
*
|
|
* @param {string} id The ID of an element rendered by a React component.
|
|
* @return {?DOMElement} DOM element that contains the `id`.
|
|
*/
|
|
findReactContainerForID: function (id) {
|
|
var reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(id);
|
|
var container = containersByReactRootID[reactRootID];
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var rootElement = rootElementsByReactRootID[reactRootID];
|
|
if (rootElement && rootElement.parentNode !== container) {
|
|
process.env.NODE_ENV !== 'production' ? warning(
|
|
// Call internalGetID here because getID calls isValid which calls
|
|
// findReactContainerForID (this function).
|
|
internalGetID(rootElement) === reactRootID, 'ReactMount: Root element ID differed from reactRootID.') : undefined;
|
|
var containerChild = container.firstChild;
|
|
if (containerChild && reactRootID === internalGetID(containerChild)) {
|
|
// If the container has a new child with the same ID as the old
|
|
// root element, then rootElementsByReactRootID[reactRootID] is
|
|
// just stale and needs to be updated. The case that deserves a
|
|
// warning is when the container is empty.
|
|
rootElementsByReactRootID[reactRootID] = containerChild;
|
|
} else {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'ReactMount: Root element has been removed from its original ' + 'container. New container: %s', rootElement.parentNode) : undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
return container;
|
|
},
|
|
|
|
/**
|
|
* Finds an element rendered by React with the supplied ID.
|
|
*
|
|
* @param {string} id ID of a DOM node in the React component.
|
|
* @return {DOMElement} Root DOM node of the React component.
|
|
*/
|
|
findReactNodeByID: function (id) {
|
|
var reactRoot = ReactMount.findReactContainerForID(id);
|
|
return ReactMount.findComponentRoot(reactRoot, id);
|
|
},
|
|
|
|
/**
|
|
* Traverses up the ancestors of the supplied node to find a node that is a
|
|
* DOM representation of a React component rendered by this copy of React.
|
|
*
|
|
* @param {*} node
|
|
* @return {?DOMEventTarget}
|
|
* @internal
|
|
*/
|
|
getFirstReactDOM: function (node) {
|
|
return findFirstReactDOMImpl(node);
|
|
},
|
|
|
|
/**
|
|
* Finds a node with the supplied `targetID` inside of the supplied
|
|
* `ancestorNode`. Exploits the ID naming scheme to perform the search
|
|
* quickly.
|
|
*
|
|
* @param {DOMEventTarget} ancestorNode Search from this root.
|
|
* @pararm {string} targetID ID of the DOM representation of the component.
|
|
* @return {DOMEventTarget} DOM node with the supplied `targetID`.
|
|
* @internal
|
|
*/
|
|
findComponentRoot: function (ancestorNode, targetID) {
|
|
var firstChildren = findComponentRootReusableArray;
|
|
var childIndex = 0;
|
|
|
|
var deepestAncestor = findDeepestCachedAncestor(targetID) || ancestorNode;
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// This will throw on the next line; give an early warning
|
|
process.env.NODE_ENV !== 'production' ? warning(deepestAncestor != null, 'React can\'t find the root component node for data-reactid value ' + '`%s`. If you\'re seeing this message, it probably means that ' + 'you\'ve loaded two copies of React on the page. At this time, only ' + 'a single copy of React can be loaded at a time.', targetID) : undefined;
|
|
}
|
|
|
|
firstChildren[0] = deepestAncestor.firstChild;
|
|
firstChildren.length = 1;
|
|
|
|
while (childIndex < firstChildren.length) {
|
|
var child = firstChildren[childIndex++];
|
|
var targetChild;
|
|
|
|
while (child) {
|
|
var childID = ReactMount.getID(child);
|
|
if (childID) {
|
|
// Even if we find the node we're looking for, we finish looping
|
|
// through its siblings to ensure they're cached so that we don't have
|
|
// to revisit this node again. Otherwise, we make n^2 calls to getID
|
|
// when visiting the many children of a single node in order.
|
|
|
|
if (targetID === childID) {
|
|
targetChild = child;
|
|
} else if (ReactInstanceHandles.isAncestorIDOf(childID, targetID)) {
|
|
// If we find a child whose ID is an ancestor of the given ID,
|
|
// then we can be sure that we only want to search the subtree
|
|
// rooted at this child, so we can throw out the rest of the
|
|
// search state.
|
|
firstChildren.length = childIndex = 0;
|
|
firstChildren.push(child.firstChild);
|
|
}
|
|
} else {
|
|
// If this child had no ID, then there's a chance that it was
|
|
// injected automatically by the browser, as when a `<table>`
|
|
// element sprouts an extra `<tbody>` child as a side effect of
|
|
// `.innerHTML` parsing. Optimistically continue down this
|
|
// branch, but not before examining the other siblings.
|
|
firstChildren.push(child.firstChild);
|
|
}
|
|
|
|
child = child.nextSibling;
|
|
}
|
|
|
|
if (targetChild) {
|
|
// Emptying firstChildren/findComponentRootReusableArray is
|
|
// not necessary for correctness, but it helps the GC reclaim
|
|
// any nodes that were left at the end of the search.
|
|
firstChildren.length = 0;
|
|
|
|
return targetChild;
|
|
}
|
|
}
|
|
|
|
firstChildren.length = 0;
|
|
|
|
!false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'findComponentRoot(..., %s): Unable to find element. This probably ' + 'means the DOM was unexpectedly mutated (e.g., by the browser), ' + 'usually due to forgetting a <tbody> when using tables, nesting tags ' + 'like <form>, <p>, or <a>, or using non-SVG elements in an <svg> ' + 'parent. ' + 'Try inspecting the child nodes of the element with React ID `%s`.', targetID, ReactMount.getID(ancestorNode)) : invariant(false) : undefined;
|
|
},
|
|
|
|
_mountImageIntoNode: function (markup, container, shouldReuseMarkup, transaction) {
|
|
!(container && (container.nodeType === ELEMENT_NODE_TYPE || container.nodeType === DOC_NODE_TYPE || container.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE)) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'mountComponentIntoNode(...): Target container is not valid.') : invariant(false) : undefined;
|
|
|
|
if (shouldReuseMarkup) {
|
|
var rootElement = getReactRootElementInContainer(container);
|
|
if (ReactMarkupChecksum.canReuseMarkup(markup, rootElement)) {
|
|
return;
|
|
} else {
|
|
var checksum = rootElement.getAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
|
|
rootElement.removeAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
|
|
|
|
var rootMarkup = rootElement.outerHTML;
|
|
rootElement.setAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME, checksum);
|
|
|
|
var normalizedMarkup = markup;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// because rootMarkup is retrieved from the DOM, various normalizations
|
|
// will have occurred which will not be present in `markup`. Here,
|
|
// insert markup into a <div> or <iframe> depending on the container
|
|
// type to perform the same normalizations before comparing.
|
|
var normalizer;
|
|
if (container.nodeType === ELEMENT_NODE_TYPE) {
|
|
normalizer = document.createElement('div');
|
|
normalizer.innerHTML = markup;
|
|
normalizedMarkup = normalizer.innerHTML;
|
|
} else {
|
|
normalizer = document.createElement('iframe');
|
|
document.body.appendChild(normalizer);
|
|
normalizer.contentDocument.write(markup);
|
|
normalizedMarkup = normalizer.contentDocument.documentElement.outerHTML;
|
|
document.body.removeChild(normalizer);
|
|
}
|
|
}
|
|
|
|
var diffIndex = firstDifferenceIndex(normalizedMarkup, rootMarkup);
|
|
var difference = ' (client) ' + normalizedMarkup.substring(diffIndex - 20, diffIndex + 20) + '\n (server) ' + rootMarkup.substring(diffIndex - 20, diffIndex + 20);
|
|
|
|
!(container.nodeType !== DOC_NODE_TYPE) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'You\'re trying to render a component to the document using ' + 'server rendering but the checksum was invalid. This usually ' + 'means you rendered a different component type or props on ' + 'the client from the one on the server, or your render() ' + 'methods are impure. React cannot handle this case due to ' + 'cross-browser quirks by rendering at the document root. You ' + 'should look for environment dependent code in your components ' + 'and ensure the props are the same client and server side:\n%s', difference) : invariant(false) : undefined;
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'React attempted to reuse markup in a container but the ' + 'checksum was invalid. This generally means that you are ' + 'using server rendering and the markup generated on the ' + 'server was not what the client was expecting. React injected ' + 'new markup to compensate which works but you have lost many ' + 'of the benefits of server rendering. Instead, figure out ' + 'why the markup being generated is different on the client ' + 'or server:\n%s', difference) : undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
!(container.nodeType !== DOC_NODE_TYPE) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'You\'re trying to render a component to the document but ' + 'you didn\'t use server rendering. We can\'t do this ' + 'without using server rendering due to cross-browser quirks. ' + 'See ReactDOMServer.renderToString() for server rendering.') : invariant(false) : undefined;
|
|
|
|
if (transaction.useCreateElement) {
|
|
while (container.lastChild) {
|
|
container.removeChild(container.lastChild);
|
|
}
|
|
container.appendChild(markup);
|
|
} else {
|
|
setInnerHTML(container, markup);
|
|
}
|
|
},
|
|
|
|
ownerDocumentContextKey: ownerDocumentContextKey,
|
|
|
|
/**
|
|
* React ID utilities.
|
|
*/
|
|
|
|
getReactRootID: getReactRootID,
|
|
|
|
getID: getID,
|
|
|
|
setID: setID,
|
|
|
|
getNode: getNode,
|
|
|
|
getNodeFromInstance: getNodeFromInstance,
|
|
|
|
isValid: isValid,
|
|
|
|
purgeID: purgeID
|
|
};
|
|
|
|
ReactPerf.measureMethods(ReactMount, 'ReactMount', {
|
|
_renderNewRootComponent: '_renderNewRootComponent',
|
|
_mountImageIntoNode: '_mountImageIntoNode'
|
|
});
|
|
|
|
module.exports = ReactMount;
|
|
}).call(this,require('_process'))
|
|
},{"./DOMProperty":144,"./Object.assign":157,"./ReactBrowserEventEmitter":161,"./ReactCurrentOwner":169,"./ReactDOMFeatureFlags":174,"./ReactElement":187,"./ReactEmptyComponentRegistry":190,"./ReactInstanceHandles":196,"./ReactInstanceMap":197,"./ReactMarkupChecksum":199,"./ReactPerf":206,"./ReactReconciler":211,"./ReactUpdateQueue":217,"./ReactUpdates":218,"./instantiateReactComponent":253,"./setInnerHTML":259,"./shouldUpdateReactComponent":261,"./validateDOMNesting":263,"_process":133,"fbjs/lib/containsNode":61,"fbjs/lib/emptyObject":65,"fbjs/lib/invariant":72,"fbjs/lib/warning":83}],201:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactMultiChild
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactComponentEnvironment = require('./ReactComponentEnvironment');
|
|
var ReactMultiChildUpdateTypes = require('./ReactMultiChildUpdateTypes');
|
|
|
|
var ReactCurrentOwner = require('./ReactCurrentOwner');
|
|
var ReactReconciler = require('./ReactReconciler');
|
|
var ReactChildReconciler = require('./ReactChildReconciler');
|
|
|
|
var flattenChildren = require('./flattenChildren');
|
|
|
|
/**
|
|
* Updating children of a component may trigger recursive updates. The depth is
|
|
* used to batch recursive updates to render markup more efficiently.
|
|
*
|
|
* @type {number}
|
|
* @private
|
|
*/
|
|
var updateDepth = 0;
|
|
|
|
/**
|
|
* Queue of update configuration objects.
|
|
*
|
|
* Each object has a `type` property that is in `ReactMultiChildUpdateTypes`.
|
|
*
|
|
* @type {array<object>}
|
|
* @private
|
|
*/
|
|
var updateQueue = [];
|
|
|
|
/**
|
|
* Queue of markup to be rendered.
|
|
*
|
|
* @type {array<string>}
|
|
* @private
|
|
*/
|
|
var markupQueue = [];
|
|
|
|
/**
|
|
* Enqueues markup to be rendered and inserted at a supplied index.
|
|
*
|
|
* @param {string} parentID ID of the parent component.
|
|
* @param {string} markup Markup that renders into an element.
|
|
* @param {number} toIndex Destination index.
|
|
* @private
|
|
*/
|
|
function enqueueInsertMarkup(parentID, markup, toIndex) {
|
|
// NOTE: Null values reduce hidden classes.
|
|
updateQueue.push({
|
|
parentID: parentID,
|
|
parentNode: null,
|
|
type: ReactMultiChildUpdateTypes.INSERT_MARKUP,
|
|
markupIndex: markupQueue.push(markup) - 1,
|
|
content: null,
|
|
fromIndex: null,
|
|
toIndex: toIndex
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Enqueues moving an existing element to another index.
|
|
*
|
|
* @param {string} parentID ID of the parent component.
|
|
* @param {number} fromIndex Source index of the existing element.
|
|
* @param {number} toIndex Destination index of the element.
|
|
* @private
|
|
*/
|
|
function enqueueMove(parentID, fromIndex, toIndex) {
|
|
// NOTE: Null values reduce hidden classes.
|
|
updateQueue.push({
|
|
parentID: parentID,
|
|
parentNode: null,
|
|
type: ReactMultiChildUpdateTypes.MOVE_EXISTING,
|
|
markupIndex: null,
|
|
content: null,
|
|
fromIndex: fromIndex,
|
|
toIndex: toIndex
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Enqueues removing an element at an index.
|
|
*
|
|
* @param {string} parentID ID of the parent component.
|
|
* @param {number} fromIndex Index of the element to remove.
|
|
* @private
|
|
*/
|
|
function enqueueRemove(parentID, fromIndex) {
|
|
// NOTE: Null values reduce hidden classes.
|
|
updateQueue.push({
|
|
parentID: parentID,
|
|
parentNode: null,
|
|
type: ReactMultiChildUpdateTypes.REMOVE_NODE,
|
|
markupIndex: null,
|
|
content: null,
|
|
fromIndex: fromIndex,
|
|
toIndex: null
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Enqueues setting the markup of a node.
|
|
*
|
|
* @param {string} parentID ID of the parent component.
|
|
* @param {string} markup Markup that renders into an element.
|
|
* @private
|
|
*/
|
|
function enqueueSetMarkup(parentID, markup) {
|
|
// NOTE: Null values reduce hidden classes.
|
|
updateQueue.push({
|
|
parentID: parentID,
|
|
parentNode: null,
|
|
type: ReactMultiChildUpdateTypes.SET_MARKUP,
|
|
markupIndex: null,
|
|
content: markup,
|
|
fromIndex: null,
|
|
toIndex: null
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Enqueues setting the text content.
|
|
*
|
|
* @param {string} parentID ID of the parent component.
|
|
* @param {string} textContent Text content to set.
|
|
* @private
|
|
*/
|
|
function enqueueTextContent(parentID, textContent) {
|
|
// NOTE: Null values reduce hidden classes.
|
|
updateQueue.push({
|
|
parentID: parentID,
|
|
parentNode: null,
|
|
type: ReactMultiChildUpdateTypes.TEXT_CONTENT,
|
|
markupIndex: null,
|
|
content: textContent,
|
|
fromIndex: null,
|
|
toIndex: null
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Processes any enqueued updates.
|
|
*
|
|
* @private
|
|
*/
|
|
function processQueue() {
|
|
if (updateQueue.length) {
|
|
ReactComponentEnvironment.processChildrenUpdates(updateQueue, markupQueue);
|
|
clearQueue();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clears any enqueued updates.
|
|
*
|
|
* @private
|
|
*/
|
|
function clearQueue() {
|
|
updateQueue.length = 0;
|
|
markupQueue.length = 0;
|
|
}
|
|
|
|
/**
|
|
* ReactMultiChild are capable of reconciling multiple children.
|
|
*
|
|
* @class ReactMultiChild
|
|
* @internal
|
|
*/
|
|
var ReactMultiChild = {
|
|
|
|
/**
|
|
* Provides common functionality for components that must reconcile multiple
|
|
* children. This is used by `ReactDOMComponent` to mount, update, and
|
|
* unmount child components.
|
|
*
|
|
* @lends {ReactMultiChild.prototype}
|
|
*/
|
|
Mixin: {
|
|
|
|
_reconcilerInstantiateChildren: function (nestedChildren, transaction, context) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
if (this._currentElement) {
|
|
try {
|
|
ReactCurrentOwner.current = this._currentElement._owner;
|
|
return ReactChildReconciler.instantiateChildren(nestedChildren, transaction, context);
|
|
} finally {
|
|
ReactCurrentOwner.current = null;
|
|
}
|
|
}
|
|
}
|
|
return ReactChildReconciler.instantiateChildren(nestedChildren, transaction, context);
|
|
},
|
|
|
|
_reconcilerUpdateChildren: function (prevChildren, nextNestedChildrenElements, transaction, context) {
|
|
var nextChildren;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
if (this._currentElement) {
|
|
try {
|
|
ReactCurrentOwner.current = this._currentElement._owner;
|
|
nextChildren = flattenChildren(nextNestedChildrenElements);
|
|
} finally {
|
|
ReactCurrentOwner.current = null;
|
|
}
|
|
return ReactChildReconciler.updateChildren(prevChildren, nextChildren, transaction, context);
|
|
}
|
|
}
|
|
nextChildren = flattenChildren(nextNestedChildrenElements);
|
|
return ReactChildReconciler.updateChildren(prevChildren, nextChildren, transaction, context);
|
|
},
|
|
|
|
/**
|
|
* Generates a "mount image" for each of the supplied children. In the case
|
|
* of `ReactDOMComponent`, a mount image is a string of markup.
|
|
*
|
|
* @param {?object} nestedChildren Nested child maps.
|
|
* @return {array} An array of mounted representations.
|
|
* @internal
|
|
*/
|
|
mountChildren: function (nestedChildren, transaction, context) {
|
|
var children = this._reconcilerInstantiateChildren(nestedChildren, transaction, context);
|
|
this._renderedChildren = children;
|
|
var mountImages = [];
|
|
var index = 0;
|
|
for (var name in children) {
|
|
if (children.hasOwnProperty(name)) {
|
|
var child = children[name];
|
|
// Inlined for performance, see `ReactInstanceHandles.createReactID`.
|
|
var rootID = this._rootNodeID + name;
|
|
var mountImage = ReactReconciler.mountComponent(child, rootID, transaction, context);
|
|
child._mountIndex = index++;
|
|
mountImages.push(mountImage);
|
|
}
|
|
}
|
|
return mountImages;
|
|
},
|
|
|
|
/**
|
|
* Replaces any rendered children with a text content string.
|
|
*
|
|
* @param {string} nextContent String of content.
|
|
* @internal
|
|
*/
|
|
updateTextContent: function (nextContent) {
|
|
updateDepth++;
|
|
var errorThrown = true;
|
|
try {
|
|
var prevChildren = this._renderedChildren;
|
|
// Remove any rendered children.
|
|
ReactChildReconciler.unmountChildren(prevChildren);
|
|
// TODO: The setTextContent operation should be enough
|
|
for (var name in prevChildren) {
|
|
if (prevChildren.hasOwnProperty(name)) {
|
|
this._unmountChild(prevChildren[name]);
|
|
}
|
|
}
|
|
// Set new text content.
|
|
this.setTextContent(nextContent);
|
|
errorThrown = false;
|
|
} finally {
|
|
updateDepth--;
|
|
if (!updateDepth) {
|
|
if (errorThrown) {
|
|
clearQueue();
|
|
} else {
|
|
processQueue();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Replaces any rendered children with a markup string.
|
|
*
|
|
* @param {string} nextMarkup String of markup.
|
|
* @internal
|
|
*/
|
|
updateMarkup: function (nextMarkup) {
|
|
updateDepth++;
|
|
var errorThrown = true;
|
|
try {
|
|
var prevChildren = this._renderedChildren;
|
|
// Remove any rendered children.
|
|
ReactChildReconciler.unmountChildren(prevChildren);
|
|
for (var name in prevChildren) {
|
|
if (prevChildren.hasOwnProperty(name)) {
|
|
this._unmountChildByName(prevChildren[name], name);
|
|
}
|
|
}
|
|
this.setMarkup(nextMarkup);
|
|
errorThrown = false;
|
|
} finally {
|
|
updateDepth--;
|
|
if (!updateDepth) {
|
|
if (errorThrown) {
|
|
clearQueue();
|
|
} else {
|
|
processQueue();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Updates the rendered children with new children.
|
|
*
|
|
* @param {?object} nextNestedChildrenElements Nested child element maps.
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @internal
|
|
*/
|
|
updateChildren: function (nextNestedChildrenElements, transaction, context) {
|
|
updateDepth++;
|
|
var errorThrown = true;
|
|
try {
|
|
this._updateChildren(nextNestedChildrenElements, transaction, context);
|
|
errorThrown = false;
|
|
} finally {
|
|
updateDepth--;
|
|
if (!updateDepth) {
|
|
if (errorThrown) {
|
|
clearQueue();
|
|
} else {
|
|
processQueue();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Improve performance by isolating this hot code path from the try/catch
|
|
* block in `updateChildren`.
|
|
*
|
|
* @param {?object} nextNestedChildrenElements Nested child element maps.
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @final
|
|
* @protected
|
|
*/
|
|
_updateChildren: function (nextNestedChildrenElements, transaction, context) {
|
|
var prevChildren = this._renderedChildren;
|
|
var nextChildren = this._reconcilerUpdateChildren(prevChildren, nextNestedChildrenElements, transaction, context);
|
|
this._renderedChildren = nextChildren;
|
|
if (!nextChildren && !prevChildren) {
|
|
return;
|
|
}
|
|
var name;
|
|
// `nextIndex` will increment for each child in `nextChildren`, but
|
|
// `lastIndex` will be the last index visited in `prevChildren`.
|
|
var lastIndex = 0;
|
|
var nextIndex = 0;
|
|
for (name in nextChildren) {
|
|
if (!nextChildren.hasOwnProperty(name)) {
|
|
continue;
|
|
}
|
|
var prevChild = prevChildren && prevChildren[name];
|
|
var nextChild = nextChildren[name];
|
|
if (prevChild === nextChild) {
|
|
this.moveChild(prevChild, nextIndex, lastIndex);
|
|
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
|
|
prevChild._mountIndex = nextIndex;
|
|
} else {
|
|
if (prevChild) {
|
|
// Update `lastIndex` before `_mountIndex` gets unset by unmounting.
|
|
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
|
|
this._unmountChild(prevChild);
|
|
}
|
|
// The child must be instantiated before it's mounted.
|
|
this._mountChildByNameAtIndex(nextChild, name, nextIndex, transaction, context);
|
|
}
|
|
nextIndex++;
|
|
}
|
|
// Remove children that are no longer present.
|
|
for (name in prevChildren) {
|
|
if (prevChildren.hasOwnProperty(name) && !(nextChildren && nextChildren.hasOwnProperty(name))) {
|
|
this._unmountChild(prevChildren[name]);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Unmounts all rendered children. This should be used to clean up children
|
|
* when this component is unmounted.
|
|
*
|
|
* @internal
|
|
*/
|
|
unmountChildren: function () {
|
|
var renderedChildren = this._renderedChildren;
|
|
ReactChildReconciler.unmountChildren(renderedChildren);
|
|
this._renderedChildren = null;
|
|
},
|
|
|
|
/**
|
|
* Moves a child component to the supplied index.
|
|
*
|
|
* @param {ReactComponent} child Component to move.
|
|
* @param {number} toIndex Destination index of the element.
|
|
* @param {number} lastIndex Last index visited of the siblings of `child`.
|
|
* @protected
|
|
*/
|
|
moveChild: function (child, toIndex, lastIndex) {
|
|
// If the index of `child` is less than `lastIndex`, then it needs to
|
|
// be moved. Otherwise, we do not need to move it because a child will be
|
|
// inserted or moved before `child`.
|
|
if (child._mountIndex < lastIndex) {
|
|
enqueueMove(this._rootNodeID, child._mountIndex, toIndex);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Creates a child component.
|
|
*
|
|
* @param {ReactComponent} child Component to create.
|
|
* @param {string} mountImage Markup to insert.
|
|
* @protected
|
|
*/
|
|
createChild: function (child, mountImage) {
|
|
enqueueInsertMarkup(this._rootNodeID, mountImage, child._mountIndex);
|
|
},
|
|
|
|
/**
|
|
* Removes a child component.
|
|
*
|
|
* @param {ReactComponent} child Child to remove.
|
|
* @protected
|
|
*/
|
|
removeChild: function (child) {
|
|
enqueueRemove(this._rootNodeID, child._mountIndex);
|
|
},
|
|
|
|
/**
|
|
* Sets this text content string.
|
|
*
|
|
* @param {string} textContent Text content to set.
|
|
* @protected
|
|
*/
|
|
setTextContent: function (textContent) {
|
|
enqueueTextContent(this._rootNodeID, textContent);
|
|
},
|
|
|
|
/**
|
|
* Sets this markup string.
|
|
*
|
|
* @param {string} markup Markup to set.
|
|
* @protected
|
|
*/
|
|
setMarkup: function (markup) {
|
|
enqueueSetMarkup(this._rootNodeID, markup);
|
|
},
|
|
|
|
/**
|
|
* Mounts a child with the supplied name.
|
|
*
|
|
* NOTE: This is part of `updateChildren` and is here for readability.
|
|
*
|
|
* @param {ReactComponent} child Component to mount.
|
|
* @param {string} name Name of the child.
|
|
* @param {number} index Index at which to insert the child.
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @private
|
|
*/
|
|
_mountChildByNameAtIndex: function (child, name, index, transaction, context) {
|
|
// Inlined for performance, see `ReactInstanceHandles.createReactID`.
|
|
var rootID = this._rootNodeID + name;
|
|
var mountImage = ReactReconciler.mountComponent(child, rootID, transaction, context);
|
|
child._mountIndex = index;
|
|
this.createChild(child, mountImage);
|
|
},
|
|
|
|
/**
|
|
* Unmounts a rendered child.
|
|
*
|
|
* NOTE: This is part of `updateChildren` and is here for readability.
|
|
*
|
|
* @param {ReactComponent} child Component to unmount.
|
|
* @private
|
|
*/
|
|
_unmountChild: function (child) {
|
|
this.removeChild(child);
|
|
child._mountIndex = null;
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactMultiChild;
|
|
}).call(this,require('_process'))
|
|
},{"./ReactChildReconciler":162,"./ReactComponentEnvironment":167,"./ReactCurrentOwner":169,"./ReactMultiChildUpdateTypes":202,"./ReactReconciler":211,"./flattenChildren":244,"_process":133}],202:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactMultiChildUpdateTypes
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var keyMirror = require('fbjs/lib/keyMirror');
|
|
|
|
/**
|
|
* When a component's children are updated, a series of update configuration
|
|
* objects are created in order to batch and serialize the required changes.
|
|
*
|
|
* Enumerates all the possible types of update configurations.
|
|
*
|
|
* @internal
|
|
*/
|
|
var ReactMultiChildUpdateTypes = keyMirror({
|
|
INSERT_MARKUP: null,
|
|
MOVE_EXISTING: null,
|
|
REMOVE_NODE: null,
|
|
SET_MARKUP: null,
|
|
TEXT_CONTENT: null
|
|
});
|
|
|
|
module.exports = ReactMultiChildUpdateTypes;
|
|
},{"fbjs/lib/keyMirror":75}],203:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2014-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactNativeComponent
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var assign = require('./Object.assign');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
var autoGenerateWrapperClass = null;
|
|
var genericComponentClass = null;
|
|
// This registry keeps track of wrapper classes around native tags.
|
|
var tagToComponentClass = {};
|
|
var textComponentClass = null;
|
|
|
|
var ReactNativeComponentInjection = {
|
|
// This accepts a class that receives the tag string. This is a catch all
|
|
// that can render any kind of tag.
|
|
injectGenericComponentClass: function (componentClass) {
|
|
genericComponentClass = componentClass;
|
|
},
|
|
// This accepts a text component class that takes the text string to be
|
|
// rendered as props.
|
|
injectTextComponentClass: function (componentClass) {
|
|
textComponentClass = componentClass;
|
|
},
|
|
// This accepts a keyed object with classes as values. Each key represents a
|
|
// tag. That particular tag will use this class instead of the generic one.
|
|
injectComponentClasses: function (componentClasses) {
|
|
assign(tagToComponentClass, componentClasses);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get a composite component wrapper class for a specific tag.
|
|
*
|
|
* @param {ReactElement} element The tag for which to get the class.
|
|
* @return {function} The React class constructor function.
|
|
*/
|
|
function getComponentClassForElement(element) {
|
|
if (typeof element.type === 'function') {
|
|
return element.type;
|
|
}
|
|
var tag = element.type;
|
|
var componentClass = tagToComponentClass[tag];
|
|
if (componentClass == null) {
|
|
tagToComponentClass[tag] = componentClass = autoGenerateWrapperClass(tag);
|
|
}
|
|
return componentClass;
|
|
}
|
|
|
|
/**
|
|
* Get a native internal component class for a specific tag.
|
|
*
|
|
* @param {ReactElement} element The element to create.
|
|
* @return {function} The internal class constructor function.
|
|
*/
|
|
function createInternalComponent(element) {
|
|
!genericComponentClass ? process.env.NODE_ENV !== 'production' ? invariant(false, 'There is no registered component for the tag %s', element.type) : invariant(false) : undefined;
|
|
return new genericComponentClass(element.type, element.props);
|
|
}
|
|
|
|
/**
|
|
* @param {ReactText} text
|
|
* @return {ReactComponent}
|
|
*/
|
|
function createInstanceForText(text) {
|
|
return new textComponentClass(text);
|
|
}
|
|
|
|
/**
|
|
* @param {ReactComponent} component
|
|
* @return {boolean}
|
|
*/
|
|
function isTextComponent(component) {
|
|
return component instanceof textComponentClass;
|
|
}
|
|
|
|
var ReactNativeComponent = {
|
|
getComponentClassForElement: getComponentClassForElement,
|
|
createInternalComponent: createInternalComponent,
|
|
createInstanceForText: createInstanceForText,
|
|
isTextComponent: isTextComponent,
|
|
injection: ReactNativeComponentInjection
|
|
};
|
|
|
|
module.exports = ReactNativeComponent;
|
|
}).call(this,require('_process'))
|
|
},{"./Object.assign":157,"_process":133,"fbjs/lib/invariant":72}],204:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactNoopUpdateQueue
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
function warnTDZ(publicInstance, callerName) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, '%s(...): Can only update a mounted or mounting component. ' + 'This usually means you called %s() on an unmounted component. ' + 'This is a no-op. Please check the code for the %s component.', callerName, callerName, publicInstance.constructor && publicInstance.constructor.displayName || '') : undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is the abstract API for an update queue.
|
|
*/
|
|
var ReactNoopUpdateQueue = {
|
|
|
|
/**
|
|
* Checks whether or not this composite component is mounted.
|
|
* @param {ReactClass} publicInstance The instance we want to test.
|
|
* @return {boolean} True if mounted, false otherwise.
|
|
* @protected
|
|
* @final
|
|
*/
|
|
isMounted: function (publicInstance) {
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Enqueue a callback that will be executed after all the pending updates
|
|
* have processed.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance to use as `this` context.
|
|
* @param {?function} callback Called after state is updated.
|
|
* @internal
|
|
*/
|
|
enqueueCallback: function (publicInstance, callback) {},
|
|
|
|
/**
|
|
* Forces an update. This should only be invoked when it is known with
|
|
* certainty that we are **not** in a DOM transaction.
|
|
*
|
|
* You may want to call this when you know that some deeper aspect of the
|
|
* component's state has changed but `setState` was not called.
|
|
*
|
|
* This will not invoke `shouldComponentUpdate`, but it will invoke
|
|
* `componentWillUpdate` and `componentDidUpdate`.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @internal
|
|
*/
|
|
enqueueForceUpdate: function (publicInstance) {
|
|
warnTDZ(publicInstance, 'forceUpdate');
|
|
},
|
|
|
|
/**
|
|
* Replaces all of the state. Always use this or `setState` to mutate state.
|
|
* You should treat `this.state` as immutable.
|
|
*
|
|
* There is no guarantee that `this.state` will be immediately updated, so
|
|
* accessing `this.state` after calling this method may return the old value.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @param {object} completeState Next state.
|
|
* @internal
|
|
*/
|
|
enqueueReplaceState: function (publicInstance, completeState) {
|
|
warnTDZ(publicInstance, 'replaceState');
|
|
},
|
|
|
|
/**
|
|
* Sets a subset of the state. This only exists because _pendingState is
|
|
* internal. This provides a merging strategy that is not available to deep
|
|
* properties which is confusing. TODO: Expose pendingState or don't use it
|
|
* during the merge.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @param {object} partialState Next partial state to be merged with state.
|
|
* @internal
|
|
*/
|
|
enqueueSetState: function (publicInstance, partialState) {
|
|
warnTDZ(publicInstance, 'setState');
|
|
},
|
|
|
|
/**
|
|
* Sets a subset of the props.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @param {object} partialProps Subset of the next props.
|
|
* @internal
|
|
*/
|
|
enqueueSetProps: function (publicInstance, partialProps) {
|
|
warnTDZ(publicInstance, 'setProps');
|
|
},
|
|
|
|
/**
|
|
* Replaces all of the props.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @param {object} props New props.
|
|
* @internal
|
|
*/
|
|
enqueueReplaceProps: function (publicInstance, props) {
|
|
warnTDZ(publicInstance, 'replaceProps');
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactNoopUpdateQueue;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133,"fbjs/lib/warning":83}],205:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactOwner
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
/**
|
|
* ReactOwners are capable of storing references to owned components.
|
|
*
|
|
* All components are capable of //being// referenced by owner components, but
|
|
* only ReactOwner components are capable of //referencing// owned components.
|
|
* The named reference is known as a "ref".
|
|
*
|
|
* Refs are available when mounted and updated during reconciliation.
|
|
*
|
|
* var MyComponent = React.createClass({
|
|
* render: function() {
|
|
* return (
|
|
* <div onClick={this.handleClick}>
|
|
* <CustomComponent ref="custom" />
|
|
* </div>
|
|
* );
|
|
* },
|
|
* handleClick: function() {
|
|
* this.refs.custom.handleClick();
|
|
* },
|
|
* componentDidMount: function() {
|
|
* this.refs.custom.initialize();
|
|
* }
|
|
* });
|
|
*
|
|
* Refs should rarely be used. When refs are used, they should only be done to
|
|
* control data that is not handled by React's data flow.
|
|
*
|
|
* @class ReactOwner
|
|
*/
|
|
var ReactOwner = {
|
|
|
|
/**
|
|
* @param {?object} object
|
|
* @return {boolean} True if `object` is a valid owner.
|
|
* @final
|
|
*/
|
|
isValidOwner: function (object) {
|
|
return !!(object && typeof object.attachRef === 'function' && typeof object.detachRef === 'function');
|
|
},
|
|
|
|
/**
|
|
* Adds a component by ref to an owner component.
|
|
*
|
|
* @param {ReactComponent} component Component to reference.
|
|
* @param {string} ref Name by which to refer to the component.
|
|
* @param {ReactOwner} owner Component on which to record the ref.
|
|
* @final
|
|
* @internal
|
|
*/
|
|
addComponentAsRefTo: function (component, ref, owner) {
|
|
!ReactOwner.isValidOwner(owner) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'addComponentAsRefTo(...): Only a ReactOwner can have refs. You might ' + 'be adding a ref to a component that was not created inside a component\'s ' + '`render` method, or you have multiple copies of React loaded ' + '(details: https://fb.me/react-refs-must-have-owner).') : invariant(false) : undefined;
|
|
owner.attachRef(ref, component);
|
|
},
|
|
|
|
/**
|
|
* Removes a component by ref from an owner component.
|
|
*
|
|
* @param {ReactComponent} component Component to dereference.
|
|
* @param {string} ref Name of the ref to remove.
|
|
* @param {ReactOwner} owner Component on which the ref is recorded.
|
|
* @final
|
|
* @internal
|
|
*/
|
|
removeComponentAsRefFrom: function (component, ref, owner) {
|
|
!ReactOwner.isValidOwner(owner) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'removeComponentAsRefFrom(...): Only a ReactOwner can have refs. You might ' + 'be removing a ref to a component that was not created inside a component\'s ' + '`render` method, or you have multiple copies of React loaded ' + '(details: https://fb.me/react-refs-must-have-owner).') : invariant(false) : undefined;
|
|
// Check that `component` is still the current ref because we do not want to
|
|
// detach the ref if another component stole it.
|
|
if (owner.getPublicInstance().refs[ref] === component.getPublicInstance()) {
|
|
owner.detachRef(ref);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactOwner;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133,"fbjs/lib/invariant":72}],206:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactPerf
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* ReactPerf is a general AOP system designed to measure performance. This
|
|
* module only has the hooks: see ReactDefaultPerf for the analysis tool.
|
|
*/
|
|
var ReactPerf = {
|
|
/**
|
|
* Boolean to enable/disable measurement. Set to false by default to prevent
|
|
* accidental logging and perf loss.
|
|
*/
|
|
enableMeasure: false,
|
|
|
|
/**
|
|
* Holds onto the measure function in use. By default, don't measure
|
|
* anything, but we'll override this if we inject a measure function.
|
|
*/
|
|
storedMeasure: _noMeasure,
|
|
|
|
/**
|
|
* @param {object} object
|
|
* @param {string} objectName
|
|
* @param {object<string>} methodNames
|
|
*/
|
|
measureMethods: function (object, objectName, methodNames) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
for (var key in methodNames) {
|
|
if (!methodNames.hasOwnProperty(key)) {
|
|
continue;
|
|
}
|
|
object[key] = ReactPerf.measure(objectName, methodNames[key], object[key]);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Use this to wrap methods you want to measure. Zero overhead in production.
|
|
*
|
|
* @param {string} objName
|
|
* @param {string} fnName
|
|
* @param {function} func
|
|
* @return {function}
|
|
*/
|
|
measure: function (objName, fnName, func) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var measuredFunc = null;
|
|
var wrapper = function () {
|
|
if (ReactPerf.enableMeasure) {
|
|
if (!measuredFunc) {
|
|
measuredFunc = ReactPerf.storedMeasure(objName, fnName, func);
|
|
}
|
|
return measuredFunc.apply(this, arguments);
|
|
}
|
|
return func.apply(this, arguments);
|
|
};
|
|
wrapper.displayName = objName + '_' + fnName;
|
|
return wrapper;
|
|
}
|
|
return func;
|
|
},
|
|
|
|
injection: {
|
|
/**
|
|
* @param {function} measure
|
|
*/
|
|
injectMeasure: function (measure) {
|
|
ReactPerf.storedMeasure = measure;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Simply passes through the measured function, without measuring it.
|
|
*
|
|
* @param {string} objName
|
|
* @param {string} fnName
|
|
* @param {function} func
|
|
* @return {function}
|
|
*/
|
|
function _noMeasure(objName, fnName, func) {
|
|
return func;
|
|
}
|
|
|
|
module.exports = ReactPerf;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133}],207:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactPropTypeLocationNames
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactPropTypeLocationNames = {};
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
ReactPropTypeLocationNames = {
|
|
prop: 'prop',
|
|
context: 'context',
|
|
childContext: 'child context'
|
|
};
|
|
}
|
|
|
|
module.exports = ReactPropTypeLocationNames;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133}],208:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactPropTypeLocations
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var keyMirror = require('fbjs/lib/keyMirror');
|
|
|
|
var ReactPropTypeLocations = keyMirror({
|
|
prop: null,
|
|
context: null,
|
|
childContext: null
|
|
});
|
|
|
|
module.exports = ReactPropTypeLocations;
|
|
},{"fbjs/lib/keyMirror":75}],209:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactPropTypes
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactElement = require('./ReactElement');
|
|
var ReactPropTypeLocationNames = require('./ReactPropTypeLocationNames');
|
|
|
|
var emptyFunction = require('fbjs/lib/emptyFunction');
|
|
var getIteratorFn = require('./getIteratorFn');
|
|
|
|
/**
|
|
* Collection of methods that allow declaration and validation of props that are
|
|
* supplied to React components. Example usage:
|
|
*
|
|
* var Props = require('ReactPropTypes');
|
|
* var MyArticle = React.createClass({
|
|
* propTypes: {
|
|
* // An optional string prop named "description".
|
|
* description: Props.string,
|
|
*
|
|
* // A required enum prop named "category".
|
|
* category: Props.oneOf(['News','Photos']).isRequired,
|
|
*
|
|
* // A prop named "dialog" that requires an instance of Dialog.
|
|
* dialog: Props.instanceOf(Dialog).isRequired
|
|
* },
|
|
* render: function() { ... }
|
|
* });
|
|
*
|
|
* A more formal specification of how these methods are used:
|
|
*
|
|
* type := array|bool|func|object|number|string|oneOf([...])|instanceOf(...)
|
|
* decl := ReactPropTypes.{type}(.isRequired)?
|
|
*
|
|
* Each and every declaration produces a function with the same signature. This
|
|
* allows the creation of custom validation functions. For example:
|
|
*
|
|
* var MyLink = React.createClass({
|
|
* propTypes: {
|
|
* // An optional string or URI prop named "href".
|
|
* href: function(props, propName, componentName) {
|
|
* var propValue = props[propName];
|
|
* if (propValue != null && typeof propValue !== 'string' &&
|
|
* !(propValue instanceof URI)) {
|
|
* return new Error(
|
|
* 'Expected a string or an URI for ' + propName + ' in ' +
|
|
* componentName
|
|
* );
|
|
* }
|
|
* }
|
|
* },
|
|
* render: function() {...}
|
|
* });
|
|
*
|
|
* @internal
|
|
*/
|
|
|
|
var ANONYMOUS = '<<anonymous>>';
|
|
|
|
var ReactPropTypes = {
|
|
array: createPrimitiveTypeChecker('array'),
|
|
bool: createPrimitiveTypeChecker('boolean'),
|
|
func: createPrimitiveTypeChecker('function'),
|
|
number: createPrimitiveTypeChecker('number'),
|
|
object: createPrimitiveTypeChecker('object'),
|
|
string: createPrimitiveTypeChecker('string'),
|
|
|
|
any: createAnyTypeChecker(),
|
|
arrayOf: createArrayOfTypeChecker,
|
|
element: createElementTypeChecker(),
|
|
instanceOf: createInstanceTypeChecker,
|
|
node: createNodeChecker(),
|
|
objectOf: createObjectOfTypeChecker,
|
|
oneOf: createEnumTypeChecker,
|
|
oneOfType: createUnionTypeChecker,
|
|
shape: createShapeTypeChecker
|
|
};
|
|
|
|
function createChainableTypeChecker(validate) {
|
|
function checkType(isRequired, props, propName, componentName, location, propFullName) {
|
|
componentName = componentName || ANONYMOUS;
|
|
propFullName = propFullName || propName;
|
|
if (props[propName] == null) {
|
|
var locationName = ReactPropTypeLocationNames[location];
|
|
if (isRequired) {
|
|
return new Error('Required ' + locationName + ' `' + propFullName + '` was not specified in ' + ('`' + componentName + '`.'));
|
|
}
|
|
return null;
|
|
} else {
|
|
return validate(props, propName, componentName, location, propFullName);
|
|
}
|
|
}
|
|
|
|
var chainedCheckType = checkType.bind(null, false);
|
|
chainedCheckType.isRequired = checkType.bind(null, true);
|
|
|
|
return chainedCheckType;
|
|
}
|
|
|
|
function createPrimitiveTypeChecker(expectedType) {
|
|
function validate(props, propName, componentName, location, propFullName) {
|
|
var propValue = props[propName];
|
|
var propType = getPropType(propValue);
|
|
if (propType !== expectedType) {
|
|
var locationName = ReactPropTypeLocationNames[location];
|
|
// `propValue` being instance of, say, date/regexp, pass the 'object'
|
|
// check, but we can offer a more precise error message here rather than
|
|
// 'of type `object`'.
|
|
var preciseType = getPreciseType(propValue);
|
|
|
|
return new Error('Invalid ' + locationName + ' `' + propFullName + '` of type ' + ('`' + preciseType + '` supplied to `' + componentName + '`, expected ') + ('`' + expectedType + '`.'));
|
|
}
|
|
return null;
|
|
}
|
|
return createChainableTypeChecker(validate);
|
|
}
|
|
|
|
function createAnyTypeChecker() {
|
|
return createChainableTypeChecker(emptyFunction.thatReturns(null));
|
|
}
|
|
|
|
function createArrayOfTypeChecker(typeChecker) {
|
|
function validate(props, propName, componentName, location, propFullName) {
|
|
var propValue = props[propName];
|
|
if (!Array.isArray(propValue)) {
|
|
var locationName = ReactPropTypeLocationNames[location];
|
|
var propType = getPropType(propValue);
|
|
return new Error('Invalid ' + locationName + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an array.'));
|
|
}
|
|
for (var i = 0; i < propValue.length; i++) {
|
|
var error = typeChecker(propValue, i, componentName, location, propFullName + '[' + i + ']');
|
|
if (error instanceof Error) {
|
|
return error;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
return createChainableTypeChecker(validate);
|
|
}
|
|
|
|
function createElementTypeChecker() {
|
|
function validate(props, propName, componentName, location, propFullName) {
|
|
if (!ReactElement.isValidElement(props[propName])) {
|
|
var locationName = ReactPropTypeLocationNames[location];
|
|
return new Error('Invalid ' + locationName + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`, expected a single ReactElement.'));
|
|
}
|
|
return null;
|
|
}
|
|
return createChainableTypeChecker(validate);
|
|
}
|
|
|
|
function createInstanceTypeChecker(expectedClass) {
|
|
function validate(props, propName, componentName, location, propFullName) {
|
|
if (!(props[propName] instanceof expectedClass)) {
|
|
var locationName = ReactPropTypeLocationNames[location];
|
|
var expectedClassName = expectedClass.name || ANONYMOUS;
|
|
var actualClassName = getClassName(props[propName]);
|
|
return new Error('Invalid ' + locationName + ' `' + propFullName + '` of type ' + ('`' + actualClassName + '` supplied to `' + componentName + '`, expected ') + ('instance of `' + expectedClassName + '`.'));
|
|
}
|
|
return null;
|
|
}
|
|
return createChainableTypeChecker(validate);
|
|
}
|
|
|
|
function createEnumTypeChecker(expectedValues) {
|
|
if (!Array.isArray(expectedValues)) {
|
|
return createChainableTypeChecker(function () {
|
|
return new Error('Invalid argument supplied to oneOf, expected an instance of array.');
|
|
});
|
|
}
|
|
|
|
function validate(props, propName, componentName, location, propFullName) {
|
|
var propValue = props[propName];
|
|
for (var i = 0; i < expectedValues.length; i++) {
|
|
if (propValue === expectedValues[i]) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
var locationName = ReactPropTypeLocationNames[location];
|
|
var valuesString = JSON.stringify(expectedValues);
|
|
return new Error('Invalid ' + locationName + ' `' + propFullName + '` of value `' + propValue + '` ' + ('supplied to `' + componentName + '`, expected one of ' + valuesString + '.'));
|
|
}
|
|
return createChainableTypeChecker(validate);
|
|
}
|
|
|
|
function createObjectOfTypeChecker(typeChecker) {
|
|
function validate(props, propName, componentName, location, propFullName) {
|
|
var propValue = props[propName];
|
|
var propType = getPropType(propValue);
|
|
if (propType !== 'object') {
|
|
var locationName = ReactPropTypeLocationNames[location];
|
|
return new Error('Invalid ' + locationName + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an object.'));
|
|
}
|
|
for (var key in propValue) {
|
|
if (propValue.hasOwnProperty(key)) {
|
|
var error = typeChecker(propValue, key, componentName, location, propFullName + '.' + key);
|
|
if (error instanceof Error) {
|
|
return error;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
return createChainableTypeChecker(validate);
|
|
}
|
|
|
|
function createUnionTypeChecker(arrayOfTypeCheckers) {
|
|
if (!Array.isArray(arrayOfTypeCheckers)) {
|
|
return createChainableTypeChecker(function () {
|
|
return new Error('Invalid argument supplied to oneOfType, expected an instance of array.');
|
|
});
|
|
}
|
|
|
|
function validate(props, propName, componentName, location, propFullName) {
|
|
for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
|
|
var checker = arrayOfTypeCheckers[i];
|
|
if (checker(props, propName, componentName, location, propFullName) == null) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
var locationName = ReactPropTypeLocationNames[location];
|
|
return new Error('Invalid ' + locationName + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`.'));
|
|
}
|
|
return createChainableTypeChecker(validate);
|
|
}
|
|
|
|
function createNodeChecker() {
|
|
function validate(props, propName, componentName, location, propFullName) {
|
|
if (!isNode(props[propName])) {
|
|
var locationName = ReactPropTypeLocationNames[location];
|
|
return new Error('Invalid ' + locationName + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`, expected a ReactNode.'));
|
|
}
|
|
return null;
|
|
}
|
|
return createChainableTypeChecker(validate);
|
|
}
|
|
|
|
function createShapeTypeChecker(shapeTypes) {
|
|
function validate(props, propName, componentName, location, propFullName) {
|
|
var propValue = props[propName];
|
|
var propType = getPropType(propValue);
|
|
if (propType !== 'object') {
|
|
var locationName = ReactPropTypeLocationNames[location];
|
|
return new Error('Invalid ' + locationName + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));
|
|
}
|
|
for (var key in shapeTypes) {
|
|
var checker = shapeTypes[key];
|
|
if (!checker) {
|
|
continue;
|
|
}
|
|
var error = checker(propValue, key, componentName, location, propFullName + '.' + key);
|
|
if (error) {
|
|
return error;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
return createChainableTypeChecker(validate);
|
|
}
|
|
|
|
function isNode(propValue) {
|
|
switch (typeof propValue) {
|
|
case 'number':
|
|
case 'string':
|
|
case 'undefined':
|
|
return true;
|
|
case 'boolean':
|
|
return !propValue;
|
|
case 'object':
|
|
if (Array.isArray(propValue)) {
|
|
return propValue.every(isNode);
|
|
}
|
|
if (propValue === null || ReactElement.isValidElement(propValue)) {
|
|
return true;
|
|
}
|
|
|
|
var iteratorFn = getIteratorFn(propValue);
|
|
if (iteratorFn) {
|
|
var iterator = iteratorFn.call(propValue);
|
|
var step;
|
|
if (iteratorFn !== propValue.entries) {
|
|
while (!(step = iterator.next()).done) {
|
|
if (!isNode(step.value)) {
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
// Iterator will provide entry [k,v] tuples rather than values.
|
|
while (!(step = iterator.next()).done) {
|
|
var entry = step.value;
|
|
if (entry) {
|
|
if (!isNode(entry[1])) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Equivalent of `typeof` but with special handling for array and regexp.
|
|
function getPropType(propValue) {
|
|
var propType = typeof propValue;
|
|
if (Array.isArray(propValue)) {
|
|
return 'array';
|
|
}
|
|
if (propValue instanceof RegExp) {
|
|
// Old webkits (at least until Android 4.0) return 'function' rather than
|
|
// 'object' for typeof a RegExp. We'll normalize this here so that /bla/
|
|
// passes PropTypes.object.
|
|
return 'object';
|
|
}
|
|
return propType;
|
|
}
|
|
|
|
// This handles more types than `getPropType`. Only used for error messages.
|
|
// See `createPrimitiveTypeChecker`.
|
|
function getPreciseType(propValue) {
|
|
var propType = getPropType(propValue);
|
|
if (propType === 'object') {
|
|
if (propValue instanceof Date) {
|
|
return 'date';
|
|
} else if (propValue instanceof RegExp) {
|
|
return 'regexp';
|
|
}
|
|
}
|
|
return propType;
|
|
}
|
|
|
|
// Returns class name of the object, if any.
|
|
function getClassName(propValue) {
|
|
if (!propValue.constructor || !propValue.constructor.name) {
|
|
return '<<anonymous>>';
|
|
}
|
|
return propValue.constructor.name;
|
|
}
|
|
|
|
module.exports = ReactPropTypes;
|
|
},{"./ReactElement":187,"./ReactPropTypeLocationNames":207,"./getIteratorFn":250,"fbjs/lib/emptyFunction":64}],210:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactReconcileTransaction
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var CallbackQueue = require('./CallbackQueue');
|
|
var PooledClass = require('./PooledClass');
|
|
var ReactBrowserEventEmitter = require('./ReactBrowserEventEmitter');
|
|
var ReactDOMFeatureFlags = require('./ReactDOMFeatureFlags');
|
|
var ReactInputSelection = require('./ReactInputSelection');
|
|
var Transaction = require('./Transaction');
|
|
|
|
var assign = require('./Object.assign');
|
|
|
|
/**
|
|
* Ensures that, when possible, the selection range (currently selected text
|
|
* input) is not disturbed by performing the transaction.
|
|
*/
|
|
var SELECTION_RESTORATION = {
|
|
/**
|
|
* @return {Selection} Selection information.
|
|
*/
|
|
initialize: ReactInputSelection.getSelectionInformation,
|
|
/**
|
|
* @param {Selection} sel Selection information returned from `initialize`.
|
|
*/
|
|
close: ReactInputSelection.restoreSelection
|
|
};
|
|
|
|
/**
|
|
* Suppresses events (blur/focus) that could be inadvertently dispatched due to
|
|
* high level DOM manipulations (like temporarily removing a text input from the
|
|
* DOM).
|
|
*/
|
|
var EVENT_SUPPRESSION = {
|
|
/**
|
|
* @return {boolean} The enabled status of `ReactBrowserEventEmitter` before
|
|
* the reconciliation.
|
|
*/
|
|
initialize: function () {
|
|
var currentlyEnabled = ReactBrowserEventEmitter.isEnabled();
|
|
ReactBrowserEventEmitter.setEnabled(false);
|
|
return currentlyEnabled;
|
|
},
|
|
|
|
/**
|
|
* @param {boolean} previouslyEnabled Enabled status of
|
|
* `ReactBrowserEventEmitter` before the reconciliation occurred. `close`
|
|
* restores the previous value.
|
|
*/
|
|
close: function (previouslyEnabled) {
|
|
ReactBrowserEventEmitter.setEnabled(previouslyEnabled);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Provides a queue for collecting `componentDidMount` and
|
|
* `componentDidUpdate` callbacks during the the transaction.
|
|
*/
|
|
var ON_DOM_READY_QUEUEING = {
|
|
/**
|
|
* Initializes the internal `onDOMReady` queue.
|
|
*/
|
|
initialize: function () {
|
|
this.reactMountReady.reset();
|
|
},
|
|
|
|
/**
|
|
* After DOM is flushed, invoke all registered `onDOMReady` callbacks.
|
|
*/
|
|
close: function () {
|
|
this.reactMountReady.notifyAll();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Executed within the scope of the `Transaction` instance. Consider these as
|
|
* being member methods, but with an implied ordering while being isolated from
|
|
* each other.
|
|
*/
|
|
var TRANSACTION_WRAPPERS = [SELECTION_RESTORATION, EVENT_SUPPRESSION, ON_DOM_READY_QUEUEING];
|
|
|
|
/**
|
|
* Currently:
|
|
* - The order that these are listed in the transaction is critical:
|
|
* - Suppresses events.
|
|
* - Restores selection range.
|
|
*
|
|
* Future:
|
|
* - Restore document/overflow scroll positions that were unintentionally
|
|
* modified via DOM insertions above the top viewport boundary.
|
|
* - Implement/integrate with customized constraint based layout system and keep
|
|
* track of which dimensions must be remeasured.
|
|
*
|
|
* @class ReactReconcileTransaction
|
|
*/
|
|
function ReactReconcileTransaction(forceHTML) {
|
|
this.reinitializeTransaction();
|
|
// Only server-side rendering really needs this option (see
|
|
// `ReactServerRendering`), but server-side uses
|
|
// `ReactServerRenderingTransaction` instead. This option is here so that it's
|
|
// accessible and defaults to false when `ReactDOMComponent` and
|
|
// `ReactTextComponent` checks it in `mountComponent`.`
|
|
this.renderToStaticMarkup = false;
|
|
this.reactMountReady = CallbackQueue.getPooled(null);
|
|
this.useCreateElement = !forceHTML && ReactDOMFeatureFlags.useCreateElement;
|
|
}
|
|
|
|
var Mixin = {
|
|
/**
|
|
* @see Transaction
|
|
* @abstract
|
|
* @final
|
|
* @return {array<object>} List of operation wrap procedures.
|
|
* TODO: convert to array<TransactionWrapper>
|
|
*/
|
|
getTransactionWrappers: function () {
|
|
return TRANSACTION_WRAPPERS;
|
|
},
|
|
|
|
/**
|
|
* @return {object} The queue to collect `onDOMReady` callbacks with.
|
|
*/
|
|
getReactMountReady: function () {
|
|
return this.reactMountReady;
|
|
},
|
|
|
|
/**
|
|
* `PooledClass` looks for this, and will invoke this before allowing this
|
|
* instance to be reused.
|
|
*/
|
|
destructor: function () {
|
|
CallbackQueue.release(this.reactMountReady);
|
|
this.reactMountReady = null;
|
|
}
|
|
};
|
|
|
|
assign(ReactReconcileTransaction.prototype, Transaction.Mixin, Mixin);
|
|
|
|
PooledClass.addPoolingTo(ReactReconcileTransaction);
|
|
|
|
module.exports = ReactReconcileTransaction;
|
|
},{"./CallbackQueue":140,"./Object.assign":157,"./PooledClass":158,"./ReactBrowserEventEmitter":161,"./ReactDOMFeatureFlags":174,"./ReactInputSelection":195,"./Transaction":235}],211:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactReconciler
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactRef = require('./ReactRef');
|
|
|
|
/**
|
|
* Helper to call ReactRef.attachRefs with this composite component, split out
|
|
* to avoid allocations in the transaction mount-ready queue.
|
|
*/
|
|
function attachRefs() {
|
|
ReactRef.attachRefs(this, this._currentElement);
|
|
}
|
|
|
|
var ReactReconciler = {
|
|
|
|
/**
|
|
* Initializes the component, renders markup, and registers event listeners.
|
|
*
|
|
* @param {ReactComponent} internalInstance
|
|
* @param {string} rootID DOM ID of the root node.
|
|
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
|
* @return {?string} Rendered markup to be inserted into the DOM.
|
|
* @final
|
|
* @internal
|
|
*/
|
|
mountComponent: function (internalInstance, rootID, transaction, context) {
|
|
var markup = internalInstance.mountComponent(rootID, transaction, context);
|
|
if (internalInstance._currentElement && internalInstance._currentElement.ref != null) {
|
|
transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
|
|
}
|
|
return markup;
|
|
},
|
|
|
|
/**
|
|
* Releases any resources allocated by `mountComponent`.
|
|
*
|
|
* @final
|
|
* @internal
|
|
*/
|
|
unmountComponent: function (internalInstance) {
|
|
ReactRef.detachRefs(internalInstance, internalInstance._currentElement);
|
|
internalInstance.unmountComponent();
|
|
},
|
|
|
|
/**
|
|
* Update a component using a new element.
|
|
*
|
|
* @param {ReactComponent} internalInstance
|
|
* @param {ReactElement} nextElement
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @param {object} context
|
|
* @internal
|
|
*/
|
|
receiveComponent: function (internalInstance, nextElement, transaction, context) {
|
|
var prevElement = internalInstance._currentElement;
|
|
|
|
if (nextElement === prevElement && context === internalInstance._context) {
|
|
// Since elements are immutable after the owner is rendered,
|
|
// we can do a cheap identity compare here to determine if this is a
|
|
// superfluous reconcile. It's possible for state to be mutable but such
|
|
// change should trigger an update of the owner which would recreate
|
|
// the element. We explicitly check for the existence of an owner since
|
|
// it's possible for an element created outside a composite to be
|
|
// deeply mutated and reused.
|
|
|
|
// TODO: Bailing out early is just a perf optimization right?
|
|
// TODO: Removing the return statement should affect correctness?
|
|
return;
|
|
}
|
|
|
|
var refsChanged = ReactRef.shouldUpdateRefs(prevElement, nextElement);
|
|
|
|
if (refsChanged) {
|
|
ReactRef.detachRefs(internalInstance, prevElement);
|
|
}
|
|
|
|
internalInstance.receiveComponent(nextElement, transaction, context);
|
|
|
|
if (refsChanged && internalInstance._currentElement && internalInstance._currentElement.ref != null) {
|
|
transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Flush any dirty changes in a component.
|
|
*
|
|
* @param {ReactComponent} internalInstance
|
|
* @param {ReactReconcileTransaction} transaction
|
|
* @internal
|
|
*/
|
|
performUpdateIfNecessary: function (internalInstance, transaction) {
|
|
internalInstance.performUpdateIfNecessary(transaction);
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactReconciler;
|
|
},{"./ReactRef":212}],212:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactRef
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactOwner = require('./ReactOwner');
|
|
|
|
var ReactRef = {};
|
|
|
|
function attachRef(ref, component, owner) {
|
|
if (typeof ref === 'function') {
|
|
ref(component.getPublicInstance());
|
|
} else {
|
|
// Legacy ref
|
|
ReactOwner.addComponentAsRefTo(component, ref, owner);
|
|
}
|
|
}
|
|
|
|
function detachRef(ref, component, owner) {
|
|
if (typeof ref === 'function') {
|
|
ref(null);
|
|
} else {
|
|
// Legacy ref
|
|
ReactOwner.removeComponentAsRefFrom(component, ref, owner);
|
|
}
|
|
}
|
|
|
|
ReactRef.attachRefs = function (instance, element) {
|
|
if (element === null || element === false) {
|
|
return;
|
|
}
|
|
var ref = element.ref;
|
|
if (ref != null) {
|
|
attachRef(ref, instance, element._owner);
|
|
}
|
|
};
|
|
|
|
ReactRef.shouldUpdateRefs = function (prevElement, nextElement) {
|
|
// If either the owner or a `ref` has changed, make sure the newest owner
|
|
// has stored a reference to `this`, and the previous owner (if different)
|
|
// has forgotten the reference to `this`. We use the element instead
|
|
// of the public this.props because the post processing cannot determine
|
|
// a ref. The ref conceptually lives on the element.
|
|
|
|
// TODO: Should this even be possible? The owner cannot change because
|
|
// it's forbidden by shouldUpdateReactComponent. The ref can change
|
|
// if you swap the keys of but not the refs. Reconsider where this check
|
|
// is made. It probably belongs where the key checking and
|
|
// instantiateReactComponent is done.
|
|
|
|
var prevEmpty = prevElement === null || prevElement === false;
|
|
var nextEmpty = nextElement === null || nextElement === false;
|
|
|
|
return(
|
|
// This has a few false positives w/r/t empty components.
|
|
prevEmpty || nextEmpty || nextElement._owner !== prevElement._owner || nextElement.ref !== prevElement.ref
|
|
);
|
|
};
|
|
|
|
ReactRef.detachRefs = function (instance, element) {
|
|
if (element === null || element === false) {
|
|
return;
|
|
}
|
|
var ref = element.ref;
|
|
if (ref != null) {
|
|
detachRef(ref, instance, element._owner);
|
|
}
|
|
};
|
|
|
|
module.exports = ReactRef;
|
|
},{"./ReactOwner":205}],213:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactRootIndex
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactRootIndexInjection = {
|
|
/**
|
|
* @param {function} _createReactRootIndex
|
|
*/
|
|
injectCreateReactRootIndex: function (_createReactRootIndex) {
|
|
ReactRootIndex.createReactRootIndex = _createReactRootIndex;
|
|
}
|
|
};
|
|
|
|
var ReactRootIndex = {
|
|
createReactRootIndex: null,
|
|
injection: ReactRootIndexInjection
|
|
};
|
|
|
|
module.exports = ReactRootIndex;
|
|
},{}],214:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2014-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactServerBatchingStrategy
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactServerBatchingStrategy = {
|
|
isBatchingUpdates: false,
|
|
batchedUpdates: function (callback) {
|
|
// Don't do anything here. During the server rendering we don't want to
|
|
// schedule any updates. We will simply ignore them.
|
|
}
|
|
};
|
|
|
|
module.exports = ReactServerBatchingStrategy;
|
|
},{}],215:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @typechecks static-only
|
|
* @providesModule ReactServerRendering
|
|
*/
|
|
'use strict';
|
|
|
|
var ReactDefaultBatchingStrategy = require('./ReactDefaultBatchingStrategy');
|
|
var ReactElement = require('./ReactElement');
|
|
var ReactInstanceHandles = require('./ReactInstanceHandles');
|
|
var ReactMarkupChecksum = require('./ReactMarkupChecksum');
|
|
var ReactServerBatchingStrategy = require('./ReactServerBatchingStrategy');
|
|
var ReactServerRenderingTransaction = require('./ReactServerRenderingTransaction');
|
|
var ReactUpdates = require('./ReactUpdates');
|
|
|
|
var emptyObject = require('fbjs/lib/emptyObject');
|
|
var instantiateReactComponent = require('./instantiateReactComponent');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
/**
|
|
* @param {ReactElement} element
|
|
* @return {string} the HTML markup
|
|
*/
|
|
function renderToString(element) {
|
|
!ReactElement.isValidElement(element) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'renderToString(): You must pass a valid ReactElement.') : invariant(false) : undefined;
|
|
|
|
var transaction;
|
|
try {
|
|
ReactUpdates.injection.injectBatchingStrategy(ReactServerBatchingStrategy);
|
|
|
|
var id = ReactInstanceHandles.createReactRootID();
|
|
transaction = ReactServerRenderingTransaction.getPooled(false);
|
|
|
|
return transaction.perform(function () {
|
|
var componentInstance = instantiateReactComponent(element, null);
|
|
var markup = componentInstance.mountComponent(id, transaction, emptyObject);
|
|
return ReactMarkupChecksum.addChecksumToMarkup(markup);
|
|
}, null);
|
|
} finally {
|
|
ReactServerRenderingTransaction.release(transaction);
|
|
// Revert to the DOM batching strategy since these two renderers
|
|
// currently share these stateful modules.
|
|
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {ReactElement} element
|
|
* @return {string} the HTML markup, without the extra React ID and checksum
|
|
* (for generating static pages)
|
|
*/
|
|
function renderToStaticMarkup(element) {
|
|
!ReactElement.isValidElement(element) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'renderToStaticMarkup(): You must pass a valid ReactElement.') : invariant(false) : undefined;
|
|
|
|
var transaction;
|
|
try {
|
|
ReactUpdates.injection.injectBatchingStrategy(ReactServerBatchingStrategy);
|
|
|
|
var id = ReactInstanceHandles.createReactRootID();
|
|
transaction = ReactServerRenderingTransaction.getPooled(true);
|
|
|
|
return transaction.perform(function () {
|
|
var componentInstance = instantiateReactComponent(element, null);
|
|
return componentInstance.mountComponent(id, transaction, emptyObject);
|
|
}, null);
|
|
} finally {
|
|
ReactServerRenderingTransaction.release(transaction);
|
|
// Revert to the DOM batching strategy since these two renderers
|
|
// currently share these stateful modules.
|
|
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
renderToString: renderToString,
|
|
renderToStaticMarkup: renderToStaticMarkup
|
|
};
|
|
}).call(this,require('_process'))
|
|
},{"./ReactDefaultBatchingStrategy":183,"./ReactElement":187,"./ReactInstanceHandles":196,"./ReactMarkupChecksum":199,"./ReactServerBatchingStrategy":214,"./ReactServerRenderingTransaction":216,"./ReactUpdates":218,"./instantiateReactComponent":253,"_process":133,"fbjs/lib/emptyObject":65,"fbjs/lib/invariant":72}],216:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2014-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactServerRenderingTransaction
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var PooledClass = require('./PooledClass');
|
|
var CallbackQueue = require('./CallbackQueue');
|
|
var Transaction = require('./Transaction');
|
|
|
|
var assign = require('./Object.assign');
|
|
var emptyFunction = require('fbjs/lib/emptyFunction');
|
|
|
|
/**
|
|
* Provides a `CallbackQueue` queue for collecting `onDOMReady` callbacks
|
|
* during the performing of the transaction.
|
|
*/
|
|
var ON_DOM_READY_QUEUEING = {
|
|
/**
|
|
* Initializes the internal `onDOMReady` queue.
|
|
*/
|
|
initialize: function () {
|
|
this.reactMountReady.reset();
|
|
},
|
|
|
|
close: emptyFunction
|
|
};
|
|
|
|
/**
|
|
* Executed within the scope of the `Transaction` instance. Consider these as
|
|
* being member methods, but with an implied ordering while being isolated from
|
|
* each other.
|
|
*/
|
|
var TRANSACTION_WRAPPERS = [ON_DOM_READY_QUEUEING];
|
|
|
|
/**
|
|
* @class ReactServerRenderingTransaction
|
|
* @param {boolean} renderToStaticMarkup
|
|
*/
|
|
function ReactServerRenderingTransaction(renderToStaticMarkup) {
|
|
this.reinitializeTransaction();
|
|
this.renderToStaticMarkup = renderToStaticMarkup;
|
|
this.reactMountReady = CallbackQueue.getPooled(null);
|
|
this.useCreateElement = false;
|
|
}
|
|
|
|
var Mixin = {
|
|
/**
|
|
* @see Transaction
|
|
* @abstract
|
|
* @final
|
|
* @return {array} Empty list of operation wrap procedures.
|
|
*/
|
|
getTransactionWrappers: function () {
|
|
return TRANSACTION_WRAPPERS;
|
|
},
|
|
|
|
/**
|
|
* @return {object} The queue to collect `onDOMReady` callbacks with.
|
|
*/
|
|
getReactMountReady: function () {
|
|
return this.reactMountReady;
|
|
},
|
|
|
|
/**
|
|
* `PooledClass` looks for this, and will invoke this before allowing this
|
|
* instance to be reused.
|
|
*/
|
|
destructor: function () {
|
|
CallbackQueue.release(this.reactMountReady);
|
|
this.reactMountReady = null;
|
|
}
|
|
};
|
|
|
|
assign(ReactServerRenderingTransaction.prototype, Transaction.Mixin, Mixin);
|
|
|
|
PooledClass.addPoolingTo(ReactServerRenderingTransaction);
|
|
|
|
module.exports = ReactServerRenderingTransaction;
|
|
},{"./CallbackQueue":140,"./Object.assign":157,"./PooledClass":158,"./Transaction":235,"fbjs/lib/emptyFunction":64}],217:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactUpdateQueue
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactCurrentOwner = require('./ReactCurrentOwner');
|
|
var ReactElement = require('./ReactElement');
|
|
var ReactInstanceMap = require('./ReactInstanceMap');
|
|
var ReactUpdates = require('./ReactUpdates');
|
|
|
|
var assign = require('./Object.assign');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
function enqueueUpdate(internalInstance) {
|
|
ReactUpdates.enqueueUpdate(internalInstance);
|
|
}
|
|
|
|
function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
|
|
var internalInstance = ReactInstanceMap.get(publicInstance);
|
|
if (!internalInstance) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// Only warn when we have a callerName. Otherwise we should be silent.
|
|
// We're probably calling from enqueueCallback. We don't want to warn
|
|
// there because we already warned for the corresponding lifecycle method.
|
|
process.env.NODE_ENV !== 'production' ? warning(!callerName, '%s(...): Can only update a mounted or mounting component. ' + 'This usually means you called %s() on an unmounted component. ' + 'This is a no-op. Please check the code for the %s component.', callerName, callerName, publicInstance.constructor.displayName) : undefined;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(ReactCurrentOwner.current == null, '%s(...): Cannot update during an existing state transition ' + '(such as within `render`). Render methods should be a pure function ' + 'of props and state.', callerName) : undefined;
|
|
}
|
|
|
|
return internalInstance;
|
|
}
|
|
|
|
/**
|
|
* ReactUpdateQueue allows for state updates to be scheduled into a later
|
|
* reconciliation step.
|
|
*/
|
|
var ReactUpdateQueue = {
|
|
|
|
/**
|
|
* Checks whether or not this composite component is mounted.
|
|
* @param {ReactClass} publicInstance The instance we want to test.
|
|
* @return {boolean} True if mounted, false otherwise.
|
|
* @protected
|
|
* @final
|
|
*/
|
|
isMounted: function (publicInstance) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var owner = ReactCurrentOwner.current;
|
|
if (owner !== null) {
|
|
process.env.NODE_ENV !== 'production' ? warning(owner._warnedAboutRefsInRender, '%s is accessing isMounted inside its render() function. ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', owner.getName() || 'A component') : undefined;
|
|
owner._warnedAboutRefsInRender = true;
|
|
}
|
|
}
|
|
var internalInstance = ReactInstanceMap.get(publicInstance);
|
|
if (internalInstance) {
|
|
// During componentWillMount and render this will still be null but after
|
|
// that will always render to something. At least for now. So we can use
|
|
// this hack.
|
|
return !!internalInstance._renderedComponent;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Enqueue a callback that will be executed after all the pending updates
|
|
* have processed.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance to use as `this` context.
|
|
* @param {?function} callback Called after state is updated.
|
|
* @internal
|
|
*/
|
|
enqueueCallback: function (publicInstance, callback) {
|
|
!(typeof callback === 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'enqueueCallback(...): You called `setProps`, `replaceProps`, ' + '`setState`, `replaceState`, or `forceUpdate` with a callback that ' + 'isn\'t callable.') : invariant(false) : undefined;
|
|
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
|
|
|
|
// Previously we would throw an error if we didn't have an internal
|
|
// instance. Since we want to make it a no-op instead, we mirror the same
|
|
// behavior we have in other enqueue* methods.
|
|
// We also need to ignore callbacks in componentWillMount. See
|
|
// enqueueUpdates.
|
|
if (!internalInstance) {
|
|
return null;
|
|
}
|
|
|
|
if (internalInstance._pendingCallbacks) {
|
|
internalInstance._pendingCallbacks.push(callback);
|
|
} else {
|
|
internalInstance._pendingCallbacks = [callback];
|
|
}
|
|
// TODO: The callback here is ignored when setState is called from
|
|
// componentWillMount. Either fix it or disallow doing so completely in
|
|
// favor of getInitialState. Alternatively, we can disallow
|
|
// componentWillMount during server-side rendering.
|
|
enqueueUpdate(internalInstance);
|
|
},
|
|
|
|
enqueueCallbackInternal: function (internalInstance, callback) {
|
|
!(typeof callback === 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'enqueueCallback(...): You called `setProps`, `replaceProps`, ' + '`setState`, `replaceState`, or `forceUpdate` with a callback that ' + 'isn\'t callable.') : invariant(false) : undefined;
|
|
if (internalInstance._pendingCallbacks) {
|
|
internalInstance._pendingCallbacks.push(callback);
|
|
} else {
|
|
internalInstance._pendingCallbacks = [callback];
|
|
}
|
|
enqueueUpdate(internalInstance);
|
|
},
|
|
|
|
/**
|
|
* Forces an update. This should only be invoked when it is known with
|
|
* certainty that we are **not** in a DOM transaction.
|
|
*
|
|
* You may want to call this when you know that some deeper aspect of the
|
|
* component's state has changed but `setState` was not called.
|
|
*
|
|
* This will not invoke `shouldComponentUpdate`, but it will invoke
|
|
* `componentWillUpdate` and `componentDidUpdate`.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @internal
|
|
*/
|
|
enqueueForceUpdate: function (publicInstance) {
|
|
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'forceUpdate');
|
|
|
|
if (!internalInstance) {
|
|
return;
|
|
}
|
|
|
|
internalInstance._pendingForceUpdate = true;
|
|
|
|
enqueueUpdate(internalInstance);
|
|
},
|
|
|
|
/**
|
|
* Replaces all of the state. Always use this or `setState` to mutate state.
|
|
* You should treat `this.state` as immutable.
|
|
*
|
|
* There is no guarantee that `this.state` will be immediately updated, so
|
|
* accessing `this.state` after calling this method may return the old value.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @param {object} completeState Next state.
|
|
* @internal
|
|
*/
|
|
enqueueReplaceState: function (publicInstance, completeState) {
|
|
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'replaceState');
|
|
|
|
if (!internalInstance) {
|
|
return;
|
|
}
|
|
|
|
internalInstance._pendingStateQueue = [completeState];
|
|
internalInstance._pendingReplaceState = true;
|
|
|
|
enqueueUpdate(internalInstance);
|
|
},
|
|
|
|
/**
|
|
* Sets a subset of the state. This only exists because _pendingState is
|
|
* internal. This provides a merging strategy that is not available to deep
|
|
* properties which is confusing. TODO: Expose pendingState or don't use it
|
|
* during the merge.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @param {object} partialState Next partial state to be merged with state.
|
|
* @internal
|
|
*/
|
|
enqueueSetState: function (publicInstance, partialState) {
|
|
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
|
|
|
|
if (!internalInstance) {
|
|
return;
|
|
}
|
|
|
|
var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
|
|
queue.push(partialState);
|
|
|
|
enqueueUpdate(internalInstance);
|
|
},
|
|
|
|
/**
|
|
* Sets a subset of the props.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @param {object} partialProps Subset of the next props.
|
|
* @internal
|
|
*/
|
|
enqueueSetProps: function (publicInstance, partialProps) {
|
|
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setProps');
|
|
if (!internalInstance) {
|
|
return;
|
|
}
|
|
ReactUpdateQueue.enqueueSetPropsInternal(internalInstance, partialProps);
|
|
},
|
|
|
|
enqueueSetPropsInternal: function (internalInstance, partialProps) {
|
|
var topLevelWrapper = internalInstance._topLevelWrapper;
|
|
!topLevelWrapper ? process.env.NODE_ENV !== 'production' ? invariant(false, 'setProps(...): You called `setProps` on a ' + 'component with a parent. This is an anti-pattern since props will ' + 'get reactively updated when rendered. Instead, change the owner\'s ' + '`render` method to pass the correct value as props to the component ' + 'where it is created.') : invariant(false) : undefined;
|
|
|
|
// Merge with the pending element if it exists, otherwise with existing
|
|
// element props.
|
|
var wrapElement = topLevelWrapper._pendingElement || topLevelWrapper._currentElement;
|
|
var element = wrapElement.props;
|
|
var props = assign({}, element.props, partialProps);
|
|
topLevelWrapper._pendingElement = ReactElement.cloneAndReplaceProps(wrapElement, ReactElement.cloneAndReplaceProps(element, props));
|
|
|
|
enqueueUpdate(topLevelWrapper);
|
|
},
|
|
|
|
/**
|
|
* Replaces all of the props.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @param {object} props New props.
|
|
* @internal
|
|
*/
|
|
enqueueReplaceProps: function (publicInstance, props) {
|
|
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'replaceProps');
|
|
if (!internalInstance) {
|
|
return;
|
|
}
|
|
ReactUpdateQueue.enqueueReplacePropsInternal(internalInstance, props);
|
|
},
|
|
|
|
enqueueReplacePropsInternal: function (internalInstance, props) {
|
|
var topLevelWrapper = internalInstance._topLevelWrapper;
|
|
!topLevelWrapper ? process.env.NODE_ENV !== 'production' ? invariant(false, 'replaceProps(...): You called `replaceProps` on a ' + 'component with a parent. This is an anti-pattern since props will ' + 'get reactively updated when rendered. Instead, change the owner\'s ' + '`render` method to pass the correct value as props to the component ' + 'where it is created.') : invariant(false) : undefined;
|
|
|
|
// Merge with the pending element if it exists, otherwise with existing
|
|
// element props.
|
|
var wrapElement = topLevelWrapper._pendingElement || topLevelWrapper._currentElement;
|
|
var element = wrapElement.props;
|
|
topLevelWrapper._pendingElement = ReactElement.cloneAndReplaceProps(wrapElement, ReactElement.cloneAndReplaceProps(element, props));
|
|
|
|
enqueueUpdate(topLevelWrapper);
|
|
},
|
|
|
|
enqueueElementInternal: function (internalInstance, newElement) {
|
|
internalInstance._pendingElement = newElement;
|
|
enqueueUpdate(internalInstance);
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ReactUpdateQueue;
|
|
}).call(this,require('_process'))
|
|
},{"./Object.assign":157,"./ReactCurrentOwner":169,"./ReactElement":187,"./ReactInstanceMap":197,"./ReactUpdates":218,"_process":133,"fbjs/lib/invariant":72,"fbjs/lib/warning":83}],218:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactUpdates
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var CallbackQueue = require('./CallbackQueue');
|
|
var PooledClass = require('./PooledClass');
|
|
var ReactPerf = require('./ReactPerf');
|
|
var ReactReconciler = require('./ReactReconciler');
|
|
var Transaction = require('./Transaction');
|
|
|
|
var assign = require('./Object.assign');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
var dirtyComponents = [];
|
|
var asapCallbackQueue = CallbackQueue.getPooled();
|
|
var asapEnqueued = false;
|
|
|
|
var batchingStrategy = null;
|
|
|
|
function ensureInjected() {
|
|
!(ReactUpdates.ReactReconcileTransaction && batchingStrategy) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactUpdates: must inject a reconcile transaction class and batching ' + 'strategy') : invariant(false) : undefined;
|
|
}
|
|
|
|
var NESTED_UPDATES = {
|
|
initialize: function () {
|
|
this.dirtyComponentsLength = dirtyComponents.length;
|
|
},
|
|
close: function () {
|
|
if (this.dirtyComponentsLength !== dirtyComponents.length) {
|
|
// Additional updates were enqueued by componentDidUpdate handlers or
|
|
// similar; before our own UPDATE_QUEUEING wrapper closes, we want to run
|
|
// these new updates so that if A's componentDidUpdate calls setState on
|
|
// B, B will update before the callback A's updater provided when calling
|
|
// setState.
|
|
dirtyComponents.splice(0, this.dirtyComponentsLength);
|
|
flushBatchedUpdates();
|
|
} else {
|
|
dirtyComponents.length = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
var UPDATE_QUEUEING = {
|
|
initialize: function () {
|
|
this.callbackQueue.reset();
|
|
},
|
|
close: function () {
|
|
this.callbackQueue.notifyAll();
|
|
}
|
|
};
|
|
|
|
var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];
|
|
|
|
function ReactUpdatesFlushTransaction() {
|
|
this.reinitializeTransaction();
|
|
this.dirtyComponentsLength = null;
|
|
this.callbackQueue = CallbackQueue.getPooled();
|
|
this.reconcileTransaction = ReactUpdates.ReactReconcileTransaction.getPooled( /* forceHTML */false);
|
|
}
|
|
|
|
assign(ReactUpdatesFlushTransaction.prototype, Transaction.Mixin, {
|
|
getTransactionWrappers: function () {
|
|
return TRANSACTION_WRAPPERS;
|
|
},
|
|
|
|
destructor: function () {
|
|
this.dirtyComponentsLength = null;
|
|
CallbackQueue.release(this.callbackQueue);
|
|
this.callbackQueue = null;
|
|
ReactUpdates.ReactReconcileTransaction.release(this.reconcileTransaction);
|
|
this.reconcileTransaction = null;
|
|
},
|
|
|
|
perform: function (method, scope, a) {
|
|
// Essentially calls `this.reconcileTransaction.perform(method, scope, a)`
|
|
// with this transaction's wrappers around it.
|
|
return Transaction.Mixin.perform.call(this, this.reconcileTransaction.perform, this.reconcileTransaction, method, scope, a);
|
|
}
|
|
});
|
|
|
|
PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);
|
|
|
|
function batchedUpdates(callback, a, b, c, d, e) {
|
|
ensureInjected();
|
|
batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
|
|
}
|
|
|
|
/**
|
|
* Array comparator for ReactComponents by mount ordering.
|
|
*
|
|
* @param {ReactComponent} c1 first component you're comparing
|
|
* @param {ReactComponent} c2 second component you're comparing
|
|
* @return {number} Return value usable by Array.prototype.sort().
|
|
*/
|
|
function mountOrderComparator(c1, c2) {
|
|
return c1._mountOrder - c2._mountOrder;
|
|
}
|
|
|
|
function runBatchedUpdates(transaction) {
|
|
var len = transaction.dirtyComponentsLength;
|
|
!(len === dirtyComponents.length) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Expected flush transaction\'s stored dirty-components length (%s) to ' + 'match dirty-components array length (%s).', len, dirtyComponents.length) : invariant(false) : undefined;
|
|
|
|
// Since reconciling a component higher in the owner hierarchy usually (not
|
|
// always -- see shouldComponentUpdate()) will reconcile children, reconcile
|
|
// them before their children by sorting the array.
|
|
dirtyComponents.sort(mountOrderComparator);
|
|
|
|
for (var i = 0; i < len; i++) {
|
|
// If a component is unmounted before pending changes apply, it will still
|
|
// be here, but we assume that it has cleared its _pendingCallbacks and
|
|
// that performUpdateIfNecessary is a noop.
|
|
var component = dirtyComponents[i];
|
|
|
|
// If performUpdateIfNecessary happens to enqueue any new updates, we
|
|
// shouldn't execute the callbacks until the next render happens, so
|
|
// stash the callbacks first
|
|
var callbacks = component._pendingCallbacks;
|
|
component._pendingCallbacks = null;
|
|
|
|
ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction);
|
|
|
|
if (callbacks) {
|
|
for (var j = 0; j < callbacks.length; j++) {
|
|
transaction.callbackQueue.enqueue(callbacks[j], component.getPublicInstance());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var flushBatchedUpdates = function () {
|
|
// ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents
|
|
// array and perform any updates enqueued by mount-ready handlers (i.e.,
|
|
// componentDidUpdate) but we need to check here too in order to catch
|
|
// updates enqueued by setState callbacks and asap calls.
|
|
while (dirtyComponents.length || asapEnqueued) {
|
|
if (dirtyComponents.length) {
|
|
var transaction = ReactUpdatesFlushTransaction.getPooled();
|
|
transaction.perform(runBatchedUpdates, null, transaction);
|
|
ReactUpdatesFlushTransaction.release(transaction);
|
|
}
|
|
|
|
if (asapEnqueued) {
|
|
asapEnqueued = false;
|
|
var queue = asapCallbackQueue;
|
|
asapCallbackQueue = CallbackQueue.getPooled();
|
|
queue.notifyAll();
|
|
CallbackQueue.release(queue);
|
|
}
|
|
}
|
|
};
|
|
flushBatchedUpdates = ReactPerf.measure('ReactUpdates', 'flushBatchedUpdates', flushBatchedUpdates);
|
|
|
|
/**
|
|
* Mark a component as needing a rerender, adding an optional callback to a
|
|
* list of functions which will be executed once the rerender occurs.
|
|
*/
|
|
function enqueueUpdate(component) {
|
|
ensureInjected();
|
|
|
|
// Various parts of our code (such as ReactCompositeComponent's
|
|
// _renderValidatedComponent) assume that calls to render aren't nested;
|
|
// verify that that's the case. (This is called by each top-level update
|
|
// function, like setProps, setState, forceUpdate, etc.; creation and
|
|
// destruction of top-level components is guarded in ReactMount.)
|
|
|
|
if (!batchingStrategy.isBatchingUpdates) {
|
|
batchingStrategy.batchedUpdates(enqueueUpdate, component);
|
|
return;
|
|
}
|
|
|
|
dirtyComponents.push(component);
|
|
}
|
|
|
|
/**
|
|
* Enqueue a callback to be run at the end of the current batching cycle. Throws
|
|
* if no updates are currently being performed.
|
|
*/
|
|
function asap(callback, context) {
|
|
!batchingStrategy.isBatchingUpdates ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactUpdates.asap: Can\'t enqueue an asap callback in a context where' + 'updates are not being batched.') : invariant(false) : undefined;
|
|
asapCallbackQueue.enqueue(callback, context);
|
|
asapEnqueued = true;
|
|
}
|
|
|
|
var ReactUpdatesInjection = {
|
|
injectReconcileTransaction: function (ReconcileTransaction) {
|
|
!ReconcileTransaction ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactUpdates: must provide a reconcile transaction class') : invariant(false) : undefined;
|
|
ReactUpdates.ReactReconcileTransaction = ReconcileTransaction;
|
|
},
|
|
|
|
injectBatchingStrategy: function (_batchingStrategy) {
|
|
!_batchingStrategy ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactUpdates: must provide a batching strategy') : invariant(false) : undefined;
|
|
!(typeof _batchingStrategy.batchedUpdates === 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactUpdates: must provide a batchedUpdates() function') : invariant(false) : undefined;
|
|
!(typeof _batchingStrategy.isBatchingUpdates === 'boolean') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactUpdates: must provide an isBatchingUpdates boolean attribute') : invariant(false) : undefined;
|
|
batchingStrategy = _batchingStrategy;
|
|
}
|
|
};
|
|
|
|
var ReactUpdates = {
|
|
/**
|
|
* React references `ReactReconcileTransaction` using this property in order
|
|
* to allow dependency injection.
|
|
*
|
|
* @internal
|
|
*/
|
|
ReactReconcileTransaction: null,
|
|
|
|
batchedUpdates: batchedUpdates,
|
|
enqueueUpdate: enqueueUpdate,
|
|
flushBatchedUpdates: flushBatchedUpdates,
|
|
injection: ReactUpdatesInjection,
|
|
asap: asap
|
|
};
|
|
|
|
module.exports = ReactUpdates;
|
|
}).call(this,require('_process'))
|
|
},{"./CallbackQueue":140,"./Object.assign":157,"./PooledClass":158,"./ReactPerf":206,"./ReactReconciler":211,"./Transaction":235,"_process":133,"fbjs/lib/invariant":72}],219:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ReactVersion
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
module.exports = '0.14.8';
|
|
},{}],220:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SVGDOMPropertyConfig
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var DOMProperty = require('./DOMProperty');
|
|
|
|
var MUST_USE_ATTRIBUTE = DOMProperty.injection.MUST_USE_ATTRIBUTE;
|
|
|
|
var NS = {
|
|
xlink: 'http://www.w3.org/1999/xlink',
|
|
xml: 'http://www.w3.org/XML/1998/namespace'
|
|
};
|
|
|
|
var SVGDOMPropertyConfig = {
|
|
Properties: {
|
|
clipPath: MUST_USE_ATTRIBUTE,
|
|
cx: MUST_USE_ATTRIBUTE,
|
|
cy: MUST_USE_ATTRIBUTE,
|
|
d: MUST_USE_ATTRIBUTE,
|
|
dx: MUST_USE_ATTRIBUTE,
|
|
dy: MUST_USE_ATTRIBUTE,
|
|
fill: MUST_USE_ATTRIBUTE,
|
|
fillOpacity: MUST_USE_ATTRIBUTE,
|
|
fontFamily: MUST_USE_ATTRIBUTE,
|
|
fontSize: MUST_USE_ATTRIBUTE,
|
|
fx: MUST_USE_ATTRIBUTE,
|
|
fy: MUST_USE_ATTRIBUTE,
|
|
gradientTransform: MUST_USE_ATTRIBUTE,
|
|
gradientUnits: MUST_USE_ATTRIBUTE,
|
|
markerEnd: MUST_USE_ATTRIBUTE,
|
|
markerMid: MUST_USE_ATTRIBUTE,
|
|
markerStart: MUST_USE_ATTRIBUTE,
|
|
offset: MUST_USE_ATTRIBUTE,
|
|
opacity: MUST_USE_ATTRIBUTE,
|
|
patternContentUnits: MUST_USE_ATTRIBUTE,
|
|
patternUnits: MUST_USE_ATTRIBUTE,
|
|
points: MUST_USE_ATTRIBUTE,
|
|
preserveAspectRatio: MUST_USE_ATTRIBUTE,
|
|
r: MUST_USE_ATTRIBUTE,
|
|
rx: MUST_USE_ATTRIBUTE,
|
|
ry: MUST_USE_ATTRIBUTE,
|
|
spreadMethod: MUST_USE_ATTRIBUTE,
|
|
stopColor: MUST_USE_ATTRIBUTE,
|
|
stopOpacity: MUST_USE_ATTRIBUTE,
|
|
stroke: MUST_USE_ATTRIBUTE,
|
|
strokeDasharray: MUST_USE_ATTRIBUTE,
|
|
strokeLinecap: MUST_USE_ATTRIBUTE,
|
|
strokeOpacity: MUST_USE_ATTRIBUTE,
|
|
strokeWidth: MUST_USE_ATTRIBUTE,
|
|
textAnchor: MUST_USE_ATTRIBUTE,
|
|
transform: MUST_USE_ATTRIBUTE,
|
|
version: MUST_USE_ATTRIBUTE,
|
|
viewBox: MUST_USE_ATTRIBUTE,
|
|
x1: MUST_USE_ATTRIBUTE,
|
|
x2: MUST_USE_ATTRIBUTE,
|
|
x: MUST_USE_ATTRIBUTE,
|
|
xlinkActuate: MUST_USE_ATTRIBUTE,
|
|
xlinkArcrole: MUST_USE_ATTRIBUTE,
|
|
xlinkHref: MUST_USE_ATTRIBUTE,
|
|
xlinkRole: MUST_USE_ATTRIBUTE,
|
|
xlinkShow: MUST_USE_ATTRIBUTE,
|
|
xlinkTitle: MUST_USE_ATTRIBUTE,
|
|
xlinkType: MUST_USE_ATTRIBUTE,
|
|
xmlBase: MUST_USE_ATTRIBUTE,
|
|
xmlLang: MUST_USE_ATTRIBUTE,
|
|
xmlSpace: MUST_USE_ATTRIBUTE,
|
|
y1: MUST_USE_ATTRIBUTE,
|
|
y2: MUST_USE_ATTRIBUTE,
|
|
y: MUST_USE_ATTRIBUTE
|
|
},
|
|
DOMAttributeNamespaces: {
|
|
xlinkActuate: NS.xlink,
|
|
xlinkArcrole: NS.xlink,
|
|
xlinkHref: NS.xlink,
|
|
xlinkRole: NS.xlink,
|
|
xlinkShow: NS.xlink,
|
|
xlinkTitle: NS.xlink,
|
|
xlinkType: NS.xlink,
|
|
xmlBase: NS.xml,
|
|
xmlLang: NS.xml,
|
|
xmlSpace: NS.xml
|
|
},
|
|
DOMAttributeNames: {
|
|
clipPath: 'clip-path',
|
|
fillOpacity: 'fill-opacity',
|
|
fontFamily: 'font-family',
|
|
fontSize: 'font-size',
|
|
gradientTransform: 'gradientTransform',
|
|
gradientUnits: 'gradientUnits',
|
|
markerEnd: 'marker-end',
|
|
markerMid: 'marker-mid',
|
|
markerStart: 'marker-start',
|
|
patternContentUnits: 'patternContentUnits',
|
|
patternUnits: 'patternUnits',
|
|
preserveAspectRatio: 'preserveAspectRatio',
|
|
spreadMethod: 'spreadMethod',
|
|
stopColor: 'stop-color',
|
|
stopOpacity: 'stop-opacity',
|
|
strokeDasharray: 'stroke-dasharray',
|
|
strokeLinecap: 'stroke-linecap',
|
|
strokeOpacity: 'stroke-opacity',
|
|
strokeWidth: 'stroke-width',
|
|
textAnchor: 'text-anchor',
|
|
viewBox: 'viewBox',
|
|
xlinkActuate: 'xlink:actuate',
|
|
xlinkArcrole: 'xlink:arcrole',
|
|
xlinkHref: 'xlink:href',
|
|
xlinkRole: 'xlink:role',
|
|
xlinkShow: 'xlink:show',
|
|
xlinkTitle: 'xlink:title',
|
|
xlinkType: 'xlink:type',
|
|
xmlBase: 'xml:base',
|
|
xmlLang: 'xml:lang',
|
|
xmlSpace: 'xml:space'
|
|
}
|
|
};
|
|
|
|
module.exports = SVGDOMPropertyConfig;
|
|
},{"./DOMProperty":144}],221:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SelectEventPlugin
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var EventConstants = require('./EventConstants');
|
|
var EventPropagators = require('./EventPropagators');
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
var ReactInputSelection = require('./ReactInputSelection');
|
|
var SyntheticEvent = require('./SyntheticEvent');
|
|
|
|
var getActiveElement = require('fbjs/lib/getActiveElement');
|
|
var isTextInputElement = require('./isTextInputElement');
|
|
var keyOf = require('fbjs/lib/keyOf');
|
|
var shallowEqual = require('fbjs/lib/shallowEqual');
|
|
|
|
var topLevelTypes = EventConstants.topLevelTypes;
|
|
|
|
var skipSelectionChangeEvent = ExecutionEnvironment.canUseDOM && 'documentMode' in document && document.documentMode <= 11;
|
|
|
|
var eventTypes = {
|
|
select: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onSelect: null }),
|
|
captured: keyOf({ onSelectCapture: null })
|
|
},
|
|
dependencies: [topLevelTypes.topBlur, topLevelTypes.topContextMenu, topLevelTypes.topFocus, topLevelTypes.topKeyDown, topLevelTypes.topMouseDown, topLevelTypes.topMouseUp, topLevelTypes.topSelectionChange]
|
|
}
|
|
};
|
|
|
|
var activeElement = null;
|
|
var activeElementID = null;
|
|
var lastSelection = null;
|
|
var mouseDown = false;
|
|
|
|
// Track whether a listener exists for this plugin. If none exist, we do
|
|
// not extract events.
|
|
var hasListener = false;
|
|
var ON_SELECT_KEY = keyOf({ onSelect: null });
|
|
|
|
/**
|
|
* Get an object which is a unique representation of the current selection.
|
|
*
|
|
* The return value will not be consistent across nodes or browsers, but
|
|
* two identical selections on the same node will return identical objects.
|
|
*
|
|
* @param {DOMElement} node
|
|
* @return {object}
|
|
*/
|
|
function getSelection(node) {
|
|
if ('selectionStart' in node && ReactInputSelection.hasSelectionCapabilities(node)) {
|
|
return {
|
|
start: node.selectionStart,
|
|
end: node.selectionEnd
|
|
};
|
|
} else if (window.getSelection) {
|
|
var selection = window.getSelection();
|
|
return {
|
|
anchorNode: selection.anchorNode,
|
|
anchorOffset: selection.anchorOffset,
|
|
focusNode: selection.focusNode,
|
|
focusOffset: selection.focusOffset
|
|
};
|
|
} else if (document.selection) {
|
|
var range = document.selection.createRange();
|
|
return {
|
|
parentElement: range.parentElement(),
|
|
text: range.text,
|
|
top: range.boundingTop,
|
|
left: range.boundingLeft
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Poll selection to see whether it's changed.
|
|
*
|
|
* @param {object} nativeEvent
|
|
* @return {?SyntheticEvent}
|
|
*/
|
|
function constructSelectEvent(nativeEvent, nativeEventTarget) {
|
|
// Ensure we have the right element, and that the user is not dragging a
|
|
// selection (this matches native `select` event behavior). In HTML5, select
|
|
// fires only on input and textarea thus if there's no focused element we
|
|
// won't dispatch.
|
|
if (mouseDown || activeElement == null || activeElement !== getActiveElement()) {
|
|
return null;
|
|
}
|
|
|
|
// Only fire when selection has actually changed.
|
|
var currentSelection = getSelection(activeElement);
|
|
if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
|
|
lastSelection = currentSelection;
|
|
|
|
var syntheticEvent = SyntheticEvent.getPooled(eventTypes.select, activeElementID, nativeEvent, nativeEventTarget);
|
|
|
|
syntheticEvent.type = 'select';
|
|
syntheticEvent.target = activeElement;
|
|
|
|
EventPropagators.accumulateTwoPhaseDispatches(syntheticEvent);
|
|
|
|
return syntheticEvent;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* This plugin creates an `onSelect` event that normalizes select events
|
|
* across form elements.
|
|
*
|
|
* Supported elements are:
|
|
* - input (see `isTextInputElement`)
|
|
* - textarea
|
|
* - contentEditable
|
|
*
|
|
* This differs from native browser implementations in the following ways:
|
|
* - Fires on contentEditable fields as well as inputs.
|
|
* - Fires for collapsed selection.
|
|
* - Fires after user input.
|
|
*/
|
|
var SelectEventPlugin = {
|
|
|
|
eventTypes: eventTypes,
|
|
|
|
/**
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {DOMEventTarget} topLevelTarget The listening component root node.
|
|
* @param {string} topLevelTargetID ID of `topLevelTarget`.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {*} An accumulation of synthetic events.
|
|
* @see {EventPluginHub.extractEvents}
|
|
*/
|
|
extractEvents: function (topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget) {
|
|
if (!hasListener) {
|
|
return null;
|
|
}
|
|
|
|
switch (topLevelType) {
|
|
// Track the input node that has focus.
|
|
case topLevelTypes.topFocus:
|
|
if (isTextInputElement(topLevelTarget) || topLevelTarget.contentEditable === 'true') {
|
|
activeElement = topLevelTarget;
|
|
activeElementID = topLevelTargetID;
|
|
lastSelection = null;
|
|
}
|
|
break;
|
|
case topLevelTypes.topBlur:
|
|
activeElement = null;
|
|
activeElementID = null;
|
|
lastSelection = null;
|
|
break;
|
|
|
|
// Don't fire the event while the user is dragging. This matches the
|
|
// semantics of the native select event.
|
|
case topLevelTypes.topMouseDown:
|
|
mouseDown = true;
|
|
break;
|
|
case topLevelTypes.topContextMenu:
|
|
case topLevelTypes.topMouseUp:
|
|
mouseDown = false;
|
|
return constructSelectEvent(nativeEvent, nativeEventTarget);
|
|
|
|
// Chrome and IE fire non-standard event when selection is changed (and
|
|
// sometimes when it hasn't). IE's event fires out of order with respect
|
|
// to key and input events on deletion, so we discard it.
|
|
//
|
|
// Firefox doesn't support selectionchange, so check selection status
|
|
// after each key entry. The selection changes after keydown and before
|
|
// keyup, but we check on keydown as well in the case of holding down a
|
|
// key, when multiple keydown events are fired but only one keyup is.
|
|
// This is also our approach for IE handling, for the reason above.
|
|
case topLevelTypes.topSelectionChange:
|
|
if (skipSelectionChangeEvent) {
|
|
break;
|
|
}
|
|
// falls through
|
|
case topLevelTypes.topKeyDown:
|
|
case topLevelTypes.topKeyUp:
|
|
return constructSelectEvent(nativeEvent, nativeEventTarget);
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
didPutListener: function (id, registrationName, listener) {
|
|
if (registrationName === ON_SELECT_KEY) {
|
|
hasListener = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
module.exports = SelectEventPlugin;
|
|
},{"./EventConstants":149,"./EventPropagators":153,"./ReactInputSelection":195,"./SyntheticEvent":227,"./isTextInputElement":255,"fbjs/lib/ExecutionEnvironment":58,"fbjs/lib/getActiveElement":67,"fbjs/lib/keyOf":76,"fbjs/lib/shallowEqual":81}],222:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ServerReactRootIndex
|
|
* @typechecks
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Size of the reactRoot ID space. We generate random numbers for React root
|
|
* IDs and if there's a collision the events and DOM update system will
|
|
* get confused. In the future we need a way to generate GUIDs but for
|
|
* now this will work on a smaller scale.
|
|
*/
|
|
var GLOBAL_MOUNT_POINT_MAX = Math.pow(2, 53);
|
|
|
|
var ServerReactRootIndex = {
|
|
createReactRootIndex: function () {
|
|
return Math.ceil(Math.random() * GLOBAL_MOUNT_POINT_MAX);
|
|
}
|
|
};
|
|
|
|
module.exports = ServerReactRootIndex;
|
|
},{}],223:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SimpleEventPlugin
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var EventConstants = require('./EventConstants');
|
|
var EventListener = require('fbjs/lib/EventListener');
|
|
var EventPropagators = require('./EventPropagators');
|
|
var ReactMount = require('./ReactMount');
|
|
var SyntheticClipboardEvent = require('./SyntheticClipboardEvent');
|
|
var SyntheticEvent = require('./SyntheticEvent');
|
|
var SyntheticFocusEvent = require('./SyntheticFocusEvent');
|
|
var SyntheticKeyboardEvent = require('./SyntheticKeyboardEvent');
|
|
var SyntheticMouseEvent = require('./SyntheticMouseEvent');
|
|
var SyntheticDragEvent = require('./SyntheticDragEvent');
|
|
var SyntheticTouchEvent = require('./SyntheticTouchEvent');
|
|
var SyntheticUIEvent = require('./SyntheticUIEvent');
|
|
var SyntheticWheelEvent = require('./SyntheticWheelEvent');
|
|
|
|
var emptyFunction = require('fbjs/lib/emptyFunction');
|
|
var getEventCharCode = require('./getEventCharCode');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var keyOf = require('fbjs/lib/keyOf');
|
|
|
|
var topLevelTypes = EventConstants.topLevelTypes;
|
|
|
|
var eventTypes = {
|
|
abort: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onAbort: true }),
|
|
captured: keyOf({ onAbortCapture: true })
|
|
}
|
|
},
|
|
blur: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onBlur: true }),
|
|
captured: keyOf({ onBlurCapture: true })
|
|
}
|
|
},
|
|
canPlay: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onCanPlay: true }),
|
|
captured: keyOf({ onCanPlayCapture: true })
|
|
}
|
|
},
|
|
canPlayThrough: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onCanPlayThrough: true }),
|
|
captured: keyOf({ onCanPlayThroughCapture: true })
|
|
}
|
|
},
|
|
click: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onClick: true }),
|
|
captured: keyOf({ onClickCapture: true })
|
|
}
|
|
},
|
|
contextMenu: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onContextMenu: true }),
|
|
captured: keyOf({ onContextMenuCapture: true })
|
|
}
|
|
},
|
|
copy: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onCopy: true }),
|
|
captured: keyOf({ onCopyCapture: true })
|
|
}
|
|
},
|
|
cut: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onCut: true }),
|
|
captured: keyOf({ onCutCapture: true })
|
|
}
|
|
},
|
|
doubleClick: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onDoubleClick: true }),
|
|
captured: keyOf({ onDoubleClickCapture: true })
|
|
}
|
|
},
|
|
drag: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onDrag: true }),
|
|
captured: keyOf({ onDragCapture: true })
|
|
}
|
|
},
|
|
dragEnd: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onDragEnd: true }),
|
|
captured: keyOf({ onDragEndCapture: true })
|
|
}
|
|
},
|
|
dragEnter: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onDragEnter: true }),
|
|
captured: keyOf({ onDragEnterCapture: true })
|
|
}
|
|
},
|
|
dragExit: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onDragExit: true }),
|
|
captured: keyOf({ onDragExitCapture: true })
|
|
}
|
|
},
|
|
dragLeave: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onDragLeave: true }),
|
|
captured: keyOf({ onDragLeaveCapture: true })
|
|
}
|
|
},
|
|
dragOver: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onDragOver: true }),
|
|
captured: keyOf({ onDragOverCapture: true })
|
|
}
|
|
},
|
|
dragStart: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onDragStart: true }),
|
|
captured: keyOf({ onDragStartCapture: true })
|
|
}
|
|
},
|
|
drop: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onDrop: true }),
|
|
captured: keyOf({ onDropCapture: true })
|
|
}
|
|
},
|
|
durationChange: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onDurationChange: true }),
|
|
captured: keyOf({ onDurationChangeCapture: true })
|
|
}
|
|
},
|
|
emptied: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onEmptied: true }),
|
|
captured: keyOf({ onEmptiedCapture: true })
|
|
}
|
|
},
|
|
encrypted: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onEncrypted: true }),
|
|
captured: keyOf({ onEncryptedCapture: true })
|
|
}
|
|
},
|
|
ended: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onEnded: true }),
|
|
captured: keyOf({ onEndedCapture: true })
|
|
}
|
|
},
|
|
error: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onError: true }),
|
|
captured: keyOf({ onErrorCapture: true })
|
|
}
|
|
},
|
|
focus: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onFocus: true }),
|
|
captured: keyOf({ onFocusCapture: true })
|
|
}
|
|
},
|
|
input: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onInput: true }),
|
|
captured: keyOf({ onInputCapture: true })
|
|
}
|
|
},
|
|
keyDown: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onKeyDown: true }),
|
|
captured: keyOf({ onKeyDownCapture: true })
|
|
}
|
|
},
|
|
keyPress: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onKeyPress: true }),
|
|
captured: keyOf({ onKeyPressCapture: true })
|
|
}
|
|
},
|
|
keyUp: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onKeyUp: true }),
|
|
captured: keyOf({ onKeyUpCapture: true })
|
|
}
|
|
},
|
|
load: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onLoad: true }),
|
|
captured: keyOf({ onLoadCapture: true })
|
|
}
|
|
},
|
|
loadedData: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onLoadedData: true }),
|
|
captured: keyOf({ onLoadedDataCapture: true })
|
|
}
|
|
},
|
|
loadedMetadata: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onLoadedMetadata: true }),
|
|
captured: keyOf({ onLoadedMetadataCapture: true })
|
|
}
|
|
},
|
|
loadStart: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onLoadStart: true }),
|
|
captured: keyOf({ onLoadStartCapture: true })
|
|
}
|
|
},
|
|
// Note: We do not allow listening to mouseOver events. Instead, use the
|
|
// onMouseEnter/onMouseLeave created by `EnterLeaveEventPlugin`.
|
|
mouseDown: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onMouseDown: true }),
|
|
captured: keyOf({ onMouseDownCapture: true })
|
|
}
|
|
},
|
|
mouseMove: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onMouseMove: true }),
|
|
captured: keyOf({ onMouseMoveCapture: true })
|
|
}
|
|
},
|
|
mouseOut: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onMouseOut: true }),
|
|
captured: keyOf({ onMouseOutCapture: true })
|
|
}
|
|
},
|
|
mouseOver: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onMouseOver: true }),
|
|
captured: keyOf({ onMouseOverCapture: true })
|
|
}
|
|
},
|
|
mouseUp: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onMouseUp: true }),
|
|
captured: keyOf({ onMouseUpCapture: true })
|
|
}
|
|
},
|
|
paste: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onPaste: true }),
|
|
captured: keyOf({ onPasteCapture: true })
|
|
}
|
|
},
|
|
pause: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onPause: true }),
|
|
captured: keyOf({ onPauseCapture: true })
|
|
}
|
|
},
|
|
play: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onPlay: true }),
|
|
captured: keyOf({ onPlayCapture: true })
|
|
}
|
|
},
|
|
playing: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onPlaying: true }),
|
|
captured: keyOf({ onPlayingCapture: true })
|
|
}
|
|
},
|
|
progress: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onProgress: true }),
|
|
captured: keyOf({ onProgressCapture: true })
|
|
}
|
|
},
|
|
rateChange: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onRateChange: true }),
|
|
captured: keyOf({ onRateChangeCapture: true })
|
|
}
|
|
},
|
|
reset: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onReset: true }),
|
|
captured: keyOf({ onResetCapture: true })
|
|
}
|
|
},
|
|
scroll: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onScroll: true }),
|
|
captured: keyOf({ onScrollCapture: true })
|
|
}
|
|
},
|
|
seeked: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onSeeked: true }),
|
|
captured: keyOf({ onSeekedCapture: true })
|
|
}
|
|
},
|
|
seeking: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onSeeking: true }),
|
|
captured: keyOf({ onSeekingCapture: true })
|
|
}
|
|
},
|
|
stalled: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onStalled: true }),
|
|
captured: keyOf({ onStalledCapture: true })
|
|
}
|
|
},
|
|
submit: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onSubmit: true }),
|
|
captured: keyOf({ onSubmitCapture: true })
|
|
}
|
|
},
|
|
suspend: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onSuspend: true }),
|
|
captured: keyOf({ onSuspendCapture: true })
|
|
}
|
|
},
|
|
timeUpdate: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onTimeUpdate: true }),
|
|
captured: keyOf({ onTimeUpdateCapture: true })
|
|
}
|
|
},
|
|
touchCancel: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onTouchCancel: true }),
|
|
captured: keyOf({ onTouchCancelCapture: true })
|
|
}
|
|
},
|
|
touchEnd: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onTouchEnd: true }),
|
|
captured: keyOf({ onTouchEndCapture: true })
|
|
}
|
|
},
|
|
touchMove: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onTouchMove: true }),
|
|
captured: keyOf({ onTouchMoveCapture: true })
|
|
}
|
|
},
|
|
touchStart: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onTouchStart: true }),
|
|
captured: keyOf({ onTouchStartCapture: true })
|
|
}
|
|
},
|
|
volumeChange: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onVolumeChange: true }),
|
|
captured: keyOf({ onVolumeChangeCapture: true })
|
|
}
|
|
},
|
|
waiting: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onWaiting: true }),
|
|
captured: keyOf({ onWaitingCapture: true })
|
|
}
|
|
},
|
|
wheel: {
|
|
phasedRegistrationNames: {
|
|
bubbled: keyOf({ onWheel: true }),
|
|
captured: keyOf({ onWheelCapture: true })
|
|
}
|
|
}
|
|
};
|
|
|
|
var topLevelEventsToDispatchConfig = {
|
|
topAbort: eventTypes.abort,
|
|
topBlur: eventTypes.blur,
|
|
topCanPlay: eventTypes.canPlay,
|
|
topCanPlayThrough: eventTypes.canPlayThrough,
|
|
topClick: eventTypes.click,
|
|
topContextMenu: eventTypes.contextMenu,
|
|
topCopy: eventTypes.copy,
|
|
topCut: eventTypes.cut,
|
|
topDoubleClick: eventTypes.doubleClick,
|
|
topDrag: eventTypes.drag,
|
|
topDragEnd: eventTypes.dragEnd,
|
|
topDragEnter: eventTypes.dragEnter,
|
|
topDragExit: eventTypes.dragExit,
|
|
topDragLeave: eventTypes.dragLeave,
|
|
topDragOver: eventTypes.dragOver,
|
|
topDragStart: eventTypes.dragStart,
|
|
topDrop: eventTypes.drop,
|
|
topDurationChange: eventTypes.durationChange,
|
|
topEmptied: eventTypes.emptied,
|
|
topEncrypted: eventTypes.encrypted,
|
|
topEnded: eventTypes.ended,
|
|
topError: eventTypes.error,
|
|
topFocus: eventTypes.focus,
|
|
topInput: eventTypes.input,
|
|
topKeyDown: eventTypes.keyDown,
|
|
topKeyPress: eventTypes.keyPress,
|
|
topKeyUp: eventTypes.keyUp,
|
|
topLoad: eventTypes.load,
|
|
topLoadedData: eventTypes.loadedData,
|
|
topLoadedMetadata: eventTypes.loadedMetadata,
|
|
topLoadStart: eventTypes.loadStart,
|
|
topMouseDown: eventTypes.mouseDown,
|
|
topMouseMove: eventTypes.mouseMove,
|
|
topMouseOut: eventTypes.mouseOut,
|
|
topMouseOver: eventTypes.mouseOver,
|
|
topMouseUp: eventTypes.mouseUp,
|
|
topPaste: eventTypes.paste,
|
|
topPause: eventTypes.pause,
|
|
topPlay: eventTypes.play,
|
|
topPlaying: eventTypes.playing,
|
|
topProgress: eventTypes.progress,
|
|
topRateChange: eventTypes.rateChange,
|
|
topReset: eventTypes.reset,
|
|
topScroll: eventTypes.scroll,
|
|
topSeeked: eventTypes.seeked,
|
|
topSeeking: eventTypes.seeking,
|
|
topStalled: eventTypes.stalled,
|
|
topSubmit: eventTypes.submit,
|
|
topSuspend: eventTypes.suspend,
|
|
topTimeUpdate: eventTypes.timeUpdate,
|
|
topTouchCancel: eventTypes.touchCancel,
|
|
topTouchEnd: eventTypes.touchEnd,
|
|
topTouchMove: eventTypes.touchMove,
|
|
topTouchStart: eventTypes.touchStart,
|
|
topVolumeChange: eventTypes.volumeChange,
|
|
topWaiting: eventTypes.waiting,
|
|
topWheel: eventTypes.wheel
|
|
};
|
|
|
|
for (var type in topLevelEventsToDispatchConfig) {
|
|
topLevelEventsToDispatchConfig[type].dependencies = [type];
|
|
}
|
|
|
|
var ON_CLICK_KEY = keyOf({ onClick: null });
|
|
var onClickListeners = {};
|
|
|
|
var SimpleEventPlugin = {
|
|
|
|
eventTypes: eventTypes,
|
|
|
|
/**
|
|
* @param {string} topLevelType Record from `EventConstants`.
|
|
* @param {DOMEventTarget} topLevelTarget The listening component root node.
|
|
* @param {string} topLevelTargetID ID of `topLevelTarget`.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {*} An accumulation of synthetic events.
|
|
* @see {EventPluginHub.extractEvents}
|
|
*/
|
|
extractEvents: function (topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget) {
|
|
var dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];
|
|
if (!dispatchConfig) {
|
|
return null;
|
|
}
|
|
var EventConstructor;
|
|
switch (topLevelType) {
|
|
case topLevelTypes.topAbort:
|
|
case topLevelTypes.topCanPlay:
|
|
case topLevelTypes.topCanPlayThrough:
|
|
case topLevelTypes.topDurationChange:
|
|
case topLevelTypes.topEmptied:
|
|
case topLevelTypes.topEncrypted:
|
|
case topLevelTypes.topEnded:
|
|
case topLevelTypes.topError:
|
|
case topLevelTypes.topInput:
|
|
case topLevelTypes.topLoad:
|
|
case topLevelTypes.topLoadedData:
|
|
case topLevelTypes.topLoadedMetadata:
|
|
case topLevelTypes.topLoadStart:
|
|
case topLevelTypes.topPause:
|
|
case topLevelTypes.topPlay:
|
|
case topLevelTypes.topPlaying:
|
|
case topLevelTypes.topProgress:
|
|
case topLevelTypes.topRateChange:
|
|
case topLevelTypes.topReset:
|
|
case topLevelTypes.topSeeked:
|
|
case topLevelTypes.topSeeking:
|
|
case topLevelTypes.topStalled:
|
|
case topLevelTypes.topSubmit:
|
|
case topLevelTypes.topSuspend:
|
|
case topLevelTypes.topTimeUpdate:
|
|
case topLevelTypes.topVolumeChange:
|
|
case topLevelTypes.topWaiting:
|
|
// HTML Events
|
|
// @see http://www.w3.org/TR/html5/index.html#events-0
|
|
EventConstructor = SyntheticEvent;
|
|
break;
|
|
case topLevelTypes.topKeyPress:
|
|
// FireFox creates a keypress event for function keys too. This removes
|
|
// the unwanted keypress events. Enter is however both printable and
|
|
// non-printable. One would expect Tab to be as well (but it isn't).
|
|
if (getEventCharCode(nativeEvent) === 0) {
|
|
return null;
|
|
}
|
|
/* falls through */
|
|
case topLevelTypes.topKeyDown:
|
|
case topLevelTypes.topKeyUp:
|
|
EventConstructor = SyntheticKeyboardEvent;
|
|
break;
|
|
case topLevelTypes.topBlur:
|
|
case topLevelTypes.topFocus:
|
|
EventConstructor = SyntheticFocusEvent;
|
|
break;
|
|
case topLevelTypes.topClick:
|
|
// Firefox creates a click event on right mouse clicks. This removes the
|
|
// unwanted click events.
|
|
if (nativeEvent.button === 2) {
|
|
return null;
|
|
}
|
|
/* falls through */
|
|
case topLevelTypes.topContextMenu:
|
|
case topLevelTypes.topDoubleClick:
|
|
case topLevelTypes.topMouseDown:
|
|
case topLevelTypes.topMouseMove:
|
|
case topLevelTypes.topMouseOut:
|
|
case topLevelTypes.topMouseOver:
|
|
case topLevelTypes.topMouseUp:
|
|
EventConstructor = SyntheticMouseEvent;
|
|
break;
|
|
case topLevelTypes.topDrag:
|
|
case topLevelTypes.topDragEnd:
|
|
case topLevelTypes.topDragEnter:
|
|
case topLevelTypes.topDragExit:
|
|
case topLevelTypes.topDragLeave:
|
|
case topLevelTypes.topDragOver:
|
|
case topLevelTypes.topDragStart:
|
|
case topLevelTypes.topDrop:
|
|
EventConstructor = SyntheticDragEvent;
|
|
break;
|
|
case topLevelTypes.topTouchCancel:
|
|
case topLevelTypes.topTouchEnd:
|
|
case topLevelTypes.topTouchMove:
|
|
case topLevelTypes.topTouchStart:
|
|
EventConstructor = SyntheticTouchEvent;
|
|
break;
|
|
case topLevelTypes.topScroll:
|
|
EventConstructor = SyntheticUIEvent;
|
|
break;
|
|
case topLevelTypes.topWheel:
|
|
EventConstructor = SyntheticWheelEvent;
|
|
break;
|
|
case topLevelTypes.topCopy:
|
|
case topLevelTypes.topCut:
|
|
case topLevelTypes.topPaste:
|
|
EventConstructor = SyntheticClipboardEvent;
|
|
break;
|
|
}
|
|
!EventConstructor ? process.env.NODE_ENV !== 'production' ? invariant(false, 'SimpleEventPlugin: Unhandled event type, `%s`.', topLevelType) : invariant(false) : undefined;
|
|
var event = EventConstructor.getPooled(dispatchConfig, topLevelTargetID, nativeEvent, nativeEventTarget);
|
|
EventPropagators.accumulateTwoPhaseDispatches(event);
|
|
return event;
|
|
},
|
|
|
|
didPutListener: function (id, registrationName, listener) {
|
|
// Mobile Safari does not fire properly bubble click events on
|
|
// non-interactive elements, which means delegated click listeners do not
|
|
// fire. The workaround for this bug involves attaching an empty click
|
|
// listener on the target node.
|
|
if (registrationName === ON_CLICK_KEY) {
|
|
var node = ReactMount.getNode(id);
|
|
if (!onClickListeners[id]) {
|
|
onClickListeners[id] = EventListener.listen(node, 'click', emptyFunction);
|
|
}
|
|
}
|
|
},
|
|
|
|
willDeleteListener: function (id, registrationName) {
|
|
if (registrationName === ON_CLICK_KEY) {
|
|
onClickListeners[id].remove();
|
|
delete onClickListeners[id];
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = SimpleEventPlugin;
|
|
}).call(this,require('_process'))
|
|
},{"./EventConstants":149,"./EventPropagators":153,"./ReactMount":200,"./SyntheticClipboardEvent":224,"./SyntheticDragEvent":226,"./SyntheticEvent":227,"./SyntheticFocusEvent":228,"./SyntheticKeyboardEvent":230,"./SyntheticMouseEvent":231,"./SyntheticTouchEvent":232,"./SyntheticUIEvent":233,"./SyntheticWheelEvent":234,"./getEventCharCode":246,"_process":133,"fbjs/lib/EventListener":57,"fbjs/lib/emptyFunction":64,"fbjs/lib/invariant":72,"fbjs/lib/keyOf":76}],224:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SyntheticClipboardEvent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var SyntheticEvent = require('./SyntheticEvent');
|
|
|
|
/**
|
|
* @interface Event
|
|
* @see http://www.w3.org/TR/clipboard-apis/
|
|
*/
|
|
var ClipboardEventInterface = {
|
|
clipboardData: function (event) {
|
|
return 'clipboardData' in event ? event.clipboardData : window.clipboardData;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
|
* @param {string} dispatchMarker Marker identifying the event target.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @extends {SyntheticUIEvent}
|
|
*/
|
|
function SyntheticClipboardEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
|
|
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
|
|
}
|
|
|
|
SyntheticEvent.augmentClass(SyntheticClipboardEvent, ClipboardEventInterface);
|
|
|
|
module.exports = SyntheticClipboardEvent;
|
|
},{"./SyntheticEvent":227}],225:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SyntheticCompositionEvent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var SyntheticEvent = require('./SyntheticEvent');
|
|
|
|
/**
|
|
* @interface Event
|
|
* @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
|
|
*/
|
|
var CompositionEventInterface = {
|
|
data: null
|
|
};
|
|
|
|
/**
|
|
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
|
* @param {string} dispatchMarker Marker identifying the event target.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @extends {SyntheticUIEvent}
|
|
*/
|
|
function SyntheticCompositionEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
|
|
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
|
|
}
|
|
|
|
SyntheticEvent.augmentClass(SyntheticCompositionEvent, CompositionEventInterface);
|
|
|
|
module.exports = SyntheticCompositionEvent;
|
|
},{"./SyntheticEvent":227}],226:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SyntheticDragEvent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var SyntheticMouseEvent = require('./SyntheticMouseEvent');
|
|
|
|
/**
|
|
* @interface DragEvent
|
|
* @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|
*/
|
|
var DragEventInterface = {
|
|
dataTransfer: null
|
|
};
|
|
|
|
/**
|
|
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
|
* @param {string} dispatchMarker Marker identifying the event target.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @extends {SyntheticUIEvent}
|
|
*/
|
|
function SyntheticDragEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
|
|
SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
|
|
}
|
|
|
|
SyntheticMouseEvent.augmentClass(SyntheticDragEvent, DragEventInterface);
|
|
|
|
module.exports = SyntheticDragEvent;
|
|
},{"./SyntheticMouseEvent":231}],227:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SyntheticEvent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var PooledClass = require('./PooledClass');
|
|
|
|
var assign = require('./Object.assign');
|
|
var emptyFunction = require('fbjs/lib/emptyFunction');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
/**
|
|
* @interface Event
|
|
* @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|
*/
|
|
var EventInterface = {
|
|
type: null,
|
|
target: null,
|
|
// currentTarget is set when dispatching; no use in copying it here
|
|
currentTarget: emptyFunction.thatReturnsNull,
|
|
eventPhase: null,
|
|
bubbles: null,
|
|
cancelable: null,
|
|
timeStamp: function (event) {
|
|
return event.timeStamp || Date.now();
|
|
},
|
|
defaultPrevented: null,
|
|
isTrusted: null
|
|
};
|
|
|
|
/**
|
|
* Synthetic events are dispatched by event plugins, typically in response to a
|
|
* top-level event delegation handler.
|
|
*
|
|
* These systems should generally use pooling to reduce the frequency of garbage
|
|
* collection. The system should check `isPersistent` to determine whether the
|
|
* event should be released into the pool after being dispatched. Users that
|
|
* need a persisted event should invoke `persist`.
|
|
*
|
|
* Synthetic events (and subclasses) implement the DOM Level 3 Events API by
|
|
* normalizing browser quirks. Subclasses do not necessarily have to implement a
|
|
* DOM interface; custom application-specific events can also subclass this.
|
|
*
|
|
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
|
* @param {string} dispatchMarker Marker identifying the event target.
|
|
* @param {object} nativeEvent Native browser event.
|
|
*/
|
|
function SyntheticEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
|
|
this.dispatchConfig = dispatchConfig;
|
|
this.dispatchMarker = dispatchMarker;
|
|
this.nativeEvent = nativeEvent;
|
|
|
|
var Interface = this.constructor.Interface;
|
|
for (var propName in Interface) {
|
|
if (!Interface.hasOwnProperty(propName)) {
|
|
continue;
|
|
}
|
|
var normalize = Interface[propName];
|
|
if (normalize) {
|
|
this[propName] = normalize(nativeEvent);
|
|
} else {
|
|
if (propName === 'target') {
|
|
this.target = nativeEventTarget;
|
|
} else {
|
|
this[propName] = nativeEvent[propName];
|
|
}
|
|
}
|
|
}
|
|
|
|
var defaultPrevented = nativeEvent.defaultPrevented != null ? nativeEvent.defaultPrevented : nativeEvent.returnValue === false;
|
|
if (defaultPrevented) {
|
|
this.isDefaultPrevented = emptyFunction.thatReturnsTrue;
|
|
} else {
|
|
this.isDefaultPrevented = emptyFunction.thatReturnsFalse;
|
|
}
|
|
this.isPropagationStopped = emptyFunction.thatReturnsFalse;
|
|
}
|
|
|
|
assign(SyntheticEvent.prototype, {
|
|
|
|
preventDefault: function () {
|
|
this.defaultPrevented = true;
|
|
var event = this.nativeEvent;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(event, 'This synthetic event is reused for performance reasons. If you\'re ' + 'seeing this, you\'re calling `preventDefault` on a ' + 'released/nullified synthetic event. This is a no-op. See ' + 'https://fb.me/react-event-pooling for more information.') : undefined;
|
|
}
|
|
if (!event) {
|
|
return;
|
|
}
|
|
|
|
if (event.preventDefault) {
|
|
event.preventDefault();
|
|
} else {
|
|
event.returnValue = false;
|
|
}
|
|
this.isDefaultPrevented = emptyFunction.thatReturnsTrue;
|
|
},
|
|
|
|
stopPropagation: function () {
|
|
var event = this.nativeEvent;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(event, 'This synthetic event is reused for performance reasons. If you\'re ' + 'seeing this, you\'re calling `stopPropagation` on a ' + 'released/nullified synthetic event. This is a no-op. See ' + 'https://fb.me/react-event-pooling for more information.') : undefined;
|
|
}
|
|
if (!event) {
|
|
return;
|
|
}
|
|
|
|
if (event.stopPropagation) {
|
|
event.stopPropagation();
|
|
} else {
|
|
event.cancelBubble = true;
|
|
}
|
|
this.isPropagationStopped = emptyFunction.thatReturnsTrue;
|
|
},
|
|
|
|
/**
|
|
* We release all dispatched `SyntheticEvent`s after each event loop, adding
|
|
* them back into the pool. This allows a way to hold onto a reference that
|
|
* won't be added back into the pool.
|
|
*/
|
|
persist: function () {
|
|
this.isPersistent = emptyFunction.thatReturnsTrue;
|
|
},
|
|
|
|
/**
|
|
* Checks if this event should be released back into the pool.
|
|
*
|
|
* @return {boolean} True if this should not be released, false otherwise.
|
|
*/
|
|
isPersistent: emptyFunction.thatReturnsFalse,
|
|
|
|
/**
|
|
* `PooledClass` looks for `destructor` on each instance it releases.
|
|
*/
|
|
destructor: function () {
|
|
var Interface = this.constructor.Interface;
|
|
for (var propName in Interface) {
|
|
this[propName] = null;
|
|
}
|
|
this.dispatchConfig = null;
|
|
this.dispatchMarker = null;
|
|
this.nativeEvent = null;
|
|
}
|
|
|
|
});
|
|
|
|
SyntheticEvent.Interface = EventInterface;
|
|
|
|
/**
|
|
* Helper to reduce boilerplate when creating subclasses.
|
|
*
|
|
* @param {function} Class
|
|
* @param {?object} Interface
|
|
*/
|
|
SyntheticEvent.augmentClass = function (Class, Interface) {
|
|
var Super = this;
|
|
|
|
var prototype = Object.create(Super.prototype);
|
|
assign(prototype, Class.prototype);
|
|
Class.prototype = prototype;
|
|
Class.prototype.constructor = Class;
|
|
|
|
Class.Interface = assign({}, Super.Interface, Interface);
|
|
Class.augmentClass = Super.augmentClass;
|
|
|
|
PooledClass.addPoolingTo(Class, PooledClass.fourArgumentPooler);
|
|
};
|
|
|
|
PooledClass.addPoolingTo(SyntheticEvent, PooledClass.fourArgumentPooler);
|
|
|
|
module.exports = SyntheticEvent;
|
|
}).call(this,require('_process'))
|
|
},{"./Object.assign":157,"./PooledClass":158,"_process":133,"fbjs/lib/emptyFunction":64,"fbjs/lib/warning":83}],228:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SyntheticFocusEvent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var SyntheticUIEvent = require('./SyntheticUIEvent');
|
|
|
|
/**
|
|
* @interface FocusEvent
|
|
* @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|
*/
|
|
var FocusEventInterface = {
|
|
relatedTarget: null
|
|
};
|
|
|
|
/**
|
|
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
|
* @param {string} dispatchMarker Marker identifying the event target.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @extends {SyntheticUIEvent}
|
|
*/
|
|
function SyntheticFocusEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
|
|
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
|
|
}
|
|
|
|
SyntheticUIEvent.augmentClass(SyntheticFocusEvent, FocusEventInterface);
|
|
|
|
module.exports = SyntheticFocusEvent;
|
|
},{"./SyntheticUIEvent":233}],229:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SyntheticInputEvent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var SyntheticEvent = require('./SyntheticEvent');
|
|
|
|
/**
|
|
* @interface Event
|
|
* @see http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105
|
|
* /#events-inputevents
|
|
*/
|
|
var InputEventInterface = {
|
|
data: null
|
|
};
|
|
|
|
/**
|
|
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
|
* @param {string} dispatchMarker Marker identifying the event target.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @extends {SyntheticUIEvent}
|
|
*/
|
|
function SyntheticInputEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
|
|
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
|
|
}
|
|
|
|
SyntheticEvent.augmentClass(SyntheticInputEvent, InputEventInterface);
|
|
|
|
module.exports = SyntheticInputEvent;
|
|
},{"./SyntheticEvent":227}],230:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SyntheticKeyboardEvent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var SyntheticUIEvent = require('./SyntheticUIEvent');
|
|
|
|
var getEventCharCode = require('./getEventCharCode');
|
|
var getEventKey = require('./getEventKey');
|
|
var getEventModifierState = require('./getEventModifierState');
|
|
|
|
/**
|
|
* @interface KeyboardEvent
|
|
* @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|
*/
|
|
var KeyboardEventInterface = {
|
|
key: getEventKey,
|
|
location: null,
|
|
ctrlKey: null,
|
|
shiftKey: null,
|
|
altKey: null,
|
|
metaKey: null,
|
|
repeat: null,
|
|
locale: null,
|
|
getModifierState: getEventModifierState,
|
|
// Legacy Interface
|
|
charCode: function (event) {
|
|
// `charCode` is the result of a KeyPress event and represents the value of
|
|
// the actual printable character.
|
|
|
|
// KeyPress is deprecated, but its replacement is not yet final and not
|
|
// implemented in any major browser. Only KeyPress has charCode.
|
|
if (event.type === 'keypress') {
|
|
return getEventCharCode(event);
|
|
}
|
|
return 0;
|
|
},
|
|
keyCode: function (event) {
|
|
// `keyCode` is the result of a KeyDown/Up event and represents the value of
|
|
// physical keyboard key.
|
|
|
|
// The actual meaning of the value depends on the users' keyboard layout
|
|
// which cannot be detected. Assuming that it is a US keyboard layout
|
|
// provides a surprisingly accurate mapping for US and European users.
|
|
// Due to this, it is left to the user to implement at this time.
|
|
if (event.type === 'keydown' || event.type === 'keyup') {
|
|
return event.keyCode;
|
|
}
|
|
return 0;
|
|
},
|
|
which: function (event) {
|
|
// `which` is an alias for either `keyCode` or `charCode` depending on the
|
|
// type of the event.
|
|
if (event.type === 'keypress') {
|
|
return getEventCharCode(event);
|
|
}
|
|
if (event.type === 'keydown' || event.type === 'keyup') {
|
|
return event.keyCode;
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
|
* @param {string} dispatchMarker Marker identifying the event target.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @extends {SyntheticUIEvent}
|
|
*/
|
|
function SyntheticKeyboardEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
|
|
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
|
|
}
|
|
|
|
SyntheticUIEvent.augmentClass(SyntheticKeyboardEvent, KeyboardEventInterface);
|
|
|
|
module.exports = SyntheticKeyboardEvent;
|
|
},{"./SyntheticUIEvent":233,"./getEventCharCode":246,"./getEventKey":247,"./getEventModifierState":248}],231:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SyntheticMouseEvent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var SyntheticUIEvent = require('./SyntheticUIEvent');
|
|
var ViewportMetrics = require('./ViewportMetrics');
|
|
|
|
var getEventModifierState = require('./getEventModifierState');
|
|
|
|
/**
|
|
* @interface MouseEvent
|
|
* @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|
*/
|
|
var MouseEventInterface = {
|
|
screenX: null,
|
|
screenY: null,
|
|
clientX: null,
|
|
clientY: null,
|
|
ctrlKey: null,
|
|
shiftKey: null,
|
|
altKey: null,
|
|
metaKey: null,
|
|
getModifierState: getEventModifierState,
|
|
button: function (event) {
|
|
// Webkit, Firefox, IE9+
|
|
// which: 1 2 3
|
|
// button: 0 1 2 (standard)
|
|
var button = event.button;
|
|
if ('which' in event) {
|
|
return button;
|
|
}
|
|
// IE<9
|
|
// which: undefined
|
|
// button: 0 0 0
|
|
// button: 1 4 2 (onmouseup)
|
|
return button === 2 ? 2 : button === 4 ? 1 : 0;
|
|
},
|
|
buttons: null,
|
|
relatedTarget: function (event) {
|
|
return event.relatedTarget || (event.fromElement === event.srcElement ? event.toElement : event.fromElement);
|
|
},
|
|
// "Proprietary" Interface.
|
|
pageX: function (event) {
|
|
return 'pageX' in event ? event.pageX : event.clientX + ViewportMetrics.currentScrollLeft;
|
|
},
|
|
pageY: function (event) {
|
|
return 'pageY' in event ? event.pageY : event.clientY + ViewportMetrics.currentScrollTop;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
|
* @param {string} dispatchMarker Marker identifying the event target.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @extends {SyntheticUIEvent}
|
|
*/
|
|
function SyntheticMouseEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
|
|
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
|
|
}
|
|
|
|
SyntheticUIEvent.augmentClass(SyntheticMouseEvent, MouseEventInterface);
|
|
|
|
module.exports = SyntheticMouseEvent;
|
|
},{"./SyntheticUIEvent":233,"./ViewportMetrics":236,"./getEventModifierState":248}],232:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SyntheticTouchEvent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var SyntheticUIEvent = require('./SyntheticUIEvent');
|
|
|
|
var getEventModifierState = require('./getEventModifierState');
|
|
|
|
/**
|
|
* @interface TouchEvent
|
|
* @see http://www.w3.org/TR/touch-events/
|
|
*/
|
|
var TouchEventInterface = {
|
|
touches: null,
|
|
targetTouches: null,
|
|
changedTouches: null,
|
|
altKey: null,
|
|
metaKey: null,
|
|
ctrlKey: null,
|
|
shiftKey: null,
|
|
getModifierState: getEventModifierState
|
|
};
|
|
|
|
/**
|
|
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
|
* @param {string} dispatchMarker Marker identifying the event target.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @extends {SyntheticUIEvent}
|
|
*/
|
|
function SyntheticTouchEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
|
|
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
|
|
}
|
|
|
|
SyntheticUIEvent.augmentClass(SyntheticTouchEvent, TouchEventInterface);
|
|
|
|
module.exports = SyntheticTouchEvent;
|
|
},{"./SyntheticUIEvent":233,"./getEventModifierState":248}],233:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SyntheticUIEvent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var SyntheticEvent = require('./SyntheticEvent');
|
|
|
|
var getEventTarget = require('./getEventTarget');
|
|
|
|
/**
|
|
* @interface UIEvent
|
|
* @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|
*/
|
|
var UIEventInterface = {
|
|
view: function (event) {
|
|
if (event.view) {
|
|
return event.view;
|
|
}
|
|
|
|
var target = getEventTarget(event);
|
|
if (target != null && target.window === target) {
|
|
// target is a window object
|
|
return target;
|
|
}
|
|
|
|
var doc = target.ownerDocument;
|
|
// TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
|
|
if (doc) {
|
|
return doc.defaultView || doc.parentWindow;
|
|
} else {
|
|
return window;
|
|
}
|
|
},
|
|
detail: function (event) {
|
|
return event.detail || 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
|
* @param {string} dispatchMarker Marker identifying the event target.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @extends {SyntheticEvent}
|
|
*/
|
|
function SyntheticUIEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
|
|
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
|
|
}
|
|
|
|
SyntheticEvent.augmentClass(SyntheticUIEvent, UIEventInterface);
|
|
|
|
module.exports = SyntheticUIEvent;
|
|
},{"./SyntheticEvent":227,"./getEventTarget":249}],234:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule SyntheticWheelEvent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var SyntheticMouseEvent = require('./SyntheticMouseEvent');
|
|
|
|
/**
|
|
* @interface WheelEvent
|
|
* @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|
*/
|
|
var WheelEventInterface = {
|
|
deltaX: function (event) {
|
|
return 'deltaX' in event ? event.deltaX :
|
|
// Fallback to `wheelDeltaX` for Webkit and normalize (right is positive).
|
|
'wheelDeltaX' in event ? -event.wheelDeltaX : 0;
|
|
},
|
|
deltaY: function (event) {
|
|
return 'deltaY' in event ? event.deltaY :
|
|
// Fallback to `wheelDeltaY` for Webkit and normalize (down is positive).
|
|
'wheelDeltaY' in event ? -event.wheelDeltaY :
|
|
// Fallback to `wheelDelta` for IE<9 and normalize (down is positive).
|
|
'wheelDelta' in event ? -event.wheelDelta : 0;
|
|
},
|
|
deltaZ: null,
|
|
|
|
// Browsers without "deltaMode" is reporting in raw wheel delta where one
|
|
// notch on the scroll is always +/- 120, roughly equivalent to pixels.
|
|
// A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or
|
|
// ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size.
|
|
deltaMode: null
|
|
};
|
|
|
|
/**
|
|
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
|
* @param {string} dispatchMarker Marker identifying the event target.
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @extends {SyntheticMouseEvent}
|
|
*/
|
|
function SyntheticWheelEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
|
|
SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
|
|
}
|
|
|
|
SyntheticMouseEvent.augmentClass(SyntheticWheelEvent, WheelEventInterface);
|
|
|
|
module.exports = SyntheticWheelEvent;
|
|
},{"./SyntheticMouseEvent":231}],235:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule Transaction
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
/**
|
|
* `Transaction` creates a black box that is able to wrap any method such that
|
|
* certain invariants are maintained before and after the method is invoked
|
|
* (Even if an exception is thrown while invoking the wrapped method). Whoever
|
|
* instantiates a transaction can provide enforcers of the invariants at
|
|
* creation time. The `Transaction` class itself will supply one additional
|
|
* automatic invariant for you - the invariant that any transaction instance
|
|
* should not be run while it is already being run. You would typically create a
|
|
* single instance of a `Transaction` for reuse multiple times, that potentially
|
|
* is used to wrap several different methods. Wrappers are extremely simple -
|
|
* they only require implementing two methods.
|
|
*
|
|
* <pre>
|
|
* wrappers (injected at creation time)
|
|
* + +
|
|
* | |
|
|
* +-----------------|--------|--------------+
|
|
* | v | |
|
|
* | +---------------+ | |
|
|
* | +--| wrapper1 |---|----+ |
|
|
* | | +---------------+ v | |
|
|
* | | +-------------+ | |
|
|
* | | +----| wrapper2 |--------+ |
|
|
* | | | +-------------+ | | |
|
|
* | | | | | |
|
|
* | v v v v | wrapper
|
|
* | +---+ +---+ +---------+ +---+ +---+ | invariants
|
|
* perform(anyMethod) | | | | | | | | | | | | maintained
|
|
* +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
|
|
* | | | | | | | | | | | |
|
|
* | | | | | | | | | | | |
|
|
* | | | | | | | | | | | |
|
|
* | +---+ +---+ +---------+ +---+ +---+ |
|
|
* | initialize close |
|
|
* +-----------------------------------------+
|
|
* </pre>
|
|
*
|
|
* Use cases:
|
|
* - Preserving the input selection ranges before/after reconciliation.
|
|
* Restoring selection even in the event of an unexpected error.
|
|
* - Deactivating events while rearranging the DOM, preventing blurs/focuses,
|
|
* while guaranteeing that afterwards, the event system is reactivated.
|
|
* - Flushing a queue of collected DOM mutations to the main UI thread after a
|
|
* reconciliation takes place in a worker thread.
|
|
* - Invoking any collected `componentDidUpdate` callbacks after rendering new
|
|
* content.
|
|
* - (Future use case): Wrapping particular flushes of the `ReactWorker` queue
|
|
* to preserve the `scrollTop` (an automatic scroll aware DOM).
|
|
* - (Future use case): Layout calculations before and after DOM updates.
|
|
*
|
|
* Transactional plugin API:
|
|
* - A module that has an `initialize` method that returns any precomputation.
|
|
* - and a `close` method that accepts the precomputation. `close` is invoked
|
|
* when the wrapped process is completed, or has failed.
|
|
*
|
|
* @param {Array<TransactionalWrapper>} transactionWrapper Wrapper modules
|
|
* that implement `initialize` and `close`.
|
|
* @return {Transaction} Single transaction for reuse in thread.
|
|
*
|
|
* @class Transaction
|
|
*/
|
|
var Mixin = {
|
|
/**
|
|
* Sets up this instance so that it is prepared for collecting metrics. Does
|
|
* so such that this setup method may be used on an instance that is already
|
|
* initialized, in a way that does not consume additional memory upon reuse.
|
|
* That can be useful if you decide to make your subclass of this mixin a
|
|
* "PooledClass".
|
|
*/
|
|
reinitializeTransaction: function () {
|
|
this.transactionWrappers = this.getTransactionWrappers();
|
|
if (this.wrapperInitData) {
|
|
this.wrapperInitData.length = 0;
|
|
} else {
|
|
this.wrapperInitData = [];
|
|
}
|
|
this._isInTransaction = false;
|
|
},
|
|
|
|
_isInTransaction: false,
|
|
|
|
/**
|
|
* @abstract
|
|
* @return {Array<TransactionWrapper>} Array of transaction wrappers.
|
|
*/
|
|
getTransactionWrappers: null,
|
|
|
|
isInTransaction: function () {
|
|
return !!this._isInTransaction;
|
|
},
|
|
|
|
/**
|
|
* Executes the function within a safety window. Use this for the top level
|
|
* methods that result in large amounts of computation/mutations that would
|
|
* need to be safety checked. The optional arguments helps prevent the need
|
|
* to bind in many cases.
|
|
*
|
|
* @param {function} method Member of scope to call.
|
|
* @param {Object} scope Scope to invoke from.
|
|
* @param {Object?=} a Argument to pass to the method.
|
|
* @param {Object?=} b Argument to pass to the method.
|
|
* @param {Object?=} c Argument to pass to the method.
|
|
* @param {Object?=} d Argument to pass to the method.
|
|
* @param {Object?=} e Argument to pass to the method.
|
|
* @param {Object?=} f Argument to pass to the method.
|
|
*
|
|
* @return {*} Return value from `method`.
|
|
*/
|
|
perform: function (method, scope, a, b, c, d, e, f) {
|
|
!!this.isInTransaction() ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Transaction.perform(...): Cannot initialize a transaction when there ' + 'is already an outstanding transaction.') : invariant(false) : undefined;
|
|
var errorThrown;
|
|
var ret;
|
|
try {
|
|
this._isInTransaction = true;
|
|
// Catching errors makes debugging more difficult, so we start with
|
|
// errorThrown set to true before setting it to false after calling
|
|
// close -- if it's still set to true in the finally block, it means
|
|
// one of these calls threw.
|
|
errorThrown = true;
|
|
this.initializeAll(0);
|
|
ret = method.call(scope, a, b, c, d, e, f);
|
|
errorThrown = false;
|
|
} finally {
|
|
try {
|
|
if (errorThrown) {
|
|
// If `method` throws, prefer to show that stack trace over any thrown
|
|
// by invoking `closeAll`.
|
|
try {
|
|
this.closeAll(0);
|
|
} catch (err) {}
|
|
} else {
|
|
// Since `method` didn't throw, we don't want to silence the exception
|
|
// here.
|
|
this.closeAll(0);
|
|
}
|
|
} finally {
|
|
this._isInTransaction = false;
|
|
}
|
|
}
|
|
return ret;
|
|
},
|
|
|
|
initializeAll: function (startIndex) {
|
|
var transactionWrappers = this.transactionWrappers;
|
|
for (var i = startIndex; i < transactionWrappers.length; i++) {
|
|
var wrapper = transactionWrappers[i];
|
|
try {
|
|
// Catching errors makes debugging more difficult, so we start with the
|
|
// OBSERVED_ERROR state before overwriting it with the real return value
|
|
// of initialize -- if it's still set to OBSERVED_ERROR in the finally
|
|
// block, it means wrapper.initialize threw.
|
|
this.wrapperInitData[i] = Transaction.OBSERVED_ERROR;
|
|
this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null;
|
|
} finally {
|
|
if (this.wrapperInitData[i] === Transaction.OBSERVED_ERROR) {
|
|
// The initializer for wrapper i threw an error; initialize the
|
|
// remaining wrappers but silence any exceptions from them to ensure
|
|
// that the first error is the one to bubble up.
|
|
try {
|
|
this.initializeAll(i + 1);
|
|
} catch (err) {}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Invokes each of `this.transactionWrappers.close[i]` functions, passing into
|
|
* them the respective return values of `this.transactionWrappers.init[i]`
|
|
* (`close`rs that correspond to initializers that failed will not be
|
|
* invoked).
|
|
*/
|
|
closeAll: function (startIndex) {
|
|
!this.isInTransaction() ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Transaction.closeAll(): Cannot close transaction when none are open.') : invariant(false) : undefined;
|
|
var transactionWrappers = this.transactionWrappers;
|
|
for (var i = startIndex; i < transactionWrappers.length; i++) {
|
|
var wrapper = transactionWrappers[i];
|
|
var initData = this.wrapperInitData[i];
|
|
var errorThrown;
|
|
try {
|
|
// Catching errors makes debugging more difficult, so we start with
|
|
// errorThrown set to true before setting it to false after calling
|
|
// close -- if it's still set to true in the finally block, it means
|
|
// wrapper.close threw.
|
|
errorThrown = true;
|
|
if (initData !== Transaction.OBSERVED_ERROR && wrapper.close) {
|
|
wrapper.close.call(this, initData);
|
|
}
|
|
errorThrown = false;
|
|
} finally {
|
|
if (errorThrown) {
|
|
// The closer for wrapper i threw an error; close the remaining
|
|
// wrappers but silence any exceptions from them to ensure that the
|
|
// first error is the one to bubble up.
|
|
try {
|
|
this.closeAll(i + 1);
|
|
} catch (e) {}
|
|
}
|
|
}
|
|
}
|
|
this.wrapperInitData.length = 0;
|
|
}
|
|
};
|
|
|
|
var Transaction = {
|
|
|
|
Mixin: Mixin,
|
|
|
|
/**
|
|
* Token to look for to determine if an error occurred.
|
|
*/
|
|
OBSERVED_ERROR: {}
|
|
|
|
};
|
|
|
|
module.exports = Transaction;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133,"fbjs/lib/invariant":72}],236:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule ViewportMetrics
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ViewportMetrics = {
|
|
|
|
currentScrollLeft: 0,
|
|
|
|
currentScrollTop: 0,
|
|
|
|
refreshScrollValues: function (scrollPosition) {
|
|
ViewportMetrics.currentScrollLeft = scrollPosition.x;
|
|
ViewportMetrics.currentScrollTop = scrollPosition.y;
|
|
}
|
|
|
|
};
|
|
|
|
module.exports = ViewportMetrics;
|
|
},{}],237:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2014-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule accumulateInto
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
/**
|
|
*
|
|
* Accumulates items that must not be null or undefined into the first one. This
|
|
* is used to conserve memory by avoiding array allocations, and thus sacrifices
|
|
* API cleanness. Since `current` can be null before being passed in and not
|
|
* null after this function, make sure to assign it back to `current`:
|
|
*
|
|
* `a = accumulateInto(a, b);`
|
|
*
|
|
* This API should be sparingly used. Try `accumulate` for something cleaner.
|
|
*
|
|
* @return {*|array<*>} An accumulation of items.
|
|
*/
|
|
|
|
function accumulateInto(current, next) {
|
|
!(next != null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'accumulateInto(...): Accumulated items must not be null or undefined.') : invariant(false) : undefined;
|
|
if (current == null) {
|
|
return next;
|
|
}
|
|
|
|
// Both are not empty. Warning: Never call x.concat(y) when you are not
|
|
// certain that x is an Array (x could be a string with concat method).
|
|
var currentIsArray = Array.isArray(current);
|
|
var nextIsArray = Array.isArray(next);
|
|
|
|
if (currentIsArray && nextIsArray) {
|
|
current.push.apply(current, next);
|
|
return current;
|
|
}
|
|
|
|
if (currentIsArray) {
|
|
current.push(next);
|
|
return current;
|
|
}
|
|
|
|
if (nextIsArray) {
|
|
// A bit too dangerous to mutate `next`.
|
|
return [current].concat(next);
|
|
}
|
|
|
|
return [current, next];
|
|
}
|
|
|
|
module.exports = accumulateInto;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133,"fbjs/lib/invariant":72}],238:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule adler32
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var MOD = 65521;
|
|
|
|
// adler32 is not cryptographically strong, and is only used to sanity check that
|
|
// markup generated on the server matches the markup generated on the client.
|
|
// This implementation (a modified version of the SheetJS version) has been optimized
|
|
// for our use case, at the expense of conforming to the adler32 specification
|
|
// for non-ascii inputs.
|
|
function adler32(data) {
|
|
var a = 1;
|
|
var b = 0;
|
|
var i = 0;
|
|
var l = data.length;
|
|
var m = l & ~0x3;
|
|
while (i < m) {
|
|
for (; i < Math.min(i + 4096, m); i += 4) {
|
|
b += (a += data.charCodeAt(i)) + (a += data.charCodeAt(i + 1)) + (a += data.charCodeAt(i + 2)) + (a += data.charCodeAt(i + 3));
|
|
}
|
|
a %= MOD;
|
|
b %= MOD;
|
|
}
|
|
for (; i < l; i++) {
|
|
b += a += data.charCodeAt(i);
|
|
}
|
|
a %= MOD;
|
|
b %= MOD;
|
|
return a | b << 16;
|
|
}
|
|
|
|
module.exports = adler32;
|
|
},{}],239:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule canDefineProperty
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var canDefineProperty = false;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
try {
|
|
Object.defineProperty({}, 'x', { get: function () {} });
|
|
canDefineProperty = true;
|
|
} catch (x) {
|
|
// IE will fail on defineProperty
|
|
}
|
|
}
|
|
|
|
module.exports = canDefineProperty;
|
|
}).call(this,require('_process'))
|
|
},{"_process":133}],240:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule dangerousStyleValue
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var CSSProperty = require('./CSSProperty');
|
|
|
|
var isUnitlessNumber = CSSProperty.isUnitlessNumber;
|
|
|
|
/**
|
|
* Convert a value into the proper css writable value. The style name `name`
|
|
* should be logical (no hyphens), as specified
|
|
* in `CSSProperty.isUnitlessNumber`.
|
|
*
|
|
* @param {string} name CSS property name such as `topMargin`.
|
|
* @param {*} value CSS property value such as `10px`.
|
|
* @return {string} Normalized style value with dimensions applied.
|
|
*/
|
|
function dangerousStyleValue(name, value) {
|
|
// Note that we've removed escapeTextForBrowser() calls here since the
|
|
// whole string will be escaped when the attribute is injected into
|
|
// the markup. If you provide unsafe user data here they can inject
|
|
// arbitrary CSS which may be problematic (I couldn't repro this):
|
|
// https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
|
|
// http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/
|
|
// This is not an XSS hole but instead a potential CSS injection issue
|
|
// which has lead to a greater discussion about how we're going to
|
|
// trust URLs moving forward. See #2115901
|
|
|
|
var isEmpty = value == null || typeof value === 'boolean' || value === '';
|
|
if (isEmpty) {
|
|
return '';
|
|
}
|
|
|
|
var isNonNumeric = isNaN(value);
|
|
if (isNonNumeric || value === 0 || isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name]) {
|
|
return '' + value; // cast to string
|
|
}
|
|
|
|
if (typeof value === 'string') {
|
|
value = value.trim();
|
|
}
|
|
return value + 'px';
|
|
}
|
|
|
|
module.exports = dangerousStyleValue;
|
|
},{"./CSSProperty":138}],241:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule deprecated
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var assign = require('./Object.assign');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
/**
|
|
* This will log a single deprecation notice per function and forward the call
|
|
* on to the new API.
|
|
*
|
|
* @param {string} fnName The name of the function
|
|
* @param {string} newModule The module that fn will exist in
|
|
* @param {string} newPackage The module that fn will exist in
|
|
* @param {*} ctx The context this forwarded call should run in
|
|
* @param {function} fn The function to forward on to
|
|
* @return {function} The function that will warn once and then call fn
|
|
*/
|
|
function deprecated(fnName, newModule, newPackage, ctx, fn) {
|
|
var warned = false;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var newFn = function () {
|
|
process.env.NODE_ENV !== 'production' ? warning(warned,
|
|
// Require examples in this string must be split to prevent React's
|
|
// build tools from mistaking them for real requires.
|
|
// Otherwise the build tools will attempt to build a '%s' module.
|
|
'React.%s is deprecated. Please use %s.%s from require' + '(\'%s\') ' + 'instead.', fnName, newModule, fnName, newPackage) : undefined;
|
|
warned = true;
|
|
return fn.apply(ctx, arguments);
|
|
};
|
|
// We need to make sure all properties of the original fn are copied over.
|
|
// In particular, this is needed to support PropTypes
|
|
return assign(newFn, fn);
|
|
}
|
|
|
|
return fn;
|
|
}
|
|
|
|
module.exports = deprecated;
|
|
}).call(this,require('_process'))
|
|
},{"./Object.assign":157,"_process":133,"fbjs/lib/warning":83}],242:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule escapeTextContentForBrowser
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ESCAPE_LOOKUP = {
|
|
'&': '&',
|
|
'>': '>',
|
|
'<': '<',
|
|
'"': '"',
|
|
'\'': '''
|
|
};
|
|
|
|
var ESCAPE_REGEX = /[&><"']/g;
|
|
|
|
function escaper(match) {
|
|
return ESCAPE_LOOKUP[match];
|
|
}
|
|
|
|
/**
|
|
* Escapes text to prevent scripting attacks.
|
|
*
|
|
* @param {*} text Text value to escape.
|
|
* @return {string} An escaped string.
|
|
*/
|
|
function escapeTextContentForBrowser(text) {
|
|
return ('' + text).replace(ESCAPE_REGEX, escaper);
|
|
}
|
|
|
|
module.exports = escapeTextContentForBrowser;
|
|
},{}],243:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule findDOMNode
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactCurrentOwner = require('./ReactCurrentOwner');
|
|
var ReactInstanceMap = require('./ReactInstanceMap');
|
|
var ReactMount = require('./ReactMount');
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
/**
|
|
* Returns the DOM node rendered by this element.
|
|
*
|
|
* @param {ReactComponent|DOMElement} componentOrElement
|
|
* @return {?DOMElement} The root node of this element.
|
|
*/
|
|
function findDOMNode(componentOrElement) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var owner = ReactCurrentOwner.current;
|
|
if (owner !== null) {
|
|
process.env.NODE_ENV !== 'production' ? warning(owner._warnedAboutRefsInRender, '%s is accessing getDOMNode or findDOMNode inside its render(). ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', owner.getName() || 'A component') : undefined;
|
|
owner._warnedAboutRefsInRender = true;
|
|
}
|
|
}
|
|
if (componentOrElement == null) {
|
|
return null;
|
|
}
|
|
if (componentOrElement.nodeType === 1) {
|
|
return componentOrElement;
|
|
}
|
|
if (ReactInstanceMap.has(componentOrElement)) {
|
|
return ReactMount.getNodeFromInstance(componentOrElement);
|
|
}
|
|
!(componentOrElement.render == null || typeof componentOrElement.render !== 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'findDOMNode was called on an unmounted component.') : invariant(false) : undefined;
|
|
!false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Element appears to be neither ReactComponent nor DOMNode (keys: %s)', Object.keys(componentOrElement)) : invariant(false) : undefined;
|
|
}
|
|
|
|
module.exports = findDOMNode;
|
|
}).call(this,require('_process'))
|
|
},{"./ReactCurrentOwner":169,"./ReactInstanceMap":197,"./ReactMount":200,"_process":133,"fbjs/lib/invariant":72,"fbjs/lib/warning":83}],244:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule flattenChildren
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var traverseAllChildren = require('./traverseAllChildren');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
/**
|
|
* @param {function} traverseContext Context passed through traversal.
|
|
* @param {?ReactComponent} child React child component.
|
|
* @param {!string} name String name of key path to child.
|
|
*/
|
|
function flattenSingleChildIntoContext(traverseContext, child, name) {
|
|
// We found a component instance.
|
|
var result = traverseContext;
|
|
var keyUnique = result[name] === undefined;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(keyUnique, 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.', name) : undefined;
|
|
}
|
|
if (keyUnique && child != null) {
|
|
result[name] = child;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Flattens children that are typically specified as `props.children`. Any null
|
|
* children will not be included in the resulting object.
|
|
* @return {!object} flattened children keyed by name.
|
|
*/
|
|
function flattenChildren(children) {
|
|
if (children == null) {
|
|
return children;
|
|
}
|
|
var result = {};
|
|
traverseAllChildren(children, flattenSingleChildIntoContext, result);
|
|
return result;
|
|
}
|
|
|
|
module.exports = flattenChildren;
|
|
}).call(this,require('_process'))
|
|
},{"./traverseAllChildren":262,"_process":133,"fbjs/lib/warning":83}],245:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule forEachAccumulated
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* @param {array} arr an "accumulation" of items which is either an Array or
|
|
* a single item. Useful when paired with the `accumulate` module. This is a
|
|
* simple utility that allows us to reason about a collection of items, but
|
|
* handling the case when there is exactly one item (and we do not need to
|
|
* allocate an array).
|
|
*/
|
|
var forEachAccumulated = function (arr, cb, scope) {
|
|
if (Array.isArray(arr)) {
|
|
arr.forEach(cb, scope);
|
|
} else if (arr) {
|
|
cb.call(scope, arr);
|
|
}
|
|
};
|
|
|
|
module.exports = forEachAccumulated;
|
|
},{}],246:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule getEventCharCode
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* `charCode` represents the actual "character code" and is safe to use with
|
|
* `String.fromCharCode`. As such, only keys that correspond to printable
|
|
* characters produce a valid `charCode`, the only exception to this is Enter.
|
|
* The Tab-key is considered non-printable and does not have a `charCode`,
|
|
* presumably because it does not produce a tab-character in browsers.
|
|
*
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {number} Normalized `charCode` property.
|
|
*/
|
|
function getEventCharCode(nativeEvent) {
|
|
var charCode;
|
|
var keyCode = nativeEvent.keyCode;
|
|
|
|
if ('charCode' in nativeEvent) {
|
|
charCode = nativeEvent.charCode;
|
|
|
|
// FF does not set `charCode` for the Enter-key, check against `keyCode`.
|
|
if (charCode === 0 && keyCode === 13) {
|
|
charCode = 13;
|
|
}
|
|
} else {
|
|
// IE8 does not implement `charCode`, but `keyCode` has the correct value.
|
|
charCode = keyCode;
|
|
}
|
|
|
|
// Some non-printable keys are reported in `charCode`/`keyCode`, discard them.
|
|
// Must not discard the (non-)printable Enter-key.
|
|
if (charCode >= 32 || charCode === 13) {
|
|
return charCode;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
module.exports = getEventCharCode;
|
|
},{}],247:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule getEventKey
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var getEventCharCode = require('./getEventCharCode');
|
|
|
|
/**
|
|
* Normalization of deprecated HTML5 `key` values
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
|
|
*/
|
|
var normalizeKey = {
|
|
'Esc': 'Escape',
|
|
'Spacebar': ' ',
|
|
'Left': 'ArrowLeft',
|
|
'Up': 'ArrowUp',
|
|
'Right': 'ArrowRight',
|
|
'Down': 'ArrowDown',
|
|
'Del': 'Delete',
|
|
'Win': 'OS',
|
|
'Menu': 'ContextMenu',
|
|
'Apps': 'ContextMenu',
|
|
'Scroll': 'ScrollLock',
|
|
'MozPrintableKey': 'Unidentified'
|
|
};
|
|
|
|
/**
|
|
* Translation from legacy `keyCode` to HTML5 `key`
|
|
* Only special keys supported, all others depend on keyboard layout or browser
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
|
|
*/
|
|
var translateToKey = {
|
|
8: 'Backspace',
|
|
9: 'Tab',
|
|
12: 'Clear',
|
|
13: 'Enter',
|
|
16: 'Shift',
|
|
17: 'Control',
|
|
18: 'Alt',
|
|
19: 'Pause',
|
|
20: 'CapsLock',
|
|
27: 'Escape',
|
|
32: ' ',
|
|
33: 'PageUp',
|
|
34: 'PageDown',
|
|
35: 'End',
|
|
36: 'Home',
|
|
37: 'ArrowLeft',
|
|
38: 'ArrowUp',
|
|
39: 'ArrowRight',
|
|
40: 'ArrowDown',
|
|
45: 'Insert',
|
|
46: 'Delete',
|
|
112: 'F1', 113: 'F2', 114: 'F3', 115: 'F4', 116: 'F5', 117: 'F6',
|
|
118: 'F7', 119: 'F8', 120: 'F9', 121: 'F10', 122: 'F11', 123: 'F12',
|
|
144: 'NumLock',
|
|
145: 'ScrollLock',
|
|
224: 'Meta'
|
|
};
|
|
|
|
/**
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {string} Normalized `key` property.
|
|
*/
|
|
function getEventKey(nativeEvent) {
|
|
if (nativeEvent.key) {
|
|
// Normalize inconsistent values reported by browsers due to
|
|
// implementations of a working draft specification.
|
|
|
|
// FireFox implements `key` but returns `MozPrintableKey` for all
|
|
// printable characters (normalized to `Unidentified`), ignore it.
|
|
var key = normalizeKey[nativeEvent.key] || nativeEvent.key;
|
|
if (key !== 'Unidentified') {
|
|
return key;
|
|
}
|
|
}
|
|
|
|
// Browser does not implement `key`, polyfill as much of it as we can.
|
|
if (nativeEvent.type === 'keypress') {
|
|
var charCode = getEventCharCode(nativeEvent);
|
|
|
|
// The enter-key is technically both printable and non-printable and can
|
|
// thus be captured by `keypress`, no other non-printable key should.
|
|
return charCode === 13 ? 'Enter' : String.fromCharCode(charCode);
|
|
}
|
|
if (nativeEvent.type === 'keydown' || nativeEvent.type === 'keyup') {
|
|
// While user keyboard layout determines the actual meaning of each
|
|
// `keyCode` value, almost all function keys have a universal value.
|
|
return translateToKey[nativeEvent.keyCode] || 'Unidentified';
|
|
}
|
|
return '';
|
|
}
|
|
|
|
module.exports = getEventKey;
|
|
},{"./getEventCharCode":246}],248:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule getEventModifierState
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Translation from modifier key to the associated property in the event.
|
|
* @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
|
|
*/
|
|
|
|
var modifierKeyToProp = {
|
|
'Alt': 'altKey',
|
|
'Control': 'ctrlKey',
|
|
'Meta': 'metaKey',
|
|
'Shift': 'shiftKey'
|
|
};
|
|
|
|
// IE8 does not implement getModifierState so we simply map it to the only
|
|
// modifier keys exposed by the event itself, does not support Lock-keys.
|
|
// Currently, all major browsers except Chrome seems to support Lock-keys.
|
|
function modifierStateGetter(keyArg) {
|
|
var syntheticEvent = this;
|
|
var nativeEvent = syntheticEvent.nativeEvent;
|
|
if (nativeEvent.getModifierState) {
|
|
return nativeEvent.getModifierState(keyArg);
|
|
}
|
|
var keyProp = modifierKeyToProp[keyArg];
|
|
return keyProp ? !!nativeEvent[keyProp] : false;
|
|
}
|
|
|
|
function getEventModifierState(nativeEvent) {
|
|
return modifierStateGetter;
|
|
}
|
|
|
|
module.exports = getEventModifierState;
|
|
},{}],249:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule getEventTarget
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Gets the target node from a native browser event by accounting for
|
|
* inconsistencies in browser DOM APIs.
|
|
*
|
|
* @param {object} nativeEvent Native browser event.
|
|
* @return {DOMEventTarget} Target node.
|
|
*/
|
|
function getEventTarget(nativeEvent) {
|
|
var target = nativeEvent.target || nativeEvent.srcElement || window;
|
|
// Safari may fire events on text nodes (Node.TEXT_NODE is 3).
|
|
// @see http://www.quirksmode.org/js/events_properties.html
|
|
return target.nodeType === 3 ? target.parentNode : target;
|
|
}
|
|
|
|
module.exports = getEventTarget;
|
|
},{}],250:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule getIteratorFn
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/* global Symbol */
|
|
var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
|
|
var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
|
|
|
|
/**
|
|
* Returns the iterator method function contained on the iterable object.
|
|
*
|
|
* Be sure to invoke the function with the iterable as context:
|
|
*
|
|
* var iteratorFn = getIteratorFn(myIterable);
|
|
* if (iteratorFn) {
|
|
* var iterator = iteratorFn.call(myIterable);
|
|
* ...
|
|
* }
|
|
*
|
|
* @param {?object} maybeIterable
|
|
* @return {?function}
|
|
*/
|
|
function getIteratorFn(maybeIterable) {
|
|
var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]);
|
|
if (typeof iteratorFn === 'function') {
|
|
return iteratorFn;
|
|
}
|
|
}
|
|
|
|
module.exports = getIteratorFn;
|
|
},{}],251:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule getNodeForCharacterOffset
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Given any node return the first leaf node without children.
|
|
*
|
|
* @param {DOMElement|DOMTextNode} node
|
|
* @return {DOMElement|DOMTextNode}
|
|
*/
|
|
function getLeafNode(node) {
|
|
while (node && node.firstChild) {
|
|
node = node.firstChild;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Get the next sibling within a container. This will walk up the
|
|
* DOM if a node's siblings have been exhausted.
|
|
*
|
|
* @param {DOMElement|DOMTextNode} node
|
|
* @return {?DOMElement|DOMTextNode}
|
|
*/
|
|
function getSiblingNode(node) {
|
|
while (node) {
|
|
if (node.nextSibling) {
|
|
return node.nextSibling;
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get object describing the nodes which contain characters at offset.
|
|
*
|
|
* @param {DOMElement|DOMTextNode} root
|
|
* @param {number} offset
|
|
* @return {?object}
|
|
*/
|
|
function getNodeForCharacterOffset(root, offset) {
|
|
var node = getLeafNode(root);
|
|
var nodeStart = 0;
|
|
var nodeEnd = 0;
|
|
|
|
while (node) {
|
|
if (node.nodeType === 3) {
|
|
nodeEnd = nodeStart + node.textContent.length;
|
|
|
|
if (nodeStart <= offset && nodeEnd >= offset) {
|
|
return {
|
|
node: node,
|
|
offset: offset - nodeStart
|
|
};
|
|
}
|
|
|
|
nodeStart = nodeEnd;
|
|
}
|
|
|
|
node = getLeafNode(getSiblingNode(node));
|
|
}
|
|
}
|
|
|
|
module.exports = getNodeForCharacterOffset;
|
|
},{}],252:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule getTextContentAccessor
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
|
|
var contentKey = null;
|
|
|
|
/**
|
|
* Gets the key used to access text content on a DOM node.
|
|
*
|
|
* @return {?string} Key used to access text content.
|
|
* @internal
|
|
*/
|
|
function getTextContentAccessor() {
|
|
if (!contentKey && ExecutionEnvironment.canUseDOM) {
|
|
// Prefer textContent to innerText because many browsers support both but
|
|
// SVG <text> elements don't support innerText even when <div> does.
|
|
contentKey = 'textContent' in document.documentElement ? 'textContent' : 'innerText';
|
|
}
|
|
return contentKey;
|
|
}
|
|
|
|
module.exports = getTextContentAccessor;
|
|
},{"fbjs/lib/ExecutionEnvironment":58}],253:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule instantiateReactComponent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactCompositeComponent = require('./ReactCompositeComponent');
|
|
var ReactEmptyComponent = require('./ReactEmptyComponent');
|
|
var ReactNativeComponent = require('./ReactNativeComponent');
|
|
|
|
var assign = require('./Object.assign');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
// To avoid a cyclic dependency, we create the final class in this module
|
|
var ReactCompositeComponentWrapper = function () {};
|
|
assign(ReactCompositeComponentWrapper.prototype, ReactCompositeComponent.Mixin, {
|
|
_instantiateReactComponent: instantiateReactComponent
|
|
});
|
|
|
|
function getDeclarationErrorAddendum(owner) {
|
|
if (owner) {
|
|
var name = owner.getName();
|
|
if (name) {
|
|
return ' Check the render method of `' + name + '`.';
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Check if the type reference is a known internal type. I.e. not a user
|
|
* provided composite type.
|
|
*
|
|
* @param {function} type
|
|
* @return {boolean} Returns true if this is a valid internal type.
|
|
*/
|
|
function isInternalComponentType(type) {
|
|
return typeof type === 'function' && typeof type.prototype !== 'undefined' && typeof type.prototype.mountComponent === 'function' && typeof type.prototype.receiveComponent === 'function';
|
|
}
|
|
|
|
/**
|
|
* Given a ReactNode, create an instance that will actually be mounted.
|
|
*
|
|
* @param {ReactNode} node
|
|
* @return {object} A new instance of the element's constructor.
|
|
* @protected
|
|
*/
|
|
function instantiateReactComponent(node) {
|
|
var instance;
|
|
|
|
if (node === null || node === false) {
|
|
instance = new ReactEmptyComponent(instantiateReactComponent);
|
|
} else if (typeof node === 'object') {
|
|
var element = node;
|
|
!(element && (typeof element.type === 'function' || typeof element.type === 'string')) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Element type is invalid: expected a string (for built-in components) ' + 'or a class/function (for composite components) but got: %s.%s', element.type == null ? element.type : typeof element.type, getDeclarationErrorAddendum(element._owner)) : invariant(false) : undefined;
|
|
|
|
// Special case string values
|
|
if (typeof element.type === 'string') {
|
|
instance = ReactNativeComponent.createInternalComponent(element);
|
|
} else if (isInternalComponentType(element.type)) {
|
|
// This is temporarily available for custom components that are not string
|
|
// representations. I.e. ART. Once those are updated to use the string
|
|
// representation, we can drop this code path.
|
|
instance = new element.type(element);
|
|
} else {
|
|
instance = new ReactCompositeComponentWrapper();
|
|
}
|
|
} else if (typeof node === 'string' || typeof node === 'number') {
|
|
instance = ReactNativeComponent.createInstanceForText(node);
|
|
} else {
|
|
!false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Encountered invalid React node of type %s', typeof node) : invariant(false) : undefined;
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(typeof instance.construct === 'function' && typeof instance.mountComponent === 'function' && typeof instance.receiveComponent === 'function' && typeof instance.unmountComponent === 'function', 'Only React Components can be mounted.') : undefined;
|
|
}
|
|
|
|
// Sets up the instance. This can probably just move into the constructor now.
|
|
instance.construct(node);
|
|
|
|
// These two fields are used by the DOM and ART diffing algorithms
|
|
// respectively. Instead of using expandos on components, we should be
|
|
// storing the state needed by the diffing algorithms elsewhere.
|
|
instance._mountIndex = 0;
|
|
instance._mountImage = null;
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
instance._isOwnerNecessary = false;
|
|
instance._warnedAboutRefsInRender = false;
|
|
}
|
|
|
|
// Internal instances should fully constructed at this point, so they should
|
|
// not get any new fields added to them at this point.
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
if (Object.preventExtensions) {
|
|
Object.preventExtensions(instance);
|
|
}
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
|
|
module.exports = instantiateReactComponent;
|
|
}).call(this,require('_process'))
|
|
},{"./Object.assign":157,"./ReactCompositeComponent":168,"./ReactEmptyComponent":189,"./ReactNativeComponent":203,"_process":133,"fbjs/lib/invariant":72,"fbjs/lib/warning":83}],254:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule isEventSupported
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
|
|
var useHasFeature;
|
|
if (ExecutionEnvironment.canUseDOM) {
|
|
useHasFeature = document.implementation && document.implementation.hasFeature &&
|
|
// always returns true in newer browsers as per the standard.
|
|
// @see http://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
|
|
document.implementation.hasFeature('', '') !== true;
|
|
}
|
|
|
|
/**
|
|
* Checks if an event is supported in the current execution environment.
|
|
*
|
|
* NOTE: This will not work correctly for non-generic events such as `change`,
|
|
* `reset`, `load`, `error`, and `select`.
|
|
*
|
|
* Borrows from Modernizr.
|
|
*
|
|
* @param {string} eventNameSuffix Event name, e.g. "click".
|
|
* @param {?boolean} capture Check if the capture phase is supported.
|
|
* @return {boolean} True if the event is supported.
|
|
* @internal
|
|
* @license Modernizr 3.0.0pre (Custom Build) | MIT
|
|
*/
|
|
function isEventSupported(eventNameSuffix, capture) {
|
|
if (!ExecutionEnvironment.canUseDOM || capture && !('addEventListener' in document)) {
|
|
return false;
|
|
}
|
|
|
|
var eventName = 'on' + eventNameSuffix;
|
|
var isSupported = (eventName in document);
|
|
|
|
if (!isSupported) {
|
|
var element = document.createElement('div');
|
|
element.setAttribute(eventName, 'return;');
|
|
isSupported = typeof element[eventName] === 'function';
|
|
}
|
|
|
|
if (!isSupported && useHasFeature && eventNameSuffix === 'wheel') {
|
|
// This is the only way to test support for the `wheel` event in IE9+.
|
|
isSupported = document.implementation.hasFeature('Events.wheel', '3.0');
|
|
}
|
|
|
|
return isSupported;
|
|
}
|
|
|
|
module.exports = isEventSupported;
|
|
},{"fbjs/lib/ExecutionEnvironment":58}],255:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule isTextInputElement
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
|
|
*/
|
|
var supportedInputTypes = {
|
|
'color': true,
|
|
'date': true,
|
|
'datetime': true,
|
|
'datetime-local': true,
|
|
'email': true,
|
|
'month': true,
|
|
'number': true,
|
|
'password': true,
|
|
'range': true,
|
|
'search': true,
|
|
'tel': true,
|
|
'text': true,
|
|
'time': true,
|
|
'url': true,
|
|
'week': true
|
|
};
|
|
|
|
function isTextInputElement(elem) {
|
|
var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
|
|
return nodeName && (nodeName === 'input' && supportedInputTypes[elem.type] || nodeName === 'textarea');
|
|
}
|
|
|
|
module.exports = isTextInputElement;
|
|
},{}],256:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule onlyChild
|
|
*/
|
|
'use strict';
|
|
|
|
var ReactElement = require('./ReactElement');
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
|
|
/**
|
|
* Returns the first child in a collection of children and verifies that there
|
|
* is only one child in the collection. The current implementation of this
|
|
* function assumes that a single child gets passed without a wrapper, but the
|
|
* purpose of this helper function is to abstract away the particular structure
|
|
* of children.
|
|
*
|
|
* @param {?object} children Child collection structure.
|
|
* @return {ReactComponent} The first and only `ReactComponent` contained in the
|
|
* structure.
|
|
*/
|
|
function onlyChild(children) {
|
|
!ReactElement.isValidElement(children) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'onlyChild must be passed a children with exactly one child.') : invariant(false) : undefined;
|
|
return children;
|
|
}
|
|
|
|
module.exports = onlyChild;
|
|
}).call(this,require('_process'))
|
|
},{"./ReactElement":187,"_process":133,"fbjs/lib/invariant":72}],257:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule quoteAttributeValueForBrowser
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var escapeTextContentForBrowser = require('./escapeTextContentForBrowser');
|
|
|
|
/**
|
|
* Escapes attribute value to prevent scripting attacks.
|
|
*
|
|
* @param {*} value Value to escape.
|
|
* @return {string} An escaped string.
|
|
*/
|
|
function quoteAttributeValueForBrowser(value) {
|
|
return '"' + escapeTextContentForBrowser(value) + '"';
|
|
}
|
|
|
|
module.exports = quoteAttributeValueForBrowser;
|
|
},{"./escapeTextContentForBrowser":242}],258:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule renderSubtreeIntoContainer
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactMount = require('./ReactMount');
|
|
|
|
module.exports = ReactMount.renderSubtreeIntoContainer;
|
|
},{"./ReactMount":200}],259:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule setInnerHTML
|
|
*/
|
|
|
|
/* globals MSApp */
|
|
|
|
'use strict';
|
|
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
|
|
var WHITESPACE_TEST = /^[ \r\n\t\f]/;
|
|
var NONVISIBLE_TEST = /<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/;
|
|
|
|
/**
|
|
* Set the innerHTML property of a node, ensuring that whitespace is preserved
|
|
* even in IE8.
|
|
*
|
|
* @param {DOMElement} node
|
|
* @param {string} html
|
|
* @internal
|
|
*/
|
|
var setInnerHTML = function (node, html) {
|
|
node.innerHTML = html;
|
|
};
|
|
|
|
// Win8 apps: Allow all html to be inserted
|
|
if (typeof MSApp !== 'undefined' && MSApp.execUnsafeLocalFunction) {
|
|
setInnerHTML = function (node, html) {
|
|
MSApp.execUnsafeLocalFunction(function () {
|
|
node.innerHTML = html;
|
|
});
|
|
};
|
|
}
|
|
|
|
if (ExecutionEnvironment.canUseDOM) {
|
|
// IE8: When updating a just created node with innerHTML only leading
|
|
// whitespace is removed. When updating an existing node with innerHTML
|
|
// whitespace in root TextNodes is also collapsed.
|
|
// @see quirksmode.org/bugreports/archives/2004/11/innerhtml_and_t.html
|
|
|
|
// Feature detection; only IE8 is known to behave improperly like this.
|
|
var testElement = document.createElement('div');
|
|
testElement.innerHTML = ' ';
|
|
if (testElement.innerHTML === '') {
|
|
setInnerHTML = function (node, html) {
|
|
// Magic theory: IE8 supposedly differentiates between added and updated
|
|
// nodes when processing innerHTML, innerHTML on updated nodes suffers
|
|
// from worse whitespace behavior. Re-adding a node like this triggers
|
|
// the initial and more favorable whitespace behavior.
|
|
// TODO: What to do on a detached node?
|
|
if (node.parentNode) {
|
|
node.parentNode.replaceChild(node, node);
|
|
}
|
|
|
|
// We also implement a workaround for non-visible tags disappearing into
|
|
// thin air on IE8, this only happens if there is no visible text
|
|
// in-front of the non-visible tags. Piggyback on the whitespace fix
|
|
// and simply check if any non-visible tags appear in the source.
|
|
if (WHITESPACE_TEST.test(html) || html[0] === '<' && NONVISIBLE_TEST.test(html)) {
|
|
// Recover leading whitespace by temporarily prepending any character.
|
|
// \uFEFF has the potential advantage of being zero-width/invisible.
|
|
// UglifyJS drops U+FEFF chars when parsing, so use String.fromCharCode
|
|
// in hopes that this is preserved even if "\uFEFF" is transformed to
|
|
// the actual Unicode character (by Babel, for example).
|
|
// https://github.com/mishoo/UglifyJS2/blob/v2.4.20/lib/parse.js#L216
|
|
node.innerHTML = String.fromCharCode(0xFEFF) + html;
|
|
|
|
// deleteData leaves an empty `TextNode` which offsets the index of all
|
|
// children. Definitely want to avoid this.
|
|
var textNode = node.firstChild;
|
|
if (textNode.data.length === 1) {
|
|
node.removeChild(textNode);
|
|
} else {
|
|
textNode.deleteData(0, 1);
|
|
}
|
|
} else {
|
|
node.innerHTML = html;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = setInnerHTML;
|
|
},{"fbjs/lib/ExecutionEnvironment":58}],260:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule setTextContent
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
|
var escapeTextContentForBrowser = require('./escapeTextContentForBrowser');
|
|
var setInnerHTML = require('./setInnerHTML');
|
|
|
|
/**
|
|
* Set the textContent property of a node, ensuring that whitespace is preserved
|
|
* even in IE8. innerText is a poor substitute for textContent and, among many
|
|
* issues, inserts <br> instead of the literal newline chars. innerHTML behaves
|
|
* as it should.
|
|
*
|
|
* @param {DOMElement} node
|
|
* @param {string} text
|
|
* @internal
|
|
*/
|
|
var setTextContent = function (node, text) {
|
|
node.textContent = text;
|
|
};
|
|
|
|
if (ExecutionEnvironment.canUseDOM) {
|
|
if (!('textContent' in document.documentElement)) {
|
|
setTextContent = function (node, text) {
|
|
setInnerHTML(node, escapeTextContentForBrowser(text));
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = setTextContent;
|
|
},{"./escapeTextContentForBrowser":242,"./setInnerHTML":259,"fbjs/lib/ExecutionEnvironment":58}],261:[function(require,module,exports){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule shouldUpdateReactComponent
|
|
* @typechecks static-only
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Given a `prevElement` and `nextElement`, determines if the existing
|
|
* instance should be updated as opposed to being destroyed or replaced by a new
|
|
* instance. Both arguments are elements. This ensures that this logic can
|
|
* operate on stateless trees without any backing instance.
|
|
*
|
|
* @param {?object} prevElement
|
|
* @param {?object} nextElement
|
|
* @return {boolean} True if the existing instance should be updated.
|
|
* @protected
|
|
*/
|
|
function shouldUpdateReactComponent(prevElement, nextElement) {
|
|
var prevEmpty = prevElement === null || prevElement === false;
|
|
var nextEmpty = nextElement === null || nextElement === false;
|
|
if (prevEmpty || nextEmpty) {
|
|
return prevEmpty === nextEmpty;
|
|
}
|
|
|
|
var prevType = typeof prevElement;
|
|
var nextType = typeof nextElement;
|
|
if (prevType === 'string' || prevType === 'number') {
|
|
return nextType === 'string' || nextType === 'number';
|
|
} else {
|
|
return nextType === 'object' && prevElement.type === nextElement.type && prevElement.key === nextElement.key;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
module.exports = shouldUpdateReactComponent;
|
|
},{}],262:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule traverseAllChildren
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactCurrentOwner = require('./ReactCurrentOwner');
|
|
var ReactElement = require('./ReactElement');
|
|
var ReactInstanceHandles = require('./ReactInstanceHandles');
|
|
|
|
var getIteratorFn = require('./getIteratorFn');
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
var SEPARATOR = ReactInstanceHandles.SEPARATOR;
|
|
var SUBSEPARATOR = ':';
|
|
|
|
/**
|
|
* TODO: Test that a single child and an array with one item have the same key
|
|
* pattern.
|
|
*/
|
|
|
|
var userProvidedKeyEscaperLookup = {
|
|
'=': '=0',
|
|
'.': '=1',
|
|
':': '=2'
|
|
};
|
|
|
|
var userProvidedKeyEscapeRegex = /[=.:]/g;
|
|
|
|
var didWarnAboutMaps = false;
|
|
|
|
function userProvidedKeyEscaper(match) {
|
|
return userProvidedKeyEscaperLookup[match];
|
|
}
|
|
|
|
/**
|
|
* Generate a key string that identifies a component within a set.
|
|
*
|
|
* @param {*} component A component that could contain a manual key.
|
|
* @param {number} index Index that is used if a manual key is not provided.
|
|
* @return {string}
|
|
*/
|
|
function getComponentKey(component, index) {
|
|
if (component && component.key != null) {
|
|
// Explicit key
|
|
return wrapUserProvidedKey(component.key);
|
|
}
|
|
// Implicit key determined by the index in the set
|
|
return index.toString(36);
|
|
}
|
|
|
|
/**
|
|
* Escape a component key so that it is safe to use in a reactid.
|
|
*
|
|
* @param {*} text Component key to be escaped.
|
|
* @return {string} An escaped string.
|
|
*/
|
|
function escapeUserProvidedKey(text) {
|
|
return ('' + text).replace(userProvidedKeyEscapeRegex, userProvidedKeyEscaper);
|
|
}
|
|
|
|
/**
|
|
* Wrap a `key` value explicitly provided by the user to distinguish it from
|
|
* implicitly-generated keys generated by a component's index in its parent.
|
|
*
|
|
* @param {string} key Value of a user-provided `key` attribute
|
|
* @return {string}
|
|
*/
|
|
function wrapUserProvidedKey(key) {
|
|
return '$' + escapeUserProvidedKey(key);
|
|
}
|
|
|
|
/**
|
|
* @param {?*} children Children tree container.
|
|
* @param {!string} nameSoFar Name of the key path so far.
|
|
* @param {!function} callback Callback to invoke with each child found.
|
|
* @param {?*} traverseContext Used to pass information throughout the traversal
|
|
* process.
|
|
* @return {!number} The number of children in this subtree.
|
|
*/
|
|
function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext) {
|
|
var type = typeof children;
|
|
|
|
if (type === 'undefined' || type === 'boolean') {
|
|
// All of the above are perceived as null.
|
|
children = null;
|
|
}
|
|
|
|
if (children === null || type === 'string' || type === 'number' || ReactElement.isValidElement(children)) {
|
|
callback(traverseContext, children,
|
|
// If it's the only child, treat the name as if it was wrapped in an array
|
|
// so that it's consistent if the number of children grows.
|
|
nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar);
|
|
return 1;
|
|
}
|
|
|
|
var child;
|
|
var nextName;
|
|
var subtreeCount = 0; // Count of children found in the current subtree.
|
|
var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
|
|
|
|
if (Array.isArray(children)) {
|
|
for (var i = 0; i < children.length; i++) {
|
|
child = children[i];
|
|
nextName = nextNamePrefix + getComponentKey(child, i);
|
|
subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
|
|
}
|
|
} else {
|
|
var iteratorFn = getIteratorFn(children);
|
|
if (iteratorFn) {
|
|
var iterator = iteratorFn.call(children);
|
|
var step;
|
|
if (iteratorFn !== children.entries) {
|
|
var ii = 0;
|
|
while (!(step = iterator.next()).done) {
|
|
child = step.value;
|
|
nextName = nextNamePrefix + getComponentKey(child, ii++);
|
|
subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
|
|
}
|
|
} else {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(didWarnAboutMaps, 'Using Maps as children is not yet fully supported. It is an ' + 'experimental feature that might be removed. Convert it to a ' + 'sequence / iterable of keyed ReactElements instead.') : undefined;
|
|
didWarnAboutMaps = true;
|
|
}
|
|
// Iterator will provide entry [k,v] tuples rather than values.
|
|
while (!(step = iterator.next()).done) {
|
|
var entry = step.value;
|
|
if (entry) {
|
|
child = entry[1];
|
|
nextName = nextNamePrefix + wrapUserProvidedKey(entry[0]) + SUBSEPARATOR + getComponentKey(child, 0);
|
|
subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
|
|
}
|
|
}
|
|
}
|
|
} else if (type === 'object') {
|
|
var addendum = '';
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
addendum = ' If you meant to render a collection of children, use an array ' + 'instead or wrap the object using createFragment(object) from the ' + 'React add-ons.';
|
|
if (children._isReactElement) {
|
|
addendum = ' It looks like you\'re using an element created by a different ' + 'version of React. Make sure to use only one copy of React.';
|
|
}
|
|
if (ReactCurrentOwner.current) {
|
|
var name = ReactCurrentOwner.current.getName();
|
|
if (name) {
|
|
addendum += ' Check the render method of `' + name + '`.';
|
|
}
|
|
}
|
|
}
|
|
var childrenString = String(children);
|
|
!false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Objects are not valid as a React child (found: %s).%s', childrenString === '[object Object]' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString, addendum) : invariant(false) : undefined;
|
|
}
|
|
}
|
|
|
|
return subtreeCount;
|
|
}
|
|
|
|
/**
|
|
* Traverses children that are typically specified as `props.children`, but
|
|
* might also be specified through attributes:
|
|
*
|
|
* - `traverseAllChildren(this.props.children, ...)`
|
|
* - `traverseAllChildren(this.props.leftPanelChildren, ...)`
|
|
*
|
|
* The `traverseContext` is an optional argument that is passed through the
|
|
* entire traversal. It can be used to store accumulations or anything else that
|
|
* the callback might find relevant.
|
|
*
|
|
* @param {?*} children Children tree object.
|
|
* @param {!function} callback To invoke upon traversing each child.
|
|
* @param {?*} traverseContext Context for traversal.
|
|
* @return {!number} The number of children in this subtree.
|
|
*/
|
|
function traverseAllChildren(children, callback, traverseContext) {
|
|
if (children == null) {
|
|
return 0;
|
|
}
|
|
|
|
return traverseAllChildrenImpl(children, '', callback, traverseContext);
|
|
}
|
|
|
|
module.exports = traverseAllChildren;
|
|
}).call(this,require('_process'))
|
|
},{"./ReactCurrentOwner":169,"./ReactElement":187,"./ReactInstanceHandles":196,"./getIteratorFn":250,"_process":133,"fbjs/lib/invariant":72,"fbjs/lib/warning":83}],263:[function(require,module,exports){
|
|
(function (process){
|
|
/**
|
|
* Copyright 2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule validateDOMNesting
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var assign = require('./Object.assign');
|
|
var emptyFunction = require('fbjs/lib/emptyFunction');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
var validateDOMNesting = emptyFunction;
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// This validation code was written based on the HTML5 parsing spec:
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
|
|
//
|
|
// Note: this does not catch all invalid nesting, nor does it try to (as it's
|
|
// not clear what practical benefit doing so provides); instead, we warn only
|
|
// for cases where the parser will give a parse tree differing from what React
|
|
// intended. For example, <b><div></div></b> is invalid but we don't warn
|
|
// because it still parses correctly; we do warn for other cases like nested
|
|
// <p> tags where the beginning of the second element implicitly closes the
|
|
// first, causing a confusing mess.
|
|
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#special
|
|
var specialTags = ['address', 'applet', 'area', 'article', 'aside', 'base', 'basefont', 'bgsound', 'blockquote', 'body', 'br', 'button', 'caption', 'center', 'col', 'colgroup', 'dd', 'details', 'dir', 'div', 'dl', 'dt', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'iframe', 'img', 'input', 'isindex', 'li', 'link', 'listing', 'main', 'marquee', 'menu', 'menuitem', 'meta', 'nav', 'noembed', 'noframes', 'noscript', 'object', 'ol', 'p', 'param', 'plaintext', 'pre', 'script', 'section', 'select', 'source', 'style', 'summary', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'title', 'tr', 'track', 'ul', 'wbr', 'xmp'];
|
|
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
|
|
var inScopeTags = ['applet', 'caption', 'html', 'table', 'td', 'th', 'marquee', 'object', 'template',
|
|
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#html-integration-point
|
|
// TODO: Distinguish by namespace here -- for <title>, including it here
|
|
// errs on the side of fewer warnings
|
|
'foreignObject', 'desc', 'title'];
|
|
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-button-scope
|
|
var buttonScopeTags = inScopeTags.concat(['button']);
|
|
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#generate-implied-end-tags
|
|
var impliedEndTags = ['dd', 'dt', 'li', 'option', 'optgroup', 'p', 'rp', 'rt'];
|
|
|
|
var emptyAncestorInfo = {
|
|
parentTag: null,
|
|
|
|
formTag: null,
|
|
aTagInScope: null,
|
|
buttonTagInScope: null,
|
|
nobrTagInScope: null,
|
|
pTagInButtonScope: null,
|
|
|
|
listItemTagAutoclosing: null,
|
|
dlItemTagAutoclosing: null
|
|
};
|
|
|
|
var updatedAncestorInfo = function (oldInfo, tag, instance) {
|
|
var ancestorInfo = assign({}, oldInfo || emptyAncestorInfo);
|
|
var info = { tag: tag, instance: instance };
|
|
|
|
if (inScopeTags.indexOf(tag) !== -1) {
|
|
ancestorInfo.aTagInScope = null;
|
|
ancestorInfo.buttonTagInScope = null;
|
|
ancestorInfo.nobrTagInScope = null;
|
|
}
|
|
if (buttonScopeTags.indexOf(tag) !== -1) {
|
|
ancestorInfo.pTagInButtonScope = null;
|
|
}
|
|
|
|
// See rules for 'li', 'dd', 'dt' start tags in
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
|
|
if (specialTags.indexOf(tag) !== -1 && tag !== 'address' && tag !== 'div' && tag !== 'p') {
|
|
ancestorInfo.listItemTagAutoclosing = null;
|
|
ancestorInfo.dlItemTagAutoclosing = null;
|
|
}
|
|
|
|
ancestorInfo.parentTag = info;
|
|
|
|
if (tag === 'form') {
|
|
ancestorInfo.formTag = info;
|
|
}
|
|
if (tag === 'a') {
|
|
ancestorInfo.aTagInScope = info;
|
|
}
|
|
if (tag === 'button') {
|
|
ancestorInfo.buttonTagInScope = info;
|
|
}
|
|
if (tag === 'nobr') {
|
|
ancestorInfo.nobrTagInScope = info;
|
|
}
|
|
if (tag === 'p') {
|
|
ancestorInfo.pTagInButtonScope = info;
|
|
}
|
|
if (tag === 'li') {
|
|
ancestorInfo.listItemTagAutoclosing = info;
|
|
}
|
|
if (tag === 'dd' || tag === 'dt') {
|
|
ancestorInfo.dlItemTagAutoclosing = info;
|
|
}
|
|
|
|
return ancestorInfo;
|
|
};
|
|
|
|
/**
|
|
* Returns whether
|
|
*/
|
|
var isTagValidWithParent = function (tag, parentTag) {
|
|
// First, let's check if we're in an unusual parsing mode...
|
|
switch (parentTag) {
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inselect
|
|
case 'select':
|
|
return tag === 'option' || tag === 'optgroup' || tag === '#text';
|
|
case 'optgroup':
|
|
return tag === 'option' || tag === '#text';
|
|
// Strictly speaking, seeing an <option> doesn't mean we're in a <select>
|
|
// but
|
|
case 'option':
|
|
return tag === '#text';
|
|
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intd
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incaption
|
|
// No special behavior since these rules fall back to "in body" mode for
|
|
// all except special table nodes which cause bad parsing behavior anyway.
|
|
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intr
|
|
case 'tr':
|
|
return tag === 'th' || tag === 'td' || tag === 'style' || tag === 'script' || tag === 'template';
|
|
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intbody
|
|
case 'tbody':
|
|
case 'thead':
|
|
case 'tfoot':
|
|
return tag === 'tr' || tag === 'style' || tag === 'script' || tag === 'template';
|
|
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incolgroup
|
|
case 'colgroup':
|
|
return tag === 'col' || tag === 'template';
|
|
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intable
|
|
case 'table':
|
|
return tag === 'caption' || tag === 'colgroup' || tag === 'tbody' || tag === 'tfoot' || tag === 'thead' || tag === 'style' || tag === 'script' || tag === 'template';
|
|
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inhead
|
|
case 'head':
|
|
return tag === 'base' || tag === 'basefont' || tag === 'bgsound' || tag === 'link' || tag === 'meta' || tag === 'title' || tag === 'noscript' || tag === 'noframes' || tag === 'style' || tag === 'script' || tag === 'template';
|
|
|
|
// https://html.spec.whatwg.org/multipage/semantics.html#the-html-element
|
|
case 'html':
|
|
return tag === 'head' || tag === 'body';
|
|
}
|
|
|
|
// Probably in the "in body" parsing mode, so we outlaw only tag combos
|
|
// where the parsing rules cause implicit opens or closes to be added.
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
|
|
switch (tag) {
|
|
case 'h1':
|
|
case 'h2':
|
|
case 'h3':
|
|
case 'h4':
|
|
case 'h5':
|
|
case 'h6':
|
|
return parentTag !== 'h1' && parentTag !== 'h2' && parentTag !== 'h3' && parentTag !== 'h4' && parentTag !== 'h5' && parentTag !== 'h6';
|
|
|
|
case 'rp':
|
|
case 'rt':
|
|
return impliedEndTags.indexOf(parentTag) === -1;
|
|
|
|
case 'caption':
|
|
case 'col':
|
|
case 'colgroup':
|
|
case 'frame':
|
|
case 'head':
|
|
case 'tbody':
|
|
case 'td':
|
|
case 'tfoot':
|
|
case 'th':
|
|
case 'thead':
|
|
case 'tr':
|
|
// These tags are only valid with a few parents that have special child
|
|
// parsing rules -- if we're down here, then none of those matched and
|
|
// so we allow it only if we don't know what the parent is, as all other
|
|
// cases are invalid.
|
|
return parentTag == null;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Returns whether
|
|
*/
|
|
var findInvalidAncestorForTag = function (tag, ancestorInfo) {
|
|
switch (tag) {
|
|
case 'address':
|
|
case 'article':
|
|
case 'aside':
|
|
case 'blockquote':
|
|
case 'center':
|
|
case 'details':
|
|
case 'dialog':
|
|
case 'dir':
|
|
case 'div':
|
|
case 'dl':
|
|
case 'fieldset':
|
|
case 'figcaption':
|
|
case 'figure':
|
|
case 'footer':
|
|
case 'header':
|
|
case 'hgroup':
|
|
case 'main':
|
|
case 'menu':
|
|
case 'nav':
|
|
case 'ol':
|
|
case 'p':
|
|
case 'section':
|
|
case 'summary':
|
|
case 'ul':
|
|
|
|
case 'pre':
|
|
case 'listing':
|
|
|
|
case 'table':
|
|
|
|
case 'hr':
|
|
|
|
case 'xmp':
|
|
|
|
case 'h1':
|
|
case 'h2':
|
|
case 'h3':
|
|
case 'h4':
|
|
case 'h5':
|
|
case 'h6':
|
|
return ancestorInfo.pTagInButtonScope;
|
|
|
|
case 'form':
|
|
return ancestorInfo.formTag || ancestorInfo.pTagInButtonScope;
|
|
|
|
case 'li':
|
|
return ancestorInfo.listItemTagAutoclosing;
|
|
|
|
case 'dd':
|
|
case 'dt':
|
|
return ancestorInfo.dlItemTagAutoclosing;
|
|
|
|
case 'button':
|
|
return ancestorInfo.buttonTagInScope;
|
|
|
|
case 'a':
|
|
// Spec says something about storing a list of markers, but it sounds
|
|
// equivalent to this check.
|
|
return ancestorInfo.aTagInScope;
|
|
|
|
case 'nobr':
|
|
return ancestorInfo.nobrTagInScope;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Given a ReactCompositeComponent instance, return a list of its recursive
|
|
* owners, starting at the root and ending with the instance itself.
|
|
*/
|
|
var findOwnerStack = function (instance) {
|
|
if (!instance) {
|
|
return [];
|
|
}
|
|
|
|
var stack = [];
|
|
/*eslint-disable space-after-keywords */
|
|
do {
|
|
/*eslint-enable space-after-keywords */
|
|
stack.push(instance);
|
|
} while (instance = instance._currentElement._owner);
|
|
stack.reverse();
|
|
return stack;
|
|
};
|
|
|
|
var didWarn = {};
|
|
|
|
validateDOMNesting = function (childTag, childInstance, ancestorInfo) {
|
|
ancestorInfo = ancestorInfo || emptyAncestorInfo;
|
|
var parentInfo = ancestorInfo.parentTag;
|
|
var parentTag = parentInfo && parentInfo.tag;
|
|
|
|
var invalidParent = isTagValidWithParent(childTag, parentTag) ? null : parentInfo;
|
|
var invalidAncestor = invalidParent ? null : findInvalidAncestorForTag(childTag, ancestorInfo);
|
|
var problematic = invalidParent || invalidAncestor;
|
|
|
|
if (problematic) {
|
|
var ancestorTag = problematic.tag;
|
|
var ancestorInstance = problematic.instance;
|
|
|
|
var childOwner = childInstance && childInstance._currentElement._owner;
|
|
var ancestorOwner = ancestorInstance && ancestorInstance._currentElement._owner;
|
|
|
|
var childOwners = findOwnerStack(childOwner);
|
|
var ancestorOwners = findOwnerStack(ancestorOwner);
|
|
|
|
var minStackLen = Math.min(childOwners.length, ancestorOwners.length);
|
|
var i;
|
|
|
|
var deepestCommon = -1;
|
|
for (i = 0; i < minStackLen; i++) {
|
|
if (childOwners[i] === ancestorOwners[i]) {
|
|
deepestCommon = i;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
var UNKNOWN = '(unknown)';
|
|
var childOwnerNames = childOwners.slice(deepestCommon + 1).map(function (inst) {
|
|
return inst.getName() || UNKNOWN;
|
|
});
|
|
var ancestorOwnerNames = ancestorOwners.slice(deepestCommon + 1).map(function (inst) {
|
|
return inst.getName() || UNKNOWN;
|
|
});
|
|
var ownerInfo = [].concat(
|
|
// If the parent and child instances have a common owner ancestor, start
|
|
// with that -- otherwise we just start with the parent's owners.
|
|
deepestCommon !== -1 ? childOwners[deepestCommon].getName() || UNKNOWN : [], ancestorOwnerNames, ancestorTag,
|
|
// If we're warning about an invalid (non-parent) ancestry, add '...'
|
|
invalidAncestor ? ['...'] : [], childOwnerNames, childTag).join(' > ');
|
|
|
|
var warnKey = !!invalidParent + '|' + childTag + '|' + ancestorTag + '|' + ownerInfo;
|
|
if (didWarn[warnKey]) {
|
|
return;
|
|
}
|
|
didWarn[warnKey] = true;
|
|
|
|
if (invalidParent) {
|
|
var info = '';
|
|
if (ancestorTag === 'table' && childTag === 'tr') {
|
|
info += ' Add a <tbody> to your code to match the DOM tree generated by ' + 'the browser.';
|
|
}
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'validateDOMNesting(...): <%s> cannot appear as a child of <%s>. ' + 'See %s.%s', childTag, ancestorTag, ownerInfo, info) : undefined;
|
|
} else {
|
|
process.env.NODE_ENV !== 'production' ? warning(false, 'validateDOMNesting(...): <%s> cannot appear as a descendant of ' + '<%s>. See %s.', childTag, ancestorTag, ownerInfo) : undefined;
|
|
}
|
|
}
|
|
};
|
|
|
|
validateDOMNesting.ancestorInfoContextKey = '__validateDOMNesting_ancestorInfo$' + Math.random().toString(36).slice(2);
|
|
|
|
validateDOMNesting.updatedAncestorInfo = updatedAncestorInfo;
|
|
|
|
// For testing
|
|
validateDOMNesting.isTagValidInContext = function (tag, ancestorInfo) {
|
|
ancestorInfo = ancestorInfo || emptyAncestorInfo;
|
|
var parentInfo = ancestorInfo.parentTag;
|
|
var parentTag = parentInfo && parentInfo.tag;
|
|
return isTagValidWithParent(tag, parentTag) && !findInvalidAncestorForTag(tag, ancestorInfo);
|
|
};
|
|
}
|
|
|
|
module.exports = validateDOMNesting;
|
|
}).call(this,require('_process'))
|
|
},{"./Object.assign":157,"_process":133,"fbjs/lib/emptyFunction":64,"fbjs/lib/warning":83}],264:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
module.exports = require('./lib/React');
|
|
|
|
},{"./lib/React":159}],265:[function(require,module,exports){
|
|
module.exports = require("./lib/_stream_duplex.js")
|
|
|
|
},{"./lib/_stream_duplex.js":266}],266:[function(require,module,exports){
|
|
arguments[4][126][0].apply(exports,arguments)
|
|
},{"./_stream_readable":268,"./_stream_writable":270,"core-util-is":44,"dup":126,"inherits":87,"process-nextick-args":132}],267:[function(require,module,exports){
|
|
// a passthrough stream.
|
|
// basically just the most minimal sort of Transform stream.
|
|
// Every written chunk gets output as-is.
|
|
|
|
'use strict';
|
|
|
|
module.exports = PassThrough;
|
|
|
|
var Transform = require('./_stream_transform');
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
util.inherits(PassThrough, Transform);
|
|
|
|
function PassThrough(options) {
|
|
if (!(this instanceof PassThrough)) return new PassThrough(options);
|
|
|
|
Transform.call(this, options);
|
|
}
|
|
|
|
PassThrough.prototype._transform = function (chunk, encoding, cb) {
|
|
cb(null, chunk);
|
|
};
|
|
},{"./_stream_transform":269,"core-util-is":44,"inherits":87}],268:[function(require,module,exports){
|
|
(function (process){
|
|
'use strict';
|
|
|
|
module.exports = Readable;
|
|
|
|
/*<replacement>*/
|
|
var processNextTick = require('process-nextick-args');
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var isArray = require('isarray');
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var Duplex;
|
|
/*</replacement>*/
|
|
|
|
Readable.ReadableState = ReadableState;
|
|
|
|
/*<replacement>*/
|
|
var EE = require('events').EventEmitter;
|
|
|
|
var EElistenerCount = function (emitter, type) {
|
|
return emitter.listeners(type).length;
|
|
};
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var Stream;
|
|
(function () {
|
|
try {
|
|
Stream = require('st' + 'ream');
|
|
} catch (_) {} finally {
|
|
if (!Stream) Stream = require('events').EventEmitter;
|
|
}
|
|
})();
|
|
/*</replacement>*/
|
|
|
|
var Buffer = require('buffer').Buffer;
|
|
/*<replacement>*/
|
|
var bufferShim = require('buffer-shims');
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var debugUtil = require('util');
|
|
var debug = void 0;
|
|
if (debugUtil && debugUtil.debuglog) {
|
|
debug = debugUtil.debuglog('stream');
|
|
} else {
|
|
debug = function () {};
|
|
}
|
|
/*</replacement>*/
|
|
|
|
var BufferList = require('./internal/streams/BufferList');
|
|
var StringDecoder;
|
|
|
|
util.inherits(Readable, Stream);
|
|
|
|
function prependListener(emitter, event, fn) {
|
|
// Sadly this is not cacheable as some libraries bundle their own
|
|
// event emitter implementation with them.
|
|
if (typeof emitter.prependListener === 'function') {
|
|
return emitter.prependListener(event, fn);
|
|
} else {
|
|
// This is a hack to make sure that our error handler is attached before any
|
|
// userland ones. NEVER DO THIS. This is here only because this code needs
|
|
// to continue to work with older versions of Node.js that do not include
|
|
// the prependListener() method. The goal is to eventually remove this hack.
|
|
if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]];
|
|
}
|
|
}
|
|
|
|
function ReadableState(options, stream) {
|
|
Duplex = Duplex || require('./_stream_duplex');
|
|
|
|
options = options || {};
|
|
|
|
// object stream flag. Used to make read(n) ignore n and to
|
|
// make all the buffer merging and length checks go away
|
|
this.objectMode = !!options.objectMode;
|
|
|
|
if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.readableObjectMode;
|
|
|
|
// the point at which it stops calling _read() to fill the buffer
|
|
// Note: 0 is a valid value, means "don't call _read preemptively ever"
|
|
var hwm = options.highWaterMark;
|
|
var defaultHwm = this.objectMode ? 16 : 16 * 1024;
|
|
this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm;
|
|
|
|
// cast to ints.
|
|
this.highWaterMark = ~ ~this.highWaterMark;
|
|
|
|
// A linked list is used to store data chunks instead of an array because the
|
|
// linked list can remove elements from the beginning faster than
|
|
// array.shift()
|
|
this.buffer = new BufferList();
|
|
this.length = 0;
|
|
this.pipes = null;
|
|
this.pipesCount = 0;
|
|
this.flowing = null;
|
|
this.ended = false;
|
|
this.endEmitted = false;
|
|
this.reading = false;
|
|
|
|
// a flag to be able to tell if the onwrite cb is called immediately,
|
|
// or on a later tick. We set this to true at first, because any
|
|
// actions that shouldn't happen until "later" should generally also
|
|
// not happen before the first write call.
|
|
this.sync = true;
|
|
|
|
// whenever we return null, then we set a flag to say
|
|
// that we're awaiting a 'readable' event emission.
|
|
this.needReadable = false;
|
|
this.emittedReadable = false;
|
|
this.readableListening = false;
|
|
this.resumeScheduled = false;
|
|
|
|
// Crypto is kind of old and crusty. Historically, its default string
|
|
// encoding is 'binary' so we have to make this configurable.
|
|
// Everything else in the universe uses 'utf8', though.
|
|
this.defaultEncoding = options.defaultEncoding || 'utf8';
|
|
|
|
// when piping, we only care about 'readable' events that happen
|
|
// after read()ing all the bytes and not getting any pushback.
|
|
this.ranOut = false;
|
|
|
|
// the number of writers that are awaiting a drain event in .pipe()s
|
|
this.awaitDrain = 0;
|
|
|
|
// if true, a maybeReadMore has been scheduled
|
|
this.readingMore = false;
|
|
|
|
this.decoder = null;
|
|
this.encoding = null;
|
|
if (options.encoding) {
|
|
if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder;
|
|
this.decoder = new StringDecoder(options.encoding);
|
|
this.encoding = options.encoding;
|
|
}
|
|
}
|
|
|
|
function Readable(options) {
|
|
Duplex = Duplex || require('./_stream_duplex');
|
|
|
|
if (!(this instanceof Readable)) return new Readable(options);
|
|
|
|
this._readableState = new ReadableState(options, this);
|
|
|
|
// legacy
|
|
this.readable = true;
|
|
|
|
if (options && typeof options.read === 'function') this._read = options.read;
|
|
|
|
Stream.call(this);
|
|
}
|
|
|
|
// Manually shove something into the read() buffer.
|
|
// This returns true if the highWaterMark has not been hit yet,
|
|
// similar to how Writable.write() returns true if you should
|
|
// write() some more.
|
|
Readable.prototype.push = function (chunk, encoding) {
|
|
var state = this._readableState;
|
|
|
|
if (!state.objectMode && typeof chunk === 'string') {
|
|
encoding = encoding || state.defaultEncoding;
|
|
if (encoding !== state.encoding) {
|
|
chunk = bufferShim.from(chunk, encoding);
|
|
encoding = '';
|
|
}
|
|
}
|
|
|
|
return readableAddChunk(this, state, chunk, encoding, false);
|
|
};
|
|
|
|
// Unshift should *always* be something directly out of read()
|
|
Readable.prototype.unshift = function (chunk) {
|
|
var state = this._readableState;
|
|
return readableAddChunk(this, state, chunk, '', true);
|
|
};
|
|
|
|
Readable.prototype.isPaused = function () {
|
|
return this._readableState.flowing === false;
|
|
};
|
|
|
|
function readableAddChunk(stream, state, chunk, encoding, addToFront) {
|
|
var er = chunkInvalid(state, chunk);
|
|
if (er) {
|
|
stream.emit('error', er);
|
|
} else if (chunk === null) {
|
|
state.reading = false;
|
|
onEofChunk(stream, state);
|
|
} else if (state.objectMode || chunk && chunk.length > 0) {
|
|
if (state.ended && !addToFront) {
|
|
var e = new Error('stream.push() after EOF');
|
|
stream.emit('error', e);
|
|
} else if (state.endEmitted && addToFront) {
|
|
var _e = new Error('stream.unshift() after end event');
|
|
stream.emit('error', _e);
|
|
} else {
|
|
var skipAdd;
|
|
if (state.decoder && !addToFront && !encoding) {
|
|
chunk = state.decoder.write(chunk);
|
|
skipAdd = !state.objectMode && chunk.length === 0;
|
|
}
|
|
|
|
if (!addToFront) state.reading = false;
|
|
|
|
// Don't add to the buffer if we've decoded to an empty string chunk and
|
|
// we're not in object mode
|
|
if (!skipAdd) {
|
|
// if we want the data now, just emit it.
|
|
if (state.flowing && state.length === 0 && !state.sync) {
|
|
stream.emit('data', chunk);
|
|
stream.read(0);
|
|
} else {
|
|
// update the buffer info.
|
|
state.length += state.objectMode ? 1 : chunk.length;
|
|
if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk);
|
|
|
|
if (state.needReadable) emitReadable(stream);
|
|
}
|
|
}
|
|
|
|
maybeReadMore(stream, state);
|
|
}
|
|
} else if (!addToFront) {
|
|
state.reading = false;
|
|
}
|
|
|
|
return needMoreData(state);
|
|
}
|
|
|
|
// if it's past the high water mark, we can push in some more.
|
|
// Also, if we have no data yet, we can stand some
|
|
// more bytes. This is to work around cases where hwm=0,
|
|
// such as the repl. Also, if the push() triggered a
|
|
// readable event, and the user called read(largeNumber) such that
|
|
// needReadable was set, then we ought to push more, so that another
|
|
// 'readable' event will be triggered.
|
|
function needMoreData(state) {
|
|
return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0);
|
|
}
|
|
|
|
// backwards compatibility.
|
|
Readable.prototype.setEncoding = function (enc) {
|
|
if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder;
|
|
this._readableState.decoder = new StringDecoder(enc);
|
|
this._readableState.encoding = enc;
|
|
return this;
|
|
};
|
|
|
|
// Don't raise the hwm > 8MB
|
|
var MAX_HWM = 0x800000;
|
|
function computeNewHighWaterMark(n) {
|
|
if (n >= MAX_HWM) {
|
|
n = MAX_HWM;
|
|
} else {
|
|
// Get the next highest power of 2 to prevent increasing hwm excessively in
|
|
// tiny amounts
|
|
n--;
|
|
n |= n >>> 1;
|
|
n |= n >>> 2;
|
|
n |= n >>> 4;
|
|
n |= n >>> 8;
|
|
n |= n >>> 16;
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
// This function is designed to be inlinable, so please take care when making
|
|
// changes to the function body.
|
|
function howMuchToRead(n, state) {
|
|
if (n <= 0 || state.length === 0 && state.ended) return 0;
|
|
if (state.objectMode) return 1;
|
|
if (n !== n) {
|
|
// Only flow one buffer at a time
|
|
if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length;
|
|
}
|
|
// If we're asking for more than the current hwm, then raise the hwm.
|
|
if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n);
|
|
if (n <= state.length) return n;
|
|
// Don't have enough
|
|
if (!state.ended) {
|
|
state.needReadable = true;
|
|
return 0;
|
|
}
|
|
return state.length;
|
|
}
|
|
|
|
// you can override either this method, or the async _read(n) below.
|
|
Readable.prototype.read = function (n) {
|
|
debug('read', n);
|
|
n = parseInt(n, 10);
|
|
var state = this._readableState;
|
|
var nOrig = n;
|
|
|
|
if (n !== 0) state.emittedReadable = false;
|
|
|
|
// if we're doing read(0) to trigger a readable event, but we
|
|
// already have a bunch of data in the buffer, then just trigger
|
|
// the 'readable' event and move on.
|
|
if (n === 0 && state.needReadable && (state.length >= state.highWaterMark || state.ended)) {
|
|
debug('read: emitReadable', state.length, state.ended);
|
|
if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this);
|
|
return null;
|
|
}
|
|
|
|
n = howMuchToRead(n, state);
|
|
|
|
// if we've ended, and we're now clear, then finish it up.
|
|
if (n === 0 && state.ended) {
|
|
if (state.length === 0) endReadable(this);
|
|
return null;
|
|
}
|
|
|
|
// All the actual chunk generation logic needs to be
|
|
// *below* the call to _read. The reason is that in certain
|
|
// synthetic stream cases, such as passthrough streams, _read
|
|
// may be a completely synchronous operation which may change
|
|
// the state of the read buffer, providing enough data when
|
|
// before there was *not* enough.
|
|
//
|
|
// So, the steps are:
|
|
// 1. Figure out what the state of things will be after we do
|
|
// a read from the buffer.
|
|
//
|
|
// 2. If that resulting state will trigger a _read, then call _read.
|
|
// Note that this may be asynchronous, or synchronous. Yes, it is
|
|
// deeply ugly to write APIs this way, but that still doesn't mean
|
|
// that the Readable class should behave improperly, as streams are
|
|
// designed to be sync/async agnostic.
|
|
// Take note if the _read call is sync or async (ie, if the read call
|
|
// has returned yet), so that we know whether or not it's safe to emit
|
|
// 'readable' etc.
|
|
//
|
|
// 3. Actually pull the requested chunks out of the buffer and return.
|
|
|
|
// if we need a readable event, then we need to do some reading.
|
|
var doRead = state.needReadable;
|
|
debug('need readable', doRead);
|
|
|
|
// if we currently have less than the highWaterMark, then also read some
|
|
if (state.length === 0 || state.length - n < state.highWaterMark) {
|
|
doRead = true;
|
|
debug('length less than watermark', doRead);
|
|
}
|
|
|
|
// however, if we've ended, then there's no point, and if we're already
|
|
// reading, then it's unnecessary.
|
|
if (state.ended || state.reading) {
|
|
doRead = false;
|
|
debug('reading or ended', doRead);
|
|
} else if (doRead) {
|
|
debug('do read');
|
|
state.reading = true;
|
|
state.sync = true;
|
|
// if the length is currently zero, then we *need* a readable event.
|
|
if (state.length === 0) state.needReadable = true;
|
|
// call internal read method
|
|
this._read(state.highWaterMark);
|
|
state.sync = false;
|
|
// If _read pushed data synchronously, then `reading` will be false,
|
|
// and we need to re-evaluate how much data we can return to the user.
|
|
if (!state.reading) n = howMuchToRead(nOrig, state);
|
|
}
|
|
|
|
var ret;
|
|
if (n > 0) ret = fromList(n, state);else ret = null;
|
|
|
|
if (ret === null) {
|
|
state.needReadable = true;
|
|
n = 0;
|
|
} else {
|
|
state.length -= n;
|
|
}
|
|
|
|
if (state.length === 0) {
|
|
// If we have nothing in the buffer, then we want to know
|
|
// as soon as we *do* get something into the buffer.
|
|
if (!state.ended) state.needReadable = true;
|
|
|
|
// If we tried to read() past the EOF, then emit end on the next tick.
|
|
if (nOrig !== n && state.ended) endReadable(this);
|
|
}
|
|
|
|
if (ret !== null) this.emit('data', ret);
|
|
|
|
return ret;
|
|
};
|
|
|
|
function chunkInvalid(state, chunk) {
|
|
var er = null;
|
|
if (!Buffer.isBuffer(chunk) && typeof chunk !== 'string' && chunk !== null && chunk !== undefined && !state.objectMode) {
|
|
er = new TypeError('Invalid non-string/buffer chunk');
|
|
}
|
|
return er;
|
|
}
|
|
|
|
function onEofChunk(stream, state) {
|
|
if (state.ended) return;
|
|
if (state.decoder) {
|
|
var chunk = state.decoder.end();
|
|
if (chunk && chunk.length) {
|
|
state.buffer.push(chunk);
|
|
state.length += state.objectMode ? 1 : chunk.length;
|
|
}
|
|
}
|
|
state.ended = true;
|
|
|
|
// emit 'readable' now to make sure it gets picked up.
|
|
emitReadable(stream);
|
|
}
|
|
|
|
// Don't emit readable right away in sync mode, because this can trigger
|
|
// another read() call => stack overflow. This way, it might trigger
|
|
// a nextTick recursion warning, but that's not so bad.
|
|
function emitReadable(stream) {
|
|
var state = stream._readableState;
|
|
state.needReadable = false;
|
|
if (!state.emittedReadable) {
|
|
debug('emitReadable', state.flowing);
|
|
state.emittedReadable = true;
|
|
if (state.sync) processNextTick(emitReadable_, stream);else emitReadable_(stream);
|
|
}
|
|
}
|
|
|
|
function emitReadable_(stream) {
|
|
debug('emit readable');
|
|
stream.emit('readable');
|
|
flow(stream);
|
|
}
|
|
|
|
// at this point, the user has presumably seen the 'readable' event,
|
|
// and called read() to consume some data. that may have triggered
|
|
// in turn another _read(n) call, in which case reading = true if
|
|
// it's in progress.
|
|
// However, if we're not ended, or reading, and the length < hwm,
|
|
// then go ahead and try to read some more preemptively.
|
|
function maybeReadMore(stream, state) {
|
|
if (!state.readingMore) {
|
|
state.readingMore = true;
|
|
processNextTick(maybeReadMore_, stream, state);
|
|
}
|
|
}
|
|
|
|
function maybeReadMore_(stream, state) {
|
|
var len = state.length;
|
|
while (!state.reading && !state.flowing && !state.ended && state.length < state.highWaterMark) {
|
|
debug('maybeReadMore read 0');
|
|
stream.read(0);
|
|
if (len === state.length)
|
|
// didn't get any data, stop spinning.
|
|
break;else len = state.length;
|
|
}
|
|
state.readingMore = false;
|
|
}
|
|
|
|
// abstract method. to be overridden in specific implementation classes.
|
|
// call cb(er, data) where data is <= n in length.
|
|
// for virtual (non-string, non-buffer) streams, "length" is somewhat
|
|
// arbitrary, and perhaps not very meaningful.
|
|
Readable.prototype._read = function (n) {
|
|
this.emit('error', new Error('_read() is not implemented'));
|
|
};
|
|
|
|
Readable.prototype.pipe = function (dest, pipeOpts) {
|
|
var src = this;
|
|
var state = this._readableState;
|
|
|
|
switch (state.pipesCount) {
|
|
case 0:
|
|
state.pipes = dest;
|
|
break;
|
|
case 1:
|
|
state.pipes = [state.pipes, dest];
|
|
break;
|
|
default:
|
|
state.pipes.push(dest);
|
|
break;
|
|
}
|
|
state.pipesCount += 1;
|
|
debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts);
|
|
|
|
var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr;
|
|
|
|
var endFn = doEnd ? onend : cleanup;
|
|
if (state.endEmitted) processNextTick(endFn);else src.once('end', endFn);
|
|
|
|
dest.on('unpipe', onunpipe);
|
|
function onunpipe(readable) {
|
|
debug('onunpipe');
|
|
if (readable === src) {
|
|
cleanup();
|
|
}
|
|
}
|
|
|
|
function onend() {
|
|
debug('onend');
|
|
dest.end();
|
|
}
|
|
|
|
// when the dest drains, it reduces the awaitDrain counter
|
|
// on the source. This would be more elegant with a .once()
|
|
// handler in flow(), but adding and removing repeatedly is
|
|
// too slow.
|
|
var ondrain = pipeOnDrain(src);
|
|
dest.on('drain', ondrain);
|
|
|
|
var cleanedUp = false;
|
|
function cleanup() {
|
|
debug('cleanup');
|
|
// cleanup event handlers once the pipe is broken
|
|
dest.removeListener('close', onclose);
|
|
dest.removeListener('finish', onfinish);
|
|
dest.removeListener('drain', ondrain);
|
|
dest.removeListener('error', onerror);
|
|
dest.removeListener('unpipe', onunpipe);
|
|
src.removeListener('end', onend);
|
|
src.removeListener('end', cleanup);
|
|
src.removeListener('data', ondata);
|
|
|
|
cleanedUp = true;
|
|
|
|
// if the reader is waiting for a drain event from this
|
|
// specific writer, then it would cause it to never start
|
|
// flowing again.
|
|
// So, if this is awaiting a drain, then we just call it now.
|
|
// If we don't know, then assume that we are waiting for one.
|
|
if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain();
|
|
}
|
|
|
|
// If the user pushes more data while we're writing to dest then we'll end up
|
|
// in ondata again. However, we only want to increase awaitDrain once because
|
|
// dest will only emit one 'drain' event for the multiple writes.
|
|
// => Introduce a guard on increasing awaitDrain.
|
|
var increasedAwaitDrain = false;
|
|
src.on('data', ondata);
|
|
function ondata(chunk) {
|
|
debug('ondata');
|
|
increasedAwaitDrain = false;
|
|
var ret = dest.write(chunk);
|
|
if (false === ret && !increasedAwaitDrain) {
|
|
// If the user unpiped during `dest.write()`, it is possible
|
|
// to get stuck in a permanently paused state if that write
|
|
// also returned false.
|
|
// => Check whether `dest` is still a piping destination.
|
|
if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) {
|
|
debug('false write response, pause', src._readableState.awaitDrain);
|
|
src._readableState.awaitDrain++;
|
|
increasedAwaitDrain = true;
|
|
}
|
|
src.pause();
|
|
}
|
|
}
|
|
|
|
// if the dest has an error, then stop piping into it.
|
|
// however, don't suppress the throwing behavior for this.
|
|
function onerror(er) {
|
|
debug('onerror', er);
|
|
unpipe();
|
|
dest.removeListener('error', onerror);
|
|
if (EElistenerCount(dest, 'error') === 0) dest.emit('error', er);
|
|
}
|
|
|
|
// Make sure our error handler is attached before userland ones.
|
|
prependListener(dest, 'error', onerror);
|
|
|
|
// Both close and finish should trigger unpipe, but only once.
|
|
function onclose() {
|
|
dest.removeListener('finish', onfinish);
|
|
unpipe();
|
|
}
|
|
dest.once('close', onclose);
|
|
function onfinish() {
|
|
debug('onfinish');
|
|
dest.removeListener('close', onclose);
|
|
unpipe();
|
|
}
|
|
dest.once('finish', onfinish);
|
|
|
|
function unpipe() {
|
|
debug('unpipe');
|
|
src.unpipe(dest);
|
|
}
|
|
|
|
// tell the dest that it's being piped to
|
|
dest.emit('pipe', src);
|
|
|
|
// start the flow if it hasn't been started already.
|
|
if (!state.flowing) {
|
|
debug('pipe resume');
|
|
src.resume();
|
|
}
|
|
|
|
return dest;
|
|
};
|
|
|
|
function pipeOnDrain(src) {
|
|
return function () {
|
|
var state = src._readableState;
|
|
debug('pipeOnDrain', state.awaitDrain);
|
|
if (state.awaitDrain) state.awaitDrain--;
|
|
if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) {
|
|
state.flowing = true;
|
|
flow(src);
|
|
}
|
|
};
|
|
}
|
|
|
|
Readable.prototype.unpipe = function (dest) {
|
|
var state = this._readableState;
|
|
|
|
// if we're not piping anywhere, then do nothing.
|
|
if (state.pipesCount === 0) return this;
|
|
|
|
// just one destination. most common case.
|
|
if (state.pipesCount === 1) {
|
|
// passed in one, but it's not the right one.
|
|
if (dest && dest !== state.pipes) return this;
|
|
|
|
if (!dest) dest = state.pipes;
|
|
|
|
// got a match.
|
|
state.pipes = null;
|
|
state.pipesCount = 0;
|
|
state.flowing = false;
|
|
if (dest) dest.emit('unpipe', this);
|
|
return this;
|
|
}
|
|
|
|
// slow case. multiple pipe destinations.
|
|
|
|
if (!dest) {
|
|
// remove all.
|
|
var dests = state.pipes;
|
|
var len = state.pipesCount;
|
|
state.pipes = null;
|
|
state.pipesCount = 0;
|
|
state.flowing = false;
|
|
|
|
for (var i = 0; i < len; i++) {
|
|
dests[i].emit('unpipe', this);
|
|
}return this;
|
|
}
|
|
|
|
// try to find the right one.
|
|
var index = indexOf(state.pipes, dest);
|
|
if (index === -1) return this;
|
|
|
|
state.pipes.splice(index, 1);
|
|
state.pipesCount -= 1;
|
|
if (state.pipesCount === 1) state.pipes = state.pipes[0];
|
|
|
|
dest.emit('unpipe', this);
|
|
|
|
return this;
|
|
};
|
|
|
|
// set up data events if they are asked for
|
|
// Ensure readable listeners eventually get something
|
|
Readable.prototype.on = function (ev, fn) {
|
|
var res = Stream.prototype.on.call(this, ev, fn);
|
|
|
|
if (ev === 'data') {
|
|
// Start flowing on next tick if stream isn't explicitly paused
|
|
if (this._readableState.flowing !== false) this.resume();
|
|
} else if (ev === 'readable') {
|
|
var state = this._readableState;
|
|
if (!state.endEmitted && !state.readableListening) {
|
|
state.readableListening = state.needReadable = true;
|
|
state.emittedReadable = false;
|
|
if (!state.reading) {
|
|
processNextTick(nReadingNextTick, this);
|
|
} else if (state.length) {
|
|
emitReadable(this, state);
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
};
|
|
Readable.prototype.addListener = Readable.prototype.on;
|
|
|
|
function nReadingNextTick(self) {
|
|
debug('readable nexttick read 0');
|
|
self.read(0);
|
|
}
|
|
|
|
// pause() and resume() are remnants of the legacy readable stream API
|
|
// If the user uses them, then switch into old mode.
|
|
Readable.prototype.resume = function () {
|
|
var state = this._readableState;
|
|
if (!state.flowing) {
|
|
debug('resume');
|
|
state.flowing = true;
|
|
resume(this, state);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
function resume(stream, state) {
|
|
if (!state.resumeScheduled) {
|
|
state.resumeScheduled = true;
|
|
processNextTick(resume_, stream, state);
|
|
}
|
|
}
|
|
|
|
function resume_(stream, state) {
|
|
if (!state.reading) {
|
|
debug('resume read 0');
|
|
stream.read(0);
|
|
}
|
|
|
|
state.resumeScheduled = false;
|
|
state.awaitDrain = 0;
|
|
stream.emit('resume');
|
|
flow(stream);
|
|
if (state.flowing && !state.reading) stream.read(0);
|
|
}
|
|
|
|
Readable.prototype.pause = function () {
|
|
debug('call pause flowing=%j', this._readableState.flowing);
|
|
if (false !== this._readableState.flowing) {
|
|
debug('pause');
|
|
this._readableState.flowing = false;
|
|
this.emit('pause');
|
|
}
|
|
return this;
|
|
};
|
|
|
|
function flow(stream) {
|
|
var state = stream._readableState;
|
|
debug('flow', state.flowing);
|
|
while (state.flowing && stream.read() !== null) {}
|
|
}
|
|
|
|
// wrap an old-style stream as the async data source.
|
|
// This is *not* part of the readable stream interface.
|
|
// It is an ugly unfortunate mess of history.
|
|
Readable.prototype.wrap = function (stream) {
|
|
var state = this._readableState;
|
|
var paused = false;
|
|
|
|
var self = this;
|
|
stream.on('end', function () {
|
|
debug('wrapped end');
|
|
if (state.decoder && !state.ended) {
|
|
var chunk = state.decoder.end();
|
|
if (chunk && chunk.length) self.push(chunk);
|
|
}
|
|
|
|
self.push(null);
|
|
});
|
|
|
|
stream.on('data', function (chunk) {
|
|
debug('wrapped data');
|
|
if (state.decoder) chunk = state.decoder.write(chunk);
|
|
|
|
// don't skip over falsy values in objectMode
|
|
if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return;
|
|
|
|
var ret = self.push(chunk);
|
|
if (!ret) {
|
|
paused = true;
|
|
stream.pause();
|
|
}
|
|
});
|
|
|
|
// proxy all the other methods.
|
|
// important when wrapping filters and duplexes.
|
|
for (var i in stream) {
|
|
if (this[i] === undefined && typeof stream[i] === 'function') {
|
|
this[i] = function (method) {
|
|
return function () {
|
|
return stream[method].apply(stream, arguments);
|
|
};
|
|
}(i);
|
|
}
|
|
}
|
|
|
|
// proxy certain important events.
|
|
var events = ['error', 'close', 'destroy', 'pause', 'resume'];
|
|
forEach(events, function (ev) {
|
|
stream.on(ev, self.emit.bind(self, ev));
|
|
});
|
|
|
|
// when we try to consume some more bytes, simply unpause the
|
|
// underlying stream.
|
|
self._read = function (n) {
|
|
debug('wrapped _read', n);
|
|
if (paused) {
|
|
paused = false;
|
|
stream.resume();
|
|
}
|
|
};
|
|
|
|
return self;
|
|
};
|
|
|
|
// exposed for testing purposes only.
|
|
Readable._fromList = fromList;
|
|
|
|
// Pluck off n bytes from an array of buffers.
|
|
// Length is the combined lengths of all the buffers in the list.
|
|
// This function is designed to be inlinable, so please take care when making
|
|
// changes to the function body.
|
|
function fromList(n, state) {
|
|
// nothing buffered
|
|
if (state.length === 0) return null;
|
|
|
|
var ret;
|
|
if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) {
|
|
// read it all, truncate the list
|
|
if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.head.data;else ret = state.buffer.concat(state.length);
|
|
state.buffer.clear();
|
|
} else {
|
|
// read part of list
|
|
ret = fromListPartial(n, state.buffer, state.decoder);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Extracts only enough buffered data to satisfy the amount requested.
|
|
// This function is designed to be inlinable, so please take care when making
|
|
// changes to the function body.
|
|
function fromListPartial(n, list, hasStrings) {
|
|
var ret;
|
|
if (n < list.head.data.length) {
|
|
// slice is the same for buffers and strings
|
|
ret = list.head.data.slice(0, n);
|
|
list.head.data = list.head.data.slice(n);
|
|
} else if (n === list.head.data.length) {
|
|
// first chunk is a perfect match
|
|
ret = list.shift();
|
|
} else {
|
|
// result spans more than one buffer
|
|
ret = hasStrings ? copyFromBufferString(n, list) : copyFromBuffer(n, list);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Copies a specified amount of characters from the list of buffered data
|
|
// chunks.
|
|
// This function is designed to be inlinable, so please take care when making
|
|
// changes to the function body.
|
|
function copyFromBufferString(n, list) {
|
|
var p = list.head;
|
|
var c = 1;
|
|
var ret = p.data;
|
|
n -= ret.length;
|
|
while (p = p.next) {
|
|
var str = p.data;
|
|
var nb = n > str.length ? str.length : n;
|
|
if (nb === str.length) ret += str;else ret += str.slice(0, n);
|
|
n -= nb;
|
|
if (n === 0) {
|
|
if (nb === str.length) {
|
|
++c;
|
|
if (p.next) list.head = p.next;else list.head = list.tail = null;
|
|
} else {
|
|
list.head = p;
|
|
p.data = str.slice(nb);
|
|
}
|
|
break;
|
|
}
|
|
++c;
|
|
}
|
|
list.length -= c;
|
|
return ret;
|
|
}
|
|
|
|
// Copies a specified amount of bytes from the list of buffered data chunks.
|
|
// This function is designed to be inlinable, so please take care when making
|
|
// changes to the function body.
|
|
function copyFromBuffer(n, list) {
|
|
var ret = bufferShim.allocUnsafe(n);
|
|
var p = list.head;
|
|
var c = 1;
|
|
p.data.copy(ret);
|
|
n -= p.data.length;
|
|
while (p = p.next) {
|
|
var buf = p.data;
|
|
var nb = n > buf.length ? buf.length : n;
|
|
buf.copy(ret, ret.length - n, 0, nb);
|
|
n -= nb;
|
|
if (n === 0) {
|
|
if (nb === buf.length) {
|
|
++c;
|
|
if (p.next) list.head = p.next;else list.head = list.tail = null;
|
|
} else {
|
|
list.head = p;
|
|
p.data = buf.slice(nb);
|
|
}
|
|
break;
|
|
}
|
|
++c;
|
|
}
|
|
list.length -= c;
|
|
return ret;
|
|
}
|
|
|
|
function endReadable(stream) {
|
|
var state = stream._readableState;
|
|
|
|
// If we get here before consuming all the bytes, then that is a
|
|
// bug in node. Should never happen.
|
|
if (state.length > 0) throw new Error('"endReadable()" called on non-empty stream');
|
|
|
|
if (!state.endEmitted) {
|
|
state.ended = true;
|
|
processNextTick(endReadableNT, state, stream);
|
|
}
|
|
}
|
|
|
|
function endReadableNT(state, stream) {
|
|
// Check that we didn't get one last unshift.
|
|
if (!state.endEmitted && state.length === 0) {
|
|
state.endEmitted = true;
|
|
stream.readable = false;
|
|
stream.emit('end');
|
|
}
|
|
}
|
|
|
|
function forEach(xs, f) {
|
|
for (var i = 0, l = xs.length; i < l; i++) {
|
|
f(xs[i], i);
|
|
}
|
|
}
|
|
|
|
function indexOf(xs, x) {
|
|
for (var i = 0, l = xs.length; i < l; i++) {
|
|
if (xs[i] === x) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
}).call(this,require('_process'))
|
|
},{"./_stream_duplex":266,"./internal/streams/BufferList":271,"_process":133,"buffer":43,"buffer-shims":42,"core-util-is":44,"events":56,"inherits":87,"isarray":89,"process-nextick-args":132,"string_decoder/":279,"util":41}],269:[function(require,module,exports){
|
|
// a transform stream is a readable/writable stream where you do
|
|
// something with the data. Sometimes it's called a "filter",
|
|
// but that's not a great name for it, since that implies a thing where
|
|
// some bits pass through, and others are simply ignored. (That would
|
|
// be a valid example of a transform, of course.)
|
|
//
|
|
// While the output is causally related to the input, it's not a
|
|
// necessarily symmetric or synchronous transformation. For example,
|
|
// a zlib stream might take multiple plain-text writes(), and then
|
|
// emit a single compressed chunk some time in the future.
|
|
//
|
|
// Here's how this works:
|
|
//
|
|
// The Transform stream has all the aspects of the readable and writable
|
|
// stream classes. When you write(chunk), that calls _write(chunk,cb)
|
|
// internally, and returns false if there's a lot of pending writes
|
|
// buffered up. When you call read(), that calls _read(n) until
|
|
// there's enough pending readable data buffered up.
|
|
//
|
|
// In a transform stream, the written data is placed in a buffer. When
|
|
// _read(n) is called, it transforms the queued up data, calling the
|
|
// buffered _write cb's as it consumes chunks. If consuming a single
|
|
// written chunk would result in multiple output chunks, then the first
|
|
// outputted bit calls the readcb, and subsequent chunks just go into
|
|
// the read buffer, and will cause it to emit 'readable' if necessary.
|
|
//
|
|
// This way, back-pressure is actually determined by the reading side,
|
|
// since _read has to be called to start processing a new chunk. However,
|
|
// a pathological inflate type of transform can cause excessive buffering
|
|
// here. For example, imagine a stream where every byte of input is
|
|
// interpreted as an integer from 0-255, and then results in that many
|
|
// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
|
|
// 1kb of data being output. In this case, you could write a very small
|
|
// amount of input, and end up with a very large amount of output. In
|
|
// such a pathological inflating mechanism, there'd be no way to tell
|
|
// the system to stop doing the transform. A single 4MB write could
|
|
// cause the system to run out of memory.
|
|
//
|
|
// However, even in such a pathological case, only a single written chunk
|
|
// would be consumed, and then the rest would wait (un-transformed) until
|
|
// the results of the previous transformed chunk were consumed.
|
|
|
|
'use strict';
|
|
|
|
module.exports = Transform;
|
|
|
|
var Duplex = require('./_stream_duplex');
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
util.inherits(Transform, Duplex);
|
|
|
|
function TransformState(stream) {
|
|
this.afterTransform = function (er, data) {
|
|
return afterTransform(stream, er, data);
|
|
};
|
|
|
|
this.needTransform = false;
|
|
this.transforming = false;
|
|
this.writecb = null;
|
|
this.writechunk = null;
|
|
this.writeencoding = null;
|
|
}
|
|
|
|
function afterTransform(stream, er, data) {
|
|
var ts = stream._transformState;
|
|
ts.transforming = false;
|
|
|
|
var cb = ts.writecb;
|
|
|
|
if (!cb) return stream.emit('error', new Error('no writecb in Transform class'));
|
|
|
|
ts.writechunk = null;
|
|
ts.writecb = null;
|
|
|
|
if (data !== null && data !== undefined) stream.push(data);
|
|
|
|
cb(er);
|
|
|
|
var rs = stream._readableState;
|
|
rs.reading = false;
|
|
if (rs.needReadable || rs.length < rs.highWaterMark) {
|
|
stream._read(rs.highWaterMark);
|
|
}
|
|
}
|
|
|
|
function Transform(options) {
|
|
if (!(this instanceof Transform)) return new Transform(options);
|
|
|
|
Duplex.call(this, options);
|
|
|
|
this._transformState = new TransformState(this);
|
|
|
|
var stream = this;
|
|
|
|
// start out asking for a readable event once data is transformed.
|
|
this._readableState.needReadable = true;
|
|
|
|
// we have implemented the _read method, and done the other things
|
|
// that Readable wants before the first _read call, so unset the
|
|
// sync guard flag.
|
|
this._readableState.sync = false;
|
|
|
|
if (options) {
|
|
if (typeof options.transform === 'function') this._transform = options.transform;
|
|
|
|
if (typeof options.flush === 'function') this._flush = options.flush;
|
|
}
|
|
|
|
// When the writable side finishes, then flush out anything remaining.
|
|
this.once('prefinish', function () {
|
|
if (typeof this._flush === 'function') this._flush(function (er, data) {
|
|
done(stream, er, data);
|
|
});else done(stream);
|
|
});
|
|
}
|
|
|
|
Transform.prototype.push = function (chunk, encoding) {
|
|
this._transformState.needTransform = false;
|
|
return Duplex.prototype.push.call(this, chunk, encoding);
|
|
};
|
|
|
|
// This is the part where you do stuff!
|
|
// override this function in implementation classes.
|
|
// 'chunk' is an input chunk.
|
|
//
|
|
// Call `push(newChunk)` to pass along transformed output
|
|
// to the readable side. You may call 'push' zero or more times.
|
|
//
|
|
// Call `cb(err)` when you are done with this chunk. If you pass
|
|
// an error, then that'll put the hurt on the whole operation. If you
|
|
// never call cb(), then you'll never get another chunk.
|
|
Transform.prototype._transform = function (chunk, encoding, cb) {
|
|
throw new Error('_transform() is not implemented');
|
|
};
|
|
|
|
Transform.prototype._write = function (chunk, encoding, cb) {
|
|
var ts = this._transformState;
|
|
ts.writecb = cb;
|
|
ts.writechunk = chunk;
|
|
ts.writeencoding = encoding;
|
|
if (!ts.transforming) {
|
|
var rs = this._readableState;
|
|
if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark);
|
|
}
|
|
};
|
|
|
|
// Doesn't matter what the args are here.
|
|
// _transform does all the work.
|
|
// That we got here means that the readable side wants more data.
|
|
Transform.prototype._read = function (n) {
|
|
var ts = this._transformState;
|
|
|
|
if (ts.writechunk !== null && ts.writecb && !ts.transforming) {
|
|
ts.transforming = true;
|
|
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
|
|
} else {
|
|
// mark that we need a transform, so that any data that comes in
|
|
// will get processed, now that we've asked for it.
|
|
ts.needTransform = true;
|
|
}
|
|
};
|
|
|
|
function done(stream, er, data) {
|
|
if (er) return stream.emit('error', er);
|
|
|
|
if (data !== null && data !== undefined) stream.push(data);
|
|
|
|
// if there's nothing in the write buffer, then that means
|
|
// that nothing more will ever be provided
|
|
var ws = stream._writableState;
|
|
var ts = stream._transformState;
|
|
|
|
if (ws.length) throw new Error('Calling transform done when ws.length != 0');
|
|
|
|
if (ts.transforming) throw new Error('Calling transform done when still transforming');
|
|
|
|
return stream.push(null);
|
|
}
|
|
},{"./_stream_duplex":266,"core-util-is":44,"inherits":87}],270:[function(require,module,exports){
|
|
(function (process){
|
|
// A bit simpler than readable streams.
|
|
// Implement an async ._write(chunk, encoding, cb), and it'll handle all
|
|
// the drain event emission and buffering.
|
|
|
|
'use strict';
|
|
|
|
module.exports = Writable;
|
|
|
|
/*<replacement>*/
|
|
var processNextTick = require('process-nextick-args');
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : processNextTick;
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var Duplex;
|
|
/*</replacement>*/
|
|
|
|
Writable.WritableState = WritableState;
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var internalUtil = {
|
|
deprecate: require('util-deprecate')
|
|
};
|
|
/*</replacement>*/
|
|
|
|
/*<replacement>*/
|
|
var Stream;
|
|
(function () {
|
|
try {
|
|
Stream = require('st' + 'ream');
|
|
} catch (_) {} finally {
|
|
if (!Stream) Stream = require('events').EventEmitter;
|
|
}
|
|
})();
|
|
/*</replacement>*/
|
|
|
|
var Buffer = require('buffer').Buffer;
|
|
/*<replacement>*/
|
|
var bufferShim = require('buffer-shims');
|
|
/*</replacement>*/
|
|
|
|
util.inherits(Writable, Stream);
|
|
|
|
function nop() {}
|
|
|
|
function WriteReq(chunk, encoding, cb) {
|
|
this.chunk = chunk;
|
|
this.encoding = encoding;
|
|
this.callback = cb;
|
|
this.next = null;
|
|
}
|
|
|
|
function WritableState(options, stream) {
|
|
Duplex = Duplex || require('./_stream_duplex');
|
|
|
|
options = options || {};
|
|
|
|
// object stream flag to indicate whether or not this stream
|
|
// contains buffers or objects.
|
|
this.objectMode = !!options.objectMode;
|
|
|
|
if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.writableObjectMode;
|
|
|
|
// the point at which write() starts returning false
|
|
// Note: 0 is a valid value, means that we always return false if
|
|
// the entire buffer is not flushed immediately on write()
|
|
var hwm = options.highWaterMark;
|
|
var defaultHwm = this.objectMode ? 16 : 16 * 1024;
|
|
this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm;
|
|
|
|
// cast to ints.
|
|
this.highWaterMark = ~ ~this.highWaterMark;
|
|
|
|
// drain event flag.
|
|
this.needDrain = false;
|
|
// at the start of calling end()
|
|
this.ending = false;
|
|
// when end() has been called, and returned
|
|
this.ended = false;
|
|
// when 'finish' is emitted
|
|
this.finished = false;
|
|
|
|
// should we decode strings into buffers before passing to _write?
|
|
// this is here so that some node-core streams can optimize string
|
|
// handling at a lower level.
|
|
var noDecode = options.decodeStrings === false;
|
|
this.decodeStrings = !noDecode;
|
|
|
|
// Crypto is kind of old and crusty. Historically, its default string
|
|
// encoding is 'binary' so we have to make this configurable.
|
|
// Everything else in the universe uses 'utf8', though.
|
|
this.defaultEncoding = options.defaultEncoding || 'utf8';
|
|
|
|
// not an actual buffer we keep track of, but a measurement
|
|
// of how much we're waiting to get pushed to some underlying
|
|
// socket or file.
|
|
this.length = 0;
|
|
|
|
// a flag to see when we're in the middle of a write.
|
|
this.writing = false;
|
|
|
|
// when true all writes will be buffered until .uncork() call
|
|
this.corked = 0;
|
|
|
|
// a flag to be able to tell if the onwrite cb is called immediately,
|
|
// or on a later tick. We set this to true at first, because any
|
|
// actions that shouldn't happen until "later" should generally also
|
|
// not happen before the first write call.
|
|
this.sync = true;
|
|
|
|
// a flag to know if we're processing previously buffered items, which
|
|
// may call the _write() callback in the same tick, so that we don't
|
|
// end up in an overlapped onwrite situation.
|
|
this.bufferProcessing = false;
|
|
|
|
// the callback that's passed to _write(chunk,cb)
|
|
this.onwrite = function (er) {
|
|
onwrite(stream, er);
|
|
};
|
|
|
|
// the callback that the user supplies to write(chunk,encoding,cb)
|
|
this.writecb = null;
|
|
|
|
// the amount that is being written when _write is called.
|
|
this.writelen = 0;
|
|
|
|
this.bufferedRequest = null;
|
|
this.lastBufferedRequest = null;
|
|
|
|
// number of pending user-supplied write callbacks
|
|
// this must be 0 before 'finish' can be emitted
|
|
this.pendingcb = 0;
|
|
|
|
// emit prefinish if the only thing we're waiting for is _write cbs
|
|
// This is relevant for synchronous Transform streams
|
|
this.prefinished = false;
|
|
|
|
// True if the error was already emitted and should not be thrown again
|
|
this.errorEmitted = false;
|
|
|
|
// count buffered requests
|
|
this.bufferedRequestCount = 0;
|
|
|
|
// allocate the first CorkedRequest, there is always
|
|
// one allocated and free to use, and we maintain at most two
|
|
this.corkedRequestsFree = new CorkedRequest(this);
|
|
}
|
|
|
|
WritableState.prototype.getBuffer = function getBuffer() {
|
|
var current = this.bufferedRequest;
|
|
var out = [];
|
|
while (current) {
|
|
out.push(current);
|
|
current = current.next;
|
|
}
|
|
return out;
|
|
};
|
|
|
|
(function () {
|
|
try {
|
|
Object.defineProperty(WritableState.prototype, 'buffer', {
|
|
get: internalUtil.deprecate(function () {
|
|
return this.getBuffer();
|
|
}, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.')
|
|
});
|
|
} catch (_) {}
|
|
})();
|
|
|
|
// Test _writableState for inheritance to account for Duplex streams,
|
|
// whose prototype chain only points to Readable.
|
|
var realHasInstance;
|
|
if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') {
|
|
realHasInstance = Function.prototype[Symbol.hasInstance];
|
|
Object.defineProperty(Writable, Symbol.hasInstance, {
|
|
value: function (object) {
|
|
if (realHasInstance.call(this, object)) return true;
|
|
|
|
return object && object._writableState instanceof WritableState;
|
|
}
|
|
});
|
|
} else {
|
|
realHasInstance = function (object) {
|
|
return object instanceof this;
|
|
};
|
|
}
|
|
|
|
function Writable(options) {
|
|
Duplex = Duplex || require('./_stream_duplex');
|
|
|
|
// Writable ctor is applied to Duplexes, too.
|
|
// `realHasInstance` is necessary because using plain `instanceof`
|
|
// would return false, as no `_writableState` property is attached.
|
|
|
|
// Trying to use the custom `instanceof` for Writable here will also break the
|
|
// Node.js LazyTransform implementation, which has a non-trivial getter for
|
|
// `_writableState` that would lead to infinite recursion.
|
|
if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) {
|
|
return new Writable(options);
|
|
}
|
|
|
|
this._writableState = new WritableState(options, this);
|
|
|
|
// legacy.
|
|
this.writable = true;
|
|
|
|
if (options) {
|
|
if (typeof options.write === 'function') this._write = options.write;
|
|
|
|
if (typeof options.writev === 'function') this._writev = options.writev;
|
|
}
|
|
|
|
Stream.call(this);
|
|
}
|
|
|
|
// Otherwise people can pipe Writable streams, which is just wrong.
|
|
Writable.prototype.pipe = function () {
|
|
this.emit('error', new Error('Cannot pipe, not readable'));
|
|
};
|
|
|
|
function writeAfterEnd(stream, cb) {
|
|
var er = new Error('write after end');
|
|
// TODO: defer error events consistently everywhere, not just the cb
|
|
stream.emit('error', er);
|
|
processNextTick(cb, er);
|
|
}
|
|
|
|
// If we get something that is not a buffer, string, null, or undefined,
|
|
// and we're not in objectMode, then that's an error.
|
|
// Otherwise stream chunks are all considered to be of length=1, and the
|
|
// watermarks determine how many objects to keep in the buffer, rather than
|
|
// how many bytes or characters.
|
|
function validChunk(stream, state, chunk, cb) {
|
|
var valid = true;
|
|
var er = false;
|
|
// Always throw error if a null is written
|
|
// if we are not in object mode then throw
|
|
// if it is not a buffer, string, or undefined.
|
|
if (chunk === null) {
|
|
er = new TypeError('May not write null values to stream');
|
|
} else if (!Buffer.isBuffer(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) {
|
|
er = new TypeError('Invalid non-string/buffer chunk');
|
|
}
|
|
if (er) {
|
|
stream.emit('error', er);
|
|
processNextTick(cb, er);
|
|
valid = false;
|
|
}
|
|
return valid;
|
|
}
|
|
|
|
Writable.prototype.write = function (chunk, encoding, cb) {
|
|
var state = this._writableState;
|
|
var ret = false;
|
|
|
|
if (typeof encoding === 'function') {
|
|
cb = encoding;
|
|
encoding = null;
|
|
}
|
|
|
|
if (Buffer.isBuffer(chunk)) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding;
|
|
|
|
if (typeof cb !== 'function') cb = nop;
|
|
|
|
if (state.ended) writeAfterEnd(this, cb);else if (validChunk(this, state, chunk, cb)) {
|
|
state.pendingcb++;
|
|
ret = writeOrBuffer(this, state, chunk, encoding, cb);
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
Writable.prototype.cork = function () {
|
|
var state = this._writableState;
|
|
|
|
state.corked++;
|
|
};
|
|
|
|
Writable.prototype.uncork = function () {
|
|
var state = this._writableState;
|
|
|
|
if (state.corked) {
|
|
state.corked--;
|
|
|
|
if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state);
|
|
}
|
|
};
|
|
|
|
Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {
|
|
// node::ParseEncoding() requires lower case.
|
|
if (typeof encoding === 'string') encoding = encoding.toLowerCase();
|
|
if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding);
|
|
this._writableState.defaultEncoding = encoding;
|
|
return this;
|
|
};
|
|
|
|
function decodeChunk(state, chunk, encoding) {
|
|
if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') {
|
|
chunk = bufferShim.from(chunk, encoding);
|
|
}
|
|
return chunk;
|
|
}
|
|
|
|
// if we're already writing something, then just put this
|
|
// in the queue, and wait our turn. Otherwise, call _write
|
|
// If we return false, then we need a drain event, so set that flag.
|
|
function writeOrBuffer(stream, state, chunk, encoding, cb) {
|
|
chunk = decodeChunk(state, chunk, encoding);
|
|
|
|
if (Buffer.isBuffer(chunk)) encoding = 'buffer';
|
|
var len = state.objectMode ? 1 : chunk.length;
|
|
|
|
state.length += len;
|
|
|
|
var ret = state.length < state.highWaterMark;
|
|
// we must ensure that previous needDrain will not be reset to false.
|
|
if (!ret) state.needDrain = true;
|
|
|
|
if (state.writing || state.corked) {
|
|
var last = state.lastBufferedRequest;
|
|
state.lastBufferedRequest = new WriteReq(chunk, encoding, cb);
|
|
if (last) {
|
|
last.next = state.lastBufferedRequest;
|
|
} else {
|
|
state.bufferedRequest = state.lastBufferedRequest;
|
|
}
|
|
state.bufferedRequestCount += 1;
|
|
} else {
|
|
doWrite(stream, state, false, len, chunk, encoding, cb);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
function doWrite(stream, state, writev, len, chunk, encoding, cb) {
|
|
state.writelen = len;
|
|
state.writecb = cb;
|
|
state.writing = true;
|
|
state.sync = true;
|
|
if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite);
|
|
state.sync = false;
|
|
}
|
|
|
|
function onwriteError(stream, state, sync, er, cb) {
|
|
--state.pendingcb;
|
|
if (sync) processNextTick(cb, er);else cb(er);
|
|
|
|
stream._writableState.errorEmitted = true;
|
|
stream.emit('error', er);
|
|
}
|
|
|
|
function onwriteStateUpdate(state) {
|
|
state.writing = false;
|
|
state.writecb = null;
|
|
state.length -= state.writelen;
|
|
state.writelen = 0;
|
|
}
|
|
|
|
function onwrite(stream, er) {
|
|
var state = stream._writableState;
|
|
var sync = state.sync;
|
|
var cb = state.writecb;
|
|
|
|
onwriteStateUpdate(state);
|
|
|
|
if (er) onwriteError(stream, state, sync, er, cb);else {
|
|
// Check if we're actually ready to finish, but don't emit yet
|
|
var finished = needFinish(state);
|
|
|
|
if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) {
|
|
clearBuffer(stream, state);
|
|
}
|
|
|
|
if (sync) {
|
|
/*<replacement>*/
|
|
asyncWrite(afterWrite, stream, state, finished, cb);
|
|
/*</replacement>*/
|
|
} else {
|
|
afterWrite(stream, state, finished, cb);
|
|
}
|
|
}
|
|
}
|
|
|
|
function afterWrite(stream, state, finished, cb) {
|
|
if (!finished) onwriteDrain(stream, state);
|
|
state.pendingcb--;
|
|
cb();
|
|
finishMaybe(stream, state);
|
|
}
|
|
|
|
// Must force callback to be called on nextTick, so that we don't
|
|
// emit 'drain' before the write() consumer gets the 'false' return
|
|
// value, and has a chance to attach a 'drain' listener.
|
|
function onwriteDrain(stream, state) {
|
|
if (state.length === 0 && state.needDrain) {
|
|
state.needDrain = false;
|
|
stream.emit('drain');
|
|
}
|
|
}
|
|
|
|
// if there's something in the buffer waiting, then process it
|
|
function clearBuffer(stream, state) {
|
|
state.bufferProcessing = true;
|
|
var entry = state.bufferedRequest;
|
|
|
|
if (stream._writev && entry && entry.next) {
|
|
// Fast case, write everything using _writev()
|
|
var l = state.bufferedRequestCount;
|
|
var buffer = new Array(l);
|
|
var holder = state.corkedRequestsFree;
|
|
holder.entry = entry;
|
|
|
|
var count = 0;
|
|
while (entry) {
|
|
buffer[count] = entry;
|
|
entry = entry.next;
|
|
count += 1;
|
|
}
|
|
|
|
doWrite(stream, state, true, state.length, buffer, '', holder.finish);
|
|
|
|
// doWrite is almost always async, defer these to save a bit of time
|
|
// as the hot path ends with doWrite
|
|
state.pendingcb++;
|
|
state.lastBufferedRequest = null;
|
|
if (holder.next) {
|
|
state.corkedRequestsFree = holder.next;
|
|
holder.next = null;
|
|
} else {
|
|
state.corkedRequestsFree = new CorkedRequest(state);
|
|
}
|
|
} else {
|
|
// Slow case, write chunks one-by-one
|
|
while (entry) {
|
|
var chunk = entry.chunk;
|
|
var encoding = entry.encoding;
|
|
var cb = entry.callback;
|
|
var len = state.objectMode ? 1 : chunk.length;
|
|
|
|
doWrite(stream, state, false, len, chunk, encoding, cb);
|
|
entry = entry.next;
|
|
// if we didn't call the onwrite immediately, then
|
|
// it means that we need to wait until it does.
|
|
// also, that means that the chunk and cb are currently
|
|
// being processed, so move the buffer counter past them.
|
|
if (state.writing) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (entry === null) state.lastBufferedRequest = null;
|
|
}
|
|
|
|
state.bufferedRequestCount = 0;
|
|
state.bufferedRequest = entry;
|
|
state.bufferProcessing = false;
|
|
}
|
|
|
|
Writable.prototype._write = function (chunk, encoding, cb) {
|
|
cb(new Error('_write() is not implemented'));
|
|
};
|
|
|
|
Writable.prototype._writev = null;
|
|
|
|
Writable.prototype.end = function (chunk, encoding, cb) {
|
|
var state = this._writableState;
|
|
|
|
if (typeof chunk === 'function') {
|
|
cb = chunk;
|
|
chunk = null;
|
|
encoding = null;
|
|
} else if (typeof encoding === 'function') {
|
|
cb = encoding;
|
|
encoding = null;
|
|
}
|
|
|
|
if (chunk !== null && chunk !== undefined) this.write(chunk, encoding);
|
|
|
|
// .end() fully uncorks
|
|
if (state.corked) {
|
|
state.corked = 1;
|
|
this.uncork();
|
|
}
|
|
|
|
// ignore unnecessary end() calls.
|
|
if (!state.ending && !state.finished) endWritable(this, state, cb);
|
|
};
|
|
|
|
function needFinish(state) {
|
|
return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing;
|
|
}
|
|
|
|
function prefinish(stream, state) {
|
|
if (!state.prefinished) {
|
|
state.prefinished = true;
|
|
stream.emit('prefinish');
|
|
}
|
|
}
|
|
|
|
function finishMaybe(stream, state) {
|
|
var need = needFinish(state);
|
|
if (need) {
|
|
if (state.pendingcb === 0) {
|
|
prefinish(stream, state);
|
|
state.finished = true;
|
|
stream.emit('finish');
|
|
} else {
|
|
prefinish(stream, state);
|
|
}
|
|
}
|
|
return need;
|
|
}
|
|
|
|
function endWritable(stream, state, cb) {
|
|
state.ending = true;
|
|
finishMaybe(stream, state);
|
|
if (cb) {
|
|
if (state.finished) processNextTick(cb);else stream.once('finish', cb);
|
|
}
|
|
state.ended = true;
|
|
stream.writable = false;
|
|
}
|
|
|
|
// It seems a linked list but it is not
|
|
// there will be only 2 of these for each stream
|
|
function CorkedRequest(state) {
|
|
var _this = this;
|
|
|
|
this.next = null;
|
|
this.entry = null;
|
|
|
|
this.finish = function (err) {
|
|
var entry = _this.entry;
|
|
_this.entry = null;
|
|
while (entry) {
|
|
var cb = entry.callback;
|
|
state.pendingcb--;
|
|
cb(err);
|
|
entry = entry.next;
|
|
}
|
|
if (state.corkedRequestsFree) {
|
|
state.corkedRequestsFree.next = _this;
|
|
} else {
|
|
state.corkedRequestsFree = _this;
|
|
}
|
|
};
|
|
}
|
|
}).call(this,require('_process'))
|
|
},{"./_stream_duplex":266,"_process":133,"buffer":43,"buffer-shims":42,"core-util-is":44,"events":56,"inherits":87,"process-nextick-args":132,"util-deprecate":294}],271:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var Buffer = require('buffer').Buffer;
|
|
/*<replacement>*/
|
|
var bufferShim = require('buffer-shims');
|
|
/*</replacement>*/
|
|
|
|
module.exports = BufferList;
|
|
|
|
function BufferList() {
|
|
this.head = null;
|
|
this.tail = null;
|
|
this.length = 0;
|
|
}
|
|
|
|
BufferList.prototype.push = function (v) {
|
|
var entry = { data: v, next: null };
|
|
if (this.length > 0) this.tail.next = entry;else this.head = entry;
|
|
this.tail = entry;
|
|
++this.length;
|
|
};
|
|
|
|
BufferList.prototype.unshift = function (v) {
|
|
var entry = { data: v, next: this.head };
|
|
if (this.length === 0) this.tail = entry;
|
|
this.head = entry;
|
|
++this.length;
|
|
};
|
|
|
|
BufferList.prototype.shift = function () {
|
|
if (this.length === 0) return;
|
|
var ret = this.head.data;
|
|
if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next;
|
|
--this.length;
|
|
return ret;
|
|
};
|
|
|
|
BufferList.prototype.clear = function () {
|
|
this.head = this.tail = null;
|
|
this.length = 0;
|
|
};
|
|
|
|
BufferList.prototype.join = function (s) {
|
|
if (this.length === 0) return '';
|
|
var p = this.head;
|
|
var ret = '' + p.data;
|
|
while (p = p.next) {
|
|
ret += s + p.data;
|
|
}return ret;
|
|
};
|
|
|
|
BufferList.prototype.concat = function (n) {
|
|
if (this.length === 0) return bufferShim.alloc(0);
|
|
if (this.length === 1) return this.head.data;
|
|
var ret = bufferShim.allocUnsafe(n >>> 0);
|
|
var p = this.head;
|
|
var i = 0;
|
|
while (p) {
|
|
p.data.copy(ret, i);
|
|
i += p.data.length;
|
|
p = p.next;
|
|
}
|
|
return ret;
|
|
};
|
|
},{"buffer":43,"buffer-shims":42}],272:[function(require,module,exports){
|
|
module.exports = require("./lib/_stream_passthrough.js")
|
|
|
|
},{"./lib/_stream_passthrough.js":267}],273:[function(require,module,exports){
|
|
(function (process){
|
|
var Stream = (function (){
|
|
try {
|
|
return require('st' + 'ream'); // hack to fix a circular dependency issue when used with browserify
|
|
} catch(_){}
|
|
}());
|
|
exports = module.exports = require('./lib/_stream_readable.js');
|
|
exports.Stream = Stream || exports;
|
|
exports.Readable = exports;
|
|
exports.Writable = require('./lib/_stream_writable.js');
|
|
exports.Duplex = require('./lib/_stream_duplex.js');
|
|
exports.Transform = require('./lib/_stream_transform.js');
|
|
exports.PassThrough = require('./lib/_stream_passthrough.js');
|
|
|
|
if (!process.browser && process.env.READABLE_STREAM === 'disable' && Stream) {
|
|
module.exports = Stream;
|
|
}
|
|
|
|
}).call(this,require('_process'))
|
|
},{"./lib/_stream_duplex.js":266,"./lib/_stream_passthrough.js":267,"./lib/_stream_readable.js":268,"./lib/_stream_transform.js":269,"./lib/_stream_writable.js":270,"_process":133}],274:[function(require,module,exports){
|
|
arguments[4][130][0].apply(exports,arguments)
|
|
},{"./lib/_stream_transform.js":269,"dup":130}],275:[function(require,module,exports){
|
|
module.exports = require("./lib/_stream_writable.js")
|
|
|
|
},{"./lib/_stream_writable.js":270}],276:[function(require,module,exports){
|
|
// Generated by CoffeeScript 1.9.2
|
|
(function() {
|
|
var hasProp = {}.hasOwnProperty,
|
|
slice = [].slice;
|
|
|
|
module.exports = function(source, scope) {
|
|
var key, keys, value, values;
|
|
keys = [];
|
|
values = [];
|
|
for (key in scope) {
|
|
if (!hasProp.call(scope, key)) continue;
|
|
value = scope[key];
|
|
if (key === 'this') {
|
|
continue;
|
|
}
|
|
keys.push(key);
|
|
values.push(value);
|
|
}
|
|
return Function.apply(null, slice.call(keys).concat([source])).apply(scope["this"], values);
|
|
};
|
|
|
|
}).call(this);
|
|
|
|
},{}],277:[function(require,module,exports){
|
|
(function (factory) {
|
|
if (typeof exports === 'object') {
|
|
// Node/CommonJS
|
|
module.exports = factory();
|
|
} else if (typeof define === 'function' && define.amd) {
|
|
// AMD
|
|
define(factory);
|
|
} else {
|
|
// Browser globals (with support for web workers)
|
|
var glob;
|
|
|
|
try {
|
|
glob = window;
|
|
} catch (e) {
|
|
glob = self;
|
|
}
|
|
|
|
glob.SparkMD5 = factory();
|
|
}
|
|
}(function (undefined) {
|
|
|
|
'use strict';
|
|
|
|
/*
|
|
* Fastest md5 implementation around (JKM md5).
|
|
* Credits: Joseph Myers
|
|
*
|
|
* @see http://www.myersdaily.org/joseph/javascript/md5-text.html
|
|
* @see http://jsperf.com/md5-shootout/7
|
|
*/
|
|
|
|
/* this function is much faster,
|
|
so if possible we use it. Some IEs
|
|
are the only ones I know of that
|
|
need the idiotic second function,
|
|
generated by an if clause. */
|
|
var add32 = function (a, b) {
|
|
return (a + b) & 0xFFFFFFFF;
|
|
},
|
|
hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
|
|
|
|
|
|
function cmn(q, a, b, x, s, t) {
|
|
a = add32(add32(a, q), add32(x, t));
|
|
return add32((a << s) | (a >>> (32 - s)), b);
|
|
}
|
|
|
|
function ff(a, b, c, d, x, s, t) {
|
|
return cmn((b & c) | ((~b) & d), a, b, x, s, t);
|
|
}
|
|
|
|
function gg(a, b, c, d, x, s, t) {
|
|
return cmn((b & d) | (c & (~d)), a, b, x, s, t);
|
|
}
|
|
|
|
function hh(a, b, c, d, x, s, t) {
|
|
return cmn(b ^ c ^ d, a, b, x, s, t);
|
|
}
|
|
|
|
function ii(a, b, c, d, x, s, t) {
|
|
return cmn(c ^ (b | (~d)), a, b, x, s, t);
|
|
}
|
|
|
|
function md5cycle(x, k) {
|
|
var a = x[0],
|
|
b = x[1],
|
|
c = x[2],
|
|
d = x[3];
|
|
|
|
a = ff(a, b, c, d, k[0], 7, -680876936);
|
|
d = ff(d, a, b, c, k[1], 12, -389564586);
|
|
c = ff(c, d, a, b, k[2], 17, 606105819);
|
|
b = ff(b, c, d, a, k[3], 22, -1044525330);
|
|
a = ff(a, b, c, d, k[4], 7, -176418897);
|
|
d = ff(d, a, b, c, k[5], 12, 1200080426);
|
|
c = ff(c, d, a, b, k[6], 17, -1473231341);
|
|
b = ff(b, c, d, a, k[7], 22, -45705983);
|
|
a = ff(a, b, c, d, k[8], 7, 1770035416);
|
|
d = ff(d, a, b, c, k[9], 12, -1958414417);
|
|
c = ff(c, d, a, b, k[10], 17, -42063);
|
|
b = ff(b, c, d, a, k[11], 22, -1990404162);
|
|
a = ff(a, b, c, d, k[12], 7, 1804603682);
|
|
d = ff(d, a, b, c, k[13], 12, -40341101);
|
|
c = ff(c, d, a, b, k[14], 17, -1502002290);
|
|
b = ff(b, c, d, a, k[15], 22, 1236535329);
|
|
|
|
a = gg(a, b, c, d, k[1], 5, -165796510);
|
|
d = gg(d, a, b, c, k[6], 9, -1069501632);
|
|
c = gg(c, d, a, b, k[11], 14, 643717713);
|
|
b = gg(b, c, d, a, k[0], 20, -373897302);
|
|
a = gg(a, b, c, d, k[5], 5, -701558691);
|
|
d = gg(d, a, b, c, k[10], 9, 38016083);
|
|
c = gg(c, d, a, b, k[15], 14, -660478335);
|
|
b = gg(b, c, d, a, k[4], 20, -405537848);
|
|
a = gg(a, b, c, d, k[9], 5, 568446438);
|
|
d = gg(d, a, b, c, k[14], 9, -1019803690);
|
|
c = gg(c, d, a, b, k[3], 14, -187363961);
|
|
b = gg(b, c, d, a, k[8], 20, 1163531501);
|
|
a = gg(a, b, c, d, k[13], 5, -1444681467);
|
|
d = gg(d, a, b, c, k[2], 9, -51403784);
|
|
c = gg(c, d, a, b, k[7], 14, 1735328473);
|
|
b = gg(b, c, d, a, k[12], 20, -1926607734);
|
|
|
|
a = hh(a, b, c, d, k[5], 4, -378558);
|
|
d = hh(d, a, b, c, k[8], 11, -2022574463);
|
|
c = hh(c, d, a, b, k[11], 16, 1839030562);
|
|
b = hh(b, c, d, a, k[14], 23, -35309556);
|
|
a = hh(a, b, c, d, k[1], 4, -1530992060);
|
|
d = hh(d, a, b, c, k[4], 11, 1272893353);
|
|
c = hh(c, d, a, b, k[7], 16, -155497632);
|
|
b = hh(b, c, d, a, k[10], 23, -1094730640);
|
|
a = hh(a, b, c, d, k[13], 4, 681279174);
|
|
d = hh(d, a, b, c, k[0], 11, -358537222);
|
|
c = hh(c, d, a, b, k[3], 16, -722521979);
|
|
b = hh(b, c, d, a, k[6], 23, 76029189);
|
|
a = hh(a, b, c, d, k[9], 4, -640364487);
|
|
d = hh(d, a, b, c, k[12], 11, -421815835);
|
|
c = hh(c, d, a, b, k[15], 16, 530742520);
|
|
b = hh(b, c, d, a, k[2], 23, -995338651);
|
|
|
|
a = ii(a, b, c, d, k[0], 6, -198630844);
|
|
d = ii(d, a, b, c, k[7], 10, 1126891415);
|
|
c = ii(c, d, a, b, k[14], 15, -1416354905);
|
|
b = ii(b, c, d, a, k[5], 21, -57434055);
|
|
a = ii(a, b, c, d, k[12], 6, 1700485571);
|
|
d = ii(d, a, b, c, k[3], 10, -1894986606);
|
|
c = ii(c, d, a, b, k[10], 15, -1051523);
|
|
b = ii(b, c, d, a, k[1], 21, -2054922799);
|
|
a = ii(a, b, c, d, k[8], 6, 1873313359);
|
|
d = ii(d, a, b, c, k[15], 10, -30611744);
|
|
c = ii(c, d, a, b, k[6], 15, -1560198380);
|
|
b = ii(b, c, d, a, k[13], 21, 1309151649);
|
|
a = ii(a, b, c, d, k[4], 6, -145523070);
|
|
d = ii(d, a, b, c, k[11], 10, -1120210379);
|
|
c = ii(c, d, a, b, k[2], 15, 718787259);
|
|
b = ii(b, c, d, a, k[9], 21, -343485551);
|
|
|
|
x[0] = add32(a, x[0]);
|
|
x[1] = add32(b, x[1]);
|
|
x[2] = add32(c, x[2]);
|
|
x[3] = add32(d, x[3]);
|
|
}
|
|
|
|
function md5blk(s) {
|
|
var md5blks = [],
|
|
i; /* Andy King said do it this way. */
|
|
|
|
for (i = 0; i < 64; i += 4) {
|
|
md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
|
|
}
|
|
return md5blks;
|
|
}
|
|
|
|
function md5blk_array(a) {
|
|
var md5blks = [],
|
|
i; /* Andy King said do it this way. */
|
|
|
|
for (i = 0; i < 64; i += 4) {
|
|
md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
|
|
}
|
|
return md5blks;
|
|
}
|
|
|
|
function md51(s) {
|
|
var n = s.length,
|
|
state = [1732584193, -271733879, -1732584194, 271733878],
|
|
i,
|
|
length,
|
|
tail,
|
|
tmp,
|
|
lo,
|
|
hi;
|
|
|
|
for (i = 64; i <= n; i += 64) {
|
|
md5cycle(state, md5blk(s.substring(i - 64, i)));
|
|
}
|
|
s = s.substring(i - 64);
|
|
length = s.length;
|
|
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
for (i = 0; i < length; i += 1) {
|
|
tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
|
|
}
|
|
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
|
|
if (i > 55) {
|
|
md5cycle(state, tail);
|
|
for (i = 0; i < 16; i += 1) {
|
|
tail[i] = 0;
|
|
}
|
|
}
|
|
|
|
// Beware that the final length might not fit in 32 bits so we take care of that
|
|
tmp = n * 8;
|
|
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
|
lo = parseInt(tmp[2], 16);
|
|
hi = parseInt(tmp[1], 16) || 0;
|
|
|
|
tail[14] = lo;
|
|
tail[15] = hi;
|
|
|
|
md5cycle(state, tail);
|
|
return state;
|
|
}
|
|
|
|
function md51_array(a) {
|
|
var n = a.length,
|
|
state = [1732584193, -271733879, -1732584194, 271733878],
|
|
i,
|
|
length,
|
|
tail,
|
|
tmp,
|
|
lo,
|
|
hi;
|
|
|
|
for (i = 64; i <= n; i += 64) {
|
|
md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
|
|
}
|
|
|
|
// Not sure if it is a bug, however IE10 will always produce a sub array of length 1
|
|
// containing the last element of the parent array if the sub array specified starts
|
|
// beyond the length of the parent array - weird.
|
|
// https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue
|
|
a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);
|
|
|
|
length = a.length;
|
|
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
for (i = 0; i < length; i += 1) {
|
|
tail[i >> 2] |= a[i] << ((i % 4) << 3);
|
|
}
|
|
|
|
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
|
|
if (i > 55) {
|
|
md5cycle(state, tail);
|
|
for (i = 0; i < 16; i += 1) {
|
|
tail[i] = 0;
|
|
}
|
|
}
|
|
|
|
// Beware that the final length might not fit in 32 bits so we take care of that
|
|
tmp = n * 8;
|
|
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
|
lo = parseInt(tmp[2], 16);
|
|
hi = parseInt(tmp[1], 16) || 0;
|
|
|
|
tail[14] = lo;
|
|
tail[15] = hi;
|
|
|
|
md5cycle(state, tail);
|
|
|
|
return state;
|
|
}
|
|
|
|
function rhex(n) {
|
|
var s = '',
|
|
j;
|
|
for (j = 0; j < 4; j += 1) {
|
|
s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
|
|
}
|
|
return s;
|
|
}
|
|
|
|
function hex(x) {
|
|
var i;
|
|
for (i = 0; i < x.length; i += 1) {
|
|
x[i] = rhex(x[i]);
|
|
}
|
|
return x.join('');
|
|
}
|
|
|
|
// In some cases the fast add32 function cannot be used..
|
|
if (hex(md51('hello')) !== '5d41402abc4b2a76b9719d911017c592') {
|
|
add32 = function (x, y) {
|
|
var lsw = (x & 0xFFFF) + (y & 0xFFFF),
|
|
msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
|
return (msw << 16) | (lsw & 0xFFFF);
|
|
};
|
|
}
|
|
|
|
// ---------------------------------------------------
|
|
|
|
/**
|
|
* ArrayBuffer slice polyfill.
|
|
*
|
|
* @see https://github.com/ttaubert/node-arraybuffer-slice
|
|
*/
|
|
|
|
if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {
|
|
(function () {
|
|
function clamp(val, length) {
|
|
val = (val | 0) || 0;
|
|
|
|
if (val < 0) {
|
|
return Math.max(val + length, 0);
|
|
}
|
|
|
|
return Math.min(val, length);
|
|
}
|
|
|
|
ArrayBuffer.prototype.slice = function (from, to) {
|
|
var length = this.byteLength,
|
|
begin = clamp(from, length),
|
|
end = length,
|
|
num,
|
|
target,
|
|
targetArray,
|
|
sourceArray;
|
|
|
|
if (to !== undefined) {
|
|
end = clamp(to, length);
|
|
}
|
|
|
|
if (begin > end) {
|
|
return new ArrayBuffer(0);
|
|
}
|
|
|
|
num = end - begin;
|
|
target = new ArrayBuffer(num);
|
|
targetArray = new Uint8Array(target);
|
|
|
|
sourceArray = new Uint8Array(this, begin, num);
|
|
targetArray.set(sourceArray);
|
|
|
|
return target;
|
|
};
|
|
})();
|
|
}
|
|
|
|
// ---------------------------------------------------
|
|
|
|
/**
|
|
* Helpers.
|
|
*/
|
|
|
|
function toUtf8(str) {
|
|
if (/[\u0080-\uFFFF]/.test(str)) {
|
|
str = unescape(encodeURIComponent(str));
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
function utf8Str2ArrayBuffer(str, returnUInt8Array) {
|
|
var length = str.length,
|
|
buff = new ArrayBuffer(length),
|
|
arr = new Uint8Array(buff),
|
|
i;
|
|
|
|
for (i = 0; i < length; i += 1) {
|
|
arr[i] = str.charCodeAt(i);
|
|
}
|
|
|
|
return returnUInt8Array ? arr : buff;
|
|
}
|
|
|
|
function arrayBuffer2Utf8Str(buff) {
|
|
return String.fromCharCode.apply(null, new Uint8Array(buff));
|
|
}
|
|
|
|
function concatenateArrayBuffers(first, second, returnUInt8Array) {
|
|
var result = new Uint8Array(first.byteLength + second.byteLength);
|
|
|
|
result.set(new Uint8Array(first));
|
|
result.set(new Uint8Array(second), first.byteLength);
|
|
|
|
return returnUInt8Array ? result : result.buffer;
|
|
}
|
|
|
|
function hexToBinaryString(hex) {
|
|
var bytes = [],
|
|
length = hex.length,
|
|
x;
|
|
|
|
for (x = 0; x < length - 1; x += 2) {
|
|
bytes.push(parseInt(hex.substr(x, 2), 16));
|
|
}
|
|
|
|
return String.fromCharCode.apply(String, bytes);
|
|
}
|
|
|
|
// ---------------------------------------------------
|
|
|
|
/**
|
|
* SparkMD5 OOP implementation.
|
|
*
|
|
* Use this class to perform an incremental md5, otherwise use the
|
|
* static methods instead.
|
|
*/
|
|
|
|
function SparkMD5() {
|
|
// call reset to init the instance
|
|
this.reset();
|
|
}
|
|
|
|
/**
|
|
* Appends a string.
|
|
* A conversion will be applied if an utf8 string is detected.
|
|
*
|
|
* @param {String} str The string to be appended
|
|
*
|
|
* @return {SparkMD5} The instance itself
|
|
*/
|
|
SparkMD5.prototype.append = function (str) {
|
|
// Converts the string to utf8 bytes if necessary
|
|
// Then append as binary
|
|
this.appendBinary(toUtf8(str));
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Appends a binary string.
|
|
*
|
|
* @param {String} contents The binary string to be appended
|
|
*
|
|
* @return {SparkMD5} The instance itself
|
|
*/
|
|
SparkMD5.prototype.appendBinary = function (contents) {
|
|
this._buff += contents;
|
|
this._length += contents.length;
|
|
|
|
var length = this._buff.length,
|
|
i;
|
|
|
|
for (i = 64; i <= length; i += 64) {
|
|
md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
|
|
}
|
|
|
|
this._buff = this._buff.substring(i - 64);
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Finishes the incremental computation, reseting the internal state and
|
|
* returning the result.
|
|
*
|
|
* @param {Boolean} raw True to get the raw string, false to get the hex string
|
|
*
|
|
* @return {String} The result
|
|
*/
|
|
SparkMD5.prototype.end = function (raw) {
|
|
var buff = this._buff,
|
|
length = buff.length,
|
|
i,
|
|
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
ret;
|
|
|
|
for (i = 0; i < length; i += 1) {
|
|
tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);
|
|
}
|
|
|
|
this._finish(tail, length);
|
|
ret = hex(this._hash);
|
|
|
|
if (raw) {
|
|
ret = hexToBinaryString(ret);
|
|
}
|
|
|
|
this.reset();
|
|
|
|
return ret;
|
|
};
|
|
|
|
/**
|
|
* Resets the internal state of the computation.
|
|
*
|
|
* @return {SparkMD5} The instance itself
|
|
*/
|
|
SparkMD5.prototype.reset = function () {
|
|
this._buff = '';
|
|
this._length = 0;
|
|
this._hash = [1732584193, -271733879, -1732584194, 271733878];
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Gets the internal state of the computation.
|
|
*
|
|
* @return {Object} The state
|
|
*/
|
|
SparkMD5.prototype.getState = function () {
|
|
return {
|
|
buff: this._buff,
|
|
length: this._length,
|
|
hash: this._hash
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Gets the internal state of the computation.
|
|
*
|
|
* @param {Object} state The state
|
|
*
|
|
* @return {SparkMD5} The instance itself
|
|
*/
|
|
SparkMD5.prototype.setState = function (state) {
|
|
this._buff = state.buff;
|
|
this._length = state.length;
|
|
this._hash = state.hash;
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Releases memory used by the incremental buffer and other additional
|
|
* resources. If you plan to use the instance again, use reset instead.
|
|
*/
|
|
SparkMD5.prototype.destroy = function () {
|
|
delete this._hash;
|
|
delete this._buff;
|
|
delete this._length;
|
|
};
|
|
|
|
/**
|
|
* Finish the final calculation based on the tail.
|
|
*
|
|
* @param {Array} tail The tail (will be modified)
|
|
* @param {Number} length The length of the remaining buffer
|
|
*/
|
|
SparkMD5.prototype._finish = function (tail, length) {
|
|
var i = length,
|
|
tmp,
|
|
lo,
|
|
hi;
|
|
|
|
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
|
|
if (i > 55) {
|
|
md5cycle(this._hash, tail);
|
|
for (i = 0; i < 16; i += 1) {
|
|
tail[i] = 0;
|
|
}
|
|
}
|
|
|
|
// Do the final computation based on the tail and length
|
|
// Beware that the final length may not fit in 32 bits so we take care of that
|
|
tmp = this._length * 8;
|
|
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
|
lo = parseInt(tmp[2], 16);
|
|
hi = parseInt(tmp[1], 16) || 0;
|
|
|
|
tail[14] = lo;
|
|
tail[15] = hi;
|
|
md5cycle(this._hash, tail);
|
|
};
|
|
|
|
/**
|
|
* Performs the md5 hash on a string.
|
|
* A conversion will be applied if utf8 string is detected.
|
|
*
|
|
* @param {String} str The string
|
|
* @param {Boolean} raw True to get the raw string, false to get the hex string
|
|
*
|
|
* @return {String} The result
|
|
*/
|
|
SparkMD5.hash = function (str, raw) {
|
|
// Converts the string to utf8 bytes if necessary
|
|
// Then compute it using the binary function
|
|
return SparkMD5.hashBinary(toUtf8(str), raw);
|
|
};
|
|
|
|
/**
|
|
* Performs the md5 hash on a binary string.
|
|
*
|
|
* @param {String} content The binary string
|
|
* @param {Boolean} raw True to get the raw string, false to get the hex string
|
|
*
|
|
* @return {String} The result
|
|
*/
|
|
SparkMD5.hashBinary = function (content, raw) {
|
|
var hash = md51(content),
|
|
ret = hex(hash);
|
|
|
|
return raw ? hexToBinaryString(ret) : ret;
|
|
};
|
|
|
|
// ---------------------------------------------------
|
|
|
|
/**
|
|
* SparkMD5 OOP implementation for array buffers.
|
|
*
|
|
* Use this class to perform an incremental md5 ONLY for array buffers.
|
|
*/
|
|
SparkMD5.ArrayBuffer = function () {
|
|
// call reset to init the instance
|
|
this.reset();
|
|
};
|
|
|
|
/**
|
|
* Appends an array buffer.
|
|
*
|
|
* @param {ArrayBuffer} arr The array to be appended
|
|
*
|
|
* @return {SparkMD5.ArrayBuffer} The instance itself
|
|
*/
|
|
SparkMD5.ArrayBuffer.prototype.append = function (arr) {
|
|
var buff = concatenateArrayBuffers(this._buff.buffer, arr, true),
|
|
length = buff.length,
|
|
i;
|
|
|
|
this._length += arr.byteLength;
|
|
|
|
for (i = 64; i <= length; i += 64) {
|
|
md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
|
|
}
|
|
|
|
this._buff = (i - 64) < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Finishes the incremental computation, reseting the internal state and
|
|
* returning the result.
|
|
*
|
|
* @param {Boolean} raw True to get the raw string, false to get the hex string
|
|
*
|
|
* @return {String} The result
|
|
*/
|
|
SparkMD5.ArrayBuffer.prototype.end = function (raw) {
|
|
var buff = this._buff,
|
|
length = buff.length,
|
|
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
i,
|
|
ret;
|
|
|
|
for (i = 0; i < length; i += 1) {
|
|
tail[i >> 2] |= buff[i] << ((i % 4) << 3);
|
|
}
|
|
|
|
this._finish(tail, length);
|
|
ret = hex(this._hash);
|
|
|
|
if (raw) {
|
|
ret = hexToBinaryString(ret);
|
|
}
|
|
|
|
this.reset();
|
|
|
|
return ret;
|
|
};
|
|
|
|
/**
|
|
* Resets the internal state of the computation.
|
|
*
|
|
* @return {SparkMD5.ArrayBuffer} The instance itself
|
|
*/
|
|
SparkMD5.ArrayBuffer.prototype.reset = function () {
|
|
this._buff = new Uint8Array(0);
|
|
this._length = 0;
|
|
this._hash = [1732584193, -271733879, -1732584194, 271733878];
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Gets the internal state of the computation.
|
|
*
|
|
* @return {Object} The state
|
|
*/
|
|
SparkMD5.ArrayBuffer.prototype.getState = function () {
|
|
var state = SparkMD5.prototype.getState.call(this);
|
|
|
|
// Convert buffer to a string
|
|
state.buff = arrayBuffer2Utf8Str(state.buff);
|
|
|
|
return state;
|
|
};
|
|
|
|
/**
|
|
* Gets the internal state of the computation.
|
|
*
|
|
* @param {Object} state The state
|
|
*
|
|
* @return {SparkMD5.ArrayBuffer} The instance itself
|
|
*/
|
|
SparkMD5.ArrayBuffer.prototype.setState = function (state) {
|
|
// Convert string to buffer
|
|
state.buff = utf8Str2ArrayBuffer(state.buff, true);
|
|
|
|
return SparkMD5.prototype.setState.call(this, state);
|
|
};
|
|
|
|
SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
|
|
|
|
SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
|
|
|
|
/**
|
|
* Performs the md5 hash on an array buffer.
|
|
*
|
|
* @param {ArrayBuffer} arr The array buffer
|
|
* @param {Boolean} raw True to get the raw string, false to get the hex one
|
|
*
|
|
* @return {String} The result
|
|
*/
|
|
SparkMD5.ArrayBuffer.hash = function (arr, raw) {
|
|
var hash = md51_array(new Uint8Array(arr)),
|
|
ret = hex(hash);
|
|
|
|
return raw ? hexToBinaryString(ret) : ret;
|
|
};
|
|
|
|
return SparkMD5;
|
|
}));
|
|
|
|
},{}],278:[function(require,module,exports){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
module.exports = Stream;
|
|
|
|
var EE = require('events').EventEmitter;
|
|
var inherits = require('inherits');
|
|
|
|
inherits(Stream, EE);
|
|
Stream.Readable = require('readable-stream/readable.js');
|
|
Stream.Writable = require('readable-stream/writable.js');
|
|
Stream.Duplex = require('readable-stream/duplex.js');
|
|
Stream.Transform = require('readable-stream/transform.js');
|
|
Stream.PassThrough = require('readable-stream/passthrough.js');
|
|
|
|
// Backwards-compat with node 0.4.x
|
|
Stream.Stream = Stream;
|
|
|
|
|
|
|
|
// old-style streams. Note that the pipe method (the only relevant
|
|
// part of this class) is overridden in the Readable class.
|
|
|
|
function Stream() {
|
|
EE.call(this);
|
|
}
|
|
|
|
Stream.prototype.pipe = function(dest, options) {
|
|
var source = this;
|
|
|
|
function ondata(chunk) {
|
|
if (dest.writable) {
|
|
if (false === dest.write(chunk) && source.pause) {
|
|
source.pause();
|
|
}
|
|
}
|
|
}
|
|
|
|
source.on('data', ondata);
|
|
|
|
function ondrain() {
|
|
if (source.readable && source.resume) {
|
|
source.resume();
|
|
}
|
|
}
|
|
|
|
dest.on('drain', ondrain);
|
|
|
|
// If the 'end' option is not supplied, dest.end() will be called when
|
|
// source gets the 'end' or 'close' events. Only dest.end() once.
|
|
if (!dest._isStdio && (!options || options.end !== false)) {
|
|
source.on('end', onend);
|
|
source.on('close', onclose);
|
|
}
|
|
|
|
var didOnEnd = false;
|
|
function onend() {
|
|
if (didOnEnd) return;
|
|
didOnEnd = true;
|
|
|
|
dest.end();
|
|
}
|
|
|
|
|
|
function onclose() {
|
|
if (didOnEnd) return;
|
|
didOnEnd = true;
|
|
|
|
if (typeof dest.destroy === 'function') dest.destroy();
|
|
}
|
|
|
|
// don't leave dangling pipes when there are errors.
|
|
function onerror(er) {
|
|
cleanup();
|
|
if (EE.listenerCount(this, 'error') === 0) {
|
|
throw er; // Unhandled stream error in pipe.
|
|
}
|
|
}
|
|
|
|
source.on('error', onerror);
|
|
dest.on('error', onerror);
|
|
|
|
// remove all the event listeners that were added.
|
|
function cleanup() {
|
|
source.removeListener('data', ondata);
|
|
dest.removeListener('drain', ondrain);
|
|
|
|
source.removeListener('end', onend);
|
|
source.removeListener('close', onclose);
|
|
|
|
source.removeListener('error', onerror);
|
|
dest.removeListener('error', onerror);
|
|
|
|
source.removeListener('end', cleanup);
|
|
source.removeListener('close', cleanup);
|
|
|
|
dest.removeListener('close', cleanup);
|
|
}
|
|
|
|
source.on('end', cleanup);
|
|
source.on('close', cleanup);
|
|
|
|
dest.on('close', cleanup);
|
|
|
|
dest.emit('pipe', source);
|
|
|
|
// Allow for unix-like usage: A.pipe(B).pipe(C)
|
|
return dest;
|
|
};
|
|
|
|
},{"events":56,"inherits":87,"readable-stream/duplex.js":265,"readable-stream/passthrough.js":272,"readable-stream/readable.js":273,"readable-stream/transform.js":274,"readable-stream/writable.js":275}],279:[function(require,module,exports){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
var Buffer = require('buffer').Buffer;
|
|
|
|
var isBufferEncoding = Buffer.isEncoding
|
|
|| function(encoding) {
|
|
switch (encoding && encoding.toLowerCase()) {
|
|
case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': case 'raw': return true;
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
|
|
function assertEncoding(encoding) {
|
|
if (encoding && !isBufferEncoding(encoding)) {
|
|
throw new Error('Unknown encoding: ' + encoding);
|
|
}
|
|
}
|
|
|
|
// StringDecoder provides an interface for efficiently splitting a series of
|
|
// buffers into a series of JS strings without breaking apart multi-byte
|
|
// characters. CESU-8 is handled as part of the UTF-8 encoding.
|
|
//
|
|
// @TODO Handling all encodings inside a single object makes it very difficult
|
|
// to reason about this code, so it should be split up in the future.
|
|
// @TODO There should be a utf8-strict encoding that rejects invalid UTF-8 code
|
|
// points as used by CESU-8.
|
|
var StringDecoder = exports.StringDecoder = function(encoding) {
|
|
this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, '');
|
|
assertEncoding(encoding);
|
|
switch (this.encoding) {
|
|
case 'utf8':
|
|
// CESU-8 represents each of Surrogate Pair by 3-bytes
|
|
this.surrogateSize = 3;
|
|
break;
|
|
case 'ucs2':
|
|
case 'utf16le':
|
|
// UTF-16 represents each of Surrogate Pair by 2-bytes
|
|
this.surrogateSize = 2;
|
|
this.detectIncompleteChar = utf16DetectIncompleteChar;
|
|
break;
|
|
case 'base64':
|
|
// Base-64 stores 3 bytes in 4 chars, and pads the remainder.
|
|
this.surrogateSize = 3;
|
|
this.detectIncompleteChar = base64DetectIncompleteChar;
|
|
break;
|
|
default:
|
|
this.write = passThroughWrite;
|
|
return;
|
|
}
|
|
|
|
// Enough space to store all bytes of a single character. UTF-8 needs 4
|
|
// bytes, but CESU-8 may require up to 6 (3 bytes per surrogate).
|
|
this.charBuffer = new Buffer(6);
|
|
// Number of bytes received for the current incomplete multi-byte character.
|
|
this.charReceived = 0;
|
|
// Number of bytes expected for the current incomplete multi-byte character.
|
|
this.charLength = 0;
|
|
};
|
|
|
|
|
|
// write decodes the given buffer and returns it as JS string that is
|
|
// guaranteed to not contain any partial multi-byte characters. Any partial
|
|
// character found at the end of the buffer is buffered up, and will be
|
|
// returned when calling write again with the remaining bytes.
|
|
//
|
|
// Note: Converting a Buffer containing an orphan surrogate to a String
|
|
// currently works, but converting a String to a Buffer (via `new Buffer`, or
|
|
// Buffer#write) will replace incomplete surrogates with the unicode
|
|
// replacement character. See https://codereview.chromium.org/121173009/ .
|
|
StringDecoder.prototype.write = function(buffer) {
|
|
var charStr = '';
|
|
// if our last write ended with an incomplete multibyte character
|
|
while (this.charLength) {
|
|
// determine how many remaining bytes this buffer has to offer for this char
|
|
var available = (buffer.length >= this.charLength - this.charReceived) ?
|
|
this.charLength - this.charReceived :
|
|
buffer.length;
|
|
|
|
// add the new bytes to the char buffer
|
|
buffer.copy(this.charBuffer, this.charReceived, 0, available);
|
|
this.charReceived += available;
|
|
|
|
if (this.charReceived < this.charLength) {
|
|
// still not enough chars in this buffer? wait for more ...
|
|
return '';
|
|
}
|
|
|
|
// remove bytes belonging to the current character from the buffer
|
|
buffer = buffer.slice(available, buffer.length);
|
|
|
|
// get the character that was split
|
|
charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding);
|
|
|
|
// CESU-8: lead surrogate (D800-DBFF) is also the incomplete character
|
|
var charCode = charStr.charCodeAt(charStr.length - 1);
|
|
if (charCode >= 0xD800 && charCode <= 0xDBFF) {
|
|
this.charLength += this.surrogateSize;
|
|
charStr = '';
|
|
continue;
|
|
}
|
|
this.charReceived = this.charLength = 0;
|
|
|
|
// if there are no more bytes in this buffer, just emit our char
|
|
if (buffer.length === 0) {
|
|
return charStr;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// determine and set charLength / charReceived
|
|
this.detectIncompleteChar(buffer);
|
|
|
|
var end = buffer.length;
|
|
if (this.charLength) {
|
|
// buffer the incomplete character bytes we got
|
|
buffer.copy(this.charBuffer, 0, buffer.length - this.charReceived, end);
|
|
end -= this.charReceived;
|
|
}
|
|
|
|
charStr += buffer.toString(this.encoding, 0, end);
|
|
|
|
var end = charStr.length - 1;
|
|
var charCode = charStr.charCodeAt(end);
|
|
// CESU-8: lead surrogate (D800-DBFF) is also the incomplete character
|
|
if (charCode >= 0xD800 && charCode <= 0xDBFF) {
|
|
var size = this.surrogateSize;
|
|
this.charLength += size;
|
|
this.charReceived += size;
|
|
this.charBuffer.copy(this.charBuffer, size, 0, size);
|
|
buffer.copy(this.charBuffer, 0, 0, size);
|
|
return charStr.substring(0, end);
|
|
}
|
|
|
|
// or just emit the charStr
|
|
return charStr;
|
|
};
|
|
|
|
// detectIncompleteChar determines if there is an incomplete UTF-8 character at
|
|
// the end of the given buffer. If so, it sets this.charLength to the byte
|
|
// length that character, and sets this.charReceived to the number of bytes
|
|
// that are available for this character.
|
|
StringDecoder.prototype.detectIncompleteChar = function(buffer) {
|
|
// determine how many bytes we have to check at the end of this buffer
|
|
var i = (buffer.length >= 3) ? 3 : buffer.length;
|
|
|
|
// Figure out if one of the last i bytes of our buffer announces an
|
|
// incomplete char.
|
|
for (; i > 0; i--) {
|
|
var c = buffer[buffer.length - i];
|
|
|
|
// See http://en.wikipedia.org/wiki/UTF-8#Description
|
|
|
|
// 110XXXXX
|
|
if (i == 1 && c >> 5 == 0x06) {
|
|
this.charLength = 2;
|
|
break;
|
|
}
|
|
|
|
// 1110XXXX
|
|
if (i <= 2 && c >> 4 == 0x0E) {
|
|
this.charLength = 3;
|
|
break;
|
|
}
|
|
|
|
// 11110XXX
|
|
if (i <= 3 && c >> 3 == 0x1E) {
|
|
this.charLength = 4;
|
|
break;
|
|
}
|
|
}
|
|
this.charReceived = i;
|
|
};
|
|
|
|
StringDecoder.prototype.end = function(buffer) {
|
|
var res = '';
|
|
if (buffer && buffer.length)
|
|
res = this.write(buffer);
|
|
|
|
if (this.charReceived) {
|
|
var cr = this.charReceived;
|
|
var buf = this.charBuffer;
|
|
var enc = this.encoding;
|
|
res += buf.slice(0, cr).toString(enc);
|
|
}
|
|
|
|
return res;
|
|
};
|
|
|
|
function passThroughWrite(buffer) {
|
|
return buffer.toString(this.encoding);
|
|
}
|
|
|
|
function utf16DetectIncompleteChar(buffer) {
|
|
this.charReceived = buffer.length % 2;
|
|
this.charLength = this.charReceived ? 2 : 0;
|
|
}
|
|
|
|
function base64DetectIncompleteChar(buffer) {
|
|
this.charReceived = buffer.length % 3;
|
|
this.charLength = this.charReceived ? 3 : 0;
|
|
}
|
|
|
|
},{"buffer":43}],280:[function(require,module,exports){
|
|
module.exports = {
|
|
encode: function (decodedKey) {
|
|
return '\xff' + decodedKey[0] + '\xff' + decodedKey[1]
|
|
},
|
|
decode: function (encodedKeyAsBuffer) {
|
|
var str = encodedKeyAsBuffer.toString()
|
|
var idx = str.indexOf('\xff', 1)
|
|
return [str.substring(1, idx), str.substring(idx + 1)]
|
|
},
|
|
lowerBound: '\x00',
|
|
upperBound: '\xff'
|
|
}
|
|
|
|
|
|
},{}],281:[function(require,module,exports){
|
|
/* Copyright (c) 2012-2014 LevelUP contributors
|
|
* See list at <https://github.com/rvagg/node-levelup#contributing>
|
|
* MIT License
|
|
* <https://github.com/rvagg/node-levelup/blob/master/LICENSE.md>
|
|
*/
|
|
|
|
var createError = require('errno').create
|
|
, LevelUPError = createError('LevelUPError')
|
|
, NotFoundError = createError('NotFoundError', LevelUPError)
|
|
|
|
NotFoundError.prototype.notFound = true
|
|
NotFoundError.prototype.status = 404
|
|
|
|
module.exports = {
|
|
LevelUPError : LevelUPError
|
|
, InitializationError : createError('InitializationError', LevelUPError)
|
|
, OpenError : createError('OpenError', LevelUPError)
|
|
, ReadError : createError('ReadError', LevelUPError)
|
|
, WriteError : createError('WriteError', LevelUPError)
|
|
, NotFoundError : NotFoundError
|
|
, EncodingError : createError('EncodingError', LevelUPError)
|
|
}
|
|
|
|
},{"errno":54}],282:[function(require,module,exports){
|
|
var nut = require('./nut')
|
|
var shell = require('./shell') //the shell surrounds the nut
|
|
var Codec = require('level-codec')
|
|
var codec = new Codec();
|
|
|
|
var ReadStream = require('./read-stream')
|
|
|
|
var precodec = require('./codec/legacy')
|
|
|
|
module.exports = function (db) {
|
|
return shell ( nut ( db, precodec, codec ), [], ReadStream, db.options)
|
|
}
|
|
|
|
|
|
},{"./codec/legacy":280,"./nut":283,"./read-stream":284,"./shell":285,"level-codec":91}],283:[function(require,module,exports){
|
|
var ltgt = require('ltgt')
|
|
|
|
function isFunction (f) {
|
|
return 'function' === typeof f
|
|
}
|
|
|
|
function getPrefix (db) {
|
|
if(db == null) return db
|
|
if(isFunction(db.prefix)) return db.prefix()
|
|
return db
|
|
}
|
|
|
|
function clone (_obj) {
|
|
var obj = {}
|
|
for(var k in _obj)
|
|
obj[k] = _obj[k]
|
|
return obj
|
|
}
|
|
|
|
module.exports = function (db, precodec, codec, compare) {
|
|
var waiting = [], ready = false
|
|
|
|
function encodePrefix(prefix, key, opts1, opts2) {
|
|
return precodec.encode([ prefix, codec.encodeKey(key, opts1, opts2 ) ])
|
|
}
|
|
|
|
function decodePrefix(data) {
|
|
return precodec.decode(data)
|
|
}
|
|
|
|
function addEncodings(op, prefix) {
|
|
if(prefix && prefix.options) {
|
|
op.keyEncoding =
|
|
op.keyEncoding || prefix.options.keyEncoding
|
|
op.valueEncoding =
|
|
op.valueEncoding || prefix.options.valueEncoding
|
|
}
|
|
return op
|
|
}
|
|
|
|
function start () {
|
|
ready = true
|
|
while(waiting.length)
|
|
waiting.shift()()
|
|
}
|
|
|
|
if(isFunction(db.isOpen)) {
|
|
if(db.isOpen())
|
|
ready = true
|
|
else
|
|
db.open(start)
|
|
} else {
|
|
db.open(start)
|
|
}
|
|
|
|
return {
|
|
apply: function (ops, opts, cb) {
|
|
for(var i = 0; i < ops.length; i++) {
|
|
var op = ops[i]
|
|
addEncodings(op, op.prefix)
|
|
op.prefix = getPrefix(op.prefix)
|
|
}
|
|
|
|
opts = opts || {}
|
|
|
|
if('object' !== typeof opts) throw new Error('opts must be object, was:'+ opts)
|
|
|
|
if('function' === typeof opts) cb = opts, opts = {}
|
|
|
|
if(ops.length)
|
|
(db.db || db).batch(
|
|
ops.map(function (op) {
|
|
return {
|
|
key: encodePrefix(op.prefix, op.key, opts, op),
|
|
value:
|
|
op.type !== 'del'
|
|
? codec.encodeValue(
|
|
op.value,
|
|
opts,
|
|
op
|
|
)
|
|
: undefined,
|
|
type:
|
|
op.type || (op.value === undefined ? 'del' : 'put')
|
|
}
|
|
}),
|
|
opts,
|
|
function (err) {
|
|
if(err) return cb(err)
|
|
cb()
|
|
}
|
|
)
|
|
else
|
|
cb()
|
|
},
|
|
get: function (key, prefix, opts, cb) {
|
|
opts.asBuffer = codec.valueAsBuffer(opts)
|
|
return (db.db || db).get(
|
|
encodePrefix(prefix, key, opts),
|
|
opts,
|
|
function (err, value) {
|
|
if(err) cb(err)
|
|
else cb(null, codec.decodeValue(value, opts))
|
|
}
|
|
)
|
|
},
|
|
createDecoder: function (opts) {
|
|
return function (key, value) {
|
|
return {
|
|
key: codec.decodeKey(precodec.decode(key)[1], opts),
|
|
value: codec.decodeValue(value, opts)
|
|
}
|
|
}
|
|
},
|
|
isOpen: function isOpen() {
|
|
if (db.db && isFunction(db.db.isOpen))
|
|
return db.db.isOpen()
|
|
|
|
return db.isOpen()
|
|
},
|
|
isClosed: function isClosed() {
|
|
if (db.db && isFunction(db.db.isClosed))
|
|
return db.db.isClosed()
|
|
|
|
return db.isClosed()
|
|
},
|
|
close: function close (cb) {
|
|
return db.close(cb)
|
|
},
|
|
iterator: function (_opts, cb) {
|
|
var opts = clone(_opts || {})
|
|
var prefix = _opts.prefix || []
|
|
|
|
function encodeKey(key) {
|
|
return encodePrefix(prefix, key, opts, {})
|
|
}
|
|
|
|
ltgt.toLtgt(_opts, opts, encodeKey, precodec.lowerBound, precodec.upperBound)
|
|
|
|
// if these legacy values are in the options, remove them
|
|
|
|
opts.prefix = null
|
|
|
|
//************************************************
|
|
//hard coded defaults, for now...
|
|
//TODO: pull defaults and encoding out of levelup.
|
|
opts.keyAsBuffer = opts.valueAsBuffer = false
|
|
//************************************************
|
|
|
|
|
|
//this is vital, otherwise limit: undefined will
|
|
//create an empty stream.
|
|
if ('number' !== typeof opts.limit)
|
|
opts.limit = -1
|
|
|
|
opts.keyAsBuffer = precodec.buffer
|
|
opts.valueAsBuffer = codec.valueAsBuffer(opts)
|
|
|
|
function wrapIterator (iterator) {
|
|
return {
|
|
next: function (cb) {
|
|
return iterator.next(cb)
|
|
},
|
|
end: function (cb) {
|
|
iterator.end(cb)
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ready)
|
|
return wrapIterator((db.db || db).iterator(opts))
|
|
else
|
|
waiting.push(function () {
|
|
cb(null, wrapIterator((db.db || db).iterator(opts)))
|
|
})
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
},{"ltgt":287}],284:[function(require,module,exports){
|
|
/* Copyright (c) 2012-2014 LevelUP contributors
|
|
* See list at <https://github.com/rvagg/node-levelup#contributing>
|
|
* MIT License <https://github.com/rvagg/node-levelup/blob/master/LICENSE.md>
|
|
*/
|
|
|
|
// NOTE: we are fixed to readable-stream@1.0.x for now
|
|
// for pure Streams2 across Node versions
|
|
var Readable = require('readable-stream').Readable
|
|
, inherits = require('inherits')
|
|
, EncodingError = require('./errors').EncodingError;
|
|
|
|
function ReadStream (options, makeData) {
|
|
if (!(this instanceof ReadStream))
|
|
return new ReadStream(options, makeData)
|
|
|
|
Readable.call(this, { objectMode: true, highWaterMark: options.highWaterMark })
|
|
|
|
// purely to keep `db` around until we're done so it's not GCed if the user doesn't keep a ref
|
|
|
|
this._waiting = false
|
|
this._options = options
|
|
this._makeData = makeData
|
|
}
|
|
|
|
inherits(ReadStream, Readable)
|
|
|
|
ReadStream.prototype.setIterator = function (it) {
|
|
var self = this
|
|
this._iterator = it
|
|
if(this._destroyed) return it.end(function () {})
|
|
if(this._waiting) {
|
|
this._waiting = false
|
|
return this._read()
|
|
}
|
|
return this
|
|
}
|
|
|
|
ReadStream.prototype._read = function read () {
|
|
var self = this
|
|
if (self._destroyed)
|
|
return
|
|
if(!self._iterator)
|
|
return this._waiting = true
|
|
|
|
self._iterator.next(function(err, key, value) {
|
|
if (err || (key === undefined && value === undefined)) {
|
|
if (!err && !self._destroyed)
|
|
self.push(null)
|
|
return self._cleanup(err)
|
|
}
|
|
|
|
|
|
try {
|
|
value = self._makeData(key, value)
|
|
} catch (e) {
|
|
return self._cleanup(new EncodingError(e))
|
|
}
|
|
if (!self._destroyed)
|
|
self.push(value)
|
|
})
|
|
}
|
|
|
|
ReadStream.prototype._cleanup = function (err) {
|
|
if (this._destroyed)
|
|
return
|
|
|
|
this._destroyed = true
|
|
|
|
var self = this
|
|
if (err)
|
|
self.emit('error', err)
|
|
|
|
if (self._iterator) {
|
|
self._iterator.end(function () {
|
|
self._iterator = null
|
|
self.emit('close')
|
|
})
|
|
} else {
|
|
self.emit('close')
|
|
}
|
|
}
|
|
|
|
ReadStream.prototype.destroy = function () {
|
|
this._cleanup()
|
|
}
|
|
|
|
ReadStream.prototype.toString = function () {
|
|
return 'LevelUP.ReadStream'
|
|
}
|
|
|
|
|
|
module.exports = ReadStream
|
|
|
|
|
|
},{"./errors":281,"inherits":87,"readable-stream":293}],285:[function(require,module,exports){
|
|
(function (process){
|
|
var EventEmitter = require('events').EventEmitter
|
|
|
|
var errors = require('./errors')
|
|
|
|
var version = "6.5.4"
|
|
|
|
var sublevel = module.exports = function (nut, prefix, createStream, options) {
|
|
var emitter = new EventEmitter()
|
|
emitter.sublevels = {}
|
|
emitter.options = options
|
|
|
|
emitter.version = version
|
|
|
|
emitter.methods = {}
|
|
prefix = prefix || []
|
|
|
|
function errback (err) { if (err) emitter.emit('error', err) }
|
|
|
|
function mergeOpts(opts) {
|
|
var o = {}
|
|
if(options)
|
|
for(var k in options)
|
|
if(options[k] != undefined)o[k] = options[k]
|
|
if(opts)
|
|
for(var k in opts)
|
|
if(opts[k] != undefined) o[k] = opts[k]
|
|
return o
|
|
}
|
|
|
|
emitter.put = function (key, value, opts, cb) {
|
|
if('function' === typeof opts) cb = opts, opts = {}
|
|
if(!cb) cb = errback
|
|
|
|
nut.apply([{
|
|
key: key, value: value,
|
|
prefix: prefix.slice(), type: 'put'
|
|
}], mergeOpts(opts), function (err) {
|
|
if(!err) { emitter.emit('put', key, value); cb(null) }
|
|
if(err) return cb(err)
|
|
})
|
|
}
|
|
|
|
emitter.prefix = function () {
|
|
return prefix.slice()
|
|
}
|
|
|
|
emitter.del = function (key, opts, cb) {
|
|
if('function' === typeof opts) cb = opts, opts = {}
|
|
if(!cb) cb = errback
|
|
|
|
nut.apply([{
|
|
key: key,
|
|
prefix: prefix.slice(), type: 'del'
|
|
}], mergeOpts(opts), function (err) {
|
|
if(!err) { emitter.emit('del', key); cb(null) }
|
|
if(err) return cb(err)
|
|
})
|
|
}
|
|
|
|
emitter.batch = function (ops, opts, cb) {
|
|
if('function' === typeof opts)
|
|
cb = opts, opts = {}
|
|
if(!cb) cb = errback
|
|
|
|
ops = ops.map(function (op) {
|
|
return {
|
|
key: op.key,
|
|
value: op.value,
|
|
prefix: op.prefix || prefix,
|
|
keyEncoding: op.keyEncoding, // *
|
|
valueEncoding: op.valueEncoding, // * (TODO: encodings on sublevel)
|
|
type: op.type
|
|
}
|
|
})
|
|
|
|
nut.apply(ops, mergeOpts(opts), function (err) {
|
|
if(!err) { emitter.emit('batch', ops); cb(null) }
|
|
if(err) return cb(err)
|
|
})
|
|
}
|
|
|
|
emitter.get = function (key, opts, cb) {
|
|
if('function' === typeof opts)
|
|
cb = opts, opts = {}
|
|
nut.get(key, prefix, mergeOpts(opts), function (err, value) {
|
|
if(err) cb(new errors.NotFoundError('Key not found in database', err))
|
|
else cb(null, value)
|
|
})
|
|
}
|
|
|
|
emitter.clone = function(opts) {
|
|
return sublevel(nut, prefix, createStream, mergeOpts(opts))
|
|
}
|
|
|
|
emitter.sublevel = function (name, opts) {
|
|
return emitter.sublevels[name] =
|
|
emitter.sublevels[name] || sublevel(nut, prefix.concat(name), createStream, mergeOpts(opts))
|
|
}
|
|
|
|
emitter.readStream = emitter.createReadStream = function (opts) {
|
|
opts = mergeOpts(opts)
|
|
opts.prefix = prefix
|
|
var stream
|
|
var it = nut.iterator(opts, function (err, it) {
|
|
stream.setIterator(it)
|
|
})
|
|
|
|
stream = createStream(opts, nut.createDecoder(opts))
|
|
if(it) stream.setIterator(it)
|
|
|
|
return stream
|
|
}
|
|
|
|
emitter.valueStream =
|
|
emitter.createValueStream = function (opts) {
|
|
opts = opts || {}
|
|
opts.values = true
|
|
opts.keys = false
|
|
return emitter.createReadStream(opts)
|
|
}
|
|
|
|
emitter.keyStream =
|
|
emitter.createKeyStream = function (opts) {
|
|
opts = opts || {}
|
|
opts.values = false
|
|
opts.keys = true
|
|
return emitter.createReadStream(opts)
|
|
}
|
|
|
|
emitter.close = function (cb) {
|
|
//TODO: deregister all hooks
|
|
cb = cb || function () {}
|
|
if (!prefix.length) nut.close(cb)
|
|
else process.nextTick(cb)
|
|
}
|
|
|
|
emitter.isOpen = nut.isOpen
|
|
emitter.isClosed = nut.isClosed
|
|
|
|
return emitter
|
|
}
|
|
|
|
}).call(this,require('_process'))
|
|
},{"./errors":281,"_process":133,"events":56}],286:[function(require,module,exports){
|
|
arguments[4][95][0].apply(exports,arguments)
|
|
},{"dup":95}],287:[function(require,module,exports){
|
|
(function (Buffer){
|
|
|
|
exports.compare = function (a, b) {
|
|
|
|
if(Buffer.isBuffer(a)) {
|
|
var l = Math.min(a.length, b.length)
|
|
for(var i = 0; i < l; i++) {
|
|
var cmp = a[i] - b[i]
|
|
if(cmp) return cmp
|
|
}
|
|
return a.length - b.length
|
|
}
|
|
|
|
return a < b ? -1 : a > b ? 1 : 0
|
|
}
|
|
|
|
function has(obj, key) {
|
|
return Object.hasOwnProperty.call(obj, key)
|
|
}
|
|
|
|
// to be compatible with the current abstract-leveldown tests
|
|
// nullish or empty strings.
|
|
// I could use !!val but I want to permit numbers and booleans,
|
|
// if possible.
|
|
|
|
function isDef (val) {
|
|
return val !== undefined && val !== ''
|
|
}
|
|
|
|
function has (range, name) {
|
|
return Object.hasOwnProperty.call(range, name)
|
|
}
|
|
|
|
function hasKey(range, name) {
|
|
return Object.hasOwnProperty.call(range, name) && name
|
|
}
|
|
|
|
var lowerBoundKey = exports.lowerBoundKey = function (range) {
|
|
return (
|
|
hasKey(range, 'gt')
|
|
|| hasKey(range, 'gte')
|
|
|| hasKey(range, 'min')
|
|
|| (range.reverse ? hasKey(range, 'end') : hasKey(range, 'start'))
|
|
|| undefined
|
|
)
|
|
}
|
|
|
|
var lowerBound = exports.lowerBound = function (range) {
|
|
var k = lowerBoundKey(range)
|
|
return k && range[k]
|
|
}
|
|
|
|
exports.lowerBoundInclusive = function (range) {
|
|
return has(range, 'gt') ? false : true
|
|
}
|
|
|
|
exports.upperBoundInclusive =
|
|
function (range) {
|
|
return has(range, 'lt') || !range.minEx ? false : true
|
|
}
|
|
|
|
var lowerBoundExclusive = exports.lowerBoundExclusive =
|
|
function (range) {
|
|
return has(range, 'gt') || range.minEx ? true : false
|
|
}
|
|
|
|
var upperBoundExclusive = exports.upperBoundExclusive =
|
|
function (range) {
|
|
return has(range, 'lt') ? true : false
|
|
}
|
|
|
|
var upperBoundKey = exports.upperBoundKey = function (range) {
|
|
return (
|
|
hasKey(range, 'lt')
|
|
|| hasKey(range, 'lte')
|
|
|| hasKey(range, 'max')
|
|
|| (range.reverse ? hasKey(range, 'start') : hasKey(range, 'end'))
|
|
|| undefined
|
|
)
|
|
}
|
|
|
|
var upperBound = exports.upperBound = function (range) {
|
|
var k = upperBoundKey(range)
|
|
return k && range[k]
|
|
}
|
|
|
|
function id (e) { return e }
|
|
|
|
exports.toLtgt = function (range, _range, map, lower, upper) {
|
|
_range = _range || {}
|
|
map = map || id
|
|
var defaults = arguments.length > 3
|
|
var lb = exports.lowerBoundKey(range)
|
|
var ub = exports.upperBoundKey(range)
|
|
if(lb) {
|
|
if(lb === 'gt') _range.gt = map(range.gt, false)
|
|
else _range.gte = map(range[lb], false)
|
|
}
|
|
else if(defaults)
|
|
_range.gte = map(lower, false)
|
|
|
|
if(ub) {
|
|
if(ub === 'lt') _range.lt = map(range.lt, true)
|
|
else _range.lte = map(range[ub], true)
|
|
}
|
|
else if(defaults)
|
|
_range.lte = map(upper, true)
|
|
|
|
if(range.reverse != null)
|
|
_range.reverse = !!range.reverse
|
|
|
|
//if range was used mutably
|
|
//(in level-sublevel it's part of an options object
|
|
//that has more properties on it.)
|
|
if(has(_range, 'max')) delete _range.max
|
|
if(has(_range, 'min')) delete _range.min
|
|
if(has(_range, 'start')) delete _range.start
|
|
if(has(_range, 'end')) delete _range.end
|
|
|
|
return _range
|
|
}
|
|
|
|
exports.contains = function (range, key, compare) {
|
|
compare = compare || exports.compare
|
|
|
|
var lb = lowerBound(range)
|
|
if(isDef(lb)) {
|
|
var cmp = compare(key, lb)
|
|
if(cmp < 0 || (cmp === 0 && lowerBoundExclusive(range)))
|
|
return false
|
|
}
|
|
|
|
var ub = upperBound(range)
|
|
if(isDef(ub)) {
|
|
var cmp = compare(key, ub)
|
|
if(cmp > 0 || (cmp === 0) && upperBoundExclusive(range))
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
exports.filter = function (range, compare) {
|
|
return function (key) {
|
|
return exports.contains(range, key, compare)
|
|
}
|
|
}
|
|
|
|
}).call(this,{"isBuffer":require("../../../is-buffer/index.js")})
|
|
},{"../../../is-buffer/index.js":88}],288:[function(require,module,exports){
|
|
arguments[4][96][0].apply(exports,arguments)
|
|
},{"./_stream_readable":290,"./_stream_writable":292,"_process":133,"core-util-is":44,"dup":96,"inherits":87}],289:[function(require,module,exports){
|
|
arguments[4][97][0].apply(exports,arguments)
|
|
},{"./_stream_transform":291,"core-util-is":44,"dup":97,"inherits":87}],290:[function(require,module,exports){
|
|
(function (process){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
module.exports = Readable;
|
|
|
|
/*<replacement>*/
|
|
var isArray = require('isarray');
|
|
/*</replacement>*/
|
|
|
|
|
|
/*<replacement>*/
|
|
var Buffer = require('buffer').Buffer;
|
|
/*</replacement>*/
|
|
|
|
Readable.ReadableState = ReadableState;
|
|
|
|
var EE = require('events').EventEmitter;
|
|
|
|
/*<replacement>*/
|
|
if (!EE.listenerCount) EE.listenerCount = function(emitter, type) {
|
|
return emitter.listeners(type).length;
|
|
};
|
|
/*</replacement>*/
|
|
|
|
var Stream = require('stream');
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
var StringDecoder;
|
|
|
|
util.inherits(Readable, Stream);
|
|
|
|
function ReadableState(options, stream) {
|
|
options = options || {};
|
|
|
|
// the point at which it stops calling _read() to fill the buffer
|
|
// Note: 0 is a valid value, means "don't call _read preemptively ever"
|
|
var hwm = options.highWaterMark;
|
|
this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;
|
|
|
|
// cast to ints.
|
|
this.highWaterMark = ~~this.highWaterMark;
|
|
|
|
this.buffer = [];
|
|
this.length = 0;
|
|
this.pipes = null;
|
|
this.pipesCount = 0;
|
|
this.flowing = false;
|
|
this.ended = false;
|
|
this.endEmitted = false;
|
|
this.reading = false;
|
|
|
|
// In streams that never have any data, and do push(null) right away,
|
|
// the consumer can miss the 'end' event if they do some I/O before
|
|
// consuming the stream. So, we don't emit('end') until some reading
|
|
// happens.
|
|
this.calledRead = false;
|
|
|
|
// a flag to be able to tell if the onwrite cb is called immediately,
|
|
// or on a later tick. We set this to true at first, becuase any
|
|
// actions that shouldn't happen until "later" should generally also
|
|
// not happen before the first write call.
|
|
this.sync = true;
|
|
|
|
// whenever we return null, then we set a flag to say
|
|
// that we're awaiting a 'readable' event emission.
|
|
this.needReadable = false;
|
|
this.emittedReadable = false;
|
|
this.readableListening = false;
|
|
|
|
|
|
// object stream flag. Used to make read(n) ignore n and to
|
|
// make all the buffer merging and length checks go away
|
|
this.objectMode = !!options.objectMode;
|
|
|
|
// Crypto is kind of old and crusty. Historically, its default string
|
|
// encoding is 'binary' so we have to make this configurable.
|
|
// Everything else in the universe uses 'utf8', though.
|
|
this.defaultEncoding = options.defaultEncoding || 'utf8';
|
|
|
|
// when piping, we only care about 'readable' events that happen
|
|
// after read()ing all the bytes and not getting any pushback.
|
|
this.ranOut = false;
|
|
|
|
// the number of writers that are awaiting a drain event in .pipe()s
|
|
this.awaitDrain = 0;
|
|
|
|
// if true, a maybeReadMore has been scheduled
|
|
this.readingMore = false;
|
|
|
|
this.decoder = null;
|
|
this.encoding = null;
|
|
if (options.encoding) {
|
|
if (!StringDecoder)
|
|
StringDecoder = require('string_decoder/').StringDecoder;
|
|
this.decoder = new StringDecoder(options.encoding);
|
|
this.encoding = options.encoding;
|
|
}
|
|
}
|
|
|
|
function Readable(options) {
|
|
if (!(this instanceof Readable))
|
|
return new Readable(options);
|
|
|
|
this._readableState = new ReadableState(options, this);
|
|
|
|
// legacy
|
|
this.readable = true;
|
|
|
|
Stream.call(this);
|
|
}
|
|
|
|
// Manually shove something into the read() buffer.
|
|
// This returns true if the highWaterMark has not been hit yet,
|
|
// similar to how Writable.write() returns true if you should
|
|
// write() some more.
|
|
Readable.prototype.push = function(chunk, encoding) {
|
|
var state = this._readableState;
|
|
|
|
if (typeof chunk === 'string' && !state.objectMode) {
|
|
encoding = encoding || state.defaultEncoding;
|
|
if (encoding !== state.encoding) {
|
|
chunk = new Buffer(chunk, encoding);
|
|
encoding = '';
|
|
}
|
|
}
|
|
|
|
return readableAddChunk(this, state, chunk, encoding, false);
|
|
};
|
|
|
|
// Unshift should *always* be something directly out of read()
|
|
Readable.prototype.unshift = function(chunk) {
|
|
var state = this._readableState;
|
|
return readableAddChunk(this, state, chunk, '', true);
|
|
};
|
|
|
|
function readableAddChunk(stream, state, chunk, encoding, addToFront) {
|
|
var er = chunkInvalid(state, chunk);
|
|
if (er) {
|
|
stream.emit('error', er);
|
|
} else if (chunk === null || chunk === undefined) {
|
|
state.reading = false;
|
|
if (!state.ended)
|
|
onEofChunk(stream, state);
|
|
} else if (state.objectMode || chunk && chunk.length > 0) {
|
|
if (state.ended && !addToFront) {
|
|
var e = new Error('stream.push() after EOF');
|
|
stream.emit('error', e);
|
|
} else if (state.endEmitted && addToFront) {
|
|
var e = new Error('stream.unshift() after end event');
|
|
stream.emit('error', e);
|
|
} else {
|
|
if (state.decoder && !addToFront && !encoding)
|
|
chunk = state.decoder.write(chunk);
|
|
|
|
// update the buffer info.
|
|
state.length += state.objectMode ? 1 : chunk.length;
|
|
if (addToFront) {
|
|
state.buffer.unshift(chunk);
|
|
} else {
|
|
state.reading = false;
|
|
state.buffer.push(chunk);
|
|
}
|
|
|
|
if (state.needReadable)
|
|
emitReadable(stream);
|
|
|
|
maybeReadMore(stream, state);
|
|
}
|
|
} else if (!addToFront) {
|
|
state.reading = false;
|
|
}
|
|
|
|
return needMoreData(state);
|
|
}
|
|
|
|
|
|
|
|
// if it's past the high water mark, we can push in some more.
|
|
// Also, if we have no data yet, we can stand some
|
|
// more bytes. This is to work around cases where hwm=0,
|
|
// such as the repl. Also, if the push() triggered a
|
|
// readable event, and the user called read(largeNumber) such that
|
|
// needReadable was set, then we ought to push more, so that another
|
|
// 'readable' event will be triggered.
|
|
function needMoreData(state) {
|
|
return !state.ended &&
|
|
(state.needReadable ||
|
|
state.length < state.highWaterMark ||
|
|
state.length === 0);
|
|
}
|
|
|
|
// backwards compatibility.
|
|
Readable.prototype.setEncoding = function(enc) {
|
|
if (!StringDecoder)
|
|
StringDecoder = require('string_decoder/').StringDecoder;
|
|
this._readableState.decoder = new StringDecoder(enc);
|
|
this._readableState.encoding = enc;
|
|
};
|
|
|
|
// Don't raise the hwm > 128MB
|
|
var MAX_HWM = 0x800000;
|
|
function roundUpToNextPowerOf2(n) {
|
|
if (n >= MAX_HWM) {
|
|
n = MAX_HWM;
|
|
} else {
|
|
// Get the next highest power of 2
|
|
n--;
|
|
for (var p = 1; p < 32; p <<= 1) n |= n >> p;
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
function howMuchToRead(n, state) {
|
|
if (state.length === 0 && state.ended)
|
|
return 0;
|
|
|
|
if (state.objectMode)
|
|
return n === 0 ? 0 : 1;
|
|
|
|
if (n === null || isNaN(n)) {
|
|
// only flow one buffer at a time
|
|
if (state.flowing && state.buffer.length)
|
|
return state.buffer[0].length;
|
|
else
|
|
return state.length;
|
|
}
|
|
|
|
if (n <= 0)
|
|
return 0;
|
|
|
|
// If we're asking for more than the target buffer level,
|
|
// then raise the water mark. Bump up to the next highest
|
|
// power of 2, to prevent increasing it excessively in tiny
|
|
// amounts.
|
|
if (n > state.highWaterMark)
|
|
state.highWaterMark = roundUpToNextPowerOf2(n);
|
|
|
|
// don't have that much. return null, unless we've ended.
|
|
if (n > state.length) {
|
|
if (!state.ended) {
|
|
state.needReadable = true;
|
|
return 0;
|
|
} else
|
|
return state.length;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
// you can override either this method, or the async _read(n) below.
|
|
Readable.prototype.read = function(n) {
|
|
var state = this._readableState;
|
|
state.calledRead = true;
|
|
var nOrig = n;
|
|
var ret;
|
|
|
|
if (typeof n !== 'number' || n > 0)
|
|
state.emittedReadable = false;
|
|
|
|
// if we're doing read(0) to trigger a readable event, but we
|
|
// already have a bunch of data in the buffer, then just trigger
|
|
// the 'readable' event and move on.
|
|
if (n === 0 &&
|
|
state.needReadable &&
|
|
(state.length >= state.highWaterMark || state.ended)) {
|
|
emitReadable(this);
|
|
return null;
|
|
}
|
|
|
|
n = howMuchToRead(n, state);
|
|
|
|
// if we've ended, and we're now clear, then finish it up.
|
|
if (n === 0 && state.ended) {
|
|
ret = null;
|
|
|
|
// In cases where the decoder did not receive enough data
|
|
// to produce a full chunk, then immediately received an
|
|
// EOF, state.buffer will contain [<Buffer >, <Buffer 00 ...>].
|
|
// howMuchToRead will see this and coerce the amount to
|
|
// read to zero (because it's looking at the length of the
|
|
// first <Buffer > in state.buffer), and we'll end up here.
|
|
//
|
|
// This can only happen via state.decoder -- no other venue
|
|
// exists for pushing a zero-length chunk into state.buffer
|
|
// and triggering this behavior. In this case, we return our
|
|
// remaining data and end the stream, if appropriate.
|
|
if (state.length > 0 && state.decoder) {
|
|
ret = fromList(n, state);
|
|
state.length -= ret.length;
|
|
}
|
|
|
|
if (state.length === 0)
|
|
endReadable(this);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// All the actual chunk generation logic needs to be
|
|
// *below* the call to _read. The reason is that in certain
|
|
// synthetic stream cases, such as passthrough streams, _read
|
|
// may be a completely synchronous operation which may change
|
|
// the state of the read buffer, providing enough data when
|
|
// before there was *not* enough.
|
|
//
|
|
// So, the steps are:
|
|
// 1. Figure out what the state of things will be after we do
|
|
// a read from the buffer.
|
|
//
|
|
// 2. If that resulting state will trigger a _read, then call _read.
|
|
// Note that this may be asynchronous, or synchronous. Yes, it is
|
|
// deeply ugly to write APIs this way, but that still doesn't mean
|
|
// that the Readable class should behave improperly, as streams are
|
|
// designed to be sync/async agnostic.
|
|
// Take note if the _read call is sync or async (ie, if the read call
|
|
// has returned yet), so that we know whether or not it's safe to emit
|
|
// 'readable' etc.
|
|
//
|
|
// 3. Actually pull the requested chunks out of the buffer and return.
|
|
|
|
// if we need a readable event, then we need to do some reading.
|
|
var doRead = state.needReadable;
|
|
|
|
// if we currently have less than the highWaterMark, then also read some
|
|
if (state.length - n <= state.highWaterMark)
|
|
doRead = true;
|
|
|
|
// however, if we've ended, then there's no point, and if we're already
|
|
// reading, then it's unnecessary.
|
|
if (state.ended || state.reading)
|
|
doRead = false;
|
|
|
|
if (doRead) {
|
|
state.reading = true;
|
|
state.sync = true;
|
|
// if the length is currently zero, then we *need* a readable event.
|
|
if (state.length === 0)
|
|
state.needReadable = true;
|
|
// call internal read method
|
|
this._read(state.highWaterMark);
|
|
state.sync = false;
|
|
}
|
|
|
|
// If _read called its callback synchronously, then `reading`
|
|
// will be false, and we need to re-evaluate how much data we
|
|
// can return to the user.
|
|
if (doRead && !state.reading)
|
|
n = howMuchToRead(nOrig, state);
|
|
|
|
if (n > 0)
|
|
ret = fromList(n, state);
|
|
else
|
|
ret = null;
|
|
|
|
if (ret === null) {
|
|
state.needReadable = true;
|
|
n = 0;
|
|
}
|
|
|
|
state.length -= n;
|
|
|
|
// If we have nothing in the buffer, then we want to know
|
|
// as soon as we *do* get something into the buffer.
|
|
if (state.length === 0 && !state.ended)
|
|
state.needReadable = true;
|
|
|
|
// If we happened to read() exactly the remaining amount in the
|
|
// buffer, and the EOF has been seen at this point, then make sure
|
|
// that we emit 'end' on the very next tick.
|
|
if (state.ended && !state.endEmitted && state.length === 0)
|
|
endReadable(this);
|
|
|
|
return ret;
|
|
};
|
|
|
|
function chunkInvalid(state, chunk) {
|
|
var er = null;
|
|
if (!Buffer.isBuffer(chunk) &&
|
|
'string' !== typeof chunk &&
|
|
chunk !== null &&
|
|
chunk !== undefined &&
|
|
!state.objectMode) {
|
|
er = new TypeError('Invalid non-string/buffer chunk');
|
|
}
|
|
return er;
|
|
}
|
|
|
|
|
|
function onEofChunk(stream, state) {
|
|
if (state.decoder && !state.ended) {
|
|
var chunk = state.decoder.end();
|
|
if (chunk && chunk.length) {
|
|
state.buffer.push(chunk);
|
|
state.length += state.objectMode ? 1 : chunk.length;
|
|
}
|
|
}
|
|
state.ended = true;
|
|
|
|
// if we've ended and we have some data left, then emit
|
|
// 'readable' now to make sure it gets picked up.
|
|
if (state.length > 0)
|
|
emitReadable(stream);
|
|
else
|
|
endReadable(stream);
|
|
}
|
|
|
|
// Don't emit readable right away in sync mode, because this can trigger
|
|
// another read() call => stack overflow. This way, it might trigger
|
|
// a nextTick recursion warning, but that's not so bad.
|
|
function emitReadable(stream) {
|
|
var state = stream._readableState;
|
|
state.needReadable = false;
|
|
if (state.emittedReadable)
|
|
return;
|
|
|
|
state.emittedReadable = true;
|
|
if (state.sync)
|
|
process.nextTick(function() {
|
|
emitReadable_(stream);
|
|
});
|
|
else
|
|
emitReadable_(stream);
|
|
}
|
|
|
|
function emitReadable_(stream) {
|
|
stream.emit('readable');
|
|
}
|
|
|
|
|
|
// at this point, the user has presumably seen the 'readable' event,
|
|
// and called read() to consume some data. that may have triggered
|
|
// in turn another _read(n) call, in which case reading = true if
|
|
// it's in progress.
|
|
// However, if we're not ended, or reading, and the length < hwm,
|
|
// then go ahead and try to read some more preemptively.
|
|
function maybeReadMore(stream, state) {
|
|
if (!state.readingMore) {
|
|
state.readingMore = true;
|
|
process.nextTick(function() {
|
|
maybeReadMore_(stream, state);
|
|
});
|
|
}
|
|
}
|
|
|
|
function maybeReadMore_(stream, state) {
|
|
var len = state.length;
|
|
while (!state.reading && !state.flowing && !state.ended &&
|
|
state.length < state.highWaterMark) {
|
|
stream.read(0);
|
|
if (len === state.length)
|
|
// didn't get any data, stop spinning.
|
|
break;
|
|
else
|
|
len = state.length;
|
|
}
|
|
state.readingMore = false;
|
|
}
|
|
|
|
// abstract method. to be overridden in specific implementation classes.
|
|
// call cb(er, data) where data is <= n in length.
|
|
// for virtual (non-string, non-buffer) streams, "length" is somewhat
|
|
// arbitrary, and perhaps not very meaningful.
|
|
Readable.prototype._read = function(n) {
|
|
this.emit('error', new Error('not implemented'));
|
|
};
|
|
|
|
Readable.prototype.pipe = function(dest, pipeOpts) {
|
|
var src = this;
|
|
var state = this._readableState;
|
|
|
|
switch (state.pipesCount) {
|
|
case 0:
|
|
state.pipes = dest;
|
|
break;
|
|
case 1:
|
|
state.pipes = [state.pipes, dest];
|
|
break;
|
|
default:
|
|
state.pipes.push(dest);
|
|
break;
|
|
}
|
|
state.pipesCount += 1;
|
|
|
|
var doEnd = (!pipeOpts || pipeOpts.end !== false) &&
|
|
dest !== process.stdout &&
|
|
dest !== process.stderr;
|
|
|
|
var endFn = doEnd ? onend : cleanup;
|
|
if (state.endEmitted)
|
|
process.nextTick(endFn);
|
|
else
|
|
src.once('end', endFn);
|
|
|
|
dest.on('unpipe', onunpipe);
|
|
function onunpipe(readable) {
|
|
if (readable !== src) return;
|
|
cleanup();
|
|
}
|
|
|
|
function onend() {
|
|
dest.end();
|
|
}
|
|
|
|
// when the dest drains, it reduces the awaitDrain counter
|
|
// on the source. This would be more elegant with a .once()
|
|
// handler in flow(), but adding and removing repeatedly is
|
|
// too slow.
|
|
var ondrain = pipeOnDrain(src);
|
|
dest.on('drain', ondrain);
|
|
|
|
function cleanup() {
|
|
// cleanup event handlers once the pipe is broken
|
|
dest.removeListener('close', onclose);
|
|
dest.removeListener('finish', onfinish);
|
|
dest.removeListener('drain', ondrain);
|
|
dest.removeListener('error', onerror);
|
|
dest.removeListener('unpipe', onunpipe);
|
|
src.removeListener('end', onend);
|
|
src.removeListener('end', cleanup);
|
|
|
|
// if the reader is waiting for a drain event from this
|
|
// specific writer, then it would cause it to never start
|
|
// flowing again.
|
|
// So, if this is awaiting a drain, then we just call it now.
|
|
// If we don't know, then assume that we are waiting for one.
|
|
if (!dest._writableState || dest._writableState.needDrain)
|
|
ondrain();
|
|
}
|
|
|
|
// if the dest has an error, then stop piping into it.
|
|
// however, don't suppress the throwing behavior for this.
|
|
function onerror(er) {
|
|
unpipe();
|
|
dest.removeListener('error', onerror);
|
|
if (EE.listenerCount(dest, 'error') === 0)
|
|
dest.emit('error', er);
|
|
}
|
|
// This is a brutally ugly hack to make sure that our error handler
|
|
// is attached before any userland ones. NEVER DO THIS.
|
|
if (!dest._events || !dest._events.error)
|
|
dest.on('error', onerror);
|
|
else if (isArray(dest._events.error))
|
|
dest._events.error.unshift(onerror);
|
|
else
|
|
dest._events.error = [onerror, dest._events.error];
|
|
|
|
|
|
|
|
// Both close and finish should trigger unpipe, but only once.
|
|
function onclose() {
|
|
dest.removeListener('finish', onfinish);
|
|
unpipe();
|
|
}
|
|
dest.once('close', onclose);
|
|
function onfinish() {
|
|
dest.removeListener('close', onclose);
|
|
unpipe();
|
|
}
|
|
dest.once('finish', onfinish);
|
|
|
|
function unpipe() {
|
|
src.unpipe(dest);
|
|
}
|
|
|
|
// tell the dest that it's being piped to
|
|
dest.emit('pipe', src);
|
|
|
|
// start the flow if it hasn't been started already.
|
|
if (!state.flowing) {
|
|
// the handler that waits for readable events after all
|
|
// the data gets sucked out in flow.
|
|
// This would be easier to follow with a .once() handler
|
|
// in flow(), but that is too slow.
|
|
this.on('readable', pipeOnReadable);
|
|
|
|
state.flowing = true;
|
|
process.nextTick(function() {
|
|
flow(src);
|
|
});
|
|
}
|
|
|
|
return dest;
|
|
};
|
|
|
|
function pipeOnDrain(src) {
|
|
return function() {
|
|
var dest = this;
|
|
var state = src._readableState;
|
|
state.awaitDrain--;
|
|
if (state.awaitDrain === 0)
|
|
flow(src);
|
|
};
|
|
}
|
|
|
|
function flow(src) {
|
|
var state = src._readableState;
|
|
var chunk;
|
|
state.awaitDrain = 0;
|
|
|
|
function write(dest, i, list) {
|
|
var written = dest.write(chunk);
|
|
if (false === written) {
|
|
state.awaitDrain++;
|
|
}
|
|
}
|
|
|
|
while (state.pipesCount && null !== (chunk = src.read())) {
|
|
|
|
if (state.pipesCount === 1)
|
|
write(state.pipes, 0, null);
|
|
else
|
|
forEach(state.pipes, write);
|
|
|
|
src.emit('data', chunk);
|
|
|
|
// if anyone needs a drain, then we have to wait for that.
|
|
if (state.awaitDrain > 0)
|
|
return;
|
|
}
|
|
|
|
// if every destination was unpiped, either before entering this
|
|
// function, or in the while loop, then stop flowing.
|
|
//
|
|
// NB: This is a pretty rare edge case.
|
|
if (state.pipesCount === 0) {
|
|
state.flowing = false;
|
|
|
|
// if there were data event listeners added, then switch to old mode.
|
|
if (EE.listenerCount(src, 'data') > 0)
|
|
emitDataEvents(src);
|
|
return;
|
|
}
|
|
|
|
// at this point, no one needed a drain, so we just ran out of data
|
|
// on the next readable event, start it over again.
|
|
state.ranOut = true;
|
|
}
|
|
|
|
function pipeOnReadable() {
|
|
if (this._readableState.ranOut) {
|
|
this._readableState.ranOut = false;
|
|
flow(this);
|
|
}
|
|
}
|
|
|
|
|
|
Readable.prototype.unpipe = function(dest) {
|
|
var state = this._readableState;
|
|
|
|
// if we're not piping anywhere, then do nothing.
|
|
if (state.pipesCount === 0)
|
|
return this;
|
|
|
|
// just one destination. most common case.
|
|
if (state.pipesCount === 1) {
|
|
// passed in one, but it's not the right one.
|
|
if (dest && dest !== state.pipes)
|
|
return this;
|
|
|
|
if (!dest)
|
|
dest = state.pipes;
|
|
|
|
// got a match.
|
|
state.pipes = null;
|
|
state.pipesCount = 0;
|
|
this.removeListener('readable', pipeOnReadable);
|
|
state.flowing = false;
|
|
if (dest)
|
|
dest.emit('unpipe', this);
|
|
return this;
|
|
}
|
|
|
|
// slow case. multiple pipe destinations.
|
|
|
|
if (!dest) {
|
|
// remove all.
|
|
var dests = state.pipes;
|
|
var len = state.pipesCount;
|
|
state.pipes = null;
|
|
state.pipesCount = 0;
|
|
this.removeListener('readable', pipeOnReadable);
|
|
state.flowing = false;
|
|
|
|
for (var i = 0; i < len; i++)
|
|
dests[i].emit('unpipe', this);
|
|
return this;
|
|
}
|
|
|
|
// try to find the right one.
|
|
var i = indexOf(state.pipes, dest);
|
|
if (i === -1)
|
|
return this;
|
|
|
|
state.pipes.splice(i, 1);
|
|
state.pipesCount -= 1;
|
|
if (state.pipesCount === 1)
|
|
state.pipes = state.pipes[0];
|
|
|
|
dest.emit('unpipe', this);
|
|
|
|
return this;
|
|
};
|
|
|
|
// set up data events if they are asked for
|
|
// Ensure readable listeners eventually get something
|
|
Readable.prototype.on = function(ev, fn) {
|
|
var res = Stream.prototype.on.call(this, ev, fn);
|
|
|
|
if (ev === 'data' && !this._readableState.flowing)
|
|
emitDataEvents(this);
|
|
|
|
if (ev === 'readable' && this.readable) {
|
|
var state = this._readableState;
|
|
if (!state.readableListening) {
|
|
state.readableListening = true;
|
|
state.emittedReadable = false;
|
|
state.needReadable = true;
|
|
if (!state.reading) {
|
|
this.read(0);
|
|
} else if (state.length) {
|
|
emitReadable(this, state);
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
};
|
|
Readable.prototype.addListener = Readable.prototype.on;
|
|
|
|
// pause() and resume() are remnants of the legacy readable stream API
|
|
// If the user uses them, then switch into old mode.
|
|
Readable.prototype.resume = function() {
|
|
emitDataEvents(this);
|
|
this.read(0);
|
|
this.emit('resume');
|
|
};
|
|
|
|
Readable.prototype.pause = function() {
|
|
emitDataEvents(this, true);
|
|
this.emit('pause');
|
|
};
|
|
|
|
function emitDataEvents(stream, startPaused) {
|
|
var state = stream._readableState;
|
|
|
|
if (state.flowing) {
|
|
// https://github.com/isaacs/readable-stream/issues/16
|
|
throw new Error('Cannot switch to old mode now.');
|
|
}
|
|
|
|
var paused = startPaused || false;
|
|
var readable = false;
|
|
|
|
// convert to an old-style stream.
|
|
stream.readable = true;
|
|
stream.pipe = Stream.prototype.pipe;
|
|
stream.on = stream.addListener = Stream.prototype.on;
|
|
|
|
stream.on('readable', function() {
|
|
readable = true;
|
|
|
|
var c;
|
|
while (!paused && (null !== (c = stream.read())))
|
|
stream.emit('data', c);
|
|
|
|
if (c === null) {
|
|
readable = false;
|
|
stream._readableState.needReadable = true;
|
|
}
|
|
});
|
|
|
|
stream.pause = function() {
|
|
paused = true;
|
|
this.emit('pause');
|
|
};
|
|
|
|
stream.resume = function() {
|
|
paused = false;
|
|
if (readable)
|
|
process.nextTick(function() {
|
|
stream.emit('readable');
|
|
});
|
|
else
|
|
this.read(0);
|
|
this.emit('resume');
|
|
};
|
|
|
|
// now make it start, just in case it hadn't already.
|
|
stream.emit('readable');
|
|
}
|
|
|
|
// wrap an old-style stream as the async data source.
|
|
// This is *not* part of the readable stream interface.
|
|
// It is an ugly unfortunate mess of history.
|
|
Readable.prototype.wrap = function(stream) {
|
|
var state = this._readableState;
|
|
var paused = false;
|
|
|
|
var self = this;
|
|
stream.on('end', function() {
|
|
if (state.decoder && !state.ended) {
|
|
var chunk = state.decoder.end();
|
|
if (chunk && chunk.length)
|
|
self.push(chunk);
|
|
}
|
|
|
|
self.push(null);
|
|
});
|
|
|
|
stream.on('data', function(chunk) {
|
|
if (state.decoder)
|
|
chunk = state.decoder.write(chunk);
|
|
|
|
// don't skip over falsy values in objectMode
|
|
//if (state.objectMode && util.isNullOrUndefined(chunk))
|
|
if (state.objectMode && (chunk === null || chunk === undefined))
|
|
return;
|
|
else if (!state.objectMode && (!chunk || !chunk.length))
|
|
return;
|
|
|
|
var ret = self.push(chunk);
|
|
if (!ret) {
|
|
paused = true;
|
|
stream.pause();
|
|
}
|
|
});
|
|
|
|
// proxy all the other methods.
|
|
// important when wrapping filters and duplexes.
|
|
for (var i in stream) {
|
|
if (typeof stream[i] === 'function' &&
|
|
typeof this[i] === 'undefined') {
|
|
this[i] = function(method) { return function() {
|
|
return stream[method].apply(stream, arguments);
|
|
}}(i);
|
|
}
|
|
}
|
|
|
|
// proxy certain important events.
|
|
var events = ['error', 'close', 'destroy', 'pause', 'resume'];
|
|
forEach(events, function(ev) {
|
|
stream.on(ev, self.emit.bind(self, ev));
|
|
});
|
|
|
|
// when we try to consume some more bytes, simply unpause the
|
|
// underlying stream.
|
|
self._read = function(n) {
|
|
if (paused) {
|
|
paused = false;
|
|
stream.resume();
|
|
}
|
|
};
|
|
|
|
return self;
|
|
};
|
|
|
|
|
|
|
|
// exposed for testing purposes only.
|
|
Readable._fromList = fromList;
|
|
|
|
// Pluck off n bytes from an array of buffers.
|
|
// Length is the combined lengths of all the buffers in the list.
|
|
function fromList(n, state) {
|
|
var list = state.buffer;
|
|
var length = state.length;
|
|
var stringMode = !!state.decoder;
|
|
var objectMode = !!state.objectMode;
|
|
var ret;
|
|
|
|
// nothing in the list, definitely empty.
|
|
if (list.length === 0)
|
|
return null;
|
|
|
|
if (length === 0)
|
|
ret = null;
|
|
else if (objectMode)
|
|
ret = list.shift();
|
|
else if (!n || n >= length) {
|
|
// read it all, truncate the array.
|
|
if (stringMode)
|
|
ret = list.join('');
|
|
else
|
|
ret = Buffer.concat(list, length);
|
|
list.length = 0;
|
|
} else {
|
|
// read just some of it.
|
|
if (n < list[0].length) {
|
|
// just take a part of the first list item.
|
|
// slice is the same for buffers and strings.
|
|
var buf = list[0];
|
|
ret = buf.slice(0, n);
|
|
list[0] = buf.slice(n);
|
|
} else if (n === list[0].length) {
|
|
// first list is a perfect match
|
|
ret = list.shift();
|
|
} else {
|
|
// complex case.
|
|
// we have enough to cover it, but it spans past the first buffer.
|
|
if (stringMode)
|
|
ret = '';
|
|
else
|
|
ret = new Buffer(n);
|
|
|
|
var c = 0;
|
|
for (var i = 0, l = list.length; i < l && c < n; i++) {
|
|
var buf = list[0];
|
|
var cpy = Math.min(n - c, buf.length);
|
|
|
|
if (stringMode)
|
|
ret += buf.slice(0, cpy);
|
|
else
|
|
buf.copy(ret, c, 0, cpy);
|
|
|
|
if (cpy < buf.length)
|
|
list[0] = buf.slice(cpy);
|
|
else
|
|
list.shift();
|
|
|
|
c += cpy;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
function endReadable(stream) {
|
|
var state = stream._readableState;
|
|
|
|
// If we get here before consuming all the bytes, then that is a
|
|
// bug in node. Should never happen.
|
|
if (state.length > 0)
|
|
throw new Error('endReadable called on non-empty stream');
|
|
|
|
if (!state.endEmitted && state.calledRead) {
|
|
state.ended = true;
|
|
process.nextTick(function() {
|
|
// Check that we didn't get one last unshift.
|
|
if (!state.endEmitted && state.length === 0) {
|
|
state.endEmitted = true;
|
|
stream.readable = false;
|
|
stream.emit('end');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function forEach (xs, f) {
|
|
for (var i = 0, l = xs.length; i < l; i++) {
|
|
f(xs[i], i);
|
|
}
|
|
}
|
|
|
|
function indexOf (xs, x) {
|
|
for (var i = 0, l = xs.length; i < l; i++) {
|
|
if (xs[i] === x) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
}).call(this,require('_process'))
|
|
},{"_process":133,"buffer":43,"core-util-is":44,"events":56,"inherits":87,"isarray":286,"stream":278,"string_decoder/":279}],291:[function(require,module,exports){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
|
|
// a transform stream is a readable/writable stream where you do
|
|
// something with the data. Sometimes it's called a "filter",
|
|
// but that's not a great name for it, since that implies a thing where
|
|
// some bits pass through, and others are simply ignored. (That would
|
|
// be a valid example of a transform, of course.)
|
|
//
|
|
// While the output is causally related to the input, it's not a
|
|
// necessarily symmetric or synchronous transformation. For example,
|
|
// a zlib stream might take multiple plain-text writes(), and then
|
|
// emit a single compressed chunk some time in the future.
|
|
//
|
|
// Here's how this works:
|
|
//
|
|
// The Transform stream has all the aspects of the readable and writable
|
|
// stream classes. When you write(chunk), that calls _write(chunk,cb)
|
|
// internally, and returns false if there's a lot of pending writes
|
|
// buffered up. When you call read(), that calls _read(n) until
|
|
// there's enough pending readable data buffered up.
|
|
//
|
|
// In a transform stream, the written data is placed in a buffer. When
|
|
// _read(n) is called, it transforms the queued up data, calling the
|
|
// buffered _write cb's as it consumes chunks. If consuming a single
|
|
// written chunk would result in multiple output chunks, then the first
|
|
// outputted bit calls the readcb, and subsequent chunks just go into
|
|
// the read buffer, and will cause it to emit 'readable' if necessary.
|
|
//
|
|
// This way, back-pressure is actually determined by the reading side,
|
|
// since _read has to be called to start processing a new chunk. However,
|
|
// a pathological inflate type of transform can cause excessive buffering
|
|
// here. For example, imagine a stream where every byte of input is
|
|
// interpreted as an integer from 0-255, and then results in that many
|
|
// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
|
|
// 1kb of data being output. In this case, you could write a very small
|
|
// amount of input, and end up with a very large amount of output. In
|
|
// such a pathological inflating mechanism, there'd be no way to tell
|
|
// the system to stop doing the transform. A single 4MB write could
|
|
// cause the system to run out of memory.
|
|
//
|
|
// However, even in such a pathological case, only a single written chunk
|
|
// would be consumed, and then the rest would wait (un-transformed) until
|
|
// the results of the previous transformed chunk were consumed.
|
|
|
|
module.exports = Transform;
|
|
|
|
var Duplex = require('./_stream_duplex');
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
util.inherits(Transform, Duplex);
|
|
|
|
|
|
function TransformState(options, stream) {
|
|
this.afterTransform = function(er, data) {
|
|
return afterTransform(stream, er, data);
|
|
};
|
|
|
|
this.needTransform = false;
|
|
this.transforming = false;
|
|
this.writecb = null;
|
|
this.writechunk = null;
|
|
}
|
|
|
|
function afterTransform(stream, er, data) {
|
|
var ts = stream._transformState;
|
|
ts.transforming = false;
|
|
|
|
var cb = ts.writecb;
|
|
|
|
if (!cb)
|
|
return stream.emit('error', new Error('no writecb in Transform class'));
|
|
|
|
ts.writechunk = null;
|
|
ts.writecb = null;
|
|
|
|
if (data !== null && data !== undefined)
|
|
stream.push(data);
|
|
|
|
if (cb)
|
|
cb(er);
|
|
|
|
var rs = stream._readableState;
|
|
rs.reading = false;
|
|
if (rs.needReadable || rs.length < rs.highWaterMark) {
|
|
stream._read(rs.highWaterMark);
|
|
}
|
|
}
|
|
|
|
|
|
function Transform(options) {
|
|
if (!(this instanceof Transform))
|
|
return new Transform(options);
|
|
|
|
Duplex.call(this, options);
|
|
|
|
var ts = this._transformState = new TransformState(options, this);
|
|
|
|
// when the writable side finishes, then flush out anything remaining.
|
|
var stream = this;
|
|
|
|
// start out asking for a readable event once data is transformed.
|
|
this._readableState.needReadable = true;
|
|
|
|
// we have implemented the _read method, and done the other things
|
|
// that Readable wants before the first _read call, so unset the
|
|
// sync guard flag.
|
|
this._readableState.sync = false;
|
|
|
|
this.once('finish', function() {
|
|
if ('function' === typeof this._flush)
|
|
this._flush(function(er) {
|
|
done(stream, er);
|
|
});
|
|
else
|
|
done(stream);
|
|
});
|
|
}
|
|
|
|
Transform.prototype.push = function(chunk, encoding) {
|
|
this._transformState.needTransform = false;
|
|
return Duplex.prototype.push.call(this, chunk, encoding);
|
|
};
|
|
|
|
// This is the part where you do stuff!
|
|
// override this function in implementation classes.
|
|
// 'chunk' is an input chunk.
|
|
//
|
|
// Call `push(newChunk)` to pass along transformed output
|
|
// to the readable side. You may call 'push' zero or more times.
|
|
//
|
|
// Call `cb(err)` when you are done with this chunk. If you pass
|
|
// an error, then that'll put the hurt on the whole operation. If you
|
|
// never call cb(), then you'll never get another chunk.
|
|
Transform.prototype._transform = function(chunk, encoding, cb) {
|
|
throw new Error('not implemented');
|
|
};
|
|
|
|
Transform.prototype._write = function(chunk, encoding, cb) {
|
|
var ts = this._transformState;
|
|
ts.writecb = cb;
|
|
ts.writechunk = chunk;
|
|
ts.writeencoding = encoding;
|
|
if (!ts.transforming) {
|
|
var rs = this._readableState;
|
|
if (ts.needTransform ||
|
|
rs.needReadable ||
|
|
rs.length < rs.highWaterMark)
|
|
this._read(rs.highWaterMark);
|
|
}
|
|
};
|
|
|
|
// Doesn't matter what the args are here.
|
|
// _transform does all the work.
|
|
// That we got here means that the readable side wants more data.
|
|
Transform.prototype._read = function(n) {
|
|
var ts = this._transformState;
|
|
|
|
if (ts.writechunk !== null && ts.writecb && !ts.transforming) {
|
|
ts.transforming = true;
|
|
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
|
|
} else {
|
|
// mark that we need a transform, so that any data that comes in
|
|
// will get processed, now that we've asked for it.
|
|
ts.needTransform = true;
|
|
}
|
|
};
|
|
|
|
|
|
function done(stream, er) {
|
|
if (er)
|
|
return stream.emit('error', er);
|
|
|
|
// if there's nothing in the write buffer, then that means
|
|
// that nothing more will ever be provided
|
|
var ws = stream._writableState;
|
|
var rs = stream._readableState;
|
|
var ts = stream._transformState;
|
|
|
|
if (ws.length)
|
|
throw new Error('calling transform done when ws.length != 0');
|
|
|
|
if (ts.transforming)
|
|
throw new Error('calling transform done when still transforming');
|
|
|
|
return stream.push(null);
|
|
}
|
|
|
|
},{"./_stream_duplex":288,"core-util-is":44,"inherits":87}],292:[function(require,module,exports){
|
|
(function (process){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
// A bit simpler than readable streams.
|
|
// Implement an async ._write(chunk, cb), and it'll handle all
|
|
// the drain event emission and buffering.
|
|
|
|
module.exports = Writable;
|
|
|
|
/*<replacement>*/
|
|
var Buffer = require('buffer').Buffer;
|
|
/*</replacement>*/
|
|
|
|
Writable.WritableState = WritableState;
|
|
|
|
|
|
/*<replacement>*/
|
|
var util = require('core-util-is');
|
|
util.inherits = require('inherits');
|
|
/*</replacement>*/
|
|
|
|
var Stream = require('stream');
|
|
|
|
util.inherits(Writable, Stream);
|
|
|
|
function WriteReq(chunk, encoding, cb) {
|
|
this.chunk = chunk;
|
|
this.encoding = encoding;
|
|
this.callback = cb;
|
|
}
|
|
|
|
function WritableState(options, stream) {
|
|
options = options || {};
|
|
|
|
// the point at which write() starts returning false
|
|
// Note: 0 is a valid value, means that we always return false if
|
|
// the entire buffer is not flushed immediately on write()
|
|
var hwm = options.highWaterMark;
|
|
this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;
|
|
|
|
// object stream flag to indicate whether or not this stream
|
|
// contains buffers or objects.
|
|
this.objectMode = !!options.objectMode;
|
|
|
|
// cast to ints.
|
|
this.highWaterMark = ~~this.highWaterMark;
|
|
|
|
this.needDrain = false;
|
|
// at the start of calling end()
|
|
this.ending = false;
|
|
// when end() has been called, and returned
|
|
this.ended = false;
|
|
// when 'finish' is emitted
|
|
this.finished = false;
|
|
|
|
// should we decode strings into buffers before passing to _write?
|
|
// this is here so that some node-core streams can optimize string
|
|
// handling at a lower level.
|
|
var noDecode = options.decodeStrings === false;
|
|
this.decodeStrings = !noDecode;
|
|
|
|
// Crypto is kind of old and crusty. Historically, its default string
|
|
// encoding is 'binary' so we have to make this configurable.
|
|
// Everything else in the universe uses 'utf8', though.
|
|
this.defaultEncoding = options.defaultEncoding || 'utf8';
|
|
|
|
// not an actual buffer we keep track of, but a measurement
|
|
// of how much we're waiting to get pushed to some underlying
|
|
// socket or file.
|
|
this.length = 0;
|
|
|
|
// a flag to see when we're in the middle of a write.
|
|
this.writing = false;
|
|
|
|
// a flag to be able to tell if the onwrite cb is called immediately,
|
|
// or on a later tick. We set this to true at first, becuase any
|
|
// actions that shouldn't happen until "later" should generally also
|
|
// not happen before the first write call.
|
|
this.sync = true;
|
|
|
|
// a flag to know if we're processing previously buffered items, which
|
|
// may call the _write() callback in the same tick, so that we don't
|
|
// end up in an overlapped onwrite situation.
|
|
this.bufferProcessing = false;
|
|
|
|
// the callback that's passed to _write(chunk,cb)
|
|
this.onwrite = function(er) {
|
|
onwrite(stream, er);
|
|
};
|
|
|
|
// the callback that the user supplies to write(chunk,encoding,cb)
|
|
this.writecb = null;
|
|
|
|
// the amount that is being written when _write is called.
|
|
this.writelen = 0;
|
|
|
|
this.buffer = [];
|
|
|
|
// True if the error was already emitted and should not be thrown again
|
|
this.errorEmitted = false;
|
|
}
|
|
|
|
function Writable(options) {
|
|
var Duplex = require('./_stream_duplex');
|
|
|
|
// Writable ctor is applied to Duplexes, though they're not
|
|
// instanceof Writable, they're instanceof Readable.
|
|
if (!(this instanceof Writable) && !(this instanceof Duplex))
|
|
return new Writable(options);
|
|
|
|
this._writableState = new WritableState(options, this);
|
|
|
|
// legacy.
|
|
this.writable = true;
|
|
|
|
Stream.call(this);
|
|
}
|
|
|
|
// Otherwise people can pipe Writable streams, which is just wrong.
|
|
Writable.prototype.pipe = function() {
|
|
this.emit('error', new Error('Cannot pipe. Not readable.'));
|
|
};
|
|
|
|
|
|
function writeAfterEnd(stream, state, cb) {
|
|
var er = new Error('write after end');
|
|
// TODO: defer error events consistently everywhere, not just the cb
|
|
stream.emit('error', er);
|
|
process.nextTick(function() {
|
|
cb(er);
|
|
});
|
|
}
|
|
|
|
// If we get something that is not a buffer, string, null, or undefined,
|
|
// and we're not in objectMode, then that's an error.
|
|
// Otherwise stream chunks are all considered to be of length=1, and the
|
|
// watermarks determine how many objects to keep in the buffer, rather than
|
|
// how many bytes or characters.
|
|
function validChunk(stream, state, chunk, cb) {
|
|
var valid = true;
|
|
if (!Buffer.isBuffer(chunk) &&
|
|
'string' !== typeof chunk &&
|
|
chunk !== null &&
|
|
chunk !== undefined &&
|
|
!state.objectMode) {
|
|
var er = new TypeError('Invalid non-string/buffer chunk');
|
|
stream.emit('error', er);
|
|
process.nextTick(function() {
|
|
cb(er);
|
|
});
|
|
valid = false;
|
|
}
|
|
return valid;
|
|
}
|
|
|
|
Writable.prototype.write = function(chunk, encoding, cb) {
|
|
var state = this._writableState;
|
|
var ret = false;
|
|
|
|
if (typeof encoding === 'function') {
|
|
cb = encoding;
|
|
encoding = null;
|
|
}
|
|
|
|
if (Buffer.isBuffer(chunk))
|
|
encoding = 'buffer';
|
|
else if (!encoding)
|
|
encoding = state.defaultEncoding;
|
|
|
|
if (typeof cb !== 'function')
|
|
cb = function() {};
|
|
|
|
if (state.ended)
|
|
writeAfterEnd(this, state, cb);
|
|
else if (validChunk(this, state, chunk, cb))
|
|
ret = writeOrBuffer(this, state, chunk, encoding, cb);
|
|
|
|
return ret;
|
|
};
|
|
|
|
function decodeChunk(state, chunk, encoding) {
|
|
if (!state.objectMode &&
|
|
state.decodeStrings !== false &&
|
|
typeof chunk === 'string') {
|
|
chunk = new Buffer(chunk, encoding);
|
|
}
|
|
return chunk;
|
|
}
|
|
|
|
// if we're already writing something, then just put this
|
|
// in the queue, and wait our turn. Otherwise, call _write
|
|
// If we return false, then we need a drain event, so set that flag.
|
|
function writeOrBuffer(stream, state, chunk, encoding, cb) {
|
|
chunk = decodeChunk(state, chunk, encoding);
|
|
if (Buffer.isBuffer(chunk))
|
|
encoding = 'buffer';
|
|
var len = state.objectMode ? 1 : chunk.length;
|
|
|
|
state.length += len;
|
|
|
|
var ret = state.length < state.highWaterMark;
|
|
// we must ensure that previous needDrain will not be reset to false.
|
|
if (!ret)
|
|
state.needDrain = true;
|
|
|
|
if (state.writing)
|
|
state.buffer.push(new WriteReq(chunk, encoding, cb));
|
|
else
|
|
doWrite(stream, state, len, chunk, encoding, cb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
function doWrite(stream, state, len, chunk, encoding, cb) {
|
|
state.writelen = len;
|
|
state.writecb = cb;
|
|
state.writing = true;
|
|
state.sync = true;
|
|
stream._write(chunk, encoding, state.onwrite);
|
|
state.sync = false;
|
|
}
|
|
|
|
function onwriteError(stream, state, sync, er, cb) {
|
|
if (sync)
|
|
process.nextTick(function() {
|
|
cb(er);
|
|
});
|
|
else
|
|
cb(er);
|
|
|
|
stream._writableState.errorEmitted = true;
|
|
stream.emit('error', er);
|
|
}
|
|
|
|
function onwriteStateUpdate(state) {
|
|
state.writing = false;
|
|
state.writecb = null;
|
|
state.length -= state.writelen;
|
|
state.writelen = 0;
|
|
}
|
|
|
|
function onwrite(stream, er) {
|
|
var state = stream._writableState;
|
|
var sync = state.sync;
|
|
var cb = state.writecb;
|
|
|
|
onwriteStateUpdate(state);
|
|
|
|
if (er)
|
|
onwriteError(stream, state, sync, er, cb);
|
|
else {
|
|
// Check if we're actually ready to finish, but don't emit yet
|
|
var finished = needFinish(stream, state);
|
|
|
|
if (!finished && !state.bufferProcessing && state.buffer.length)
|
|
clearBuffer(stream, state);
|
|
|
|
if (sync) {
|
|
process.nextTick(function() {
|
|
afterWrite(stream, state, finished, cb);
|
|
});
|
|
} else {
|
|
afterWrite(stream, state, finished, cb);
|
|
}
|
|
}
|
|
}
|
|
|
|
function afterWrite(stream, state, finished, cb) {
|
|
if (!finished)
|
|
onwriteDrain(stream, state);
|
|
cb();
|
|
if (finished)
|
|
finishMaybe(stream, state);
|
|
}
|
|
|
|
// Must force callback to be called on nextTick, so that we don't
|
|
// emit 'drain' before the write() consumer gets the 'false' return
|
|
// value, and has a chance to attach a 'drain' listener.
|
|
function onwriteDrain(stream, state) {
|
|
if (state.length === 0 && state.needDrain) {
|
|
state.needDrain = false;
|
|
stream.emit('drain');
|
|
}
|
|
}
|
|
|
|
|
|
// if there's something in the buffer waiting, then process it
|
|
function clearBuffer(stream, state) {
|
|
state.bufferProcessing = true;
|
|
|
|
for (var c = 0; c < state.buffer.length; c++) {
|
|
var entry = state.buffer[c];
|
|
var chunk = entry.chunk;
|
|
var encoding = entry.encoding;
|
|
var cb = entry.callback;
|
|
var len = state.objectMode ? 1 : chunk.length;
|
|
|
|
doWrite(stream, state, len, chunk, encoding, cb);
|
|
|
|
// if we didn't call the onwrite immediately, then
|
|
// it means that we need to wait until it does.
|
|
// also, that means that the chunk and cb are currently
|
|
// being processed, so move the buffer counter past them.
|
|
if (state.writing) {
|
|
c++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
state.bufferProcessing = false;
|
|
if (c < state.buffer.length)
|
|
state.buffer = state.buffer.slice(c);
|
|
else
|
|
state.buffer.length = 0;
|
|
}
|
|
|
|
Writable.prototype._write = function(chunk, encoding, cb) {
|
|
cb(new Error('not implemented'));
|
|
};
|
|
|
|
Writable.prototype.end = function(chunk, encoding, cb) {
|
|
var state = this._writableState;
|
|
|
|
if (typeof chunk === 'function') {
|
|
cb = chunk;
|
|
chunk = null;
|
|
encoding = null;
|
|
} else if (typeof encoding === 'function') {
|
|
cb = encoding;
|
|
encoding = null;
|
|
}
|
|
|
|
if (typeof chunk !== 'undefined' && chunk !== null)
|
|
this.write(chunk, encoding);
|
|
|
|
// ignore unnecessary end() calls.
|
|
if (!state.ending && !state.finished)
|
|
endWritable(this, state, cb);
|
|
};
|
|
|
|
|
|
function needFinish(stream, state) {
|
|
return (state.ending &&
|
|
state.length === 0 &&
|
|
!state.finished &&
|
|
!state.writing);
|
|
}
|
|
|
|
function finishMaybe(stream, state) {
|
|
var need = needFinish(stream, state);
|
|
if (need) {
|
|
state.finished = true;
|
|
stream.emit('finish');
|
|
}
|
|
return need;
|
|
}
|
|
|
|
function endWritable(stream, state, cb) {
|
|
state.ending = true;
|
|
finishMaybe(stream, state);
|
|
if (cb) {
|
|
if (state.finished)
|
|
process.nextTick(cb);
|
|
else
|
|
stream.once('finish', cb);
|
|
}
|
|
state.ended = true;
|
|
}
|
|
|
|
}).call(this,require('_process'))
|
|
},{"./_stream_duplex":288,"_process":133,"buffer":43,"core-util-is":44,"inherits":87,"stream":278}],293:[function(require,module,exports){
|
|
var Stream = require('stream'); // hack to fix a circular dependency issue when used with browserify
|
|
exports = module.exports = require('./lib/_stream_readable.js');
|
|
exports.Stream = Stream;
|
|
exports.Readable = exports;
|
|
exports.Writable = require('./lib/_stream_writable.js');
|
|
exports.Duplex = require('./lib/_stream_duplex.js');
|
|
exports.Transform = require('./lib/_stream_transform.js');
|
|
exports.PassThrough = require('./lib/_stream_passthrough.js');
|
|
|
|
},{"./lib/_stream_duplex.js":288,"./lib/_stream_passthrough.js":289,"./lib/_stream_readable.js":290,"./lib/_stream_transform.js":291,"./lib/_stream_writable.js":292,"stream":278}],294:[function(require,module,exports){
|
|
(function (global){
|
|
|
|
/**
|
|
* Module exports.
|
|
*/
|
|
|
|
module.exports = deprecate;
|
|
|
|
/**
|
|
* Mark that a method should not be used.
|
|
* Returns a modified function which warns once by default.
|
|
*
|
|
* If `localStorage.noDeprecation = true` is set, then it is a no-op.
|
|
*
|
|
* If `localStorage.throwDeprecation = true` is set, then deprecated functions
|
|
* will throw an Error when invoked.
|
|
*
|
|
* If `localStorage.traceDeprecation = true` is set, then deprecated functions
|
|
* will invoke `console.trace()` instead of `console.error()`.
|
|
*
|
|
* @param {Function} fn - the function to deprecate
|
|
* @param {String} msg - the string to print to the console when `fn` is invoked
|
|
* @returns {Function} a new "deprecated" version of `fn`
|
|
* @api public
|
|
*/
|
|
|
|
function deprecate (fn, msg) {
|
|
if (config('noDeprecation')) {
|
|
return fn;
|
|
}
|
|
|
|
var warned = false;
|
|
function deprecated() {
|
|
if (!warned) {
|
|
if (config('throwDeprecation')) {
|
|
throw new Error(msg);
|
|
} else if (config('traceDeprecation')) {
|
|
console.trace(msg);
|
|
} else {
|
|
console.warn(msg);
|
|
}
|
|
warned = true;
|
|
}
|
|
return fn.apply(this, arguments);
|
|
}
|
|
|
|
return deprecated;
|
|
}
|
|
|
|
/**
|
|
* Checks `localStorage` for boolean values for the given `name`.
|
|
*
|
|
* @param {String} name
|
|
* @returns {Boolean}
|
|
* @api private
|
|
*/
|
|
|
|
function config (name) {
|
|
// accessing global.localStorage can trigger a DOMException in sandboxed iframes
|
|
try {
|
|
if (!global.localStorage) return false;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
var val = global.localStorage[name];
|
|
if (null == val) return false;
|
|
return String(val).toLowerCase() === 'true';
|
|
}
|
|
|
|
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{}],295:[function(require,module,exports){
|
|
arguments[4][87][0].apply(exports,arguments)
|
|
},{"dup":87}],296:[function(require,module,exports){
|
|
module.exports = function isBuffer(arg) {
|
|
return arg && typeof arg === 'object'
|
|
&& typeof arg.copy === 'function'
|
|
&& typeof arg.fill === 'function'
|
|
&& typeof arg.readUInt8 === 'function';
|
|
}
|
|
},{}],297:[function(require,module,exports){
|
|
(function (process,global){
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// 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.
|
|
|
|
var formatRegExp = /%[sdj%]/g;
|
|
exports.format = function(f) {
|
|
if (!isString(f)) {
|
|
var objects = [];
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
objects.push(inspect(arguments[i]));
|
|
}
|
|
return objects.join(' ');
|
|
}
|
|
|
|
var i = 1;
|
|
var args = arguments;
|
|
var len = args.length;
|
|
var str = String(f).replace(formatRegExp, function(x) {
|
|
if (x === '%%') return '%';
|
|
if (i >= len) return x;
|
|
switch (x) {
|
|
case '%s': return String(args[i++]);
|
|
case '%d': return Number(args[i++]);
|
|
case '%j':
|
|
try {
|
|
return JSON.stringify(args[i++]);
|
|
} catch (_) {
|
|
return '[Circular]';
|
|
}
|
|
default:
|
|
return x;
|
|
}
|
|
});
|
|
for (var x = args[i]; i < len; x = args[++i]) {
|
|
if (isNull(x) || !isObject(x)) {
|
|
str += ' ' + x;
|
|
} else {
|
|
str += ' ' + inspect(x);
|
|
}
|
|
}
|
|
return str;
|
|
};
|
|
|
|
|
|
// Mark that a method should not be used.
|
|
// Returns a modified function which warns once by default.
|
|
// If --no-deprecation is set, then it is a no-op.
|
|
exports.deprecate = function(fn, msg) {
|
|
// Allow for deprecating things in the process of starting up.
|
|
if (isUndefined(global.process)) {
|
|
return function() {
|
|
return exports.deprecate(fn, msg).apply(this, arguments);
|
|
};
|
|
}
|
|
|
|
if (process.noDeprecation === true) {
|
|
return fn;
|
|
}
|
|
|
|
var warned = false;
|
|
function deprecated() {
|
|
if (!warned) {
|
|
if (process.throwDeprecation) {
|
|
throw new Error(msg);
|
|
} else if (process.traceDeprecation) {
|
|
console.trace(msg);
|
|
} else {
|
|
console.error(msg);
|
|
}
|
|
warned = true;
|
|
}
|
|
return fn.apply(this, arguments);
|
|
}
|
|
|
|
return deprecated;
|
|
};
|
|
|
|
|
|
var debugs = {};
|
|
var debugEnviron;
|
|
exports.debuglog = function(set) {
|
|
if (isUndefined(debugEnviron))
|
|
debugEnviron = process.env.NODE_DEBUG || '';
|
|
set = set.toUpperCase();
|
|
if (!debugs[set]) {
|
|
if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
|
|
var pid = process.pid;
|
|
debugs[set] = function() {
|
|
var msg = exports.format.apply(exports, arguments);
|
|
console.error('%s %d: %s', set, pid, msg);
|
|
};
|
|
} else {
|
|
debugs[set] = function() {};
|
|
}
|
|
}
|
|
return debugs[set];
|
|
};
|
|
|
|
|
|
/**
|
|
* Echos the value of a value. Trys to print the value out
|
|
* in the best way possible given the different types.
|
|
*
|
|
* @param {Object} obj The object to print out.
|
|
* @param {Object} opts Optional options object that alters the output.
|
|
*/
|
|
/* legacy: obj, showHidden, depth, colors*/
|
|
function inspect(obj, opts) {
|
|
// default options
|
|
var ctx = {
|
|
seen: [],
|
|
stylize: stylizeNoColor
|
|
};
|
|
// legacy...
|
|
if (arguments.length >= 3) ctx.depth = arguments[2];
|
|
if (arguments.length >= 4) ctx.colors = arguments[3];
|
|
if (isBoolean(opts)) {
|
|
// legacy...
|
|
ctx.showHidden = opts;
|
|
} else if (opts) {
|
|
// got an "options" object
|
|
exports._extend(ctx, opts);
|
|
}
|
|
// set default options
|
|
if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
|
|
if (isUndefined(ctx.depth)) ctx.depth = 2;
|
|
if (isUndefined(ctx.colors)) ctx.colors = false;
|
|
if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
|
|
if (ctx.colors) ctx.stylize = stylizeWithColor;
|
|
return formatValue(ctx, obj, ctx.depth);
|
|
}
|
|
exports.inspect = inspect;
|
|
|
|
|
|
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
|
|
inspect.colors = {
|
|
'bold' : [1, 22],
|
|
'italic' : [3, 23],
|
|
'underline' : [4, 24],
|
|
'inverse' : [7, 27],
|
|
'white' : [37, 39],
|
|
'grey' : [90, 39],
|
|
'black' : [30, 39],
|
|
'blue' : [34, 39],
|
|
'cyan' : [36, 39],
|
|
'green' : [32, 39],
|
|
'magenta' : [35, 39],
|
|
'red' : [31, 39],
|
|
'yellow' : [33, 39]
|
|
};
|
|
|
|
// Don't use 'blue' not visible on cmd.exe
|
|
inspect.styles = {
|
|
'special': 'cyan',
|
|
'number': 'yellow',
|
|
'boolean': 'yellow',
|
|
'undefined': 'grey',
|
|
'null': 'bold',
|
|
'string': 'green',
|
|
'date': 'magenta',
|
|
// "name": intentionally not styling
|
|
'regexp': 'red'
|
|
};
|
|
|
|
|
|
function stylizeWithColor(str, styleType) {
|
|
var style = inspect.styles[styleType];
|
|
|
|
if (style) {
|
|
return '\u001b[' + inspect.colors[style][0] + 'm' + str +
|
|
'\u001b[' + inspect.colors[style][1] + 'm';
|
|
} else {
|
|
return str;
|
|
}
|
|
}
|
|
|
|
|
|
function stylizeNoColor(str, styleType) {
|
|
return str;
|
|
}
|
|
|
|
|
|
function arrayToHash(array) {
|
|
var hash = {};
|
|
|
|
array.forEach(function(val, idx) {
|
|
hash[val] = true;
|
|
});
|
|
|
|
return hash;
|
|
}
|
|
|
|
|
|
function formatValue(ctx, value, recurseTimes) {
|
|
// Provide a hook for user-specified inspect functions.
|
|
// Check that value is an object with an inspect function on it
|
|
if (ctx.customInspect &&
|
|
value &&
|
|
isFunction(value.inspect) &&
|
|
// Filter out the util module, it's inspect function is special
|
|
value.inspect !== exports.inspect &&
|
|
// Also filter out any prototype objects using the circular check.
|
|
!(value.constructor && value.constructor.prototype === value)) {
|
|
var ret = value.inspect(recurseTimes, ctx);
|
|
if (!isString(ret)) {
|
|
ret = formatValue(ctx, ret, recurseTimes);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Primitive types cannot have properties
|
|
var primitive = formatPrimitive(ctx, value);
|
|
if (primitive) {
|
|
return primitive;
|
|
}
|
|
|
|
// Look up the keys of the object.
|
|
var keys = Object.keys(value);
|
|
var visibleKeys = arrayToHash(keys);
|
|
|
|
if (ctx.showHidden) {
|
|
keys = Object.getOwnPropertyNames(value);
|
|
}
|
|
|
|
// IE doesn't make error fields non-enumerable
|
|
// http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
|
|
if (isError(value)
|
|
&& (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
|
|
return formatError(value);
|
|
}
|
|
|
|
// Some type of object without properties can be shortcutted.
|
|
if (keys.length === 0) {
|
|
if (isFunction(value)) {
|
|
var name = value.name ? ': ' + value.name : '';
|
|
return ctx.stylize('[Function' + name + ']', 'special');
|
|
}
|
|
if (isRegExp(value)) {
|
|
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
|
|
}
|
|
if (isDate(value)) {
|
|
return ctx.stylize(Date.prototype.toString.call(value), 'date');
|
|
}
|
|
if (isError(value)) {
|
|
return formatError(value);
|
|
}
|
|
}
|
|
|
|
var base = '', array = false, braces = ['{', '}'];
|
|
|
|
// Make Array say that they are Array
|
|
if (isArray(value)) {
|
|
array = true;
|
|
braces = ['[', ']'];
|
|
}
|
|
|
|
// Make functions say that they are functions
|
|
if (isFunction(value)) {
|
|
var n = value.name ? ': ' + value.name : '';
|
|
base = ' [Function' + n + ']';
|
|
}
|
|
|
|
// Make RegExps say that they are RegExps
|
|
if (isRegExp(value)) {
|
|
base = ' ' + RegExp.prototype.toString.call(value);
|
|
}
|
|
|
|
// Make dates with properties first say the date
|
|
if (isDate(value)) {
|
|
base = ' ' + Date.prototype.toUTCString.call(value);
|
|
}
|
|
|
|
// Make error with message first say the error
|
|
if (isError(value)) {
|
|
base = ' ' + formatError(value);
|
|
}
|
|
|
|
if (keys.length === 0 && (!array || value.length == 0)) {
|
|
return braces[0] + base + braces[1];
|
|
}
|
|
|
|
if (recurseTimes < 0) {
|
|
if (isRegExp(value)) {
|
|
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
|
|
} else {
|
|
return ctx.stylize('[Object]', 'special');
|
|
}
|
|
}
|
|
|
|
ctx.seen.push(value);
|
|
|
|
var output;
|
|
if (array) {
|
|
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
|
|
} else {
|
|
output = keys.map(function(key) {
|
|
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
|
|
});
|
|
}
|
|
|
|
ctx.seen.pop();
|
|
|
|
return reduceToSingleString(output, base, braces);
|
|
}
|
|
|
|
|
|
function formatPrimitive(ctx, value) {
|
|
if (isUndefined(value))
|
|
return ctx.stylize('undefined', 'undefined');
|
|
if (isString(value)) {
|
|
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
|
|
.replace(/'/g, "\\'")
|
|
.replace(/\\"/g, '"') + '\'';
|
|
return ctx.stylize(simple, 'string');
|
|
}
|
|
if (isNumber(value))
|
|
return ctx.stylize('' + value, 'number');
|
|
if (isBoolean(value))
|
|
return ctx.stylize('' + value, 'boolean');
|
|
// For some reason typeof null is "object", so special case here.
|
|
if (isNull(value))
|
|
return ctx.stylize('null', 'null');
|
|
}
|
|
|
|
|
|
function formatError(value) {
|
|
return '[' + Error.prototype.toString.call(value) + ']';
|
|
}
|
|
|
|
|
|
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
|
|
var output = [];
|
|
for (var i = 0, l = value.length; i < l; ++i) {
|
|
if (hasOwnProperty(value, String(i))) {
|
|
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
|
|
String(i), true));
|
|
} else {
|
|
output.push('');
|
|
}
|
|
}
|
|
keys.forEach(function(key) {
|
|
if (!key.match(/^\d+$/)) {
|
|
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
|
|
key, true));
|
|
}
|
|
});
|
|
return output;
|
|
}
|
|
|
|
|
|
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
|
|
var name, str, desc;
|
|
desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
|
|
if (desc.get) {
|
|
if (desc.set) {
|
|
str = ctx.stylize('[Getter/Setter]', 'special');
|
|
} else {
|
|
str = ctx.stylize('[Getter]', 'special');
|
|
}
|
|
} else {
|
|
if (desc.set) {
|
|
str = ctx.stylize('[Setter]', 'special');
|
|
}
|
|
}
|
|
if (!hasOwnProperty(visibleKeys, key)) {
|
|
name = '[' + key + ']';
|
|
}
|
|
if (!str) {
|
|
if (ctx.seen.indexOf(desc.value) < 0) {
|
|
if (isNull(recurseTimes)) {
|
|
str = formatValue(ctx, desc.value, null);
|
|
} else {
|
|
str = formatValue(ctx, desc.value, recurseTimes - 1);
|
|
}
|
|
if (str.indexOf('\n') > -1) {
|
|
if (array) {
|
|
str = str.split('\n').map(function(line) {
|
|
return ' ' + line;
|
|
}).join('\n').substr(2);
|
|
} else {
|
|
str = '\n' + str.split('\n').map(function(line) {
|
|
return ' ' + line;
|
|
}).join('\n');
|
|
}
|
|
}
|
|
} else {
|
|
str = ctx.stylize('[Circular]', 'special');
|
|
}
|
|
}
|
|
if (isUndefined(name)) {
|
|
if (array && key.match(/^\d+$/)) {
|
|
return str;
|
|
}
|
|
name = JSON.stringify('' + key);
|
|
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
|
|
name = name.substr(1, name.length - 2);
|
|
name = ctx.stylize(name, 'name');
|
|
} else {
|
|
name = name.replace(/'/g, "\\'")
|
|
.replace(/\\"/g, '"')
|
|
.replace(/(^"|"$)/g, "'");
|
|
name = ctx.stylize(name, 'string');
|
|
}
|
|
}
|
|
|
|
return name + ': ' + str;
|
|
}
|
|
|
|
|
|
function reduceToSingleString(output, base, braces) {
|
|
var numLinesEst = 0;
|
|
var length = output.reduce(function(prev, cur) {
|
|
numLinesEst++;
|
|
if (cur.indexOf('\n') >= 0) numLinesEst++;
|
|
return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
|
|
}, 0);
|
|
|
|
if (length > 60) {
|
|
return braces[0] +
|
|
(base === '' ? '' : base + '\n ') +
|
|
' ' +
|
|
output.join(',\n ') +
|
|
' ' +
|
|
braces[1];
|
|
}
|
|
|
|
return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
|
|
}
|
|
|
|
|
|
// NOTE: These type checking functions intentionally don't use `instanceof`
|
|
// because it is fragile and can be easily faked with `Object.create()`.
|
|
function isArray(ar) {
|
|
return Array.isArray(ar);
|
|
}
|
|
exports.isArray = isArray;
|
|
|
|
function isBoolean(arg) {
|
|
return typeof arg === 'boolean';
|
|
}
|
|
exports.isBoolean = isBoolean;
|
|
|
|
function isNull(arg) {
|
|
return arg === null;
|
|
}
|
|
exports.isNull = isNull;
|
|
|
|
function isNullOrUndefined(arg) {
|
|
return arg == null;
|
|
}
|
|
exports.isNullOrUndefined = isNullOrUndefined;
|
|
|
|
function isNumber(arg) {
|
|
return typeof arg === 'number';
|
|
}
|
|
exports.isNumber = isNumber;
|
|
|
|
function isString(arg) {
|
|
return typeof arg === 'string';
|
|
}
|
|
exports.isString = isString;
|
|
|
|
function isSymbol(arg) {
|
|
return typeof arg === 'symbol';
|
|
}
|
|
exports.isSymbol = isSymbol;
|
|
|
|
function isUndefined(arg) {
|
|
return arg === void 0;
|
|
}
|
|
exports.isUndefined = isUndefined;
|
|
|
|
function isRegExp(re) {
|
|
return isObject(re) && objectToString(re) === '[object RegExp]';
|
|
}
|
|
exports.isRegExp = isRegExp;
|
|
|
|
function isObject(arg) {
|
|
return typeof arg === 'object' && arg !== null;
|
|
}
|
|
exports.isObject = isObject;
|
|
|
|
function isDate(d) {
|
|
return isObject(d) && objectToString(d) === '[object Date]';
|
|
}
|
|
exports.isDate = isDate;
|
|
|
|
function isError(e) {
|
|
return isObject(e) &&
|
|
(objectToString(e) === '[object Error]' || e instanceof Error);
|
|
}
|
|
exports.isError = isError;
|
|
|
|
function isFunction(arg) {
|
|
return typeof arg === 'function';
|
|
}
|
|
exports.isFunction = isFunction;
|
|
|
|
function isPrimitive(arg) {
|
|
return arg === null ||
|
|
typeof arg === 'boolean' ||
|
|
typeof arg === 'number' ||
|
|
typeof arg === 'string' ||
|
|
typeof arg === 'symbol' || // ES6 symbol
|
|
typeof arg === 'undefined';
|
|
}
|
|
exports.isPrimitive = isPrimitive;
|
|
|
|
exports.isBuffer = require('./support/isBuffer');
|
|
|
|
function objectToString(o) {
|
|
return Object.prototype.toString.call(o);
|
|
}
|
|
|
|
|
|
function pad(n) {
|
|
return n < 10 ? '0' + n.toString(10) : n.toString(10);
|
|
}
|
|
|
|
|
|
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
|
|
'Oct', 'Nov', 'Dec'];
|
|
|
|
// 26 Feb 16:19:34
|
|
function timestamp() {
|
|
var d = new Date();
|
|
var time = [pad(d.getHours()),
|
|
pad(d.getMinutes()),
|
|
pad(d.getSeconds())].join(':');
|
|
return [d.getDate(), months[d.getMonth()], time].join(' ');
|
|
}
|
|
|
|
|
|
// log is just a thin wrapper to console.log that prepends a timestamp
|
|
exports.log = function() {
|
|
console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
|
|
};
|
|
|
|
|
|
/**
|
|
* Inherit the prototype methods from one constructor into another.
|
|
*
|
|
* The Function.prototype.inherits from lang.js rewritten as a standalone
|
|
* function (not on Function.prototype). NOTE: If this file is to be loaded
|
|
* during bootstrapping this function needs to be rewritten using some native
|
|
* functions as prototype setup using normal JavaScript does not work as
|
|
* expected during bootstrapping (see mirror.js in r114903).
|
|
*
|
|
* @param {function} ctor Constructor function which needs to inherit the
|
|
* prototype.
|
|
* @param {function} superCtor Constructor function to inherit prototype from.
|
|
*/
|
|
exports.inherits = require('inherits');
|
|
|
|
exports._extend = function(origin, add) {
|
|
// Don't do anything if add isn't an object
|
|
if (!add || !isObject(add)) return origin;
|
|
|
|
var keys = Object.keys(add);
|
|
var i = keys.length;
|
|
while (i--) {
|
|
origin[keys[i]] = add[keys[i]];
|
|
}
|
|
return origin;
|
|
};
|
|
|
|
function hasOwnProperty(obj, prop) {
|
|
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
}
|
|
|
|
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{"./support/isBuffer":296,"_process":133,"inherits":295}],298:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
/**
|
|
* Stringify/parse functions that don't operate
|
|
* recursively, so they avoid call stack exceeded
|
|
* errors.
|
|
*/
|
|
exports.stringify = function stringify(input) {
|
|
var queue = [];
|
|
queue.push({obj: input});
|
|
|
|
var res = '';
|
|
var next, obj, prefix, val, i, arrayPrefix, keys, k, key, value, objPrefix;
|
|
while ((next = queue.pop())) {
|
|
obj = next.obj;
|
|
prefix = next.prefix || '';
|
|
val = next.val || '';
|
|
res += prefix;
|
|
if (val) {
|
|
res += val;
|
|
} else if (typeof obj !== 'object') {
|
|
res += typeof obj === 'undefined' ? null : JSON.stringify(obj);
|
|
} else if (obj === null) {
|
|
res += 'null';
|
|
} else if (Array.isArray(obj)) {
|
|
queue.push({val: ']'});
|
|
for (i = obj.length - 1; i >= 0; i--) {
|
|
arrayPrefix = i === 0 ? '' : ',';
|
|
queue.push({obj: obj[i], prefix: arrayPrefix});
|
|
}
|
|
queue.push({val: '['});
|
|
} else { // object
|
|
keys = [];
|
|
for (k in obj) {
|
|
if (obj.hasOwnProperty(k)) {
|
|
keys.push(k);
|
|
}
|
|
}
|
|
queue.push({val: '}'});
|
|
for (i = keys.length - 1; i >= 0; i--) {
|
|
key = keys[i];
|
|
value = obj[key];
|
|
objPrefix = (i > 0 ? ',' : '');
|
|
objPrefix += JSON.stringify(key) + ':';
|
|
queue.push({obj: value, prefix: objPrefix});
|
|
}
|
|
queue.push({val: '{'});
|
|
}
|
|
}
|
|
return res;
|
|
};
|
|
|
|
// Convenience function for the parse function.
|
|
// This pop function is basically copied from
|
|
// pouchCollate.parseIndexableString
|
|
function pop(obj, stack, metaStack) {
|
|
var lastMetaElement = metaStack[metaStack.length - 1];
|
|
if (obj === lastMetaElement.element) {
|
|
// popping a meta-element, e.g. an object whose value is another object
|
|
metaStack.pop();
|
|
lastMetaElement = metaStack[metaStack.length - 1];
|
|
}
|
|
var element = lastMetaElement.element;
|
|
var lastElementIndex = lastMetaElement.index;
|
|
if (Array.isArray(element)) {
|
|
element.push(obj);
|
|
} else if (lastElementIndex === stack.length - 2) { // obj with key+value
|
|
var key = stack.pop();
|
|
element[key] = obj;
|
|
} else {
|
|
stack.push(obj); // obj with key only
|
|
}
|
|
}
|
|
|
|
exports.parse = function (str) {
|
|
var stack = [];
|
|
var metaStack = []; // stack for arrays and objects
|
|
var i = 0;
|
|
var collationIndex,parsedNum,numChar;
|
|
var parsedString,lastCh,numConsecutiveSlashes,ch;
|
|
var arrayElement, objElement;
|
|
while (true) {
|
|
collationIndex = str[i++];
|
|
if (collationIndex === '}' ||
|
|
collationIndex === ']' ||
|
|
typeof collationIndex === 'undefined') {
|
|
if (stack.length === 1) {
|
|
return stack.pop();
|
|
} else {
|
|
pop(stack.pop(), stack, metaStack);
|
|
continue;
|
|
}
|
|
}
|
|
switch (collationIndex) {
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
case ':':
|
|
case ',':
|
|
break;
|
|
case 'n':
|
|
i += 3; // 'ull'
|
|
pop(null, stack, metaStack);
|
|
break;
|
|
case 't':
|
|
i += 3; // 'rue'
|
|
pop(true, stack, metaStack);
|
|
break;
|
|
case 'f':
|
|
i += 4; // 'alse'
|
|
pop(false, stack, metaStack);
|
|
break;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
case '-':
|
|
parsedNum = '';
|
|
i--;
|
|
while (true) {
|
|
numChar = str[i++];
|
|
if (/[\d\.\-e\+]/.test(numChar)) {
|
|
parsedNum += numChar;
|
|
} else {
|
|
i--;
|
|
break;
|
|
}
|
|
}
|
|
pop(parseFloat(parsedNum), stack, metaStack);
|
|
break;
|
|
case '"':
|
|
parsedString = '';
|
|
lastCh = void 0;
|
|
numConsecutiveSlashes = 0;
|
|
while (true) {
|
|
ch = str[i++];
|
|
if (ch !== '"' || (lastCh === '\\' &&
|
|
numConsecutiveSlashes % 2 === 1)) {
|
|
parsedString += ch;
|
|
lastCh = ch;
|
|
if (lastCh === '\\') {
|
|
numConsecutiveSlashes++;
|
|
} else {
|
|
numConsecutiveSlashes = 0;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
pop(JSON.parse('"' + parsedString + '"'), stack, metaStack);
|
|
break;
|
|
case '[':
|
|
arrayElement = { element: [], index: stack.length };
|
|
stack.push(arrayElement.element);
|
|
metaStack.push(arrayElement);
|
|
break;
|
|
case '{':
|
|
objElement = { element: {}, index: stack.length };
|
|
stack.push(objElement.element);
|
|
metaStack.push(objElement);
|
|
break;
|
|
default:
|
|
throw new Error(
|
|
'unexpectedly reached end of input: ' + collationIndex);
|
|
}
|
|
}
|
|
};
|
|
|
|
},{}],299:[function(require,module,exports){
|
|
module.exports = extend
|
|
|
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
|
|
function extend() {
|
|
var target = {}
|
|
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
var source = arguments[i]
|
|
|
|
for (var key in source) {
|
|
if (hasOwnProperty.call(source, key)) {
|
|
target[key] = source[key]
|
|
}
|
|
}
|
|
}
|
|
|
|
return target
|
|
}
|
|
|
|
},{}]},{},[1]);
|