<commit_before>/**
 * @author: Meki Cheraoui, Jeff Thompson
 * See COPYING for copyright and distribution information.
 * This class represents a Name as an array of components where each is a byte array.
 */

/*
 * Create a new Name from _components.
 * If _components is a string, parse it as a URI.  Otherwise it is an array of components
 * where each is a string, byte array, ArrayBuffer or Uint8Array.
 * Convert and store as an array of Uint8Array.
 * If a component is a string, encode as utf8.
 */
var Name = function Name(_components){
	if( typeof _components == 'string') {
		if(LOG>3)console.log('Content Name String '+_components);
		this.components = Name.createNameArray(_components);
	}
	else if(typeof _components === 'object'){
		if(LOG>4)console.log('Content Name Array '+_components);
		this.components = [];
        for (var i = 0; i < _components.length; ++i)
            this.add(_components[i]);
	}
	else if(_components==null)
		this.components =[];
	else
		if(LOG>1)console.log("NO CONTENT NAME GIVEN");
};

Name.prototype.getName = function() {
    return this.to_uri();
};

/* Parse name as a URI and return an array of Uint8Array components.
 *
 */
Name.createNameArray = function(name) {
    name = name.trim();
    if (name.length <= 0)
        return [];

    var iColon = name.indexOf(':');
    if (iColon >= 0) {
        // Make sure the colon came before a '/'.
        var iFirstSlash = name.indexOf('/');
        if (iFirstSlash < 0 || iColon < iFirstSlash)
            // Omit the leading protocol such as ndn:
            name = name.substr(iColon + 1, name.length - iColon - 1).trim();
    }

  	if (name[0] == '/') {
        if (name.length >= 2 && name[1] == '/') {
            // Strip the authority following "//".
            var iAfterAuthority = name.indexOf('/', 2);
            if (iAfterAuthority < 0)
                // Unusual case: there was only an authority.
                return [];
            else
                name = name.substr(iAfterAuthority + 1, name.length - iAfterAuthority - 1).trim();
        }
        else
            name = name.substr(1, name.length - 1).trim();
    }

	var array = name.split('/');

    // Unescape the components.
    for (var i = 0; i < array.length; ++i) {
        var component = unescape(array[i].trim());

        if (component.match(/[^.]/) == null) {
            // Special case for component of only periods.
            if (component.length <= 2) {
                // Zero, one or two periods is illegal.  Ignore this componenent to be
                //   consistent with the C implmentation.
                // This also gets rid of a trailing '/'.
                array.splice(i, 1);
                --i;
                continue;
            }
            else
                // Remove 3 periods.
                array[i] = component.substr(3, component.length - 3);
        }
        else
            array[i] = component;

        // Change the component to Uint8Array now.
        array[i] = DataUtils.toNumbersFromString(array[i]);
    }

	return array;
}

Name.prototype.from_ccnb = function(/*XMLDecoder*/ decoder)  {
		decoder.readStartElement(this.getElementLabel());

		this.components = new Array(); //new ArrayList<byte []>();

		while (decoder.peekStartElement(CCNProtocolDTags.Component)) {
			this.add(decoder.readBinaryElement(CCNProtocolDTags.Component));
		}

		decoder.readEndElement();
};

Name.prototype.to_ccnb = function(/*XMLEncoder*/ encoder)  {

		if( this.components ==null )
			throw new Error("CANNOT ENCODE EMPTY CONTENT NAME");

		encoder.writeStartElement(this.getElementLabel());
		var count = this.components.length;
		for (var i=0; i < count; i++) {
			encoder.writeElement(CCNProtocolDTags.Component, this.components[i]);
		}
		encoder.writeEndElement();
};

Name.prototype.getElementLabel = function(){
	return CCNProtocolDTags.Name;
};

/*
 * component is a string, byte array, ArrayBuffer or Uint8Array.
 * Convert to Uint8Array and add to this Name.
 * If a component is a string, encode as utf8.
 * Return the converted value.
 */
Name.prototype.add = function(component){
    var result;
    if(typeof component == 'string')
        result = DataUtils.stringToUtf8Array(component);
	else if(typeof component == 'object' && component instanceof Uint8Array)
        result = new Uint8Array(component);
	else if(typeof component == 'object' && component instanceof ArrayBuffer) {
        // Make a copy.  Don't use ArrayBuffer.slice since it isn't always supported.
        result = new Uint8Array(new ArrayBuffer(component.byteLength));
        result.set(new Uint8Array(component));
    }
	else if(typeof component == 'object')
        // Assume component is a byte array.  We can't check instanceof Array because
        //   this doesn't work in JavaScript if the array comes from a different module.
        result = new Uint8Array(component);
	else
		throw new Error("Cannot add Name element at index " + this.components.length +
            ": Invalid type");

	return this.components.push(result);
};

// Return the escaped name string according to "CCNx URI Scheme".
Name.prototype.to_uri = function() {
	var result = "";

	for(var i = 0; i < this.components.length; ++i)
		result += "/"+ Name.toEscapedString(this.components[i]);

	return result;
};

/*
 * Return a new Name with the first nComponents components of this Name.
 */
Name.prototype.getPrefix = function(nComponents) {
    return new Name(this.components.slice(0, nComponents));
}

/*
 * Return a new ArrayBuffer of the component at i.
 */
Name.prototype.getComponent = function(i) {
    var result = new ArrayBuffer(this.components[i].length);
    new Uint8Array(result).set(this.components[i]);
    return result;
}

/*
 * The "file name" in a name is the last component that isn't blank and doesn't start with one of the
 *   special marker octets (for version, etc.).  Return the index in this.components of
 *   the file name, or -1 if not found.
 */
Name.prototype.indexOfFileName = function() {
    for (var i = this.components.length - 1; i >= 0; --i) {
        var component = this.components[i];
        if (component.length <= 0)
            continue;

        if (component[0] == 0 || component[0] == 0xC0 || component[0] == 0xC1 ||
            (component[0] >= 0xF5 && component[0] <= 0xFF))
            continue;

        return i;
    }

    return -1;
}

/*
 * Find the last component in name that has a ContentDigest and return the digest value as Uint8Array,
 *   or null if not found.
 * A ContentDigest component is Name.ContentDigestPrefix + 32 bytes + Name.ContentDigestSuffix.
 */
Name.prototype.getContentDigestValue = function() {
    var digestComponentLength = Name.ContentDigestPrefix.length + 32 + Name.ContentDigestSuffix.length;
    for (var i = this.components.length - 1; i >= 0; --i) {
        // Check for the correct length and equal ContentDigestPrefix and ContentDigestSuffix.
        if (this.components[i].length == digestComponentLength &&
            DataUtils.arraysEqual(this.components[i].subarray(0, Name.ContentDigestPrefix.length),
                                  Name.ContentDigestPrefix) &&
            DataUtils.arraysEqual(this.components[i].subarray
               (this.components[i].length - Name.ContentDigestSuffix.length, this.components[i].length),
                                  Name.ContentDigestSuffix))
           return this.components[i].subarray
               (Name.ContentDigestPrefix.length, Name.ContentDigestPrefix.length + 32);
    }

    return null;
}

// Meta GUID "%C1.M.G%C1" + ContentDigest with a 32 byte BLOB.
Name.ContentDigestPrefix = new Uint8Array([0xc1, 0x2e, 0x4d, 0x2e, 0x47, 0xc1, 0x01, 0xaa, 0x02, 0x85]);
Name.ContentDigestSuffix = new Uint8Array([0x00]);

/**
 * Return component as an escaped string according to "CCNx URI Scheme".
 * We can't use encodeURIComponent because that doesn't encode all the characters we want to.
 */
Name.toEscapedString = function(component) {
    var result = "";
    var gotNonDot = false;
    for (var i = 0; i < component.length; ++i) {
        if (component[i] != 0x2e) {
            gotNonDot = true;
            break;
        }
    }
    if (!gotNonDot) {
        // Special case for component of zero or more periods.  Add 3 periods.
        result = "...";
        for (var i = 0; i < component.length; ++i)
            result += ".";
    }
    else {
        for (var i = 0; i < component.length; ++i) {
            var value = component[i];
            // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
            if (value >= 0x30 && value <= 0x39 || value >= 0x41 && value <= 0x5a ||
                value >= 0x61 && value <= 0x7a || value == 0x2b || value == 0x2d ||
                value == 0x2e || value == 0x5f)
                result += String.fromCharCode(value);
            else
                result += "%" + (value < 16 ? "0" : "") + value.toString(16).toUpperCase();
        }
    }
    return result;
};
<commit_msg>Added equalsName.<commit_after>/**
 * @author: Meki Cheraoui, Jeff Thompson
 * See COPYING for copyright and distribution information.
 * This class represents a Name as an array of components where each is a byte array.
 */

/*
 * Create a new Name from _components.
 * If _components is a string, parse it as a URI.  Otherwise it is an array of components
 * where each is a string, byte array, ArrayBuffer or Uint8Array.
 * Convert and store as an array of Uint8Array.
 * If a component is a string, encode as utf8.
 */
var Name = function Name(_components){
	if( typeof _components == 'string') {
		if(LOG>3)console.log('Content Name String '+_components);
		this.components = Name.createNameArray(_components);
	}
	else if(typeof _components === 'object'){
		if(LOG>4)console.log('Content Name Array '+_components);
		this.components = [];
        for (var i = 0; i < _components.length; ++i)
            this.add(_components[i]);
	}
	else if(_components==null)
		this.components =[];
	else
		if(LOG>1)console.log("NO CONTENT NAME GIVEN");
};

Name.prototype.getName = function() {
    return this.to_uri();
};

/* Parse name as a URI and return an array of Uint8Array components.
 *
 */
Name.createNameArray = function(name) {
    name = name.trim();
    if (name.length <= 0)
        return [];

    var iColon = name.indexOf(':');
    if (iColon >= 0) {
        // Make sure the colon came before a '/'.
        var iFirstSlash = name.indexOf('/');
        if (iFirstSlash < 0 || iColon < iFirstSlash)
            // Omit the leading protocol such as ndn:
            name = name.substr(iColon + 1, name.length - iColon - 1).trim();
    }

  	if (name[0] == '/') {
        if (name.length >= 2 && name[1] == '/') {
            // Strip the authority following "//".
            var iAfterAuthority = name.indexOf('/', 2);
            if (iAfterAuthority < 0)
                // Unusual case: there was only an authority.
                return [];
            else
                name = name.substr(iAfterAuthority + 1, name.length - iAfterAuthority - 1).trim();
        }
        else
            name = name.substr(1, name.length - 1).trim();
    }

	var array = name.split('/');

    // Unescape the components.
    for (var i = 0; i < array.length; ++i) {
        var component = unescape(array[i].trim());

        if (component.match(/[^.]/) == null) {
            // Special case for component of only periods.
            if (component.length <= 2) {
                // Zero, one or two periods is illegal.  Ignore this componenent to be
                //   consistent with the C implmentation.
                // This also gets rid of a trailing '/'.
                array.splice(i, 1);
                --i;
                continue;
            }
            else
                // Remove 3 periods.
                array[i] = component.substr(3, component.length - 3);
        }
        else
            array[i] = component;

        // Change the component to Uint8Array now.
        array[i] = DataUtils.toNumbersFromString(array[i]);
    }

	return array;
}

Name.prototype.from_ccnb = function(/*XMLDecoder*/ decoder)  {
		decoder.readStartElement(this.getElementLabel());

		this.components = new Array(); //new ArrayList<byte []>();

		while (decoder.peekStartElement(CCNProtocolDTags.Component)) {
			this.add(decoder.readBinaryElement(CCNProtocolDTags.Component));
		}

		decoder.readEndElement();
};

Name.prototype.to_ccnb = function(/*XMLEncoder*/ encoder)  {

		if( this.components ==null )
			throw new Error("CANNOT ENCODE EMPTY CONTENT NAME");

		encoder.writeStartElement(this.getElementLabel());
		var count = this.components.length;
		for (var i=0; i < count; i++) {
			encoder.writeElement(CCNProtocolDTags.Component, this.components[i]);
		}
		encoder.writeEndElement();
};

Name.prototype.getElementLabel = function(){
	return CCNProtocolDTags.Name;
};

/*
 * component is a string, byte array, ArrayBuffer or Uint8Array.
 * Convert to Uint8Array and add to this Name.
 * If a component is a string, encode as utf8.
 * Return the converted value.
 */
Name.prototype.add = function(component){
    var result;
    if(typeof component == 'string')
        result = DataUtils.stringToUtf8Array(component);
	else if(typeof component == 'object' && component instanceof Uint8Array)
        result = new Uint8Array(component);
	else if(typeof component == 'object' && component instanceof ArrayBuffer) {
        // Make a copy.  Don't use ArrayBuffer.slice since it isn't always supported.
        result = new Uint8Array(new ArrayBuffer(component.byteLength));
        result.set(new Uint8Array(component));
    }
	else if(typeof component == 'object')
        // Assume component is a byte array.  We can't check instanceof Array because
        //   this doesn't work in JavaScript if the array comes from a different module.
        result = new Uint8Array(component);
	else
		throw new Error("Cannot add Name element at index " + this.components.length +
            ": Invalid type");

	return this.components.push(result);
};

// Return the escaped name string according to "CCNx URI Scheme".
Name.prototype.to_uri = function() {
	var result = "";

	for(var i = 0; i < this.components.length; ++i)
		result += "/"+ Name.toEscapedString(this.components[i]);

	return result;
};

/*
 * Return a new Name with the first nComponents components of this Name.
 */
Name.prototype.getPrefix = function(nComponents) {
    return new Name(this.components.slice(0, nComponents));
}

/*
 * Return a new ArrayBuffer of the component at i.
 */
Name.prototype.getComponent = function(i) {
    var result = new ArrayBuffer(this.components[i].length);
    new Uint8Array(result).set(this.components[i]);
    return result;
}

/*
 * The "file name" in a name is the last component that isn't blank and doesn't start with one of the
 *   special marker octets (for version, etc.).  Return the index in this.components of
 *   the file name, or -1 if not found.
 */
Name.prototype.indexOfFileName = function() {
    for (var i = this.components.length - 1; i >= 0; --i) {
        var component = this.components[i];
        if (component.length <= 0)
            continue;

        if (component[0] == 0 || component[0] == 0xC0 || component[0] == 0xC1 ||
            (component[0] >= 0xF5 && component[0] <= 0xFF))
            continue;

        return i;
    }

    return -1;
}

/*
 * Return true if this Name has the same components as name.
 */
Name.prototype.equalsName = function(name) {
    if (this.components.length != name.components.length)
        return false;

    // Start from the last component because they are more likely to differ.
    for (var i = this.components.length - 1; i >= 0; --i) {
        if (!DataUtils.arraysEqual(this.components[i], name.components[i]))
            return false;
    }

    return true;
}

/*
 * Find the last component in name that has a ContentDigest and return the digest value as Uint8Array,
 *   or null if not found.
 * A ContentDigest component is Name.ContentDigestPrefix + 32 bytes + Name.ContentDigestSuffix.
 */
Name.prototype.getContentDigestValue = function() {
    var digestComponentLength = Name.ContentDigestPrefix.length + 32 + Name.ContentDigestSuffix.length;
    for (var i = this.components.length - 1; i >= 0; --i) {
        // Check for the correct length and equal ContentDigestPrefix and ContentDigestSuffix.
        if (this.components[i].length == digestComponentLength &&
            DataUtils.arraysEqual(this.components[i].subarray(0, Name.ContentDigestPrefix.length),
                                  Name.ContentDigestPrefix) &&
            DataUtils.arraysEqual(this.components[i].subarray
               (this.components[i].length - Name.ContentDigestSuffix.length, this.components[i].length),
                                  Name.ContentDigestSuffix))
           return this.components[i].subarray
               (Name.ContentDigestPrefix.length, Name.ContentDigestPrefix.length + 32);
    }

    return null;
}

// Meta GUID "%C1.M.G%C1" + ContentDigest with a 32 byte BLOB.
Name.ContentDigestPrefix = new Uint8Array([0xc1, 0x2e, 0x4d, 0x2e, 0x47, 0xc1, 0x01, 0xaa, 0x02, 0x85]);
Name.ContentDigestSuffix = new Uint8Array([0x00]);

/**
 * Return component as an escaped string according to "CCNx URI Scheme".
 * We can't use encodeURIComponent because that doesn't encode all the characters we want to.
 */
Name.toEscapedString = function(component) {
    var result = "";
    var gotNonDot = false;
    for (var i = 0; i < component.length; ++i) {
        if (component[i] != 0x2e) {
            gotNonDot = true;
            break;
        }
    }
    if (!gotNonDot) {
        // Special case for component of zero or more periods.  Add 3 periods.
        result = "...";
        for (var i = 0; i < component.length; ++i)
            result += ".";
    }
    else {
        for (var i = 0; i < component.length; ++i) {
            var value = component[i];
            // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
            if (value >= 0x30 && value <= 0x39 || value >= 0x41 && value <= 0x5a ||
                value >= 0x61 && value <= 0x7a || value == 0x2b || value == 0x2d ||
                value == 0x2e || value == 0x5f)
                result += String.fromCharCode(value);
            else
                result += "%" + (value < 16 ? "0" : "") + value.toString(16).toUpperCase();
        }
    }
    return result;
};
<|endoftext|>


<commit_before>/**
 * Creates a DOM input element of type submit, and attaches event listener to
 * it.
 * @param {string} action - The action for the post form.
 * @param {string} value - The value of the button.
 * @return {Node} An input DOM element.
 */
function createInputSubmit(action, value) {
  let input = document.createElement('input');
  input.type = 'submit';
  input.formAction = action;
  input.value = value;
  addEvent(input, 'click', function(e) {
    let el = e.currentTarget;
    el.form.action = el.formAction;
  });
  return input;
}

/**
 * Creates a form with a textarea for users to edit a comment.
 * @param {Node} commentNode - A node containing a comment.
 * @return {Node} A DOM form node.
 */
function createCommentForm(commentNode) {
  let form = document.createElement('form');
  form.id = commentNode.id + '-edit';
  form.dataset.id = commentNode.id;
  form.classList.add('row');
  form.method = 'post';
  form.action = commentNode.querySelector('.edit-comment').href;

  let div = document.createElement('div');
  div.classList.add('col-md-8', 'col-centered');

  let textArea = document.createElement('textarea');
  textArea.classList.add('form-control', 'input-lg');
  textArea.name = 'text';
  textArea.value = commentNode.querySelector('p').innerHTML;

  div.appendChild(textArea);
  div.appendChild(createInputSubmit('cancel', 'Cancel'));
  div.appendChild(createInputSubmit('save', 'Save'));
  form.appendChild(div);

  return form;
}

// Hides a comment and displays a form to edit a given comment, bringing focus
// to the form.
function tryEditComment(e) {
  e.preventDefault();
  let comment = document.getElementById(e.currentTarget.dataset.id);
  let form = createCommentForm(comment);
  addEvent(form, 'submit', editComment);
  comment.style.display = 'none';
  comment.insertAdjacentElement('afterend', form);
  form.focus();
}

// Removes the form to edit a comment, the old comment, and adds the modified
// comment.
function refreshComment(data) {
  let form = document.getElementById(data.id + '-edit');
  let oldComment = form.previousElementSibling;
  let comments = oldComment.parentNode;
  comments.removeChild(form);
  if (!oldComment.previousElementSibling) {
    // This comment is first comment
    comments.removeChild(oldComment);
    comments.insertAdjacentHTML('afterbegin', data.comment);
  } else {
    // This is not the first comment
    let sibling = oldComment.previousElementSibling;
    comments.removeChild(oldComment);
    sibling.insertAdjacentHTML('afterend', data.comment);
  }
  let comment = comments.querySelector('#' + data.id);
  addEvent(comment.querySelector('.delete-comment'), 'click', deleteComment);
  addEvent(comment.querySelector('.edit-comment'), 'click', tryEditComment);
}

// Removes the form to edit a comment and re-displays the original comment.
function putCommentBack(form) {
  form.previousElementSibling.style.display = 'block';
  form.parentNode.removeChild(form);
}

function editComment(e) {
  e.preventDefault();
  let form = e.currentTarget;
  if (form.action.includes('cancel')) {
    putCommentBack(form);
    return;
  }
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var data = JSON.parse(this.responseText);
      refreshComment(data);
    }
    // TODO: need to revert changes if there is an error
  };
  xhr.open('POST', '/edit-comment', true);
  let msg = JSON.stringify({
    'id': form.dataset.id,
    'text': form.elements.text.value
  });
  xhr.send(msg);
}

function deleteComment(e) {
  e.preventDefault();
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var data = JSON.parse(this.responseText);
      if (data.id) {
        var el = document.getElementById(data.id);
        el.parentNode.removeChild(el);
      }
    }
  };
  xhr.open('POST', '/delete-comment', true);
  xhr.send(JSON.stringify({'id': e.currentTarget.dataset.id}));
}

function createComment(e) {
  e.preventDefault();
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      let data = JSON.parse(this.responseText);
      let comments = document.querySelector('.blog-comments');
      comments.insertAdjacentHTML('beforeend', data.comment);
      document.querySelector('form').reset();
      let lc = comments.lastChild;
      lc.scrollIntoView();
      addEvent(lc.querySelector('.edit-comment'), 'click', tryEditComment);
      addEvent(lc.querySelector('.delete-comment'), 'click', deleteComment);
    }
  };
  let form = document.querySelector('form');
  xhr.open('POST', form.action, true);
  let msg = JSON.stringify({'text': form.elements.text.value});
  xhr.send(msg);
}

function clickLike(e) {
  e.preventDefault();
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      let response = JSON.parse(this.responseText);
      if (response.add) {
        // Enable red heart and increase likes count
        let cl = document.getElementById('likes-heart').classList;
        cl.add('red-heart');
        cl.remove('normal');
        let el = document.querySelector('.likes-number');
        el.innerText = parseInt(el.innerText, 10) + 1;
      } else if (response.remove) {
        // Disable red heart and decrease likes count
        let cl = document.getElementById('likes-heart').classList;
        cl.remove('red-heart');
        cl.add('normal');
        let el = document.querySelector('.likes-number');
        let count = parseInt(el.innerText, 10);
        // double check that we are not taking number below zero
        if (count) {
          el.innerText = count - 1;
        }
      }
    }
  };
  let href = document.getElementById('like-button').href;
  xhr.open('GET', href, true);
  xhr.send();
}

(function() {
  let delLinks = document.querySelectorAll('.delete-comment');
  for (let i = 0; i < delLinks.length; i++) {
    addEvent(delLinks[i], 'click', deleteComment);
  }

  let editLinks = document.querySelectorAll('.edit-comment');
  for (let i = 0; i < editLinks.length; i++) {
    addEvent(editLinks[i], 'click', tryEditComment);
  }

  let form = document.querySelector('form');
  if (form.action.includes('create-comment')) {
    addEvent(form, 'submit', createComment);
  }

  addEvent(document.getElementById('like-button'), 'click', clickLike);
})();
<commit_msg>Create doc for tryEditComment()<commit_after>/**
 * Creates a DOM input element of type submit, and attaches event listener to
 * it.
 * @param {string} action - The action for the post form.
 * @param {string} value - The value of the button.
 * @return {Node} An input DOM element.
 */
function createInputSubmit(action, value) {
  let input = document.createElement('input');
  input.type = 'submit';
  input.formAction = action;
  input.value = value;
  addEvent(input, 'click', function(e) {
    let el = e.currentTarget;
    el.form.action = el.formAction;
  });
  return input;
}

/**
 * Creates a form with a textarea for users to edit a comment.
 * @param {Node} commentNode - A node containing a comment.
 * @return {Node} A DOM form node.
 */
function createCommentForm(commentNode) {
  let form = document.createElement('form');
  form.id = commentNode.id + '-edit';
  form.dataset.id = commentNode.id;
  form.classList.add('row');
  form.method = 'post';
  form.action = commentNode.querySelector('.edit-comment').href;

  let div = document.createElement('div');
  div.classList.add('col-md-8', 'col-centered');

  let textArea = document.createElement('textarea');
  textArea.classList.add('form-control', 'input-lg');
  textArea.name = 'text';
  textArea.value = commentNode.querySelector('p').innerHTML;

  div.appendChild(textArea);
  div.appendChild(createInputSubmit('cancel', 'Cancel'));
  div.appendChild(createInputSubmit('save', 'Save'));
  form.appendChild(div);

  return form;
}

/**
 * Hides a comment and displays a form to edit the comment.
 * @param {Event} e - An event dispatched to edit a comment.
 */
function tryEditComment(e) {
  e.preventDefault();
  let comment = document.getElementById(e.currentTarget.dataset.id);
  let form = createCommentForm(comment);
  addEvent(form, 'submit', editComment);
  comment.style.display = 'none';
  comment.insertAdjacentElement('afterend', form);
  form.focus();
}

// Removes the form to edit a comment, the old comment, and adds the modified
// comment.
function refreshComment(data) {
  let form = document.getElementById(data.id + '-edit');
  let oldComment = form.previousElementSibling;
  let comments = oldComment.parentNode;
  comments.removeChild(form);
  if (!oldComment.previousElementSibling) {
    // This comment is first comment
    comments.removeChild(oldComment);
    comments.insertAdjacentHTML('afterbegin', data.comment);
  } else {
    // This is not the first comment
    let sibling = oldComment.previousElementSibling;
    comments.removeChild(oldComment);
    sibling.insertAdjacentHTML('afterend', data.comment);
  }
  let comment = comments.querySelector('#' + data.id);
  addEvent(comment.querySelector('.delete-comment'), 'click', deleteComment);
  addEvent(comment.querySelector('.edit-comment'), 'click', tryEditComment);
}

// Removes the form to edit a comment and re-displays the original comment.
function putCommentBack(form) {
  form.previousElementSibling.style.display = 'block';
  form.parentNode.removeChild(form);
}

function editComment(e) {
  e.preventDefault();
  let form = e.currentTarget;
  if (form.action.includes('cancel')) {
    putCommentBack(form);
    return;
  }
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var data = JSON.parse(this.responseText);
      refreshComment(data);
    }
    // TODO: need to revert changes if there is an error
  };
  xhr.open('POST', '/edit-comment', true);
  let msg = JSON.stringify({
    'id': form.dataset.id,
    'text': form.elements.text.value
  });
  xhr.send(msg);
}

function deleteComment(e) {
  e.preventDefault();
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var data = JSON.parse(this.responseText);
      if (data.id) {
        var el = document.getElementById(data.id);
        el.parentNode.removeChild(el);
      }
    }
  };
  xhr.open('POST', '/delete-comment', true);
  xhr.send(JSON.stringify({'id': e.currentTarget.dataset.id}));
}

function createComment(e) {
  e.preventDefault();
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      let data = JSON.parse(this.responseText);
      let comments = document.querySelector('.blog-comments');
      comments.insertAdjacentHTML('beforeend', data.comment);
      document.querySelector('form').reset();
      let lc = comments.lastChild;
      lc.scrollIntoView();
      addEvent(lc.querySelector('.edit-comment'), 'click', tryEditComment);
      addEvent(lc.querySelector('.delete-comment'), 'click', deleteComment);
    }
  };
  let form = document.querySelector('form');
  xhr.open('POST', form.action, true);
  let msg = JSON.stringify({'text': form.elements.text.value});
  xhr.send(msg);
}

function clickLike(e) {
  e.preventDefault();
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      let response = JSON.parse(this.responseText);
      if (response.add) {
        // Enable red heart and increase likes count
        let cl = document.getElementById('likes-heart').classList;
        cl.add('red-heart');
        cl.remove('normal');
        let el = document.querySelector('.likes-number');
        el.innerText = parseInt(el.innerText, 10) + 1;
      } else if (response.remove) {
        // Disable red heart and decrease likes count
        let cl = document.getElementById('likes-heart').classList;
        cl.remove('red-heart');
        cl.add('normal');
        let el = document.querySelector('.likes-number');
        let count = parseInt(el.innerText, 10);
        // double check that we are not taking number below zero
        if (count) {
          el.innerText = count - 1;
        }
      }
    }
  };
  let href = document.getElementById('like-button').href;
  xhr.open('GET', href, true);
  xhr.send();
}

(function() {
  let delLinks = document.querySelectorAll('.delete-comment');
  for (let i = 0; i < delLinks.length; i++) {
    addEvent(delLinks[i], 'click', deleteComment);
  }

  let editLinks = document.querySelectorAll('.edit-comment');
  for (let i = 0; i < editLinks.length; i++) {
    addEvent(editLinks[i], 'click', tryEditComment);
  }

  let form = document.querySelector('form');
  if (form.action.includes('create-comment')) {
    addEvent(form, 'submit', createComment);
  }

  addEvent(document.getElementById('like-button'), 'click', clickLike);
})();
<|endoftext|>


<commit_before>/*
 * Carto pulls in a reference from the `mapnik-reference`
 * module. This file builds indexes from that file for its various
 * options, and provides validation methods for property: value
 * combinations.
 */
(function(tree) {

var _ = require('underscore');
var mapnik_reference = require('mapnik-reference');

tree.Reference = {
    data: mapnik_reference.version.latest
};

tree.Reference.setVersion = function(version) {
    tree.Reference.data = mapnik_reference.version[version];
};

tree.Reference.required_prop_list_cache = {};

tree.Reference.selectors = tree.Reference.selectors || (function() {
    var list = [];
    for (var i in tree.Reference.data.symbolizers) {
        for (var j in tree.Reference.data.symbolizers[i]) {
            if (tree.Reference.data.symbolizers[i][j].hasOwnProperty('css')) {
                list.push(tree.Reference.data.symbolizers[i][j].css);
            }
        }
    }
    return list;
})();

tree.Reference.validSelector = function(selector) {
    return tree.Reference.selectors.indexOf(selector) !== -1;
};

tree.Reference.selectorName = function(selector) {
    for (var i in tree.Reference.data.symbolizers) {
        for (var j in tree.Reference.data.symbolizers[i]) {
            if (selector == tree.Reference.data.symbolizers[i][j].css) {
                return j;
            }
        }
    }
};

tree.Reference.selector = function(selector) {
    for (var i in tree.Reference.data.symbolizers) {
        for (var j in tree.Reference.data.symbolizers[i]) {
            if (selector == tree.Reference.data.symbolizers[i][j].css) {
                return tree.Reference.data.symbolizers[i][j];
            }
        }
    }
};

tree.Reference.symbolizer = function(selector) {
    for (var i in tree.Reference.data.symbolizers) {
        for (var j in tree.Reference.data.symbolizers[i]) {
            if (selector == tree.Reference.data.symbolizers[i][j].css) {
                return i;
            }
        }
    }
};

/*
 * For transform properties and image-filters,
 * mapnik has its own functions.
 */
tree.Reference.mapnikFunction = function(name) {
    var functions = [];
    for (var i in tree.Reference.data.symbolizers) {
        for (var j in tree.Reference.data.symbolizers[i]) {
            if (tree.Reference.data.symbolizers[i][j].type === 'functions') {
                functions = functions.concat(tree.Reference.data.symbolizers[i][j].functions);
            }
        }
    }
    return _.find(functions, function(f) {
        return f[0] === name;
    });
};

tree.Reference.requiredPropertyList = function(symbolizer_name) {
    if (this.required_prop_list_cache[symbolizer_name]) {
        return this.required_prop_list_cache[symbolizer_name];
    }
    var properties = [];
    for (var j in tree.Reference.data.symbolizers[symbolizer_name]) {
        if (tree.Reference.data.symbolizers[symbolizer_name][j].required) {
            properties.push(tree.Reference.data.symbolizers[symbolizer_name][j].css);
        }
    }
    return this.required_prop_list_cache[symbolizer_name] = properties;
};

tree.Reference.requiredProperties = function(symbolizer_name, rules) {
    var req = tree.Reference.requiredPropertyList(symbolizer_name);
    for (var i in req) {
        if (!(req[i] in rules)) {
            return 'Property ' + req[i] + ' required for defining ' +
                symbolizer_name + ' styles.';
        }
    }
};

/**
 * TODO: finish implementation - this is dead code
 */
tree.Reference._validateValue = {
    'font': function(env, value) {
        if (env.validation_data && env.validation_data.fonts) {
            return env.validation_data.fonts.indexOf(value) != -1;
        } else {
            return true;
        }
    }
};

tree.Reference.isFont = function(selector) {
    return tree.Reference.selector(selector).validate == 'font';
};

tree.Reference.validValue = function(env, selector, value) {
    var i, j;
    // TODO: handle in reusable way
    if (!tree.Reference.selector(selector)) {
        return false;
    } else if (value.value[0].is == 'keyword') {
        return tree.Reference
            .selector(selector).type
            .indexOf(value.value[0].value) !== -1;
    } else if (value.value[0].is == 'undefined') {
        // caught earlier in the chain - ignore here so that
        // error is not overridden
        return true;
    } else if (tree.Reference.selector(selector).type == 'numbers') {
        for (i in value.value) {
            if (value.value[i].is !== 'float') {
                return false;
            }
        }
        return true;
    } else if (tree.Reference.selector(selector).type == 'functions') {
        // For backwards compatibility, you can specify a string for `functions`-compatible
        // values, though they will not be validated.
        if (value.value[0].is === 'string') {
            return true;
        } else {
            for (i in value.value) {
                for (j in value.value[i].value) {
                    if (value.value[i].value[j].is !== 'call') {
                        return false;
                    }
                    var f = _.find(tree.Reference
                        .selector(selector).functions, function(x) {
                            return x[0] == value.value[i].value[j].name;
                        });
                    // This filter is unknown
                    if (!f) return false;
                    // The filter has been given an incorrect number of arguments
                    if (f[1] !== value.value[i].value[j].args.length) return false;
                }
            }
            return true;
        }
    } else if (tree.Reference.selector(selector).type == 'expression') {
        return true;
    } else {
        if (tree.Reference.selector(selector).validate) {
            var valid = false;
            for (i = 0; i < value.value.length; i++) {
                if (tree.Reference.selector(selector).type == value.value[i].is &&
                    tree.Reference
                        ._validateValue
                            [tree.Reference.selector(selector).validate]
                            (env, value.value[i].value)) {
                    return true;
                }
            }
            return valid;
        } else {
            return tree.Reference.selector(selector).type == value.value[0].is;
        }
    }
};

})(require('../tree'));
<commit_msg>Throw an exception when an unsupported mapnik version is requested<commit_after>/*
 * Carto pulls in a reference from the `mapnik-reference`
 * module. This file builds indexes from that file for its various
 * options, and provides validation methods for property: value
 * combinations.
 */
(function(tree) {

var _ = require('underscore');
var mapnik_reference = require('mapnik-reference');

tree.Reference = {
    data: mapnik_reference.version.latest
};

tree.Reference.setVersion = function(version) {
    if ( ! mapnik_reference.version.hasOwnProperty(version) ) throw new Error("Unsupported mapnik version " + version);
    tree.Reference.data = mapnik_reference.version[version];
};

tree.Reference.required_prop_list_cache = {};

tree.Reference.selectors = tree.Reference.selectors || (function() {
    var list = [];
    for (var i in tree.Reference.data.symbolizers) {
        for (var j in tree.Reference.data.symbolizers[i]) {
            if (tree.Reference.data.symbolizers[i][j].hasOwnProperty('css')) {
                list.push(tree.Reference.data.symbolizers[i][j].css);
            }
        }
    }
    return list;
})();

tree.Reference.validSelector = function(selector) {
    return tree.Reference.selectors.indexOf(selector) !== -1;
};

tree.Reference.selectorName = function(selector) {
    for (var i in tree.Reference.data.symbolizers) {
        for (var j in tree.Reference.data.symbolizers[i]) {
            if (selector == tree.Reference.data.symbolizers[i][j].css) {
                return j;
            }
        }
    }
};

tree.Reference.selector = function(selector) {
    for (var i in tree.Reference.data.symbolizers) {
        for (var j in tree.Reference.data.symbolizers[i]) {
            if (selector == tree.Reference.data.symbolizers[i][j].css) {
                return tree.Reference.data.symbolizers[i][j];
            }
        }
    }
};

tree.Reference.symbolizer = function(selector) {
    for (var i in tree.Reference.data.symbolizers) {
        for (var j in tree.Reference.data.symbolizers[i]) {
            if (selector == tree.Reference.data.symbolizers[i][j].css) {
                return i;
            }
        }
    }
};

/*
 * For transform properties and image-filters,
 * mapnik has its own functions.
 */
tree.Reference.mapnikFunction = function(name) {
    var functions = [];
    for (var i in tree.Reference.data.symbolizers) {
        for (var j in tree.Reference.data.symbolizers[i]) {
            if (tree.Reference.data.symbolizers[i][j].type === 'functions') {
                functions = functions.concat(tree.Reference.data.symbolizers[i][j].functions);
            }
        }
    }
    return _.find(functions, function(f) {
        return f[0] === name;
    });
};

tree.Reference.requiredPropertyList = function(symbolizer_name) {
    if (this.required_prop_list_cache[symbolizer_name]) {
        return this.required_prop_list_cache[symbolizer_name];
    }
    var properties = [];
    for (var j in tree.Reference.data.symbolizers[symbolizer_name]) {
        if (tree.Reference.data.symbolizers[symbolizer_name][j].required) {
            properties.push(tree.Reference.data.symbolizers[symbolizer_name][j].css);
        }
    }
    return this.required_prop_list_cache[symbolizer_name] = properties;
};

tree.Reference.requiredProperties = function(symbolizer_name, rules) {
    var req = tree.Reference.requiredPropertyList(symbolizer_name);
    for (var i in req) {
        if (!(req[i] in rules)) {
            return 'Property ' + req[i] + ' required for defining ' +
                symbolizer_name + ' styles.';
        }
    }
};

/**
 * TODO: finish implementation - this is dead code
 */
tree.Reference._validateValue = {
    'font': function(env, value) {
        if (env.validation_data && env.validation_data.fonts) {
            return env.validation_data.fonts.indexOf(value) != -1;
        } else {
            return true;
        }
    }
};

tree.Reference.isFont = function(selector) {
    return tree.Reference.selector(selector).validate == 'font';
};

tree.Reference.validValue = function(env, selector, value) {
    var i, j;
    // TODO: handle in reusable way
    if (!tree.Reference.selector(selector)) {
        return false;
    } else if (value.value[0].is == 'keyword') {
        return tree.Reference
            .selector(selector).type
            .indexOf(value.value[0].value) !== -1;
    } else if (value.value[0].is == 'undefined') {
        // caught earlier in the chain - ignore here so that
        // error is not overridden
        return true;
    } else if (tree.Reference.selector(selector).type == 'numbers') {
        for (i in value.value) {
            if (value.value[i].is !== 'float') {
                return false;
            }
        }
        return true;
    } else if (tree.Reference.selector(selector).type == 'functions') {
        // For backwards compatibility, you can specify a string for `functions`-compatible
        // values, though they will not be validated.
        if (value.value[0].is === 'string') {
            return true;
        } else {
            for (i in value.value) {
                for (j in value.value[i].value) {
                    if (value.value[i].value[j].is !== 'call') {
                        return false;
                    }
                    var f = _.find(tree.Reference
                        .selector(selector).functions, function(x) {
                            return x[0] == value.value[i].value[j].name;
                        });
                    // This filter is unknown
                    if (!f) return false;
                    // The filter has been given an incorrect number of arguments
                    if (f[1] !== value.value[i].value[j].args.length) return false;
                }
            }
            return true;
        }
    } else if (tree.Reference.selector(selector).type == 'expression') {
        return true;
    } else {
        if (tree.Reference.selector(selector).validate) {
            var valid = false;
            for (i = 0; i < value.value.length; i++) {
                if (tree.Reference.selector(selector).type == value.value[i].is &&
                    tree.Reference
                        ._validateValue
                            [tree.Reference.selector(selector).validate]
                            (env, value.value[i].value)) {
                    return true;
                }
            }
            return valid;
        } else {
            return tree.Reference.selector(selector).type == value.value[0].is;
        }
    }
};

})(require('../tree'));
<|endoftext|>


<commit_before>/*
 *    boot.js
 *    Sets up game and loads preloader assets
 */

/* Sets a global object to hold all the states of the game */
YINS = {

	/* declare global variables, these will persist across different states */
	score: 0,
	sprite_scale: 3.5,

	/* Declare global colors used throughout the game
	Usage example: YINS.color.purple  */
	color: {
		purple: '#673ab7',
		purple_light: '#b39ddb'
	},

	/* Text styles are available by calling: YINS.text.STYLE
	for example: YINS.text.header  */
	text: {
		title: {
			font: '12vh Courier New',
			fill: '#404040',
			align: 'center'
		},

		subtitle: {
			font: '3vh Courier New',
			fill: '#707070',
			align: 'center',
		},

		header: {
			font: '8vh Arial',
			fill: '#f0f0f0',
			align: 'center'
		}
	}

};

/* Constructor creates the game object */
YINS.Boot = function(game) {
	console.log('%cStarting YINS..', 'color: white; background: #673ab7');
};

YINS.Boot.prototype = {
	preload: function() {
		// Load assets for the preloader state
	},

	create: function() {
		//  Phaser will automatically pause if the browser tab the game is in loses focus.
		//  true disables that; false keeps it
        YINS.game.stage.disableVisibilityChange = false;

		YINS.game.state.start('preloader');
	}
};
<commit_msg>Add extra colors<commit_after>/*
 *    boot.js
 *    Sets up game and loads preloader assets
 */

/* Sets a global object to hold all the states of the game */
YINS = {

	/* declare global variables, these will persist across different states */
	score: 0,
	sprite_scale: 3.5,

	/* Declare global colors used throughout the game
	Usage example: YINS.color.purple  */
	color: {
		purple: '#673ab7',
		purple_light: '#b39ddb',
		green_light: '#c5e1a5',
		white: '#f0f0f0'
	},

	/* Text styles are available by calling: YINS.text.STYLE
	for example: YINS.text.header  */
	text: {
		title: {
			font: '12vh Courier New',
			fill: '#404040',
			align: 'center'
		},

		subtitle: {
			font: '3vh Courier New',
			fill: '#707070',
			align: 'center',
		},

		header: {
			font: '8vh Arial',
			fill: '#f0f0f0',
			align: 'center'
		}
	}

};

/* Constructor creates the game object */
YINS.Boot = function(game) {
	console.log('%cStarting YINS..', 'color: white; background: #673ab7');
};

YINS.Boot.prototype = {
	preload: function() {
		// Load assets for the preloader state
	},

	create: function() {
		//  Phaser will automatically pause if the browser tab the game is in loses focus.
		//  true disables that; false keeps it
        YINS.game.stage.disableVisibilityChange = false;

		YINS.game.state.start('preloader');
	}
};
<|endoftext|>


<commit_before>//import "./playerFeature.js";
//import "./createPlayerController.js";

new function() {

    bt.package(this, {
        name:    "course",
        imports: "miruken.mvc,miruken.ng,bt",
        exports: "CoursesController"
    });

    eval(this.imports);

    const CoursesController = Controller.extend({
        $properties: {
            courses:   [],
            dtOptions: undefined,
            dtColumns: undefined
        },
        $inject: ["DTOptionsBuilder", "DTColumnBuilder", $appContext],
        constructor(optionsBuilder, columnBuilder) {
            this.dtOptions = optionsBuilder.newOptions()
                .withOption('ajax', {
                    url: 'api/courses',
                    type: 'POST'
                })
                .withDataProp('data')
                .withOption('processing', true)
                .withOption('serverSide', true)
                .withPaginationType('full_numbers')
                .withDOM('<"html5buttons"B>lTfgitp')
                .withButtons([
                    {extend: 'copy'},
                    {extend: 'csv'},
                    {extend: 'excel', title: 'ExampleFile'},
                    {extend: 'pdf', title: 'ExampleFile'},
                    {extend: 'print',
                        customize: function (win){
                            $(win.document.body).addClass('white-bg');
                            $(win.document.body).css('font-size', '10px');

                            $(win.document.body).find('table')
                                .addClass('compact')
                                .css('font-size', 'inherit');
                        }
                    }
                ])
                .withLightColumnFilter({
                    '0' : { html: 'input', type: 'text', attr: { class: 'form-control' }},
                    '1' : { html: 'input', type: 'text', attr: { class: 'form-control' }}
                });

            this.dtColumns = [
                columnBuilder.newColumn('name').withTitle('Name'),
                columnBuilder.newColumn('description').withTitle('Description')
            ];
        },
        showCourses() {
            return ViewRegion(this.io).show("app/courses/courses");
        },
        goToCreate() {
            bt.course.CreateCourseController(this.io).next(ctrl => ctrl.showCreateCourse());
        }
    });

    eval(this.exports);

};
<commit_msg>handling row selection<commit_after>new function() {

    bt.package(this, {
        name:    "course",
        imports: "miruken.context,miruken.mvc,miruken.ng,bt",
        exports: "CoursesController"
    });

    eval(this.imports);

    const CoursesController = Controller.extend({
        $properties: {
            courses:   [],
            dtOptions: undefined,
            dtColumns: undefined
        },
        $inject: ["DTOptionsBuilder", "DTColumnBuilder", "$scope", Context],
        constructor(optionsBuilder, columnBuilder, $scope, context) {
            this.$scope = $scope;
            this.context = context;

            this.dtOptions = optionsBuilder.newOptions()
                .withOption('ajax', {
                    url: 'api/courses',
                    type: 'POST'
                })
                .withDataProp('data')
                .withOption('processing', true)
                .withOption('serverSide', true)
                .withPaginationType('full_numbers')
                .withDOM('<"html5buttons"B>lTfgitp')
                .withButtons([
                    {extend: 'copy'},
                    {extend: 'csv'},
                    {extend: 'excel', title: 'ExampleFile'},
                    {extend: 'pdf', title: 'ExampleFile'},
                    {extend: 'print',
                        customize: function (win){
                            $(win.document.body).addClass('white-bg');
                            $(win.document.body).css('font-size', '10px');

                            $(win.document.body).find('table')
                                .addClass('compact')
                                .css('font-size', 'inherit');
                        }
                    }
                ])
                .withLightColumnFilter({
                    '0' : { html: 'input', type: 'text', attr: { class: 'form-control' }},
                    '1' : { html: 'input', type: 'text', attr: { class: 'form-control' }}
                })
                .withOption('rowCallback', this.rowCallback.bind(this));

            this.dtColumns = [
                columnBuilder.newColumn('name').withTitle('Name'),
                columnBuilder.newColumn('description').withTitle('Description')
            ];
        },
        rowCallback(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
            // Unbind first in order to avoid any duplicate handler
            //(see https://github.com/l-lin/angular-datatables/issues/87)
            $('td', nRow).unbind('click');
            $('td', nRow).bind('click', () => {
                this.$scope.$apply(() => {
                    this.rowSelected(aData);
                });
            });
            return nRow;
        },
        showCourses() {
            return ViewRegion(this.io).show("app/courses/courses");
        },
        goToCreate() {
            bt.course.CreateCourseController(this.io).next(ctrl => ctrl.showCreateCourse());
        },
        rowSelected(data) {
           bt.course.CourseController(this.context).next(ctrl => ctrl.showCourse(data));
        }
    });

    eval(this.exports);

};
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2016 Steven Gago

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// Character used to demarcate the end of a frame.
const DELIMITER = "\u0000";

// The maximum length of a section that can be encoded in COBS.
const MAX_SECTION_LENGTH = 0x05;

/*
 * Returns a new string in which a character at a specified
 * index is replaced with a given character.
 * @param {string} text - The string to replace the character in.
 * @param {number} index - Index of the character to replace.
 * @param {string} char - The character to replace at the specified index.
 * @return {string} - A string with the replaced character.
 */
function replaceCharAt(text, index, char)
{
  var result = "";

  // Does the index exist in the string?
  if ((index >= 0) && (index < text.length - 1))
  {
    // Replace the character at the specified position
    result = text.substr(0, index) + char + text.substr(index + 1);
  }

  return result;
}

/*
 * Returns a array of strings in which the given text is
 * split first by DELIMITER and then by MAX_SECTION_LENGTH.
 * @param {string} text -
 * @return {array} - Sections of
 */
function getSections(text)
{
  var frames;
  var section;
  var sections = [];
  var startIndex = 0;
  var endIndex = 0;

  if ((typeof text === "string") && (text !== ""))
  {
    // Split the text by our delimiter
    frames = text.split(DELIMITER);

    // For each frame...
    for (var i = 0; i < frames.length; i++)
    {
      // While startIndex is less than length of a given frame...
      while (startIndex < frames[i].length)
      {
        // Get last index
        endIndex = startIndex + MAX_SECTION_LENGTH - 1;

        // Slice off the next section from our frame
        // Up to endIndex or end of the string.
        section = frames[i].slice(startIndex, endIndex);

        // Append the new section to sections array
        sections.push(section);

        // Get the starting index of the next section
        startIndex += section.length;
      }

      if (frames[i].length === 0)
      {
        sections.push("");
      }

      startIndex = 0;  // Reset the index for the next frame
    }
  }

  return sections;
}

/*
 * Returns a COBS-encoded string.
 * @param {string} text - The specified string to encode with COBS.
 * return {string} - A COBS encoded string.
 */
function stuff(text)
{
  var lines;
  var sections;
  var result = "";
  var i = 0;

  if ((typeof text === "string") && (text !== ""))
  {
    // Divide text into an array of strings
    sections = getSections(text);

    // For each section...
    for (var i = 0; i < sections.length; i++)
    {
      // Prepend the section with its length + 1
      sections[i] = String.fromCharCode(sections[i].length + 1) + sections[i];
    }

    sections.push("\u0000");

    // Join all sections together and store as final result
    result = sections.join("");
  }

  return result;
}

/*
 * Returns a COBS-decoded string.
 * @param {string} text - The specified COBS-encoded string to decode.
 * @return {string} - A COBS-decoded string.
 */
function unstuff(text)
{
  var sectionLength = 0;
  var result = "";

  if ((typeof text === "string") && (text !== ""))
  {
    for (var i = 0; i < text.length - 1; i += sectionLength)
    {
      // Get length of the next section
      sectionLength = text.charCodeAt(i);

      result += text.substr(i + 1, sectionLength - 1);

      // Is the next section length less than MAX_SECTION_LENGTH?
      if (sectionLength < MAX_SECTION_LENGTH)
      {
        // DELIMITER goes at the end of the section
        result += DELIMITER;
      }
    }

    result = result.slice(0, result.length - 1);
  }

  return result;
}<commit_msg>Added more comments in cobs.js. Unstuff now tells user if a given string is improperly formatted.<commit_after>/*
The MIT License (MIT)

Copyright (c) 2016 Steven Gago

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// Character used to demarcate the end of a frame.
const DELIMITER = "\u0000";

// The maximum length of a section that can be encoded in COBS.
// Normally, this is 0xFF, but this makes it play with, test, etc.
const MAX_SECTION_LENGTH = 0x05;

/*
 * Returns a new string in which a character at a specified
 * index is replaced with a given character.
 * @param {string} text - The string to replace the character in.
 * @param {number} index - Index of the character to replace.
 * @param {string} char - The character to replace at the specified index.
 * @return {string} - A string with the replaced character.
 */
function replaceCharAt(text, index, char)
{
  var result = "";

  // Does the index exist in the string?
  if ((index >= 0) && (index < text.length - 1))
  {
    // Replace the character at the specified position
    result = text.substr(0, index) + char + text.substr(index + 1);
  }

  return result;
}

/*
 * Returns a array of strings in which the given text is
 * split first by DELIMITER and then by MAX_SECTION_LENGTH.
 * @param {string} text - The string to divide into sections.
 * @return {array} - An array of the given string divided into sections.
 */
function getSections(text)
{
  var frames;
  var section;
  var sections = [];
  var startIndex = 0;
  var endIndex = 0;

  if ((typeof text === "string") && (text !== ""))
  {
    // Split the text by our delimiter
    frames = text.split(DELIMITER);

    // For each frame...
    for (var i = 0; i < frames.length; i++)
    {
      // While startIndex is less than length of a given frame...
      while (startIndex < frames[i].length)
      {
        // Get last index
        endIndex = startIndex + MAX_SECTION_LENGTH - 1;

        // Slice off the next section from our frame
        // Up to endIndex or end of the string.
        section = frames[i].slice(startIndex, endIndex);

        // Append the new section to sections array
        sections.push(section);

        // Get the starting index of the next section
        startIndex += section.length;
      }

      // Is this frame an empty string?
      if (frames[i].length === 0)
      {
        // Append an empty string onto sections
        sections.push("");
      }

      // Reset the startIndex for the next frame
      startIndex = 0;
    }
  }

  return sections;
}

/*
 * Returns a COBS-encoded string.
 * @param {string} text - The specified string to encode with COBS.
 * return {string} - A COBS encoded string.
 */
function stuff(text)
{
  var sections;
  var result = "";
  var i = 0;

  if ((typeof text === "string") && (text !== ""))
  {
    // Divide text into an array of strings
    sections = getSections(text);

    // For each section...
    for (var i = 0; i < sections.length; i++)
    {
      // Prepend the section with its length + 1
      sections[i] = String.fromCharCode(sections[i].length + 1) + sections[i];
    }

    // Append the final zero byte
    sections.push("\u0000");

    // Join all sections together and store as final result
    result = sections.join("");
  }

  return result;
}

/*
 * Returns a COBS-decoded string.
 * @param {string} text - The specified COBS-encoded string to decode.
 * @return {string} - A COBS-decoded string.
 */
function unstuff(text)
{
  var sectionLength = 0;
  var result = "";

  if ((typeof text === "string") && (text !== ""))
  {
    for (var i = 0; i < text.length - 1; i += sectionLength)
    {
      // Get length of the next section
      sectionLength = text.charCodeAt(i);

      // Is the sectionLength + i - 1 a valid index position in the string?
      if (text.length < (i + sectionLength - 1))
      {
        // Uhoh, text is an improper COBS encoded string
        // Let's tell the user and give up
        alert("Error: Invalid COBS encoded string \"" + text + "\".");
        results = "";
        break;
      }

      // Append the next section
      result += text.substr(i + 1, sectionLength - 1);

      // Is the next section length less than MAX_SECTION_LENGTH?
      if (sectionLength < MAX_SECTION_LENGTH)
      {
        // DELIMITER goes at the end of the section
        result += DELIMITER;
      }
    }

    // Is there a final null character in result?
    if (result[result.length - 1] === "\u0000")
    {
      // Remove null character at the end of the string
      result = result.slice(0, result.length - 1);
    }
  }

  return result;
}<|endoftext|>


<commit_before>//my JS will go here//
var allHours = ['6:00am', '7:00am', '8:00am', '9:00am', '10:00am', '11:00am', '12:00pm', '1:00pm', '2:00pm', '3:00pm', '4:00pm', '5:00pm', '6:00pm', '7:00pm', '8:00pm']
var allStores = [];

//Building my constructor
var Store = function(storeName, minHr, maxHr, cupCust, lbCust){
  this.storeName = storeName;
  this.hours = allHours;
  this.customerHours = [];
  this.cupsEachHour = [];
  this.beansEachHour = [];
  this.minHr = minHr;
  this.maxHr = maxHr;
  this.cupCust = cupCust;
  this.lbCust = lbCust;
  this.dailyCups = 0;
  this.dailyBeans = 0;
  allStores.push(this);

  this.custHour = function() {
    for (var i = 0; i < this.hours.length; i ++) {
      this.customerHours[i] = Math.floor(Math.random() * (this.maxHr - this.minHr +1)) + this.minHr;
    }
  };
  this.cupsPerHr = function() {
    for (var i = 0; i < this.hours.length; i ++) {
    this.cupsEachHour[i] = this.customerHours[i] * this.cupCust;
    this.dailyCups += this.cupsEachHour[i];
    this.dailyBeans += this.cupsEachHour[i]/20;
  }
};
  this.beanHr = function() {
    for (var i = 0; i < this.hours.length; i ++) {
    this.beansEachHour[i] = this.customerHours[i] * this.lbCust;
    this.dailyBeans += this.beansEachHour[i];
  }
};
  this.populate = function() {
    this.custHour();
    this.cupsPerHr();
    this.beanHr();

//Creates Title Box for the list
    var h2El = document.createElement('h2');
    h2El.textContent = this.storeName;
    document.body.appendChild(h2El);

//Populate list to website
    for (var i = 0; i < this.hours.length; i ++) {
      var paragraphEl = document.createElement('p');
      paragraphEl.textContent = this.hours[i] + ':' + this.beansEachHour[i].toFixed(1) + ' lbs [' + this.customerHours[i].toFixed(1) + ' customers, ' + this.cupsEachHour[i].toFixed(1) + ' cups (' + (this.cupsEachHour[i]/20).toFixed(1) + ' lbs), ' + this.beansEachHour[i].toFixed(1) + ' lbs to-go]'
      document.body.appendChild(paragraphEl);
    }
  };
};

//Creating each Store
var pike = new Store('Pike Place Market', 14, 55, 1.2, 3.7);
var capHill = new Store('Capitol Hill', 32, 38, 3.2, 0.4);
var spl = new Store('Seattle Public Library', 49, 75, 2.6, 0.2);
var slu = new Store('South Lake Union', 35, 88, 1.3, 3.7);
var seaTac = new Store('Sea-Tac Airport', 68, 124, 1.1, 2.7);
var website = new Store('Website Sales', 3, 6, 0, 6.7);

// //populate to the webpage
// for (var i = 0; i < allStores.length; i++) {
//   allStores[i].populate();
// };

var toTables = function () {
  //calculate data
  for (var i = 0; i < allStores.length; i++) {
    allStores[i].custHour();
    allStores[i].cupsPerHr();
    allStores[i].beanHr();
  }
  //make the title
  var h2El = document.createElement('h2');
  h2El.textContent = 'Bean Forecast Data';
  document.body.appendChild(h2El);
  var paragraphEl = document.createElement('p');
  paragraphEl.textContent = 'All values are in pounds';
  document.body.appendChild(paragraphEl);
  //make the table
  var tableEl = document.createElement('table');

  //display the header row of the table
  var trEl = document.createElement('tr');
  var thEl = document.createElement('th');
  thEl.textContent = ' ';
  trEl.appendChild(thEl);
  tableEl.appendChild(trEl);

  for (var i = 0; i < allStores.length; i++) {
    var thEl = document.createElement('th');
    thEl.textContent = allStores[i].storeName;
    trEl.appendChild(thEl);
  }

// Display cofee data
for (var i = 0; i < allHours.length; i++) {
  var trEl = document.createElement('tr');
  var thEl = document.createElement('th');
  thEl.textContent = allHours[i];
  trEl.appendChild(thEl);
  for (var j = 0; j < (allStores.length); j++) {
    var tdEl = document.createElement('td');
    tdEl.textContent = allStores[j].beansEachHour[i].toFixed(1);
    trEl.appendChild(tdEl)
    }
  tableEl.appendChild(trEl);
  }

  // Display dailyBeans in table
  var trEl = document.createElement('tr');
  var thEl = document.createElement('th');
  trEl.appendChild(thEl);
  thEl.textContent = 'Totals:';
  for (var i = 0; i < allStores.length; i++) {
    var thEl = document.createElement('th');
    thEl.textContent = allStores[i].dailyBeans.toFixed(1);
    trEl.appendChild(thEl);
  }
  tableEl.appendChild(trEl);

  //Display total beans needed for the day for all stores
  var totalDailyBeans = 0;
  for (var i = 0; i < allStores.length; i++) {
    totalDailyBeans += allStores[i].dailyBeans;
    console.log(totalDailyBeans);
  }
  var trEl = document.createElement('tr');
  var thEl = document.createElement('th');
  thEl.textContent = 'All Stores total:'
  trEl.appendChild(thEl);
  var thEl = document.createElement('th');
  thEl.textContent = totalDailyBeans.toFixed(1);
  trEl.appendChild(thEl);
  tableEl.appendChild(trEl);

  document.body.appendChild(tableEl);
}

toTables();
<commit_msg>added new data tables to data.html<commit_after>//my JS will go here//
var allHours = ['6:00am', '7:00am', '8:00am', '9:00am', '10:00am', '11:00am', '12:00pm', '1:00pm', '2:00pm', '3:00pm', '4:00pm', '5:00pm', '6:00pm', '7:00pm', '8:00pm']
var allStores = [];

//Building my constructor
var Store = function(storeName, minHr, maxHr, cupCust, lbCust){
  this.storeName = storeName;
  this.hours = allHours;
  this.customerHours = [];
  this.cupsEachHour = [];
  this.beansEachHour = [];
  this.minHr = minHr;
  this.maxHr = maxHr;
  this.cupCust = cupCust;
  this.lbCust = lbCust;
  this.dailyCups = 0;
  this.dailyBeans = 0;
  this.dailyBeansToSell = 0;
  allStores.push(this);

  this.custHour = function() {
    for (var i = 0; i < this.hours.length; i ++) {
      this.customerHours[i] = Math.floor(Math.random() * (this.maxHr - this.minHr +1)) + this.minHr;
    }
  };
  this.cupsPerHr = function() {
    for (var i = 0; i < this.hours.length; i ++) {
    this.cupsEachHour[i] = this.customerHours[i] * this.cupCust;
    this.dailyCups += this.cupsEachHour[i];
    this.dailyBeans += this.cupsEachHour[i]/20;
  }
};
  this.beanHr = function() {
    for (var i = 0; i < this.hours.length; i ++) {
    this.beansEachHour[i] = this.customerHours[i] * this.lbCust;
    this.dailyBeans += this.beansEachHour[i];
    this.dailyBeansToSell += this.beansEachHour[i];
  }
};
  this.populate = function() {
    this.custHour();
    this.cupsPerHr();
    this.beanHr();

//Creates Title Box for the list
    var h2El = document.createElement('h2');
    h2El.textContent = this.storeName;
    document.body.appendChild(h2El);

//Populate list to website
    for (var i = 0; i < this.hours.length; i ++) {
      var paragraphEl = document.createElement('p');
      paragraphEl.textContent = this.hours[i] + ':' + this.beansEachHour[i].toFixed(1) + ' lbs [' + this.customerHours[i].toFixed(1) + ' customers, ' + this.cupsEachHour[i].toFixed(1) + ' cups (' + (this.cupsEachHour[i]/20).toFixed(1) + ' lbs), ' + this.beansEachHour[i].toFixed(1) + ' lbs to-go]'
      document.body.appendChild(paragraphEl);
    }
  };
};

//Creating each Store
var pike = new Store('Pike Place Market', 14, 55, 1.2, 3.7);
var capHill = new Store('Capitol Hill', 32, 38, 3.2, 0.4);
var spl = new Store('Seattle Public Library', 49, 75, 2.6, 0.2);
var slu = new Store('South Lake Union', 35, 88, 1.3, 3.7);
var seaTac = new Store('Sea-Tac Airport', 68, 124, 1.1, 2.7);
var website = new Store('Website Sales', 3, 6, 0, 6.7);

// //populate to the webpage
// for (var i = 0; i < allStores.length; i++) {
//   allStores[i].populate();
// };

var displayForecast = function () {
  //calculate data
  for (var i = 0; i < allStores.length; i++) {
    allStores[i].custHour();
    allStores[i].cupsPerHr();
    allStores[i].beanHr();
  }
  allBeansToday();
  beansSellBrewToday();
  beansPerHour();
}

var makeTitleTable = function(title,subtitle) {
  //make the title
  var h2El = document.createElement('h2');
  h2El.textContent = title;
  document.body.appendChild(h2El);
  var paragraphEl = document.createElement('p');
  paragraphEl.textContent = subtitle;
  document.body.appendChild(paragraphEl);
  //make the table
  var tableEl = document.createElement('table');
}

//creates table for total beans needed today for all stores
var allBeansToday = function() {
  makeTitleTable('Beans needed for today', 'data is in lbs')
  var totalDailyBeans = 0;
  for (var i = 0; i < allStores.length; i++) {
    totalDailyBeans += allStores[i].dailyBeans;
  }
  var tableEl = document.createElement('table');
  var trEl = document.createElement('tr');
  var thEl = document.createElement('th');
  thEl.textContent = 'All Stores total:'
  trEl.appendChild(thEl);
  var thEl = document.createElement('th');
  thEl.textContent = totalDailyBeans.toFixed(1);
  trEl.appendChild(thEl);
  tableEl.appendChild(trEl);
  document.body.appendChild(tableEl);
}

var beansSellBrewToday = function () {
  makeTitleTable('Beans for each store by type','All values are in pounds');
  var tableEl = document.createElement('table');
  var trEl = document.createElement('tr');
  var thEl = document.createElement('th');
  thEl.textContent = 'Store Name';
  trEl.appendChild(thEl);
  thEl = document.createElement('th');
  thEl.textContent = 'lbs to sell';
  trEl.appendChild(thEl);
  thEl =document.createElement('th');
  thEl.textContent = 'lbs to brew';
  trEl.appendChild(thEl);
  tableEl.appendChild(trEl);

//variables for total beans across all stores
  allStoresBeansSell = 0;
  allStoresBeansBrew = 0;

  for (i = 0; i < allStores.length; i++) {
    trEl = document.createElement('tr');
    var tdEl = document.createElement('td');
    tdEl.textContent = allStores[i].storeName;
    trEl.appendChild(tdEl);
    tdEl = document.createElement('td');
    tdEl.textContent = allStores[i].dailyBeansToSell.toFixed(1);
    trEl.appendChild(tdEl);
    tdEl = document.createElement('td');
    tdEl.textContent = (allStores[i].dailyCups/20).toFixed(1);
    trEl.appendChild(tdEl);
    tableEl.appendChild(trEl);
    allStoresBeansSell += allStores[i].dailyBeansToSell;
    allStoresBeansBrew += allStores[i].dailyCups/20;
  }
  trEl = document.createElement('tr');
  thEl = document.createElement('th');
  thEl.textContent = 'Totals:';
  trEl.appendChild(thEl);
  thEl = document.createElement('th');
  thEl.textContent = Math.round(allStoresBeansSell);
  trEl.appendChild(thEl);
  thEl = document.createElement('th');
  thEl.textContent = Math.round(allStoresBeansBrew);
  trEl.appendChild(thEl);
  tableEl.appendChild(trEl);

  document.body.appendChild(tableEl);
}

//creates big list of total beans needed per hour
var beansPerHour = function () {
  //make the title
  makeTitleTable('Beans Per Hour Forecast Data', 'All values are in pounds');

  var tableEl = document.createElement('table');

  //display the header row of the table
  var trEl = document.createElement('tr');
  var thEl = document.createElement('th');
  thEl.textContent = ' ';
  trEl.appendChild(thEl);
  tableEl.appendChild(trEl);

  for (var i = 0; i < allStores.length; i++) {
    var thEl = document.createElement('th');
    thEl.textContent = allStores[i].storeName;
    trEl.appendChild(thEl);
  }

// Display cofee data
for (var i = 0; i < allHours.length; i++) {
  var trEl = document.createElement('tr');
  var thEl = document.createElement('th');
  thEl.textContent = allHours[i];
  trEl.appendChild(thEl);
  for (var j = 0; j < (allStores.length); j++) {
    var tdEl = document.createElement('td');
    tdEl.textContent = allStores[j].beansEachHour[i].toFixed(1);
    trEl.appendChild(tdEl)
    }
  tableEl.appendChild(trEl);
  }

  // Display dailyBeans in table
  var trEl = document.createElement('tr');
  var thEl = document.createElement('th');
  trEl.appendChild(thEl);
  thEl.textContent = 'Totals:';
  for (var i = 0; i < allStores.length; i++) {
    var thEl = document.createElement('th');
    thEl.textContent = allStores[i].dailyBeans.toFixed(1);
    trEl.appendChild(thEl);
  }
  tableEl.appendChild(trEl);

  document.body.appendChild(tableEl);
}
displayForecast();
<|endoftext|>


<commit_before>define(['jquery'], function () {
    var data = {};

    var init = function (type) {
        var dtd = $.Deferred();
        if (!type) {
            dtd.reject();
            return dtd.promise();
        }
        var key = "Table_" + type;
        var self = this;
        return self.isDataTooOld().then(function (force) {
            var json = localStorage.getItem(key);
            if (json && !force) {
                var jsondata = JSON.parse(json);
                console.log("Get data from cache. ", key);
                data[type] = jsondata;
                dtd.resolve();
                return dtd.promise();
            }
            else {
                var url = '/data/' + key + '.json'
                return $.ajax({
                    url: url,
                    cache: false,
                    dataType: "json"
                })
                    .done(function (jsondata) {
                        localStorage[key] = JSON.stringify(jsondata);
                        console.log("Get data from web. ", key);
                        data[type] = jsondata;
                    });
            }
        });
    };

    var isDataTooOld = function () {
        var dtd = $.Deferred();
        var key = "lastUpdate";
        var lastUpdate = localStorage.getItem(key);
        if (!lastUpdate) {
            dtd.resolve(true);
            return dtd.promise();
        }
        var url = '/data/lastUpdate.json'
        return $.ajax({
            url: url,
            cache: false,
            dataType: "json"
        }).then(function (data) {
            var local = JSON.parse(lastUpdate);
            var remote = data;
            return new Date(local).getTime() < new Date(remote).getTime();
        });
    };

    var getClassById = function (id) {
        return _.find(data.Class, function (p) { return p.Id === parseInt(id); });
    };

    var getParentClassById = function (id) {
        return _.find(data.Class, function (o) { return _.contains(o.AdvanceClass, id) });
    };

    var getSkillByClassId = function (id) {
        var self = this;
        var skillIds = [];
        var currentClass = getClassById(id);
        var parentClass = currentClass;
        while (parentClass.Id != 1) {
            skillIds = skillIds.concat(parentClass.Skill);
            parentClass = getParentClassById(parentClass.Id);
        }
        var skills = [];
        _.each(skillIds, function (o) {
            var skill = _.find(self.data.Skill, function (p) { return p.Id === o; });
            if (skill) {
                var skillDesc = _.find(self.data.SkillDesc, function (p) { return p.Id === skill.Desc.Id; });
                if (skillDesc) {
                    skill.Desc.Text = skillDesc.Desc;
                }
                //var skillMould = _.find(self.data.SkillMould, function (p) { return p.Id === o; });
                //if (skillMould) {
                //    skill.Mould = skillMould;
                //}
                skills.push(skill);
            }

        });
        return skills;
    };

    var getSkillById = function (id) {
        var self = this;
        var skill = _.find(self.data.Skill, function (p) { return p.Id === id; });
        if (skill) {
            var skillDesc = _.find(self.data.SkillDesc, function (p) { return p.Id === skill.Desc.Id; });
            if (skillDesc) {
                skill.Desc.Text = skillDesc.Desc;
                skill.Desc.Html = formatSkillText(skill.Desc);
            }
        }
        return skill || null;
    };

    var getSkillByNextId = function (nextid) {
        var self = this;
        var skill = _.find(self.data.Skill, function (p) { return p.NextId === nextid; });
        if (skill) {
            var skillDesc = _.find(self.data.SkillDesc, function (p) { return p.Id === skill.Desc.Id; });
            if (skillDesc) {
                skill.Desc.Text = skillDesc.Desc;
                skill.Desc.Html = formatSkillText(skill.Desc);
            }
        }
        return skill || null;
    };

    var getSkillMouldByClassId = function (id, joblv) {
        var self = this;
        var skillIds = [];
        var classes = [];
        var currentClass = getClassById(id);
        var parentClass = currentClass;
        while (parentClass.Id != 1) {
            classes.push(parentClass);
            parentClass = getParentClassById(parentClass.Id);
        }
        if (joblv) {
            classes = _.sortBy(classes, 'MaxJobLevel');
            var hasJoblv = true;
            _.each(classes, function (o, i) {
                if (hasJoblv) {
                    skillIds = skillIds.concat(o.Skill);
                }
                if (o.MaxJobLevel > joblv) {
                    hasJoblv = false;
                }
            });
        }
        else {
            _.each(classes, function (o, i) {
                skillIds = skillIds.concat(o.Skill);
            });
        }
        var skills = [];
        _.each(skillIds, function (o) {
            var skillMould = _.find(self.data.SkillMould, function (p) { return p.Id === o; });
            if (skillMould) {
                skills.push(skillMould);
            }
        });
        return skills;
    };

    var formatSkillText = function (skillDesc) {
        var text = formatRichText(skillDesc.Text);
        for (var i = 0; i < skillDesc.Params.length; i++) {
            text = text.replace("%s", skillDesc.Params[i]);
        }
        return text.replace(/%%/g, "%");
    };

    var formatRichText = function (richText) {
        return richText.replace(/(?:\r\n|\r|\n|\\n)/g, "
").replace(/\[-\]/g, "").replace(/\[([A-Za-z0-9]{6})\]/g, "");
    };

    return {
        data: data,
        init: init,
        isDataTooOld: isDataTooOld,
        getClassById: getClassById,
        getParentClassById: getParentClassById,
        getSkillById: getSkillById,
        getSkillByNextId: getSkillByNextId,
        getSkillByClassId: getSkillByClassId,
        getSkillMouldByClassId: getSkillMouldByClassId,
    };
});<commit_msg>bugfix<commit_after>define(['jquery'], function () {
    var data = {};

    var init = function (type) {
        var dtd = $.Deferred();
        if (!type) {
            dtd.reject();
            return dtd.promise();
        }
        var key = "Table_" + type;
        var self = this;
        return self.isDataTooOld().then(function (force) {
            var json = localStorage.getItem(key);
            if (json && !force) {
                var jsondata = JSON.parse(json);
                console.log("Get data from cache. ", key);
                data[type] = jsondata;
                dtd.resolve();
                return dtd.promise();
            }
            else {
                var url = '../data/' + key + '.json'
                return $.ajax({
                    url: url,
                    cache: false,
                    dataType: "json"
                })
                    .done(function (jsondata) {
                        localStorage[key] = JSON.stringify(jsondata);
                        console.log("Get data from web. ", key);
                        data[type] = jsondata;
                    });
            }
        });
    };

    var isDataTooOld = function () {
        var dtd = $.Deferred();
        var key = "lastUpdate";
        var lastUpdate = localStorage.getItem(key);
        if (!lastUpdate) {
            dtd.resolve(true);
            return dtd.promise();
        }
        var url = '../data/lastUpdate.json'
        return $.ajax({
            url: url,
            cache: false,
            dataType: "json"
        }).then(function (data) {
            var local = JSON.parse(lastUpdate);
            var remote = data;
            return new Date(local).getTime() < new Date(remote).getTime();
        });
    };

    var getClassById = function (id) {
        return _.find(data.Class, function (p) { return p.Id === parseInt(id); });
    };

    var getParentClassById = function (id) {
        return _.find(data.Class, function (o) { return _.contains(o.AdvanceClass, id) });
    };

    var getSkillByClassId = function (id) {
        var self = this;
        var skillIds = [];
        var currentClass = getClassById(id);
        var parentClass = currentClass;
        while (parentClass.Id != 1) {
            skillIds = skillIds.concat(parentClass.Skill);
            parentClass = getParentClassById(parentClass.Id);
        }
        var skills = [];
        _.each(skillIds, function (o) {
            var skill = _.find(self.data.Skill, function (p) { return p.Id === o; });
            if (skill) {
                var skillDesc = _.find(self.data.SkillDesc, function (p) { return p.Id === skill.Desc.Id; });
                if (skillDesc) {
                    skill.Desc.Text = skillDesc.Desc;
                }
                //var skillMould = _.find(self.data.SkillMould, function (p) { return p.Id === o; });
                //if (skillMould) {
                //    skill.Mould = skillMould;
                //}
                skills.push(skill);
            }

        });
        return skills;
    };

    var getSkillById = function (id) {
        var self = this;
        var skill = _.find(self.data.Skill, function (p) { return p.Id === id; });
        if (skill) {
            var skillDesc = _.find(self.data.SkillDesc, function (p) { return p.Id === skill.Desc.Id; });
            if (skillDesc) {
                skill.Desc.Text = skillDesc.Desc;
                skill.Desc.Html = formatSkillText(skill.Desc);
            }
        }
        return skill || null;
    };

    var getSkillByNextId = function (nextid) {
        var self = this;
        var skill = _.find(self.data.Skill, function (p) { return p.NextId === nextid; });
        if (skill) {
            var skillDesc = _.find(self.data.SkillDesc, function (p) { return p.Id === skill.Desc.Id; });
            if (skillDesc) {
                skill.Desc.Text = skillDesc.Desc;
                skill.Desc.Html = formatSkillText(skill.Desc);
            }
        }
        return skill || null;
    };

    var getSkillMouldByClassId = function (id, joblv) {
        var self = this;
        var skillIds = [];
        var classes = [];
        var currentClass = getClassById(id);
        var parentClass = currentClass;
        while (parentClass.Id != 1) {
            classes.push(parentClass);
            parentClass = getParentClassById(parentClass.Id);
        }
        if (joblv) {
            classes = _.sortBy(classes, 'MaxJobLevel');
            var hasJoblv = true;
            _.each(classes, function (o, i) {
                if (hasJoblv) {
                    skillIds = skillIds.concat(o.Skill);
                }
                if (o.MaxJobLevel > joblv) {
                    hasJoblv = false;
                }
            });
        }
        else {
            _.each(classes, function (o, i) {
                skillIds = skillIds.concat(o.Skill);
            });
        }
        var skills = [];
        _.each(skillIds, function (o) {
            var skillMould = _.find(self.data.SkillMould, function (p) { return p.Id === o; });
            if (skillMould) {
                skills.push(skillMould);
            }
        });
        return skills;
    };

    var formatSkillText = function (skillDesc) {
        var text = formatRichText(skillDesc.Text);
        for (var i = 0; i < skillDesc.Params.length; i++) {
            text = text.replace("%s", skillDesc.Params[i]);
        }
        return text.replace(/%%/g, "%");
    };

    var formatRichText = function (richText) {
        return richText.replace(/(?:\r\n|\r|\n|\\n)/g, "
").replace(/\[-\]/g, "").replace(/\[([A-Za-z0-9]{6})\]/g, "");
    };

    return {
        data: data,
        init: init,
        isDataTooOld: isDataTooOld,
        getClassById: getClassById,
        getParentClassById: getParentClassById,
        getSkillById: getSkillById,
        getSkillByNextId: getSkillByNextId,
        getSkillByClassId: getSkillByClassId,
        getSkillMouldByClassId: getSkillMouldByClassId,
    };
});<|endoftext|>


<commit_before>(function($) {
	var options = {
		events_url: 'events.json.php',
		onAfterEventsLoad: function(events) {
			if(!events) {
				return;
			}
			var list = $('#eventlist');
			list.html('');

			$.each(events, function(key, val) {
				$(document.createElement('li'))
					.html('' + val.title + '')
					.appendTo(list);
			});
		}
	};

	var calendar = $('#calendar').calendar(options);

	$('.page-header h1').text(calendar.title());

	$('.btn-group button[data-calendar-nav]').each(function() {
		var $this = $(this);
		$this.click(function() {
			calendar.navigate($this.data('calendar-nav'), function() {
				$('.page-header h1').text(calendar.title());
			});
		});
	});

	$('.btn-group button[data-calendar-view]').each(function() {
		var $this = $(this);
		$this.click(function() {
			$('.btn-group button').removeClass('active');

			calendar.view($this.data('calendar-view'));
			$this.addClass('active');

			$('.page-header h1').text(calendar.title());
		});
	});
}(jQuery));<commit_msg>Some code optimizations<commit_after>(function($) {
	var options = {
		events_url: 'events.json.php',
		onAfterEventsLoad: function(events) {
			if(!events) {
				return;
			}
			var list = $('#eventlist');
			list.html('');

			$.each(events, function(key, val) {
				$(document.createElement('li'))
					.html('' + val.title + '')
					.appendTo(list);
			});
		}

	};

	var calendar = $('#calendar').calendar(options);

	$('.page-header h1').text(calendar.title());

	$('.btn-group button[data-calendar-nav]').each(function() {
		var $this = $(this);
		$this.click(function() {
			calendar.navigate($this.data('calendar-nav'), function() {
				$('.page-header h1').text(calendar.title());
			});
		});
	});

	$('.btn-group button[data-calendar-view]').each(function() {
		var $this = $(this);
		$this.click(function() {
			$('.btn-group button').removeClass('active');

			calendar.view($this.data('calendar-view'));
			$this.addClass('active');

			$('.page-header h1').text(calendar.title());
		});
	});
}(jQuery));<|endoftext|>


<commit_before>(function( global,factory ){
	factory(global);
}( window,function(){
	window.Draw = function( id ) {
		this.current = null; //定义当前对象
	    this.changeEvent = []; //定义事件栈
	    this.stage = new createjs.Stage( id );//创建舞台对象

		createjs.Touch.enable(this.stage);
		createjs.Ticker.setFPS(20);
		var that = this;
	    createjs.Ticker.addEventListener("tick", function() {
	    	that.stage.update();
	    });
	}

	Draw.prototype.onchange = function( cb ) {
		this.changeEvent.push(cb);
	    return this;
	}

	Draw.prototype.addBitmap = function( img, name ) {
		var bitmap = new createjs.Bitmap();
		bitmap.name = name || 'bitmap' + bitmap.id;
		bitmap.image = img;

		//设置缩放
		if( img.width > this.stage.canvas.width ) {
			bitmap.scaleX = bitmap.scaleY = this.stage.canvas.width / img.width;
		}
		//设置点击区域
		var bounds = bitmap.getBounds();
		var hit = new createjs.Shape();
	    hit.graphics.beginFill('#000').drawRect(0,0,bounds.width,bounds.height);
	    bitmap.hitArea = hit;
		//设置中心点
    	bitmap.regX = bounds.width / 2  || 0;
    	bitmap.regY = bounds.height / 2 || 0;
    	bitmap.x = bitmap.regX * bitmap.scaleX;
    	bitmap.y = bitmap.regY * bitmap.scaleY;

		//设置事件
		var that = this;
		bitmap.on("mousedown", function( evt ) {
			that.__dragstart(evt);
		});
		bitmap.on("pressmove", function( evt ) {
			that.__dragmove(evt);
		});

		//切换当前组件
		this.__changeCurrent(bitmap);
		this.stage.addChildAt(bitmap,0);

		return this;
	}

	Draw.prototype.addText = function( str ) {
		str = str || "请输入";
		var text = new createjs.Text(str,"20px Arial", "#000000");
		text.name = 'text' + text.id;

		//设置点击区域
		var bounds = text.getBounds();
		var hit = new createjs.Shape();
	    hit.graphics.beginFill('#000').drawRect(0,0,bounds.width,bounds.height);
	    text.hitArea = hit;
		//设置中心点
    	text.regX = bounds.width / 2  || 0;
    	text.regY = bounds.height / 2 || 0;
		text.x = text.regX + 50;
		text.y = text.regY + 50;

		//设置事件
		var that = this;
		text.on("mousedown", function( evt ) {
			that.__dragstart(evt);
		});
		text.on("pressmove", function( evt ){
			that.__dragmove(evt);
		});

		//切换激活对象
		this.__changeCurrent(text);
		this.stage.addChildAt(text,this.stage.children.length);

		return this;
	}

	Draw.prototype.save = function() {
		//清除透明
		this.__clearAlpha(1);
		var d = $.Deferred();

		var that = this;
		setTimeout( function() {
	        d.resolve( that.stage.toDataURL("transparent","image/png") );
	    },300 );
		return d.promise();
	}

	Draw.prototype.delete = function() {
		//删除当前
		this.stage.removeChild(this.current);
		this.current = null;

		//如果舞台中还有其它图形，则激活第一个
		var children = this.stage.children;
		if( children.length ) {
			//切换激活对象
			this.__changeCurrent(children[0]);
		}
	}

	Draw.prototype.clear = function() {
		this.stage.removeAllChildren();
	}

	//图形大小
	Draw.prototype.setScale = function( x, y ) {
		this.current.scaleX = x;
		this.current.scaleY = y;
	}
	Draw.prototype.getScale = function() {
		return {
			x : this.current.scaleX ,
			y : this.current.scaleY
		}
	}

	//旋转
	Draw.prototype.setRotation = function( deg ) {
		//设置点击区域
//  	console.log(this.current.x,this.current.y);
//	    this.current.x = this.current.x || this.current.regX * this.current.scaleX;
//	    this.current.y = this.current.y || this.current.regY * this.current.scaleY;
//	    console.log(this.current.x,this.current.y);

	    this.current.rotation = deg;
	}
	Draw.prototype.getRotation = function() {
		return this.current.rotation;
	}

	//颜色
	Draw.prototype.setColor = function( color ) {
		this.current.color = color;
	}
	Draw.prototype.getColor = function() {
		return this.current.color;
	}

	//字体
	Draw.prototype.setFamily = function( family ) {
		var arr = this.current.font.split(" ");
		arr[1] = family;
		this.current.font = arr.join(" ");
	}
	Draw.prototype.getFamily = function() {
		return this.current.font.split(" ")[1];
	}

		//图片
	Draw.prototype.setImage = function( img ) {
		this.current.image = img;
	}

	Draw.prototype.__firechange = function() {
		for( var i = 0;i < this.changeEvent.length;i++ ) {
			this.changeEvent[i]();
		}
	}

	Draw.prototype.__dragstart = function(evt) {
		//计算鼠标与图形的相对坐标
		evt.target.diffx = evt.stageX - evt.target.x;
		evt.target.diffy = evt.stageY - evt.target.y;

		//切换激活对象
		this.__changeCurrent(evt.target);
	}

	Draw.prototype.__dragmove = function(evt){
	    evt.target.x = evt.stageX - evt.target.diffx;
	    evt.target.y = evt.stageY - evt.target.diffy;
	}

	Draw.prototype.__clearAlpha = function( alpha ) {
		alpha = alpha || 0.5;
		this.stage.children.map(function( item ) {
			item.alpha = alpha;
		});
	}

	Draw.prototype.__changeCurrent = function( obj ) {
		//清空
	    if( this.current != obj  ) {
	        this.current = obj;
	        this.__clearAlpha();
	        this.current.alpha = 1;
	        this.__firechange();
	    }
	}
}));<commit_msg>解决点击后图片层次调整问题<commit_after>(function( global,factory ){
	factory(global);
}( window,function(){
	window.Draw = function( id ) {
		this.current = null; //定义当前对象
	    this.changeEvent = []; //定义事件栈
	    this.stage = new createjs.Stage( id );//创建舞台对象

		createjs.Touch.enable(this.stage);
		createjs.Ticker.setFPS(20);
		var that = this;
	    createjs.Ticker.addEventListener("tick", function() {
	    	that.stage.update();
	    });
	}

	Draw.prototype.onchange = function( cb ) {
		this.changeEvent.push(cb);
	    return this;
	}

	Draw.prototype.addBitmap = function( img, name ) {
		var bitmap = new createjs.Bitmap();
		bitmap.name = name || 'bitmap';
		bitmap.image = img;

		//设置缩放
		if( img.width > this.stage.canvas.width ) {
			bitmap.scaleX = bitmap.scaleY = this.stage.canvas.width / img.width;
		}
		//设置点击区域
		var bounds = bitmap.getBounds();
		var hit = new createjs.Shape();
	    hit.graphics.beginFill('#000').drawRect(0,0,bounds.width,bounds.height);
	    bitmap.hitArea = hit;
		//设置中心点
    	bitmap.regX = bounds.width / 2  || 0;
    	bitmap.regY = bounds.height / 2 || 0;
    	bitmap.x = bitmap.regX * bitmap.scaleX;
    	bitmap.y = bitmap.regY * bitmap.scaleY;

		//设置事件
		var that = this;
		bitmap.on("mousedown", function( evt ) {
			that.__dragstart(evt);
		});
		bitmap.on("pressmove", function( evt ) {
			that.__dragmove(evt);
		});

		//切换当前组件
		this.__changeCurrent(bitmap);
		this.stage.addChildAt(bitmap,0);

		return this;
	}

	Draw.prototype.addText = function( str ) {
		str = str || "请输入";
		var text = new createjs.Text(str,"20px Arial", "#000000");
		text.name = 'text';

		//设置点击区域
		var bounds = text.getBounds();
		var hit = new createjs.Shape();
	    hit.graphics.beginFill('#000').drawRect(0,0,bounds.width,bounds.height);
	    text.hitArea = hit;
		//设置中心点
    	text.regX = bounds.width / 2  || 0;
    	text.regY = bounds.height / 2 || 0;
		text.x = text.regX + 50;
		text.y = text.regY + 50;

		//设置事件
		var that = this;
		text.on("mousedown", function( evt ) {
			that.__dragstart(evt);
		});
		text.on("pressmove", function( evt ){
			that.__dragmove(evt);
		});

		//切换激活对象
		this.__changeCurrent(text);
		this.stage.addChildAt(text,this.stage.children.length);

		return this;
	}

	Draw.prototype.save = function() {
		//清除透明
		this.__clearAlpha(1);
		var d = $.Deferred();

		var that = this;
		setTimeout( function() {
	        d.resolve( that.stage.toDataURL("transparent","image/png") );
	    },300 );
		return d.promise();
	}

	Draw.prototype.delete = function() {
		//删除当前
		this.stage.removeChild(this.current);
		this.current = null;

		//如果舞台中还有其它图形，则激活第一个
		var children = this.stage.children;
		if( children.length ) {
			//切换激活对象
			this.__changeCurrent(children[0]);
		}
	}

	Draw.prototype.clear = function() {
		this.stage.removeAllChildren();
	}

	//图形大小
	Draw.prototype.setScale = function( x, y ) {
		this.current.scaleX = x;
		this.current.scaleY = y;
	}
	Draw.prototype.getScale = function() {
		return {
			x : this.current.scaleX ,
			y : this.current.scaleY
		}
	}

	//旋转
	Draw.prototype.setRotation = function( deg ) {
	    this.current.rotation = deg;
	}
	Draw.prototype.getRotation = function() {
		return this.current.rotation;
	}

	//颜色
	Draw.prototype.setColor = function( color ) {
		this.current.color = color;
	}
	Draw.prototype.getColor = function() {
		return this.current.color;
	}

	//字体
	Draw.prototype.setFamily = function( family ) {
		var arr = this.current.font.split(" ");
		arr[1] = family;
		this.current.font = arr.join(" ");
	}
	Draw.prototype.getFamily = function() {
		return this.current.font.split(" ")[1];
	}

		//图片
	Draw.prototype.setImage = function( img ) {
		this.current.image = img;
	}

	Draw.prototype.__firechange = function() {
		for( var i = 0;i < this.changeEvent.length;i++ ) {
			this.changeEvent[i]();
		}
	}

	Draw.prototype.__dragstart = function(evt) {
		//计算鼠标与图形的相对坐标
		evt.target.diffx = evt.stageX - evt.target.x;
		evt.target.diffy = evt.stageY - evt.target.y;

		//将选中对象放到最上层，但不能超过文字
		if( this.current.name == "text" ) {
			var children = this.stage.children;
			this.stage.swapChildren( evt.target,children[children.length-1] );
		} else {
			this.stage.swapChildren( evt.target, this.__getBitmapMaxIndex() );
		}
//		this.stage.addChild( evt.target );

		//切换激活对象
		this.__changeCurrent(evt.target);
	}

	Draw.prototype.__dragmove = function(evt){
	    evt.target.x = evt.stageX - evt.target.diffx;
	    evt.target.y = evt.stageY - evt.target.diffy;
	}

	Draw.prototype.__clearAlpha = function( alpha ) {
		alpha = alpha || 0.5;
		this.stage.children.map(function( item ) {
			item.alpha = alpha;
		});
	}

	Draw.prototype.__changeCurrent = function( obj ) {
		//清空
	    if( this.current != obj  ) {
	        this.current = obj;
	        this.__clearAlpha();
	        this.current.alpha = 1;
	        this.__firechange();
	    }
	}

	Draw.prototype.__getBitmapMaxIndex = function() {
		var swap = 0;
		var children = this.stage.children;
		for( var i = 0;i < children.length;i++ ) {
			if( children[i].name != "bitmap") {
				break;
			}
			swap = children[i];
		}

		return swap;
	}
}));<|endoftext|>