<commit_before>// Simple persistence driver that saves data to plain text files

'use strict';

var assign            = require('es5-ext/object/assign')
  , forEach           = require('es5-ext/object/for-each')
  , setPrototypeOf    = require('es5-ext/object/set-prototype-of')
  , toArray           = require('es5-ext/object/to-array')
  , ensureObject      = require('es5-ext/object/valid-object')
  , ensureString      = require('es5-ext/object/validate-stringifiable-value')
  , d                 = require('d')
  , memoizeMethods    = require('memoizee/methods')
  , deferred          = require('deferred')
  , resolveKeyPath    = require('dbjs/_setup/utils/resolve-key-path')
  , resolve           = require('path').resolve
  , mkdir             = require('fs2/mkdir')
  , readFile          = require('fs2/read-file')
  , readdir           = require('fs2/readdir')
  , rmdir             = require('fs2/rmdir')
  , writeFile         = require('fs2/write-file')
  , PersistenceDriver = require('./abstract')

  , isId = RegExp.prototype.test.bind(/^[a-z0-9][a-z0-9A-Z]*$/)
  , isArray = Array.isArray, keys = Object.keys, create = Object.create
  , parse = JSON.parse, stringify = JSON.stringify;

var byStamp = function (a, b) {
	return (this[a].stamp - this[b].stamp) || a.toLowerCase().localeCompare(b.toLowerCase());
};

var resolveObjectMap = function (ownerId, map, keyPaths, result) {
	if (!result) result = create(null);
	forEach(map, function (data, path) {
		if (keyPaths && (path !== '.')) {
			if (!keyPaths.has(resolveKeyPath(path))) return;
		}
		result[(path === '.') ? ownerId : ownerId + '/' + path] = data;
	});
	return result;
};

var TextFileDriver = module.exports = function (dbjs, data) {
	if (!(this instanceof TextFileDriver)) return new TextFileDriver(dbjs, data);
	ensureObject(data);
	this.dirPath = resolve(ensureString(data.path));
	PersistenceDriver.call(this, dbjs, data);
	this.dbDir = mkdir(this.dirPath, { intermediate: true }).aside(null, function (err) {
		this.isClosed = true;
		this.emitError(err);
	}.bind(this));
};
setPrototypeOf(TextFileDriver, PersistenceDriver);

TextFileDriver.prototype = Object.create(PersistenceDriver.prototype, assign({
	constructor: d(TextFileDriver),
	// Any data
	__getRaw: d(function (cat, ns, path) {
		if (cat === 'reduced') {
			return this._getReducedStorage(ns)(function (map) { return map[path || '.'] || null; });
		}
		if (cat === 'computed') {
			return this._getIndexStorage(ns)(function (map) { return map[path] || null; });
		}
		return this._getObjectStorage(ns)(function (map) { return map[path || '.'] || null; });
	}),
	__getRawObject: d(function (ownerId, keyPaths) {
		return this._getObjectStorage(ownerId)(function (map) {
			return resolveObjectMap(ownerId, map, keyPaths);
		});
	}),
	__storeRaw: d(function (cat, ns, path, data) {
		if (cat === 'reduced') {
			return this._getReducedStorage(ns)(function (map) {
				map[path || '.'] = data;
				return this._writeStorage('reduced/' + ns, map);
			}.bind(this));
		}
		if (cat === 'computed') {
			return this._getIndexStorage(ns)(function (map) {
				map[path] = data;
				return this._writeStorage('computed/=' + (new Buffer(ns)).toString('base64'), map);
			}.bind(this));
		}
		return this._getObjectStorage(ns)(function (map) {
			map[path || '.'] = data;
			return this._writeStorage('direct/' + ns, map);
		}.bind(this));
	}),

	// Database data
	__getRawAllDirect: d(function () {
		return this._getAllObjectIds().map(function (ownerId) {
			return this._getObjectStorage(ownerId)(function (map) {
				return { ownerId: ownerId, map: map };
			});
		}, this)(function (maps) {
			var result = create(null);
			maps.forEach(function (data) { resolveObjectMap(data.ownerId, data.map, null, result); });
			return result;
		});
	}),

	// Size tracking
	__searchDirect: d(function (callback) {
		return this._getAllObjectIds().map(function (ownerId) {
			return this._getObjectStorage(ownerId)(function (map) {
				forEach(map, function (data, keyPath) {
					var postfix = keyPath === '.' ? '' : '/' + keyPath;
					callback(ownerId + postfix, data);
				});
			});
		}, this);
	}),
	__searchComputed: d(function (keyPath, callback) {
		return this._getIndexStorage(keyPath)(function (map) {
			forEach(map, function (data, ownerId) { callback(ownerId, data); });
		});
	}),

	// Reduced
	__getReducedNs: d(function (ns, keyPaths) {
		return this._getReducedStorage(ns)(function (map) {
			var result = create(null);
			forEach(map, function (data, path) {
				if (path === '.') path = null;
				if (path && keyPaths && !keyPaths.has(path)) return;
				result[ns + (path ? ('/' + path) : '')] = data;
			});
			return result;
		});
	}),

	// Storage import/export
	__exportAll: d(function (destDriver) {
		var count = 0;
		var promise = this.dbDir()(function () {
			return deferred(
				readdir(resolve(this.dirPath, 'direct'), { type: { file: true } }).catch(function (e) {
					if (e.code === 'ENOENT') return [];
					throw e;
				}).map(function (filename) {
					var ownerId = filename;
					return this._getObjectStorage(ownerId)(function (map) {
						return deferred.map(keys(map), function (path) {
							var data = this[path];
							if (path === '.') path = null;
							if (!(++count % 1000)) promise.emit('progress');
							return destDriver._storeRaw('direct', ownerId, path, data);
						}, map);
					});
				}.bind(this)),
				readdir(resolve(this.dirPath, 'computed'), { type: { file: true } }).catch(function (e) {
					if (e.code === 'ENOENT') return [];
					throw e;
				}).map(function (filename) {
					var keyPath = String(new Buffer(filename.slice(1), 'base64'));
					return this._getIndexStorage(keyPath)(function (map) {
						return deferred.map(keys(map), function (ownerId) {
							if (!(++count % 1000)) promise.emit('progress');
							return destDriver._storeRaw('computed', keyPath, ownerId, this[ownerId]);
						}, map);
					});
				}.bind(this)),
				readdir(resolve(this.dirPath, 'reduced'), { type: { file: true } }).catch(function (e) {
					if (e.code === 'ENOENT') return [];
					throw e;
				}).map(function (ns) {
					return this._getReducedStorage(ns)(function (map) {
						return deferred.map(keys(map), function (path) {
							if (!(++count % 1000)) promise.emit('progress');
							return destDriver._storeRaw('reduced', ns, (path === '.') ? null : path, this[path]);
						}, map);
					});
				}.bind(this))
			);
		}.bind(this));
		return promise;
	}),
	__clear: d(function () {
		return rmdir(this.dirPath, { recursive: true, force: true })(function () {
			this._getObjectStorage.clear();
			this._getIndexStorage.clear();
			this._getReducedStorage.clear();
			return (this.dbDir = mkdir(this.dirPath, { intermediate: true }));
		}.bind(this));
	}),

	// Connection related
	__close: d(function () { return deferred(undefined); }), // Nothing to close

	// Specific to driver
	_getAllObjectIds: d(function () {
		return this.dbDir()(function () {
			return readdir(resolve(this.dirPath, 'direct'), { type: { file: true } })(function (data) {
				return data.filter(isId).sort();
			}, function (e) {
				if (e.code === 'ENOENT') return [];
				throw e;
			});
		}.bind(this));
	}),
	_writeStorage: d(function (name, map) {
		return this.dbDir()(function () {
			return writeFile(resolve(this.dirPath, name), toArray(map, function (data, id) {
				var value = data.value;
				if (value === '') value = '-';
				else if (isArray(value)) value = stringify(value);
				return id + '\n' + data.stamp + '\n' + value;
			}, this, byStamp).join('\n\n'), { intermediate: true });
		}.bind(this));
	})
}, memoizeMethods({
	_getObjectStorage: d(function (ownerId) {
		return this.dbDir()(function () {
			var map = create(null);
			return readFile(resolve(this.dirPath, 'direct', ownerId))(function (data) {
				var value;
				try {
					String(data).split('\n\n').forEach(function (data) {
						data = data.split('\n');
						value = data[2];
						if (value === '-') value = '';
						map[data[0]] = {
							stamp: Number(data[1]),
							value: value
						};
					});
				} catch (ignore) {}
				return map;
			}, function (err) {
				if (err.code !== 'ENOENT') throw err;
				return map;
			});
		}.bind(this));
	}, { primitive: true }),
	_getIndexStorage: d(function (keyPath) {
		return this.dbDir()(function () {
			var map = create(null), filename = '=' + (new Buffer(keyPath)).toString('base64');
			return readFile(resolve(this.dirPath, 'computed', filename))(function (data) {
				var value;
				try {
					String(data).split('\n\n').forEach(function (data) {
						data = data.split('\n');
						value = data[2];
						if (value[0] === '[') value = parse(data[2]);
						else if (value === '-') value = '';
						map[data[0]] = {
							stamp: Number(data[1]),
							value: value
						};
					});
				} catch (ignore) {}
				return map;
			}, function (err) {
				if (err.code !== 'ENOENT') throw err;
				return map;
			});
		}.bind(this));
	}, { primitive: true }),
	_getReducedStorage: d(function (ns) {
		return this.dbDir()(function () {
			var map = create(null);
			return readFile(resolve(this.dirPath, 'reduced', ns))(function (data) {
				var value;
				try {
					String(data).split('\n\n').forEach(function (data) {
						data = data.split('\n');
						value = data[2];
						if (value[0] === '[') value = parse(data[2]);
						else if (value === '-') value = '';
						map[data[0]] = {
							stamp: Number(data[1]),
							value: value
						};
					});
				} catch (ignore) {}
				return map;
			}, function (err) {
				if (err.code !== 'ENOENT') throw err;
				return map;
			});
		}.bind(this));
	}, { primitive: true })
})));
<commit_msg>Rename _getIndexStorage into _getComputedStorage<commit_after>// Simple persistence driver that saves data to plain text files

'use strict';

var assign            = require('es5-ext/object/assign')
  , forEach           = require('es5-ext/object/for-each')
  , setPrototypeOf    = require('es5-ext/object/set-prototype-of')
  , toArray           = require('es5-ext/object/to-array')
  , ensureObject      = require('es5-ext/object/valid-object')
  , ensureString      = require('es5-ext/object/validate-stringifiable-value')
  , d                 = require('d')
  , memoizeMethods    = require('memoizee/methods')
  , deferred          = require('deferred')
  , resolveKeyPath    = require('dbjs/_setup/utils/resolve-key-path')
  , resolve           = require('path').resolve
  , mkdir             = require('fs2/mkdir')
  , readFile          = require('fs2/read-file')
  , readdir           = require('fs2/readdir')
  , rmdir             = require('fs2/rmdir')
  , writeFile         = require('fs2/write-file')
  , PersistenceDriver = require('./abstract')

  , isId = RegExp.prototype.test.bind(/^[a-z0-9][a-z0-9A-Z]*$/)
  , isArray = Array.isArray, keys = Object.keys, create = Object.create
  , parse = JSON.parse, stringify = JSON.stringify;

var byStamp = function (a, b) {
	return (this[a].stamp - this[b].stamp) || a.toLowerCase().localeCompare(b.toLowerCase());
};

var resolveObjectMap = function (ownerId, map, keyPaths, result) {
	if (!result) result = create(null);
	forEach(map, function (data, path) {
		if (keyPaths && (path !== '.')) {
			if (!keyPaths.has(resolveKeyPath(path))) return;
		}
		result[(path === '.') ? ownerId : ownerId + '/' + path] = data;
	});
	return result;
};

var TextFileDriver = module.exports = function (dbjs, data) {
	if (!(this instanceof TextFileDriver)) return new TextFileDriver(dbjs, data);
	ensureObject(data);
	this.dirPath = resolve(ensureString(data.path));
	PersistenceDriver.call(this, dbjs, data);
	this.dbDir = mkdir(this.dirPath, { intermediate: true }).aside(null, function (err) {
		this.isClosed = true;
		this.emitError(err);
	}.bind(this));
};
setPrototypeOf(TextFileDriver, PersistenceDriver);

TextFileDriver.prototype = Object.create(PersistenceDriver.prototype, assign({
	constructor: d(TextFileDriver),
	// Any data
	__getRaw: d(function (cat, ns, path) {
		if (cat === 'reduced') {
			return this._getReducedStorage(ns)(function (map) { return map[path || '.'] || null; });
		}
		if (cat === 'computed') {
			return this._getComputedStorage(ns)(function (map) { return map[path] || null; });
		}
		return this._getObjectStorage(ns)(function (map) { return map[path || '.'] || null; });
	}),
	__getRawObject: d(function (ownerId, keyPaths) {
		return this._getObjectStorage(ownerId)(function (map) {
			return resolveObjectMap(ownerId, map, keyPaths);
		});
	}),
	__storeRaw: d(function (cat, ns, path, data) {
		if (cat === 'reduced') {
			return this._getReducedStorage(ns)(function (map) {
				map[path || '.'] = data;
				return this._writeStorage('reduced/' + ns, map);
			}.bind(this));
		}
		if (cat === 'computed') {
			return this._getComputedStorage(ns)(function (map) {
				map[path] = data;
				return this._writeStorage('computed/=' + (new Buffer(ns)).toString('base64'), map);
			}.bind(this));
		}
		return this._getObjectStorage(ns)(function (map) {
			map[path || '.'] = data;
			return this._writeStorage('direct/' + ns, map);
		}.bind(this));
	}),

	// Database data
	__getRawAllDirect: d(function () {
		return this._getAllObjectIds().map(function (ownerId) {
			return this._getObjectStorage(ownerId)(function (map) {
				return { ownerId: ownerId, map: map };
			});
		}, this)(function (maps) {
			var result = create(null);
			maps.forEach(function (data) { resolveObjectMap(data.ownerId, data.map, null, result); });
			return result;
		});
	}),

	// Size tracking
	__searchDirect: d(function (callback) {
		return this._getAllObjectIds().map(function (ownerId) {
			return this._getObjectStorage(ownerId)(function (map) {
				forEach(map, function (data, keyPath) {
					var postfix = keyPath === '.' ? '' : '/' + keyPath;
					callback(ownerId + postfix, data);
				});
			});
		}, this);
	}),
	__searchComputed: d(function (keyPath, callback) {
		return this._getComputedStorage(keyPath)(function (map) {
			forEach(map, function (data, ownerId) { callback(ownerId, data); });
		});
	}),

	// Reduced
	__getReducedNs: d(function (ns, keyPaths) {
		return this._getReducedStorage(ns)(function (map) {
			var result = create(null);
			forEach(map, function (data, path) {
				if (path === '.') path = null;
				if (path && keyPaths && !keyPaths.has(path)) return;
				result[ns + (path ? ('/' + path) : '')] = data;
			});
			return result;
		});
	}),

	// Storage import/export
	__exportAll: d(function (destDriver) {
		var count = 0;
		var promise = this.dbDir()(function () {
			return deferred(
				readdir(resolve(this.dirPath, 'direct'), { type: { file: true } }).catch(function (e) {
					if (e.code === 'ENOENT') return [];
					throw e;
				}).map(function (filename) {
					var ownerId = filename;
					return this._getObjectStorage(ownerId)(function (map) {
						return deferred.map(keys(map), function (path) {
							var data = this[path];
							if (path === '.') path = null;
							if (!(++count % 1000)) promise.emit('progress');
							return destDriver._storeRaw('direct', ownerId, path, data);
						}, map);
					});
				}.bind(this)),
				readdir(resolve(this.dirPath, 'computed'), { type: { file: true } }).catch(function (e) {
					if (e.code === 'ENOENT') return [];
					throw e;
				}).map(function (filename) {
					var keyPath = String(new Buffer(filename.slice(1), 'base64'));
					return this._getComputedStorage(keyPath)(function (map) {
						return deferred.map(keys(map), function (ownerId) {
							if (!(++count % 1000)) promise.emit('progress');
							return destDriver._storeRaw('computed', keyPath, ownerId, this[ownerId]);
						}, map);
					});
				}.bind(this)),
				readdir(resolve(this.dirPath, 'reduced'), { type: { file: true } }).catch(function (e) {
					if (e.code === 'ENOENT') return [];
					throw e;
				}).map(function (ns) {
					return this._getReducedStorage(ns)(function (map) {
						return deferred.map(keys(map), function (path) {
							if (!(++count % 1000)) promise.emit('progress');
							return destDriver._storeRaw('reduced', ns, (path === '.') ? null : path, this[path]);
						}, map);
					});
				}.bind(this))
			);
		}.bind(this));
		return promise;
	}),
	__clear: d(function () {
		return rmdir(this.dirPath, { recursive: true, force: true })(function () {
			this._getObjectStorage.clear();
			this._getComputedStorage.clear();
			this._getReducedStorage.clear();
			return (this.dbDir = mkdir(this.dirPath, { intermediate: true }));
		}.bind(this));
	}),

	// Connection related
	__close: d(function () { return deferred(undefined); }), // Nothing to close

	// Specific to driver
	_getAllObjectIds: d(function () {
		return this.dbDir()(function () {
			return readdir(resolve(this.dirPath, 'direct'), { type: { file: true } })(function (data) {
				return data.filter(isId).sort();
			}, function (e) {
				if (e.code === 'ENOENT') return [];
				throw e;
			});
		}.bind(this));
	}),
	_writeStorage: d(function (name, map) {
		return this.dbDir()(function () {
			return writeFile(resolve(this.dirPath, name), toArray(map, function (data, id) {
				var value = data.value;
				if (value === '') value = '-';
				else if (isArray(value)) value = stringify(value);
				return id + '\n' + data.stamp + '\n' + value;
			}, this, byStamp).join('\n\n'), { intermediate: true });
		}.bind(this));
	})
}, memoizeMethods({
	_getObjectStorage: d(function (ownerId) {
		return this.dbDir()(function () {
			var map = create(null);
			return readFile(resolve(this.dirPath, 'direct', ownerId))(function (data) {
				var value;
				try {
					String(data).split('\n\n').forEach(function (data) {
						data = data.split('\n');
						value = data[2];
						if (value === '-') value = '';
						map[data[0]] = {
							stamp: Number(data[1]),
							value: value
						};
					});
				} catch (ignore) {}
				return map;
			}, function (err) {
				if (err.code !== 'ENOENT') throw err;
				return map;
			});
		}.bind(this));
	}, { primitive: true }),
	_getComputedStorage: d(function (keyPath) {
		return this.dbDir()(function () {
			var map = create(null), filename = '=' + (new Buffer(keyPath)).toString('base64');
			return readFile(resolve(this.dirPath, 'computed', filename))(function (data) {
				var value;
				try {
					String(data).split('\n\n').forEach(function (data) {
						data = data.split('\n');
						value = data[2];
						if (value[0] === '[') value = parse(data[2]);
						else if (value === '-') value = '';
						map[data[0]] = {
							stamp: Number(data[1]),
							value: value
						};
					});
				} catch (ignore) {}
				return map;
			}, function (err) {
				if (err.code !== 'ENOENT') throw err;
				return map;
			});
		}.bind(this));
	}, { primitive: true }),
	_getReducedStorage: d(function (ns) {
		return this.dbDir()(function () {
			var map = create(null);
			return readFile(resolve(this.dirPath, 'reduced', ns))(function (data) {
				var value;
				try {
					String(data).split('\n\n').forEach(function (data) {
						data = data.split('\n');
						value = data[2];
						if (value[0] === '[') value = parse(data[2]);
						else if (value === '-') value = '';
						map[data[0]] = {
							stamp: Number(data[1]),
							value: value
						};
					});
				} catch (ignore) {}
				return map;
			}, function (err) {
				if (err.code !== 'ENOENT') throw err;
				return map;
			});
		}.bind(this));
	}, { primitive: true })
})));
<|endoftext|>


<commit_before>"use strict";

const _ = require("ramda")

require("./pcss/main.pcss")

const sound = require("./sound")
const $ = require("jquery")
window.jQuery = $
require("./jquery.trap")
require("jquery-ui/ui/position")
const Notifications = require("./notifications")
const DB = require("./db")

boot().catch(console.error)

async function boot() {
    $("#root").trap();

    let syncList = []
    const dbMap = {
        "todo-db": await DB("todo-db"),
        "project-db": await DB("project-db"),
        "context-db": await DB("context-db")
    }

    // _.mapObjIndexed(db=>db.startRemoteSync(), dbMap)

    const todos = await dbMap["todo-db"].find({selector: {"_id": {"$ne": null}}})
    const projects = await dbMap["project-db"].find({selector: {"_id": {"$ne": null}}})
    const contexts = await dbMap["context-db"].find({selector: {"_id": {"$ne": null}}})

    const flags = {
        now: Date.now(),
        encodedTodoList: todos,
        encodedProjectList: projects,
        encodedContextList: contexts,
        pouchDBRemoteSyncURI: localStorage.getItem("pouchdb.remote-sync-uri") || ""
    }

    const Elm = require("elm/Main.elm")
    const app = Elm["Main"]
        .embed(document.getElementById("elm-app-container"), flags)

    app.ports["syncWithRemotePouch"].subscribe(async (uri) => {
        localStorage.setItem("pouchdb.remote-sync-uri", uri)
        await Promise.all(_.map(sync => sync.cancel())(syncList))
        syncList = _.compose(_.map(db => db.startRemoteSync(uri, "sgtd2-")), _.values)(dbMap)
    })

    app.ports["pouchDBUpsert"].subscribe(async ([dbName, id, doc]) => {
        // console.log("upserting", dbName, doc, id)
        const upsertResult = await dbMap[dbName].upsert(id, doc)
        if (_.F()) {
            console.log("upsertResult", upsertResult)
        }
    });

    Notifications.setup(app).catch(console.error)

    app.ports["focusSelector"].subscribe((selector) => {
        setTimeout(() => {
            requestAnimationFrame(() => {
                $(selector).focus()
            })
        }, 0)
    })

    app.ports["signIn"].subscribe(() => {
        // const intervalId = setInterval(() => {
        let googleAuth = document.getElementById('google-auth');
        // if (!googleAuth) return
        googleAuth
            .signInWithRedirect()
            .then(console.info)
            .catch(console.error)
        // clearTimeout(intervalId);
        // }, 500)
    })

    app.ports["signOut"].subscribe(() => {
        let googleAuth = document.getElementById('google-auth');
        googleAuth
            .signOut()
            .then(console.info)
            .catch(console.error)
    })

    app.ports["focusPaperInput"].subscribe((selector) => {
        setTimeout(() => {
            requestAnimationFrame(() => {
                const toFocus = document.querySelector(selector)
                // console.log("toFocus", toFocus, document.activeElement)
                if (toFocus && document.activeElement !== toFocus) {
                    toFocus.focus()
                } else {
                    // console.log("not focusing")
                }
                if (toFocus) {
                    // console.log(toFocus.inputElement, toFocus.$.input)
                    toFocus.inputElement.focus()
                    // toFocus.$.input.focus()
                }
            })
        }, 0)
    })

    app.ports["startAlarm"].subscribe(() => {
        sound.start()
    })

    app.ports["stopAlarm"].subscribe(() => {
        sound.stop()
    })
}

/*
 //noinspection JSUnresolvedVariable
 if (!WEB_PACK_DEV_SERVER && 'serviceWorker' in navigator) {
 //noinspection JSUnresolvedVariable
 navigator.serviceWorker.register('/service-worker.js');
 }
 */
<commit_msg>adding logout<commit_after>"use strict";

const _ = require("ramda")

require("./pcss/main.pcss")

const sound = require("./sound")
const $ = require("jquery")
window.jQuery = $
require("./jquery.trap")
require("jquery-ui/ui/position")
const Notifications = require("./notifications")
const DB = require("./db")

boot().catch(console.error)

async function boot() {
    $("#root").trap();

    let syncList = []
    const dbMap = {
        "todo-db": await DB("todo-db"),
        "project-db": await DB("project-db"),
        "context-db": await DB("context-db")
    }

    // _.mapObjIndexed(db=>db.startRemoteSync(), dbMap)

    const todos = await dbMap["todo-db"].find({selector: {"_id": {"$ne": null}}})
    const projects = await dbMap["project-db"].find({selector: {"_id": {"$ne": null}}})
    const contexts = await dbMap["context-db"].find({selector: {"_id": {"$ne": null}}})

    const flags = {
        now: Date.now(),
        encodedTodoList: todos,
        encodedProjectList: projects,
        encodedContextList: contexts,
        pouchDBRemoteSyncURI: localStorage.getItem("pouchdb.remote-sync-uri") || ""
    }

    const Elm = require("elm/Main.elm")
    const app = Elm["Main"]
        .embed(document.getElementById("elm-app-container"), flags)

    app.ports["syncWithRemotePouch"].subscribe(async (uri) => {
        localStorage.setItem("pouchdb.remote-sync-uri", uri)
        await Promise.all(_.map(sync => sync.cancel())(syncList))
        syncList = _.compose(_.map(db => db.startRemoteSync(uri, "sgtd2-")), _.values)(dbMap)
    })

    app.ports["pouchDBUpsert"].subscribe(async ([dbName, id, doc]) => {
        // console.log("upserting", dbName, doc, id)
        const upsertResult = await dbMap[dbName].upsert(id, doc)
        if (_.F()) {
            console.log("upsertResult", upsertResult)
        }
    });

    Notifications.setup(app).catch(console.error)

    app.ports["focusSelector"].subscribe((selector) => {
        setTimeout(() => {
            requestAnimationFrame(() => {
                $(selector).focus()
            })
        }, 0)
    })

    app.ports["signIn"].subscribe(() => {
        let googleAuth = document.getElementById('google-auth');
        googleAuth
            .signInWithRedirect()
            .then(console.info)
            .catch(console.error)
    })

    app.ports["signOut"].subscribe(() => {
        let googleAuth = document.getElementById('google-auth');
        googleAuth
            .signOut()
            .then(console.info)
            .catch(console.error)
    })

    app.ports["focusPaperInput"].subscribe((selector) => {
        setTimeout(() => {
            requestAnimationFrame(() => {
                const toFocus = document.querySelector(selector)
                // console.log("toFocus", toFocus, document.activeElement)
                if (toFocus && document.activeElement !== toFocus) {
                    toFocus.focus()
                } else {
                    // console.log("not focusing")
                }
                if (toFocus) {
                    // console.log(toFocus.inputElement, toFocus.$.input)
                    toFocus.inputElement.focus()
                    // toFocus.$.input.focus()
                }
            })
        }, 0)
    })

    app.ports["startAlarm"].subscribe(() => {
        sound.start()
    })

    app.ports["stopAlarm"].subscribe(() => {
        sound.stop()
    })
}

/*
 //noinspection JSUnresolvedVariable
 if (!WEB_PACK_DEV_SERVER && 'serviceWorker' in navigator) {
 //noinspection JSUnresolvedVariable
 navigator.serviceWorker.register('/service-worker.js');
 }
 */
<|endoftext|>


<commit_before>var dev = true;

var s = document.createElement('script')
if (dev) {
  s.src = 'http://localhost:8000/node_modules/d3/d3.js'
} else {
  s.src = '//d3js.org/d3.v3.min.js'
}
document.body.appendChild(s);

(function(w) {
  var d3fxDuration = 1000
  var filterProperty = determineFilterPrefix()

  function determineFilterPrefix() {
    var options = ['filter', '-webkit-filter']
    var styles = w.getComputedStyle(document.body).cssText
    var propertyToUse = null;
    options.forEach(function(option) {
      if (styles.indexOf(option + ':') !== -1) {
        propertyToUse = option
      }
    })
    return propertyToUse
  }

  function randomInRange(range) {
    return Math.random() * (range[1] - range[0]) + range[0]
  }

  function transition(selector, duration) {
    if (arguments.length < 1) {
      selector = '*'
    }
    if (arguments.length < 2) {
      d3fxDuration = 1000
    }
    d3.selectAll(selector).style('transition', 'all ' + d3fxDuration + 'ms ease-in-out')
  }

  function transform(ranges) {
    var defaultRanges = {
      rotate: [0, 0],
      translate: [0, 0],
      skew: [0, 0],
      scale: [0, 0]
    }

    Object.keys(ranges).forEach(function(rangeKey) {
      defaultRanges[rangeKey] = ranges[rangeKey]
    })
    ranges = defaultRanges

    return function doIt() {
      var currentTransform = d3.transform(d3.select(this).style('transform'))
      var newTransform = {}
      Object.keys(ranges).forEach(function(rangeKey) {
        var range = ranges[rangeKey]
        if (rangeKey === 'rotate') {
          newTransform[rangeKey] =
            currentTransform[rangeKey] + randomInRange(range)
        } else if (rangeKey === 'skew') {
          newTransform[rangeKey] = randomInRange(range)
        } else {
          newTransform[rangeKey] = [randomInRange(range), randomInRange(range)]
        }
      })
      var transformString = 'translate(' + newTransform.translate[0] + 'px,' +
         newTransform.translate[1] + 'px)' +
        'rotate(' + newTransform.rotate + 'deg)' +
        'skewX(' + newTransform.skew + 'deg)' +
        'scale(' + newTransform.scale[0] + ',' + newTransform.scale[1] + ')'
      console.log(transformString)
      d3.select(this).style('transform', transformString)
      setTimeout(doIt.bind(this), d3fxDuration)
    }
  }

  function filter(ranges) {
    var defaultRanges = {
      blur: [0, 0],
      brightness: [1, 1],
      contrast: [1, 1],
      grayscale: [0, 0],
      invert: [0, 0],
      saturate: [1, 1],
      sepia: [0, 0]
    }
    Object.keys(ranges).forEach(function(rangeKey) {
      defaultRanges[rangeKey] = ranges[rangeKey]
    })
    ranges = defaultRanges

    var units = {
      blur: 'px',
      hueRotate: 'deg',
    }

    return function doIt() {
      var newFilters = {}
      var f = Object.keys(ranges).reduce(function(filterString, rangeKey) {
        var range = ranges[rangeKey]
        var newValue = randomInRange(range)
        newFilters[rangeKey] = newValue
        var unit = units[rangeKey] || ''
        var propertyName = rangeKey
        if (propertyName === 'hueRotate') {
          propertyName = 'hue-rotate'
        }
        filterString += propertyName + '(' + newValue + unit + ')'
        return filterString
      },'')
      console.log(f)
      d3.select(this).style(filterProperty, f)
      setTimeout(doIt.bind(this), d3fxDuration)
    }

  }

  var transformBehaviors = {
    default: {
      rotate: [-Math.PI, Math.PI],
      translate: [-100, 100],
      skew: [-10, 10],
      scale: [0.5, 2]
    },
    grow: {
      scale: [0.5, 2]
    },
    tilt: {
      skew: [-10, 10],
    },
    jitter: {
      translate: [-100, 100],
    },
    rotate: {
      rotate: [-Math.PI, Math.PI]
    }
  }

  var filterBehaviors = {
    default: {
      blur: [0, 5],
      brightness: [0, 1],
      contrast: [0, 2],
      grayscale: [0, 1],
      invert: [0, 1],
      saturate: [0, 2],
      sepia: [0, 1],
      hueRotate: [0, 360]
    },
    blur: {
      blur: [0, 5]
    },
    brightness: {
      brightness: [0, 2]
    },
    contrast: {
      contrast: [0, 2]
    },
    grayscale: {
      grayscale: [0, 1]
    },
    invert: {
      invert: [0, 1]
    },
    saturate: {
      saturate: [0, 2],
    },
    sepia: {
      sepia: [0, 1]
    },
    hueRotate: {
      hueRotate: [0, 360]
    }
  }

  function mergeBehaviors() {
    var filters = []
    filters.push.apply(filters, arguments)

    var finalFilter = {}
    filters.forEach(function(filter) {
      Object.keys(filter).forEach(function(filterKey) {
        finalFilter[filterKey] = filter[filterKey]
      })
    })
    return finalFilter
  }

  w.d3fx = {
    transition: transition,
    transform: transform,
    TRANSFORMS: transformBehaviors,
    filter: filter,
    FILTERS: filterBehaviors,
    merge: mergeBehaviors,
  }
})(window)<commit_msg>unpluralized enums<commit_after>var dev = true;

var s = document.createElement('script')
if (dev) {
  s.src = 'http://localhost:8000/node_modules/d3/d3.js'
} else {
  s.src = '//d3js.org/d3.v3.min.js'
}
document.body.appendChild(s);

(function(w) {
  var d3fxDuration = 1000
  var filterProperty = determineFilterPrefix()

  function determineFilterPrefix() {
    var options = ['filter', '-webkit-filter']
    var styles = w.getComputedStyle(document.body).cssText
    var propertyToUse = null;
    options.forEach(function(option) {
      if (styles.indexOf(option + ':') !== -1) {
        propertyToUse = option
      }
    })
    return propertyToUse
  }

  function randomInRange(range) {
    return Math.random() * (range[1] - range[0]) + range[0]
  }

  function transition(selector, duration) {
    if (arguments.length < 1) {
      selector = '*'
    }
    if (arguments.length < 2) {
      d3fxDuration = 1000
    }
    d3.selectAll(selector).style('transition', 'all ' + d3fxDuration + 'ms ease-in-out')
  }

  function transform(ranges) {
    var defaultRanges = {
      rotate: [0, 0],
      translate: [0, 0],
      skew: [0, 0],
      scale: [0, 0]
    }

    Object.keys(ranges).forEach(function(rangeKey) {
      defaultRanges[rangeKey] = ranges[rangeKey]
    })
    ranges = defaultRanges

    return function doIt() {
      var currentTransform = d3.transform(d3.select(this).style('transform'))
      var newTransform = {}
      Object.keys(ranges).forEach(function(rangeKey) {
        var range = ranges[rangeKey]
        if (rangeKey === 'rotate') {
          newTransform[rangeKey] =
            currentTransform[rangeKey] + randomInRange(range)
        } else if (rangeKey === 'skew') {
          newTransform[rangeKey] = randomInRange(range)
        } else {
          newTransform[rangeKey] = [randomInRange(range), randomInRange(range)]
        }
      })
      var transformString = 'translate(' + newTransform.translate[0] + 'px,' +
         newTransform.translate[1] + 'px)' +
        'rotate(' + newTransform.rotate + 'deg)' +
        'skewX(' + newTransform.skew + 'deg)' +
        'scale(' + newTransform.scale[0] + ',' + newTransform.scale[1] + ')'
      console.log(transformString)
      d3.select(this).style('transform', transformString)
      setTimeout(doIt.bind(this), d3fxDuration)
    }
  }

  function filter(ranges) {
    var defaultRanges = {
      blur: [0, 0],
      brightness: [1, 1],
      contrast: [1, 1],
      grayscale: [0, 0],
      invert: [0, 0],
      saturate: [1, 1],
      sepia: [0, 0]
    }
    Object.keys(ranges).forEach(function(rangeKey) {
      defaultRanges[rangeKey] = ranges[rangeKey]
    })
    ranges = defaultRanges

    var units = {
      blur: 'px',
      hueRotate: 'deg',
    }

    return function doIt() {
      var newFilters = {}
      var f = Object.keys(ranges).reduce(function(filterString, rangeKey) {
        var range = ranges[rangeKey]
        var newValue = randomInRange(range)
        newFilters[rangeKey] = newValue
        var unit = units[rangeKey] || ''
        var propertyName = rangeKey
        if (propertyName === 'hueRotate') {
          propertyName = 'hue-rotate'
        }
        filterString += propertyName + '(' + newValue + unit + ')'
        return filterString
      },'')
      console.log(f)
      d3.select(this).style(filterProperty, f)
      setTimeout(doIt.bind(this), d3fxDuration)
    }

  }

  var transformBehaviors = {
    default: {
      rotate: [-Math.PI, Math.PI],
      translate: [-100, 100],
      skew: [-10, 10],
      scale: [0.5, 2]
    },
    grow: {
      scale: [0.5, 2]
    },
    tilt: {
      skew: [-10, 10],
    },
    jitter: {
      translate: [-100, 100],
    },
    rotate: {
      rotate: [-Math.PI, Math.PI]
    }
  }

  var filterBehaviors = {
    default: {
      blur: [0, 5],
      brightness: [0, 1],
      contrast: [0, 2],
      grayscale: [0, 1],
      invert: [0, 1],
      saturate: [0, 2],
      sepia: [0, 1],
      hueRotate: [0, 360]
    },
    blur: {
      blur: [0, 5]
    },
    brightness: {
      brightness: [0, 2]
    },
    contrast: {
      contrast: [0, 2]
    },
    grayscale: {
      grayscale: [0, 1]
    },
    invert: {
      invert: [0, 1]
    },
    saturate: {
      saturate: [0, 2],
    },
    sepia: {
      sepia: [0, 1]
    },
    hueRotate: {
      hueRotate: [0, 360]
    }
  }

  function mergeBehaviors() {
    var filters = []
    filters.push.apply(filters, arguments)

    var finalFilter = {}
    filters.forEach(function(filter) {
      Object.keys(filter).forEach(function(filterKey) {
        finalFilter[filterKey] = filter[filterKey]
      })
    })
    return finalFilter
  }

  w.d3fx = {
    transition: transition,
    transform: transform,
    TRANSFORM: transformBehaviors,
    filter: filter,
    FILTER: filterBehaviors,
    merge: mergeBehaviors,
  }
})(window)<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import { ApolloProvider, getDataFromTree } from 'react-apollo'
import Head from 'next/head'
import initApollo from './initApollo'

// Gets the display name of a JSX component for dev tools
function getComponentDisplayName(Component) {
  return Component.displayName || Component.name || 'Unknown'
}

export default (apollogConfig, ComposedComponent) => {
  return class WithData extends React.Component {
    static displayName = `WithData(${getComponentDisplayName(
      ComposedComponent
    )})`
    static propTypes = {
      serverState: PropTypes.object.isRequired
    }

    static async getInitialProps(ctx) {
      let serverState = { apollo: {} }

      // Evaluate the composed component's getInitialProps()
      let composedInitialProps = {}
      if (ComposedComponent.getInitialProps) {
        composedInitialProps = await ComposedComponent.getInitialProps(ctx)
      }

      // Run all GraphQL queries in the component tree
      // and extract the resulting data
      if (!process.browser) {
        const apollo = initApollo(apollogConfig)
        // Provide the `url` prop data in case a GraphQL query uses it
        const url = { query: ctx.query, pathname: ctx.pathname }
        try {
          // Run all GraphQL queries
          await getDataFromTree(
            <ApolloProvider client={apollo}>
              <ComposedComponent url={url} {...composedInitialProps} />
            </ApolloProvider>
          )
        } catch (error) {
          // Prevent Apollo Client GraphQL errors from crashing SSR.
          // Handle them in components via the data.error prop:
          // http://dev.apollodata.com/react/api-queries.html#graphql-query-data-error
        }
        // getDataFromTree does not call componentWillUnmount
        // head side effect therefore need to be cleared manually
        Head.rewind()

        // Extract query data from the Apollo store
        serverState = {
          apollo: {
            data: apollo.cache.extract()
          }
        }
      }

      return {
        serverState,
        ...composedInitialProps
      }
    }

    constructor(props) {
      super(props)
      this.apollo = initApollo(
        apollogConfig,
        this.props.serverState.apollo.data
      )
    }

    render() {
      return (
        <ApolloProvider client={this.apollo}>
          <ComposedComponent {...this.props} />
        </ApolloProvider>
      )
    }
  }
}
<commit_msg>Make next-apollo a HOC<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import { ApolloProvider, getDataFromTree } from 'react-apollo'
import Head from 'next/head'
import initApollo from './initApollo'

// Gets the display name of a JSX component for dev tools
function getComponentDisplayName(Component) {
  return Component.displayName || Component.name || 'Unknown'
}

export default apollogConfig => {
  return ComposedComponent => {
    return class WithData extends React.Component {
      static displayName = `WithData(${getComponentDisplayName(
        ComposedComponent
      )})`
      static propTypes = {
        serverState: PropTypes.object.isRequired
      }

      static async getInitialProps(ctx) {
        let serverState = { apollo: {} }

        // Evaluate the composed component's getInitialProps()
        let composedInitialProps = {}
        if (ComposedComponent.getInitialProps) {
          composedInitialProps = await ComposedComponent.getInitialProps(ctx)
        }

        // Run all GraphQL queries in the component tree
        // and extract the resulting data
        if (!process.browser) {
          const apollo = initApollo(apollogConfig)
          // Provide the `url` prop data in case a GraphQL query uses it
          const url = { query: ctx.query, pathname: ctx.pathname }
          try {
            // Run all GraphQL queries
            await getDataFromTree(
              <ApolloProvider client={apollo}>
                <ComposedComponent url={url} {...composedInitialProps} />
              </ApolloProvider>
            )
          } catch (error) {
            // Prevent Apollo Client GraphQL errors from crashing SSR.
            // Handle them in components via the data.error prop:
            // http://dev.apollodata.com/react/api-queries.html#graphql-query-data-error
          }
          // getDataFromTree does not call componentWillUnmount
          // head side effect therefore need to be cleared manually
          Head.rewind()

          // Extract query data from the Apollo store
          serverState = {
            apollo: {
              data: apollo.cache.extract()
            }
          }
        }

        return {
          serverState,
          ...composedInitialProps
        }
      }

      constructor(props) {
        super(props)
        this.apollo = initApollo(
          apollogConfig,
          this.props.serverState.apollo.data
        )
      }

      render() {
        return (
          <ApolloProvider client={this.apollo}>
            <ComposedComponent {...this.props} />
          </ApolloProvider>
        )
      }
    }
  }
}
<|endoftext|>


<commit_before>(function() {
  var module = angular.module('loom_story_service', ['ngCookies']);
  var service_ = null;
  var mapService_ = null;
  var configService_ = null;
  var httpService_ = null;
  var dialogService_ = null;
  var translate_ = null;
  var tableViewService_ = null;
  var featureManagerService_ = null;
  var rootScope_ = null;
  var q_ = null;

  module.provider('storyService', function() {

    this.$get = function($window, $http, $q, $cookies, $location, $translate, $rootScope, mapService, featureManagerService,
                         configService, dialogService, tableViewService) {
      service_ = this;
      q_ = $q;
      mapService_ = mapService;
      configService_ = configService;
      httpService_ = $http;
      dialogService_ = dialogService;
      translate_ = $translate;
      rootScope_ = $rootScope;
      tableViewService_ = tableViewService;
      featureManagerService_ = featureManagerService;

      //When initializing the story service the mapService should already be initialized
      this.title = 'New Mapstory';
      this.abstract = 'This is the default summary';
      this.category = null;
      this.is_published = false;
      //Stores the list of chapter (map) configuration objects and uses mapService to save map based on config
      this.configurations = [];
      this.removedChapterIDs = [];
      this.configurations.push(angular.copy(mapService_.configuration));
      this.active_index = 0;
      //All mapstories have one default chapter added
      this.active_layer = null;
      this.active_box = null;
      this.active_pin = null;
      this.active_chapter = this.configurations[this.active_index];
      this.active_chapter.map['id'] = 0;
      this.active_chapter.about.title = 'Untitled Chapter';
      this.active_chapter.about.abstract = 'This is the default summary';
      console.log('-----story_config:', this.active_chapter);
      this.id = this.active_chapter.id;
      this.category = null;
      this.is_published = false;
      this.keywords = [];

      return this;
    };

    this.clearSelectedItems = function() {
      this.active_layer = null;
      this.active_box = null;
      this.active_pin = null;
      this.active_chapter = null;
    };

    this.selectLayer = function(layer_config) {
      this.active_layer = layer_config;
    };

    this.selectBox = function(box) {
      this.active_box = box;
    };

    this.selectPin = function(pin) {
      this.active_pin = pin;
    };

    this.toggleVisiblity = function(layer) {
      layer.set('visible', !layer.get('visible'));
    };

    this.showTable = function() {
      service_.active_layer.get('metadata').loadingTable = true;
      tableViewService_.showTable(service_.active_layer).then(function() {
        service_.active_layer.get('metadata').loadingTable = false;
        $('#table-view-window').modal('show');
      }, function() {
        service_.active_layer.get('metadata').loadingTable = false;
        dialogService_.error(translate_.instant('show_table'), translate_.instant('show_table_failed'));
      });
    };

    this.removeLayer = function() {
      dialogResult = dialogService_.warn(translate_.instant('remove_layer'), translate_.instant('sure_remove_layer'),
          [translate_.instant('yes_btn'), translate_.instant('no_btn')], false).then(function(button) {
        switch (button) {
          case 0:
            mapService_.map.removeLayer(service_.active_layer);
            rootScope_.$broadcast('layerRemoved', service_.active_layer);
            return true;
          case 1:
            return false;
        }
      });
      return dialogResult;
    };

    this.canEditStyle = function() {
      if (this.active_layer == null) {
        return false;
      } else {
        return (this.active_layer.get('metadata').editable && this.active_layer.get('metadata').active_btn == 'style_layer');
      }

    };

    this.isEditable = function() {
      if (this.active_layer == null) {
        return true;
      }
      return (!this.active_layer.get('visible') || !this.active_layer.get('metadata').schema ||
              this.active_layer.get('metadata').readOnly);
    };

    this.startFeatureAdd = function() {
      if (this.active_layer == null) {
        return;
      }
      featureManagerService_.startFeatureInsert(this.active_layer);
    };

    //Save all chapter configuration objects
    this.saveMaps = function() {
      //Go through each chapter configuration and save accordingly through mapService
      for (var iConfig = 0; iConfig < this.configurations.length; iConfig += 1) {
        //Chapter index is determined by order in configuration
        service_.configurations[iConfig]['chapter_index'] = iConfig;
        mapService_.save(this.configurations[iConfig]);
      }
      this.print_configurations();
    };

    //Method saves mapstory and underlying chapters
    this.save = function() {
      var cfg = {
        id: this.id || 0,
        title: this.title,
        abstract: this.abstract,
        is_published: this.is_published,
        category: this.category,
        removed_chapters: this.removedChapterIDs
      };

      console.log('saving Mapstory: ', this.title);
      httpService_({
        url: service_.getSaveURL(),
        method: service_.getSaveHTTPMethod(),
        data: JSON.stringify(cfg),
        headers: {
          'X-CSRFToken': configService_.csrfToken
        }
      }).success(function(data, status, headers, config) {
        //After we successfully save a mapstory update the composer to reference the backend object
        //and save chapters
        service_.updateStoryID(data.id);
        service_.saveMaps();
        console.log('----[ mapstory.save success. ', data, status, headers, config);
      }).error(function(data, status, headers, config) {
        if (status == 403 || status == 401) {
          dialogService_.error(translate_.instant('save_failed'), translate_.instant('mapstory_save_permission'));
        } else {
          dialogService_.error(translate_.instant('save_failed'), translate_.instant('mapstory_save_failed',
              {value: status}));
        }
      });

    };

    this.updateStoryID = function(id) {
      this.id = id;
      for (var iConfig = 0; iConfig < this.configurations.length; iConfig += 1) {
        this.configurations[iConfig].id = id;
      }
    };

    this.getSaveURL = function() {
      if (goog.isDefAndNotNull(this.id) && this.id) {
        return '/maps/' + this.id + '/save';
      } else {
        return '/maps/new/story';
      }
    };

    this.getSaveHTTPMethod = function() {
      if (goog.isDefAndNotNull(this.id) && this.id) {
        return 'PUT';
      } else {
        return 'POST';
      }
    };

    this.print_configurations = function() {
      console.log('=====configurations======');
      for (var iConfig = 0; iConfig < this.configurations.length; iConfig += 1) {
        console.log('configuration ', iConfig, this.configurations[iConfig]);
      }
    };

    this.get_chapter_config = function(index) {
      return this.configurations[index];
    };

    //Composer only allows you to edit one chapter at a time
    //This function should be called whenever we select a different chapter from the list.
    this.update_active_config = function(index, deleteOverride) {
      //This function updates the active_chapter and propagates the new
      //active configuration to the other services.
      if (this.active_index === index && !goog.isDefAndNotNull(deleteOverride)) {
        return;
      }
      this.active_chapter = this.configurations[index];
      this.active_index = index;

      mapService_.updateActiveMap(this.active_index, this.active_chapter);
      rootScope_.$broadcast('chapter-switch', this.active_index);
    };

    //Updates the stored chapter_info information for the current chapter
    //Does not invoke a map save
    this.update_chapter_info = function(chapter_info) {

      this.active_chapter.about.title = chapter_info.chapter_title;
      this.active_chapter.about.abstract = chapter_info.abstract;
    };

    this.change_chapter = function(chapter_index) {
      service_.update_active_config(chapter_index);
    };

    this.next_chapter = function() {
      var nextChapter = this.active_index + 1;
      if (nextChapter > this.configurations.length - 1) {
        nextChapter = 0;
      }
      service_.update_active_config(nextChapter);
    };

    this.prev_chapter = function() {
      var prevChapter = this.active_index - 1;
      if (prevChapter < 0) {
        prevChapter = 0;
      }
      service_.update_active_config(prevChapter);
    };

    this.getLayers = function() {
      var layers = mapService_.map.getLayers().getArray();

      for (var iLayer = 0; iLayer < layers.length; iLayer += 1) {
        var layer = layers[iLayer];
        if (!goog.isDefAndNotNull(layer.get('metadata')) ||
            (goog.isDefAndNotNull(layer.get('metadata').vectorEditLayer) &&
            layer.get('metadata').vectorEditLayer)) {
          layers.splice(iLayer, 1);
          console.log(layer);

        }
      }

      return layers;
    };

    this.add_chapter = function() {
      //The config service is the entrypoint and contains the initial configuration for a chapter
      var new_chapter = angular.copy(configService_.initial_config);
      new_chapter['id'] = this.id;
      new_chapter.map['id'] = 0;
      new_chapter.about.title = 'Untitled Chapter';
      new_chapter.about.summary = '';
      this.configurations.push(new_chapter);
      //This creates the new layergroup on the open layers map that is being displayed.
      //Parameter is currently unused, but may be changed if we decide map load should occur here.
      mapService_.create_chapter(new_chapter);
      var new_index = (this.configurations.length - 1);
      //Immediately set focus to new chapter after creation. This causes the new chapter map to load
      rootScope_.$broadcast('chapter-added', new_index);
      service_.update_active_config(new_index);
      mapService_.loadMap(new_chapter);
      this.print_configurations();

      return new_index;
    };

    this.reorder_chapter = function(from_index, to_index) {
      this.configurations.splice(to_index, 0, this.configurations.splice(from_index, 1)[0]);
    };

    this.canRemoveChapter = function() {
      return (service_.configurations.length > 1);
    };

    this.remove_chapter = function() {
      var removed_index = q_.defer();
      if (this.canRemoveChapter() === false) {
        dialogService_.error(translate_.instant('remove_chapter'), translate_.instant('cannot_remove_chapter'));
        removed_index.resolve(null);
        return removed_index.promise;
      }
      removed_index = dialogService_.warn(translate_.instant('remove_chapter'), translate_.instant('sure_remove_chapter'),
          [translate_.instant('yes_btn'), translate_.instant('no_btn')], false).then(function(button) {
        switch (button) {
          case 0:
            //If the chapter map has been saved beforehand we need to remove that chapter link
            map_id = service_.configurations[service_.active_index].map.id;
            if (map_id !== 0) {
              service_.removedChapterIDs.push(map_id);
            }

            //Remove the active chapter from the list of configurations
            removed_index = service_.active_index;
            mapService_.remove_chapter(removed_index);
            service_.configurations.splice(removed_index, 1);
            rootScope_.$broadcast('chapter-removed', removed_index);
            return removed_index;
          case 1:
            return null;
        }
      });
      return removed_index;
    };

  });

}());
<commit_msg>Ensure that after deleting chapters and removing them from backend we clear the removed ids.<commit_after>(function() {
  var module = angular.module('loom_story_service', ['ngCookies']);
  var service_ = null;
  var mapService_ = null;
  var configService_ = null;
  var httpService_ = null;
  var dialogService_ = null;
  var translate_ = null;
  var tableViewService_ = null;
  var featureManagerService_ = null;
  var rootScope_ = null;
  var q_ = null;

  module.provider('storyService', function() {

    this.$get = function($window, $http, $q, $cookies, $location, $translate, $rootScope, mapService, featureManagerService,
                         configService, dialogService, tableViewService) {
      service_ = this;
      q_ = $q;
      mapService_ = mapService;
      configService_ = configService;
      httpService_ = $http;
      dialogService_ = dialogService;
      translate_ = $translate;
      rootScope_ = $rootScope;
      tableViewService_ = tableViewService;
      featureManagerService_ = featureManagerService;

      //When initializing the story service the mapService should already be initialized
      this.title = 'New Mapstory';
      this.abstract = 'This is the default summary';
      this.category = null;
      this.is_published = false;
      //Stores the list of chapter (map) configuration objects and uses mapService to save map based on config
      this.configurations = [];
      this.removedChapterIDs = [];
      this.configurations.push(angular.copy(mapService_.configuration));
      this.active_index = 0;
      //All mapstories have one default chapter added
      this.active_layer = null;
      this.active_box = null;
      this.active_pin = null;
      this.active_chapter = this.configurations[this.active_index];
      this.active_chapter.map['id'] = 0;
      this.active_chapter.about.title = 'Untitled Chapter';
      this.active_chapter.about.abstract = 'This is the default summary';
      console.log('-----story_config:', this.active_chapter);
      this.id = this.active_chapter.id;
      this.category = null;
      this.is_published = false;
      this.keywords = [];

      return this;
    };

    this.clearSelectedItems = function() {
      this.active_layer = null;
      this.active_box = null;
      this.active_pin = null;
      this.active_chapter = null;
    };

    this.selectLayer = function(layer_config) {
      this.active_layer = layer_config;
    };

    this.selectBox = function(box) {
      this.active_box = box;
    };

    this.selectPin = function(pin) {
      this.active_pin = pin;
    };

    this.toggleVisiblity = function(layer) {
      layer.set('visible', !layer.get('visible'));
    };

    this.showTable = function() {
      service_.active_layer.get('metadata').loadingTable = true;
      tableViewService_.showTable(service_.active_layer).then(function() {
        service_.active_layer.get('metadata').loadingTable = false;
        $('#table-view-window').modal('show');
      }, function() {
        service_.active_layer.get('metadata').loadingTable = false;
        dialogService_.error(translate_.instant('show_table'), translate_.instant('show_table_failed'));
      });
    };

    this.removeLayer = function() {
      dialogResult = dialogService_.warn(translate_.instant('remove_layer'), translate_.instant('sure_remove_layer'),
          [translate_.instant('yes_btn'), translate_.instant('no_btn')], false).then(function(button) {
        switch (button) {
          case 0:
            mapService_.map.removeLayer(service_.active_layer);
            rootScope_.$broadcast('layerRemoved', service_.active_layer);
            return true;
          case 1:
            return false;
        }
      });
      return dialogResult;
    };

    this.canEditStyle = function() {
      if (this.active_layer == null) {
        return false;
      } else {
        return (this.active_layer.get('metadata').editable && this.active_layer.get('metadata').active_btn == 'style_layer');
      }

    };

    this.isEditable = function() {
      if (this.active_layer == null) {
        return true;
      }
      return (!this.active_layer.get('visible') || !this.active_layer.get('metadata').schema ||
              this.active_layer.get('metadata').readOnly);
    };

    this.startFeatureAdd = function() {
      if (this.active_layer == null) {
        return;
      }
      featureManagerService_.startFeatureInsert(this.active_layer);
    };

    //Save all chapter configuration objects
    this.saveMaps = function() {
      //Go through each chapter configuration and save accordingly through mapService
      for (var iConfig = 0; iConfig < this.configurations.length; iConfig += 1) {
        //Chapter index is determined by order in configuration
        service_.configurations[iConfig]['chapter_index'] = iConfig;
        mapService_.save(this.configurations[iConfig]);
      }
      this.print_configurations();
    };

    //Method saves mapstory and underlying chapters
    this.save = function() {
      var cfg = {
        id: this.id || 0,
        title: this.title,
        abstract: this.abstract,
        is_published: this.is_published,
        category: this.category,
        removed_chapters: this.removedChapterIDs
      };

      console.log('saving Mapstory: ', this.title);
      httpService_({
        url: service_.getSaveURL(),
        method: service_.getSaveHTTPMethod(),
        data: JSON.stringify(cfg),
        headers: {
          'X-CSRFToken': configService_.csrfToken
        }
      }).success(function(data, status, headers, config) {
        //After we successfully save a mapstory update the composer to reference the backend object
        //and save chapters
        service_.updateStoryID(data.id);
        service_.removedChapterIDs = [];
        service_.saveMaps();
        console.log('----[ mapstory.save success. ', data, status, headers, config);
      }).error(function(data, status, headers, config) {
        if (status == 403 || status == 401) {
          dialogService_.error(translate_.instant('save_failed'), translate_.instant('mapstory_save_permission'));
        } else {
          dialogService_.error(translate_.instant('save_failed'), translate_.instant('mapstory_save_failed',
              {value: status}));
        }
      });

    };

    this.updateStoryID = function(id) {
      this.id = id;
      for (var iConfig = 0; iConfig < this.configurations.length; iConfig += 1) {
        this.configurations[iConfig].id = id;
      }
    };

    this.getSaveURL = function() {
      if (goog.isDefAndNotNull(this.id) && this.id) {
        return '/maps/' + this.id + '/save';
      } else {
        return '/maps/new/story';
      }
    };

    this.getSaveHTTPMethod = function() {
      if (goog.isDefAndNotNull(this.id) && this.id) {
        return 'PUT';
      } else {
        return 'POST';
      }
    };

    this.print_configurations = function() {
      console.log('=====configurations======');
      for (var iConfig = 0; iConfig < this.configurations.length; iConfig += 1) {
        console.log('configuration ', iConfig, this.configurations[iConfig]);
      }
    };

    this.get_chapter_config = function(index) {
      return this.configurations[index];
    };

    //Composer only allows you to edit one chapter at a time
    //This function should be called whenever we select a different chapter from the list.
    this.update_active_config = function(index, deleteOverride) {
      //This function updates the active_chapter and propagates the new
      //active configuration to the other services.
      if (this.active_index === index && !goog.isDefAndNotNull(deleteOverride)) {
        return;
      }
      this.active_chapter = this.configurations[index];
      this.active_index = index;

      mapService_.updateActiveMap(this.active_index, this.active_chapter);
      rootScope_.$broadcast('chapter-switch', this.active_index);
    };

    //Updates the stored chapter_info information for the current chapter
    //Does not invoke a map save
    this.update_chapter_info = function(chapter_info) {

      this.active_chapter.about.title = chapter_info.chapter_title;
      this.active_chapter.about.abstract = chapter_info.abstract;
    };

    this.change_chapter = function(chapter_index) {
      service_.update_active_config(chapter_index);
    };

    this.next_chapter = function() {
      var nextChapter = this.active_index + 1;
      if (nextChapter > this.configurations.length - 1) {
        nextChapter = 0;
      }
      service_.update_active_config(nextChapter);
    };

    this.prev_chapter = function() {
      var prevChapter = this.active_index - 1;
      if (prevChapter < 0) {
        prevChapter = 0;
      }
      service_.update_active_config(prevChapter);
    };

    this.getLayers = function() {
      var layers = mapService_.map.getLayers().getArray();

      for (var iLayer = 0; iLayer < layers.length; iLayer += 1) {
        var layer = layers[iLayer];
        if (!goog.isDefAndNotNull(layer.get('metadata')) ||
            (goog.isDefAndNotNull(layer.get('metadata').vectorEditLayer) &&
            layer.get('metadata').vectorEditLayer)) {
          layers.splice(iLayer, 1);
          console.log(layer);

        }
      }

      return layers;
    };

    this.add_chapter = function() {
      //The config service is the entrypoint and contains the initial configuration for a chapter
      var new_chapter = angular.copy(configService_.initial_config);
      new_chapter['id'] = this.id;
      new_chapter.map['id'] = 0;
      new_chapter.about.title = 'Untitled Chapter';
      new_chapter.about.summary = '';
      this.configurations.push(new_chapter);
      //This creates the new layergroup on the open layers map that is being displayed.
      //Parameter is currently unused, but may be changed if we decide map load should occur here.
      mapService_.create_chapter(new_chapter);
      var new_index = (this.configurations.length - 1);
      //Immediately set focus to new chapter after creation. This causes the new chapter map to load
      rootScope_.$broadcast('chapter-added', new_index);
      service_.update_active_config(new_index);
      mapService_.loadMap(new_chapter);
      this.print_configurations();

      return new_index;
    };

    this.reorder_chapter = function(from_index, to_index) {
      this.configurations.splice(to_index, 0, this.configurations.splice(from_index, 1)[0]);
    };

    this.canRemoveChapter = function() {
      return (service_.configurations.length > 1);
    };

    this.remove_chapter = function() {
      var removed_index = q_.defer();
      if (this.canRemoveChapter() === false) {
        dialogService_.error(translate_.instant('remove_chapter'), translate_.instant('cannot_remove_chapter'));
        removed_index.resolve(null);
        return removed_index.promise;
      }
      removed_index = dialogService_.warn(translate_.instant('remove_chapter'), translate_.instant('sure_remove_chapter'),
          [translate_.instant('yes_btn'), translate_.instant('no_btn')], false).then(function(button) {
        switch (button) {
          case 0:
            //If the chapter map has been saved beforehand we need to remove that chapter link
            map_id = service_.configurations[service_.active_index].map.id;
            if (map_id !== 0) {
              service_.removedChapterIDs.push(map_id);
            }

            //Remove the active chapter from the list of configurations
            removed_index = service_.active_index;
            mapService_.remove_chapter(removed_index);
            service_.configurations.splice(removed_index, 1);
            rootScope_.$broadcast('chapter-removed', removed_index);
            return removed_index;
          case 1:
            return null;
        }
      });
      return removed_index;
    };

  });

}());
<|endoftext|>


<commit_before>/* eslint-disable no-underscore-dangle */
/* eslint-disable no-console */

class TelegrafLogger {
  constructor(options) {
    this.options = Object.assign({
      log: console.log,
      format: '%updateType => @%username %firstName %lastName (%fromId): <%updateSubType> %content',
      contentLength: 100,
    }, options);
  }

  middleware() {
    return (ctx, next) => {
      let content;
      let updateTypeId;

      switch (ctx.updateType) {
        case 'message':
          updateTypeId = ctx.message.message_id;

          if (ctx.message.text) {
            content = ctx.message.text;
          } else if (ctx.message.sticker) {
            content = ctx.message.sticker.emoji;
          } else {
            content = '';
          }

          break;

        case 'edited_message':
          updateTypeId = ctx.editedMessage.message_id;
          content = ctx.editedMessage.text;
          break;

        case 'callback_query':
          updateTypeId = ctx.callbackQuery.id;
          content = ctx.callbackQuery.data;
          break;

        case 'inline_query':
          updateTypeId = ctx.inlineQuery.id;
          content = ctx.inlineQuery.query;
          break;

        case 'chosen_inline_result':
          updateTypeId = ctx.chosenInlineResult.result_id;
          content = ctx.chosenInlineResult.query;
          break;

        case 'shipping_query':
          updateTypeId = ctx.shippingQuery.id;
          content = ctx.shippingQuery.invoice_payload;
          break;

        case 'pre_checkout_query':
          updateTypeId = ctx.preCheckoutQuery.id;
          content = ctx.preCheckoutQuery.invoice_payload;
          break;

        default:
          content = '';
          updateTypeId = 0;
      }

      const text = this.options.format
        .replace(/%botUsername\b/igm, ctx.me || null)
        .replace(/%username\b/igm, ctx.from.username || null)
        .replace(/%firstName\b/igm, ctx.from.first_name)
        .replace(/%lastName\b/igm, ctx.from.last_name || '')
        .replace(/%fromId\b/igm, ctx.from.id)
        .replace(/%chatId\b/igm, ctx.chat && ctx.chat.id)
        .replace(/%chatType\b/igm, ctx.chat && ctx.chat.type)
        .replace(/%chatTitle\b/igm, (ctx.chat && ctx.chat.title) || null)
        .replace(/%chatUsername\b/igm, (ctx.chat && ctx.chat.username) || null)
        .replace(/%updateId\b/igm, ctx.update.update_id)
        .replace(/%updateType\b/igm, ctx.updateType)
        .replace(/%updateTypeId\b/igm, updateTypeId)
        .replace(/%updateSubType\b/igm, ctx.updateSubType || ctx.updateSubTypes[0] || ctx.updateType)
        .replace(/%sceneId\b/igm, (ctx.session && ctx.session._flow && ctx.session._flow.id) || null)
        .replace(/ +|\n/g, ' ');

      if (content.length > this.options.contentLength) {
        content = `${content.slice(0, this.options.contentLength)}...`;
      }

      this.options.log(text.replace(/%content\b/igm, content).replace(/\n/g, ' '));
      return next();
    };
  }
}

module.exports = TelegrafLogger;
<commit_msg>Add channel_post case<commit_after>/* eslint-disable no-underscore-dangle */
/* eslint-disable no-console */

class TelegrafLogger {
  constructor(options) {
    this.options = Object.assign({
      log: console.log,
      format: '%updateType => @%username %firstName %lastName (%fromId): <%updateSubType> %content',
      contentLength: 100,
    }, options);
  }

  middleware() {
    return (ctx, next) => {
      let content;
      let updateTypeId;

      switch (ctx.updateType) {
        case 'message':
          updateTypeId = ctx.message.message_id;

          if (ctx.message.text) {
            content = ctx.message.text;
          } else if (ctx.message.sticker) {
            content = ctx.message.sticker.emoji;
          } else {
            content = '';
          }

          break;

        case 'edited_message':
          updateTypeId = ctx.editedMessage.message_id;
          content = ctx.editedMessage.text;
          break;

        case 'channel_post':
          updateTypeId = ctx.channelPost.message_id;

          if (ctx.channelPost.text) {
            content = ctx.channelPost.text;
          } else if (ctx.channelPost.sticker) {
            content = ctx.channelPost.sticker.emoji;
          } else {
            content = '';
          }

          break;

        case 'edited_channel_post':
          updateTypeId = ctx.editedChannelPost.message_id;
          content = ctx.editedChannelPost.text;
          break;

        case 'callback_query':
          updateTypeId = ctx.callbackQuery.id;
          content = ctx.callbackQuery.data;
          break;

        case 'inline_query':
          updateTypeId = ctx.inlineQuery.id;
          content = ctx.inlineQuery.query;
          break;

        case 'chosen_inline_result':
          updateTypeId = ctx.chosenInlineResult.result_id;
          content = ctx.chosenInlineResult.query;
          break;

        case 'shipping_query':
          updateTypeId = ctx.shippingQuery.id;
          content = ctx.shippingQuery.invoice_payload;
          break;

        case 'pre_checkout_query':
          updateTypeId = ctx.preCheckoutQuery.id;
          content = ctx.preCheckoutQuery.invoice_payload;
          break;

        default:
          content = '';
          updateTypeId = 0;
      }

      const text = this.options.format
        .replace(/%botUsername\b/igm, ctx.me || null)
        .replace(/%username\b/igm, ctx.from.username || null)
        .replace(/%firstName\b/igm, ctx.from.first_name)
        .replace(/%lastName\b/igm, ctx.from.last_name || '')
        .replace(/%fromId\b/igm, ctx.from.id)
        .replace(/%chatId\b/igm, ctx.chat && ctx.chat.id)
        .replace(/%chatType\b/igm, ctx.chat && ctx.chat.type)
        .replace(/%chatTitle\b/igm, (ctx.chat && ctx.chat.title) || null)
        .replace(/%chatUsername\b/igm, (ctx.chat && ctx.chat.username) || null)
        .replace(/%updateId\b/igm, ctx.update.update_id)
        .replace(/%updateType\b/igm, ctx.updateType)
        .replace(/%updateTypeId\b/igm, updateTypeId)
        .replace(/%updateSubType\b/igm, ctx.updateSubType || ctx.updateSubTypes[0] || ctx.updateType)
        .replace(/%sceneId\b/igm, (ctx.session && ctx.session._flow && ctx.session._flow.id) || null)
        .replace(/ +|\n/g, ' ');

      if (content.length > this.options.contentLength) {
        content = `${content.slice(0, this.options.contentLength)}...`;
      }

      this.options.log(text.replace(/%content\b/igm, content).replace(/\n/g, ' '));
      return next();
    };
  }
}

module.exports = TelegrafLogger;
<|endoftext|>


<commit_before>/**
 * Script to make form controls control CSS styles
 * Requires css-edit.js
 * @author Lea Verou
 * MIT License
 */

(function(){

var self = window.CSSControl = function(control) {
	var me = this;

	this.control = control;

	// this holds the elements the CSS is gonna be applied to
	this.subjects = CSSEdit.getSubjects(control);

	CSSEdit.setup(control);

	control.addEventListener('input', function() {
		me.update();
	}, false);

	control.addEventListener('change', function() {
		me.update();
	}, false);

	this.update();
};

self.prototype = {
	update: function() {
		// Get code
		var code = this.control.getAttribute('data-style').replace(/\{value\}/gi, this.control.value);

		CSSEdit.updateStyle(this.subjects, code, 'style');
	}
};

})();<commit_msg>Fixed bug in css-controls<commit_after>/**
 * Script to make form controls control CSS styles
 * Requires css-edit.js
 * @author Lea Verou
 * MIT License
 */

(function(){

var self = window.CSSControl = function(control) {
	var me = this;

	this.control = control;

	// this holds the elements the CSS is gonna be applied to
	this.subjects = CSSEdit.getSubjects(control);

	CSSEdit.setupSubjects(control);

	control.addEventListener('input', function() {
		me.update();
	}, false);

	control.addEventListener('change', function() {
		me.update();
	}, false);

	this.update();
};

self.prototype = {
	update: function() {
		// Get code
		var code = this.control.getAttribute('data-style').replace(/\{value\}/gi, this.control.value);

		CSSEdit.updateStyle(this.subjects, code, 'style');
	}
};

})();<|endoftext|>


<commit_before>var page = require('webpage').create(),
  fs = require('fs'),
  system = require('system'),
  margin = system.args[5] || '0cm',
  orientation = system.args[6] || 'portrait',
  cookie_file = system.args[7] ,
  render_time = system.args[8] || 10000 ,
  time_out = system.args[9] || 90000 ,
  cookies = {},
  address, output, size, statusCode;

window.setTimeout(function () {
  console.log("Shit's being weird no result within: " + time_out + "ms");
  phantom.exit(1);
}, time_out);

try {
  f = fs.open(cookie_file, "r");
  cookies = JSON.parse(f.read());
  fs.remove(cookie_file)
} catch (e) {
  console.log(e);
}
phantom.cookiesEnabled = true;
phantom.cookies = cookies;

if (system.args.length < 3 || system.args.length > 10) {
  console.log('Usage: rasterize.js URL filename [paperwidth*paperheight|paperformat] [zoom] [margin] [orientation] [cookie_file] [render_time] [time_out]');
  console.log('  paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"');
  phantom.exit(1);
} else {
  address = system.args[1];
  output = system.args[2];
  page.viewportSize = { width:600, height:600 };
  if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") {
    size = system.args[3].split('*');
    page.paperSize = size.length === 2 ? { width:size[0], height:size[1], margin:'0px' }
      : { format:system.args[3], orientation:orientation, margin:margin };
  }
  if (system.args.length > 4) {
    page.zoomFactor = system.args[4];
  }

  // determine the statusCode
  page.onResourceReceived = function (resource) {
    if (resource.url == address) {
      statusCode = resource.status;
    }
  };

  page.open(address, function (status) {
    if (status !== 'success' || (statusCode != 200 && statusCode != null)) {
      console.log(statusCode, 'Unable to load the address!');
      if (fs.exists(output)) {
        fs.remove(output);
      }
      try {
        fs.touch(output);
      }
      catch (e) {
        phantom.exit(1);
        throw e
      }
      phantom.exit(1);
    } else {
      window.setTimeout(function () {
        page.render(output + '_tmp.pdf');

        if (fs.exists(output)) {
          fs.remove(output);
        }

        try {
          fs.move(output + '_tmp.pdf', output);
        }
        catch (e) {
          phantom.exit(1);
          throw e
        }
        console.log('rendered to: ' + output, new Date().getTime());
        phantom.exit();
      }, render_time);
    }
  });
}
<commit_msg>add header and footer printing<commit_after>var page = require('webpage').create(),
  fs = require('fs'),
  system = require('system'),
  margin = system.args[5] || '0cm',
  orientation = system.args[6] || 'portrait',
  cookie_file = system.args[7] ,
  render_time = system.args[8] || 10000 ,
  time_out = system.args[9] || 90000 ,
  cookies = {},
  address, output, size, statusCode;

window.setTimeout(function () {
  console.log("Shit's being weird no result within: " + time_out + "ms");
  phantom.exit(1);
}, time_out);

try {
  f = fs.open(cookie_file, "r");
  cookies = JSON.parse(f.read());
  fs.remove(cookie_file)
} catch (e) {
  console.log(e);
}
phantom.cookiesEnabled = true;
phantom.cookies = cookies;

if (system.args.length < 3 || system.args.length > 10) {
  console.log('Usage: rasterize.js URL filename [paperwidth*paperheight|paperformat] [zoom] [margin] [orientation] [cookie_file] [render_time] [time_out]');
  console.log('  paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"');
  phantom.exit(1);
} else {
  address = system.args[1];
  output = system.args[2];
  page.viewportSize = { width:600, height:600 };
  if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") {
    size = system.args[3].split('*');
    page.paperSize = size.length === 2 ? { width:size[0], height:size[1], margin:'0px' }
      : { format:system.args[3], orientation:orientation, margin:margin };
  }
  if (system.args.length > 4) {
    page.zoomFactor = system.args[4];
  }

  // determine the statusCode
  page.onResourceReceived = function (resource) {
    if (resource.url == address) {
      statusCode = resource.status;
    }
  };

  page.open(address, function (status) {
    if (status !== 'success' || (statusCode != 200 && statusCode != null)) {
      console.log(statusCode, 'Unable to load the address!');
      if (fs.exists(output)) {
        fs.remove(output);
      }
      try {
        fs.touch(output);
      }
      catch (e) {
        phantom.exit(1);
        throw e
      }
      phantom.exit(1);
    } else {
      if (page.evaluate(function(){return typeof PhantomJSPrinting == "object";})) {
          paperSize = page.paperSize;
          paperSize.header.height = page.evaluate(function() {
              return PhantomJSPrinting.header.height;
          });
          paperSize.header.contents = phantom.callback(function(pageNum, numPages) {
              return page.evaluate(function(pageNum, numPages){return PhantomJSPrinting.header.contents(pageNum, numPages);}, pageNum, numPages);
          });
          paperSize.footer.height = page.evaluate(function() {
              return PhantomJSPrinting.footer.height;
          });
          paperSize.footer.contents = phantom.callback(function(pageNum, numPages) {
              return page.evaluate(function(pageNum, numPages){return PhantomJSPrinting.footer.contents(pageNum, numPages);}, pageNum, numPages);
          });
          page.paperSize = paperSize;
          console.log(page.paperSize.header.height);
          console.log(page.paperSize.footer.height);
      }
      window.setTimeout(function () {
        page.render(output + '_tmp.pdf');

        if (fs.exists(output)) {
          fs.remove(output);
        }

        try {
          fs.move(output + '_tmp.pdf', output);
        }
        catch (e) {
          phantom.exit(1);
          throw e
        }
        console.log('rendered to: ' + output, new Date().getTime());
        phantom.exit();
      }, render_time);
    }
  });
}
<|endoftext|>


<commit_before>/**
 * Created by petersquicciarini on 3/9/17.
 */

const winston = require('winston');

if (!process.env.slackClientID || !process.env.slackClientSecret ||
  !process.env.ticketSystemUrl || !process.env.PORT || !process.env.mongoUri ||
  !process.env.escalationTeam) {
  winston.log('error', 'Please see README for required env vars.');
  process.exit();
}

const Botkit = require('botkit');
const mongoStorage = require('botkit-storage-mongo')({ mongoUri: process.env.mongoUri });
const url = require('url');
const quotes = require('./lib/quotes');
const freshservice = require('./lib/freshservice');
const request = require('request');

const connectionInfo = {
  slackClientID: process.env.slackClientID,
  slackClientSecret: process.env.slackClientSecret,
  ticketSystemUrl: process.env.ticketSystemUrl,
  ticketSystemAPIKey: process.env.ticketSystemAPIKey,
};
const controller = Botkit.slackbot({
  storage: mongoStorage,
}).configureSlackApp({
  clientId: connectionInfo.slackClientID,
  clientSecret: connectionInfo.slackClientSecret,
  scopes: ['bot', 'chat:write:bot', 'users:read', 'channels:read'],
});

controller.setupWebserver(process.env.PORT, () => {
  controller.createWebhookEndpoints(controller.webserver);
  controller.createHomepageEndpoint(controller.webserver);
  controller.createOauthEndpoints(controller.webserver, (err, req, res) => {
    if (err) {
      res.status(500).send(`ERROR: ${err}`);
    } else {
      res.send('Success!');
    }
  });
});

// just a simple way to make sure we don't
// connect to the RTM twice for the same team
const activeBots = {};
function trackBot(bot) {
  activeBots[bot.config.token] = bot;
}

controller.on('create_bot', (bot) => {
  if (activeBots[bot.config.token]) {
    // already online! do nothing.
  } else {
    bot.startRTM((err) => {
      if (!err) {
        trackBot(bot);
      }
    });
  }
});

controller.storage.teams.all((err, teams) => {
  if (err) {
    throw new Error(err);
  }
  // connect all teams with bots up to slack!
  Object.keys(teams).forEach((t) => {
    if (teams[t].bot) {
      controller.spawn(teams[t]).startRTM((startErr, bot) => {
        if (startErr) {
          winston.log('error', 'Could not connect to Slack RTM.');
        } else {
          trackBot(bot);
        }
      });
    }
  });
});
controller.on('rtm_open', () => {
  winston.log('info', '** The RTM api just connected!');
});
controller.on('rtm_close', (bot) => {
  winston.log('warn', '** The RTM api just closed');
  bot.startRTM((err) => {
    if (!err) {
      trackBot(bot);
    }
  });
});

// *** Begin Ticket Info/Link Handler *** //
controller.hears(['ticket ([0-9]+)'], ['ambient'], (bot, message) => {
  winston.log('info', 'Hazel heard a ticket # pattern. Crafting reply...');
  bot.api.channels.info({ channel: message.channel }, (err, info) => {
    if (err) {
      winston.log('error', 'Hazel could not get channel info.');
    } else {
      winston.log('debug', 'Hazel got the channel info.');
      if (info.channel.name === 'emg') {
        winston.log('info', 'We are in the EMG channel. Sending buttons...');
        const emgTicketReply = {
          username: 'hazel',
          icon_emoji: ':octopus:',
          text: "On Call Tech: Let everyone know you're taking care of it, and escalate if you need to:",
          attachments: [
            {
              fallback: "(Sorry. I couldn't make the cool buttons this time.)",
              callback_id: 'emgResponse',
              actions: [
                {
                  name: 'acceptEmg',
                  text: "I've got it.",
                  style: 'primary',
                  type: 'button',
                },
                {
                  name: 'escalateEmg',
                  text: 'I need to escalate!',
                  type: 'button',
                },
              ],
            },
          ],
        };
        bot.reply(message, emgTicketReply);
      } else {
        const ticketID = message.match[1];
        if (process.env.ticketSystemAPIKey) {
          bot.startTyping(message);
          winston.log('info', 'Looks like an API key for FreshService was provided. Will try to get ticket info...');
          const reqOptions = freshservice(connectionInfo.ticketSystemUrl, connectionInfo.ticketSystemAPIKey, 'GET', `${ticketID}.json`);
          request(reqOptions, (reqErr, res, body) => {
            winston.log('debug', 'Just sent request to FreshService.');
            if (err || res.statusCode !== 200) {
              winston.log('warning', 'Hazel could not get ticket info from FreshService. Sending link instead...');
              bot.reply(message, {
                text: url.resolve(connectionInfo.ticketSystemUrl, ticketID),
                username: 'hazel',
                icon_emoji: ':octopus:',
              });
            } else {
              winston.log('debug', 'Got a good response from FreshService. Sending ticket stub...');
              const ticketInfoMessage = {
                username: 'hazel',
                icon_emoji: ':octopus:',
                attachments: [{
                  title: `Ticket ${ticketID}: ${body.helpdesk_ticket.subject}`,
                  title_link: url.resolve(connectionInfo.ticketSystemUrl, ticketID),
                  fields: [
                    {
                      title: 'Description',
                      value: `${body.helpdesk_ticket.description.substring(0, 300)}...`,
                      short: false,
                    },
                    /*
                    {
                      title: 'Requester',
                      value: body.helpdesk_ticket.requester_name,
                      short: true,
                    },
                    {
                      title: 'Agent',
                      value: body.helpdesk_ticket.responder_name,
                      short: true,
                    },
                    {
                      title: 'Status',
                      value: body.helpdesk_ticket.status_name,
                      short: true,
                    },
                    {
                      title: 'Priority',
                      value: body.helpdesk_ticket.priority_name,
                      short: true,
                    },
                    */
                  ],
                }],
              };
              bot.stopTyping(message);
              bot.reply(message, ticketInfoMessage);
            }
          });
        } else {
          winston.log('info', 'No API key provided, so just sending the URL to the ticket...');
          bot.reply(message, {
            text: url.resolve(connectionInfo.ticketSystemUrl, ticketID),
            username: 'hazel',
            icon_emoji: ':octopus:',
          });
        }
      }
    }
  });
});
// *** End Ticket Info/Link Handler *** //

// *** Begin Quote Handler *** //
controller.hears(['quote', 'proverb', 'wisdom'], ['direct_mention'], (bot, message) => {
  winston.log('info', 'Hazel heard someone mention her directly. Sending reply...');
  bot.reply(message, {
    username: 'hazel',
    icon_emoji: ':octopus:',
    text: quotes.getRandomQuote(),
  });
});
// *** End Quote Handler *** //

// *** Begin EMG Buttons Handler *** //
controller.on('interactive_message_callback', (bot, message) => {
  bot.api.users.info({ user: message.user }, (err, info) => {
    let buttonPresser = null;
    if (err) {
      winston.log('error', 'Could not get user name of button presser');
    } else {
      buttonPresser = info.user.name;
    }
    if (message.actions[0].name === 'acceptEmg') {
      bot.replyInteractive(message, {
        username: 'hazel',
        icon_emoji: ':octopus:',
        text: message.original_message.text,
        attachments: [
          {
            fallback: "(Sorry. I couldn't make the cool buttons this time.)",
            callback_id: 'emgResponse',
            actions: [
              {
                name: 'escalateEmg',
                text: 'I need to escalate!',
                type: 'button',
              },
            ],
          },
        ],
      });
      bot.reply(message, {
        text: `<!everyone> ${buttonPresser || 'A tech'} is taking care of this emergency.`,
        username: 'hazel',
        icon_emoji: ':octopus:',
      });
    }
    if (message.actions[0].name === 'escalateEmg') {
      bot.replyInteractive(message, {
        username: 'hazel',
        icon_emoji: ':octopus:',
        text: message.original_message.text,
        attachments: [
          {
            fallback: 'Tech needs to escalate!',
            text: '*Message sent to the escalation team for help.*',
            mrkdwn_in: ['text'],
          },
        ],
      });
      const escalationTeam = process.env.escalationTeam.split(/,\s?/);
      escalationTeam.forEach((person) => {
        bot.say({
          username: 'hazel',
          icon_emoji: ':octopus:',
          channel: `@${person}`,
          text: `${buttonPresser || 'The On Call Tech'} needs to escalate the emergency.`,
        });
      });
    }
  });
});
// *** End EMG Buttons Handler *** //
<commit_msg>the stop typing thing might have been a mistake<commit_after>/**
 * Created by petersquicciarini on 3/9/17.
 */

const winston = require('winston');

if (!process.env.slackClientID || !process.env.slackClientSecret ||
  !process.env.ticketSystemUrl || !process.env.PORT || !process.env.mongoUri ||
  !process.env.escalationTeam) {
  winston.log('error', 'Please see README for required env vars.');
  process.exit();
}

const Botkit = require('botkit');
const mongoStorage = require('botkit-storage-mongo')({ mongoUri: process.env.mongoUri });
const url = require('url');
const quotes = require('./lib/quotes');
const freshservice = require('./lib/freshservice');
const request = require('request');

const connectionInfo = {
  slackClientID: process.env.slackClientID,
  slackClientSecret: process.env.slackClientSecret,
  ticketSystemUrl: process.env.ticketSystemUrl,
  ticketSystemAPIKey: process.env.ticketSystemAPIKey,
};
const controller = Botkit.slackbot({
  storage: mongoStorage,
}).configureSlackApp({
  clientId: connectionInfo.slackClientID,
  clientSecret: connectionInfo.slackClientSecret,
  scopes: ['bot', 'chat:write:bot', 'users:read', 'channels:read'],
});

controller.setupWebserver(process.env.PORT, () => {
  controller.createWebhookEndpoints(controller.webserver);
  controller.createHomepageEndpoint(controller.webserver);
  controller.createOauthEndpoints(controller.webserver, (err, req, res) => {
    if (err) {
      res.status(500).send(`ERROR: ${err}`);
    } else {
      res.send('Success!');
    }
  });
});

// just a simple way to make sure we don't
// connect to the RTM twice for the same team
const activeBots = {};
function trackBot(bot) {
  activeBots[bot.config.token] = bot;
}

controller.on('create_bot', (bot) => {
  if (activeBots[bot.config.token]) {
    // already online! do nothing.
  } else {
    bot.startRTM((err) => {
      if (!err) {
        trackBot(bot);
      }
    });
  }
});

controller.storage.teams.all((err, teams) => {
  if (err) {
    throw new Error(err);
  }
  // connect all teams with bots up to slack!
  Object.keys(teams).forEach((t) => {
    if (teams[t].bot) {
      controller.spawn(teams[t]).startRTM((startErr, bot) => {
        if (startErr) {
          winston.log('error', 'Could not connect to Slack RTM.');
        } else {
          trackBot(bot);
        }
      });
    }
  });
});
controller.on('rtm_open', () => {
  winston.log('info', '** The RTM api just connected!');
});
controller.on('rtm_close', (bot) => {
  winston.log('warn', '** The RTM api just closed');
  bot.startRTM((err) => {
    if (!err) {
      trackBot(bot);
    }
  });
});

// *** Begin Ticket Info/Link Handler *** //
controller.hears(['ticket ([0-9]+)'], ['ambient'], (bot, message) => {
  winston.log('info', 'Hazel heard a ticket # pattern. Crafting reply...');
  bot.api.channels.info({ channel: message.channel }, (err, info) => {
    if (err) {
      winston.log('error', 'Hazel could not get channel info.');
    } else {
      winston.log('debug', 'Hazel got the channel info.');
      if (info.channel.name === 'emg') {
        winston.log('info', 'We are in the EMG channel. Sending buttons...');
        const emgTicketReply = {
          username: 'hazel',
          icon_emoji: ':octopus:',
          text: "On Call Tech: Let everyone know you're taking care of it, and escalate if you need to:",
          attachments: [
            {
              fallback: "(Sorry. I couldn't make the cool buttons this time.)",
              callback_id: 'emgResponse',
              actions: [
                {
                  name: 'acceptEmg',
                  text: "I've got it.",
                  style: 'primary',
                  type: 'button',
                },
                {
                  name: 'escalateEmg',
                  text: 'I need to escalate!',
                  type: 'button',
                },
              ],
            },
          ],
        };
        bot.reply(message, emgTicketReply);
      } else {
        const ticketID = message.match[1];
        if (process.env.ticketSystemAPIKey) {
          bot.startTyping(message);
          winston.log('info', 'Looks like an API key for FreshService was provided. Will try to get ticket info...');
          const reqOptions = freshservice(connectionInfo.ticketSystemUrl, connectionInfo.ticketSystemAPIKey, 'GET', `${ticketID}.json`);
          request(reqOptions, (reqErr, res, body) => {
            winston.log('debug', 'Just sent request to FreshService.');
            if (err || res.statusCode !== 200) {
              winston.log('warning', 'Hazel could not get ticket info from FreshService. Sending link instead...');
              bot.reply(message, {
                text: url.resolve(connectionInfo.ticketSystemUrl, ticketID),
                username: 'hazel',
                icon_emoji: ':octopus:',
              });
            } else {
              winston.log('debug', 'Got a good response from FreshService. Sending ticket stub...');
              const ticketInfoMessage = {
                username: 'hazel',
                icon_emoji: ':octopus:',
                attachments: [{
                  title: `Ticket ${ticketID}: ${body.helpdesk_ticket.subject}`,
                  title_link: url.resolve(connectionInfo.ticketSystemUrl, ticketID),
                  fields: [
                    {
                      title: 'Description',
                      value: `${body.helpdesk_ticket.description.substring(0, 300)}...`,
                      short: false,
                    },
                    /*
                    {
                      title: 'Requester',
                      value: body.helpdesk_ticket.requester_name,
                      short: true,
                    },
                    {
                      title: 'Agent',
                      value: body.helpdesk_ticket.responder_name,
                      short: true,
                    },
                    {
                      title: 'Status',
                      value: body.helpdesk_ticket.status_name,
                      short: true,
                    },
                    {
                      title: 'Priority',
                      value: body.helpdesk_ticket.priority_name,
                      short: true,
                    },
                    */
                  ],
                }],
              };
              bot.reply(message, ticketInfoMessage);
            }
          });
        } else {
          winston.log('info', 'No API key provided, so just sending the URL to the ticket...');
          bot.reply(message, {
            text: url.resolve(connectionInfo.ticketSystemUrl, ticketID),
            username: 'hazel',
            icon_emoji: ':octopus:',
          });
        }
      }
    }
  });
});
// *** End Ticket Info/Link Handler *** //

// *** Begin Quote Handler *** //
controller.hears(['quote', 'proverb', 'wisdom'], ['direct_mention'], (bot, message) => {
  winston.log('info', 'Hazel heard someone mention her directly. Sending reply...');
  bot.reply(message, {
    username: 'hazel',
    icon_emoji: ':octopus:',
    text: quotes.getRandomQuote(),
  });
});
// *** End Quote Handler *** //

// *** Begin EMG Buttons Handler *** //
controller.on('interactive_message_callback', (bot, message) => {
  bot.api.users.info({ user: message.user }, (err, info) => {
    let buttonPresser = null;
    if (err) {
      winston.log('error', 'Could not get user name of button presser');
    } else {
      buttonPresser = info.user.name;
    }
    if (message.actions[0].name === 'acceptEmg') {
      bot.replyInteractive(message, {
        username: 'hazel',
        icon_emoji: ':octopus:',
        text: message.original_message.text,
        attachments: [
          {
            fallback: "(Sorry. I couldn't make the cool buttons this time.)",
            callback_id: 'emgResponse',
            actions: [
              {
                name: 'escalateEmg',
                text: 'I need to escalate!',
                type: 'button',
              },
            ],
          },
        ],
      });
      bot.reply(message, {
        text: `<!everyone> ${buttonPresser || 'A tech'} is taking care of this emergency.`,
        username: 'hazel',
        icon_emoji: ':octopus:',
      });
    }
    if (message.actions[0].name === 'escalateEmg') {
      bot.replyInteractive(message, {
        username: 'hazel',
        icon_emoji: ':octopus:',
        text: message.original_message.text,
        attachments: [
          {
            fallback: 'Tech needs to escalate!',
            text: '*Message sent to the escalation team for help.*',
            mrkdwn_in: ['text'],
          },
        ],
      });
      const escalationTeam = process.env.escalationTeam.split(/,\s?/);
      escalationTeam.forEach((person) => {
        bot.say({
          username: 'hazel',
          icon_emoji: ':octopus:',
          channel: `@${person}`,
          text: `${buttonPresser || 'The On Call Tech'} needs to escalate the emergency.`,
        });
      });
    }
  });
});
// *** End EMG Buttons Handler *** //
<|endoftext|>