if (typeof window !== 'undefined') { isBrowser = true; } else { var isBrowser = false; } require('./util/getStep.js'); /** * @method ImageSequencer * @param {Object|Float32Array} options Optional options * @returns {Object} */ ImageSequencer = function ImageSequencer(options) { var str = require('./Strings.js')(this.steps, modulesInfo, addSteps, copy); var sequencer = (this.name == 'ImageSequencer') ? this : this.sequencer; options = options || {}; options.inBrowser = options.inBrowser === undefined ? isBrowser : options.inBrowser; options.sequencerCounter = 0; function objTypeOf(object) { return Object.prototype.toString.call(object).split(' ')[1].slice(0, -1); } /** * @method log * @description Logs colored messages to the console using ASCII color codes * @param {String} color ASCII color code * @param {String} msg Message to be logged to the console */ function log(color, msg) { if (options.ui != 'none') { if (arguments.length == 1) console.log(arguments[0]); else if (arguments.length == 2) console.log(color, msg); } } /** * @method copy * @description Returns a clone of the input object. * @param {Object|Float32Array} a The Object/Array to be cloned * @returns {Object|Float32Array} */ function copy(a) { if (!typeof (a) == 'object') return a; if (objTypeOf(a) == 'Array') return a.slice(); if (objTypeOf(a) == 'Object') { var b = {}; for (var v in a) { b[v] = copy(a[v]); } return b; } return a; } function makeArray(input) { return (objTypeOf(input) == 'Array') ? input : [input]; } var image, steps = [], modules = require('./Modules'), sequences = require('./SavedSequences.json'), formatInput = require('./FormatInput'), inputlog = [], events = require('./ui/UserInterface')(), fs = require('fs'); if (options.inBrowser) { for (o in sequencer) { modules[o] = sequencer[o]; } sequences = JSON.parse(window.localStorage.getItem('sequences')); // Get saved sequences from localStorage if (!sequences) { sequences = {}; window.localStorage.setItem('sequences', JSON.stringify(sequences)); // Set the localStorage entry as an empty Object by default } } // if in browser, prompt for an image // if (options.imageSelect || options.inBrowser) addStep('image-select'); // else if (options.imageUrl) loadImage(imageUrl); /** * @method addSteps * @description Adds one of more steps to the sequence. * @return {Object} */ function addSteps() { var this_ = (this.name == 'ImageSequencer') ? this : this.sequencer; var args = []; var json_q = {}; for (var arg in arguments) { args.push(copy(arguments[arg])); } // Get all the module names from the arguments json_q = formatInput.call(this_, args, '+'); inputlog.push({ method: 'addSteps', json_q: copy(json_q) }); for (var j in json_q) require('./AddStep')(this_, json_q[j].name, json_q[j].o); return this; } /** * @method removeStep * @description Removes the step at the specified index from the sequence. * @param {Object} ref ImageSequencer instance * @param {Number} index Index of the step to be removed * @returns {Null} */ function removeStep(ref, index) { // Remove the step from images[image].steps and redraw remaining images if (index > 0) { // var this_ = (this.name == "ImageSequencer") ? this : this.sequencer; thisStep = ref.steps[index]; thisStep.UI.onRemove(thisStep.options.step); ref.steps.splice(index, 1); } } /** * @method removeSteps * @description Removes one or more steps from the sequence * @returns {Object} */ function removeSteps() { var indices; var this_ = (this.name == 'ImageSequencer') ? this : this.sequencer; var args = []; for (var arg in arguments) args.push(copy(arguments[arg])); var json_q = formatInput.call(this_, args, '-'); inputlog.push({ method: 'removeSteps', json_q: copy(json_q) }); indices = json_q.sort(function(a, b) { return b - a; }); for (var i in indices) removeStep(this_, indices[i]); return this; } /** * @method insertSteps * @description Inserts steps at the specified index * @returns {Object} */ function insertSteps() { var this_ = (this.name == 'ImageSequencer') ? this : this.sequencer; var args = []; for (var arg in arguments) args.push(arguments[arg]); var json_q = formatInput.call(this_, args, '^'); inputlog.push({ method: 'insertSteps', json_q: copy(json_q) }); var details = json_q; details = details.sort(function(a, b) { return b.index - a.index; }); for (var i in details) require('./InsertStep')(this_, details[i].index, details[i].name, details[i].o); return this; } /** * @method run * @param {Object} config Object which contains the runtime configuration like progress bar information and index from which the sequencer should run. * @returns {Boolean} */ function run(config) { var progressObj, index = 0; config = config || { mode: 'no-arg' }; if (config.index) index = config.index; if (config.mode != 'no-arg' && typeof config != 'function') { if (config.progressObj) progressObj = config.progressObj; delete arguments['0']; } var this_ = (this.name == 'ImageSequencer') ? this : this.sequencer; var args = []; for (var arg in arguments) args.push(copy(arguments[arg])); var callback = function() { }; for (var arg in args) if (objTypeOf(args[arg]) == 'Function') callback = args.splice(arg, 1)[0]; // Callback is formed var json_q = formatInput.call(this_, args, 'r'); require('./Run')(this_, json_q, callback, index, progressObj); return true; } /** * @method loadImages * @description Loads an image via dataURL or normal URL. Read the docs(https://github.com/publiclab/image-sequencer/blob/main/README.md) for more info. * @returns {Null} */ function loadImages() { var args = []; var prevSteps = this.getSteps().slice(1).map(step=>step.options.name); var sequencer = this; sequencer.image = arguments[0]; for (var arg in arguments) args.push(copy(arguments[arg])); var json_q = formatInput.call(this, args, 'l'); if(this.getSteps().length != 0){ this.options.sequencerCounter = 0; inputlog = []; this.steps = []; } inputlog.push({ method: 'loadImages', json_q: copy(json_q) }); var ret = { name: 'ImageSequencer Wrapper', sequencer: this, addSteps: this.addSteps, removeSteps: this.removeSteps, insertSteps: this.insertSteps, run: this.run, UI: this.UI, setUI: this.setUI }; function loadPrevSteps(ref){ if(prevSteps.length != 0){ ref.addSteps(prevSteps); prevSteps = []; } } require('./ui/LoadImage')(sequencer, 'image', json_q.image, function() { loadPrevSteps(sequencer); json_q.callback.call(ret); }); } /** * @method replaceImage * @description Replaces the current image in the sequencer * @param {String} selector DOM selector string for the image input * @param {*} steps Current steps Object * @param {Object} options * @returns {*} */ function replaceImage(selector, steps, options) { options = options || {}; options.callback = options.callback || function() { }; return require('./ReplaceImage')(this, selector, steps, options); } /** * @method getSteps * @description Returns the current sequence of steps * @returns {Object} */ function getSteps(){ return this.steps; } /** * @method setUI * @description To set up a UI for ImageSequencer via different callback methods. Read the docs(https://github.com/publiclab/image-sequencer/blob/main/README.md) for more info. * @param {Object} UI Object containing UI callback methods. Read the docs(https://github.com/publiclab/image-sequencer/blob/main/README.md) for more info. * @returns {Null} */ function setUI(UI) { this.events = require('./ui/UserInterface')(UI); } var exportBin = function(dir, basic, filename) { return require('./ExportBin')(dir, this, basic, filename); }; /** * @method modulesInfo * @description Returns information about the given module or all the available modules * @param {String} name Module name * @returns {Object} */ function modulesInfo(name) { var modulesdata = {}; if (name == 'load-image') return {}; if (arguments.length == 0) { for (var modulename in this.modules) { modulesdata[modulename] = modules[modulename][1]; } for (var sequencename in this.sequences) { modulesdata[sequencename] = { name: sequencename, steps: this.sequences[sequencename] }; } } else { if (modules[name]){ modulesdata = modules[name][1]; } else modulesdata = { 'inputs': sequences[name]['options'] }; } return modulesdata; } /** * @method loadNewModule * @description Adds a new local module to sequencer. Read the docs(https://github.com/publiclab/image-sequencer/blob/main/README.md) for mode info. * @param {String} name Name of the new module * @param {Object} options An Object containing path and info about the new module. * @returns {Object} */ function loadNewModule(name, options) { if (!options) { return this; } else if (Array.isArray(options)) { // Contains the array of module and info this.modules[name] = options; } else if (options.func && options.info) { // Passed in options object this.modules[name] = [ options.func, options.info ]; } else if (options.path && !this.inBrowser) { // Load from path(only in node) const module = [ require(`${options.path}/Module.js`), require(`${options.path}/info.json`) ]; this.modules[name] = module; } return this; } /** * @method saveNewModule * @description Saves a new local module to ImageSequencer * @param {String} name Name of the new module * @param {String} path Path to the new module * @returns {Null} */ function saveNewModule(name, path) { if (options.inBrowser) { // Not for browser context return; } var mods = fs.readFileSync('./src/Modules.js').toString(); mods = mods.substr(0, mods.length - 1) + ' \'' + name + '\': require(\'' + path + '\'),\n}'; fs.writeFileSync('./src/Modules.js', mods); } /** * @method saveSequence * @description Saves a sequence on the browser localStorage. * @param {String} name Name for the sequence * @param {String} sequenceString Sequence data as a string * @returns {Null} */ function saveSequence(name, sequenceString) { // 4. save sequence const sequence = str.stringToJSON(sequenceString); // Save the given sequence string as a module if (options.inBrowser) { // Inside the browser we save the meta-modules using the Web Storage API var sequences = JSON.parse(window.localStorage.getItem('sequences')); sequences[name] = sequence; window.localStorage.setItem('sequences', JSON.stringify(sequences)); } else { // In node we save the sequences in the json file SavedSequences.json var sequences = require('./SavedSequences.json'); sequences[name] = sequence; fs.writeFileSync('./src/SavedSequences.json', JSON.stringify(sequences)); } } function loadModules() { // loadModules function loads the modules and saved sequences. this.modules = require('./Modules'); if (options.inBrowser) this.sequences = JSON.parse(window.localStorage.getItem('sequences')); else this.sequences = require('./SavedSequences.json'); } return { // Literals and objects name: 'ImageSequencer', options: options, inputlog: inputlog, modules: modules, sequences: sequences, events: events, steps: steps, image: image, // User functions loadImages: loadImages, loadImage: loadImages, addSteps: addSteps, removeSteps: removeSteps, insertSteps: insertSteps, replaceImage: replaceImage, run: run, setUI: setUI, exportBin: exportBin, modulesInfo: modulesInfo, toCliString: str.toCliString, detectStringSyntax: str.detectStringSyntax, parseStringSyntax: str.parseStringSyntax, stringToSteps: str.stringToSteps, toString: str.toString, stepToString: str.stepToString, toJSON: str.toJSON, stringToJSON: str.stringToJSON, stringToJSONstep: str.stringToJSONstep, importString: str.importString, importJSON: str.importJSON, loadNewModule: loadNewModule, saveNewModule: saveNewModule, createMetaModule: require('./util/createMetaModule'), saveSequence: saveSequence, loadModules: loadModules, getSteps:getSteps, // Other functions log: log, objTypeOf: objTypeOf, copy: copy, getImageDimensions: require('./util/getImageDimensions'), setInputStep: require('./ui/SetInputStep')(sequencer) }; }; module.exports = ImageSequencer;