<commit_before>var ElementType = require("./ElementType.js");

function Parser(cbs, options) {
    if (options) this._options = options;
    if (cbs) this._cbs = cbs;

    this._buffer = "";
    this._tagSep = "";
    this._stack = [];
    this._contentFlags = 0;
    this._done = false;
}

//**"Static"**//
//Regular expressions used for cleaning up and parsing (stateless)
var _reTagName = /[^\s\/]+/; //matches tagnames
var _reAttrib = /([^=<>\"\'\s]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^'"\s]+))|([^=<>\"\'\s\/]+)/g;

Parser.prototype._options = {
    xmlMode: false, //Special behaviour for script/style tags by default
    lowerCaseTags: false //call .toLowerCase for each tagname
};

Parser.prototype._cbs = {
    /*
	onopentag,
	onclosetag,
	ontext,
	onprocessinginstruction,
	oncomment
	*/
};

//**Public**//
//Methods//
//Parses a complete HTML and pushes it to the handler
Parser.prototype.parseComplete = function(data) {
    this.reset();
    this.write(data);
    this.end();
};

//Parses a piece of an HTML document
Parser.prototype.write = Parser.prototype.parseChunk = function(data) {
    if (this._done)
        this._handleError(
            Error("Attempted to parse chunk after parsing already done")
        );
    this._buffer += data; //FIXME: this can be a bottleneck
    this._parseTags();
};

//Tells the parser that the HTML being parsed is complete
Parser.prototype.end = Parser.prototype.done = function(chunk) {
    if (this._done) return;
    this._done = true;

    //Parse the buffer to its end
    if (chunk) this._buffer += chunk;
    if (this._buffer) this._parseTags(true);

    if (this._cbs.onclosetag) {
        while (this._stack.length) this._cbs.onclosetag(this._stack.pop());
    }

    if (this._cbs.onend) this._cbs.onend();
};

//Resets the parser to a blank state, ready to parse a new HTML document
Parser.prototype.reset = function() {
    Parser.call(this);
    if (this._cbs.onreset) this._cbs.onreset();
};

//**Private**//
//Takes an element and adds an "attribs" property for any element attributes found
var parseAttributes = function(data) {
    var pos = data.search(/\w\s/) + 1,
        attrs = {}; //Find any whitespace
    if (pos === 0) return attrs;
    var attribRaw = data.substr(pos);

    _reAttrib.lastIndex = 0;
    var match;

    while ((match = _reAttrib.exec(attribRaw))) {
        if (match[1]) attrs[match[1]] = match[2] || match[3] || match[4];
        else attrs[match[5]] = match[5];
    }

    return attrs;
};

//Extracts the base tag name from the data value of an element
Parser.prototype._parseTagName = function(data) {
    var match = data.match(_reTagName);
    if (match === null) return "";
    if (this._options.lowerCaseTags) {
        return match[0].toLowerCase();
    } else return match[0];
};

//Special tags that are threated differently
var SpecialTags = {};
SpecialTags[ElementType.Tag] = 0;
SpecialTags[ElementType.Style] = 1; //2^0
SpecialTags[ElementType.Script] = 2; //2^1
SpecialTags.w = 4; //2^2 - if set, append prev tag sep to data
SpecialTags[ElementType.Comment] = 8; //2^3

//Parses through HTML text and returns an array of found elements
Parser.prototype._parseTags = function(force) {
    var buffer = this._buffer,
        current = 0;

    var next, rawData, elementType, elementData, lastTagSep;

    var opening = buffer.indexOf("<"),
        closing = buffer.indexOf(">");

    //if force is true, parse everything
    if (force) opening = Infinity;

    while (opening !== closing) {
        //just false if both are -1
        lastTagSep = this._tagSep;

        if ((opening !== -1 && opening < closing) || closing === -1) {
            next = opening;
            this._tagSep = "<";
            opening = buffer.indexOf("<", next + 1);
        } else {
            next = closing;
            this._tagSep = ">";
            closing = buffer.indexOf(">", next + 1);
        }
        rawData = buffer.substring(current, next); //The next chunk of data to parse

        //set elements for next run
        current = next + 1;

        if (this._contentFlags >= SpecialTags[ElementType.Comment]) {
            //We're currently in a comment tag
            this._processComment(rawData);
            continue;
        }

        if (lastTagSep === "<") {
            elementData = rawData.trimLeft();
            if (elementData.charAt(0) === "/") {
                //elementData = elementData.substr(1).trim();
                elementData = this._parseTagName(elementData.substr(1));
                if (this._contentFlags !== 0) {
                    //if it's a closing tag, remove the flag
                    if (
                        this._contentFlags >= SpecialTags[ElementType.Script] &&
                        elementData === "script"
                    ) {
                        //remove the script flag (also removes the written flag)
                        this._contentFlags %= SpecialTags[ElementType.Script];
                    } else if (
                        this._contentFlags >= SpecialTags[ElementType.Style] &&
                        elementData === "style"
                    ) {
                        //remove the style flag (also removes the written flag)
                        this._contentFlags %= SpecialTags[ElementType.Style];
                    } else {
                        this._writeSpecial(rawData, lastTagSep);
                        continue;
                    }
                }
                this._processCloseTag(elementData);
            } else if (
                elementData.charAt(0) === "!" ||
                elementData.charAt(0) === "?"
            ) {
                if (elementData.substr(0, 3) === "!--") {
                    //This tag is a comment
                    this._contentFlags += SpecialTags[ElementType.Comment];
                    this._processComment(rawData.substr(3));
                } else if (this._contentFlags !== 0) {
                    this._writeSpecial(rawData, lastTagSep);
                }
                //This tag is a directive
                //TODO: what about CDATA?
                else if (this._cbs.onprocessinginstruction) {
                    this._cbs.onprocessinginstruction(
                        elementData.charAt(0) +
                            this._parseTagName(elementData.substr(1)),
                        elementData
                    );
                }
            } else if (this._contentFlags !== 0)
                this._writeSpecial(rawData, lastTagSep);
            else
                this._processOpenTag(
                    this._parseTagName(elementData),
                    elementData
                );
        } else {
            if (this._contentFlags !== 0) {
                this._writeSpecial(rawData, lastTagSep);
            } else if (rawData !== "" && this._cbs.ontext) {
                this._cbs.ontext(rawData);
            }
        }
    }

    this._buffer = buffer.substring(current);
};

Parser.prototype._processComment = function(rawData) {
    if (this._tagSep === ">" && rawData.substr(-2) === "--") {
        //comment ends
        //remove the written flag (also removes the comment flag)
        this._contentFlags %= SpecialTags.w;
        if (this._cbs.oncomment) this._cbs.oncomment(rawData.slice(0, -2));
        if (this._cbs.oncommentend) this._cbs.oncommentend();
    } else if (this._cbs.oncomment) this._cbs.oncomment(rawData + this._tagSep);
};

Parser.prototype._writeSpecial = function(rawData, lastTagSep) {
    //if the previous element is text, append the last tag sep to element
    if (this._contentFlags >= SpecialTags.w) {
        if (this._cbs.ontext) this._cbs.ontext(lastTagSep + rawData);
    } else {
        //The previous element was not text
        this._contentFlags += SpecialTags.w;
        if (rawData !== "" && this._cbs.ontext) this._cbs.ontext(rawData);
    }
};

var emptyTags = require("./ClosingTags.js").self;

Parser.prototype._isEmptyTag = function(name) {
    return !this._options.xmlMode && emptyTags[name];
};

Parser.prototype._processCloseTag = function(name) {
    if (this._stack && !this._isEmptyTag(name)) {
        var i = this._stack.length;
        while (i !== 0 && this._stack[--i] !== name) {}
        if (i !== 0 || this._stack[0] === name)
            if (this._cbs.onclosetag) {
                while (i < this._stack.length)
                    this._cbs.onclosetag(this._stack.pop());
            } else this._stack.splice(i);
    }
    //many browsers (eg. Safari, Chrome) convert
 to

    else if (name === "br" && !this._options.xmlMode)
        this._processOpenTag(name, "/");
};

Parser.prototype._processOpenTag = function(name, data) {
    var type = ElementType.Tag;
    if (this._options.xmlMode) {
        /*do nothing*/
    } else if (name === "script") type = ElementType.Script;
    else if (name === "style") type = ElementType.Style;

    if (this._cbs.onopentag) {
        this._cbs.onopentag(name, parseAttributes(data), type);
    }

    //If tag self-terminates, add an explicit, separate closing tag
    if (data.substr(-1) === "/" || this._isEmptyTag(name)) {
        if (this._cbs.onclosetag) this._cbs.onclosetag(name);
    } else {
        this._contentFlags += SpecialTags[type];
        this._stack.push(name);
    }
};

Parser.prototype._handleError = function(error) {
    if (this._cbs.onerror) this._cbs.onerror(error);
    else throw error;
};

module.exports = Parser;
<commit_msg>Removed Parser#_isEmptyTag<commit_after>var ElementType = require("./ElementType.js");

function Parser(cbs, options) {
    if (options) this._options = options;
    if (cbs) this._cbs = cbs;

    this._buffer = "";
    this._tagSep = "";
    this._stack = [];
    this._contentFlags = 0;
    this._done = false;
}

//**"Static"**//
//Regular expressions used for cleaning up and parsing (stateless)
var _reTagName = /[^\s\/]+/; //matches tagnames
var _reAttrib = /([^=<>\"\'\s]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^'"\s]+))|([^=<>\"\'\s\/]+)/g;

Parser.prototype._options = {
    xmlMode: false, //Special behaviour for script/style tags by default
    lowerCaseTags: false //call .toLowerCase for each tagname
};

Parser.prototype._cbs = {
    /*
	onopentag,
	onclosetag,
	ontext,
	onprocessinginstruction,
	oncomment
	*/
};

//**Public**//
//Methods//
//Parses a complete HTML and pushes it to the handler
Parser.prototype.parseComplete = function(data) {
    this.reset();
    this.write(data);
    this.end();
};

//Parses a piece of an HTML document
Parser.prototype.write = Parser.prototype.parseChunk = function(data) {
    if (this._done)
        this._handleError(
            Error("Attempted to parse chunk after parsing already done")
        );
    this._buffer += data; //FIXME: this can be a bottleneck
    this._parseTags();
};

//Tells the parser that the HTML being parsed is complete
Parser.prototype.end = Parser.prototype.done = function(chunk) {
    if (this._done) return;
    this._done = true;

    //Parse the buffer to its end
    if (chunk) this._buffer += chunk;
    if (this._buffer) this._parseTags(true);

    if (this._cbs.onclosetag) {
        while (this._stack.length) this._cbs.onclosetag(this._stack.pop());
    }

    if (this._cbs.onend) this._cbs.onend();
};

//Resets the parser to a blank state, ready to parse a new HTML document
Parser.prototype.reset = function() {
    Parser.call(this);
    if (this._cbs.onreset) this._cbs.onreset();
};

//**Private**//
//Takes an element and adds an "attribs" property for any element attributes found
var parseAttributes = function(data) {
    var pos = data.search(/\w\s/) + 1,
        attrs = {}; //Find any whitespace
    if (pos === 0) return attrs;
    var attribRaw = data.substr(pos);

    _reAttrib.lastIndex = 0;
    var match;

    while ((match = _reAttrib.exec(attribRaw))) {
        if (match[1]) attrs[match[1]] = match[2] || match[3] || match[4];
        else attrs[match[5]] = match[5];
    }

    return attrs;
};

//Extracts the base tag name from the data value of an element
Parser.prototype._parseTagName = function(data) {
    var match = data.match(_reTagName);
    if (match === null) return "";
    if (this._options.lowerCaseTags) {
        return match[0].toLowerCase();
    } else return match[0];
};

//Special tags that are threated differently
var SpecialTags = {};
SpecialTags[ElementType.Tag] = 0;
SpecialTags[ElementType.Style] = 1; //2^0
SpecialTags[ElementType.Script] = 2; //2^1
SpecialTags.w = 4; //2^2 - if set, append prev tag sep to data
SpecialTags[ElementType.Comment] = 8; //2^3

//Parses through HTML text and returns an array of found elements
Parser.prototype._parseTags = function(force) {
    var buffer = this._buffer,
        current = 0;

    var next, rawData, elementType, elementData, lastTagSep;

    var opening = buffer.indexOf("<"),
        closing = buffer.indexOf(">");

    //if force is true, parse everything
    if (force) opening = Infinity;

    while (opening !== closing) {
        //just false if both are -1
        lastTagSep = this._tagSep;

        if ((opening !== -1 && opening < closing) || closing === -1) {
            next = opening;
            this._tagSep = "<";
            opening = buffer.indexOf("<", next + 1);
        } else {
            next = closing;
            this._tagSep = ">";
            closing = buffer.indexOf(">", next + 1);
        }
        rawData = buffer.substring(current, next); //The next chunk of data to parse

        //set elements for next run
        current = next + 1;

        if (this._contentFlags >= SpecialTags[ElementType.Comment]) {
            //We're currently in a comment tag
            this._processComment(rawData);
            continue;
        }

        if (lastTagSep === "<") {
            elementData = rawData.trimLeft();
            if (elementData.charAt(0) === "/") {
                //elementData = elementData.substr(1).trim();
                elementData = this._parseTagName(elementData.substr(1));
                if (this._contentFlags !== 0) {
                    //if it's a closing tag, remove the flag
                    if (
                        this._contentFlags >= SpecialTags[ElementType.Script] &&
                        elementData === "script"
                    ) {
                        //remove the script flag (also removes the written flag)
                        this._contentFlags %= SpecialTags[ElementType.Script];
                    } else if (
                        this._contentFlags >= SpecialTags[ElementType.Style] &&
                        elementData === "style"
                    ) {
                        //remove the style flag (also removes the written flag)
                        this._contentFlags %= SpecialTags[ElementType.Style];
                    } else {
                        this._writeSpecial(rawData, lastTagSep);
                        continue;
                    }
                }
                this._processCloseTag(elementData);
            } else if (
                elementData.charAt(0) === "!" ||
                elementData.charAt(0) === "?"
            ) {
                if (elementData.substr(0, 3) === "!--") {
                    //This tag is a comment
                    this._contentFlags += SpecialTags[ElementType.Comment];
                    this._processComment(rawData.substr(3));
                } else if (this._contentFlags !== 0) {
                    this._writeSpecial(rawData, lastTagSep);
                }
                //This tag is a directive
                //TODO: what about CDATA?
                else if (this._cbs.onprocessinginstruction) {
                    this._cbs.onprocessinginstruction(
                        elementData.charAt(0) +
                            this._parseTagName(elementData.substr(1)),
                        elementData
                    );
                }
            } else if (this._contentFlags !== 0)
                this._writeSpecial(rawData, lastTagSep);
            else
                this._processOpenTag(
                    this._parseTagName(elementData),
                    elementData
                );
        } else {
            if (this._contentFlags !== 0) {
                this._writeSpecial(rawData, lastTagSep);
            } else if (rawData !== "" && this._cbs.ontext) {
                this._cbs.ontext(rawData);
            }
        }
    }

    this._buffer = buffer.substring(current);
};

Parser.prototype._processComment = function(rawData) {
    if (this._tagSep === ">" && rawData.substr(-2) === "--") {
        //comment ends
        //remove the written flag (also removes the comment flag)
        this._contentFlags %= SpecialTags.w;
        if (this._cbs.oncomment) this._cbs.oncomment(rawData.slice(0, -2));
        if (this._cbs.oncommentend) this._cbs.oncommentend();
    } else if (this._cbs.oncomment) this._cbs.oncomment(rawData + this._tagSep);
};

Parser.prototype._writeSpecial = function(rawData, lastTagSep) {
    //if the previous element is text, append the last tag sep to element
    if (this._contentFlags >= SpecialTags.w) {
        if (this._cbs.ontext) this._cbs.ontext(lastTagSep + rawData);
    } else {
        //The previous element was not text
        this._contentFlags += SpecialTags.w;
        if (rawData !== "" && this._cbs.ontext) this._cbs.ontext(rawData);
    }
};

var emptyTags = require("./ClosingTags.js").self;

Parser.prototype._processCloseTag = function(name) {
    if (this._stack && (!emptyTags[name] || this._options.xmlMode)) {
        var i = this._stack.length;
        while (i !== 0 && this._stack[--i] !== name) {}
        if (i !== 0 || this._stack[0] === name)
            if (this._cbs.onclosetag) {
                while (i < this._stack.length)
                    this._cbs.onclosetag(this._stack.pop());
            } else this._stack.splice(i);
    }
    //many browsers (eg. Safari, Chrome) convert
 to

    else if (name === "br" && !this._options.xmlMode)
        this._processOpenTag(name, "/");
};

Parser.prototype._processOpenTag = function(name, data) {
    var type = ElementType.Tag;
    if (this._options.xmlMode) {
        /*do nothing*/
    } else if (name === "script") type = ElementType.Script;
    else if (name === "style") type = ElementType.Style;

    if (this._cbs.onopentag) {
        this._cbs.onopentag(name, parseAttributes(data), type);
    }

    //If tag self-terminates, add an explicit, separate closing tag
    if (
        data.substr(-1) === "/" ||
        (emptyTags[name] && !this._options.xmlMode)
    ) {
        if (this._cbs.onclosetag) this._cbs.onclosetag(name);
    } else {
        this._contentFlags += SpecialTags[type];
        this._stack.push(name);
    }
};

Parser.prototype._handleError = function(error) {
    if (this._cbs.onerror) this._cbs.onerror(error);
    else throw error;
};

module.exports = Parser;
<|endoftext|>


<commit_before>var APIURL = 'https://1dhhcnzmxi.execute-api.us-east-1.amazonaws.com/v1';
var HEADERS = {'Accept': 'application/json', 'Content-Type': 'application/json'};
var USER_URL = 'https://s3.amazonaws.com/constellational-store';
var POST_URL = 'https://d2nxl7qthm5fu1.cloudfront.net';

var SettingStore = require('../stores/SettingStore');
var AppDispatcher = require('../dispatcher/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
var React = require('react-native');

var {
    AsyncStorage,
} = React;

var CHANGE_EVENT = 'change';

var _posts = null;
var _postURLs = null;

function loadAsyncStore() {
  return AsyncStorage.getItem('posts').then(str => {
    if (!str) _posts = {};
    else _posts = JSON.parse(str);
    return AsyncStorage.getItem('postURLs').then(str => {
      if (!str) _postURLs = [];
      else _postURLs = JSON.parse(str);
      PostStore.emitChange();
    });
  });
}

function createPost(post) {
  _postURLs.unshift(post.url);
  _posts[post.url] = post;
  PostStore.emitChange();
  return updateAsyncStore();
}

function editPost(post) {
  _posts[post.url] = post;
  PostStore.emitChange();
  return updateAsyncStore();
}

function deletePost(post) {
  var i = _postURLs.indexOf(action.post.url);
  _postURLs.splice(i, 1);
  delete _posts[action.post.url];
  PostStore.emitChange();
  return updateAsyncStore();
}

function fetchUser(username) {
  return fetch(USER_URL + '/' + username).then(res => res.json());
}

function fetchPost(username, url) {
  return fetch(POST_URL + '/' + username + '/' + url).then(res => res.json()).then((post) => {
    post.url = url;
    return post;
  });
}

function fetchFromServer() {
  var username = SettingStore.getUsername();
  return fetchUser(username).then((user) => {
    _postURLs = user.posts;
    var promiseArr = user.posts.map(url => fetchPost(username, url));
    return Promise.all(promiseArr);
  }).then((posts) => {
    posts.map((post) => {
      _posts[post.url] = post;
    });
    PostStore.emitChange();
  });
}

function updateAsyncStore() {
  return AsyncStorage.setItem('posts', JSON.stringify(_posts)).then(() => {
    if (_postURLs) return AsyncStorage.setItem('postURLs', JSON.stringify(_postURLs));
  }).catch(err => {
    console.log("couldn't store post: " + err);
  });
}

var PostStore = assign({}, EventEmitter.prototype, {
  getAll: function() {
    if (!_postURLs) {
      loadAsyncStore().then(fetchFromServer).then(updateAsyncStore);
      return [];
    } else {
      _postURLs = _postURLs.filter(url => (!!url));
      return _postURLs.map(url => _posts[url]);
    }
  },

  emitChange: function() {
    this.emit(CHANGE_EVENT);
  },

  addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

  removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  }
});

AppDispatcher.register(function(action) {
  switch(action.actionType) {
    case 'create-post':
      var username = SettingStore.getUsername();
      var url = APIURL + '/' + username;
      action.post.token = SettingStore.getToken();
      var params = {method: 'POST', body: JSON.stringify(action.post), headers: HEADERS};
      fetch(url, params).then(res => res.json()).then(createPost);
      break;

    case 'edit-post':
      var username = SettingStore.getUsername();
      var key = action.post.key;
      if (!key) key = action.post.created + action.post.id;
      var url = APIURL + '/' + username + '/' + key;
      action.post.token = SettingStore.getToken();
      var params = {method: 'PUT', body: JSON.stringify(action.post), headers: HEADERS};
      fetch(url, params).then(res => res.json()).then(editPost);
      break;

    case 'delete-post':
      var username = SettingStore.getUsername();
      var token = SettingStore.getToken();
      var body = JSON.stringify({token: token});
      var key = action.post.key;
      if (!key) key = action.post.created + action.post.id;
      var url = APIURL + '/' + username + '/' + key;
      var params = {method: 'DELETE', body: body, headers};
      fetch(url, params).then(() => deletePost(action.post));
      break;

  }
});

module.exports = PostStore;
<commit_msg>missing var<commit_after>var APIURL = 'https://1dhhcnzmxi.execute-api.us-east-1.amazonaws.com/v1';
var HEADERS = {'Accept': 'application/json', 'Content-Type': 'application/json'};
var USER_URL = 'https://s3.amazonaws.com/constellational-store';
var POST_URL = 'https://d2nxl7qthm5fu1.cloudfront.net';

var SettingStore = require('../stores/SettingStore');
var AppDispatcher = require('../dispatcher/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
var React = require('react-native');

var {
    AsyncStorage,
} = React;

var CHANGE_EVENT = 'change';

var _posts = null;
var _postURLs = null;

function loadAsyncStore() {
  return AsyncStorage.getItem('posts').then(str => {
    if (!str) _posts = {};
    else _posts = JSON.parse(str);
    return AsyncStorage.getItem('postURLs').then(str => {
      if (!str) _postURLs = [];
      else _postURLs = JSON.parse(str);
      PostStore.emitChange();
    });
  });
}

function createPost(post) {
  _postURLs.unshift(post.url);
  _posts[post.url] = post;
  PostStore.emitChange();
  return updateAsyncStore();
}

function editPost(post) {
  _posts[post.url] = post;
  PostStore.emitChange();
  return updateAsyncStore();
}

function deletePost(post) {
  var i = _postURLs.indexOf(action.post.url);
  _postURLs.splice(i, 1);
  delete _posts[action.post.url];
  PostStore.emitChange();
  return updateAsyncStore();
}

function fetchUser(username) {
  return fetch(USER_URL + '/' + username).then(res => res.json());
}

function fetchPost(username, url) {
  return fetch(POST_URL + '/' + username + '/' + url).then(res => res.json()).then((post) => {
    post.url = url;
    return post;
  });
}

function fetchFromServer() {
  var username = SettingStore.getUsername();
  return fetchUser(username).then((user) => {
    _postURLs = user.posts;
    var promiseArr = user.posts.map(url => fetchPost(username, url));
    return Promise.all(promiseArr);
  }).then((posts) => {
    posts.map((post) => {
      _posts[post.url] = post;
    });
    PostStore.emitChange();
  });
}

function updateAsyncStore() {
  return AsyncStorage.setItem('posts', JSON.stringify(_posts)).then(() => {
    if (_postURLs) return AsyncStorage.setItem('postURLs', JSON.stringify(_postURLs));
  }).catch(err => {
    console.log("couldn't store post: " + err);
  });
}

var PostStore = assign({}, EventEmitter.prototype, {
  getAll: function() {
    if (!_postURLs) {
      loadAsyncStore().then(fetchFromServer).then(updateAsyncStore);
      return [];
    } else {
      _postURLs = _postURLs.filter(url => (!!url));
      return _postURLs.map(url => _posts[url]);
    }
  },

  emitChange: function() {
    this.emit(CHANGE_EVENT);
  },

  addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

  removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  }
});

AppDispatcher.register(function(action) {
  switch(action.actionType) {
    case 'create-post':
      var username = SettingStore.getUsername();
      var url = APIURL + '/' + username;
      action.post.token = SettingStore.getToken();
      var params = {method: 'POST', body: JSON.stringify(action.post), headers: HEADERS};
      fetch(url, params).then(res => res.json()).then(createPost);
      break;

    case 'edit-post':
      var username = SettingStore.getUsername();
      var key = action.post.key;
      if (!key) key = action.post.created + action.post.id;
      var url = APIURL + '/' + username + '/' + key;
      action.post.token = SettingStore.getToken();
      var params = {method: 'PUT', body: JSON.stringify(action.post), headers: HEADERS};
      fetch(url, params).then(res => res.json()).then(editPost);
      break;

    case 'delete-post':
      var username = SettingStore.getUsername();
      var token = SettingStore.getToken();
      var body = JSON.stringify({token: token});
      var key = action.post.key;
      if (!key) key = action.post.created + action.post.id;
      var url = APIURL + '/' + username + '/' + key;
      var params = {method: 'DELETE', body: body, headers: HEADERS};
      fetch(url, params).then(() => deletePost(action.post));
      break;

  }
});

module.exports = PostStore;
<|endoftext|>


<commit_before>var _ = require("lodash"),
    patterns = {
        version:      /^Starting minecraft server version ([.0-9a-zA-Z]+)$/,
        done:         /^Done \([.,0-9a-zA-Z]+\)!/,
        join:         /^(\w+) ?(\[(.+)\] )?logged in with entity id (\d+) at \(([\d\s\-\.,]+)\)$/,
        leave:        /^(\w+) lost connection: (.+)$/,
        bind:         /^\*+ FAILED TO BIND TO PORT\!$/,
        authenticate: /^UUID of player (\w+) is (.+)$/,
        banned:       /^Banned player (\w+)$/,
        unbanned:     /^Unbanned player (\w+)$/,
    };

// modify game info/state
exports.version = function (game, meta) {
    var match = meta.text.match(patterns.version);

    if (match) {
        game.version = match[1];
        game.emit("version", match[1]);
    }
};

exports.authenticated = function(game, meta) {
    if (meta.source && (meta.source.indexOf('User Authenticator #') === 0)) {
        var match = meta.text.match(patterns.authenticate),
            player, uuid;

        if (match) {
            game.emit("authenticated", match[1], match[2]);
        }
    }
}

exports.joined = function (game, meta) {
    var match = meta.text.match(patterns.join),
        player, coord;

    if (match) {
        player = match[1];
        coord = match[5].split(", ");

        game.players.push(player);

        game.emit("joined", player, {
            source: match[3],
            entity: +match[4],
            location: {
                x: parseFloat(coord[0]),
                y: parseFloat(coord[1]),
                z: parseFloat(coord[2])
            }
        });
    }
};

exports.left = function (game, meta) {
    var match = meta.text.match(patterns.leave);

    if (match) {
        game.players = _.without(game.players, match[1]);
        game.emit("left", match[1], match[2]);
    }
};

exports.banned = function (game, meta) {
    var match = meta.text.match(patterns.banned);

    if (match) {
        game.emit("banned", match[1]);
    }
};

exports.unbanned = function (game, meta) {
    var match = meta.text.match(patterns.unbanned);

    if (match) {
        game.emit("unbanned", match[1]);
    }
};

// informational
exports.error = function (game, meta) {
    if (meta.level === "ERROR") {
        game.emit("error", new Error(meta));
    }
};

exports.fail2bind = function (game, meta) {
    if (meta.level === "WARNING" && patterns.bind.test(meta.text)) {
        game.emit("error", new Error(meta));
    }
};

// start/stop
exports.started = function (game, meta) {
    if (patterns.done.test(meta.text)) {
        game.emit("started");
    }
};

// save status
exports.saveoff = function (game, meta) {
    if (meta.text === "Turned off world auto-saving") {
        game.emit("saveoff");
    }
};

exports.saveon = function (game, meta) {
    if (meta.text === "Turned on world auto-saving") {
        game.emit("saveon");
    }
};

exports.saved = function (game, meta) {
    if (meta.text === "Saved the world") {
        game.emit("saved");
    }
};
<commit_msg>also catch ban/unban from operators<commit_after>var _ = require("lodash"),
    patterns = {
        version:      /^Starting minecraft server version ([.0-9a-zA-Z]+)$/,
        done:         /^Done \([.,0-9a-zA-Z]+\)!/,
        join:         /^(\w+) ?(\[(.+)\] )?logged in with entity id (\d+) at \(([\d\s\-\.,]+)\)$/,
        leave:        /^(\w+) lost connection: (.+)$/,
        bind:         /^\*+ FAILED TO BIND TO PORT\!$/,
        authenticate: /^UUID of player (\w+) is (.+)$/,
        banned:       /^(\[(\w+): )?Banned player (\w+)(\])?$/,
        unbanned:     /^(\[(\w+): )?Unbanned player (\w+)(\])?$/
    };

// modify game info/state
exports.version = function (game, meta) {
    var match = meta.text.match(patterns.version);

    if (match) {
        game.version = match[1];
        game.emit("version", match[1]);
    }
};

exports.authenticated = function(game, meta) {
    if (meta.source && (meta.source.indexOf('User Authenticator #') === 0)) {
        var match = meta.text.match(patterns.authenticate),
            player, uuid;

        if (match) {
            game.emit("authenticated", match[1], match[2]);
        }
    }
}

exports.joined = function (game, meta) {
    var match = meta.text.match(patterns.join),
        player, coord;

    if (match) {
        player = match[1];
        coord = match[5].split(", ");

        game.players.push(player);

        game.emit("joined", player, {
            source: match[3],
            entity: +match[4],
            location: {
                x: parseFloat(coord[0]),
                y: parseFloat(coord[1]),
                z: parseFloat(coord[2])
            }
        });
    }
};

exports.left = function (game, meta) {
    var match = meta.text.match(patterns.leave);

    if (match) {
        game.players = _.without(game.players, match[1]);
        game.emit("left", match[1], match[2]);
    }
};

exports.banned = function (game, meta) {
    var match = meta.text.match(patterns.banned);

    if (match) {
        game.emit("banned", match[2], match[3]);
    }
};

exports.unbanned = function (game, meta) {
    var match = meta.text.match(patterns.unbanned);

    if (match) {
        game.emit("unbanned", match[2], match[3]);
    }
};

// informational
exports.error = function (game, meta) {
    if (meta.level === "ERROR") {
        game.emit("error", new Error(meta));
    }
};

exports.fail2bind = function (game, meta) {
    if (meta.level === "WARNING" && patterns.bind.test(meta.text)) {
        game.emit("error", new Error(meta));
    }
};

// start/stop
exports.started = function (game, meta) {
    if (patterns.done.test(meta.text)) {
        game.emit("started");
    }
};

// save status
exports.saveoff = function (game, meta) {
    if (meta.text === "Turned off world auto-saving") {
        game.emit("saveoff");
    }
};

exports.saveon = function (game, meta) {
    if (meta.text === "Turned on world auto-saving") {
        game.emit("saveon");
    }
};

exports.saved = function (game, meta) {
    if (meta.text === "Saved the world") {
        game.emit("saved");
    }
};
<|endoftext|>


<commit_before>/*!
Copyright 2020 Caf.js Labs and contributors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
/**
 * Processing pipeline for enabling CDN.
 *
 * Properties:
 *
 *      {appCDN: string=, appLocalName: string=, appSubdirCDN: string=}
 *
 * where:
 *
 * * `appCDN`: a url prefix for your CDN, or `undefined` or '' to replace by an
 * empty string.
 * `appLocalName`: the local name of the app. If `appCDN` is not a falsy,
 * it should not be a falsy.
 * `appSubdirCDN`: an optional sub directory to cache invalidate a CDN.
 *
 * When `appCDN` is `https://cdn-2332.kxcdn.com`, `appLocalName` is `myApp`,
 * and `appSubdirCDN` is `v1.01` occurrences in html and js files of
 *`json_rpc.CDN` are replaced by `https://cdn-2332.kxcdn.com/myApp/v1.01`
 *
 * @module caf_platform/pipe_cdn
 * @augments module:caf_platform/gen_pipe
 */
// @ts-ignore: augments not attached to a class
const assert = require('assert');
const gen_pipe = require('./gen_pipe');
const {stringReplace} = require('string-replace-middleware');
const json_rpc = require('caf_transport').json_rpc;
const url = require('url');

exports.newInstance = async function($, spec) {
    try {
        spec.env.appCDN &&
            assert.equal(typeof spec.env.appCDN, 'string',
                         "'spec.env.appCDN' is not a string");
        const appCDN = spec.env.appCDN || '';

        spec.env.appLocalName &&
            assert.equal(typeof spec.env.appLocalName, 'string',
                         "'spec.env.appLocalName' is not a string");
        const appLocalName = spec.env.appLocalName || '';
        if (appCDN && !appLocalName) {
            throw new Error("CDN needs non-falsy 'spec.env.appLocalName'");
        }

        spec.env.appSubdirCDN &&
                assert.equal(typeof spec.env.appSubdirCDN, 'string',
                             "'spec.env.appSubdirCDN' is not a string");
        const appSubdirCDN = spec.env.appSubdirCDN || '';
        if (appCDN && !appSubdirCDN) {
            throw new Error("CDN needs non-falsy 'spec.env.appSubdirCDN'");
        }

        const that = gen_pipe.create($, spec);

        $._.$.log && $._.$.log.debug('New CDN pipe');

        const removeSubdirCDN = function(req) {
            const reqURL = url.parse(req.url);
            const pathSplit = reqURL.pathname.split('/');
            if ((pathSplit.length >= 3) &&
                (pathSplit[0] === '') &&
                (pathSplit[1] === appLocalName) &&
                (pathSplit[2] === appSubdirCDN)) {
                pathSplit.shift();
                pathSplit.shift();
                pathSplit.shift();
                pathSplit.unshift('');
                reqURL.pathname = pathSplit.join('/');
                req.url = url.format(reqURL);
            }
        };

        that.__ca_connectSetup__ = function(app) {
            const replacePath = appCDN ?
                appCDN + '/' + appLocalName + '/' + appSubdirCDN :
                '';
            const options = {
                contentTypeFilterRegexp:
                    /^text\/|^application\/json$|^application\/javascript/
            };
            const cdnKey = {};
            cdnKey[json_rpc.CDN] = replacePath;
            // Cannot figure out cdnKey is 'Record<string, string>'
            // @ts-ignore
            const replace = stringReplace(cdnKey, options);

            app.use(spec.env.path, function(req, res, next) {
                //backwards compatibility
                res.get = function(field){
                    return this.getHeader(field);
                };

                removeSubdirCDN(req);
                replace(req, res, next);
            });
        };

        return [null, that];
    } catch (err) {
        return [err];
    }
};
<commit_msg>Fix cdn path demangling<commit_after>/*!
Copyright 2020 Caf.js Labs and contributors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
/**
 * Processing pipeline for enabling CDN.
 *
 * Properties:
 *
 *      {appCDN: string=, appLocalName: string=, appSubdirCDN: string=}
 *
 * where:
 *
 * * `appCDN`: a url prefix for your CDN, or `undefined` or '' to replace by an
 * empty string.
 * `appLocalName`: the local name of the app. If `appCDN` is not a falsy,
 * it should not be a falsy.
 * `appSubdirCDN`: an optional sub directory to cache invalidate a CDN.
 *
 * When `appCDN` is `https://cdn-2332.kxcdn.com`, `appLocalName` is `myApp`,
 * and `appSubdirCDN` is `v1.01` occurrences in html and js files of
 *`json_rpc.CDN` are replaced by `https://cdn-2332.kxcdn.com/myApp/v1.01`
 *
 * @module caf_platform/pipe_cdn
 * @augments module:caf_platform/gen_pipe
 */
// @ts-ignore: augments not attached to a class
const assert = require('assert');
const gen_pipe = require('./gen_pipe');
const {stringReplace} = require('string-replace-middleware');
const json_rpc = require('caf_transport').json_rpc;
const url = require('url');

exports.newInstance = async function($, spec) {
    try {
        spec.env.appCDN &&
            assert.equal(typeof spec.env.appCDN, 'string',
                         "'spec.env.appCDN' is not a string");
        const appCDN = spec.env.appCDN || '';

        spec.env.appLocalName &&
            assert.equal(typeof spec.env.appLocalName, 'string',
                         "'spec.env.appLocalName' is not a string");
        const appLocalName = spec.env.appLocalName || '';
        if (appCDN && !appLocalName) {
            throw new Error("CDN needs non-falsy 'spec.env.appLocalName'");
        }

        spec.env.appSubdirCDN &&
                assert.equal(typeof spec.env.appSubdirCDN, 'string',
                             "'spec.env.appSubdirCDN' is not a string");
        const appSubdirCDN = spec.env.appSubdirCDN || '';
        if (appCDN && !appSubdirCDN) {
            throw new Error("CDN needs non-falsy 'spec.env.appSubdirCDN'");
        }

        const that = gen_pipe.create($, spec);

        $._.$.log && $._.$.log.debug('New CDN pipe');

        const removeSubdirCDN = function(req) {
            const reqURL = url.parse(req.url);
            const pathSplit = reqURL.pathname.split('/');
            // appLocalName subdir already removed by nginx
            if ((pathSplit.length >= 2) &&
                (pathSplit[0] === '') &&
                (pathSplit[1] === appSubdirCDN)) {
                pathSplit.shift();
                pathSplit.shift();
                pathSplit.unshift('');
                reqURL.pathname = pathSplit.join('/');
                req.url = url.format(reqURL);
            }
        };

        that.__ca_connectSetup__ = function(app) {
            const replacePath = appCDN ?
                appCDN + '/' + appLocalName + '/' + appSubdirCDN :
                '';
            const options = {
                contentTypeFilterRegexp:
                    /^text\/|^application\/json$|^application\/javascript/
            };
            const cdnKey = {};
            cdnKey[json_rpc.CDN] = replacePath;
            // Cannot figure out cdnKey is 'Record<string, string>'
            // @ts-ignore
            const replace = stringReplace(cdnKey, options);

            app.use(spec.env.path, function(req, res, next) {
                //backwards compatibility
                res.get = function(field){
                    return this.getHeader(field);
                };

                removeSubdirCDN(req);
                replace(req, res, next);
            });
        };

        return [null, that];
    } catch (err) {
        return [err];
    }
};
<|endoftext|>


<commit_before>'use strict';

var Message = Message || require('./Message');

var RootCerts = require('./common/RootCerts');

var PayPro = require('./common/PayPro');

var KJUR = require('jsrsasign');

var asn1 = require('asn1.js');
var rfc3280 = require('asn1.js/rfc/3280');

PayPro.prototype.x509Sign = function(key) {
  var self = this;
  var crypto = require('crypto');
  var pki_type = this.get('pki_type');
  var pki_data = this.get('pki_data'); // contains one or more x509 certs
  pki_data = PayPro.X509Certificates.decode(pki_data);
  pki_data = pki_data.certificate;
  var details = this.get('serialized_payment_details');
  var type = pki_type.split('+')[1].toUpperCase();

  var trusted = pki_data.map(function(cert) {
    var der = cert.toString('hex');
    var pem = self._DERtoPEM(der, 'CERTIFICATE');
    return RootCerts.getTrusted(pem);
  });

  // XXX Figure out what to do here
  if (!trusted.length) {
    // throw new Error('Unstrusted certificate.');
  } else {
    trusted.forEach(function(name) {
      // console.log('Certificate: %s', name);
    });
  }

  var signature = crypto.createSign('RSA-' + type);
  var buf = this.serializeForSig();
  signature.update(buf);
  var sig = signature.sign(key);
  return sig;
};

PayPro.prototype.x509Verify = function() {
  var self = this;
  var crypto = require('crypto');
  var pki_type = this.get('pki_type');
  var sig = this.get('signature');
  var pki_data = this.get('pki_data');
  pki_data = PayPro.X509Certificates.decode(pki_data);
  pki_data = pki_data.certificate;
  var details = this.get('serialized_payment_details');
  var buf = this.serializeForSig();
  var type = pki_type.split('+')[1].toUpperCase();

  var verifier = crypto.createVerify('RSA-' + type);
  verifier.update(buf);

  var signedCert = pki_data[0];
  var der = signedCert.toString('hex');
  var pem = this._DERtoPEM(der, 'CERTIFICATE');
  var verified = verifier.verify(pem, sig);

  var chain = pki_data;

  // Verifying the cert chain:
  // 1. Extract public key from next certificate.
  // 2. Extract signature from current certificate.
  // 3. If current cert is not trusted, verify that the current cert is signed
  //    by NEXT by the certificate.
  // NOTE: XXX What to do when the certificate is revoked?

  var chainVerified = chain.every(function(cert, i) {
    var der = cert.toString('hex');
    var pem = self._DERtoPEM(der, 'CERTIFICATE');
    var name = RootCerts.getTrusted(pem);

    var ncert = chain[i + 1];
    // The root cert, check if it's trusted:
    if (!ncert || name) {
      if (!ncert && !name) {
        return false;
      }
      chain.length = 0;
      return true;
    }
    var nder = ncert.toString('hex');
    var npem = self._DERtoPEM(nder, 'CERTIFICATE');

    //
    // Get Public Key from next certificate:
    //
    var ndata = new Buffer(nder, 'hex');
    var nc = rfc3280.Certificate.decode(ndata, 'der');
    var npubKeyAlg = PayPro.getAlgorithm(
      nc.tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm);
    var npubKey = nc.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
    npubKey = self._DERtoPEM(npubKey, npubKeyAlg + ' PUBLIC KEY');

    //
    // Get Signature Value from current certificate:
    //
    var data = new Buffer(der, 'hex');
    var c = rfc3280.Certificate.decode(data, 'der');
    var sigAlg = PayPro.getAlgorithm(c.signatureAlgorithm.algorithm, 1);
    var sig = c.signature.data;

    //
    // Check the Issuer matches the Subject of the next certificate:
    //
    var issuer = c.tbsCertificate.issuer;
    var subject = nc.tbsCertificate.subject;
    var issuerVerified = issuer.type === subject.type && issuer.value.every(function(issuerArray, i) {
      var subjectArray = subject.value[i];
      return issuerArray.every(function(issuerObject, i) {
        var subjectObject = subjectArray[i];

        var issuerObjectType = issuerObject.type.join('.');
        var subjectObjectType = subjectObject.type.join('.');

        var issuerObjectValue = issuerObject.value.toString('hex');
        var subjectObjectValue = subjectObject.value.toString('hex');

        return issuerObjectType === subjectObjectType
          && issuerObjectValue === subjectObjectValue;
      });
    });

    //
    // Handle Cert Extensions
    // http://tools.ietf.org/html/rfc5280#section-4.2
    //

    var ext;
    var eid;
    var extensions = {
      basicConstraints: null,
      keyUsage: null,
      subjectKeyIdentifier: null,
      authKeyIdentifier: null,
      CRLDistributionPoints: null,
      certificatePolicies: null,
      standardUnknown: [],
      unknown: [],
    };

    for (var i = 0; i < nc.tbsCertificate.extensions.length; i++) {
      ext = nc.tbsCertificate.extensions[i];
      eid = ext.extnID;
      if (eid.length === 4 && eid[0] === 2 && eid[1] === 5 && eid[2] === 29) {
        switch (eid[3]) {
          // Basic Constraints
          case 19:
            extensions.basicConstraints = ext;
            break;
          // Key Usage
          case 15:
            extensions.keyUsage = ext;
            break;
          // Subject Key Identifier
          case 14:
            extensions.subjectKeyIdentifier = ext;
            break;
          // Authority Key Identifier
          case 35:
            extensions.authKeyIdentifier = ext;
            break;
          // CRL Distribution Points
          case 31:
            extensions.CRLDistributionPoints = ext;
            break;
          // Certificate Policies
          case 32:
            extensions.certificatePolicies = ext;
            break;
          // Unknown Extension (not documented anywhere, probably non-standard)
          default:
            extensions.standardUnknown.push(ext);
            break;
        }
      } else {
        extensions.unknown.push(ext);
      }
    }

    print(c);
    print(nc);
    print('issuerVerified: %s', issuerVerified);
    print(extensions);

    //
    // Create a To-Be-Signed Certificate to verify using asn1.js:
    //
    var tbs = rfc3280.TBSCertificate.encode(c.tbsCertificate, 'der');

    //
    // Verify current certificate signature:
    //
    var verifier = crypto.createVerify('RSA-' + sigAlg);
    verifier.update(tbs);
    return verifier.verify(npubKey, sig) && issuerVerified;
  });

  return verified && chainVerified;
};

var util = require('util');
function inspect(obj) {
  return typeof obj !== 'string'
    ? util.inspect(obj, false, 20, true)
    : obj;
}
function print(obj) {
  return typeof obj === 'object'
    ? process.stdout.write(inspect(obj) + '\n')
    : console.log.apply(console, arguments);
}

module.exports = PayPro;
<commit_msg>paypro: check validity time - cert expiration.<commit_after>'use strict';

var Message = Message || require('./Message');

var RootCerts = require('./common/RootCerts');

var PayPro = require('./common/PayPro');

var KJUR = require('jsrsasign');

var asn1 = require('asn1.js');
var rfc3280 = require('asn1.js/rfc/3280');

PayPro.prototype.x509Sign = function(key) {
  var self = this;
  var crypto = require('crypto');
  var pki_type = this.get('pki_type');
  var pki_data = this.get('pki_data'); // contains one or more x509 certs
  pki_data = PayPro.X509Certificates.decode(pki_data);
  pki_data = pki_data.certificate;
  var details = this.get('serialized_payment_details');
  var type = pki_type.split('+')[1].toUpperCase();

  var trusted = pki_data.map(function(cert) {
    var der = cert.toString('hex');
    var pem = self._DERtoPEM(der, 'CERTIFICATE');
    return RootCerts.getTrusted(pem);
  });

  // XXX Figure out what to do here
  if (!trusted.length) {
    // throw new Error('Unstrusted certificate.');
  } else {
    trusted.forEach(function(name) {
      // console.log('Certificate: %s', name);
    });
  }

  var signature = crypto.createSign('RSA-' + type);
  var buf = this.serializeForSig();
  signature.update(buf);
  var sig = signature.sign(key);
  return sig;
};

PayPro.prototype.x509Verify = function() {
  var self = this;
  var crypto = require('crypto');
  var pki_type = this.get('pki_type');
  var sig = this.get('signature');
  var pki_data = this.get('pki_data');
  pki_data = PayPro.X509Certificates.decode(pki_data);
  pki_data = pki_data.certificate;
  var details = this.get('serialized_payment_details');
  var buf = this.serializeForSig();
  var type = pki_type.split('+')[1].toUpperCase();

  var verifier = crypto.createVerify('RSA-' + type);
  verifier.update(buf);

  var signedCert = pki_data[0];
  var der = signedCert.toString('hex');
  var pem = this._DERtoPEM(der, 'CERTIFICATE');
  var verified = verifier.verify(pem, sig);

  var chain = pki_data;

  // Verifying the cert chain:
  // 1. Extract public key from next certificate.
  // 2. Extract signature from current certificate.
  // 3. If current cert is not trusted, verify that the current cert is signed
  //    by NEXT by the certificate.
  // NOTE: XXX What to do when the certificate is revoked?

  var chainVerified = chain.every(function(cert, i) {
    var der = cert.toString('hex');
    var pem = self._DERtoPEM(der, 'CERTIFICATE');
    var name = RootCerts.getTrusted(pem);

    var ncert = chain[i + 1];
    // The root cert, check if it's trusted:
    if (!ncert || name) {
      if (!ncert && !name) {
        return false;
      }
      chain.length = 0;
      return true;
    }
    var nder = ncert.toString('hex');
    var npem = self._DERtoPEM(nder, 'CERTIFICATE');

    //
    // Get Public Key from next certificate:
    //
    var ndata = new Buffer(nder, 'hex');
    var nc = rfc3280.Certificate.decode(ndata, 'der');
    var npubKeyAlg = PayPro.getAlgorithm(
      nc.tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm);
    var npubKey = nc.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
    npubKey = self._DERtoPEM(npubKey, npubKeyAlg + ' PUBLIC KEY');

    //
    // Get Signature Value from current certificate:
    //
    var data = new Buffer(der, 'hex');
    var c = rfc3280.Certificate.decode(data, 'der');
    var sigAlg = PayPro.getAlgorithm(c.signatureAlgorithm.algorithm, 1);
    var sig = c.signature.data;

    //
    // Check Validity of Certificates
    //
    var validityVerified = true;
    var now = Date.now();
    var cBefore = c.tbsCertificate.validity.notBefore.value;
    var cAfter = c.tbsCertificate.validity.notAfter.value;
    var nBefore = nc.tbsCertificate.validity.notBefore.value;
    var nAfter = nc.tbsCertificate.validity.notAfter.value;
    if (cBefore > now || cAfter < now || nBefore > now || nAfter < now) {
      validityVerified = false;
    }

    //
    // Check the Issuer matches the Subject of the next certificate:
    //
    var issuer = c.tbsCertificate.issuer;
    var subject = nc.tbsCertificate.subject;
    var issuerVerified = issuer.type === subject.type && issuer.value.every(function(issuerArray, i) {
      var subjectArray = subject.value[i];
      return issuerArray.every(function(issuerObject, i) {
        var subjectObject = subjectArray[i];

        var issuerObjectType = issuerObject.type.join('.');
        var subjectObjectType = subjectObject.type.join('.');

        var issuerObjectValue = issuerObject.value.toString('hex');
        var subjectObjectValue = subjectObject.value.toString('hex');

        return issuerObjectType === subjectObjectType
          && issuerObjectValue === subjectObjectValue;
      });
    });

    //
    // Handle Cert Extensions
    // http://tools.ietf.org/html/rfc5280#section-4.2
    //
    var ext;
    var eid;
    var extensions = {
      basicConstraints: null,
      keyUsage: null,
      subjectKeyIdentifier: null,
      authKeyIdentifier: null,
      CRLDistributionPoints: null,
      certificatePolicies: null,
      standardUnknown: [],
      unknown: [],
    };

    for (var i = 0; i < nc.tbsCertificate.extensions.length; i++) {
      ext = nc.tbsCertificate.extensions[i];
      eid = ext.extnID;
      if (eid.length === 4 && eid[0] === 2 && eid[1] === 5 && eid[2] === 29) {
        switch (eid[3]) {
          // Basic Constraints
          case 19:
            extensions.basicConstraints = ext.extnValue;
            break;
          // Key Usage
          case 15:
            extensions.keyUsage = ext.extnValue;
            break;
          // Subject Key Identifier
          case 14:
            extensions.subjectKeyIdentifier = ext.extnValue;
            break;
          // Authority Key Identifier
          case 35:
            extensions.authKeyIdentifier = ext.extnValue;
            break;
          // CRL Distribution Points
          case 31:
            extensions.CRLDistributionPoints = ext.extnValue;
            break;
          // Certificate Policies
          case 32:
            extensions.certificatePolicies = ext.extnValue;
            break;
          // Unknown Extension (not documented anywhere, probably non-standard)
          default:
            extensions.unknown.push(ext);
            extensions.standardUnknown.push(ext);
            break;
        }
      } else {
        extensions.unknown.push(ext);
      }
    }

    var rejectUnknown = !!extensions.unknown.filter(function(ext) {
      return ext.critical;
    }).length;

    print(c);
    print(nc);
    print(extensions);
    print('issuerVerified: %s', issuerVerified);
    print('rejectUnknown: %s', rejectUnknown);
    print('validityVerified: %s', validityVerified);

    //
    // Create a To-Be-Signed Certificate to verify using asn1.js:
    //
    var tbs = rfc3280.TBSCertificate.encode(c.tbsCertificate, 'der');

    //
    // Verify current certificate signature:
    //
    var verifier = crypto.createVerify('RSA-' + sigAlg);
    verifier.update(tbs);
    return verifier.verify(npubKey, sig) && issuerVerified;
  });

  return verified && chainVerified;
};

var util = require('util');
function inspect(obj) {
  return typeof obj !== 'string'
    ? util.inspect(obj, false, 20, true)
    : obj;
}
function print(obj) {
  return typeof obj === 'object'
    ? process.stdout.write(inspect(obj) + '\n')
    : console.log.apply(console, arguments);
}

module.exports = PayPro;
<|endoftext|>


<commit_before>/*jslint browser: false, node: true, maxlen: 128 */

"use strict";

var cluster, merge, defaults;

cluster = require('cluster');
merge   = require("lodash.merge");

defaults = {
    "privs": {
        "user":  "nobody",
        "group": "nogroup"
    },
    "waitTimeout": 30000,
    "numWorkers": require('os').cpus().length,
    "silent": true
};

/**
 * execute()
 * Sets up a pluribus cluster
 * @param label The label for the process (required)
 * @param config Configuration options (optional)
 * */
function execute(label, config) {

    var options = merge({}, defaults, config);

    /**
     * log()
     * Writes log lines, using console.log, with some nice meta data
     * @todo Allow user to specify external loggers
     */
    function log() {

        var args;

        if (options.silent) {
            return;
        }

        // Convert arguments into real array
        args = Array.prototype.slice.call(arguments);

        // Add in some meta data. Who am I?
        args.unshift(cluster.isMaster ? "(master)" : "(worker)");

        // Process ID
        args.unshift("[" + process.pid + "]:");

        // Current datestamp
        args.unshift((new Date()).toISOString());

        // Send to the logger
        console.log.apply(console, args);
    }

    /**
     * messageFromWorker()
     * Listens for messages from the workers. If it get a pluribus-noprivs message, then
     * abort entire cluster, because shizzle is going DOWN.
     */
    function messageFromWorker(msg) {
        if (msg === "pluribus-noprivs") {
            log("Got noprivs message from a worker - shut down");
            process.exit(1);
        }
    }

    /**
     * spawnWorker()
     * Creates and configures a new worker process
     */
    function spawnWorker() {

        var worker;

        // Create a new worker
        worker = cluster.fork();

        // Automatically respawn the workers on exit
        worker.on("exit", function () {
            log("Spawning new worker to replace", worker.process.pid);
            spawnWorker();
        });

        // Listen for messages from this worker
        worker.on("message", messageFromWorker);
    }

    /**
     * stopRespawning()
     * Prevents the master process from respawning new workers when the old ones die
     */
    function stopRespawning() {
        Object.keys(cluster.workers).forEach(function (id) {
            cluster.workers[id].removeAllListeners("exit");
        });
    }

    /**
     * closeAllWorkers()
     * Tells all the worker processes to finish what they are doing and abort.
     * If they're too slow about it, then we kill them anyway.
     */
    function closeAllWorkers() {
        Object.keys(cluster.workers).forEach(function (id) {

            var timeout, worker;

            // Get a handle on the worker process
            worker = cluster.workers[id];

            // Tell the worker to shutdown
            worker.send("pluribus-shutdown");
            worker.disconnect();

            // If the worker is too slow, shut it down anyway
            timeout = setTimeout(function () {
                log("Forcing", worker.process.pid, "to shutdown");
                worker.kill("SIGKILL");
            }, options.waitTimeout);

            // If the worker shuts down okay, cancel the forced shutdown
            worker.on("exit", function () {
                log("Worker", worker.process.pid, "exited");
                clearTimeout(timeout);
            });
        });
    }

    // If we are the master process, set up as a master process
    if (cluster.isMaster) {

        log("Starting");

        // Set the process title to show who we are
        process.title = label + ': master process (' + process.title + ')';

        // On SIGHUP, terminate and respawn all our workers
        process.on("SIGHUP", function shouldHangup() {

            log("Got SIGHUP - reloading all workers");

            // Shut down all the workers (they will respawn automatically)
            closeAllWorkers();
        });

        // On SIGINT, cleanly shut down the workers and let the master drop out
        process.on("SIGINT", function shouldShutdown() {

            log("Got SIGINT - closing down");

            // Prevent respawns, then shut down the workers
            stopRespawning();
            closeAllWorkers();
        });

        // On SIGTERM, cleanly shut down the workers and force the master to drop out if
        // it doesn't drop out automatically within the timeout period
        process.on("SIGTERM", function mustShutdown() {

            var killtimer;

            log("Got SIGTERM - halting");

            // Clean up our worker processes
            stopRespawning();
            closeAllWorkers();

            // If we don't exit quickly enough, force exit
            killtimer = setTimeout(function () {
                process.exit(1);
            }, options.waitTimeout);

            // Don't let this timer hold the process open though
            killtimer.unref();
        });

        // Spawn the appropriate number of worker processes
        while (Object.keys(cluster.workers).length < options.numWorkers) {
            spawnWorker();
        }

        // If the user passed us a master function to run, call that function
        // and pass the cluster object in there so they can talk to workers and stuff.
        if (typeof options.master === "function") {
            log('Running ' + label + ' master method');
            options.master(cluster);
        }

    // Otherwise, we are a worker process - configure as a worker
    } else {

        log("Starting");

        // Set the process title to show who we are
        process.title = label + ': worker process';

        // Downgrade privileges for workers, if the options are set in config
        // We must downgrade group first, otherwise won't have permission to
        // change group because we just changed user.
        if (process.getgid && process.setgid) {

            try {

                // Decrement privileges
                process.setgid(options.privs.group);
                process.setuid(options.privs.user);

            } catch (exception) {

                // If we failed to change our privileges, then notify the master process
                console.error(exception);
                log("Refusing to spawn with elevated privileges");
                process.send("pluribus-noprivs");
                return;
            }

        } else {

            // User didn't specify privileges, or privileges are not supported on this
            // platform, so tell the user we have elevated privileges and continue.
            log("CAUTION: I am running with privileges of the master");
        }

        // If the user passed a worker function, execute that now.
        if (typeof options.worker === "function") {
            options.worker();
        }
    }
}

// Expose public API
module.exports.execute = execute;
<commit_msg>restart on changes, allow manual restart<commit_after>/*jslint browser: false, node: true, maxlen: 128 */

"use strict";

var cluster, watch, lodash, path, defaults;

cluster = require('cluster');
lodash   = require("lodash");
watch   = require("glob-watcher");
path = require("path");

defaults = {
    "privs": {
        "user":  "nobody",
        "group": "nogroup"
    },
    "waitTimeout": 30000,
    "numWorkers": require('os').cpus().length,
    "silent": true
};

/**
 * execute()
 * Sets up a pluribus cluster
 * @param label The label for the process (required)
 * @param config Configuration options (optional)
 * */
function execute(label, config) {

    var globs, watcher, options;

    options = lodash.defaults(config, defaults);

    /**
     * log()
     * Writes log lines, using console.log, with some nice meta data
     * @todo Allow user to specify external loggers
     */
    function log() {

        var args;

        if (options.silent) {
            return;
        }

        // Convert arguments into real array
        args = Array.prototype.slice.call(arguments);

        // Add in some meta data. Who am I?
        args.unshift(cluster.isMaster ? "(master)" : "(worker)");

        // Process ID
        args.unshift("[" + process.pid + "]:");

        // Current datestamp
        args.unshift((new Date()).toISOString());

        // Send to the logger
        console.log.apply(console, args);
    }

    /**
     * messageFromWorker()
     * Listens for messages from the workers. If it get a pluribus-noprivs message, then
     * abort entire cluster, because shizzle is going DOWN.
     */
    function messageFromWorker(msg) {
        if (msg === "pluribus-noprivs") {
            log("Got noprivs message from a worker - shut down");
            process.exit(1);
        }
    }

    /**
     * spawnWorker()
     * Creates and configures a new worker process
     */
    function spawnWorker() {

        var worker;

        // Create a new worker
        worker = cluster.fork();

        // Automatically respawn the workers on exit
        worker.on("exit", function () {
            log("Spawning new worker to replace", worker.process.pid);
            spawnWorker();
        });

        // Listen for messages from this worker
        worker.on("message", messageFromWorker);
    }

    /**
     * stopRespawning()
     * Prevents the master process from respawning new workers when the old ones die
     */
    function stopRespawning() {
        Object.keys(cluster.workers).forEach(function (id) {
            cluster.workers[id].removeAllListeners("exit");
        });
    }

    /**
     * closeAllWorkers()
     * Tells all the worker processes to finish what they are doing and abort.
     * If they're too slow about it, then we kill them anyway.
     */
    function closeAllWorkers() {
        Object.keys(cluster.workers).forEach(function (id) {

            var timeout, worker;

            // Get a handle on the worker process
            worker = cluster.workers[id];

            // Tell the worker to shutdown
            worker.send("pluribus-shutdown");
            worker.disconnect();

            // If the worker is too slow, shut it down anyway
            timeout = setTimeout(function () {
                log("Forcing", worker.process.pid, "to shutdown");
                worker.kill("SIGKILL");
            }, options.waitTimeout);

            // If the worker shuts down okay, cancel the forced shutdown
            worker.on("exit", function () {
                log("Worker", worker.process.pid, "exited");
                clearTimeout(timeout);
            });
        });
    }

    // If we are the master process, set up as a master process
    if (cluster.isMaster) {

        log("Starting");

        // Set the process title to show who we are
        process.title = label + ': master process (' + process.title + ')';

        // Allow the user to restart manually
        process.stdin.setEncoding("utf8");
        process.stdin.on('readable', function () {

            var phrase = process.stdin.read();

            if (phrase !== null) {
                // Remove the newline character from the input
                phrase = phrase.replace("\n", "");

                if (["rs", "restart"].indexOf(phrase.toLowerCase()) >= 0) {
                    options.waitTimeout = 10;
                    closeAllWorkers();
                } else {
                    log(phrase + " is not valid command line input");
                    log("Type 'rs' or 'restart' to restart your application");
                }
            }
        });

        // If a flag was passed in, validate it.
        if (options.watch) {
            options.watch = options.watch.toLowerCase();

            if (["-w", "--w", "-watch", "--watch"].indexOf(options.watch) < 0) {
                log(options.watch + " is not a valid pluribus option flag");
                log("To enable file watch, use --watch");
            } else {

                // A valid watch flag was passed in. Watch for file changes.
                log("Watching for changes...");
                options.waitTimeout = 10;

                // If a globs array was supplied, transform it into one with true paths.
                // If not, default to the node execution path.
                // Note that this is the path of the application which required pluribus,
                // not the directory in which pluribus was found.

                /*jslint nomen:true*/
                globs = options.globs ? options.globs.map(function (glob) {
                    return path.join(path.resolve("."), glob);
                }) : [path.resolve(".")];
                /*jslint nomen:false*/

                console.log(globs);

                // Watch the globs we have specified.
                watcher = watch(globs);
                watcher.on("change", function (evt) {
                    // On a file change event, close all workers.
                    // They will restart automatically.
                    if (evt.type === "changed") {
                        log("Restarting due to changes...");
                        closeAllWorkers();
                    }
                });
            }
        }

        // On SIGHUP, terminate and respawn all our workers
        process.on("SIGHUP", function shouldHangup() {

            log("Got SIGHUP - reloading all workers");

            // Shut down all the workers (they will respawn automatically)
            closeAllWorkers();
        });

        // On SIGINT, cleanly shut down the workers and let the master drop out
        process.on("SIGINT", function shouldShutdown() {

            log("Got SIGINT - closing down");

            // Close the watcher, stop listening for input
            if (watcher) {
                watcher.end();
            }

            process.stdin.unref();

            // Prevent respawns, then shut down the workers.
            stopRespawning();
            closeAllWorkers();
        });

        // On SIGTERM, cleanly shut down the workers and force the master to drop out if
        // it doesn't drop out automatically within the timeout period
        process.on("SIGTERM", function mustShutdown() {

            var killtimer;

            log("Got SIGTERM - halting");

            // Close the watcher, stop listening for input
            if (watcher) {
                watcher.end();
            }

            process.stdin.unref();

            // Clean up our worker processes
            stopRespawning();
            closeAllWorkers();

            // If we don't exit quickly enough, force exit
            killtimer = setTimeout(function () {
                process.exit(1);
            }, options.waitTimeout);

            // Don't let this timer hold the process open though
            killtimer.unref();
        });

        // Spawn the appropriate number of worker processes
        while (Object.keys(cluster.workers).length < options.numWorkers) {
            spawnWorker();
        }

        // If the user passed us a master function to run, call that function
        // and pass the cluster object in there so they can talk to workers and stuff.
        if (typeof options.master === "function") {
            log('Running ' + label + ' master method');
            options.master(cluster);
        }

    // Otherwise, we are a worker process - configure as a worker
    } else {

        log("Starting");
        log("Type 'rs' to restart");

        // Set the process title to show who we are
        process.title = label + ': worker process';

        // Downgrade privileges for workers, if the options are set in config
        // We must downgrade group first, otherwise won't have permission to
        // change group because we just changed user.
        if (process.getgid && process.setgid) {

            try {

                // Decrement privileges
                process.setgid(options.privs.group);
                process.setuid(options.privs.user);

            } catch (exception) {

                // If we failed to change our privileges, then notify the master process
                console.error(exception);
                log("Refusing to spawn with elevated privileges");
                process.send("pluribus-noprivs");
                return;
            }

        } else {

            // User didn't specify privileges, or privileges are not supported on this
            // platform, so tell the user we have elevated privileges and continue.
            log("CAUTION: I am running with privileges of the master");
        }

        // If the user passed a worker function, execute that now.
        if (typeof options.worker === "function") {
            options.worker();
        }
    }
}

// Expose public API
module.exports.execute = execute;
<|endoftext|>


<commit_before>'use strict';

var Bin = require('bin-wrapper');
var path = require('path');

var options = {
  name: 'pngcrush',
  bin: 'pngcrush',
  path: path.join(__dirname, '../vendor'),
  url: 'https://raw.github.com/1000ch/node-pngcrush-bin/master/vendor/pngcrush',
  src: 'http://downloads.sourceforge.net/project/pmt/pngcrush/1.7.70/pngcrush-1.7.70.zip',
  buildScript: 'make && mv ./pngcrush ' + path.join(__dirname, '../vendor'),
  platform: {
    osx: {
      url: 'https://raw.github.com/1000ch/node-pngcrush-bin/master/vendor/osx/pngcrush'
    },
    linux: {
      url: 'https://raw.github.com/1000ch/node-pngcrush-bin/master/vendor/linux/x86/pngcrush'
    }
  }
};
var bin = new Bin(options);

exports.bin = bin;
exports.options = options;
exports.path = bin.path;<commit_msg>fix broken link #1<commit_after>'use strict';

var Bin = require('bin-wrapper');
var path = require('path');

var options = {
  name: 'pngcrush',
  bin: 'pngcrush',
  path: path.join(__dirname, '../vendor'),
  url: 'https://raw.github.com/1000ch/node-pngcrush-bin/master/vendor/pngcrush',
  src: 'http://downloads.sourceforge.net/project/pmt/pngcrush/1.7.72/pngcrush-1.7.72.zip',
  buildScript: 'make && mv ./pngcrush ' + path.join(__dirname, '../vendor'),
  platform: {
    osx: {
      url: 'https://raw.github.com/1000ch/node-pngcrush-bin/master/vendor/osx/pngcrush'
    },
    linux: {
      url: 'https://raw.github.com/1000ch/node-pngcrush-bin/master/vendor/linux/x86/pngcrush'
    }
  }
};
var bin = new Bin(options);

exports.bin = bin;
exports.options = options;
exports.path = bin.path;<|endoftext|>


<commit_before>var Projects, _isUndefined = require('../mixin');

module.exports = Projects = function (api) {
    this.api = api;
    this.client = api.client;
};

Projects.prototype.list = function (options, cb) {
    var url = '/projects';
    this.client.get(url, {}, cb);
};

Projects.prototype.get = function (options, cb) {
    if (_isUndefined(options, 'id')) {
        return cb(new Error('getting a project requires an id'));
    }

    var url = '/projects/' + options.id;
    this.client.get(url, {}, cb);
};

Projects.prototype.create = function (options, cb) {
    var url = '/projects';
    this.client.post(url, options, cb);
};

Projects.prototype.update = function (options, cb) {
    if (_isUndefined(options, 'id')) {
        return cb(new Error('updating a project requires an id'));
    }

    var url = '/projects/' + options.id;
    delete options.id;
    this.client.put(url, options, cb);
};

Projects.prototype.toggleActivation = function (options, cb) {
    if (_isUndefined(options, 'id')) {
        return cb(new Error('toggling activation on a project requires an id'));
    }

    var url = '/projects/' + options.id + '/toggle';
    this.client.put(url, {}, cb);
};

Projects.prototype.delete = function (options, cb) {
    if (_isUndefined(options, 'id')) {
        return cb(new Error('deleting a project requires an id'));
    }

    var url = '/projects/' + options.id;
    this.client.delete(url, {}, cb);
};
<commit_msg>Added support for retreiving a project's entries by date range<commit_after>var Projects, _isUndefined = require('../mixin');

module.exports = Projects = function (api) {
    this.api = api;
    this.client = api.client;
};

Projects.prototype.list = function (options, cb) {
    var url = '/projects';
    this.client.get(url, {}, cb);
};

Projects.prototype.get = function (options, cb) {
    if (_isUndefined(options, 'id')) {
        return cb(new Error('getting a project requires an id'));
    }

    var url = '/projects/' + options.id;
    this.client.get(url, {}, cb);
};

Projects.prototype.create = function (options, cb) {
    var url = '/projects';
    this.client.post(url, options, cb);
};

Projects.prototype.update = function (options, cb) {
    if (_isUndefined(options, 'id')) {
        return cb(new Error('updating a project requires an id'));
    }

    var url = '/projects/' + options.id;
    delete options.id;
    this.client.put(url, options, cb);
};

Projects.prototype.toggleActivation = function (options, cb) {
    if (_isUndefined(options, 'id')) {
        return cb(new Error('toggling activation on a project requires an id'));
    }

    var url = '/projects/' + options.id + '/toggle';
    this.client.put(url, {}, cb);
};

Projects.prototype.delete = function (options, cb) {
    if (_isUndefined(options, 'id')) {
        return cb(new Error('deleting a project requires an id'));
    }

    var url = '/projects/' + options.id;
    this.client.delete(url, {}, cb);
};

Projects.prototype.entries = function (options, cb) {
    if (_isUndefined(options, 'id')) {
        return cb(new Error('getting project entries requires an id'));
    }
    if (_isUndefined(options, 'from')) {
        return cb(new Error('getting project entries requires a from date'));
    }
    if (_isUndefined(options, 'to')) {
        return cb(new Error('getting project entries requires a to date'));
    }

    var url = '/projects/' + options.id + '/entries?from=' + options.from + '&to=' + options.to;
    this.client.get(url, {}, cb);
};

<|endoftext|>


<commit_before>'use strict';

module.exports = exports = Property;

/**
 * Module dependencies.
 */

var utils = require('./utils');

/**
 * CSS property constructor.
 *
 * @param {String} property
 * @param {String} value
 * @param {Selector} selector the property originates from
 * @param {Integer} priority 0 for normal properties, 2 for !important properties.
 * @param {Array} additional array of integers representing more detailed priorities (sorting)
 * @api public
 */

function Property(prop, value, selector, priority, additionalPriority) {
  this.prop = prop;
  this.value = value;
  this.selector = selector;
  this.priority = priority || 0;
  this.additionalPriority = additionalPriority;
}

/**
 * Compares with another Property based on Selector#specificity.
 *
 * @api public
 */

Property.prototype.compareFunc = function(property) {
  var a = [];
  a.push.apply(a, this.selector.specificity());
  if (this.additionalPriority) {
    a.push.apply(a, this.additionalPriority);
  }
  a[0] += this.priority;
  var b = [];
  b.push.apply(b, property.selector.specificity());
  if (property.additionalPriority) {
    b.push.apply(b, property.additionalPriority);
  }
  b[0] += property.priority;
  return utils.compareFunc(a, b);
};

Property.prototype.compare = function(property) {
  var winner = this.compareFunc(property);
  if (winner === 1) return this;
  return property;
};

/**
 * Returns CSS property
 *
 * @api public
 */

Property.prototype.toString = function() {
  return this.prop + ': ' + this.value.replace(/['"]+/g, '') + ';';
};
<commit_msg>Change optional parameter coding style<commit_after>'use strict';

module.exports = exports = Property;

/**
 * Module dependencies.
 */

var utils = require('./utils');

/**
 * CSS property constructor.
 *
 * @param {String} property
 * @param {String} value
 * @param {Selector} selector the property originates from
 * @param {Integer} priority 0 for normal properties, 2 for !important properties.
 * @param {Array} additional array of integers representing more detailed priorities (sorting)
 * @api public
 */

function Property(prop, value, selector, priority, additionalPriority) {
  this.prop = prop;
  this.value = value;
  this.selector = selector;
  this.priority = priority || 0;
  this.additionalPriority = additionalPriority || [];
}

/**
 * Compares with another Property based on Selector#specificity.
 *
 * @api public
 */

Property.prototype.compareFunc = function(property) {
  var a = [];
  a.push.apply(a, this.selector.specificity());
  a.push.apply(a, this.additionalPriority);
  a[0] += this.priority;
  var b = [];
  b.push.apply(b, property.selector.specificity());
  b.push.apply(b, property.additionalPriority);
  b[0] += property.priority;
  return utils.compareFunc(a, b);
};

Property.prototype.compare = function(property) {
  var winner = this.compareFunc(property);
  if (winner === 1) return this;
  return property;
};

/**
 * Returns CSS property
 *
 * @api public
 */

Property.prototype.toString = function() {
  return this.prop + ': ' + this.value.replace(/['"]+/g, '') + ';';
};
<|endoftext|>