<commit_before>
const {keys, values, extend, sortBy, pairs} = require("../common")

export function patchMetaData(scope$$, newModules) {
  const {modules, exports} = scope$$

  const oldModulesByFile = {}
  values(modules).forEach(mod => oldModulesByFile[mod.file] = mod)

  const rearrangedExports = {}
  keys(newModules).forEach(id => {
    const oldModule = oldModulesByFile[newModules[id].file]
    if (oldModule) {
      rearrangedExports[id] = exports[oldModule.id]
    }
  })

  scope$$.exports = rearrangedExports
  scope$$.modules = newModules
  scope$$.initModules()
}

export function diff(modules, newModules, newEntryId) {
  const oldModulesByFile = {}
  values(modules).forEach(mod => oldModulesByFile[mod.file] = mod)

  const changedModules =
    values(newModules).filter(({entry, file, hash}) => {
      return !oldModulesByFile[file] || oldModulesByFile[file].hash !== hash
    })

  // resolve reverse dependencies so that we can calculate
  // weights for correct reloading order
  const dependencies = {}
  function resolveDeps(mod) {
    const deps = values(mod.deps)
    if (!deps) return;
    dependencies[mod.id] = deps
    deps.forEach(d => {
      if (!dependencies[d]) resolveDeps(newModules[d])
    })
  }
  resolveDeps(newModules[newEntryId])

  const parents = {}
  pairs(dependencies).forEach(([id, deps]) => {
    deps.forEach(d => parents[d] = [id, ...(parents[d] || [])])
  })

  // idea behind weighting: each file has initial weight = 1
  // each file gets also the sum of its dependency weights
  // finally files are sorted by weight => smaller ones must
  // be reloaded before their dependants (bigger weights)
  const weights = {}
  const hasChanged = {}
  changedModules.forEach(({id}) => {
    hasChanged[id] = true
    addWeightsStartingFrom(id, weights, parents)
  })

  const modulesToReload =
    sortBy(pairs(weights), ([_, weight]) => weight)
      .map(([id]) => newModules[id])
      .filter(module => !!module && !module.entry)
      .map(module => ({
        ...module,
        changed: !!hasChanged[module.id],
        parents: parents[module.id] || [],
        isNew: !oldModulesByFile[module.file]
      }))

  return modulesToReload

  function addWeightsStartingFrom(id, weights, parents) {
    const visited = {}
    weightRecur(id, 1)
    function weightRecur(id, w) {
      if (visited[id]) {
        // prevent circular dependency stack overflow
        return
      }
      const dependants = parents[id] || []
      visited[id] = true
      weights[id] = (weights[id] || 0) + w
      dependants.forEach(d => weightRecur(d, weights[id] + 1))
    }
  }
}
<commit_msg>Better fix<commit_after>
const {keys, values, extend, sortBy, pairs} = require("../common")

export function patchMetaData(scope$$, newModules) {
  const {modules, exports} = scope$$

  const oldModulesByFile = {}
  values(modules).forEach(mod => oldModulesByFile[mod.file] = mod)

  const rearrangedExports = {}
  keys(newModules).forEach(id => {
    const oldModule = oldModulesByFile[newModules[id].file]
    if (oldModule) {
      rearrangedExports[id] = exports[oldModule.id]
    }
  })

  scope$$.exports = rearrangedExports
  scope$$.modules = newModules
  scope$$.initModules()
}

export function diff(modules, newModules, newEntryId) {
  const oldModulesByFile = {}
  values(modules).forEach(mod => oldModulesByFile[mod.file] = mod)

  const changedModules =
    values(newModules).filter(({entry, file, hash}) => {
      return !oldModulesByFile[file] || oldModulesByFile[file].hash !== hash
    })

  // resolve reverse dependencies so that we can calculate
  // weights for correct reloading order
  const dependencies = {}
  function resolveDeps(mod) {
    const deps = values(mod.deps)
    dependencies[mod.id] = deps
    deps.forEach(d => {
      if (!dependencies[d] && newModules[d]) resolveDeps(newModules[d])
    })
  }
  resolveDeps(newModules[newEntryId])

  const parents = {}
  pairs(dependencies).forEach(([id, deps]) => {
    deps.forEach(d => parents[d] = [id, ...(parents[d] || [])])
  })

  // idea behind weighting: each file has initial weight = 1
  // each file gets also the sum of its dependency weights
  // finally files are sorted by weight => smaller ones must
  // be reloaded before their dependants (bigger weights)
  const weights = {}
  const hasChanged = {}
  changedModules.forEach(({id}) => {
    hasChanged[id] = true
    addWeightsStartingFrom(id, weights, parents)
  })

  const modulesToReload =
    sortBy(pairs(weights), ([_, weight]) => weight)
      .map(([id]) => newModules[id])
      .filter(module => !!module && !module.entry)
      .map(module => ({
        ...module,
        changed: !!hasChanged[module.id],
        parents: parents[module.id] || [],
        isNew: !oldModulesByFile[module.file]
      }))

  return modulesToReload

  function addWeightsStartingFrom(id, weights, parents) {
    const visited = {}
    weightRecur(id, 1)
    function weightRecur(id, w) {
      if (visited[id]) {
        // prevent circular dependency stack overflow
        return
      }
      const dependants = parents[id] || []
      visited[id] = true
      weights[id] = (weights[id] || 0) + w
      dependants.forEach(d => weightRecur(d, weights[id] + 1))
    }
  }
}
<|endoftext|>


<commit_before>define([
	'./DijitToMongoQueryTransform',
	'./FindByUrlMixin',
	'dojo/request/handlers',
	'dojo/request/xhr',
	'dojo/store/util/QueryResults',
	'dojo/Deferred',
	"dojo/_base/lang",
	"dojo/_base/declare",
	"dojo/store/JsonRest"//
], function (DijitToMongoQueryTransform, FindByUrlMixin, handlers, xhr, QueryResults, Deferred, lang, declare, JsonRest) {

	return declare([JsonRest, FindByUrlMixin], {
		transform: null,
		constructor: function () {
			this.transform = new DijitToMongoQueryTransform();
		},
		getChildren: function (parent, onComplete, onError) {
			var parentUrl = parent ? parent.url : "";
			var results = this.query({url: {$regex: this._getRegex(parentUrl)}});
			results.then(function (r) {
				var children = [];
				var files = {};
				r.forEach(function (node) {
					var path = node.url.substring(parentUrl.length);
					var matches = path.match(/^\/?([^\/]+)/);
					if (matches && matches.length > 1) {
						var name = matches[1]
						var existingFile = files[name];
						var exactMatch = node.url == parentUrl + matches[0];
						var folder = !!existingFile || !exactMatch;
						var id = exactMatch ? node._id : (existingFile ? existingFile.id : null);
						files[name] = {id: id, url: matches[0], name: name, folder: folder};
					}
				}, this);
				Object.keys(files).forEach(
					function (key) {
						children.push(files[key]);
					});

				onComplete(children);
			});
		},
		getRoot: function (onItem) {
			onItem({name: "", url: "", folder: true});
		},
		getLabel: function (item) {
			return item.name;
		},
		mayHaveChildren: function (object) {
			return object.folder;
		},
		query: function (query, options) {
			var params = {};

			var queryParams = this.transform.transform(query);
			if (queryParams) {
				params.query = JSON.stringify(queryParams);
			} else {
				lang.mixin(params, query);
			}

			if (options && options.sort) {
				var sort = [];
				options.sort.forEach(function (col) {
					sort.push(col.attribute + (col.descending ? "-" : "+"));
				});
				params.sort = sort.join("  ");

			}

			if (options) {
				params.skip = options.start;
				// TODO use a > than query:
				params.limit = options.count;
			}
			// TODO Baucis
			var results = xhr.get(this.target, {query: params, handleAs: "json"});
			//var countParams = {count: true};
			//r.total = xhr.get(this.target, {query: countParams, handleAs: "json"});
			var r = new Deferred();
			var total = new Deferred();
			r.total = total;
			results.then(function(data){
				total.resolve(data.total);
				r.resolve(data.data);
			});
			return r;
		}, add: function (object, options) {
			if (object[this.idProperty] === "" || object[this.idProperty] === null) {
				delete object[this.idProperty];
			}
			options = options || {};

			var noHandler = function (value) {
				return value;
			}
			handlers.register("raw", noHandler);

			var promise = xhr.post(this.target, {
				data: JSON.stringify(object),
				"handleAs": "raw",
				headers: lang.mixin({
					"Content-Type": "application/json",
					Accept: this.accepts//,
					// "If-Match": "*",
					//"If-None-Match": null
				}, this.headers, options.headers)
			})

			var newPromise = new Deferred();
			promise.then(lang.hitch(this, "onAdded", newPromise), newPromise.reject);
			return newPromise;
		}, onAdded: function (promise, response) {
			var entity = JSON.parse(response.text);
			var id = entity[0][this.idProperty];
			promise.resolve(id);
		}
	});

});
<commit_msg>cleanup<commit_after>define([
	'./DijitToMongoQueryTransform',
	'./FindByUrlMixin',
	'dojo/request/handlers',
	'dojo/request/xhr',
	'dojo/Deferred',
	"dojo/_base/lang",
	"dojo/_base/declare",
	"dojo/store/JsonRest"//
], function (DijitToMongoQueryTransform, FindByUrlMixin, handlers, xhr, Deferred, lang, declare, JsonRest) {

	return declare([JsonRest, FindByUrlMixin], {
		transform: null,
		constructor: function () {
			this.transform = new DijitToMongoQueryTransform();
		},
		get: function(id, options) {
			return this.inherited(arguments,[encodeURIComponent(id),options]);
		},
		getChildren: function (parent, onComplete, onError) {
			var parentUrl = parent ? parent.url : "";
			var results = this.query({url: {$regex: this._getRegex(parentUrl)}});
			results.then(function (r) {
				var children = [];
				var files = {};
				r.forEach(function (node) {
					var path = node.url.substring(parentUrl.length);
					var matches = path.match(/^\/?([^\/]+)/);
					if (matches && matches.length > 1) {
						var name = matches[1]
						var existingFile = files[name];
						var exactMatch = node.url == parentUrl + matches[0];
						var folder = !!existingFile || !exactMatch;
						var id = exactMatch ? node._id : (existingFile ? existingFile.id : null);
						files[name] = {id: id, url: matches[0], name: name, folder: folder};
					}
				}, this);
				Object.keys(files).forEach(
					function (key) {
						children.push(files[key]);
					});

				onComplete(children);
			});
		},
		getRoot: function (onItem) {
			onItem({name: "", url: "", folder: true});
		},
		getLabel: function (item) {
			return item.name;
		},
		mayHaveChildren: function (object) {
			return object.folder;
		},
		query: function (query, options) {
			var params = {};

			var queryParams = this.transform.transform(query);
			if (queryParams) {
				params.query = JSON.stringify(queryParams);
			} else {
				lang.mixin(params, query);
			}

			if (options && options.sort) {
				var sort = [];
				options.sort.forEach(function (col) {
					sort.push(col.attribute + (col.descending ? "-" : "+"));
				});
				params.sort = sort.join("  ");

			}

			if (options) {
				params.skip = options.start;
				// TODO use a > than query:
				params.limit = options.count;
			}
			// TODO Baucis
			var results = xhr.get(this.target, {query: params, handleAs: "json"});
			//var countParams = {count: true};
			//r.total = xhr.get(this.target, {query: countParams, handleAs: "json"});
			var r = new Deferred();
			var total = new Deferred();
			r.total = total;
			results.then(function(data){
				total.resolve(data.total);
				r.resolve(data.data);
			});
			return r;
		}, add: function (object, options) {
			if (object[this.idProperty] === "" || object[this.idProperty] === null) {
				delete object[this.idProperty];
			}
			options = options || {};

			var noHandler = function (value) {
				return value;
			}
			handlers.register("raw", noHandler);

			var promise = xhr.post(this.target, {
				data: JSON.stringify(object),
				"handleAs": "raw",
				headers: lang.mixin({
					"Content-Type": "application/json",
					Accept: this.accepts//,
					// "If-Match": "*",
					//"If-None-Match": null
				}, this.headers, options.headers)
			})

			var newPromise = new Deferred();
			promise.then(lang.hitch(this, "onAdded", newPromise), newPromise.reject);
			return newPromise;
		}, onAdded: function (promise, response) {
			var entity = JSON.parse(response.text);
			var id = entity[0][this.idProperty];
			promise.resolve(id);
		}
	});

});
<|endoftext|>


<commit_before>import {Util} from './util';
import {OptionsBuilder} from './options-builder';
import {TemplateCompiler} from './template-compiler';
import {TemplateGatherer} from './template-gatherer';
import {KendoConfigBuilder} from '../config-builder';
import {inject, transient} from 'aurelia-dependency-injection';
import {TaskQueue} from 'aurelia-task-queue';
import * as LogManager from 'aurelia-logging';

const logger = LogManager.getLogger('aurelia-kendoui-bridge');

/**
* Abstraction of commonly used code across wrappers
*/
@transient()
@inject(TaskQueue, TemplateCompiler, OptionsBuilder, Util, TemplateGatherer, KendoConfigBuilder)
export class WidgetBase {

  /**
  * The element of the custom element, or the element on which a custom attribute
  * is placed. DOM events will be raised on this element
  */
  element: Element;

  /**
  * Used to prevent race conditions when events are raised before
  * all bindings have been updated.
  */
  taskQueue: TaskQueue;

  /**
  * The element on which a Kendo widget is initialized
  * This is the "element" by default
  */
  target: Element;

  /**
  * The Kendo control's name, such as kendoGrid or kendoButton
  */
  controlName: string;

  /**
  * The parent context (used for template compilation)
  */
  $parent: any;

  /**
  * The widgets parent viewmodel (this is the object instance the user will bind to)
  */
  viewModel: any;

  /**
  * A list of bindings for properties such as kEnable/kReadOnly/kValue
  * whenever one of these properties changes an API function will be called on the Kendo control
  * for example, a change of an 'kEnable' property will result in a .enable(true/false) call
  */
  bindingsToKendo: Array = [];

  constructor(taskQueue, templateCompiler, optionsBuilder, util, templateGatherer, configBuilder) {
    this.taskQueue = taskQueue;
    this.optionsBuilder = optionsBuilder;
    this.util = util;
    this.configBuilder = configBuilder;
    this.templateGatherer = templateGatherer;
    templateCompiler.initialize();
  }

  control(controlName) {
    if (!controlName || !kendo.jQuery.fn[controlName]) {
      throw new Error(`The name of control ${controlName} is invalid or not set`);
    }

    this.controlName = controlName;

    let ctor = kendo.jQuery.fn[this.controlName];
    this.kendoOptions = ctor.widget.prototype.options;
    this.kendoEvents = ctor.widget.prototype.events;

    return this;
  }

  linkViewModel(viewModel) {
    if (!viewModel) {
      throw new Error('viewModel is not set');
    }

    this.viewModel = viewModel;

    return this;
  }

  useViewResources(resources) {
    if (!resources) {
      throw new Error('resources is not set');
    }

    this.viewResources = resources;

    return this;
  }

  /**
  * adds two-way value binding
  * @param valueBindingProperty the property name of the kendo control (value/checked)
  * @param valueFunction the function on the kendo control that gets the value of the above property
  */
  useValueBinding(valueBindingProperty = 'kValue', valueFunction = 'value') {
    this.valueBindingProperty = valueBindingProperty;
    this.valueFunction = valueFunction;
    this.withValueBinding = true;

    this.bindToKendo(valueBindingProperty, valueFunction);

    return this;
  }

  /**
  * add binding to Kendo
  * whenever a bindable property in a wrapper changes, and the propertyname is in this list
  * then it will call an API function on the Kendo control to update the value
  * @param propertyName the bindable property name in the wrapper (kValue, kChecked, kEnabled)
  * @param valueFunction the API function on the kendo control that sets the value of the above property
  */
  bindToKendo(propertyName, functionName) {
    this.bindingsToKendo.push({
      propertyName: propertyName,
      functionName: functionName
    });

    return this;
  }

  /**
  * collects all options objects
  * calls all hooks
  * then initialized the Kendo control as "widget"
  */
  createWidget(options) {
    if (!options) {
      throw new Error('the createWidget() function needs to be called with an object');
    }

    if (!options.element) {
      throw new Error('element is not set');
    }

    // generate all options, including event handlers - use the rootElement if specified, otherwise fall back to the element
    // this allows a child element in a custom elements tempate to be the container for the kendo control
    // but allows the plugin to correctly discover attributes on the root element to match against events
    let allOptions = this._getOptions(options.rootElement || options.element);

    // before initialization callback
    // allows you to modify/add/remove options before the control gets initialized
    if (options.beforeInitialize) {
      options.beforeInitialize(allOptions);
    }

    // add parent context to options
    // deepExtend in kendo.core will fail with stack
    // overflow if we don't put it in an array :-\
    Object.assign(allOptions, {
      _$parent: [options.parentCtx],
      _$resources: [this.viewResources]
    });

    if (this.configBuilder.debugMode) {
      logger.debug(`initializing ${this.controlName} with the following config`, allOptions);
    }

    // instantiate the Kendo control
    let widget = this._createWidget(options.element, allOptions, this.controlName);

    widget._$parent = options.parentCtx;
    widget._$resources = this.viewResources;

    if (this.withValueBinding) {
      widget.first('change', (args) => this._handleValueChange(args.sender));
      widget.first('dataBound', (args) => this._handleValueChange(args.sender));
    }

    // use Kendo API functions to update all initial values in the wrapper
    // kValue -> .value()
    // kEnabled -> .enable()
    this.bindingsToKendo.forEach(binding => {
      let value = this.viewModel[binding.propertyName];

      // don't do this when the value of the widget is an empty string
      // as it indicates that the widget has not been databound yet
      if (typeof(value) !== 'undefined' && value !== null && value !== '') {
        widget[binding.functionName](value);
      }
    });

    if (options.afterInitialize) {
      options.afterInitialize();
    }

    return widget;
  }

  _createWidget(element, options, controlName) {
    return kendo.jQuery(element)[controlName](options).data(controlName);
  }

  /**
  * combines all options objects and properties into a single options object
  */
  _getOptions(element) {
    let options = this.optionsBuilder.getOptions(this.viewModel, this.controlName);
    let eventOptions = this.getEventOptions(element);

    // merge all option objects together
    // - options on the wrapper
    // - options compiled from all the bindable properties
    // - event handler options
    return this.util.pruneOptions(Object.assign({}, this.viewModel.kOptions || {}, options, eventOptions));
  }

  /**
  * convert attributes into a list of events a user wants to subscribe to.
  * These events are then subscribed to, which when called
  * calls the fireKendoEvent function to raise a DOM event
  */
  getEventOptions(element) {
    let options = {};
    let allowedEvents = this.kendoEvents;
    let delayedExecution = ['change'];

    // iterate all attributes on the custom elements
    // and only return the normalized kendo event's (dataBound etc)
    let events = this.util.getEventsFromAttributes(element);

    events.forEach(event => {
      // throw error if this event is not defined on the Kendo control
      if (!allowedEvents.includes(event)) {
        throw new Error(`${event} is not an event on the ${this.controlName} control`);
      }

      if (delayedExecution.includes(event)) {
        options[event] = e => {
          this.taskQueue.queueMicroTask(() => this.util.fireKendoEvent(element, this.util._hyphenate(event), e));
        };
      } else {
        options[event] = e => this.util.fireKendoEvent(element, this.util._hyphenate(event), e);
      }
    });

    return options;
  }

  _handleValueChange(widget) {
    this.viewModel[this.valueBindingProperty] = this.getValue(widget);
  }

  getValue(widget) {
    return widget[this.valueFunction]();
  }

  handlePropertyChanged(widget, property, newValue, oldValue) {
    let binding = this.bindingsToKendo.find(i => i.propertyName === property);
    if (binding) {
      widget[binding.functionName](newValue);
    }
  }

  useTemplates(target, controlName, templates) {
    return this.templateGatherer.useTemplates(target, controlName, templates);
  }

  /**
  * destroys the widget
  */
  destroy(widget) {
    if (widget) {
      kendo.destroy(widget.element);
      widget = null;

      if (this.viewModel.kWidget) {
        this.viewModel.kWidget = null;
      }
    }
  }
}
<commit_msg>fix(recreate): destroy widgets before creating them again<commit_after>import {Util} from './util';
import {OptionsBuilder} from './options-builder';
import {TemplateCompiler} from './template-compiler';
import {TemplateGatherer} from './template-gatherer';
import {KendoConfigBuilder} from '../config-builder';
import {inject, transient} from 'aurelia-dependency-injection';
import {TaskQueue} from 'aurelia-task-queue';
import * as LogManager from 'aurelia-logging';

const logger = LogManager.getLogger('aurelia-kendoui-bridge');

/**
* Abstraction of commonly used code across wrappers
*/
@transient()
@inject(TaskQueue, TemplateCompiler, OptionsBuilder, Util, TemplateGatherer, KendoConfigBuilder)
export class WidgetBase {

  /**
  * The element of the custom element, or the element on which a custom attribute
  * is placed. DOM events will be raised on this element
  */
  element: Element;

  /**
  * Used to prevent race conditions when events are raised before
  * all bindings have been updated.
  */
  taskQueue: TaskQueue;

  /**
  * The element on which a Kendo widget is initialized
  * This is the "element" by default
  */
  target: Element;

  /**
  * The Kendo control's name, such as kendoGrid or kendoButton
  */
  controlName: string;

  /**
  * The parent context (used for template compilation)
  */
  $parent: any;

  /**
  * The widgets parent viewmodel (this is the object instance the user will bind to)
  */
  viewModel: any;

  /**
  * A list of bindings for properties such as kEnable/kReadOnly/kValue
  * whenever one of these properties changes an API function will be called on the Kendo control
  * for example, a change of an 'kEnable' property will result in a .enable(true/false) call
  */
  bindingsToKendo: Array = [];

  constructor(taskQueue, templateCompiler, optionsBuilder, util, templateGatherer, configBuilder) {
    this.taskQueue = taskQueue;
    this.optionsBuilder = optionsBuilder;
    this.util = util;
    this.configBuilder = configBuilder;
    this.templateGatherer = templateGatherer;
    templateCompiler.initialize();
  }

  control(controlName) {
    if (!controlName || !kendo.jQuery.fn[controlName]) {
      throw new Error(`The name of control ${controlName} is invalid or not set`);
    }

    this.controlName = controlName;

    let ctor = kendo.jQuery.fn[this.controlName];
    this.kendoOptions = ctor.widget.prototype.options;
    this.kendoEvents = ctor.widget.prototype.events;

    return this;
  }

  linkViewModel(viewModel) {
    if (!viewModel) {
      throw new Error('viewModel is not set');
    }

    this.viewModel = viewModel;

    return this;
  }

  useViewResources(resources) {
    if (!resources) {
      throw new Error('resources is not set');
    }

    this.viewResources = resources;

    return this;
  }

  /**
  * adds two-way value binding
  * @param valueBindingProperty the property name of the kendo control (value/checked)
  * @param valueFunction the function on the kendo control that gets the value of the above property
  */
  useValueBinding(valueBindingProperty = 'kValue', valueFunction = 'value') {
    this.valueBindingProperty = valueBindingProperty;
    this.valueFunction = valueFunction;
    this.withValueBinding = true;

    this.bindToKendo(valueBindingProperty, valueFunction);

    return this;
  }

  /**
  * add binding to Kendo
  * whenever a bindable property in a wrapper changes, and the propertyname is in this list
  * then it will call an API function on the Kendo control to update the value
  * @param propertyName the bindable property name in the wrapper (kValue, kChecked, kEnabled)
  * @param valueFunction the API function on the kendo control that sets the value of the above property
  */
  bindToKendo(propertyName, functionName) {
    this.bindingsToKendo.push({
      propertyName: propertyName,
      functionName: functionName
    });

    return this;
  }

  /**
  * collects all options objects
  * calls all hooks
  * then initialized the Kendo control as "widget"
  */
  createWidget(options) {
    if (!options) {
      throw new Error('the createWidget() function needs to be called with an object');
    }

    if (!options.element) {
      throw new Error('element is not set');
    }

    // destroy old widgets
    if (this.viewModel && this.viewModel.kWidget) {
      this.destroy(this.viewModel.kWidget);
    }

    // generate all options, including event handlers - use the rootElement if specified, otherwise fall back to the element
    // this allows a child element in a custom elements tempate to be the container for the kendo control
    // but allows the plugin to correctly discover attributes on the root element to match against events
    let allOptions = this._getOptions(options.rootElement || options.element);

    // before initialization callback
    // allows you to modify/add/remove options before the control gets initialized
    if (options.beforeInitialize) {
      options.beforeInitialize(allOptions);
    }

    // add parent context to options
    // deepExtend in kendo.core will fail with stack
    // overflow if we don't put it in an array :-\
    Object.assign(allOptions, {
      _$parent: [options.parentCtx],
      _$resources: [this.viewResources]
    });

    if (this.configBuilder.debugMode) {
      logger.debug(`initializing ${this.controlName} with the following config`, allOptions);
    }

    // instantiate the Kendo control
    let widget = this._createWidget(options.element, allOptions, this.controlName);

    widget._$parent = options.parentCtx;
    widget._$resources = this.viewResources;

    if (this.withValueBinding) {
      widget.first('change', (args) => this._handleValueChange(args.sender));
      widget.first('dataBound', (args) => this._handleValueChange(args.sender));
    }

    // use Kendo API functions to update all initial values in the wrapper
    // kValue -> .value()
    // kEnabled -> .enable()
    this.bindingsToKendo.forEach(binding => {
      let value = this.viewModel[binding.propertyName];

      // don't do this when the value of the widget is an empty string
      // as it indicates that the widget has not been databound yet
      if (typeof(value) !== 'undefined' && value !== null && value !== '') {
        widget[binding.functionName](value);
      }
    });

    if (options.afterInitialize) {
      options.afterInitialize();
    }

    return widget;
  }

  _createWidget(element, options, controlName) {
    return kendo.jQuery(element)[controlName](options).data(controlName);
  }

  /**
  * combines all options objects and properties into a single options object
  */
  _getOptions(element) {
    let options = this.optionsBuilder.getOptions(this.viewModel, this.controlName);
    let eventOptions = this.getEventOptions(element);

    // merge all option objects together
    // - options on the wrapper
    // - options compiled from all the bindable properties
    // - event handler options
    return this.util.pruneOptions(Object.assign({}, this.viewModel.kOptions || {}, options, eventOptions));
  }

  /**
  * convert attributes into a list of events a user wants to subscribe to.
  * These events are then subscribed to, which when called
  * calls the fireKendoEvent function to raise a DOM event
  */
  getEventOptions(element) {
    let options = {};
    let allowedEvents = this.kendoEvents;
    let delayedExecution = ['change'];

    // iterate all attributes on the custom elements
    // and only return the normalized kendo event's (dataBound etc)
    let events = this.util.getEventsFromAttributes(element);

    events.forEach(event => {
      // throw error if this event is not defined on the Kendo control
      if (!allowedEvents.includes(event)) {
        throw new Error(`${event} is not an event on the ${this.controlName} control`);
      }

      if (delayedExecution.includes(event)) {
        options[event] = e => {
          this.taskQueue.queueMicroTask(() => this.util.fireKendoEvent(element, this.util._hyphenate(event), e));
        };
      } else {
        options[event] = e => this.util.fireKendoEvent(element, this.util._hyphenate(event), e);
      }
    });

    return options;
  }

  _handleValueChange(widget) {
    this.viewModel[this.valueBindingProperty] = this.getValue(widget);
  }

  getValue(widget) {
    return widget[this.valueFunction]();
  }

  handlePropertyChanged(widget, property, newValue, oldValue) {
    let binding = this.bindingsToKendo.find(i => i.propertyName === property);
    if (binding) {
      widget[binding.functionName](newValue);
    }
  }

  useTemplates(target, controlName, templates) {
    return this.templateGatherer.useTemplates(target, controlName, templates);
  }

  /**
  * destroys the widget
  */
  destroy(widget) {
    if (widget) {
      kendo.destroy(widget.element);
      widget = null;

      if (this.viewModel.kWidget) {
        this.viewModel.kWidget = null;
      }
    }
  }
}
<|endoftext|>


<commit_before>/**
 * echarts组件：提示框
 *
 * @desc echarts基于Canvas，纯Javascript图表库，提供直观，生动，可交互，可个性化定制的数据统计图表。
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 *
 */
define(function (require) {
    var Base = require('./base');

    var ecConfig = require('../config');
    var zrUtil = require('zrender/tool/util');

    /**
     * 构造函数
     * @param {Object} messageCenter echart消息中心
     * @param {ZRender} zr zrender实例
     * @param {Object} option 提示框参数
     * @param {HtmlElement} dom 目标对象
     */
    function DataView(ecTheme, messageCenter, zr, option, myChart) {
        Base.call(this, ecTheme, messageCenter, zr, option, myChart);

        this.dom = myChart.dom;

        // dataview dom & css
        this._tDom = document.createElement('div');
        this._textArea = document.createElement('textArea');
        this._buttonRefresh = document.createElement('button');
        this._buttonClose = document.createElement('button');
        this._hasShow = false;

        // 缓存一些高宽数据
        this._zrHeight = zr.getHeight();
        this._zrWidth = zr.getWidth();

        this._tDom.className = 'echarts-dataview',
        this.hide();
        this.dom.firstChild.appendChild(this._tDom);

        if (window.addEventListener) {
            this._tDom.addEventListener('click', this._stop);
            this._tDom.addEventListener('mousewheel', this._stop);
            this._tDom.addEventListener('mousemove', this._stop);
            this._tDom.addEventListener('mousedown', this._stop);
            this._tDom.addEventListener('mouseup', this._stop);

            // mobile支持
            this._tDom.addEventListener('touchstart', this._stop);
            this._tDom.addEventListener('touchmove', this._stop);
            this._tDom.addEventListener('touchend', this._stop);
        }
        else {
            this._tDom.attachEvent('onclick', this._stop);
            this._tDom.attachEvent('onmousewheel', this._stop);
            this._tDom.attachEvent('onmousemove', this._stop);
            this._tDom.attachEvent('onmousedown', this._stop);
            this._tDom.attachEvent('onmouseup', this._stop);
        }
    }

    DataView.prototype = {
        type : ecConfig.COMPONENT_TYPE_DATAVIEW,
        _lang : ['Data View', 'close', 'refresh'],
        // 通用样式
        _gCssText : 'position:absolute;'
                    + 'display:block;'
                    + 'overflow:hidden;'
                    + 'transition:height 0.8s,background-color 1s;'
                    + '-moz-transition:height 0.8s,background-color 1s;'
                    + '-webkit-transition:height 0.8s,background-color 1s;'
                    + '-o-transition:height 0.8s,background-color 1s;'
                    + 'z-index:1;'
                    + 'left:0;'
                    + 'top:0;',
        hide : function () {
            this._sizeCssText = 'width:' + this._zrWidth + 'px;'
                           + 'height:' + 0 + 'px;'
                           + 'background-color:#f0ffff;';
            this._tDom.style.cssText = this._gCssText + this._sizeCssText;
            // 这是个很恶心的事情
            /*
            this.dom.onselectstart = function () {
                return false;
            };
            */
        },

        show : function (newOption) {
            this._hasShow = true;
            var lang = this.query(this.option, 'toolbox.feature.dataView.lang')
                       || this._lang;

            this.option = newOption;

            this._tDom.innerHTML = '
'
                              + (lang[0] || this._lang[0])
                              + '
';

            this._textArea.style.cssText =
                'display:block;margin:0 0 8px 0;padding:4px 6px;overflow:auto;'
                + 'width:' + (this._zrWidth - 15) + 'px;'
                + 'height:' + (this._zrHeight - 100) + 'px;';
            var customContent = this.query(
                this.option, 'toolbox.feature.dataView.optionToContent'
            );
            if (typeof customContent != 'function') {
                this._textArea.value = this._optionToContent();
            }
            else {
                this._textArea.value = customContent(this.option);
            }
            this._tDom.appendChild(this._textArea);

            this._buttonClose.style.cssText = 'float:right;padding:1px 6px;';
            this._buttonClose.innerHTML = lang[1] || this._lang[1];
            var self = this;
            this._buttonClose.onclick = function (){
                self.hide();
            };
            this._tDom.appendChild(this._buttonClose);

            if (this.query(this.option, 'toolbox.feature.dataView.readOnly')
                === false
            ) {
                this._buttonRefresh.style.cssText =
                    'float:right;margin-right:10px;padding:1px 6px;';
                this._buttonRefresh.innerHTML = lang[2] || this._lang[2];
                this._buttonRefresh.onclick = function (){
                    self._save();
                };
                this._tDom.appendChild(this._buttonRefresh);
                this._textArea.readOnly = false;
                this._textArea.style.cursor = 'default';
            }
            else {
                this._textArea.readOnly = true;
                this._textArea.style.cursor = 'text';
            }

            this._sizeCssText = 'width:' + this._zrWidth + 'px;'
                           + 'height:' + this._zrHeight + 'px;'
                           + 'background-color:#fff;';
            this._tDom.style.cssText = this._gCssText + this._sizeCssText;
            // 这是个很恶心的事情
            /*
            this.dom.onselectstart = function () {
                return true;
            };
            */
        },

        _optionToContent : function () {
            var i;
            var j;
            var k;
            var len;
            var data;
            var valueList;
            var axisList = [];
            var content = '';
            if (this.option.xAxis) {
                if (this.option.xAxis instanceof Array) {
                    axisList = this.option.xAxis;
                } else {
                    axisList = [this.option.xAxis];
                }
                for (i = 0, len = axisList.length; i < len; i++) {
                    // 横纵默认为类目
                    if ((axisList[i].type || 'category') == 'category') {
                        valueList = [];
                        for (j = 0, k = axisList[i].data.length; j < k; j++) {
                            valueList.push(this.getDataFromOption(axisList[i].data[j]));
                        }
                        content += valueList.join(', ') + '\n\n';
                    }
                }
            }

            if (this.option.yAxis) {
                if (this.option.yAxis instanceof Array) {
                    axisList = this.option.yAxis;
                } else {
                    axisList = [this.option.yAxis];
                }
                for (i = 0, len = axisList.length; i < len; i++) {
                    if (axisList[i].type  == 'category') {
                        valueList = [];
                        for (j = 0, k = axisList[i].data.length; j < k; j++) {
                            valueList.push(this.getDataFromOption(axisList[i].data[j]));
                        }
                        content += valueList.join(', ') + '\n\n';
                    }
                }
            }

            var series = this.option.series;
            var itemName;
            for (i = 0, len = series.length; i < len; i++) {
                valueList = [];
                for (j = 0, k = series[i].data.length; j < k; j++) {
                    data = series[i].data[j];
                    if (series[i].type == ecConfig.CHART_TYPE_PIE
                        || series[i].type == ecConfig.CHART_TYPE_MAP
                    ) {
                        itemName = (data.name || '-') + ':';
                    }
                    else {
                        itemName = '';
                    }

                    if (series[i].type == ecConfig.CHART_TYPE_SCATTER) {
                        data = this.getDataFromOption(data).join(', ');
                    }
                    valueList.push(itemName + this.getDataFromOption(data));
                }
                content += (series[i].name || '-') + ' : \n';
                content += valueList.join(
                    series[i].type == ecConfig.CHART_TYPE_SCATTER ? '\n': ', '
                );
                content += '\n\n';
            }

            return content;
        },

        _save : function () {
            var text = this._textArea.value;
            var customContent = this.query(
                this.option, 'toolbox.feature.dataView.contentToOption'
            );
            if (typeof customContent != 'function') {
                text = text.split('\n');
                var content = [];
                for (var i = 0, l = text.length; i < l; i++) {
                    text[i] = this._trim(text[i]);
                    if (text[i] !== '') {
                        content.push(text[i]);
                    }
                }
                this._contentToOption(content);
            }
            else {
                customContent(text, this.option);
            }

            this.hide();

            var self = this;
            setTimeout(
                function (){
                    self.messageCenter && self.messageCenter.dispatch(
                        ecConfig.EVENT.DATA_VIEW_CHANGED,
                        null,
                        {option : self.option},
                        self.myChart
                    );
                },
                // 有动画，所以高级浏览器时间更长点
                self.canvasSupported ? 800 : 100
            );
        },

        _contentToOption : function (content) {
            var i;
            var j;
            var k;
            var len;
            var data;
            var axisList = [];

            var contentIdx = 0;
            var contentValueList;
            var value;

            if (this.option.xAxis) {
                if (this.option.xAxis instanceof Array) {
                    axisList = this.option.xAxis;
                } else {
                    axisList = [this.option.xAxis];
                }
                for (i = 0, len = axisList.length; i < len; i++) {
                    // 横纵默认为类目
                    if ((axisList[i].type || 'category') == 'category'
                    ) {
                        contentValueList = content[contentIdx].split(',');
                        for (j = 0, k = axisList[i].data.length; j < k; j++) {
                            value = this._trim(contentValueList[j] || '');
                            data = axisList[i].data[j];
                            if (typeof axisList[i].data[j].value != 'undefined'
                            ) {
                                axisList[i].data[j].value = value;
                            }
                            else {
                                axisList[i].data[j] = value;
                            }
                        }
                        contentIdx++;
                    }
                }
            }

            if (this.option.yAxis) {
                if (this.option.yAxis instanceof Array) {
                    axisList = this.option.yAxis;
                } else {
                    axisList = [this.option.yAxis];
                }
                for (i = 0, len = axisList.length; i < len; i++) {
                    if (axisList[i].type  == 'category') {
                        contentValueList = content[contentIdx].split(',');
                        for (j = 0, k = axisList[i].data.length; j < k; j++) {
                            value = this._trim(contentValueList[j] || '');
                            data = axisList[i].data[j];
                            if (typeof axisList[i].data[j].value != 'undefined'
                            ) {
                                axisList[i].data[j].value = value;
                            }
                            else {
                                axisList[i].data[j] = value;
                            }
                        }
                        contentIdx++;
                    }
                }
            }

            var series = this.option.series;
            for (i = 0, len = series.length; i < len; i++) {
                contentIdx++;
                if (series[i].type == ecConfig.CHART_TYPE_SCATTER) {
                    for (var j = 0, k = series[i].data.length; j < k; j++) {
                        contentValueList = content[contentIdx];
                        value = contentValueList.replace(' ','').split(',');
                        if (typeof series[i].data[j].value != 'undefined'
                        ) {
                            series[i].data[j].value = value;
                        }
                        else {
                            series[i].data[j] = value;
                        }
                        contentIdx++;
                    }
                }
                else {
                    contentValueList = content[contentIdx].split(',');
                    for (var j = 0, k = series[i].data.length; j < k; j++) {
                        value = (contentValueList[j] || '').replace(/.*:/,'');
                        value = this._trim(value);
                        value = (value != '-' && value !== '')
                                ? (value - 0)
                                : '-';
                        if (typeof series[i].data[j].value != 'undefined'
                        ) {
                            series[i].data[j].value = value;
                        }
                        else {
                            series[i].data[j] = value;
                        }
                    }
                    contentIdx++;
                }
            }
        },

        _trim : function (str){
            var trimer = new RegExp(
                '(^[\\s\\t\\xa0\\u3000]+)|([\\u3000\\xa0\\s\\t]+\x24)', 'g'
            );
            return str.replace(trimer, '');
        },

        // 阻塞zrender事件
        _stop : function (e){
            e = e || window.event;
            if (e.stopPropagation) {
                e.stopPropagation();
            }
            else {
                e.cancelBubble = true;
            }
        },

        /**
         * zrender事件响应：窗口大小改变
         */
        resize : function () {
            this._zrHeight = this.zr.getHeight();
            this._zrWidth = this.zr.getWidth();
            if (this._tDom.offsetHeight > 10) {
                this._sizeCssText = 'width:' + this._zrWidth + 'px;'
                               + 'height:' + this._zrHeight + 'px;'
                               + 'background-color:#fff;';
                this._tDom.style.cssText = this._gCssText + this._sizeCssText;
                this._textArea.style.cssText = 'display:block;margin:0 0 8px 0;'
                                        + 'padding:4px 6px;overflow:auto;'
                                        + 'width:' + (this._zrWidth - 15) + 'px;'
                                        + 'height:' + (this._zrHeight - 100) + 'px;';
            }
        },

        /**
         * 释放后实例不可用，重载基类方法
         */
        dispose : function () {
            if (window.removeEventListener) {
                this._tDom.removeEventListener('click', this._stop);
                this._tDom.removeEventListener('mousewheel', this._stop);
                this._tDom.removeEventListener('mousemove', this._stop);
                this._tDom.removeEventListener('mousedown', this._stop);
                this._tDom.removeEventListener('mouseup', this._stop);

                // mobile支持
                this._tDom.removeEventListener('touchstart', this._stop);
                this._tDom.removeEventListener('touchmove', this._stop);
                this._tDom.removeEventListener('touchend', this._stop);
            }
            else {
                this._tDom.detachEvent('onclick', this._stop);
                this._tDom.detachEvent('onmousewheel', this._stop);
                this._tDom.detachEvent('onmousemove', this._stop);
                this._tDom.detachEvent('onmousedown', this._stop);
                this._tDom.detachEvent('onmouseup', this._stop);
            }

            this._buttonRefresh.onclick = null;
            this._buttonClose.onclick = null;

            if (this._hasShow) {
                this._tDom.removeChild(this._textArea);
                this._tDom.removeChild(this._buttonRefresh);
                this._tDom.removeChild(this._buttonClose);
            }

            this._textArea = null;
            this._buttonRefresh = null;
            this._buttonClose = null;

            this.dom.firstChild.removeChild(this._tDom);
            this._tDom = null;
        }
    };

    zrUtil.inherits(DataView, Base);

    require('../component').define('dataView', DataView);

    return DataView;
});<commit_msg>jshint<commit_after>/**
 * echarts组件：提示框
 *
 * @desc echarts基于Canvas，纯Javascript图表库，提供直观，生动，可交互，可个性化定制的数据统计图表。
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 *
 */
define(function (require) {
    var Base = require('./base');

    var ecConfig = require('../config');
    var zrUtil = require('zrender/tool/util');

    /**
     * 构造函数
     * @param {Object} messageCenter echart消息中心
     * @param {ZRender} zr zrender实例
     * @param {Object} option 提示框参数
     * @param {HtmlElement} dom 目标对象
     */
    function DataView(ecTheme, messageCenter, zr, option, myChart) {
        Base.call(this, ecTheme, messageCenter, zr, option, myChart);

        this.dom = myChart.dom;

        // dataview dom & css
        this._tDom = document.createElement('div');
        this._textArea = document.createElement('textArea');
        this._buttonRefresh = document.createElement('button');
        this._buttonClose = document.createElement('button');
        this._hasShow = false;

        // 缓存一些高宽数据
        this._zrHeight = zr.getHeight();
        this._zrWidth = zr.getWidth();

        this._tDom.className = 'echarts-dataview';
        this.hide();
        this.dom.firstChild.appendChild(this._tDom);

        if (window.addEventListener) {
            this._tDom.addEventListener('click', this._stop);
            this._tDom.addEventListener('mousewheel', this._stop);
            this._tDom.addEventListener('mousemove', this._stop);
            this._tDom.addEventListener('mousedown', this._stop);
            this._tDom.addEventListener('mouseup', this._stop);

            // mobile支持
            this._tDom.addEventListener('touchstart', this._stop);
            this._tDom.addEventListener('touchmove', this._stop);
            this._tDom.addEventListener('touchend', this._stop);
        }
        else {
            this._tDom.attachEvent('onclick', this._stop);
            this._tDom.attachEvent('onmousewheel', this._stop);
            this._tDom.attachEvent('onmousemove', this._stop);
            this._tDom.attachEvent('onmousedown', this._stop);
            this._tDom.attachEvent('onmouseup', this._stop);
        }
    }

    DataView.prototype = {
        type : ecConfig.COMPONENT_TYPE_DATAVIEW,
        _lang : ['Data View', 'close', 'refresh'],
        // 通用样式
        _gCssText : 'position:absolute;'
                    + 'display:block;'
                    + 'overflow:hidden;'
                    + 'transition:height 0.8s,background-color 1s;'
                    + '-moz-transition:height 0.8s,background-color 1s;'
                    + '-webkit-transition:height 0.8s,background-color 1s;'
                    + '-o-transition:height 0.8s,background-color 1s;'
                    + 'z-index:1;'
                    + 'left:0;'
                    + 'top:0;',
        hide : function () {
            this._sizeCssText = 'width:' + this._zrWidth + 'px;'
                           + 'height:' + 0 + 'px;'
                           + 'background-color:#f0ffff;';
            this._tDom.style.cssText = this._gCssText + this._sizeCssText;
            // 这是个很恶心的事情
            /*
            this.dom.onselectstart = function () {
                return false;
            };
            */
        },

        show : function (newOption) {
            this._hasShow = true;
            var lang = this.query(this.option, 'toolbox.feature.dataView.lang')
                       || this._lang;

            this.option = newOption;

            this._tDom.innerHTML = '
'
                              + (lang[0] || this._lang[0])
                              + '
';

            this._textArea.style.cssText =
                'display:block;margin:0 0 8px 0;padding:4px 6px;overflow:auto;'
                + 'width:' + (this._zrWidth - 15) + 'px;'
                + 'height:' + (this._zrHeight - 100) + 'px;';
            var customContent = this.query(
                this.option, 'toolbox.feature.dataView.optionToContent'
            );
            if (typeof customContent != 'function') {
                this._textArea.value = this._optionToContent();
            }
            else {
                this._textArea.value = customContent(this.option);
            }
            this._tDom.appendChild(this._textArea);

            this._buttonClose.style.cssText = 'float:right;padding:1px 6px;';
            this._buttonClose.innerHTML = lang[1] || this._lang[1];
            var self = this;
            this._buttonClose.onclick = function (){
                self.hide();
            };
            this._tDom.appendChild(this._buttonClose);

            if (this.query(this.option, 'toolbox.feature.dataView.readOnly')
                === false
            ) {
                this._buttonRefresh.style.cssText =
                    'float:right;margin-right:10px;padding:1px 6px;';
                this._buttonRefresh.innerHTML = lang[2] || this._lang[2];
                this._buttonRefresh.onclick = function (){
                    self._save();
                };
                this._tDom.appendChild(this._buttonRefresh);
                this._textArea.readOnly = false;
                this._textArea.style.cursor = 'default';
            }
            else {
                this._textArea.readOnly = true;
                this._textArea.style.cursor = 'text';
            }

            this._sizeCssText = 'width:' + this._zrWidth + 'px;'
                           + 'height:' + this._zrHeight + 'px;'
                           + 'background-color:#fff;';
            this._tDom.style.cssText = this._gCssText + this._sizeCssText;
            // 这是个很恶心的事情
            /*
            this.dom.onselectstart = function () {
                return true;
            };
            */
        },

        _optionToContent : function () {
            var i;
            var j;
            var k;
            var len;
            var data;
            var valueList;
            var axisList = [];
            var content = '';
            if (this.option.xAxis) {
                if (this.option.xAxis instanceof Array) {
                    axisList = this.option.xAxis;
                } else {
                    axisList = [this.option.xAxis];
                }
                for (i = 0, len = axisList.length; i < len; i++) {
                    // 横纵默认为类目
                    if ((axisList[i].type || 'category') == 'category') {
                        valueList = [];
                        for (j = 0, k = axisList[i].data.length; j < k; j++) {
                            valueList.push(this.getDataFromOption(axisList[i].data[j]));
                        }
                        content += valueList.join(', ') + '\n\n';
                    }
                }
            }

            if (this.option.yAxis) {
                if (this.option.yAxis instanceof Array) {
                    axisList = this.option.yAxis;
                } else {
                    axisList = [this.option.yAxis];
                }
                for (i = 0, len = axisList.length; i < len; i++) {
                    if (axisList[i].type  == 'category') {
                        valueList = [];
                        for (j = 0, k = axisList[i].data.length; j < k; j++) {
                            valueList.push(this.getDataFromOption(axisList[i].data[j]));
                        }
                        content += valueList.join(', ') + '\n\n';
                    }
                }
            }

            var series = this.option.series;
            var itemName;
            for (i = 0, len = series.length; i < len; i++) {
                valueList = [];
                for (j = 0, k = series[i].data.length; j < k; j++) {
                    data = series[i].data[j];
                    if (series[i].type == ecConfig.CHART_TYPE_PIE
                        || series[i].type == ecConfig.CHART_TYPE_MAP
                    ) {
                        itemName = (data.name || '-') + ':';
                    }
                    else {
                        itemName = '';
                    }

                    if (series[i].type == ecConfig.CHART_TYPE_SCATTER) {
                        data = this.getDataFromOption(data).join(', ');
                    }
                    valueList.push(itemName + this.getDataFromOption(data));
                }
                content += (series[i].name || '-') + ' : \n';
                content += valueList.join(
                    series[i].type == ecConfig.CHART_TYPE_SCATTER ? '\n': ', '
                );
                content += '\n\n';
            }

            return content;
        },

        _save : function () {
            var text = this._textArea.value;
            var customContent = this.query(
                this.option, 'toolbox.feature.dataView.contentToOption'
            );
            if (typeof customContent != 'function') {
                text = text.split('\n');
                var content = [];
                for (var i = 0, l = text.length; i < l; i++) {
                    text[i] = this._trim(text[i]);
                    if (text[i] !== '') {
                        content.push(text[i]);
                    }
                }
                this._contentToOption(content);
            }
            else {
                customContent(text, this.option);
            }

            this.hide();

            var self = this;
            setTimeout(
                function (){
                    self.messageCenter && self.messageCenter.dispatch(
                        ecConfig.EVENT.DATA_VIEW_CHANGED,
                        null,
                        {option : self.option},
                        self.myChart
                    );
                },
                // 有动画，所以高级浏览器时间更长点
                self.canvasSupported ? 800 : 100
            );
        },

        _contentToOption : function (content) {
            var i;
            var j;
            var k;
            var len;
            var data;
            var axisList = [];

            var contentIdx = 0;
            var contentValueList;
            var value;

            if (this.option.xAxis) {
                if (this.option.xAxis instanceof Array) {
                    axisList = this.option.xAxis;
                } else {
                    axisList = [this.option.xAxis];
                }
                for (i = 0, len = axisList.length; i < len; i++) {
                    // 横纵默认为类目
                    if ((axisList[i].type || 'category') == 'category'
                    ) {
                        contentValueList = content[contentIdx].split(',');
                        for (j = 0, k = axisList[i].data.length; j < k; j++) {
                            value = this._trim(contentValueList[j] || '');
                            data = axisList[i].data[j];
                            if (typeof axisList[i].data[j].value != 'undefined'
                            ) {
                                axisList[i].data[j].value = value;
                            }
                            else {
                                axisList[i].data[j] = value;
                            }
                        }
                        contentIdx++;
                    }
                }
            }

            if (this.option.yAxis) {
                if (this.option.yAxis instanceof Array) {
                    axisList = this.option.yAxis;
                } else {
                    axisList = [this.option.yAxis];
                }
                for (i = 0, len = axisList.length; i < len; i++) {
                    if (axisList[i].type  == 'category') {
                        contentValueList = content[contentIdx].split(',');
                        for (j = 0, k = axisList[i].data.length; j < k; j++) {
                            value = this._trim(contentValueList[j] || '');
                            data = axisList[i].data[j];
                            if (typeof axisList[i].data[j].value != 'undefined'
                            ) {
                                axisList[i].data[j].value = value;
                            }
                            else {
                                axisList[i].data[j] = value;
                            }
                        }
                        contentIdx++;
                    }
                }
            }

            var series = this.option.series;
            for (i = 0, len = series.length; i < len; i++) {
                contentIdx++;
                if (series[i].type == ecConfig.CHART_TYPE_SCATTER) {
                    for (var j = 0, k = series[i].data.length; j < k; j++) {
                        contentValueList = content[contentIdx];
                        value = contentValueList.replace(' ','').split(',');
                        if (typeof series[i].data[j].value != 'undefined'
                        ) {
                            series[i].data[j].value = value;
                        }
                        else {
                            series[i].data[j] = value;
                        }
                        contentIdx++;
                    }
                }
                else {
                    contentValueList = content[contentIdx].split(',');
                    for (var j = 0, k = series[i].data.length; j < k; j++) {
                        value = (contentValueList[j] || '').replace(/.*:/,'');
                        value = this._trim(value);
                        value = (value != '-' && value !== '')
                                ? (value - 0)
                                : '-';
                        if (typeof series[i].data[j].value != 'undefined'
                        ) {
                            series[i].data[j].value = value;
                        }
                        else {
                            series[i].data[j] = value;
                        }
                    }
                    contentIdx++;
                }
            }
        },

        _trim : function (str){
            var trimer = new RegExp(
                '(^[\\s\\t\\xa0\\u3000]+)|([\\u3000\\xa0\\s\\t]+\x24)', 'g'
            );
            return str.replace(trimer, '');
        },

        // 阻塞zrender事件
        _stop : function (e){
            e = e || window.event;
            if (e.stopPropagation) {
                e.stopPropagation();
            }
            else {
                e.cancelBubble = true;
            }
        },

        /**
         * zrender事件响应：窗口大小改变
         */
        resize : function () {
            this._zrHeight = this.zr.getHeight();
            this._zrWidth = this.zr.getWidth();
            if (this._tDom.offsetHeight > 10) {
                this._sizeCssText = 'width:' + this._zrWidth + 'px;'
                               + 'height:' + this._zrHeight + 'px;'
                               + 'background-color:#fff;';
                this._tDom.style.cssText = this._gCssText + this._sizeCssText;
                this._textArea.style.cssText = 'display:block;margin:0 0 8px 0;'
                                        + 'padding:4px 6px;overflow:auto;'
                                        + 'width:' + (this._zrWidth - 15) + 'px;'
                                        + 'height:' + (this._zrHeight - 100) + 'px;';
            }
        },

        /**
         * 释放后实例不可用，重载基类方法
         */
        dispose : function () {
            if (window.removeEventListener) {
                this._tDom.removeEventListener('click', this._stop);
                this._tDom.removeEventListener('mousewheel', this._stop);
                this._tDom.removeEventListener('mousemove', this._stop);
                this._tDom.removeEventListener('mousedown', this._stop);
                this._tDom.removeEventListener('mouseup', this._stop);

                // mobile支持
                this._tDom.removeEventListener('touchstart', this._stop);
                this._tDom.removeEventListener('touchmove', this._stop);
                this._tDom.removeEventListener('touchend', this._stop);
            }
            else {
                this._tDom.detachEvent('onclick', this._stop);
                this._tDom.detachEvent('onmousewheel', this._stop);
                this._tDom.detachEvent('onmousemove', this._stop);
                this._tDom.detachEvent('onmousedown', this._stop);
                this._tDom.detachEvent('onmouseup', this._stop);
            }

            this._buttonRefresh.onclick = null;
            this._buttonClose.onclick = null;

            if (this._hasShow) {
                this._tDom.removeChild(this._textArea);
                this._tDom.removeChild(this._buttonRefresh);
                this._tDom.removeChild(this._buttonClose);
            }

            this._textArea = null;
            this._buttonRefresh = null;
            this._buttonClose = null;

            this.dom.firstChild.removeChild(this._tDom);
            this._tDom = null;
        }
    };

    zrUtil.inherits(DataView, Base);

    require('../component').define('dataView', DataView);

    return DataView;
});<|endoftext|>


<commit_before>// @flow
import React from 'react'
import { Helmet } from 'react-helmet'
import injectSheet from 'react-jss'
import type Classes from 'react-jss'

import styles from './styles'
import ButtonLink from '../Button/ButtonLink'
import Notice from '../Notice/Notice'
import GenForm from './GenForm/GenForm'
import GenResults from './GenResults/GenResults'
import genService from './GenService'

import { canonical, siteTitle } from '../../App'

declare type Props = {
  classes: Classes
}

class Gen extends React.Component {
  constructor (props: Props) {
    super(props)
    this.onChangeSelect = this.onChangeSelect.bind(this)
    this.onChangeSubpattern = this.onChangeSubpattern.bind(this)
    this.onClear = this.onClear.bind(this)
    this.onAdd = this.onAdd.bind(this)
    this.onChangePattern = this.onChangePattern.bind(this)
    this.onWordNumChange = this.onWordNumChange.bind(this)
    this.onChangeNewline = this.onChangeNewline.bind(this)
    this.onChangeDupes = this.onChangeDupes.bind(this)
    this.onGenerate = this.onGenerate.bind(this)
    this.onSave = this.onSave.bind(this)
    this.onOpen = this.onOpen.bind(this)
    this.classes = props.classes
    this.state = {
      data: genService.getData(),
      status: ['ok'],
      results: '',
      stats: {
        words: 0,
        maxWords: 0,
        filtered: 0
      }
    }
  }

  // When a Subpattern variable is changed, store that change in state
  onChangeSelect (e: SyntheticEvent<HTMLButtonElement>): void {
    const id = e.target.id.slice(1)
    const val = e.target.value
    this.setState(prevState => ({
      data: genService.changeSelect(id, val, this.state.data)
    }))
  }

  // When a Subpattern is changed, store that change in state
  onChangeSubpattern (e: SyntheticEvent<HTMLButtonElement>): void {
    const id = e.target.id.slice(1)
    const val = e.target.value
    this.setState(prevState => ({
      data: genService.changeSubpattern(id, val, this.state.data)
    }))
  }

  // When a Subpattern is cleared, delete the corresponding Subpattern from state
  onClear (e: SyntheticEvent<HTMLButtonElement>): void {
    e.preventDefault()
    const id = e.target.id.slice(1)
    this.setState(prevState => ({
      data: genService.clear(id, this.state.data)
    }))
  }

  // When the add button is clicked, add a blank Subpattern to state
  onAdd (e: SyntheticEvent<HTMLButtonElement>): void {
    e.preventDefault()
    this.setState(prevState => ({
      data: genService.add(this.state.data)
    }))
  }

  // When the pattern is changed, store the change in state
  onChangePattern (e: SyntheticEvent<HTMLButtonElement>): void {
    const val = e.target.value
    this.setState(prevState => ({
      data: genService.changePattern(val, this.state.data)
    }))
  }

  // When the number of desired words is changed, store that change in state
  onWordNumChange (e: SyntheticEvent<HTMLButtonElement>): void {
    let val = e.target.value
    // Only update state if there's a change
    if (genService.wordNumChange(val, this.state.data)) {
      this.setState(prevState => ({
        data: genService.wordNumChange(val, this.state.data)
      }))
    }
  }

  // If the selection for new lines is changed, store that change in state
  onChangeNewline (e: SyntheticEvent<HTMLButtonElement>): void {
    const checked = e.target.checked
    this.setState(prevState => ({
      data: genService.changeNewline(checked, this.state.data)
    }))
  }

  // If the selection for filtering duplicates is changed, store that change in state
  onChangeDupes (e: SyntheticEvent<HTMLButtonElement>): void {
    const checked = e.target.checked
    this.setState(prevState => ({
      data: genService.changeDupes(checked, this.state.data)
    }))
  }

  // Generate the output
  onGenerate (e: SyntheticEvent<HTMLButtonElement>): void {
    e.preventDefault()
    const response = genService.generate(this.state.data)
    this.setState(prevState => ({
      status: response.status,
      results: response.results,
      stats: response.stats
    }))
    // Save the current state to storage
    genService.setStorage(this.state.data)
  }

  // Save the current state to storage and generate a file
  onSave (e: SyntheticEvent<HTMLButtonElement>): void {
    e.preventDefault()
    genService.save(this.state.data)
  }

  // Open a file and parse it to restore a saved state
  onOpen (e: SyntheticEvent<HTMLButtonElement>): void {
    e.preventDefault()
    genService.open(this.state.data)
  }

  render () {
    const classes = this.classes
    const state = this.state

    return (

        <Helmet>
          <title>{siteTitle} Gen</title>
          <meta name='description' content='LanguaGen is a tool for automatically building a set of words based on arbitrary rules of phonotactics.' />
          <link rel='canonical' href={`${canonical}gen/`} />
        </Helmet>
        <ButtonLink route='/gen/help'>
          Help
        </ButtonLink>

LanguaGen

        <Notice>This tool is still in development. Please be patient until it is complete.</Notice>
        <GenForm
          data={state.data}
          changeSelect={this.onChangeSelect}
          changeSubpattern={this.onChangeSubpattern}
          clear={this.onClear}
          add={this.onAdd}
          changePattern={this.onChangePattern}
          changeWordNum={this.onWordNumChange}
          changeNewline={this.onChangeNewline}
          changeDupes={this.onChangeDupes}
          generate={this.onGenerate}
          save={this.onSave}
          open={this.onOpen}
        />
        <GenResults
          newLine={state.data.newline}
          results={state.results}
          stats={state.stats}
        />

    )
  }
}

export default injectSheet(styles)(Gen)
<commit_msg>Pass status to Gen results<commit_after>// @flow
import React from 'react'
import { Helmet } from 'react-helmet'
import injectSheet from 'react-jss'
import type Classes from 'react-jss'

import styles from './styles'
import ButtonLink from '../Button/ButtonLink'
import Notice from '../Notice/Notice'
import GenForm from './GenForm/GenForm'
import GenResults from './GenResults/GenResults'
import genService from './GenService'

import { canonical, siteTitle } from '../../App'

declare type Props = {
  classes: Classes
}

class Gen extends React.Component {
  constructor (props: Props) {
    super(props)
    this.onChangeSelect = this.onChangeSelect.bind(this)
    this.onChangeSubpattern = this.onChangeSubpattern.bind(this)
    this.onClear = this.onClear.bind(this)
    this.onAdd = this.onAdd.bind(this)
    this.onChangePattern = this.onChangePattern.bind(this)
    this.onWordNumChange = this.onWordNumChange.bind(this)
    this.onChangeNewline = this.onChangeNewline.bind(this)
    this.onChangeDupes = this.onChangeDupes.bind(this)
    this.onGenerate = this.onGenerate.bind(this)
    this.onSave = this.onSave.bind(this)
    this.onOpen = this.onOpen.bind(this)
    this.classes = props.classes
    this.state = {
      data: genService.getData(),
      status: ['ok'],
      results: '',
      stats: {
        words: 0,
        maxWords: 0,
        filtered: 0
      }
    }
  }

  // When a Subpattern variable is changed, store that change in state
  onChangeSelect (e: SyntheticEvent<HTMLButtonElement>): void {
    const id = e.target.id.slice(1)
    const val = e.target.value
    this.setState(prevState => ({
      data: genService.changeSelect(id, val, this.state.data)
    }))
  }

  // When a Subpattern is changed, store that change in state
  onChangeSubpattern (e: SyntheticEvent<HTMLButtonElement>): void {
    const id = e.target.id.slice(1)
    const val = e.target.value
    this.setState(prevState => ({
      data: genService.changeSubpattern(id, val, this.state.data)
    }))
  }

  // When a Subpattern is cleared, delete the corresponding Subpattern from state
  onClear (e: SyntheticEvent<HTMLButtonElement>): void {
    e.preventDefault()
    const id = e.target.id.slice(1)
    this.setState(prevState => ({
      data: genService.clear(id, this.state.data)
    }))
  }

  // When the add button is clicked, add a blank Subpattern to state
  onAdd (e: SyntheticEvent<HTMLButtonElement>): void {
    e.preventDefault()
    this.setState(prevState => ({
      data: genService.add(this.state.data)
    }))
  }

  // When the pattern is changed, store the change in state
  onChangePattern (e: SyntheticEvent<HTMLButtonElement>): void {
    const val = e.target.value
    this.setState(prevState => ({
      data: genService.changePattern(val, this.state.data)
    }))
  }

  // When the number of desired words is changed, store that change in state
  onWordNumChange (e: SyntheticEvent<HTMLButtonElement>): void {
    let val = e.target.value
    // Only update state if there's a change
    if (genService.wordNumChange(val, this.state.data)) {
      this.setState(prevState => ({
        data: genService.wordNumChange(val, this.state.data)
      }))
    }
  }

  // If the selection for new lines is changed, store that change in state
  onChangeNewline (e: SyntheticEvent<HTMLButtonElement>): void {
    const checked = e.target.checked
    this.setState(prevState => ({
      data: genService.changeNewline(checked, this.state.data)
    }))
  }

  // If the selection for filtering duplicates is changed, store that change in state
  onChangeDupes (e: SyntheticEvent<HTMLButtonElement>): void {
    const checked = e.target.checked
    this.setState(prevState => ({
      data: genService.changeDupes(checked, this.state.data)
    }))
  }

  // Generate the output
  onGenerate (e: SyntheticEvent<HTMLButtonElement>): void {
    e.preventDefault()
    const response = genService.generate(this.state.data)
    this.setState(prevState => ({
      status: response.status,
      results: response.results,
      stats: response.stats
    }))
    // Save the current state to storage
    genService.setStorage(this.state.data)
  }

  // Save the current state to storage and generate a file
  onSave (e: SyntheticEvent<HTMLButtonElement>): void {
    e.preventDefault()
    genService.save(this.state.data)
  }

  // Open a file and parse it to restore a saved state
  onOpen (e: SyntheticEvent<HTMLButtonElement>): void {
    e.preventDefault()
    genService.open(this.state.data)
  }

  render () {
    const classes = this.classes
    const state = this.state

    return (

        <Helmet>
          <title>{siteTitle} Gen</title>
          <meta name='description' content='LanguaGen is a tool for automatically building a set of words based on arbitrary rules of phonotactics.' />
          <link rel='canonical' href={`${canonical}gen/`} />
        </Helmet>
        <ButtonLink route='/gen/help'>
          Help
        </ButtonLink>

LanguaGen

        <Notice>This tool is still in development. Please be patient until it is complete.</Notice>
        <GenForm
          data={state.data}
          changeSelect={this.onChangeSelect}
          changeSubpattern={this.onChangeSubpattern}
          clear={this.onClear}
          add={this.onAdd}
          changePattern={this.onChangePattern}
          changeWordNum={this.onWordNumChange}
          changeNewline={this.onChangeNewline}
          changeDupes={this.onChangeDupes}
          generate={this.onGenerate}
          save={this.onSave}
          open={this.onOpen}
        />
        <GenResults
          newLine={state.data.newline}
          status={state.status}
          results={state.results}
          stats={state.stats}
        />

    )
  }
}

export default injectSheet(styles)(Gen)
<|endoftext|>


<commit_before>'use strict';

import SwipeCards from 'react-native-swipe-cards';
import React, {
  StyleSheet,
  Component,
  View,
  Text,
  Image,
  ListView,
  ScrollView,
  TouchableHighlight,
  TouchableWithoutFeedback,
} from 'react-native';
import Immutable from 'immutable';
import _ from 'underscore';
import { connect } from 'react-redux';
import sendtoDB from '../actions/resultsActions';
import resultsListActions from '../actions/resultsListActions';
import favoritesActions from '../actions/favoritesActions';
import Card from './Card';
import NoMoreCards from './NoMoreCards';

@connect(state => ({
  query: state.search,
  cards: state.search.searchResults,
  login: state.login,
  signup: state.signup,
  favorites: state.favorites.favoritesList,
  CAR_MAKES_AND_MODELS: state.search.allCars
}))

export default class Results extends Component{
  // static propTypes = {
  //   dispatch: React.PropTypes.func.isRequired,
  //   query: React.PropTypes.object.isRequired,
  //   cards: React.PropTypes.array.isRequired,
  //   login: React.PropTypes.object.isRequired,
  //   signup: React.PropTypes.object.isRequired,
  //   favorites: React.PropTypes.array.isRequired,
  // };
  //
  constructor(props) {
    super(props);
  }

  outOfCards = false;

  handleYup(item) {
    console.log('in submitCarData', "item", item);
    console.log('email', this.props.login.email);
    userEmail = this.props.login.email;
    dispatch = this.props.dispatch;
    const newFavorite = {
        users_email: this.props.login.email,
        image: item.image[0].src,
        make: this.props.query.carMake,
        model: this.props.query.model,
        year: this.props.query.endYear,
        price: item.price[0].text
    };
    console.log('newFavorite', newFavorite);
    return fetch('http://localhost:3000/api/carly/favorites', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(newFavorite)
    }).then(res => {
      console.log("response from SF: ", res);
      console.log("email", this.props.login.email);
      console.log("global email", userEmail);
      return fetch('http://localhost:3000/api/carly/favorites/'+userEmail, {
        method: 'GET',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        }
      }).then(res => res.json())
      .then(data => {
        console.log("favorites", data.favorites);
        dispatch(favoritesActions({favoritesList: data.favorites}));
      })
    })
  }
  handleNope (card) {
    console.log("nope")
  }
  cardRemoved (index) {
    console.log(`The index is ${index}`);

    // let CARD_REFRESH_LIMIT = 3

    // if (this.props.cards.length - index <= CARD_REFRESH_LIMIT + 1) {
    //   console.log(`There are only ${this.props.cards.length - index - 1} cards left.`);

    //   if (!this.props.outOfCards) {
    //     console.log(`Adding ${Cards2.length} more cards`)

    //     this.setState({
    //       cards: this.props.cards.concat(Cards2),
    //       outOfCards: true
    //     })
    //   }

    // }

  }
  render() {
    return (
      <SwipeCards
        cards={this.props.cards}
        loop={false}

        renderCard={(cardData) => <Card {...cardData} />}
        renderNoMoreCards={() => <NoMoreCards />}
        showYup={true}
        showNope={true}

        handleYup={this.handleYup.bind(this)}
        handleNope={this.handleNope}
        cardRemoved={this.cardRemoved}/>
    )
  }
}

const styles = StyleSheet.create({
  card: {
    alignItems: 'center',
    borderRadius: 5,
    overflow: 'hidden',
    borderColor: 'grey',
    backgroundColor: 'white',
    borderWidth: 1,
    elevation: 1,
    width: 900,
    height: 1200,
    opacity: 0.5
  },
  thumbnail: {
    flex: 1,
    width: 300,
    height: 300
  },
  text: {
    fontSize: 20,
    paddingTop: 10,
    paddingBottom: 10
  },
  noMoreCards: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  }
});<commit_msg>remove opacity property<commit_after>'use strict';

import SwipeCards from 'react-native-swipe-cards';
import React, {
  StyleSheet,
  Component,
  View,
  Text,
  Image,
  ListView,
  ScrollView,
  TouchableHighlight,
  TouchableWithoutFeedback,
} from 'react-native';
import Immutable from 'immutable';
import _ from 'underscore';
import { connect } from 'react-redux';
import sendtoDB from '../actions/resultsActions';
import resultsListActions from '../actions/resultsListActions';
import favoritesActions from '../actions/favoritesActions';
import Card from './Card';
import NoMoreCards from './NoMoreCards';

@connect(state => ({
  query: state.search,
  cards: state.search.searchResults,
  login: state.login,
  signup: state.signup,
  favorites: state.favorites.favoritesList,
  CAR_MAKES_AND_MODELS: state.search.allCars
}))

export default class Results extends Component{
  // static propTypes = {
  //   dispatch: React.PropTypes.func.isRequired,
  //   query: React.PropTypes.object.isRequired,
  //   cards: React.PropTypes.array.isRequired,
  //   login: React.PropTypes.object.isRequired,
  //   signup: React.PropTypes.object.isRequired,
  //   favorites: React.PropTypes.array.isRequired,
  // };
  //
  constructor(props) {
    super(props);
  }

  outOfCards = false;

  handleYup(item) {
    console.log('in submitCarData', "item", item);
    console.log('email', this.props.login.email);
    userEmail = this.props.login.email;
    dispatch = this.props.dispatch;
    const newFavorite = {
        users_email: this.props.login.email,
        image: item.image[0].src,
        make: this.props.query.carMake,
        model: this.props.query.model,
        year: this.props.query.endYear,
        price: item.price[0].text
    };
    console.log('newFavorite', newFavorite);
    return fetch('http://localhost:3000/api/carly/favorites', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(newFavorite)
    }).then(res => {
      console.log("response from SF: ", res);
      console.log("email", this.props.login.email);
      console.log("global email", userEmail);
      return fetch('http://localhost:3000/api/carly/favorites/'+userEmail, {
        method: 'GET',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        }
      }).then(res => res.json())
      .then(data => {
        console.log("favorites", data.favorites);
        dispatch(favoritesActions({favoritesList: data.favorites}));
      })
    })
  }
  handleNope (card) {
    console.log("nope")
  }
  cardRemoved (index) {
    console.log(`The index is ${index}`);

    // let CARD_REFRESH_LIMIT = 3

    // if (this.props.cards.length - index <= CARD_REFRESH_LIMIT + 1) {
    //   console.log(`There are only ${this.props.cards.length - index - 1} cards left.`);

    //   if (!this.props.outOfCards) {
    //     console.log(`Adding ${Cards2.length} more cards`)

    //     this.setState({
    //       cards: this.props.cards.concat(Cards2),
    //       outOfCards: true
    //     })
    //   }

    // }

  }
  render() {
    return (
      <SwipeCards
        cards={this.props.cards}
        loop={false}

        renderCard={(cardData) => <Card {...cardData} />}
        renderNoMoreCards={() => <NoMoreCards />}
        showYup={true}
        showNope={true}

        handleYup={this.handleYup.bind(this)}
        handleNope={this.handleNope}
        cardRemoved={this.cardRemoved}/>
    )
  }
}

const styles = StyleSheet.create({
  card: {
    alignItems: 'center',
    borderRadius: 5,
    overflow: 'hidden',
    borderColor: 'grey',
    backgroundColor: 'white',
    borderWidth: 1,
    elevation: 1,
    width: 900,
    height: 1200,
  },
  thumbnail: {
    flex: 1,
    width: 300,
    height: 300
  },
  text: {
    fontSize: 20,
    paddingTop: 10,
    paddingBottom: 10
  },
  noMoreCards: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  }
});<|endoftext|>


<commit_before>/*!
 * Copyright 2012 Sakai Foundation (SF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://www.osedu.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

var _ = require('underscore');

var log = require('oae-logger').logger('oae-preview-processor');
var RestAPI = require('oae-rest');

var DefaultRetriever = require('./retrievers/default');
var PreviewConstants = require('./constants');
var PreviewImages = require('./api.images');

var _retrievers = [
    require('./retrievers/flickr'),
    require('./retrievers/slideshare'),
    require('./retrievers/vimeo'),
    require('./retrievers/youtube')
];

/**
 * Inits the link processor.
 * This method will check if the phantomjs binary can be executed.
 *
 * @param  {Object} config                                  The main config object.
 * @param  {Object} config.previews                         The namespace for all preview related configuration.
 * @param  {Object} config.previews.phantomjs               The namespace for all PhantomJS related configuration.
 * @param  {String} config.previews.phantomjs.binary        The path to the `phantomjs` executable. This should either be a direct path or the filename that's on the `PATH` environment.
 * @param  {Number} config.previews.phantomjs.renderDelay   Specifies the time (in ms) between page load and taking a screenshot. This is to allow for AJAX content to be loaded into the page. (Default: 5000ms)
 */
var init = module.exports.init = function(config, callback) {
    DefaultRetriever.init(config.previews.phantomjs, callback);
};

/**
 * Generates previews for a link.
 *
 * @param  {PreviewContext}     ctx                         The preview context associated to this piece of content.
 * @param  {Object}             ctx.content                 The content profile as returned from the REST API.
 * @param  {Object}             ctx.content.link            The actual link.
 * @param  {Object}             [ctx.content.displayName]   The display name for this link. If none is specified and we have special handlers for this type of link, it will be filled in.
 * @param  {Object}             [ctx.content.description]   The description for this link. If none is specified and we have special handlers for this type of link, it will be filled in.
 * @param  {Function}           callback                    Standard callback method.
 * @param  {Object}             callback.err                Standard error object (if any.)
 */
var generatePreviews = module.exports.generatePreviews = function(ctx, callback) {
    var url = ctx.content.link;

    // Select a retriever, the default one will open up the URL in webkit and take a screenshot.
    var retriever = DefaultRetriever;
    for (var i = 0; i < _retrievers.length; i++) {
        if (_retrievers[i].test(url)) {
            retriever = _retrievers[i];
            break;
        }
    }

    // Get an image that we can use to generate activity images from
    retriever.getImage(ctx, url, function(err, path, opts) {
        if (err) {
            return callback(err);
        }

        // Crop the screenshot.
        var sizes = [
            { 'width': PreviewConstants.SIZES.IMAGE.MEDIUM, 'height': PreviewConstants.SIZES.IMAGE.MEDIUM, 'size': 'medium' },
            { 'width': PreviewConstants.SIZES.IMAGE.SMALL, 'height': PreviewConstants.SIZES.IMAGE.SMALL, 'size': 'small' }
        ];
        PreviewImages.resizeImages(ctx, path, sizes, function(err) {
            if (err) {
                return callback(err);
            }

            // Intelligently crop out a part of the image that we can use as a thumbnail.
            PreviewImages.cropThumbnail(ctx, path, function(err) {
                if (err) {
                    return callback(err);
                }

                // Check if we can update the main content metadata (displayname, description, ..)
                var params = {};
                opts = opts || {};
                if (opts.displayName && ctx.content.displayName === url) {
                    params.displayName = opts.displayName;
                }
                if (opts.description && !ctx.content.description) {
                    params.description = opts.description;
                }

                if (_.keys(params).length === 0) {
                    // The user has filled in his own metadata.
                    // Nothing further to do here.
                    return callback();
                } else {
                    // As the user didn't enter his own metadata,
                    // fill it up with whatever came back from the retriever.
                    log().trace({'contentId': ctx.contentId}, 'Updating the content displayName and description.');
                    RestAPI.Content.updateContent(ctx.tenantRestContext, ctx.contentId, params, callback);
                }
            });
        });
    });
};
<commit_msg>Removed line break<commit_after>/*!
 * Copyright 2012 Sakai Foundation (SF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://www.osedu.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

var _ = require('underscore');

var log = require('oae-logger').logger('oae-preview-processor');
var RestAPI = require('oae-rest');

var DefaultRetriever = require('./retrievers/default');
var PreviewConstants = require('./constants');
var PreviewImages = require('./api.images');

var _retrievers = [
    require('./retrievers/flickr'),
    require('./retrievers/slideshare'),
    require('./retrievers/vimeo'),
    require('./retrievers/youtube')
];

/**
 * Inits the link processor.
 * This method will check if the phantomjs binary can be executed.
 *
 * @param  {Object} config                                  The main config object.
 * @param  {Object} config.previews                         The namespace for all preview related configuration.
 * @param  {Object} config.previews.phantomjs               The namespace for all PhantomJS related configuration.
 * @param  {String} config.previews.phantomjs.binary        The path to the `phantomjs` executable. This should either be a direct path or the filename that's on the `PATH` environment.
 * @param  {Number} config.previews.phantomjs.renderDelay   Specifies the time (in ms) between page load and taking a screenshot. This is to allow for AJAX content to be loaded into the page. (Default: 5000ms)
 */
var init = module.exports.init = function(config, callback) {
    DefaultRetriever.init(config.previews.phantomjs, callback);
};

/**
 * Generates previews for a link.
 *
 * @param  {PreviewContext}     ctx                         The preview context associated to this piece of content.
 * @param  {Object}             ctx.content                 The content profile as returned from the REST API.
 * @param  {Object}             ctx.content.link            The actual link.
 * @param  {Object}             [ctx.content.displayName]   The display name for this link. If none is specified and we have special handlers for this type of link, it will be filled in.
 * @param  {Object}             [ctx.content.description]   The description for this link. If none is specified and we have special handlers for this type of link, it will be filled in.
 * @param  {Function}           callback                    Standard callback method.
 * @param  {Object}             callback.err                Standard error object (if any.)
 */
var generatePreviews = module.exports.generatePreviews = function(ctx, callback) {
    var url = ctx.content.link;

    // Select a retriever, the default one will open up the URL in webkit and take a screenshot.
    var retriever = DefaultRetriever;
    for (var i = 0; i < _retrievers.length; i++) {
        if (_retrievers[i].test(url)) {
            retriever = _retrievers[i];
            break;
        }
    }

    // Get an image that we can use to generate activity images from
    retriever.getImage(ctx, url, function(err, path, opts) {
        if (err) {
            return callback(err);
        }

        // Crop the screenshot.
        var sizes = [
            { 'width': PreviewConstants.SIZES.IMAGE.MEDIUM, 'height': PreviewConstants.SIZES.IMAGE.MEDIUM, 'size': 'medium' },
            { 'width': PreviewConstants.SIZES.IMAGE.SMALL, 'height': PreviewConstants.SIZES.IMAGE.SMALL, 'size': 'small' }
        ];
        PreviewImages.resizeImages(ctx, path, sizes, function(err) {
            if (err) {
                return callback(err);
            }

            // Intelligently crop out a part of the image that we can use as a thumbnail.
            PreviewImages.cropThumbnail(ctx, path, function(err) {
                if (err) {
                    return callback(err);
                }

                // Check if we can update the main content metadata (displayname, description, ..)
                var params = {};
                opts = opts || {};
                if (opts.displayName && ctx.content.displayName === url) {
                    params.displayName = opts.displayName;
                }
                if (opts.description && !ctx.content.description) {
                    params.description = opts.description;
                }

                if (_.keys(params).length === 0) {
                    // The user has filled in his own metadata.
                    // Nothing further to do here.
                    return callback();
                } else {
                    // As the user didn't enter his own metadata,
                    // fill it up with whatever came back from the retriever.
                    log().trace({'contentId': ctx.contentId}, 'Updating the content displayName and description.');
                    RestAPI.Content.updateContent(ctx.tenantRestContext, ctx.contentId, params, callback);
                }
            });
        });
    });
};
<|endoftext|>