<commit_before>// DOM Binder
//
// Binds data to the DOM. Changes in data are then immediately rendered
// in the nodes that display that data via template tags such as {{ name }}.
// Only those nodes containing the changed data are updated, other nodes are
// left alone. The actual DOM tree does not change. Template tags can also
// be used in the attributes href, title, id, class, style and value.
//
// dataBind(node, observeFn, unobserveFn, getFn)
//
// node
//
// A DOM node to use as a route. The inner DOM tree is traversed and references
// to property names written as {{ name }} cause bindFn to be called with name
// as property.
//
// bindFn(property, fn)
//
// Responsible for listening to changes to properties on a data object or model.
// When the named property changes, function fn must be called.
//
// getFn(property)
//
// Responsible for returning the value of the named property from a data object
// or model.

(function(Sparky) {
	"use strict";

	// For debugging
	var attributes = [
		'href',
		'title',
		'id',
		'style',
		'value',
		'src',
		'alt'
	];

	var xlink = 'http://www.w3.org/1999/xlink';
	var xsvg  = 'http://www.w3.org/2000/svg';

	window.xlink = xlink;
	window.xsvg = xsvg;

	var rname   = /\{\{\s*([\w\-]+)\s*(?:\|([^\}]+))?\s*\}\}/g;
	var rfilter = /\s*([a-zA-Z0-9_]+)\s*(?:\:(.+))?/;

	var filterCache = {};

	var types = {
	    	1: domNode,
	    	3: textNode,
	    	11: fragmentNode
	    };

	var empty = [];

	var tags = {
	    	input: function(node, name, bind, unbind, get, set) {
	    		var prop = (rname.exec(node.name) || empty)[1];

	    		// Only bind to fields that have a sparky {{tag}} in their
	    		// name attribute.
	    		if (!prop) { return; }

	    		var value1 = get(prop);
	    		var value2 = normalise(node.value);

	    		if (node.type === 'checkbox') {
	    			// If the model property does not yet exist and this input
	    			// is checked, set model property from node's value.
	    			if (!isDefined(value1) && node.checked) {
	    				set(prop, value2);
	    			}

	    			bind(prop, function() {
	    				node.checked = get(prop) === value2;
	    			});

	    			node.addEventListener('change', function(e) {
	    				set(prop, node.checked ? value2 : undefined);
	    			});
	    		}
	    		else if (node.type === 'radio') {
	    			// If the model property does not yet exist and this input
	    			// is checked, set model property from node's value.
	    			if (!isDefined(value1) && node.checked) {
	    				set(prop, value2);
	    			}

	    			bind(prop, function() {
	    				node.checked = get(prop) === value2;
	    			});

	    			node.addEventListener('change', function(e) {
	    				if (node.checked) { set(prop, value2); }
	    			});
	    		}
	    	},

	    	select: function(node, name, bind, unbind, get, set) {
	    		var prop = (rname.exec(node.name) || empty)[1];

	    		// Only bind to fields that have a sparky {{tag}} in their
	    		// name attribute.
	    		if (!prop) { return; }

	    		var value = get(prop);

	    		// If the model property does not yet exist, set it from the
	    		// node's value.
	    		if (!isDefined(value)) {
	    			set(prop, normalise(node.value));
	    		}

	    		bind(prop, function() {
	    			var value = get(prop);
	    			node.value = isDefined(value) ? value : '' ;
	    		});

	    		node.addEventListener('change', function(e) {
	    			set(prop, normalise(node.value));
	    		});
	    	},

	    	textarea: function(node, prop, bind, unbind, get) {
	    		bind(node.name, function() {
	    			var value = get(prop);
	    			node.value = isDefined(value) ? value : '' ;
	    		});
	    	}
	    };

	function normalise(value) {
		// window.isNaN() coerces non-empty strings to numbers before asking if
		// they are NaN. Number.isNaN() (ES6) does not, so beware.
		return value === '' || isNaN(value) ? value : parseFloat(value) ;
	}

	function call(fn) {
		fn();
	}

	function isDefined(val) {
		return val !== undefined && val !== null;
	}

	function domNode(node, bind, unbind, get, set, create) {
		var unobservers = [];
		var tag = node.tagName.toLowerCase();
		//var isSVG = node instanceof SVGElement;

		if (Sparky.debug === 'verbose') {
			console.log('[Sparky] <' + tag + '>, children:', node.childNodes.length, Array.prototype.slice.apply(node.childNodes));
		}

		bindClass(node, bind, unbind, get, unobservers);
		bindAttributes(node, bind, unbind, get, unobservers);
		bindNodes(node, bind, unbind, get, set, create, unobservers);

		// Set up name-value databinding for form elements
		if (tags[tag]) {
			tags[tag](node, node.name, bind, unbind, get, set);
		}

		return unobservers;
	}

	function textNode(node, bind, unbind, get, set, create) {
		var detachFn = observeProperties(node.nodeValue, bind, unbind, get, function(text) {
			node.nodeValue = text;
		});

		return [detachFn];
	}

	function fragmentNode(node, bind, unbind, get, set, create) {
		var unobservers = [];

		bindNodes(node, bind, unbind, get, create, unobservers);

		return unobservers;
	}

	function bindNodes(node, bind, unbind, get, set, create, unobservers) {
		var nodes = [];
		var n = -1;
		var l, child, sparky;

		// childNodes is a live list, and we don't want it to be because we may
		// be about to modify the DOM. Copy it.
		nodes.push.apply(nodes, node.childNodes);
		l = nodes.length;

		// Loop forwards through the children
		while (++n < l) {
			child = nodes[n];

			// Don't bind child nodes that have their own Sparky controllers.
			if (child.getAttribute &&
			   (isDefined(child.getAttribute('data-ctrl')) ||
			    isDefined(child.getAttribute('data-model')))) {
				sparky = create(child);
				unobservers.push(sparky.destroy);
			}
			else if (types[child.nodeType]) {
				unobservers.push.apply(unobservers, types[child.nodeType](child, bind, unbind, get, set, create));
			}
		}
	}

	function getClassList(node) {
		return node.classList || node.className.trim().split(/\s+/);
	}

	function updateClassSVG(node, text) {
		// Trying to use className sets a bunch of other
		// attributes on the node, bizarrely.
		node.setAttribute('class', text);
	}

	function updateClassHTML(node, text) {
		node.className = text;
	}

	function updateAttributeSVG(node, attribute, value) {
		node.setAttributeNS(xlink, attribute, value);
	}

	function updateAttributeHTML(node, attribute, value) {
		node.setAttribute(attribute, value);
	}

	function bindClass(node, bind, unbind, get, unobservers) {
		var value = node.getAttribute('class');

		if (!value) { return; }

		var update = node instanceof SVGElement ?
				updateClassSVG.bind(this, node) :
				updateClassHTML.bind(this, node) ;

		// TODO: only replace classes we've previously set here
		unobservers.push(observeProperties(value, bind, unbind, get, update));
	}

	function bindAttributes(node, bind, unbind, get, unobservers) {
		var a = attributes.length;

		while (a--) {
			bindAttribute(node, attributes[a], bind, unbind, get, unobservers);
		}
	}

	function bindAttribute(node, attribute, bind, unbind, get, unobservers) {
		var isSVG = node instanceof SVGElement;
		var value = isSVG ?
		    	node.getAttributeNS(xlink, attribute) :
		    	node.getAttribute(attribute) ;
		var update;

		if (!isDefined(value) || value === '') { return; }

		update = isSVG ?
			updateAttributeSVG.bind(this, node, attribute) :
			updateAttributeHTML.bind(this, node, attribute) ;

		unobservers.push(observeProperties(value, bind, unbind, get, update));
	}

	function extractProperties(str) {
		var propertiesCache = {},
		    properties = [];

		str.replace(rname, function($0, $1, $2){
			// Make sure properties are only added once.
			if (!propertiesCache[$1]) {
				propertiesCache[$1] = true;
				properties.push($1);
			}
		});

		return properties;
	}

	function toFilter(filter) {
		var parts = rfilter.exec(filter);

		return {
			name: parts[1],
			fn: Sparky.filters[parts[1]],
			args: parts[2] && JSON.parse('[' + parts[2].replace(/\'/g, '\"') + ']')
		};
	}

	function applyFilters(word, filterString) {
		var filters = filterCache[filterString] || (
		    	filterCache[filterString] = filterString.split('|').map(toFilter)
		    ),
		    l = filters.length,
		    n = -1;

		while (++n < l) {
			if (!filters[n].fn) {
				throw new Error('[Sparky] filter \'' + filters[n].name + '\' is not a Sparky filter');
			}

			if (Sparky.debug === 'filter') {
				console.log('[Sparky] filter:', filters[n].name, 'value:', word, 'args:', filters[n].args);
			}

			word = filters[n].fn.apply(word, filters[n].args);
		}

		return word;
	}

	function observeProperties(text, bind, unbind, get, fn) {
		var properties = extractProperties(text);

		function replaceText($0, $1, $2) {
			var word = get($1);

			return word === undefined ? '' :
				$2 ? applyFilters(word, $2) :
				word ;
		}

		function update() {
			fn(text.replace(rname, replaceText));
		}

		// Observe properties
		properties.forEach(function attach(property) {
			bind(property, update);
		});

		// Return a function that unobserves properties
		return function() {
			properties.forEach(function detach(property) {
				unbind(property, update);
			});
		};
	}

	function traverse(node, observe, unobserve, get, set, create) {
		// Assume this is a DOM node, and set the binder off. The
		// binder returns an array of unobserve functions that
		// should be kept around in case the DOM element is removed
		// and the bindings should be thrown away.
		var unobservers = types[node.nodeType](node, observe, unobserve, get, set, create);

		return function unbind() {
			unobservers.forEach(call);
		};
	}

	Sparky.bind = traverse;
	Sparky.attribtues = attributes;
})(window.Sparky || require('sparky'));
<commit_msg>Binds the for attrbute<commit_after>// DOM Binder
//
// Binds data to the DOM. Changes in data are then immediately rendered
// in the nodes that display that data via template tags such as {{ name }}.
// Only those nodes containing the changed data are updated, other nodes are
// left alone. The actual DOM tree does not change. Template tags can also
// be used in the attributes href, title, id, class, style and value.
//
// dataBind(node, observeFn, unobserveFn, getFn)
//
// node
//
// A DOM node to use as a route. The inner DOM tree is traversed and references
// to property names written as {{ name }} cause bindFn to be called with name
// as property.
//
// bindFn(property, fn)
//
// Responsible for listening to changes to properties on a data object or model.
// When the named property changes, function fn must be called.
//
// getFn(property)
//
// Responsible for returning the value of the named property from a data object
// or model.

(function(Sparky) {
	"use strict";

	// For debugging
	var attributes = [
		'href',
		'title',
		'id',
		'for',
		'style',
		'value',
		'src',
		'alt'
	];

	var xlink = 'http://www.w3.org/1999/xlink';
	var xsvg  = 'http://www.w3.org/2000/svg';

	window.xlink = xlink;
	window.xsvg = xsvg;

	var rname   = /\{\{\s*([\w\-]+)\s*(?:\|([^\}]+))?\s*\}\}/g;
	var rfilter = /\s*([a-zA-Z0-9_]+)\s*(?:\:(.+))?/;

	var filterCache = {};

	var types = {
	    	1: domNode,
	    	3: textNode,
	    	11: fragmentNode
	    };

	var empty = [];

	var tags = {
	    	input: function(node, name, bind, unbind, get, set) {
	    		var prop = (rname.exec(node.name) || empty)[1];

	    		// Only bind to fields that have a sparky {{tag}} in their
	    		// name attribute.
	    		if (!prop) { return; }

	    		var value1 = get(prop);
	    		var value2 = normalise(node.value);

	    		if (node.type === 'checkbox') {
	    			// If the model property does not yet exist and this input
	    			// is checked, set model property from node's value.
	    			if (!isDefined(value1) && node.checked) {
	    				set(prop, value2);
	    			}

	    			bind(prop, function() {
	    				node.checked = get(prop) === value2;
	    			});

	    			node.addEventListener('change', function(e) {
	    				set(prop, node.checked ? value2 : undefined);
	    			});
	    		}
	    		else if (node.type === 'radio') {
	    			// If the model property does not yet exist and this input
	    			// is checked, set model property from node's value.
	    			if (!isDefined(value1) && node.checked) {
	    				set(prop, value2);
	    			}

	    			bind(prop, function() {
	    				node.checked = get(prop) === value2;
	    			});

	    			node.addEventListener('change', function(e) {
	    				if (node.checked) { set(prop, value2); }
	    			});
	    		}
	    	},

	    	select: function(node, name, bind, unbind, get, set) {
	    		var prop = (rname.exec(node.name) || empty)[1];

	    		// Only bind to fields that have a sparky {{tag}} in their
	    		// name attribute.
	    		if (!prop) { return; }

	    		var value = get(prop);

	    		// If the model property does not yet exist, set it from the
	    		// node's value.
	    		if (!isDefined(value)) {
	    			set(prop, normalise(node.value));
	    		}

	    		bind(prop, function() {
	    			var value = get(prop);
	    			node.value = isDefined(value) ? value : '' ;
	    		});

	    		node.addEventListener('change', function(e) {
	    			set(prop, normalise(node.value));
	    		});
	    	},

	    	textarea: function(node, prop, bind, unbind, get) {
	    		bind(node.name, function() {
	    			var value = get(prop);
	    			node.value = isDefined(value) ? value : '' ;
	    		});
	    	}
	    };

	function normalise(value) {
		// window.isNaN() coerces non-empty strings to numbers before asking if
		// they are NaN. Number.isNaN() (ES6) does not, so beware.
		return value === '' || isNaN(value) ? value : parseFloat(value) ;
	}

	function call(fn) {
		fn();
	}

	function isDefined(val) {
		return val !== undefined && val !== null;
	}

	function domNode(node, bind, unbind, get, set, create) {
		var unobservers = [];
		var tag = node.tagName.toLowerCase();
		//var isSVG = node instanceof SVGElement;

		if (Sparky.debug === 'verbose') {
			console.log('[Sparky] <' + tag + '>, children:', node.childNodes.length, Array.prototype.slice.apply(node.childNodes));
		}

		bindClass(node, bind, unbind, get, unobservers);
		bindAttributes(node, bind, unbind, get, unobservers);
		bindNodes(node, bind, unbind, get, set, create, unobservers);

		// Set up name-value databinding for form elements
		if (tags[tag]) {
			tags[tag](node, node.name, bind, unbind, get, set);
		}

		return unobservers;
	}

	function textNode(node, bind, unbind, get, set, create) {
		var detachFn = observeProperties(node.nodeValue, bind, unbind, get, function(text) {
			node.nodeValue = text;
		});

		return [detachFn];
	}

	function fragmentNode(node, bind, unbind, get, set, create) {
		var unobservers = [];

		bindNodes(node, bind, unbind, get, create, unobservers);

		return unobservers;
	}

	function bindNodes(node, bind, unbind, get, set, create, unobservers) {
		var nodes = [];
		var n = -1;
		var l, child, sparky;

		// childNodes is a live list, and we don't want it to be because we may
		// be about to modify the DOM. Copy it.
		nodes.push.apply(nodes, node.childNodes);
		l = nodes.length;

		// Loop forwards through the children
		while (++n < l) {
			child = nodes[n];

			// Don't bind child nodes that have their own Sparky controllers.
			if (child.getAttribute &&
			   (isDefined(child.getAttribute('data-ctrl')) ||
			    isDefined(child.getAttribute('data-model')))) {
				sparky = create(child);
				unobservers.push(sparky.destroy);
			}
			else if (types[child.nodeType]) {
				unobservers.push.apply(unobservers, types[child.nodeType](child, bind, unbind, get, set, create));
			}
		}
	}

	function getClassList(node) {
		return node.classList || node.className.trim().split(/\s+/);
	}

	function updateClassSVG(node, text) {
		// Trying to use className sets a bunch of other
		// attributes on the node, bizarrely.
		node.setAttribute('class', text);
	}

	function updateClassHTML(node, text) {
		node.className = text;
	}

	function updateAttributeSVG(node, attribute, value) {
		node.setAttributeNS(xlink, attribute, value);
	}

	function updateAttributeHTML(node, attribute, value) {
		node.setAttribute(attribute, value);
	}

	function bindClass(node, bind, unbind, get, unobservers) {
		var value = node.getAttribute('class');

		if (!value) { return; }

		var update = node instanceof SVGElement ?
				updateClassSVG.bind(this, node) :
				updateClassHTML.bind(this, node) ;

		// TODO: only replace classes we've previously set here
		unobservers.push(observeProperties(value, bind, unbind, get, update));
	}

	function bindAttributes(node, bind, unbind, get, unobservers) {
		var a = attributes.length;

		while (a--) {
			bindAttribute(node, attributes[a], bind, unbind, get, unobservers);
		}
	}

	function bindAttribute(node, attribute, bind, unbind, get, unobservers) {
		var isSVG = node instanceof SVGElement;
		var value = isSVG ?
		    	node.getAttributeNS(xlink, attribute) :
		    	node.getAttribute(attribute) ;
		var update;

		if (!isDefined(value) || value === '') { return; }

		update = isSVG ?
			updateAttributeSVG.bind(this, node, attribute) :
			updateAttributeHTML.bind(this, node, attribute) ;

		unobservers.push(observeProperties(value, bind, unbind, get, update));
	}

	function extractProperties(str) {
		var propertiesCache = {},
		    properties = [];

		str.replace(rname, function($0, $1, $2){
			// Make sure properties are only added once.
			if (!propertiesCache[$1]) {
				propertiesCache[$1] = true;
				properties.push($1);
			}
		});

		return properties;
	}

	function toFilter(filter) {
		var parts = rfilter.exec(filter);

		return {
			name: parts[1],
			fn: Sparky.filters[parts[1]],
			args: parts[2] && JSON.parse('[' + parts[2].replace(/\'/g, '\"') + ']')
		};
	}

	function applyFilters(word, filterString) {
		var filters = filterCache[filterString] || (
		    	filterCache[filterString] = filterString.split('|').map(toFilter)
		    ),
		    l = filters.length,
		    n = -1;

		while (++n < l) {
			if (!filters[n].fn) {
				throw new Error('[Sparky] filter \'' + filters[n].name + '\' is not a Sparky filter');
			}

			if (Sparky.debug === 'filter') {
				console.log('[Sparky] filter:', filters[n].name, 'value:', word, 'args:', filters[n].args);
			}

			word = filters[n].fn.apply(word, filters[n].args);
		}

		return word;
	}

	function observeProperties(text, bind, unbind, get, fn) {
		var properties = extractProperties(text);

		function replaceText($0, $1, $2) {
			var word = get($1);

			return word === undefined ? '' :
				$2 ? applyFilters(word, $2) :
				word ;
		}

		function update() {
			fn(text.replace(rname, replaceText));
		}

		// Observe properties
		properties.forEach(function attach(property) {
			bind(property, update);
		});

		// Return a function that unobserves properties
		return function() {
			properties.forEach(function detach(property) {
				unbind(property, update);
			});
		};
	}

	function traverse(node, observe, unobserve, get, set, create) {
		// Assume this is a DOM node, and set the binder off. The
		// binder returns an array of unobserve functions that
		// should be kept around in case the DOM element is removed
		// and the bindings should be thrown away.
		var unobservers = types[node.nodeType](node, observe, unobserve, get, set, create);

		return function unbind() {
			unobservers.forEach(call);
		};
	}

	Sparky.bind = traverse;
	Sparky.attributes = attributes;
})(window.Sparky || require('sparky'));
<|endoftext|>


<commit_before>/* @flow */

import {BLOCK_TYPE, ENTITY_TYPE, INLINE_STYLE} from './Constants';
import {Entity} from 'draft-js';
import getEntityRanges from './getEntityRanges';

import type {ContentState, ContentBlock} from 'draft-js';
import type {CharacterMetaList} from './getEntityRanges';

const {
  BOLD,
  CODE,
  ITALIC,
  STRIKETHROUGH,
  UNDERLINE,
} = INLINE_STYLE;

const INDENT = '  ';
const BREAK = '
';

// TODO: Move these getter functions. Also accept alternatives via config.

// The reason this returns an array is because a single block might get wrapped
// in two tags.
function getTags(blockType: string): Array<string> {
  switch (blockType) {
    case BLOCK_TYPE.HEADER_ONE:
      return ['h1'];
    case BLOCK_TYPE.HEADER_TWO:
      return ['h2'];
    case BLOCK_TYPE.HEADER_THREE:
      return ['h3'];
    case BLOCK_TYPE.HEADER_FOUR:
      return ['h4'];
    case BLOCK_TYPE.HEADER_FIVE:
      return ['h5'];
    case BLOCK_TYPE.HEADER_SIX:
      return ['h6'];
    case BLOCK_TYPE.UNORDERED_LIST_ITEM:
    case BLOCK_TYPE.ORDERED_LIST_ITEM:
      return ['li'];
    case BLOCK_TYPE.BLOCKQUOTE:
      return ['blockquote'];
    case BLOCK_TYPE.CODE:
      return ['pre', 'code'];
    default:
      return ['p'];
  }
}

function getWrapperTag(blockType: string): ?string {
  switch (blockType) {
    case BLOCK_TYPE.UNORDERED_LIST_ITEM:
      return 'ul';
    case BLOCK_TYPE.ORDERED_LIST_ITEM:
      return 'ol';
    default:
      return null;
  }
}

function canHaveDepth(blockType: string): boolean {
  switch (blockType) {
    case BLOCK_TYPE.UNORDERED_LIST_ITEM:
    case BLOCK_TYPE.ORDERED_LIST_ITEM:
      return true;
    default:
      return false;
  }
}

function encodeContent(text: string): string {
  return text
    .split('&').join('&')
    .split('<').join('<')
    .split('>').join('>')
    .split('\xA0').join(' ')
    .split('\n').join(BREAK + '\n');
}

function encodeAttr(text: string): string {
  return text
    .split('&').join('&')
    .split('<').join('<')
    .split('>').join('>')
    .split('"').join('"');
}

class MarkupGenerator {
  blocks: Array<ContentBlock>;
  contentState: ContentState;
  currentBlock: number;
  indentLevel: number;
  output: Array<string>;
  totalBlocks: number;
  wrapperTag: ?string;

  constructor(contentState: ContentState) {
    this.contentState = contentState;
  }

  generate(): string {
    this.output = [];
    this.blocks = this.contentState.getBlockMap().toArray();
    this.totalBlocks = this.blocks.length;
    this.currentBlock = 0;
    this.indentLevel = 0;
    this.wrapperTag = null;
    while (this.currentBlock < this.totalBlocks) {
      this.processBlock();
    }
    this.closeWrapperTag();
    return this.output.join('').trim();
  }

  processBlock() {
    let block = this.blocks[this.currentBlock];
    let blockType = block.getType();
    let newWrapperTag = getWrapperTag(blockType);
    if (this.wrapperTag !== newWrapperTag) {
      if (this.wrapperTag) {
        this.closeWrapperTag();
      }
      if (newWrapperTag) {
        this.openWrapperTag(newWrapperTag);
      }
    }
    this.indent();
    this.writeStartTag(blockType);
    this.output.push(this.renderBlockContent(block));
    // Look ahead and see if we will nest list.
    let nextBlock = this.getNextBlock();
    if (
      canHaveDepth(blockType) &&
      nextBlock &&
      nextBlock.getDepth() === block.getDepth() + 1
    ) {
      this.output.push(`\n`);
      // This is a litle hacky: temporarily stash our current wrapperTag and
      // render child list(s).
      let thisWrapperTag = this.wrapperTag;
      this.wrapperTag = null;
      this.indentLevel += 1;
      this.currentBlock += 1;
      this.processBlocksAtDepth(nextBlock.getDepth());
      this.wrapperTag = thisWrapperTag;
      this.indentLevel -= 1;
      this.indent();
    } else {
      this.currentBlock += 1;
    }
    this.writeEndTag(blockType);
  }

  processBlocksAtDepth(depth: number) {
    let block = this.blocks[this.currentBlock];
    while (block && block.getDepth() === depth) {
      this.processBlock();
      block = this.blocks[this.currentBlock];
    }
    this.closeWrapperTag();
  }

  getNextBlock(): ContentBlock {
    return this.blocks[this.currentBlock + 1];
  }

  writeStartTag(blockType) {
    let tags = getTags(blockType);
    for (let tag of tags) {
      this.output.push(`<${tag}>`);
    }
  }

  writeEndTag(blockType) {
    let tags = getTags(blockType);
    if (tags.length === 1) {
      this.output.push(`</${tags[0]}>\n`);
    } else {
      let output = [];
      for (let tag of tags) {
        output.unshift(`</${tag}>`);
      }
      this.output.push(output.join('') + '\n');
    }
  }

  openWrapperTag(wrapperTag: string) {
    this.wrapperTag = wrapperTag;
    this.indent();
    this.output.push(`<${wrapperTag}>\n`);
    this.indentLevel += 1;
  }

  closeWrapperTag() {
    if (this.wrapperTag) {
      this.indentLevel -= 1;
      this.indent();
      this.output.push(`</${this.wrapperTag}>\n`);
      this.wrapperTag = null;
    }
  }

  indent() {
    this.output.push(INDENT.repeat(this.indentLevel));
  }

  renderBlockContent(block: ContentBlock): string {
    let blockType = block.getType();
    let text = block.getText();
    if (text === '') {
      // Prevent element collapse if completely empty.
      return BREAK;
    }
    text = this.preserveWhitespace(text);
    let charMetaList: CharacterMetaList = block.getCharacterList();
    let entityPieces = getEntityRanges(text, charMetaList);
    return entityPieces.map(([entityKey, stylePieces]) => {
      let content = stylePieces.map(([text, style]) => {
        let content = encodeContent(text);
        // These are reverse alphabetical by tag name.
        if (style.has(BOLD)) {
          content = `<strong>${content}</strong>`;
        }
        if (style.has(UNDERLINE)) {
          content = `<ins>${content}</ins>`;
        }
        if (style.has(ITALIC)) {
          content = `<em>${content}</em>`;
        }
        if (style.has(STRIKETHROUGH)) {
          content = `<del>${content}</del>`;
        }
        if (style.has(CODE)) {
          // If our block type is CODE then we are already wrapping the whole
          // block in a `<code>` so don't wrap inline code elements.
          content = (blockType === BLOCK_TYPE.CODE) ? content : `<code>${content}</code>`;
        }
        return content;
      }).join('');
      let entity = entityKey ? Entity.get(entityKey) : null;
      if (entity != null && entity.getType() === ENTITY_TYPE.LINK) {
        let url = entity.getData().url || '';
        return `<a href="${encodeAttr(url)}">${content}</a>`;
      } else {
        return content;
      }
    }).join('');
  }

  preserveWhitespace(text: string): string {
    let length = text.length;
    // Prevent leading/trailing/consecutive whitespace collapse.
    let newText = new Array(length);
    for (let i = 0; i < length; i++) {
      if (
        text[i] === ' ' &&
        (i === 0 || i === length - 1 || text[i - 1] === ' ')
      ) {
        newText[i] = '\xA0';
      } else {
        newText[i] = text[i];
      }
    }
    return newText.join('');
  }

}

export default function stateToHTML(content: ContentState): string {
  return new MarkupGenerator(content).generate();
}
<commit_msg>use getBlocksAsArray<commit_after>/* @flow */

import {BLOCK_TYPE, ENTITY_TYPE, INLINE_STYLE} from './Constants';
import {Entity} from 'draft-js';
import getEntityRanges from './getEntityRanges';

import type {ContentState, ContentBlock} from 'draft-js';
import type {CharacterMetaList} from './getEntityRanges';

const {
  BOLD,
  CODE,
  ITALIC,
  STRIKETHROUGH,
  UNDERLINE,
} = INLINE_STYLE;

const INDENT = '  ';
const BREAK = '
';

// TODO: Move these getter functions. Also accept alternatives via config.

// The reason this returns an array is because a single block might get wrapped
// in two tags.
function getTags(blockType: string): Array<string> {
  switch (blockType) {
    case BLOCK_TYPE.HEADER_ONE:
      return ['h1'];
    case BLOCK_TYPE.HEADER_TWO:
      return ['h2'];
    case BLOCK_TYPE.HEADER_THREE:
      return ['h3'];
    case BLOCK_TYPE.HEADER_FOUR:
      return ['h4'];
    case BLOCK_TYPE.HEADER_FIVE:
      return ['h5'];
    case BLOCK_TYPE.HEADER_SIX:
      return ['h6'];
    case BLOCK_TYPE.UNORDERED_LIST_ITEM:
    case BLOCK_TYPE.ORDERED_LIST_ITEM:
      return ['li'];
    case BLOCK_TYPE.BLOCKQUOTE:
      return ['blockquote'];
    case BLOCK_TYPE.CODE:
      return ['pre', 'code'];
    default:
      return ['p'];
  }
}

function getWrapperTag(blockType: string): ?string {
  switch (blockType) {
    case BLOCK_TYPE.UNORDERED_LIST_ITEM:
      return 'ul';
    case BLOCK_TYPE.ORDERED_LIST_ITEM:
      return 'ol';
    default:
      return null;
  }
}

function canHaveDepth(blockType: string): boolean {
  switch (blockType) {
    case BLOCK_TYPE.UNORDERED_LIST_ITEM:
    case BLOCK_TYPE.ORDERED_LIST_ITEM:
      return true;
    default:
      return false;
  }
}

function encodeContent(text: string): string {
  return text
    .split('&').join('&')
    .split('<').join('<')
    .split('>').join('>')
    .split('\xA0').join(' ')
    .split('\n').join(BREAK + '\n');
}

function encodeAttr(text: string): string {
  return text
    .split('&').join('&')
    .split('<').join('<')
    .split('>').join('>')
    .split('"').join('"');
}

class MarkupGenerator {
  blocks: Array<ContentBlock>;
  contentState: ContentState;
  currentBlock: number;
  indentLevel: number;
  output: Array<string>;
  totalBlocks: number;
  wrapperTag: ?string;

  constructor(contentState: ContentState) {
    this.contentState = contentState;
  }

  generate(): string {
    this.output = [];
    this.blocks = this.contentState.getBlocksAsArray();
    this.totalBlocks = this.blocks.length;
    this.currentBlock = 0;
    this.indentLevel = 0;
    this.wrapperTag = null;
    while (this.currentBlock < this.totalBlocks) {
      this.processBlock();
    }
    this.closeWrapperTag();
    return this.output.join('').trim();
  }

  processBlock() {
    let block = this.blocks[this.currentBlock];
    let blockType = block.getType();
    let newWrapperTag = getWrapperTag(blockType);
    if (this.wrapperTag !== newWrapperTag) {
      if (this.wrapperTag) {
        this.closeWrapperTag();
      }
      if (newWrapperTag) {
        this.openWrapperTag(newWrapperTag);
      }
    }
    this.indent();
    this.writeStartTag(blockType);
    this.output.push(this.renderBlockContent(block));
    // Look ahead and see if we will nest list.
    let nextBlock = this.getNextBlock();
    if (
      canHaveDepth(blockType) &&
      nextBlock &&
      nextBlock.getDepth() === block.getDepth() + 1
    ) {
      this.output.push(`\n`);
      // This is a litle hacky: temporarily stash our current wrapperTag and
      // render child list(s).
      let thisWrapperTag = this.wrapperTag;
      this.wrapperTag = null;
      this.indentLevel += 1;
      this.currentBlock += 1;
      this.processBlocksAtDepth(nextBlock.getDepth());
      this.wrapperTag = thisWrapperTag;
      this.indentLevel -= 1;
      this.indent();
    } else {
      this.currentBlock += 1;
    }
    this.writeEndTag(blockType);
  }

  processBlocksAtDepth(depth: number) {
    let block = this.blocks[this.currentBlock];
    while (block && block.getDepth() === depth) {
      this.processBlock();
      block = this.blocks[this.currentBlock];
    }
    this.closeWrapperTag();
  }

  getNextBlock(): ContentBlock {
    return this.blocks[this.currentBlock + 1];
  }

  writeStartTag(blockType) {
    let tags = getTags(blockType);
    for (let tag of tags) {
      this.output.push(`<${tag}>`);
    }
  }

  writeEndTag(blockType) {
    let tags = getTags(blockType);
    if (tags.length === 1) {
      this.output.push(`</${tags[0]}>\n`);
    } else {
      let output = [];
      for (let tag of tags) {
        output.unshift(`</${tag}>`);
      }
      this.output.push(output.join('') + '\n');
    }
  }

  openWrapperTag(wrapperTag: string) {
    this.wrapperTag = wrapperTag;
    this.indent();
    this.output.push(`<${wrapperTag}>\n`);
    this.indentLevel += 1;
  }

  closeWrapperTag() {
    if (this.wrapperTag) {
      this.indentLevel -= 1;
      this.indent();
      this.output.push(`</${this.wrapperTag}>\n`);
      this.wrapperTag = null;
    }
  }

  indent() {
    this.output.push(INDENT.repeat(this.indentLevel));
  }

  renderBlockContent(block: ContentBlock): string {
    let blockType = block.getType();
    let text = block.getText();
    if (text === '') {
      // Prevent element collapse if completely empty.
      return BREAK;
    }
    text = this.preserveWhitespace(text);
    let charMetaList: CharacterMetaList = block.getCharacterList();
    let entityPieces = getEntityRanges(text, charMetaList);
    return entityPieces.map(([entityKey, stylePieces]) => {
      let content = stylePieces.map(([text, style]) => {
        let content = encodeContent(text);
        // These are reverse alphabetical by tag name.
        if (style.has(BOLD)) {
          content = `<strong>${content}</strong>`;
        }
        if (style.has(UNDERLINE)) {
          content = `<ins>${content}</ins>`;
        }
        if (style.has(ITALIC)) {
          content = `<em>${content}</em>`;
        }
        if (style.has(STRIKETHROUGH)) {
          content = `<del>${content}</del>`;
        }
        if (style.has(CODE)) {
          // If our block type is CODE then we are already wrapping the whole
          // block in a `<code>` so don't wrap inline code elements.
          content = (blockType === BLOCK_TYPE.CODE) ? content : `<code>${content}</code>`;
        }
        return content;
      }).join('');
      let entity = entityKey ? Entity.get(entityKey) : null;
      if (entity != null && entity.getType() === ENTITY_TYPE.LINK) {
        let url = entity.getData().url || '';
        return `<a href="${encodeAttr(url)}">${content}</a>`;
      } else {
        return content;
      }
    }).join('');
  }

  preserveWhitespace(text: string): string {
    let length = text.length;
    // Prevent leading/trailing/consecutive whitespace collapse.
    let newText = new Array(length);
    for (let i = 0; i < length; i++) {
      if (
        text[i] === ' ' &&
        (i === 0 || i === length - 1 || text[i - 1] === ' ')
      ) {
        newText[i] = '\xA0';
      } else {
        newText[i] = text[i];
      }
    }
    return newText.join('');
  }

}

export default function stateToHTML(content: ContentState): string {
  return new MarkupGenerator(content).generate();
}
<|endoftext|>


<commit_before>import Phaser from 'phaser';

export default class extends Phaser.State {
  init() {
    this.jumpTimer = 0;
    var cursors
  }

  preload() {

  }

  create() {
    this.physics.startSystem(Phaser.Physics.ARCADE);

    this.map = this.add.tilemap('map');
    this.map.addTilesetImage('grass');

    this.layer = this.map.createLayer('Tile Layer 1');

    this.map.setCollisionBetween(1, 2);

    this.dan = this.add.sprite(0, 0, 'dan');
    this.physics.enable(this.dan, Phaser.Physics.ARCADE);
    this.dan.body.collideWorldBounds = true;
    this.dan.body.gravity.set(0, 450);

    this.cursors = this.input.keyboard.createCursorKeys();

    this.camera.follow(this.dan);
    this.camera.deadzone = new Phaser.Rectangle(100, 100, 600, 400);
  }

  update() {
    this.physics.arcade.collide(this.dan, this.layer);

    this.dan.body.velocity.x = 0;

    if (this.cursors.left.isDown) {
      this.dan.body.velocity.x = -150;
    }
    if (this.cursors.right.isDown) {
      this.dan.body.velocity.x = 150;
    }

    if (this.cursors.up.isDown && this.dan.body.onFloor() && this.time.now > this.jumpTimer) {
        this.dan.body.velocity.y = -250;
        this.jumpTimer = this.time.now + 750;
    }
  }

  render() {

  }
}
<commit_msg>added bullets (no controls)<commit_after>import Phaser from 'phaser';

export default class extends Phaser.State {
  init() {
    this.jumpTimer = 0;
    var cursors
    var weapon
  }

  preload() {

  }

  create() {
    this.physics.startSystem(Phaser.Physics.ARCADE);
    this.weapon = game.add.weapon(30, 'bullet');

    this.map = this.add.tilemap('map');
    this.map.addTilesetImage('grass');

    this.layer = this.map.createLayer('Tile Layer 1');

    this.map.setCollisionBetween(1, 2);

    this.dan = this.add.sprite(0, 0, 'dan');
    this.physics.enable(this.dan, Phaser.Physics.ARCADE);
    this.dan.body.collideWorldBounds = true;
    this.dan.body.gravity.set(0, 450);

    this.cursors = this.input.keyboard.createCursorKeys();

    this.camera.follow(this.dan);
    this.camera.deadzone = new Phaser.Rectangle(100, 100, 600, 400);

    this.weapon.bulletKillType = Phaser.Weapon.KILL_WORLD_BOUNDS;
    this.weapon.bulletSpeed = 650
    this.weapon.fireRate = 50;
  }

  update() {
    this.physics.arcade.collide(this.dan, this.layer);

    this.dan.body.velocity.x = 0;

    if (this.cursors.left.isDown) {
      this.dan.body.velocity.x = -150;
    }
    if (this.cursors.right.isDown) {
      this.dan.body.velocity.x = 150;
    }

    if (this.cursors.up.isDown && this.dan.body.onFloor() && this.time.now > this.jumpTimer) {
        this.dan.body.velocity.y = -250;
        this.jumpTimer = this.time.now + 750;
    }
  }

  render() {

  }
}
<|endoftext|>


<commit_before>import { Phaser } from 'phaser';
import { Wizard } from '../sprites/wizard';
import { Monster } from '../sprites/monster';

export class Play extends Phaser.State {
    preload() {

       this.game.load.audio('spell', '../assets/spell1_0.wav');
    }

    create() {

        this.game.add.tileSprite(0, 0, 800, 600, 'background');
        this.game.physics.startSystem(Phaser.Physics.ARCADE);

        this.wizard = new Wizard(this.game);
        this.game.add.existing(this.wizard);

        this.monsters = this.game.add.group();
        this.monsters.enableBody = true;

        for(let x = 0; x < 5; x++) {

            let monster = new Monster(this.game);
            this.monsters.add(monster);
        }

        this.monsters.setAll('body.velocity.x', 0);
        this.monsters.setAll('body.velocity.y', 0);

        this.cursors = this.game.input.keyboard.createCursorKeys();
        this.game.stage.backgroundColor = "#FFFFFF";

        this.game.input.keyboard.addKeyCapture([ Phaser.KeyCode.SPACEBAR ]);

        this.spell = this.game.add.audio('spell');
        this.spell.volume = 0.5;
        this.game.sound.setDecodedCallback(this.spell, this.start, this)
    }

    update() {
        this.wizard.skate(this.cursors);
        this.monsters.callAll('munch');

        if (this.game.input.keyboard.isDown(Phaser.KeyCode.SPACEBAR)) {
            this.wizard.castSpell();
            this.spell.play();
        }

        this.monsters.forEach(this.game.physics.arcade.moveToObject, this.game.physics.arcade, false, this.wizard, 1000, 1000);
    }
}<commit_msg>adding code for a collision (wizard and  monster group)<commit_after>import { Phaser } from 'phaser';
import { Wizard } from '../sprites/wizard';
import { Monster } from '../sprites/monster';

export class Play extends Phaser.State {
    preload() {

       this.game.load.audio('spell', '../assets/spell1_0.wav');
    }

    create() {

        this.game.add.tileSprite(0, 0, 800, 600, 'background');
        this.game.physics.startSystem(Phaser.Physics.ARCADE);

        this.wizard = new Wizard(this.game);
        this.game.add.existing(this.wizard);

        this.monsters = this.game.add.group();
        this.monsters.enableBody = true;

        for(let x = 0; x < 5; x++) {

            let monster = new Monster(this.game);
            this.monsters.add(monster);
        }

        this.monsters.setAll('body.velocity.x', 0);
        this.monsters.setAll('body.velocity.y', 0);

        this.cursors = this.game.input.keyboard.createCursorKeys();
        this.game.stage.backgroundColor = "#FFFFFF";

        this.game.input.keyboard.addKeyCapture([ Phaser.KeyCode.SPACEBAR ]);

        this.spell = this.game.add.audio('spell');
        this.spell.volume = 0.5;
        this.game.sound.setDecodedCallback(this.spell, this.start, this)
    }

    update() {
        this.wizard.skate(this.cursors);
        this.monsters.callAll('munch');

        if (this.game.input.keyboard.isDown(Phaser.KeyCode.SPACEBAR)) {
            this.wizard.castSpell();
            this.spell.play();
        }

        this.monsters.forEach(this.game.physics.arcade.moveToObject, this.game.physics.arcade, false, this.wizard, 2000, 2000);
        this.monsters.forEach(this.game.physics.arcade.collide, this.game.physics.arcade, false,  this.wizard, this.monsters, this.collisionHandler, null, this);
    }

    collisionHandler() {
        console.log('collision occurred');
    }
}<|endoftext|>


<commit_before>import { createStore, combineReducers, applyMiddleware } from 'redux';
import Actions from '../constants/actions';
import Api from '../utils/api';
import { createAction } from '../utils/ActionCreator';
import Immutable from 'immutable';
import { Maybe } from 'monet';
import { routerReducer } from 'react-router-redux';
import { reducer as UserReducer } from './Users';
import createSagaMiddleware from 'redux-saga';
import _ from 'lodash';
import mySaga from './sagas';

const sagaMiddleware = createSagaMiddleware();

const initialState = Immutable.Map({
    questions: Immutable.List(),
    answers: Immutable.List(),
    currentQuestion: Maybe.None(),
    user: Maybe.None(),
    users: Immutable.List(),
    gameType: 'freetext',
    gameQuestionCount: Maybe.Some(10)
});

const getAnswer = (question, answerGiven) => Immutable.Map({
    questionId: question.id,
    answerGiven: answerGiven,
    correct: question.answer.toLowerCase() === answerGiven.toLowerCase()
});

const getCurrentQuestion = (state) => state.get('currentQuestion').map(n => state.get('questions').get(n));

const isLastQuestion = (questionNo, questions, maxQuestions) =>
    questionNo >= questions.size - 1 || maxQuestions.map(count => questionNo >= count - 1).orSome(false);

const getNextQuestionNo = (questionNo, questions, maxQuestions) =>
    questionNo.flatMap(n => isLastQuestion(n, questions, maxQuestions) ? Maybe.None() : Maybe.Some(n + 1));

/* eslint-disable no-use-before-define */
function reducer(state = initialState, action) {
    switch (action.type) {
        case Actions.REQUEST_QUESTION_LIST:
            Api.json('/api/questions').then(questions => store.dispatch(createAction(Actions.QUESTION_LIST_LOADED, {questions})));
            return state;
        case Actions.QUESTION_LIST_LOADED:
            return state.set('questions', Immutable.List(action.questions));
        case Actions.QUESTION_ANSWERED:
            console.log('answered', action);
            return state
                .set('answers', state.get('answers').push(getAnswer(getCurrentQuestion(state).some(), action.answer)))
                .set('currentQuestion', getNextQuestionNo(state.get('currentQuestion'), state.get('questions'), state.get('gameQuestionCount')));
        case Actions.START_GAME:
            return state
                .set('answers', Immutable.List())
                .set('currentQuestion', Maybe.Some(0));
        case Actions.STOP_GAME:
            return state
                .set('currentQuestion', Maybe.None());
        case Actions.GAME_TYPE_CHANGED:
            return state
                .set('gameType', action.gameType);
        case Actions.GAME_QUESTION_COUNT_CHANGED:
            return state
                .set('gameQuestionCount', action.count);
        case Actions.REQUEST_USER_INFO:
            Api.json('/api/user').then(user =>
                store.dispatch(createAction(Actions.USER_CHANGED, {user: user.logged ? user : null}))
            );
            return state;
        case Actions.USER_CHANGED:
            if (action.user) {
                return state.set('user', Maybe.Some(action.user));
            } else {
                return state.set('user', Maybe.None());
            }
        case Actions.ADD_QUESTION:
            Api.post('/api/questions', {question: action.question, answer: action.answer})
                .then(() => store.dispatch(createAction(Actions.REQUEST_QUESTION_LIST)));
            return state;
        case Actions.DELETE_QUESTION:
            Api.del(`/api/questions/${action.id}`)
                .then(() => store.dispatch(createAction(Actions.REQUEST_QUESTION_LIST)));
            return state;
        default:
            return state;
    }
}

/* eslint-enable no-use-before-define */

const reducers = combineReducers({
    users: UserReducer,
    main: reducer,
    routing: routerReducer
});

const store = createStore(reducers, applyMiddleware(sagaMiddleware));

sagaMiddleware.run(mySaga);

export default store;

store.subscribe(() =>
  console.log('store change', store.getState())
);
<commit_msg>always shuffle questions when starting the game<commit_after>import { createStore, combineReducers, applyMiddleware } from 'redux';
import Actions from '../constants/actions';
import Api from '../utils/api';
import { createAction } from '../utils/ActionCreator';
import Immutable from 'immutable';
import { Maybe } from 'monet';
import { routerReducer } from 'react-router-redux';
import { reducer as UserReducer } from './Users';
import createSagaMiddleware from 'redux-saga';
import _ from 'lodash';
import mySaga from './sagas';

const sagaMiddleware = createSagaMiddleware();

const initialState = Immutable.Map({
    questions: Immutable.List(),
    answers: Immutable.List(),
    currentQuestion: Maybe.None(),
    user: Maybe.None(),
    users: Immutable.List(),
    gameType: 'freetext',
    gameQuestionCount: Maybe.Some(10)
});

const getAnswer = (question, answerGiven) => Immutable.Map({
    questionId: question.id,
    answerGiven: answerGiven,
    correct: question.answer.toLowerCase() === answerGiven.toLowerCase()
});

const getCurrentQuestion = (state) => state.get('currentQuestion').map(n => state.get('questions').get(n));

const isLastQuestion = (questionNo, questions, maxQuestions) =>
    questionNo >= questions.size - 1 || maxQuestions.map(count => questionNo >= count - 1).orSome(false);

const getNextQuestionNo = (questionNo, questions, maxQuestions) =>
    questionNo.flatMap(n => isLastQuestion(n, questions, maxQuestions) ? Maybe.None() : Maybe.Some(n + 1));

/* eslint-disable no-use-before-define */
function reducer(state = initialState, action) {
    switch (action.type) {
        case Actions.REQUEST_QUESTION_LIST:
            Api.json('/api/questions').then(questions => store.dispatch(createAction(Actions.QUESTION_LIST_LOADED, {questions})));
            return state;
        case Actions.QUESTION_LIST_LOADED:
            return state.set('questions', Immutable.List(action.questions));
        case Actions.QUESTION_ANSWERED:
            console.log('answered', action);
            return state
                .set('answers', state.get('answers').push(getAnswer(getCurrentQuestion(state).some(), action.answer)))
                .set('currentQuestion', getNextQuestionNo(state.get('currentQuestion'), state.get('questions'), state.get('gameQuestionCount')));
        case Actions.START_GAME:
            return state
                .set('questions', Immutable.List(_.shuffle(state.get('questions').toArray())))
                .set('answers', Immutable.List())
                .set('currentQuestion', Maybe.Some(0));
        case Actions.STOP_GAME:
            return state
                .set('currentQuestion', Maybe.None());
        case Actions.GAME_TYPE_CHANGED:
            return state
                .set('gameType', action.gameType);
        case Actions.GAME_QUESTION_COUNT_CHANGED:
            return state
                .set('gameQuestionCount', action.count);
        case Actions.REQUEST_USER_INFO:
            Api.json('/api/user').then(user =>
                store.dispatch(createAction(Actions.USER_CHANGED, {user: user.logged ? user : null}))
            );
            return state;
        case Actions.USER_CHANGED:
            if (action.user) {
                return state.set('user', Maybe.Some(action.user));
            } else {
                return state.set('user', Maybe.None());
            }
        case Actions.ADD_QUESTION:
            Api.post('/api/questions', {question: action.question, answer: action.answer})
                .then(() => store.dispatch(createAction(Actions.REQUEST_QUESTION_LIST)));
            return state;
        case Actions.DELETE_QUESTION:
            Api.del(`/api/questions/${action.id}`)
                .then(() => store.dispatch(createAction(Actions.REQUEST_QUESTION_LIST)));
            return state;
        default:
            return state;
    }
}

/* eslint-enable no-use-before-define */

const reducers = combineReducers({
    users: UserReducer,
    main: reducer,
    routing: routerReducer
});

const store = createStore(reducers, applyMiddleware(sagaMiddleware));

sagaMiddleware.run(mySaga);

export default store;

store.subscribe(() =>
  console.log('store change', store.getState())
);
<|endoftext|>


<commit_before>class StoriesAPI {

    constructor() {
        this.sections = {};
    }

    section(name) {
        if (!this.sections[name]) {
            this.sections[name] = {};
        }

        let sectionAPI = {};

        sectionAPI.story = (storyName) => {
            let section = this.sections[name];
            let storyAPI = {};
            storyAPI.add = (title, template, model) => {
                if (!section[storyName]) {
                    section[storyName] = [];
                }
                const id = Math.random().toString(36).substr(2, 9);
                section[storyName].push({ title, template, model, id });
                return storyAPI;
            }
            return storyAPI;
        };

        return sectionAPI;

    }

    getSections() {
        return this.sections;
    }

    registerContactCB(cb) {
        this.contactCB = cb;
    }

    registerReloadCB(cb) {
        this.reloadCB = cb;
    }

    contact() {
        this.contactCB();
    }

    getStory(sbObject) {
        try {
            return this.sections[sbObject.section][sbObject.story].find(i => i.id === sbObject.id)
        } catch (e) {
            console.log(e);
        }
    }

    updateStory(sbObject, data) {
        let storyPoint = this.getStory(sbObject);
        storyPoint = data;
    }

    reload() {
        if (this.reloadCB) {
            this.sections = {};
            setTimeout(() => this.reloadCB, 0);
        }
    }

}

window.sb = new StoriesAPI();<commit_msg>Minor fix<commit_after>class StoriesAPI {

    constructor() {
        this.sections = {};
    }

    section(name) {
        if (!this.sections[name]) {
            this.sections[name] = {};
        }

        let sectionAPI = {};

        sectionAPI.story = (storyName) => {
            let section = this.sections[name];
            let storyAPI = {};
            storyAPI.add = (title, template, model) => {
                if (!section[storyName]) {
                    section[storyName] = [];
                }
                const id = Math.random().toString(36).substr(2, 9);
                section[storyName].push({ title, template, model, id });
                return storyAPI;
            }
            return storyAPI;
        };

        return sectionAPI;

    }

    getSections() {
        return this.sections;
    }

    registerContactCB(cb) {
        this.contactCB = cb;
    }

    registerReloadCB(cb) {
        this.reloadCB = cb;
    }

    contact() {
        this.contactCB();
    }

    getStory(sbObject) {
        try {
            return this.sections[sbObject.section][sbObject.story].find(i => i.id === sbObject.id)
        } catch (e) {
            console.log(e);
        }
    }

    updateStory(sbObject, data) {
        let storyPoint = this.getStory(sbObject);
        storyPoint = data;
    }

    reload() {
        if (this.reloadCB) {
            this.sections = {};
            setTimeout(() => this.reloadCB(), 0);
        }
    }

}

window.sb = new StoriesAPI();<|endoftext|>


<commit_before>/**
 * SVG Painter
 * @module zrender/svg/Painter
 */

import {createElement} from './core';
import * as util from '../core/util';
import logError from '../core/log';
import Path from '../graphic/Path';
import ZImage from '../graphic/Image';
import ZText from '../graphic/Text';
import arrayDiff from '../core/arrayDiff2';
import GradientManager from './helper/GradientManager';
import ClippathManager from './helper/ClippathManager';
import ShadowManager from './helper/ShadowManager';
import {
    path as svgPath,
    image as svgImage,
    text as svgText
} from './graphic';

function parseInt10(val) {
    return parseInt(val, 10);
}

function getSvgProxy(el) {
    if (el instanceof Path) {
        return svgPath;
    }
    else if (el instanceof ZImage) {
        return svgImage;
    }
    else if (el instanceof ZText) {
        return svgText;
    }
    else {
        return svgPath;
    }
}

function checkParentAvailable(parent, child) {
    return child && parent && child.parentNode !== parent;
}

function insertAfter(parent, child, prevSibling) {
    if (checkParentAvailable(parent, child) && prevSibling) {
        var nextSibling = prevSibling.nextSibling;
        nextSibling ? parent.insertBefore(child, nextSibling)
            : parent.appendChild(child);
    }
}

function prepend(parent, child) {
    if (checkParentAvailable(parent, child)) {
        var firstChild = parent.firstChild;
        firstChild ? parent.insertBefore(child, firstChild)
            : parent.appendChild(child);
    }
}

// function append(parent, child) {
//     if (checkParentAvailable(parent, child)) {
//         parent.appendChild(child);
//     }
// }

function remove(parent, child) {
    if (child && parent && child.parentNode === parent) {
        parent.removeChild(child);
    }
}

function getTextSvgElement(displayable) {
    return displayable.__textSvgEl;
}

function getSvgElement(displayable) {
    return displayable.__svgEl;
}

/**
 * @alias module:zrender/svg/Painter
 * @constructor
 * @param {HTMLElement} root 绘图容器
 * @param {module:zrender/Storage} storage
 * @param {Object} opts
 */
var SVGPainter = function (root, storage, opts, zrId) {

    this.root = root;
    this.storage = storage;
    this._opts = opts = util.extend({}, opts || {});

    var svgRoot = createElement('svg');
    svgRoot.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    svgRoot.setAttribute('version', '1.1');
    svgRoot.setAttribute('baseProfile', 'full');
    svgRoot.style.cssText = 'user-select:none;position:absolute;left:0;top:0;';

    this.gradientManager = new GradientManager(zrId, svgRoot);
    this.clipPathManager = new ClippathManager(zrId, svgRoot);
    this.shadowManager = new ShadowManager(zrId, svgRoot);

    var viewport = document.createElement('div');
    viewport.style.cssText = 'overflow:hidden;position:relative';

    this._svgRoot = svgRoot;
    this._viewport = viewport;

    root.appendChild(viewport);
    viewport.appendChild(svgRoot);

    this.resize(opts.width, opts.height);

    this._visibleList = [];
};

SVGPainter.prototype = {

    constructor: SVGPainter,

    getType: function () {
        return 'svg';
    },

    getViewportRoot: function () {
        return this._viewport;
    },

    getViewportRootOffset: function () {
        var viewportRoot = this.getViewportRoot();
        if (viewportRoot) {
            return {
                offsetLeft: viewportRoot.offsetLeft || 0,
                offsetTop: viewportRoot.offsetTop || 0
            };
        }
    },

    refresh: function () {

        var list = this.storage.getDisplayList(true);

        this._paintList(list);
    },

    setBackgroundColor: function (backgroundColor) {
        // TODO gradient
        this._viewport.style.background = backgroundColor;
    },

    _paintList: function (list) {
        this.gradientManager.markAllUnused();
        this.clipPathManager.markAllUnused();
        this.shadowManager.markAllUnused();

        var svgRoot = this._svgRoot;
        var visibleList = this._visibleList;
        var listLen = list.length;

        var newVisibleList = [];
        var i;
        for (i = 0; i < listLen; i++) {
            var displayable = list[i];
            var svgProxy = getSvgProxy(displayable);
            var svgElement = getSvgElement(displayable)
                || getTextSvgElement(displayable);
            if (!displayable.invisible) {
                if (displayable.__dirty) {
                    svgProxy && svgProxy.brush(displayable);

                    // Update clipPath
                    this.clipPathManager.update(displayable);

                    // Update gradient and shadow
                    if (displayable.style) {
                        this.gradientManager
                            .update(displayable.style.fill);
                        this.gradientManager
                            .update(displayable.style.stroke);

                        this.shadowManager
                            .update(svgElement, displayable);
                    }

                    displayable.__dirty = false;
                }
                newVisibleList.push(displayable);
            }
        }

        var diff = arrayDiff(visibleList, newVisibleList);
        var prevSvgElement;

        // First do remove, in case element moved to the head and do remove
        // after add
        for (i = 0; i < diff.length; i++) {
            var item = diff[i];
            if (item.removed) {
                for (var k = 0; k < item.count; k++) {
                    var displayable = visibleList[item.indices[k]];
                    var svgElement = getSvgElement(displayable);
                    var textSvgElement = getTextSvgElement(displayable);
                    remove(svgRoot, svgElement);
                    remove(svgRoot, textSvgElement);
                }
            }
        }
        for (i = 0; i < diff.length; i++) {
            var item = diff[i];
            if (item.added) {
                for (var k = 0; k < item.count; k++) {
                    var displayable = newVisibleList[item.indices[k]];
                    var svgElement = getSvgElement(displayable);
                    var textSvgElement = getTextSvgElement(displayable);
                    prevSvgElement
                        ? insertAfter(svgRoot, svgElement, prevSvgElement)
                        : prepend(svgRoot, svgElement);
                    if (svgElement) {
                        insertAfter(svgRoot, textSvgElement, svgElement);
                    }
                    else if (prevSvgElement) {
                        insertAfter(
                            svgRoot, textSvgElement, prevSvgElement
                        );
                    }
                    else {
                        prepend(svgRoot, textSvgElement);
                    }
                    // Insert text
                    insertAfter(svgRoot, textSvgElement, svgElement);
                    prevSvgElement = textSvgElement || svgElement
                        || prevSvgElement;

                    // zrender.Text only create textSvgElement.
                    this.gradientManager
                        .addWithoutUpdate(svgElement || textSvgElement, displayable);
                    this.shadowManager
                        .addWithoutUpdate(svgElement || textSvgElement, displayable);
                    this.clipPathManager.markUsed(displayable);
                }
            }
            else if (!item.removed) {
                for (var k = 0; k < item.count; k++) {
                    var displayable = newVisibleList[item.indices[k]];
                    var svgElement = getSvgElement(displayable);
                    var textSvgElement = getTextSvgElement(displayable);

                    var svgElement = getSvgElement(displayable);
                    var textSvgElement = getTextSvgElement(displayable);

                    this.gradientManager.markUsed(displayable);
                    this.gradientManager
                        .addWithoutUpdate(svgElement || textSvgElement, displayable);

                    this.shadowManager.markUsed(displayable);
                    this.shadowManager
                        .addWithoutUpdate(svgElement || textSvgElement, displayable);

                    this.clipPathManager.markUsed(displayable);

                    if (textSvgElement) { // Insert text.
                        insertAfter(svgRoot, textSvgElement, svgElement);
                    }
                    prevSvgElement = svgElement
                        || textSvgElement || prevSvgElement;
                }
            }
        }

        this.gradientManager.removeUnused();
        this.clipPathManager.removeUnused();
        this.shadowManager.removeUnused();

        this._visibleList = newVisibleList;
    },

    _getDefs: function (isForceCreating) {
        var svgRoot = this._svgRoot;
        var defs = this._svgRoot.getElementsByTagName('defs');
        if (defs.length === 0) {
            // Not exist
            if (isForceCreating) {
                var defs = svgRoot.insertBefore(
                    createElement('defs'), // Create new tag
                    svgRoot.firstChild // Insert in the front of svg
                );
                if (!defs.contains) {
                    // IE doesn't support contains method
                    defs.contains = function (el) {
                        var children = defs.children;
                        if (!children) {
                            return false;
                        }
                        for (var i = children.length - 1; i >= 0; --i) {
                            if (children[i] === el) {
                                return true;
                            }
                        }
                        return false;
                    };
                }
                return defs;
            }
            else {
                return null;
            }
        }
        else {
            return defs[0];
        }
    },

    resize: function (width, height) {
        var viewport = this._viewport;
        // FIXME Why ?
        viewport.style.display = 'none';

        // Save input w/h
        var opts = this._opts;
        width != null && (opts.width = width);
        height != null && (opts.height = height);

        width = this._getSize(0);
        height = this._getSize(1);

        viewport.style.display = '';

        if (this._width !== width || this._height !== height) {
            this._width = width;
            this._height = height;

            var viewportStyle = viewport.style;
            viewportStyle.width = width + 'px';
            viewportStyle.height = height + 'px';

            var svgRoot = this._svgRoot;
            // Set width by 'svgRoot.width = width' is invalid
            svgRoot.setAttribute('width', width);
            svgRoot.setAttribute('height', height);
        }
    },

    /**
     * 获取绘图区域宽度
     */
    getWidth: function () {
        return this._width;
    },

    /**
     * 获取绘图区域高度
     */
    getHeight: function () {
        return this._height;
    },

    _getSize: function (whIdx) {
        var opts = this._opts;
        var wh = ['width', 'height'][whIdx];
        var cwh = ['clientWidth', 'clientHeight'][whIdx];
        var plt = ['paddingLeft', 'paddingTop'][whIdx];
        var prb = ['paddingRight', 'paddingBottom'][whIdx];

        if (opts[wh] != null && opts[wh] !== 'auto') {
            return parseFloat(opts[wh]);
        }

        var root = this.root;
        // IE8 does not support getComputedStyle, but it use VML.
        var stl = document.defaultView.getComputedStyle(root);

        return (
            (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))
            - (parseInt10(stl[plt]) || 0)
            - (parseInt10(stl[prb]) || 0)
        ) | 0;
    },

    dispose: function () {
        this.root.innerHTML = '';

        this._svgRoot =
            this._viewport =
            this.storage =
            null;
    },

    clear: function () {
        if (this._viewport) {
            this.root.removeChild(this._viewport);
        }
    },

    pathToDataUrl: function () {
        this.refresh();
        var html = this._svgRoot.outerHTML;
        return 'data:image/svg+xml;charset=UTF-8,' + html;
    }
};

// Not supported methods
function createMethodNotSupport(method) {
    return function () {
        logError('In SVG mode painter not support method "' + method + '"');
    };
}

// Unsuppoted methods
util.each([
    'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer',
    'eachOtherLayer', 'getLayers', 'modLayer', 'delLayer', 'clearLayer',
    'toDataURL', 'pathToImage'
], function (name) {
    SVGPainter.prototype[name] = createMethodNotSupport(name);
});

export default SVGPainter;<commit_msg>fix(svg): download svg image with toolbox<commit_after>/**
 * SVG Painter
 * @module zrender/svg/Painter
 */

import {createElement} from './core';
import * as util from '../core/util';
import logError from '../core/log';
import Path from '../graphic/Path';
import ZImage from '../graphic/Image';
import ZText from '../graphic/Text';
import arrayDiff from '../core/arrayDiff2';
import GradientManager from './helper/GradientManager';
import ClippathManager from './helper/ClippathManager';
import ShadowManager from './helper/ShadowManager';
import {
    path as svgPath,
    image as svgImage,
    text as svgText
} from './graphic';

function parseInt10(val) {
    return parseInt(val, 10);
}

function getSvgProxy(el) {
    if (el instanceof Path) {
        return svgPath;
    }
    else if (el instanceof ZImage) {
        return svgImage;
    }
    else if (el instanceof ZText) {
        return svgText;
    }
    else {
        return svgPath;
    }
}

function checkParentAvailable(parent, child) {
    return child && parent && child.parentNode !== parent;
}

function insertAfter(parent, child, prevSibling) {
    if (checkParentAvailable(parent, child) && prevSibling) {
        var nextSibling = prevSibling.nextSibling;
        nextSibling ? parent.insertBefore(child, nextSibling)
            : parent.appendChild(child);
    }
}

function prepend(parent, child) {
    if (checkParentAvailable(parent, child)) {
        var firstChild = parent.firstChild;
        firstChild ? parent.insertBefore(child, firstChild)
            : parent.appendChild(child);
    }
}

// function append(parent, child) {
//     if (checkParentAvailable(parent, child)) {
//         parent.appendChild(child);
//     }
// }

function remove(parent, child) {
    if (child && parent && child.parentNode === parent) {
        parent.removeChild(child);
    }
}

function getTextSvgElement(displayable) {
    return displayable.__textSvgEl;
}

function getSvgElement(displayable) {
    return displayable.__svgEl;
}

/**
 * @alias module:zrender/svg/Painter
 * @constructor
 * @param {HTMLElement} root 绘图容器
 * @param {module:zrender/Storage} storage
 * @param {Object} opts
 */
var SVGPainter = function (root, storage, opts, zrId) {

    this.root = root;
    this.storage = storage;
    this._opts = opts = util.extend({}, opts || {});

    var svgDom = createElement('svg');
    svgDom.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    svgDom.setAttribute('version', '1.1');
    svgDom.setAttribute('baseProfile', 'full');
    svgDom.style.cssText = 'user-select:none;position:absolute;left:0;top:0;';

    var bgRoot = createElement('g');
    svgDom.appendChild(bgRoot);
    var svgRoot = createElement('g');
    svgDom.appendChild(svgRoot);

    this.gradientManager = new GradientManager(zrId, svgRoot);
    this.clipPathManager = new ClippathManager(zrId, svgRoot);
    this.shadowManager = new ShadowManager(zrId, svgRoot);

    var viewport = document.createElement('div');
    viewport.style.cssText = 'overflow:hidden;position:relative';

    this._svgDom = svgDom;
    this._svgRoot = svgRoot;
    this._backgroundRoot = bgRoot;
    this._viewport = viewport;

    root.appendChild(viewport);
    viewport.appendChild(svgDom);

    this.resize(opts.width, opts.height);

    this._visibleList = [];
};

SVGPainter.prototype = {

    constructor: SVGPainter,

    getType: function () {
        return 'svg';
    },

    getViewportRoot: function () {
        return this._viewport;
    },

    getViewportRootOffset: function () {
        var viewportRoot = this.getViewportRoot();
        if (viewportRoot) {
            return {
                offsetLeft: viewportRoot.offsetLeft || 0,
                offsetTop: viewportRoot.offsetTop || 0
            };
        }
    },

    refresh: function () {

        var list = this.storage.getDisplayList(true);

        this._paintList(list);
    },

    setBackgroundColor: function (backgroundColor) {
        // TODO gradient
        // Insert a bg rect instead of setting background to viewport.
        // Otherwise, the exported SVG don't have background.
        var bgNode = createElement('rect');
        bgNode.setAttribute('width', this.getWidth());
        bgNode.setAttribute('height', this.getHeight());
        bgNode.setAttribute('x', 0);
        bgNode.setAttribute('y', 0);
        bgNode.setAttribute('id', 0);
        bgNode.style.fill = backgroundColor;
        this._backgroundRoot.appendChild(bgNode);
        this._backgroundNode = bgNode;
    },

    _paintList: function (list) {
        this.gradientManager.markAllUnused();
        this.clipPathManager.markAllUnused();
        this.shadowManager.markAllUnused();

        var svgRoot = this._svgRoot;
        var visibleList = this._visibleList;
        var listLen = list.length;

        var newVisibleList = [];
        var i;
        for (i = 0; i < listLen; i++) {
            var displayable = list[i];
            var svgProxy = getSvgProxy(displayable);
            var svgElement = getSvgElement(displayable)
                || getTextSvgElement(displayable);
            if (!displayable.invisible) {
                if (displayable.__dirty) {
                    svgProxy && svgProxy.brush(displayable);

                    // Update clipPath
                    this.clipPathManager.update(displayable);

                    // Update gradient and shadow
                    if (displayable.style) {
                        this.gradientManager
                            .update(displayable.style.fill);
                        this.gradientManager
                            .update(displayable.style.stroke);

                        this.shadowManager
                            .update(svgElement, displayable);
                    }

                    displayable.__dirty = false;
                }
                newVisibleList.push(displayable);
            }
        }

        var diff = arrayDiff(visibleList, newVisibleList);
        var prevSvgElement;

        // First do remove, in case element moved to the head and do remove
        // after add
        for (i = 0; i < diff.length; i++) {
            var item = diff[i];
            if (item.removed) {
                for (var k = 0; k < item.count; k++) {
                    var displayable = visibleList[item.indices[k]];
                    var svgElement = getSvgElement(displayable);
                    var textSvgElement = getTextSvgElement(displayable);
                    remove(svgRoot, svgElement);
                    remove(svgRoot, textSvgElement);
                }
            }
        }
        for (i = 0; i < diff.length; i++) {
            var item = diff[i];
            if (item.added) {
                for (var k = 0; k < item.count; k++) {
                    var displayable = newVisibleList[item.indices[k]];
                    var svgElement = getSvgElement(displayable);
                    var textSvgElement = getTextSvgElement(displayable);
                    prevSvgElement
                        ? insertAfter(svgRoot, svgElement, prevSvgElement)
                        : prepend(svgRoot, svgElement);
                    if (svgElement) {
                        insertAfter(svgRoot, textSvgElement, svgElement);
                    }
                    else if (prevSvgElement) {
                        insertAfter(
                            svgRoot, textSvgElement, prevSvgElement
                        );
                    }
                    else {
                        prepend(svgRoot, textSvgElement);
                    }
                    // Insert text
                    insertAfter(svgRoot, textSvgElement, svgElement);
                    prevSvgElement = textSvgElement || svgElement
                        || prevSvgElement;

                    // zrender.Text only create textSvgElement.
                    this.gradientManager
                        .addWithoutUpdate(svgElement || textSvgElement, displayable);
                    this.shadowManager
                        .addWithoutUpdate(svgElement || textSvgElement, displayable);
                    this.clipPathManager.markUsed(displayable);
                }
            }
            else if (!item.removed) {
                for (var k = 0; k < item.count; k++) {
                    var displayable = newVisibleList[item.indices[k]];
                    var svgElement = getSvgElement(displayable);
                    var textSvgElement = getTextSvgElement(displayable);

                    var svgElement = getSvgElement(displayable);
                    var textSvgElement = getTextSvgElement(displayable);

                    this.gradientManager.markUsed(displayable);
                    this.gradientManager
                        .addWithoutUpdate(svgElement || textSvgElement, displayable);

                    this.shadowManager.markUsed(displayable);
                    this.shadowManager
                        .addWithoutUpdate(svgElement || textSvgElement, displayable);

                    this.clipPathManager.markUsed(displayable);

                    if (textSvgElement) { // Insert text.
                        insertAfter(svgRoot, textSvgElement, svgElement);
                    }
                    prevSvgElement = svgElement
                        || textSvgElement || prevSvgElement;
                }
            }
        }

        this.gradientManager.removeUnused();
        this.clipPathManager.removeUnused();
        this.shadowManager.removeUnused();

        this._visibleList = newVisibleList;
    },

    _getDefs: function (isForceCreating) {
        var svgRoot = this._svgDom;
        var defs = svgRoot.getElementsByTagName('defs');
        if (defs.length === 0) {
            // Not exist
            if (isForceCreating) {
                var defs = svgRoot.insertBefore(
                    createElement('defs'), // Create new tag
                    svgRoot.firstChild // Insert in the front of svg
                );
                if (!defs.contains) {
                    // IE doesn't support contains method
                    defs.contains = function (el) {
                        var children = defs.children;
                        if (!children) {
                            return false;
                        }
                        for (var i = children.length - 1; i >= 0; --i) {
                            if (children[i] === el) {
                                return true;
                            }
                        }
                        return false;
                    };
                }
                return defs;
            }
            else {
                return null;
            }
        }
        else {
            return defs[0];
        }
    },

    resize: function (width, height) {
        var viewport = this._viewport;
        // FIXME Why ?
        viewport.style.display = 'none';

        // Save input w/h
        var opts = this._opts;
        width != null && (opts.width = width);
        height != null && (opts.height = height);

        width = this._getSize(0);
        height = this._getSize(1);

        viewport.style.display = '';

        if (this._width !== width || this._height !== height) {
            this._width = width;
            this._height = height;

            var viewportStyle = viewport.style;
            viewportStyle.width = width + 'px';
            viewportStyle.height = height + 'px';

            var svgRoot = this._svgDom;
            // Set width by 'svgRoot.width = width' is invalid
            svgRoot.setAttribute('width', width);
            svgRoot.setAttribute('height', height);
        }

        if (this._backgroundNode) {
            this._backgroundNode.setAttribute('width', width);
            this._backgroundNode.setAttribute('height', height);
        }
    },

    /**
     * 获取绘图区域宽度
     */
    getWidth: function () {
        return this._width;
    },

    /**
     * 获取绘图区域高度
     */
    getHeight: function () {
        return this._height;
    },

    _getSize: function (whIdx) {
        var opts = this._opts;
        var wh = ['width', 'height'][whIdx];
        var cwh = ['clientWidth', 'clientHeight'][whIdx];
        var plt = ['paddingLeft', 'paddingTop'][whIdx];
        var prb = ['paddingRight', 'paddingBottom'][whIdx];

        if (opts[wh] != null && opts[wh] !== 'auto') {
            return parseFloat(opts[wh]);
        }

        var root = this.root;
        // IE8 does not support getComputedStyle, but it use VML.
        var stl = document.defaultView.getComputedStyle(root);

        return (
            (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))
            - (parseInt10(stl[plt]) || 0)
            - (parseInt10(stl[prb]) || 0)
        ) | 0;
    },

    dispose: function () {
        this.root.innerHTML = '';

        this._svgRoot
            = this._backgroundRoot
            = this._svgDom
            = this._backgroundNode
            = this._viewport
            = this.storage
            = null;
    },

    clear: function () {
        if (this._viewport) {
            this.root.removeChild(this._viewport);
        }
    },

    pathToDataUrl: function () {
        this.refresh();
        var html = encodeURIComponent(this._svgDom.outerHTML.replace(/></g, '>\n\r<'));
        return 'data:image/svg+xml;charset=UTF-8,' + html;
    }
};

// Not supported methods
function createMethodNotSupport(method) {
    return function () {
        logError('In SVG mode painter not support method "' + method + '"');
    };
}

// Unsuppoted methods
util.each([
    'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer',
    'eachOtherLayer', 'getLayers', 'modLayer', 'delLayer', 'clearLayer',
    'toDataURL', 'pathToImage'
], function (name) {
    SVGPainter.prototype[name] = createMethodNotSupport(name);
});

export default SVGPainter;<|endoftext|>


<commit_before>(function() {
    function hsl(h, s, l) {
        return kity.Color.createHSL(h, s, l);
    }

    function generate(h) {
        var textSelectionColor = kity.Color.parse('rgb(27,171,255)');
        return {
            'background': '#fbfbfb',

            'root-color': 'white',
            'root-background': hsl(h, 37, 60),
            'root-stroke': hsl(h, 37, 60),
            'root-font-size': 16,
            'root-padding': [12, 24],
            'root-margin': [30, 100],
            'root-radius': 5,
            'root-space': 10,
            'root-text-selection-color': textSelectionColor.set('h', h).set('l', 20),

            'main-color': 'black',
            'main-background': hsl(h, 33, 95),
            'main-stroke': hsl(h, 37, 60),
            'main-stroke-width': 1,
            'main-font-size': 16,
            'main-padding': [6, 20],
            'main-margin': 20,
            'main-radius': 3,
            'main-space': 5,

            'sub-color': 'black',
            'sub-background': 'none',
            'sub-stroke': 'none',
            'sub-font-size': 12,
            'sub-padding': [5, 10],
            'sub-margin': [15, 20],
            'sub-tree-margin': 30,
            'sub-radius': 5,
            'sub-space': 5,

            'connect-color': hsl(h, 37, 60),
            'connect-width': 1,
            'connect-radius': 5,

            'selected-stroke': hsl(h, 26, 30),
            'selected-stroke-width': '3',

            'marquee-background': hsl(h, 100, 80).set('a', 0.1),
            'marquee-stroke': hsl(h, 37, 60),

            'drop-hint-color': hsl(h, 26, 35),
            'drop-hint-width': 5,

            'order-hint-area-color': hsl(h, 100, 95).set('a', 0.5),
            'order-hint-path-color': hsl(h, 100, 25),
            'order-hint-path-width': 1,

            'text-selection-color': textSelectionColor.set('h', h)
        };
    }

    var plans = {
        red: 0,
        soil: 25,
        green: 122,
        blue: 204,
        purple: 246,
        pink: 334
    };

    for (var name in plans) {
        KityMinder.registerTheme('fresh-' + name, generate(plans[name]));
    }

})();<commit_msg>配置光标颜色<commit_after>(function() {
    function hsl(h, s, l) {
        return kity.Color.createHSL(h, s, l);
    }

    function generate(h) {
        return {
            'background': '#fbfbfb',

            'root-color': 'white',
            'root-background': hsl(h, 37, 60),
            'root-stroke': hsl(h, 37, 60),
            'root-font-size': 16,
            'root-padding': [12, 24],
            'root-margin': [30, 100],
            'root-radius': 5,
            'root-space': 10,

            'main-color': 'black',
            'main-background': hsl(h, 33, 95),
            'main-stroke': hsl(h, 37, 60),
            'main-stroke-width': 1,
            'main-font-size': 14,
            'main-padding': [6, 20],
            'main-margin': 20,
            'main-radius': 3,
            'main-space': 5,

            'sub-color': 'black',
            'sub-background': 'none',
            'sub-stroke': 'none',
            'sub-font-size': 12,
            'sub-padding': [5, 10],
            'sub-margin': [15, 20],
            'sub-tree-margin': 30,
            'sub-radius': 5,
            'sub-space': 5,

            'connect-color': hsl(h, 37, 60),
            'connect-width': 1,
            'connect-radius': 5,

            'selected-stroke': hsl(h, 26, 30),
            'selected-stroke-width': '3',

            'marquee-background': hsl(h, 100, 80).set('a', 0.1),
            'marquee-stroke': hsl(h, 37, 60),

            'drop-hint-color': hsl(h, 26, 35),
            'drop-hint-width': 5,

            'order-hint-area-color': hsl(h, 100, 95).set('a', 0.5),
            'order-hint-path-color': hsl(h, 100, 25),
            'order-hint-path-width': 1,

            'text-selection-color': hsl(h, 100, 20)
        };
    }

    var plans = {
        red: 0,
        soil: 25,
        green: 122,
        blue: 204,
        purple: 246,
        pink: 334
    };

    for (var name in plans) {
        KityMinder.registerTheme('fresh-' + name, generate(plans[name]));
    }

})();<|endoftext|>


<commit_before>function Node (id, generateProposalId) { // :: Int -> (Int) -> Node
  this.id = id
  this.acceptors = {}
  this.proposal = null
  this.value = null
  this.stateLog = {}
  this.roles = []
  this.quorum = null
  this.generateProposalId = generateProposalId
}

function Cluster (nodes) { // :: [Node] -> Cluster
  this.nodes = nodes
  this.learners = []
  this.acceptors = []
  this.proposers = []
  if (nodes) {
    nodes.ForEach(function (node, _, __) {
     if (node.roles.indexOf('Learner') > -1) {
      this.learners.push(node.id)
     } else if (nodes.roles.indexOf('Acceptor') > -1) {
      this.acceptors.push(node.id)
     } else if (nodes.roles.indexOf('Proposer') > -1) {
      this.proposers.push(node.id)
     }
    })
  }
}

function initializeProposer (node, cluster, initProposal) { // :: Node -> Cluster -> [Char] ->
  node.roles.push('Proposer')
  node.proposalId = null
  node.lastId = null
  node.promises = null
  node.nextProposalNum = 1
  node.setProposal = function (proposal) {
    if (node.proposal == null) {
      node.proposal = proposal
    }
  }
  if (initProposal) { node.setProposal(initProposal) }

  node.prepare = function () {
    node.promises = []
    node.nextProposalNum += 1
  }

  node.receivePromise = function (from, proposalId, lastAcceptedId, lastValue) {
    if (proposalId != node.proposalId || (node.promises.indexOf(from) > -1)) {
      return
    }

    node.promises.push(from)

    if  (lastAcceptedId > node.lastId) {
      node.lastId = last_acceptedId
      if (lastValue) { node.proposal = lastValue }
    }

    if (node.promises.length == node.quorom) {
      if (node.proposal) {
        //send accept request
      }
    }
  }

  // Needs to parse cluster information/join cluster, alert acceptors

}

function initializeAcceptor (node, cluster) { // :: Node -> Cluster ->
  node.roles.push('Acceptor')
  // Sync stateLog with acceptors in cluster
  node.promisedId = null
  node.acceptedId = null

  node.receivePrepare = function (from, proposalId) {
    if (proposalID == node.promisedId) {
      // send prepare message to other acceptors
    } else if (proposalId > node.promisedId) {
      node.promisedId = proposalId
      // send prepare
    }
  }

  node.receiveAcceptRequest = function (from, proposalId, proposal) {
    if (proposalId >= node.promisedId) {
      node.promisedId = proposalId
      node.acceptedId = proposalId
      node.value = proposal
      // alert other nodes that a value is accepted
    }
  }
}

function initializeLearner (node, cluster) { // :: Node -> Cluster ->
  node.finalValue = null
  node.proposals = {}
  node.roles.push('Learner')
  node.receiveAccept = function (from, proposalId, acceptedValue) {
    if (node.finalValue != null) {
      return
    }

    var last = node.acceptors[from]

    if (last > proposalId) { return }

    node.acceptors[from] = proposalId

    if (last) {
      oldProposal = node.proposals[last]
    }
  }
}
<commit_msg>Complete Learner implementation.<commit_after>function Node (id, generateProposalId) { // :: Int -> (Int) -> Node
  this.id = id
  this.acceptors = {} // Address/ID -> last proposal
  this.proposal = null
  this.value = null
  this.stateLog = {}
  this.roles = []
  this.quorum = null
  this.generateProposalId = generateProposalId
}

function Cluster (nodes) { // :: [Node] -> Cluster
  this.nodes = nodes
  this.learners = []
  this.acceptors = []
  this.proposers = []
  if (nodes) {
    nodes.ForEach(function (node, _, __) {
     if (node.roles.indexOf('Learner') > -1) {
      this.learners.push(node.id)
     } else if (nodes.roles.indexOf('Acceptor') > -1) {
      this.acceptors.push(node.id)
     } else if (nodes.roles.indexOf('Proposer') > -1) {
      this.proposers.push(node.id)
     }
    })
  }
}

function initializeProposer (node, cluster, initProposal) { // :: Node -> Cluster -> [Char] ->
  node.roles.push('Proposer')
  node.proposalId = null
  node.lastId = null
  node.promises = null
  node.nextProposalNum = 1
  node.setProposal = function (proposal) {
    if (node.proposal == null) {
      node.proposal = proposal
    }
  }
  if (initProposal) { node.setProposal(initProposal) }

  node.prepare = function () {
    node.promises = []
    node.nextProposalNum += 1
  }

  node.receivePromise = function (from, proposalId, lastAcceptedId, lastValue) {
    if (proposalId != node.proposalId || (node.promises.indexOf(from) > -1)) {
      return
    }

    node.promises.push(from)

    if  (lastAcceptedId > node.lastId) {
      node.lastId = last_acceptedId
      if (lastValue) { node.proposal = lastValue }
    }

    if (node.promises.length == node.quorom) {
      if (node.proposal) {
        //send accept request
      }
    }
  }

  // Needs to parse cluster information/join cluster, alert acceptors

}

function initializeAcceptor (node, cluster) { // :: Node -> Cluster ->
  node.roles.push('Acceptor')
  // Sync stateLog with acceptors in cluster
  node.promisedId = null
  node.acceptedId = null

  node.receivePrepare = function (from, proposalId) {
    if (proposalID == node.promisedId) {
      // send prepare message to other acceptors
    } else if (proposalId > node.promisedId) {
      node.promisedId = proposalId
      // send prepare
    }
  }

  node.receiveAcceptRequest = function (from, proposalId, proposal) {
    if (proposalId >= node.promisedId) {
      node.promisedId = proposalId
      node.acceptedId = proposalId
      node.value = proposal
      // alert other nodes that a value is accepted
    }
  }
}

function initializeLearner (node, cluster) { // :: Node -> Cluster ->
  node.roles.push('Learner')
  node.finalValue = null
  node.finalProposalId = null

  node.proposals = {} // proposal ID -> [accept count, retain count, value]

  node.receiveAccept = function (from, proposalId, acceptedValue) {
    if (node.finalValue != null) {
      return
    }

    var last = node.acceptors[from]

    if (last > proposalId) { return }

    node.acceptors[from] = proposalId

    if (last) {
      oldProposal = node.proposals[last]
      oldProposal[1] -= 1
      if (oldProposal[1] == 0) { delete node.propoals[last] }
    }

    if (node.proposals[proposalId] == null) {
      node.proposals[proposalId] = [1, 1, acceptedValue]
    }

    if (node.proposals[proposalId][0] == node.quorum) { // round over
      node.finalValue = acceptedValue
      node.finalProposalId = proposalId
    }
  }
}
<|endoftext|>