<commit_before>var getNextLink = require("./util").getNextLink;

/**
 * Application
 * @constructor
 * @param {Object} client Catapult client
 */

var Application = function (client) {
	/**
	 * List the user's applications
	 * @param {Object} params Parameters for filtering applications.
	 * @param {Number} [params.size] The maximum number of applications returned by
	 * the query per page (Max size: 1000).
	 * @param {Function} [callback] A callback for the list of applications.
	 * @return {Array[ApplicationResponse]} A promise for the list of applications, has a getNextPage
	 * function if the number of applications returned by the query exceeds the page size.
	 */
	this.list = function (params, callback) {
		var self = this;
		return client.makeRequest({
			path   : "applications",
			method : "GET",
			qs     : params
		})
		.then(function (response) {
			var applicationListResponse = {
				applications : response.body
			};
			var nextLink = getNextLink(response);
			if (nextLink) {
				applicationListResponse.hasMore = true;
				applicationListResponse.getNextPage = function (nextCallback) {
					return self.list(nextLink, nextCallback);
				};
			}
			return applicationListResponse;
		})
		.asCallback(callback);
	};

	/**
	 * Create a new application
	 * @param {Object} params Parameters for creating a new call
	 * @param {String} [params.name] A name you choose for this application.
 	 * @param {String} [params.incomingCallUrl] A URL where call events will be sent for an inbound call.
 	 * This is the endpoint where the Application Platform will send all call events.
	 * Either incomingCallUrl or incomingMessageUrl is required.
 	 * @param {String} [params.incomingCallUrlCallbackTimeout] Determine how long should the platform wait
 	 * for incomingCallUrl's response before timing out in milliseconds.
 	 * @param {String} [params.incomingCallFallbackUrl] The URL used to send the callback
 	 * event if the request to incomingCallUrl fails.
 	 * @param {String} [params.incomingMessageUrl] A URL where message events will be sent for an inbound message.
 	 * This is the endpoint where the Application Platform will send all message events.
 	 * Either incomingMessageUrl or incomingCallUrl is required.
	 * @param {Number} [params.incomingMessageUrlCallbackTimeout] Determine how long should the platform wait for
 	 * incomingMessageUrl's response before timing out in milliseconds.
	 * @param {String} [params.incomingMessageFallbackUrl] The URL used to send the callback event if
	 * the request to incomingMessageUrl fails.
	 * @param {String} [params.callbackHttpMethod] Determine if the callback event should be sent via HTTP GET
 	 * or HTTP POST. Values are "get" or "post", default: "post".
 	 * @param {Boolean} [params.autoAnswer] Determines whether or not an incoming call should be
 	 * automatically answered. Default value is 'true'.
 	 * @param {Function} [callback] A callback for the list of applications
	 * @return {ApplicationResponse} A promise for the newly created application.
	 */
	this.create = function (params, callback) {
		return client.makeRequest({
			path   : "applications",
			method : "POST",
			body   : params
		})
		.then(function (response) {
			var application = params;
			var location = response.headers.location;
			var applicationId = location.substring(location.lastIndexOf("/") + 1);
			application.id = applicationId;
			return application;
		})
		.asCallback(callback);
	};

	/**
	 * Get an application.
	 * @param  {String} applicationId The ID of the application to get.
	 * @param  {Function} [callback] A callback for the application.
	 * @return {ApplicationResponse} A promise for the application.
	 */
	this.get = function (applicationId, callback) {
		return client.makeRequest({
			path   : "applications/" + applicationId,
			method : "GET"
		})
		.then(function (response) {
			return response.body;
		})
		.asCallback(callback);
	};

	/**
	 * Make changes to an application.
	 * @param  {String} applicationId The ID of the application to modify.
	 * @param {Object} params Parameters for creating a new call
	 * @param {String} [params.name] A name you choose for this application.
 	 * @param {String} [params.incomingCallUrl] A URL where call events will be sent for an inbound call.
 	 * This is the endpoint where the Application Platform will send all call events.
	 * Either incomingCallUrl or incomingMessageUrl is required.
 	 * @param {String} [params.incomingCallUrlCallbackTimeout] Determine how long should the platform wait
 	 * for incomingCallUrl's response before timing out in milliseconds.
 	 * @param {String} [params.incomingCallFallbackUrl] The URL used to send the callback
 	 * event if the request to incomingCallUrl fails.
 	 * @param {String} [params.incomingMessageUrl] A URL where message events will be sent for an inbound message.
 	 * This is the endpoint where the Application Platform will send all message events.
 	 * Either incomingMessageUrl or incomingCallUrl is required.
	 * @param {Number} [params.incomingMessageUrlCallbackTimeout] Determine how long should the platform wait for
 	 * incomingMessageUrl's response before timing out in milliseconds.
	 * @param {String} [params.incomingMessageFallbackUrl] The URL used to send the callback event if
	 * the request to incomingMessageUrl fails.
	 * @param {String} [params.callbackHttpMethod] Determine if the callback event should be sent via HTTP GET
 	 * or HTTP POST. Values are "get" or "post", default: "post".
 	 * @param {Boolean} [params.autoAnswer] Determines whether or not an incoming call should be
 	 * automatically answered. Default value is 'true'.
 	 * @param {Function} [callback] A callback for the list of applications
	 */
	this.update = function (applicationId, params, callback) {
		return client.makeRequest({
			path   : "applications/" + applicationId,
			method : "POST",
			body   : params
		})
		.then(function () {
			return;
		})
		.asCallback(callback);
	};

	/**
	 * Delete an application.
	 * @param  {String} applicationId The ID of the application to delete.
	 * @param  {Function} [callback] A callback for the application.
	 */
	this.delete = function (applicationId, callback) {
		return client.makeRequest({
			path   : "applications/" + applicationId,
			method : "DELETE"
		})
		.then(function () {
			return;
		})
		.asCallback(callback);
	};

};

module.exports = Application;

/**
 * @class ApplicationResponse
 * @type {Object}
 * @property {String} id The unique identifier for the application.
 * @property {String} name A name you choose for this application.
 * @property {String} incomingCallUrl A URL where call events will be sent for an inbound call.
 * This is the endpoint where the Application Platform will send all call events.
 * Either incomingCallUrl or incomingMessageUrl is required.
 * @property {String} incomingCallUrlCallbackTimeout Determine how long should the platform wait
 * for incomingCallUrl's response before timing out in milliseconds.
 * @property {String} incomingCallFallbackUrl The URL used to send the callback
 * event if the request to incomingCallUrl fails.
 * @property {String} callbackHttpMethod Determine if the callback event should be sent via HTTP GET
 * or HTTP POST. Values are "get" or "post", default: "post".
 * @property {Boolean} autoAnswer Determines whether or not an incoming call should be
 * automatically answered. Default value is 'true'.
 * @property {String} incomingMessageUrl A URL where message events will be sent for an inbound message.
 * This is the endpoint where the Application Platform will send all message events.
 * Either incomingMessageUrl or incomingCallUrl is required.
 * @property {Number} incomingMessageUrlCallbackTimeout Determine how long should the platform wait for
 * incomingMessageUrl's response before timing out in milliseconds.
 * @property {String} incomingMessageFallbackUrl The URL used to send the callback event if
 * the request to incomingMessageUrl fails.
 */<commit_msg>Fix Illegal trailing whitespace<commit_after>var getNextLink = require("./util").getNextLink;

/**
 * Application
 * @constructor
 * @param {Object} client Catapult client
 */

var Application = function (client) {
	/**
	 * List the user's applications
	 * @param {Object} params Parameters for filtering applications.
	 * @param {Number} [params.size] The maximum number of applications returned by
	 * the query per page (Max size: 1000).
	 * @param {Function} [callback] A callback for the list of applications.
	 * @return {Array[ApplicationResponse]} A promise for the list of applications, has a getNextPage
	 * function if the number of applications returned by the query exceeds the page size.
	 */
	this.list = function (params, callback) {
		var self = this;
		return client.makeRequest({
			path   : "applications",
			method : "GET",
			qs     : params
		})
		.then(function (response) {
			var applicationListResponse = {
				applications : response.body
			};
			var nextLink = getNextLink(response);
			if (nextLink) {
				applicationListResponse.hasMore = true;
				applicationListResponse.getNextPage = function (nextCallback) {
					return self.list(nextLink, nextCallback);
				};
			}
			return applicationListResponse;
		})
		.asCallback(callback);
	};

	/**
	 * Create a new application
	 * @param {Object} params Parameters for creating a new call
	 * @param {String} [params.name] A name you choose for this application.
 	 * @param {String} [params.incomingCallUrl] A URL where call events will be sent for an inbound call.
 	 * This is the endpoint where the Application Platform will send all call events.
	 * Either incomingCallUrl or incomingMessageUrl is required.
 	 * @param {String} [params.incomingCallUrlCallbackTimeout] Determine how long should the platform wait
 	 * for incomingCallUrl's response before timing out in milliseconds.
 	 * @param {String} [params.incomingCallFallbackUrl] The URL used to send the callback
 	 * event if the request to incomingCallUrl fails.
 	 * @param {String} [params.incomingMessageUrl] A URL where message events will be sent for an inbound message.
 	 * This is the endpoint where the Application Platform will send all message events.
 	 * Either incomingMessageUrl or incomingCallUrl is required.
	 * @param {Number} [params.incomingMessageUrlCallbackTimeout] Determine how long should the platform wait for
 	 * incomingMessageUrl's response before timing out in milliseconds.
	 * @param {String} [params.incomingMessageFallbackUrl] The URL used to send the callback event if
	 * the request to incomingMessageUrl fails.
	 * @param {String} [params.callbackHttpMethod] Determine if the callback event should be sent via HTTP GET
 	 * or HTTP POST. Values are "get" or "post", default: "post".
 	 * @param {Boolean} [params.autoAnswer] Determines whether or not an incoming call should be
 	 * automatically answered. Default value is 'true'.
 	 * @param {Function} [callback] A callback for the list of applications
	 * @return {ApplicationResponse} A promise for the newly created application.
	 */
	this.create = function (params, callback) {
		return client.makeRequest({
			path   : "applications",
			method : "POST",
			body   : params
		})
		.then(function (response) {
			var application = params;
			var location = response.headers.location;
			var applicationId = location.substring(location.lastIndexOf("/") + 1);
			application.id = applicationId;
			return application;
		})
		.asCallback(callback);
	};

	/**
	 * Get an application.
	 * @param  {String} applicationId The ID of the application to get.
	 * @param  {Function} [callback] A callback for the application.
	 * @return {ApplicationResponse} A promise for the application.
	 */
	this.get = function (applicationId, callback) {
		return client.makeRequest({
			path   : "applications/" + applicationId,
			method : "GET"
		})
		.then(function (response) {
			return response.body;
		})
		.asCallback(callback);
	};

	/**
	 * Make changes to an application.
	 * @param  {String} applicationId The ID of the application to modify.
	 * @param {Object} params Parameters for creating a new call
	 * @param {String} [params.name] A name you choose for this application.
 	 * @param {String} [params.incomingCallUrl] A URL where call events will be sent for an inbound call.
 	 * This is the endpoint where the Application Platform will send all call events.
	 * Either incomingCallUrl or incomingMessageUrl is required.
 	 * @param {String} [params.incomingCallUrlCallbackTimeout] Determine how long should the platform wait
 	 * for incomingCallUrl's response before timing out in milliseconds.
 	 * @param {String} [params.incomingCallFallbackUrl] The URL used to send the callback
 	 * event if the request to incomingCallUrl fails.
 	 * @param {String} [params.incomingMessageUrl] A URL where message events will be sent for an inbound message.
 	 * This is the endpoint where the Application Platform will send all message events.
 	 * Either incomingMessageUrl or incomingCallUrl is required.
	 * @param {Number} [params.incomingMessageUrlCallbackTimeout] Determine how long should the platform wait for
 	 * incomingMessageUrl's response before timing out in milliseconds.
	 * @param {String} [params.incomingMessageFallbackUrl] The URL used to send the callback event if
	 * the request to incomingMessageUrl fails.
	 * @param {String} [params.callbackHttpMethod] Determine if the callback event should be sent via HTTP GET
 	 * or HTTP POST. Values are "get" or "post", default: "post".
 	 * @param {Boolean} [params.autoAnswer] Determines whether or not an incoming call should be
 	 * automatically answered. Default value is 'true'.
 	 * @param {Function} [callback] A callback for the list of applications
	 */
	this.update = function (applicationId, params, callback) {
		return client.makeRequest({
			path   : "applications/" + applicationId,
			method : "POST",
			body   : params
		})
		.then(function () {
			return;
		})
		.asCallback(callback);
	};

	/**
	 * Delete an application.
	 * @param  {String} applicationId The ID of the application to delete.
	 * @param  {Function} [callback] A callback for the application.
	 */
	this.delete = function (applicationId, callback) {
		return client.makeRequest({
			path   : "applications/" + applicationId,
			method : "DELETE"
		})
		.then(function () {
			return;
		})
		.asCallback(callback);
	};

};

module.exports = Application;

/**
 * @class ApplicationResponse
 * @type {Object}
 * @property {String} id The unique identifier for the application.
 * @property {String} name A name you choose for this application.
 * @property {String} incomingCallUrl A URL where call events will be sent for an inbound call.
 * This is the endpoint where the Application Platform will send all call events.
 * Either incomingCallUrl or incomingMessageUrl is required.
 * @property {String} incomingCallUrlCallbackTimeout Determine how long should the platform wait
 * for incomingCallUrl's response before timing out in milliseconds.
 * @property {String} incomingCallFallbackUrl The URL used to send the callback
 * event if the request to incomingCallUrl fails.
 * @property {String} callbackHttpMethod Determine if the callback event should be sent via HTTP GET
 * or HTTP POST. Values are "get" or "post", default: "post".
 * @property {Boolean} autoAnswer Determines whether or not an incoming call should be
 * automatically answered. Default value is 'true'.
 * @property {String} incomingMessageUrl A URL where message events will be sent for an inbound message.
 * This is the endpoint where the Application Platform will send all message events.
 * Either incomingMessageUrl or incomingCallUrl is required.
 * @property {Number} incomingMessageUrlCallbackTimeout Determine how long should the platform wait for
 * incomingMessageUrl's response before timing out in milliseconds.
 * @property {String} incomingMessageFallbackUrl The URL used to send the callback event if
 * the request to incomingMessageUrl fails.
 */<|endoftext|>


<commit_before>/*global module, require*/
var Path = require('path'),
    _ = require('underscore'),
    passError = require('../util/passError'),
    uniqueId = require('../util/uniqueId'),
    memoizeAsyncAccessor = require('../util/memoizeAsyncAccessor');

function Base(config) {
    _.extend(this, config);
    this.id = uniqueId();
    if (this.url) {
        this.originalExtension = Path.extname(this.url);
    }
}

Base.prototype = {
    isAsset: true, // Avoid instanceof checks

    contentType: 'application/octet-stream',

    defaultExtension: '',

    getExtension: function () {
        if (this.url) {
            return Path.extname(this.url);
        } else if ('originalExtension' in this) {
            return this.originalExtension;
        } else {
            return this.defaultExtension;
        }
    },

    // Side effect: Sets the 'originalEncoding' property if the proxy provides it in the metadata (eg. from HTTP headers)
    getRawSrc: memoizeAsyncAccessor('rawSrc', function (cb) {
        var that = this;
        if (that.rawSrcProxy) {
            that.rawSrcProxy(passError(cb, function (rawSrc, metadata) {
                if (metadata && metadata.originalEncoding) {
                    that.originalEncoding = metadata.originalEncoding;
                }
                cb(null, rawSrc);
            }));
        } else {
            cb(new Error("Don't know how to get the raw asset src: " + that.toString()));
        }
    }),

    hasRawSrc: function () {
        return 'rawSrcProxy' in this || 'rawSrc' in this;
    },

    // Override in subclass if it supports relations:
    getOriginalRelations: memoizeAsyncAccessor('originalRelations', function (cb) {
        process.nextTick(function () {
            cb(null, []);
        });
    }),

    // Override in subclass if an intermediate representation is used or the asset can be manipulated:
    getSerializedSrc: function (cb) {
        this.getRawSrc(cb);
    },

    _clone: function (cb) {
        var that = this;
        that.getSerializedSrc(passError(cb, function (src) {
            cb(null, new that.constructor({
                rawSrc: src
            }));
        }));
    },

    toString: function () {
        return "[" + this.type + "/" + this.id + (this.url ? " " + this.url : "") + "]";
    }
};

module.exports = Base;
<commit_msg>assets.Base: Strip CGI parameters and fragment identifier before attempting to extract the extension using path.extname.<commit_after>/*global module, require*/
var Path = require('path'),
    _ = require('underscore'),
    passError = require('../util/passError'),
    uniqueId = require('../util/uniqueId'),
    memoizeAsyncAccessor = require('../util/memoizeAsyncAccessor');

function Base(config) {
    _.extend(this, config);
    this.id = uniqueId();
    if (this.url) {
        this.originalExtension = Path.extname(this.url.replace(/[?#].*$/, ''));
    }
}

Base.prototype = {
    isAsset: true, // Avoid instanceof checks

    contentType: 'application/octet-stream',

    defaultExtension: '',

    getExtension: function () {
        if (this.url) {
            return Path.extname(this.url.replace(/[?#].*$/, ''));
        } else if ('originalExtension' in this) {
            return this.originalExtension;
        } else {
            return this.defaultExtension;
        }
    },

    // Side effect: Sets the 'originalEncoding' property if the proxy provides it in the metadata (eg. from HTTP headers)
    getRawSrc: memoizeAsyncAccessor('rawSrc', function (cb) {
        var that = this;
        if (that.rawSrcProxy) {
            that.rawSrcProxy(passError(cb, function (rawSrc, metadata) {
                if (metadata && metadata.originalEncoding) {
                    that.originalEncoding = metadata.originalEncoding;
                }
                cb(null, rawSrc);
            }));
        } else {
            cb(new Error("Don't know how to get the raw asset src: " + that.toString()));
        }
    }),

    hasRawSrc: function () {
        return 'rawSrcProxy' in this || 'rawSrc' in this;
    },

    // Override in subclass if it supports relations:
    getOriginalRelations: memoizeAsyncAccessor('originalRelations', function (cb) {
        process.nextTick(function () {
            cb(null, []);
        });
    }),

    // Override in subclass if an intermediate representation is used or the asset can be manipulated:
    getSerializedSrc: function (cb) {
        this.getRawSrc(cb);
    },

    _clone: function (cb) {
        var that = this;
        that.getSerializedSrc(passError(cb, function (src) {
            cb(null, new that.constructor({
                rawSrc: src
            }));
        }));
    },

    toString: function () {
        return "[" + this.type + "/" + this.id + (this.url ? " " + this.url : "") + "]";
    }
};

module.exports = Base;
<|endoftext|>


<commit_before>var util = require('util'),
    _ = require('underscore'),
    jsdom = require('jsdom'),
    domtohtml = require('../3rdparty/jsdom/domtohtml'),
    error = require('../util/error'),
    memoizeAsyncAccessor = require('../util/memoizeAsyncAccessor'),
    relations = require('../relations'),
    Text = require('./Text');

function HTML(config) {
    Text.call(this, config);
}

util.inherits(HTML, Text);

function extractEncodingFromDecodedSrc(decodedSrc) {
    var metaCharset;
    (decodedSrc.match(/<meta[^>]+>/ig) || []).forEach(function (metaTagString) {
        if (/\bhttp-equiv=([\"\']|)\s*Content-Type\s*\1/i.test(metaTagString)) {
            var matchContent = metaTagString.match(/\bcontent=([\"\']|)\s*text\/html;\s*charset=([\w\-]*)\s*\1/i);
            if (matchContent) {
                metaCharset = matchContent[2];
            }
        }
    });
    return metaCharset; // Will be undefined if not found
}

_.extend(HTML.prototype, {
    contentType: 'text/html',

    defaultExtension: '.html',

    alternativeExtensions: ['.template', '.php', '.xhtml'],

    isPretty: false,

    getOriginalEncoding: function (cb) {
        var that = this;
        // An explicit encoding (Content-Type header, data: url charset, assetConfig) takes precedence, but if absent we should
        // look for a <meta http-equiv='Content-Type' ...> tag with a charset before falling back to the defaultEncoding (utf-8)
        if (that.originalEncoding) {
            return process.nextTick(function () {
                cb(null, that.originalEncoding);
            });
        } else if (that.decodedSrc) {
            process.nextTick(function () {
                cb(null, extractEncodingFromDecodedSrc(that.decodedSrc) || that.defaultEncoding);
            });
        } else if (that.hasRawSrc()) {
            that.getRawSrc(error.passToFunction(cb, function (rawSrc) {
                cb(null, extractEncodingFromDecodedSrc(rawSrc.toString("binary", 0, Math.min(1024, rawSrc.length))) || that.defaultEncoding);
            }));
        } else {
            process.nextTick(function () {
                cb(null, that.defaultEncoding);
            });
        }
    },

    getParseTree: memoizeAsyncAccessor('parseTree', function (cb) {
        var that = this;
        this.getDecodedSrc(error.passToFunction(cb, function (decodedSrc) {
            var parseTree = jsdom.jsdom(decodedSrc, undefined, {features: {ProcessExternalResources: [], FetchExternalResources: []}});
            // Jsdom (or its HTML parser) doesn't strip the newline after the <!DOCTYPE> for some reason.
            // Issue reported here: https://github.com/tmpvar/jsdom/issues/160
            if (parseTree.firstChild && parseTree.firstChild.nodeName === '#text' && parseTree.firstChild.nodeValue === "\n") {
                parseTree.removeChild(parseTree.firstChild);
            }
            cb(null, parseTree);
        }));
    }),

    _reformatParseTree: function (removeWhiteSpaceNodes, removeComments, cb) {
        var that = this;
        that.getParseTree(error.passToFunction(cb, function (parseTree) {
            var q = [parseTree];
            while (q.length) {
                var node = q.shift();
                // Non-conditional comment or whitespace-only text node?
                if ((removeComments && node.nodeType === 8 && !/^#|^\[if|^<!\[endif\]/.test(node.nodeValue)) || (removeWhiteSpaceNodes && node.nodeType === 3 && /^[\r\n\s\t]*$/.test(node.nodeValue))) {
                    node.parentNode.removeChild(node);
                } else {
                    if (node.nodeName && node.nodeName.toLowerCase() !== 'script') {
                        // Using node._childNodes instead of nodes.childNodes because of this issue:
                        // https://github.com/tmpvar/jsdom/issues/171
                        for (var i = 0 ; i < node._childNodes.length ; i += 1) {
                            q.push(node._childNodes[i]);
                        }
                    }
                }
            }
            cb();
        }));
    },

    minify: function (cb) {
        var that = this;
        that._reformatParseTree(true, true, error.passToFunction(cb, function () {
            that.isPretty = false;
            cb();
        }));
    },

    prettyPrint: function (cb) {
        var that = this;
        that._reformatParseTree(true, false, error.passToFunction(cb, function () {
            that.isPretty = true;
            cb();
        }));
    },

    getText: function (cb) {
        var that = this;
        that.getParseTree(error.passToFunction(cb, function (parseTree) {
            // My fork doesn't suffer from the CRLF "bug":
            cb(null, (parseTree.doctype ? parseTree.doctype + "\n" : "") + domtohtml.domToHtml(parseTree, !that.isPretty));

            // var html;
            // if (that.isPretty) {
            //     // domtohtml.formatHTML is hardcoded to use "\r\n" at EOL.
            //     html = domtohtml.domToHtml(parseTree, false).replace(/\r\n/g, "\n");
            // } else {
            //     html = domtohtml.domToHtml(parseTree, true);
            // }
            // cb(null, (parseTree.doctype ? parseTree.doctype + "\n" : "") + html);
        }));
    },

    getOriginalRelations: memoizeAsyncAccessor('originalRelations', function (cb) {
        var that = this;
        this.getParseTree(error.passToFunction(cb, function (parseTree) {
            var originalRelations = [];
            if (parseTree.documentElement && parseTree.documentElement.hasAttribute('manifest')) {
                originalRelations.push(new relations.HTMLCacheManifest({
                    from: that,
                    to: parseTree.documentElement.getAttribute('manifest'),
                    node: parseTree.documentElement
                }));
            }
            var queue = [parseTree];
            while (queue.length) {
                var node = queue.shift();
                if (node.childNodes) {
                    for (var i = 0 ; i < node.childNodes.length ; i += 1) {
                        queue.push(node.childNodes[i]);
                    }
                }
                if (node.nodeType === 1) { // Element
                    var nodeName = node.nodeName.toLowerCase();
                    if (nodeName === 'script') {
                        if (node.hasAttribute('src')) {
                            originalRelations.push(new relations.HTMLScript({
                                from: that,
                                to: node.getAttribute('src'),
                                node: node
                            }));
                        } else {
                            originalRelations.push(new relations.HTMLScript({
                                from: that,
                                to: {
                                    type: node.getAttribute('type') === 'text/coffeescript' ? 'CoffeeScript' : 'JavaScript',
                                    isResolved: true,
                                    decodedSrc: node.firstChild.nodeValue
                                },
                                node: node
                            }));
                        }
                    } else if (nodeName === 'style') {
                        originalRelations.push(new relations.HTMLStyle({
                            from: that,
                            to: {
                                type: 'CSS',
                                isResolved: true,
                                decodedSrc: node.firstChild.nodeValue
                            },
                            node: node
                        }));
                    } else if (nodeName === 'link') {
                        if (node.hasAttribute('rel')) {
                            var rel = node.getAttribute('rel').toLowerCase();
                            if (rel === 'stylesheet') {
                                originalRelations.push(new relations.HTMLStyle({
                                    from: that,
                                    to: node.getAttribute('href'),
                                    node: node
                                }));
                            } else if (/^(?:shortcut |apple-touch-)?icon$/.test(rel)) {
                                originalRelations.push(new relations.HTMLShortcutIcon({
                                    from: that,
                                    to: node.getAttribute('href'),
                                    node: node
                                }));
                            } else if (rel === 'alternate') {
                                originalRelations.push(new relations.HTMLAlternateLink({
                                    from: that,
                                    to: {
                                        url: node.getAttribute('href'),
                                        contentType: node.getAttribute('type')
                                    },
                                    node: node
                                }));
                            }
                        }
                    } else if (nodeName === 'img') {
                        originalRelations.push(new relations.HTMLImage({
                            from: that,
                            to: node.getAttribute('src'),
                            node: node
                        }));
                    } else if (nodeName === 'a') {
                        var href = node.getAttribute('href');
                        if (!/^mailto:/i.test(href)) {
                            originalRelations.push(new relations.HTMLAnchor({
                                from: that,
                                to: href,
                                node: node
                            }));
                        }
                    } else if (nodeName === 'iframe') {
                        originalRelations.push(new relations.HTMLIFrame({
                            from: that,
                            to: node.getAttribute('src'),
                            node: node
                        }));
                    } else if (nodeName === 'frame') {
                        originalRelations.push(new relations.HTMLFrame({
                            from: that,
                            to: node.getAttribute('src'),
                            node: node
                        }));
                    } else if (nodeName === 'esi:include') {
                        originalRelations.push(new relations.HTMLEdgeSideInclude({
                            from: that,
                            to: node.getAttribute('src'),
                            node: node
                        }));
                    } else if (nodeName === 'video') {
                        if (node.hasAttribute('src')) {
                            originalRelations.push(new relations[nodeName === 'video' ? 'HTMLVideo' : 'HTMLAudio']({
                                from: that,
                                to: node.getAttribute('src'),
                                node: node
                            }));
                        }
                        if (node.hasAttribute('poster')) {
                            originalRelations.push(new relations.HTMLVideoPoster({
                                from: that,
                                to: node.getAttribute('poster'),
                                node: node
                            }));
                        }
                    } else if (nodeName === 'audio' && node.hasAttribute('src')) {
                        originalRelations.push(new relations.HTMLAudio({
                            from: that,
                            to: node.getAttribute('src'),
                            node: node
                        }));
                    } else if (nodeName === 'source' && node.parentNode && /^(?:video|audio)$/i.test(node.parentNode.nodeName)) {
                        originalRelations.push(new relations[node.parentNode.nodeName.toLowerCase() === 'video' ? 'HTMLVideo' : 'HTMLAudio']({
                            from: that,
                            to: node.getAttribute('src'),
                            node: node
                        }));
                    } else if (nodeName === 'param' && node.getAttribute('name') === 'src' && node.parentNode && node.parentNode.nodeName.toLowerCase() === 'object') {
                        originalRelations.push(new relations.HTMLObjectSrcParam({
                            from: that,
                            to: node.getAttribute('value'),
                            node: node
                        }));
                    }
                } else if (node.nodeType === 8) { // Comment
                    var matchConditionalComment = node.nodeValue.match(/^\[(if[^\]]*)\]\>([\s\S]*)<!\[endif\]$/);
                    if (matchConditionalComment) {
                        originalRelations.push(new relations.HTMLConditionalComment({
                            from: that,
                            to: {
                                type: 'HTML',
                                isResolved: true,
                                decodedSrc: matchConditionalComment[2]
                            },
                            node: node,
                            condition: matchConditionalComment[1]
                        }));
                    }
                }
            }
            cb(null, originalRelations);
        }));
    }),

    attachRelation: function (relation, position, adjacentRelation) {
        var document = this.parseTree;
        position = position || 'first';
        relation.from = this;
        relation.node = relation.createNode(document);

        if (position === 'first') {
            if (relation.type === 'HTMLStyle') {
                document.head.appendChild(relation.node);
            } else if (relation.type === 'HTMLScript') {
                var existingScriptNodes = document.getElementsByTagName('script');
                if (existingScriptNodes.length > 0) {
                    existingScriptNodes[0].parentNode.insertBefore(relation.node, existingScriptNodes[0]);
                } else if (document.body) {
                    document.body.insertBefore(relation.node, document.body.firstChild);
                } else {
                    document.head.appendChild(relation.node);
                }
            } else if (relation.type === 'HTMLCacheManifest') {
                // Already attached to <html> as a result of relation.createNode above
            } else {
                throw "HTML.attachRelation: position=first only supported for HTMLStyle, HTMLScript, and HTMLCacheManifest relations";
            }
        } else { // 'before' or 'after'
            var parentNode = adjacentRelation.node.parentNode;
            if (position === 'after') {
                parentNode.insertBefore(relation.node, adjacentRelation.node.nextSibling);
            } else {
                parentNode.insertBefore(relation.node, adjacentRelation.node);
            }
        }
    },

    detachRelation: function (relation) {
        relation.node.parentNode.removeChild(relation.node);
        delete relation.node;
    }
});

module.exports = HTML;
<commit_msg>assets.HTML.getOriginalRelations: Fixed traversal order.<commit_after>var util = require('util'),
    _ = require('underscore'),
    jsdom = require('jsdom'),
    domtohtml = require('../3rdparty/jsdom/domtohtml'),
    error = require('../util/error'),
    memoizeAsyncAccessor = require('../util/memoizeAsyncAccessor'),
    relations = require('../relations'),
    Text = require('./Text');

function HTML(config) {
    Text.call(this, config);
}

util.inherits(HTML, Text);

function extractEncodingFromDecodedSrc(decodedSrc) {
    var metaCharset;
    (decodedSrc.match(/<meta[^>]+>/ig) || []).forEach(function (metaTagString) {
        if (/\bhttp-equiv=([\"\']|)\s*Content-Type\s*\1/i.test(metaTagString)) {
            var matchContent = metaTagString.match(/\bcontent=([\"\']|)\s*text\/html;\s*charset=([\w\-]*)\s*\1/i);
            if (matchContent) {
                metaCharset = matchContent[2];
            }
        }
    });
    return metaCharset; // Will be undefined if not found
}

_.extend(HTML.prototype, {
    contentType: 'text/html',

    defaultExtension: '.html',

    alternativeExtensions: ['.template', '.php', '.xhtml'],

    isPretty: false,

    getOriginalEncoding: function (cb) {
        var that = this;
        // An explicit encoding (Content-Type header, data: url charset, assetConfig) takes precedence, but if absent we should
        // look for a <meta http-equiv='Content-Type' ...> tag with a charset before falling back to the defaultEncoding (utf-8)
        if (that.originalEncoding) {
            return process.nextTick(function () {
                cb(null, that.originalEncoding);
            });
        } else if (that.decodedSrc) {
            process.nextTick(function () {
                cb(null, extractEncodingFromDecodedSrc(that.decodedSrc) || that.defaultEncoding);
            });
        } else if (that.hasRawSrc()) {
            that.getRawSrc(error.passToFunction(cb, function (rawSrc) {
                cb(null, extractEncodingFromDecodedSrc(rawSrc.toString("binary", 0, Math.min(1024, rawSrc.length))) || that.defaultEncoding);
            }));
        } else {
            process.nextTick(function () {
                cb(null, that.defaultEncoding);
            });
        }
    },

    getParseTree: memoizeAsyncAccessor('parseTree', function (cb) {
        var that = this;
        this.getDecodedSrc(error.passToFunction(cb, function (decodedSrc) {
            var parseTree = jsdom.jsdom(decodedSrc, undefined, {features: {ProcessExternalResources: [], FetchExternalResources: []}});
            // Jsdom (or its HTML parser) doesn't strip the newline after the <!DOCTYPE> for some reason.
            // Issue reported here: https://github.com/tmpvar/jsdom/issues/160
            if (parseTree.firstChild && parseTree.firstChild.nodeName === '#text' && parseTree.firstChild.nodeValue === "\n") {
                parseTree.removeChild(parseTree.firstChild);
            }
            cb(null, parseTree);
        }));
    }),

    _reformatParseTree: function (removeWhiteSpaceNodes, removeComments, cb) {
        var that = this;
        that.getParseTree(error.passToFunction(cb, function (parseTree) {
            var q = [parseTree];
            while (q.length) {
                var node = q.shift();
                // Non-conditional comment or whitespace-only text node?
                if ((removeComments && node.nodeType === 8 && !/^#|^\[if|^<!\[endif\]/.test(node.nodeValue)) || (removeWhiteSpaceNodes && node.nodeType === 3 && /^[\r\n\s\t]*$/.test(node.nodeValue))) {
                    node.parentNode.removeChild(node);
                } else {
                    if (node.nodeName && node.nodeName.toLowerCase() !== 'script') {
                        // Using node._childNodes instead of nodes.childNodes because of this issue:
                        // https://github.com/tmpvar/jsdom/issues/171
                        for (var i = 0 ; i < node._childNodes.length ; i += 1) {
                            q.push(node._childNodes[i]);
                        }
                    }
                }
            }
            cb();
        }));
    },

    minify: function (cb) {
        var that = this;
        that._reformatParseTree(true, true, error.passToFunction(cb, function () {
            that.isPretty = false;
            cb();
        }));
    },

    prettyPrint: function (cb) {
        var that = this;
        that._reformatParseTree(true, false, error.passToFunction(cb, function () {
            that.isPretty = true;
            cb();
        }));
    },

    getText: function (cb) {
        var that = this;
        that.getParseTree(error.passToFunction(cb, function (parseTree) {
            // My fork doesn't suffer from the CRLF "bug":
            cb(null, (parseTree.doctype ? parseTree.doctype + "\n" : "") + domtohtml.domToHtml(parseTree, !that.isPretty));

            // var html;
            // if (that.isPretty) {
            //     // domtohtml.formatHTML is hardcoded to use "\r\n" at EOL.
            //     html = domtohtml.domToHtml(parseTree, false).replace(/\r\n/g, "\n");
            // } else {
            //     html = domtohtml.domToHtml(parseTree, true);
            // }
            // cb(null, (parseTree.doctype ? parseTree.doctype + "\n" : "") + html);
        }));
    },

    getOriginalRelations: memoizeAsyncAccessor('originalRelations', function (cb) {
        var that = this;
        this.getParseTree(error.passToFunction(cb, function (parseTree) {
            var originalRelations = [];
            if (parseTree.documentElement && parseTree.documentElement.hasAttribute('manifest')) {
                originalRelations.push(new relations.HTMLCacheManifest({
                    from: that,
                    to: parseTree.documentElement.getAttribute('manifest'),
                    node: parseTree.documentElement
                }));
            }
            var queue = [parseTree];
            while (queue.length) {
                var node = queue.shift();
                if (node.childNodes) {
                    for (var i = node.childNodes.length - 1 ; i >= 0 ; i -= 1) {
                        queue.unshift(node.childNodes[i]);
                    }
                }
                if (node.nodeType === 1) { // Element
                    var nodeName = node.nodeName.toLowerCase();
                    if (nodeName === 'script') {
                        if (node.hasAttribute('src')) {
                            originalRelations.push(new relations.HTMLScript({
                                from: that,
                                to: node.getAttribute('src'),
                                node: node
                            }));
                        } else {
                            originalRelations.push(new relations.HTMLScript({
                                from: that,
                                to: {
                                    type: node.getAttribute('type') === 'text/coffeescript' ? 'CoffeeScript' : 'JavaScript',
                                    isResolved: true,
                                    decodedSrc: node.firstChild.nodeValue
                                },
                                node: node
                            }));
                        }
                    } else if (nodeName === 'style') {
                        originalRelations.push(new relations.HTMLStyle({
                            from: that,
                            to: {
                                type: 'CSS',
                                isResolved: true,
                                decodedSrc: node.firstChild.nodeValue
                            },
                            node: node
                        }));
                    } else if (nodeName === 'link') {
                        if (node.hasAttribute('rel')) {
                            var rel = node.getAttribute('rel').toLowerCase();
                            if (rel === 'stylesheet') {
                                originalRelations.push(new relations.HTMLStyle({
                                    from: that,
                                    to: node.getAttribute('href'),
                                    node: node
                                }));
                            } else if (/^(?:shortcut |apple-touch-)?icon$/.test(rel)) {
                                originalRelations.push(new relations.HTMLShortcutIcon({
                                    from: that,
                                    to: node.getAttribute('href'),
                                    node: node
                                }));
                            } else if (rel === 'alternate') {
                                originalRelations.push(new relations.HTMLAlternateLink({
                                    from: that,
                                    to: {
                                        url: node.getAttribute('href'),
                                        contentType: node.getAttribute('type')
                                    },
                                    node: node
                                }));
                            }
                        }
                    } else if (nodeName === 'img') {
                        originalRelations.push(new relations.HTMLImage({
                            from: that,
                            to: node.getAttribute('src'),
                            node: node
                        }));
                    } else if (nodeName === 'a') {
                        var href = node.getAttribute('href');
                        if (!/^mailto:/i.test(href)) {
                            originalRelations.push(new relations.HTMLAnchor({
                                from: that,
                                to: href,
                                node: node
                            }));
                        }
                    } else if (nodeName === 'iframe') {
                        originalRelations.push(new relations.HTMLIFrame({
                            from: that,
                            to: node.getAttribute('src'),
                            node: node
                        }));
                    } else if (nodeName === 'frame') {
                        originalRelations.push(new relations.HTMLFrame({
                            from: that,
                            to: node.getAttribute('src'),
                            node: node
                        }));
                    } else if (nodeName === 'esi:include') {
                        originalRelations.push(new relations.HTMLEdgeSideInclude({
                            from: that,
                            to: node.getAttribute('src'),
                            node: node
                        }));
                    } else if (nodeName === 'video') {
                        if (node.hasAttribute('src')) {
                            originalRelations.push(new relations[nodeName === 'video' ? 'HTMLVideo' : 'HTMLAudio']({
                                from: that,
                                to: node.getAttribute('src'),
                                node: node
                            }));
                        }
                        if (node.hasAttribute('poster')) {
                            originalRelations.push(new relations.HTMLVideoPoster({
                                from: that,
                                to: node.getAttribute('poster'),
                                node: node
                            }));
                        }
                    } else if (nodeName === 'audio' && node.hasAttribute('src')) {
                        originalRelations.push(new relations.HTMLAudio({
                            from: that,
                            to: node.getAttribute('src'),
                            node: node
                        }));
                    } else if (nodeName === 'source' && node.parentNode && /^(?:video|audio)$/i.test(node.parentNode.nodeName)) {
                        originalRelations.push(new relations[node.parentNode.nodeName.toLowerCase() === 'video' ? 'HTMLVideo' : 'HTMLAudio']({
                            from: that,
                            to: node.getAttribute('src'),
                            node: node
                        }));
                    } else if (nodeName === 'param' && node.getAttribute('name') === 'src' && node.parentNode && node.parentNode.nodeName.toLowerCase() === 'object') {
                        originalRelations.push(new relations.HTMLObjectSrcParam({
                            from: that,
                            to: node.getAttribute('value'),
                            node: node
                        }));
                    }
                } else if (node.nodeType === 8) { // Comment
                    var matchConditionalComment = node.nodeValue.match(/^\[(if[^\]]*)\]\>([\s\S]*)<!\[endif\]$/);
                    if (matchConditionalComment) {
                        originalRelations.push(new relations.HTMLConditionalComment({
                            from: that,
                            to: {
                                type: 'HTML',
                                isResolved: true,
                                decodedSrc: matchConditionalComment[2]
                            },
                            node: node,
                            condition: matchConditionalComment[1]
                        }));
                    }
                }
            }
            cb(null, originalRelations);
        }));
    }),

    attachRelation: function (relation, position, adjacentRelation) {
        var document = this.parseTree;
        position = position || 'first';
        relation.from = this;
        relation.node = relation.createNode(document);

        if (position === 'first') {
            if (relation.type === 'HTMLStyle') {
                document.head.appendChild(relation.node);
            } else if (relation.type === 'HTMLScript') {
                var existingScriptNodes = document.getElementsByTagName('script');
                if (existingScriptNodes.length > 0) {
                    existingScriptNodes[0].parentNode.insertBefore(relation.node, existingScriptNodes[0]);
                } else if (document.body) {
                    document.body.insertBefore(relation.node, document.body.firstChild);
                } else {
                    document.head.appendChild(relation.node);
                }
            } else if (relation.type === 'HTMLCacheManifest') {
                // Already attached to <html> as a result of relation.createNode above
            } else {
                throw "HTML.attachRelation: position=first only supported for HTMLStyle, HTMLScript, and HTMLCacheManifest relations";
            }
        } else { // 'before' or 'after'
            var parentNode = adjacentRelation.node.parentNode;
            if (position === 'after') {
                parentNode.insertBefore(relation.node, adjacentRelation.node.nextSibling);
            } else {
                parentNode.insertBefore(relation.node, adjacentRelation.node);
            }
        }
    },

    detachRelation: function (relation) {
        relation.node.parentNode.removeChild(relation.node);
        delete relation.node;
    }
});

module.exports = HTML;
<|endoftext|>


<commit_before>var Subscriber, AtomJshint;
var cp = require('child_process');
var _ = require('underscore-plus');
var JSHINT_Worker = null;

var Subscriber = require('emissary').Subscriber;
var fs = require('fs');
var path = require('path');

module.exports = AtomJshint = (function(){
  JSHINT_Worker = cp.fork(__dirname + '/worker.js');
  process.on('exit', function() {
    JSHINT_Worker.kill();
  });
  var $ = require('atom').$;
  function AtomJsHint(){
    atom.workspace.eachEditor((function(self) {
      return function(editor) {
        return self.handleEvents(editor);
      };
    })(this));
    this.config = {globals:{},options:{}};
    this.loadConfig();
  }
  Subscriber.includeInto(AtomJsHint);

  AtomJsHint.prototype.destroy = function(){
    return this.unsubscribe();
  };

  AtomJsHint.prototype.handleEvents = function(editor) {
    var buffer = editor.getBuffer();
    var listenFor = [];
    if( atom.config.get('atom-jshint.hintOnModify') ){ listenFor.push('contents-modified'); }
    if( atom.config.get('atom-jshint.hintOnSave') ) { listenFor.push('saved'); }

    this.subscribe(atom.workspaceView, 'pane-container:active-pane-item-changed', function(){
      this.run(editor);
    }.bind(this));
    this.subscribe(buffer, listenFor.join(' '), (function(self) {
      return _.debounce(function() {
        return buffer.transact(function() {
          self.run(editor);
        });
      },50);
    })(this));
    this.subscribe(buffer, 'destroyed', (function(self) {
      return function() {
        return self.unsubscribe(buffer);
      };
    })(this));
  };

  AtomJsHint.prototype.run = function(editor){
    var self = this;
    var text = this.getContents();
    if( !text ) {
      this.resetState(editor);
      return;
    }

    var cb = function (jsHintErrors) {
      JSHINT_Worker.removeListener('message', cb);
      if(jsHintErrors.length === 0) {
        self.resetState(editor);
      }
      if( editor.cursors[0] ){
        self.updateStatus(jsHintErrors, editor.cursors[0].getBufferRow());
      }
      self.updateGutter(jsHintErrors);
      self.updatePane(jsHintErrors);
      self.subscribe(atom.workspaceView, 'cursor:moved', function () {
        if( editor.cursors[0] ){
          self.updateStatus(jsHintErrors, editor.cursors[0].getBufferRow());
        }
      });
      self.subscribe(editor, 'scroll-top-changed', function () {
        self.updateGutter(jsHintErrors);
      });
    };
    JSHINT_Worker.on('message', cb);

    JSHINT_Worker.send({
      method: 'run',
      text:text,
      options: this.config.options,
      config: this.config.globals
    });
  };

  AtomJsHint.prototype.resetState = function(editor){
    this.updateStatus(false);
    this.updateGutter([]);
    this.updatePane([]);
    atom.workspaceView.off('cursor:moved');
    this.unsubscribe(editor);
  };

  AtomJsHint.prototype.updatePane = function(errors){
    $('#jshint-status-pane').remove();
    if( !errors || !atom.config.get('atom-jshint.showErrorPanel') ) return;
    var html = $('
');
    errors.forEach(function(error){
      html.append('Line: ' + error.line + ' Char:' + error.character + ' ' + error.reason);
      html.append('
');
    });
    atom.workspaceView.prependToBottom(html);
  };

  AtomJsHint.prototype.updateGutter = function(errors){
    atom.workspaceView.eachEditorView(function(editorView){
      if (editorView.active) {
        var gutter = editorView.gutter;
        gutter.removeClassFromAllLines('atom-jshint-error');
        errors.forEach(function(error){
          gutter.addClassToLine(error.line - 1, 'atom-jshint-error');
        });
      }
    });
  };

  AtomJsHint.prototype.updateStatus = function(errors, row){
    var status = document.getElementById('jshint-status');
    var msg = '';
    if( status ) status.parentElement.removeChild(status);
    if( !errors ) return;
    if (row >= 0) {
      var lineErrors = errors.filter(function (error) {
        return error.line === row + 1;
      });
      if (lineErrors.length > 0) {
        msg = 'Error: ' + lineErrors[0].line + ':' + lineErrors[0].character + ' ' + lineErrors[0].reason;
      } else {
        msg = errors.length > 0 ? errors.length + ' JSHint error' + (errors.length>1?'s':'') : '';
      }
      atom.workspaceView.statusBar.appendLeft('' + msg + '');
    }
  };

  AtomJsHint.prototype.getContents = function(){
    if( !atom.workspace.activePaneItem ) return false;
    var filename = atom.workspace.activePaneItem.getUri();
    if( !filename ) return false;
    if( filename.slice(-3) !== '.js' ) return false;
    var text = atom.workspace.activePaneItem.getText();
    if( !text ) return false;
    return text;
  };

  AtomJsHint.prototype.loadConfig = function(){
    if( fs.existsSync(atom.project.path + '/package.json') ){
      var packageJson = require(atom.project.path + '/package.json');
      if( packageJson.jshintConfig ) {
        return this.setConfig(packageJson.jshintConfig);
      }
    }
    var dirs = atom.project.path.split(path.sep);
    while ( dirs.length >= 0 ) {
      var configPath;
      if ( dirs.length === 0 ) {
        configPath = '/.jshintrc';
      } else {
        configPath = path.join(dirs.join(path.sep), '.jshintrc');
      }
      if( fs.existsSync(configPath) ){
        var configFile = fs.readFileSync(configPath, 'UTF8');
        var conf = {};
        try {
          conf = JSON.parse(configFile);
        } catch(e){
          console.error('error parsing config file');
        }
        return this.setConfig(conf);
      }
      dirs.pop();
    }
  };

  AtomJsHint.prototype.setConfig = function(conf){
    var config = {globals:{},options:{}};
    config.globals = conf.globals || {};
    if( conf.global ) { delete conf.globals; }
    config.options = conf;
    this.config = config;
  };

  return AtomJsHint;

})();
<commit_msg>resolved crashing on start<commit_after>var Subscriber, AtomJshint;
var cp = require('child_process');
var _ = require('underscore-plus');
var JSHINT_Worker = null;

var Subscriber = require('emissary').Subscriber;
var fs = require('fs');
var path = require('path');

module.exports = AtomJshint = (function(){
  JSHINT_Worker = cp.fork(__dirname + '/worker.js');
  process.on('exit', function() {
    JSHINT_Worker.kill();
  });
  var $ = require('atom').$;
  function AtomJsHint(){
    atom.workspace.eachEditor((function(self) {
      return function(editor) {
        return self.handleEvents(editor);
      };
    })(this));
    this.config = {globals:{},options:{}};
    this.loadConfig();
  }
  Subscriber.includeInto(AtomJsHint);

  AtomJsHint.prototype.destroy = function(){
    return this.unsubscribe();
  };

  AtomJsHint.prototype.handleEvents = function(editor) {
    var buffer = editor.getBuffer();
    var listenFor = [];
    if( atom.config.get('atom-jshint.hintOnModify') ){ listenFor.push('contents-modified'); }
    if( atom.config.get('atom-jshint.hintOnSave') ) { listenFor.push('saved'); }

    this.subscribe(atom.workspaceView, 'pane-container:active-pane-item-changed', function(){
      this.run(editor);
    }.bind(this));
    this.subscribe(buffer, listenFor.join(' '), (function(self) {
      return _.debounce(function() {
        return buffer.transact(function() {
          self.run(editor);
        });
      },50);
    })(this));
    this.subscribe(buffer, 'destroyed', (function(self) {
      return function() {
        return self.unsubscribe(buffer);
      };
    })(this));
  };

  AtomJsHint.prototype.run = function(editor){
    var self = this;
    var text = this.getContents();
    if( !text ) {
      this.resetState(editor);
      return;
    }

    var cb = function (jsHintErrors) {
      JSHINT_Worker.removeListener('message', cb);
      if(jsHintErrors.length === 0) {
        self.resetState(editor);
      }
      if( editor.cursors[0] ){
        self.updateStatus(jsHintErrors, editor.cursors[0].getBufferRow());
      }
      self.updateGutter(jsHintErrors);
      self.updatePane(jsHintErrors);
      self.subscribe(atom.workspaceView, 'cursor:moved', function () {
        if( editor.cursors[0] ){
          self.updateStatus(jsHintErrors, editor.cursors[0].getBufferRow());
        }
      });
      self.subscribe(editor, 'scroll-top-changed', function () {
        self.updateGutter(jsHintErrors);
      });
    };
    JSHINT_Worker.on('message', cb);

    JSHINT_Worker.send({
      method: 'run',
      text:text,
      options: this.config.options,
      config: this.config.globals
    });
  };

  AtomJsHint.prototype.resetState = function(editor){
    this.updateStatus(false);
    this.updateGutter([]);
    this.updatePane([]);
    atom.workspaceView.off('cursor:moved');
    this.unsubscribe(editor);
  };

  AtomJsHint.prototype.updatePane = function(errors){
    $('#jshint-status-pane').remove();
    if( !errors || !atom.config.get('atom-jshint.showErrorPanel') ) return;
    var html = $('
');
    errors.forEach(function(error){
      html.append('Line: ' + error.line + ' Char:' + error.character + ' ' + error.reason);
      html.append('
');
    });
    atom.workspaceView.prependToBottom(html);
  };

  AtomJsHint.prototype.updateGutter = function(errors){
    atom.workspaceView.eachEditorView(function(editorView){
      if (editorView.active) {
        var gutter = editorView.gutter;
        gutter.removeClassFromAllLines('atom-jshint-error');
        errors.forEach(function(error){
          gutter.addClassToLine(error.line - 1, 'atom-jshint-error');
        });
      }
    });
  };

  AtomJsHint.prototype.updateStatus = function(errors, row){
    var status = document.getElementById('jshint-status');
    var msg = '';
    if( status ) status.parentElement.removeChild(status);
    if( !errors ) return;
    if (row >= 0) {
      var lineErrors = errors.filter(function (error) {
        return error.line === row + 1;
      });
      if (lineErrors.length > 0) {
        msg = 'Error: ' + lineErrors[0].line + ':' + lineErrors[0].character + ' ' + lineErrors[0].reason;
      } else {
        msg = errors.length > 0 ? errors.length + ' JSHint error' + (errors.length>1?'s':'') : '';
      }
      atom.workspaceView.statusBar.appendLeft('' + msg + '');
    }
  };

  AtomJsHint.prototype.getContents = function(){
    if( !atom.workspace.activePaneItem ) return false;
    var filename = atom.workspace.activePaneItem.getUri();
    if( !filename ) return false;
    if( filename.slice(-3) !== '.js' ) return false;
    var text = atom.workspace.activePaneItem.getText();
    if( !text ) return false;
    return text;
  };

  AtomJsHint.prototype.loadConfig = function(){
    if( fs.existsSync(atom.project.path + '/package.json') ){
      var packageJson = require(atom.project.path + '/package.json');
      if( packageJson.jshintConfig ) {
        return this.setConfig(packageJson.jshintConfig);
      }
    }
    var workingPath = atom.project.path || '';
    var dirs = workingPath.split(path.sep);
    while ( dirs.length >= 1 ) {
      var configPath;
      if ( dirs.length === 1 ) {
        configPath = '/.jshintrc';
      } else {
        configPath = path.join(dirs.join(path.sep), '.jshintrc');
      }
      if( fs.existsSync(configPath) ){
        var configFile = fs.readFileSync(configPath, 'UTF8');
        var conf = {};
        try {
          conf = JSON.parse(configFile);
        } catch(e){
          console.error('error parsing config file');
        }
        return this.setConfig(conf);
      }
      dirs.pop();
    }
  };

  AtomJsHint.prototype.setConfig = function(conf){
    var config = {globals:{},options:{}};
    config.globals = conf.globals || {};
    if( conf.global ) { delete conf.globals; }
    config.options = conf;
    this.config = config;
  };

  return AtomJsHint;

})();
<|endoftext|>


<commit_before>// var Adapter     = require('../adapter');
var RSVP        = require('rsvp');
var azure       = require('azure-storage');
var chalk       = require('chalk');
var Promise     = require('ember-cli/lib/ext/promise');
var SilentError = require('ember-cli/lib/errors/silent');
var Adapter     = require('ember-deploy/utilities/adapter')

var DEFAULT_MANIFEST_SIZE   = 10;
var DEFAULT_TAGGING_ADAPTER = 'sha';

var green = chalk.green;
var white = chalk.white;

var AZURE_TABLE_NAME = 'emberdeploy';
var AZURE_MANIFEST_TAG = 'manifest';

module.exports = Adapter.extend({
  init: function() {
    this.tagging      = this.tagging || DEFAULT_TAGGING_ADAPTER;
    this.manifestSize = this.manifestSize || DEFAULT_MANIFEST_SIZE;

    // be transparant to either passing a connectionString or the storageAccount + storageAccessKey
    if(this.config.connectionString) {
      this.client = azure.createTableService(this.config.connectionString);
    } else if (this.config.storageAccount && this.config.storageAccessKey) {
      this.client = azure.createTableService(this.config.storageAccount, this.config.storageAccessKey);
    } else {
      console.error("No connection string or storage account plus access key set for this Azure deployment.");
      // TODO throw error

      return false;
    }

    /*
      TODO to make feasible for tests;
      In order to run the tests, the following environment variables need to be set up:
      AZURE_STORAGE_CONNECTION_STRING="valid storage connection string"
    */
  },

  upload: function(value) {
    var taggingAdapter = this.taggingAdapter || this._initTaggingAdapter();
    var key            = taggingAdapter.createTag();

    return this._upload(value, key);
  },

  list: function() {
    return RSVP.hash({
      revisions: this._list(),
      current: this._current()
    })
    .then(function(results) {
      var revisions = results.revisions;
      var current   = results.current;
      var message   = this._revisionListMessage(revisions, current);

      this._printSuccessMessage(message);

      return message;
    }.bind(this));
  },

  activate: function(revisionKey) {
    if (!revisionKey) {
      return this._printErrorMessage(this._noRevisionPassedMessage());
    };

    var uploadKey = this._currentKey();
    var that      = this;

    return new RSVP.Promise(function(resolve, reject) {
      that._list()
        .then(function(uploads) {
          return uploads.indexOf(revisionKey) > -1 ? resolve() : reject();
        })
        .then(function() {
          return that.client.set(uploadKey, revisionKey);
        })
        .then(resolve);
    })
    .then(this._activationSuccessfulMessage)
    .then(this._printSuccessMessage.bind(this))
    .catch(function() {
      return this._printErrorMessage(this._revisionNotFoundMessage());
    }.bind(this));
  },

  _list: function() {
    var that = this;

    return new Promise(function(resolve, reject) {
      // create table if not already existent
      that.client.createTableIfNotExists(AZURE_TABLE_NAME, function(error, result, response) {
        if(!error){
          var query = new azure.TableQuery()
                  .where('PartitionKey eq ?', AZURE_MANIFEST_TAG);

          // find the list of uploaded revisions
          that.client.queryEntities(AZURE_TABLE_NAME, query, null, function(error, result, response) {
            if(!error) {
              resolve(result.entries);
            } else {
              reject(error);
            }
          });
        } else {
          reject(error);
        }
      });
    });
  },

  _current: function() {
    var that = this;

    return new Promise(function(resolve, reject) {
      // create table if not already existent
      that.client.createTableIfNotExists(AZURE_TABLE_NAME, function(error, result, response) {
        if(!error){
          // find the current tag
          var query = new azure.TableQuery()
                  .where('PartitionKey eq ?', AZURE_MANIFEST_TAG)
                  .and('RowKey eq ?', that._currentKey());

          // find the list of uploaded revisions
          that.client.queryEntities(AZURE_TABLE_NAME, query, null, function(error, result, response) {
            if(!error){
              resolve(result.entries);
            } else {
              reject(error);
            }
          });
        } else {
          reject(error);
        }
      });
    });
  },

  _initTaggingAdapter: function() {
    var TaggingAdapter = require('ember-deploy/utilities/tagging/'+this.tagging);

    return new TaggingAdapter({
      manifest: this.manifest
    });
  },

  _upload: function(value, key) {
    return this._uploadIfNotAlreadyInManifest(value, key)
      .then(this._deploySuccessMessage.bind(this, key))
      .then(this._printSuccessMessage.bind(this))
      .then(function() { return key; })
      .catch(function() {
        var message = this._deployErrorMessage();
        return this._printErrorMessage(message);
      }.bind(this));
  },

  _uploadIfNotAlreadyInManifest: function(value, key) {
    // read the buffer
    value = value.toString();
    var that = this;

    return new RSVP.Promise(function(resolve, reject) {
      // create table if not already existent
      that.client.createTableIfNotExists(AZURE_TABLE_NAME, function(error, result, response) {
        if(!error){
          var query = new azure.TableQuery()
                  .where('PartitionKey eq ?', AZURE_MANIFEST_TAG)
                  .and('RowKey eq ?', key);

          // find the list of uploaded revisions
          that.client.queryEntities(AZURE_TABLE_NAME, query, null, function(error, result, response) {
            if(!error){
              // has this key already been uploaded once?
              if(result.entries.length > 0) {
                reject("Key already in manifest - revision already uploaded or collided.");
              } else {
                var entGen = azure.TableUtilities.entityGenerator;
                var entity = {};
                entity["PartitionKey"] = entGen.String(AZURE_MANIFEST_TAG);
                entity["RowKey"] = entGen.String(key);
                entity["content"] = entGen.String(value);

                that.client.insertEntity(AZURE_TABLE_NAME, entity,  function (error, result, response) {
                  if(!error){
                    resolve(result);
                  } else {
                    reject(error);
                  }
                });
              }
            } else {
              reject(error);
            }
          });
        } else {
          reject(error);
        }
      });
    });
  },

  _currentKey: function() {
    return this.manifest+':current';
  },

  _printSuccessMessage: function(message) {
    return this.ui.writeLine(message);
  },

  _printErrorMessage: function(message) {
    return Promise.reject(new SilentError(message));
  },

  _deploySuccessMessage: function(revisionKey) {
    var success       = green('\nUpload successful!\n\n');
    var uploadMessage = white('Uploaded revision: ')+green(revisionKey);

    return success + uploadMessage;
  },

  _deployErrorMessage: function() {
    var failure    = '\nUpload failed!\n';
    var suggestion = 'Did you try to upload an already uploaded revision?\n\n';
    var solution   = 'Please run `'+green('ember deploy:list')+'` to ' +
                     'investigate.';

    return failure + '\n' + white(suggestion) + white(solution);
  },

  _noRevisionPassedMessage: function() {
    var err = '\nError! Please pass a revision to `deploy:activate`.\n\n';

    return err + white(this._revisionSuggestion());
  },

  _activationSuccessfulMessage: function() {
    var success = green('\nActivation successful!\n\n');
    var message = white('Please run `'+green('ember deploy:list')+'` to see '+
                        'what revision is current.');

    return success + message;
  },

  _revisionNotFoundMessage: function() {
    var err = '\nError! Passed revision could not be found in manifest!\n\n';

    return err + white(this._revisionSuggestion());
  },

  _revisionSuggestion: function() {
    var suggestion = 'Try to run `'+green('ember deploy:list')+'` '+
                     'and pass a revision listed there to `' +
                     green('ember deploy:activate')+'`.\n\nExample: \n\n'+
                     'ember deploy:activate --revision <manifest>:<sha>';

    return suggestion;
  },

  _revisionListMessage: function(revisions, currentRevision) {
    var manifestSize  = this.manifestSize;
    var headline      = '\nLast '+ manifestSize + ' uploaded revisions:\n\n';
    var footer        = '\n\n# => - current revision';
    var revisionsList = revisions.reduce(function(prev, curr) {
      var prefix = (curr === currentRevision) ? '| => ' : '|    ';
      return prev + prefix + chalk.green(curr) + '\n';
    }, '');

    return headline + revisionsList + footer;
  }
});
<commit_msg>List recent uploads<commit_after>// var Adapter     = require('../adapter');
var RSVP        = require('rsvp');
var azure       = require('azure-storage');
var chalk       = require('chalk');
var Promise     = require('ember-cli/lib/ext/promise');
var SilentError = require('ember-cli/lib/errors/silent');
var Adapter     = require('ember-deploy/utilities/adapter')

var DEFAULT_MANIFEST_SIZE   = 10;
var DEFAULT_TAGGING_ADAPTER = 'sha';

var green = chalk.green;
var white = chalk.white;

var AZURE_TABLE_NAME = 'emberdeploy';
var AZURE_MANIFEST_TAG = 'manifest';

module.exports = Adapter.extend({
  init: function() {
    this.tagging      = this.tagging || DEFAULT_TAGGING_ADAPTER;
    this.manifestSize = this.manifestSize || DEFAULT_MANIFEST_SIZE;

    // be transparant to either passing a connectionString or the storageAccount + storageAccessKey
    if(this.config.connectionString) {
      this.client = azure.createTableService(this.config.connectionString);
    } else if (this.config.storageAccount && this.config.storageAccessKey) {
      this.client = azure.createTableService(this.config.storageAccount, this.config.storageAccessKey);
    } else {
      console.error("No connection string or storage account plus access key set for this Azure deployment.");
      // TODO throw error

      return false;
    }

    /*
      TODO to make feasible for tests;
      In order to run the tests, the following environment variables need to be set up:
      AZURE_STORAGE_CONNECTION_STRING="valid storage connection string"
    */
  },

  upload: function(value) {
    var taggingAdapter = this.taggingAdapter || this._initTaggingAdapter();
    var key            = taggingAdapter.createTag();

    return this._upload(value, key);
  },

  list: function() {
    return RSVP.hash({
      revisions: this._list(),
      current: this._current()
    })
    .then(function(results) {
      var revisions = results.revisions;
      var current   = results.current;
      var message   = this._revisionListMessage(revisions, current);

      this._printSuccessMessage(message);

      return message;
    }.bind(this));
  },

  activate: function(revisionKey) {
    if (!revisionKey) {
      return this._printErrorMessage(this._noRevisionPassedMessage());
    };

    var uploadKey = this._currentKey();
    var that      = this;

    return new RSVP.Promise(function(resolve, reject) {
      that._list()
        .then(function(uploads) {
          return uploads.indexOf(revisionKey) > -1 ? resolve() : reject();
        })
        .then(function() {
          return that.client.set(uploadKey, revisionKey);
        })
        .then(resolve);
    })
    .then(this._activationSuccessfulMessage)
    .then(this._printSuccessMessage.bind(this))
    .catch(function() {
      return this._printErrorMessage(this._revisionNotFoundMessage());
    }.bind(this));
  },

  _list: function() {
    var that = this;

    return new Promise(function(resolve, reject) {
      // create table if not already existent
      that.client.createTableIfNotExists(AZURE_TABLE_NAME, function(error, result, response) {
        if(!error){
          var query = new azure.TableQuery()
                  .where('PartitionKey eq ?', AZURE_MANIFEST_TAG);

          // find the list of uploaded revisions
          that.client.queryEntities(AZURE_TABLE_NAME, query, null, function(error, result, response) {
            if(!error) {
              var entries = result.entries.map(function(entry) {
                return entry["RowKey"]["_"];
              });

              resolve(entries);
            } else {
              reject(error);
            }
          });
        } else {
          reject(error);
        }
      });
    });
  },

  _current: function() {
    var that = this;

    return new Promise(function(resolve, reject) {
      // create table if not already existent
      that.client.createTableIfNotExists(AZURE_TABLE_NAME, function(error, result, response) {
        if(!error){
          // find the current tag
          var query = new azure.TableQuery()
                  .where('PartitionKey eq ?', AZURE_MANIFEST_TAG)
                  .and('RowKey eq ?', that._currentKey());

          // find the list of uploaded revisions
          that.client.queryEntities(AZURE_TABLE_NAME, query, null, function(error, result, response) {
            if(!error){
              resolve(result.entries);
            } else {
              reject(error);
            }
          });
        } else {
          reject(error);
        }
      });
    });
  },

  _initTaggingAdapter: function() {
    var TaggingAdapter = require('ember-deploy/utilities/tagging/'+this.tagging);

    return new TaggingAdapter({
      manifest: this.manifest
    });
  },

  _upload: function(value, key) {
    return this._uploadIfNotAlreadyInManifest(value, key)
      .then(this._deploySuccessMessage.bind(this, key))
      .then(this._printSuccessMessage.bind(this))
      .then(function() { return key; })
      .catch(function() {
        var message = this._deployErrorMessage();
        return this._printErrorMessage(message);
      }.bind(this));
  },

  _uploadIfNotAlreadyInManifest: function(value, key) {
    // read the buffer
    value = value.toString();
    var that = this;

    return new RSVP.Promise(function(resolve, reject) {
      // create table if not already existent
      that.client.createTableIfNotExists(AZURE_TABLE_NAME, function(error, result, response) {
        if(!error){
          var query = new azure.TableQuery()
                  .where('PartitionKey eq ?', AZURE_MANIFEST_TAG)
                  .and('RowKey eq ?', key);

          // find the list of uploaded revisions
          that.client.queryEntities(AZURE_TABLE_NAME, query, null, function(error, result, response) {
            if(!error){
              // has this key already been uploaded once?
              if(result.entries.length > 0) {
                reject("Key already in manifest - revision already uploaded or collided.");
              } else {
                var entGen = azure.TableUtilities.entityGenerator;
                var entity = {};
                entity["PartitionKey"] = entGen.String(AZURE_MANIFEST_TAG);
                entity["RowKey"] = entGen.String(key);
                entity["content"] = entGen.String(value);

                that.client.insertEntity(AZURE_TABLE_NAME, entity,  function (error, result, response) {
                  if(!error){
                    resolve(result);
                  } else {
                    reject(error);
                  }
                });
              }
            } else {
              reject(error);
            }
          });
        } else {
          reject(error);
        }
      });
    });
  },

  _currentKey: function() {
    return this.manifest+':current';
  },

  _printSuccessMessage: function(message) {
    return this.ui.writeLine(message);
  },

  _printErrorMessage: function(message) {
    return Promise.reject(new SilentError(message));
  },

  _deploySuccessMessage: function(revisionKey) {
    var success       = green('\nUpload successful!\n\n');
    var uploadMessage = white('Uploaded revision: ')+green(revisionKey);

    return success + uploadMessage;
  },

  _deployErrorMessage: function() {
    var failure    = '\nUpload failed!\n';
    var suggestion = 'Did you try to upload an already uploaded revision?\n\n';
    var solution   = 'Please run `'+green('ember deploy:list')+'` to ' +
                     'investigate.';

    return failure + '\n' + white(suggestion) + white(solution);
  },

  _noRevisionPassedMessage: function() {
    var err = '\nError! Please pass a revision to `deploy:activate`.\n\n';

    return err + white(this._revisionSuggestion());
  },

  _activationSuccessfulMessage: function() {
    var success = green('\nActivation successful!\n\n');
    var message = white('Please run `'+green('ember deploy:list')+'` to see '+
                        'what revision is current.');

    return success + message;
  },

  _revisionNotFoundMessage: function() {
    var err = '\nError! Passed revision could not be found in manifest!\n\n';

    return err + white(this._revisionSuggestion());
  },

  _revisionSuggestion: function() {
    var suggestion = 'Try to run `'+green('ember deploy:list')+'` '+
                     'and pass a revision listed there to `' +
                     green('ember deploy:activate')+'`.\n\nExample: \n\n'+
                     'ember deploy:activate --revision <manifest>:<sha>';

    return suggestion;
  },

  _revisionListMessage: function(revisions, currentRevision) {
    var manifestSize  = this.manifestSize;
    var headline      = '\nLast '+ manifestSize + ' uploaded revisions:\n\n';
    var footer        = '\n\n# => - current revision';
    var revisionsList = revisions.reduce(function(prev, curr) {
      var prefix = (curr === currentRevision) ? '| => ' : '|    ';
      return prev + prefix + chalk.green(curr) + '\n';
    }, '');

    return headline + revisionsList + footer;
  }
});
<|endoftext|>


<commit_before>mini.Module(
	"basic/utils"
)
.requires(

)
.defines(function() {
	var Utils = {

		Function: {
			bind: function(func, obj) {
				return function() {
					return func.apply(obj, arguments);
				};
			}
		},

		Array: {
			fill: function(value, length) {
				arr = [];
				for(var i = 0; i < length; i++)
					arr.push(value);
				return arr;
			},
			sort: function(arr, propOrAcc, context) {
				if(typeof propOrAcc === "string") {
					return _.sortBy(arr, function(element) { return element[propOrAcc]; }, context);
				} else if(typeof propOrAcc === "function") {
					return _.sortBy(arr, propOrAcc, context);
				} else {
					return _.sortBy(arr, function(element) { return element; } , context);
				}
			}
		},

		Object: {
			each: function(obj, func) {
				for(var propertyName in obj) {
					func(propertyName, obj[propertyName]);
				}
			}
		},

		round: function(number, precision) {
			precision = Math.pow(10, precision || 0);
			return Math.round(number * precision) / precision;
		}

	};

	return Utils;
});
<commit_msg>added comment for Object.each<commit_after>mini.Module(
	"basic/utils"
)
.requires(

)
.defines(function() {
	var Utils = {

		Function: {
			bind: function(func, obj) {
				return function() {
					return func.apply(obj, arguments);
				};
			}
		},

		Array: {
			fill: function(value, length) {
				arr = [];
				for(var i = 0; i < length; i++)
					arr.push(value);
				return arr;
			},
			sort: function(arr, propOrAcc, context) {
				if(typeof propOrAcc === "string") {
					return _.sortBy(arr, function(element) { return element[propOrAcc]; }, context);
				} else if(typeof propOrAcc === "function") {
					return _.sortBy(arr, propOrAcc, context);
				} else {
					return _.sortBy(arr, function(element) { return element; } , context);
				}
			}
		},

		Object: {
			// calls given function for each property in object with key and value as parameters
			each: function(obj, func) {
				for(var propertyName in obj) {
					func(propertyName, obj[propertyName]);
				}
			}
		},

		round: function(number, precision) {
			precision = Math.pow(10, precision || 0);
			return Math.round(number * precision) / precision;
		}

	};

	return Utils;
});
<|endoftext|>