<commit_before>var chokidar = alchemy.use('chokidar'),
    cwd      = process.cwd(),
    allowed  = /\.js$|\.json$|\.ejs$|^((?!\.).)*$/, // Allow these extensions, or no extensions at all
    ignored  = /^app\/public\/views|^temp/,
    count    = 0,
    watcher,
    watchlog,
    watchtimer;

if (alchemy.settings.config.restartOnFileChange) {

	watchlog = function watchlog() {
		log.warn(String(count).red.bold + ' files are being monitored for changes');
	};

	// Start watching all the files, starting with the current working directory
	watcher = chokidar.watch(cwd, {ignored: function ignoreThisPath(path) {

		var isAllowed,
		    file;

		if (watchtimer) {
			clearTimeout(watchtimer);
		}

		watchtimer = setTimeout(watchlog, 450);

		path = path.replace(cwd+'/', '');
		file = path.split('/');
		file = file[file.length-1];

		// Only allow the specified extensions
		isAllowed = allowed.exec(file);

		// If it's already false, return it
		if (!isAllowed) {
			return true;
		}

		// See if it's still allowed based on patterns to ignore
		isAllowed = !ignored.exec(path);

		// If it's still allowed, make sure it isn't 3 or more node_modules deep
		if (isAllowed && path.count('node_modules') > 2) {
			isAllowed = false;
		}

		// If it's still allowed, increase the watch count
		if (isAllowed) {
			count++;
		}

		// Return if it should be ignored or not
		return !isAllowed;

	}, persistent: true});

	// Kill the server when any of the files change
	watcher.on('change', function(path, stats) {

		// Don't do anything if alchemy hasn't finished loading
		if (!alchemy.loaded) {
			return false;
		}

		// Also skip files that have changed in the temporary target
		if (path.indexOf('app/public/views/') > -1) {
			return false;
		}

		// Also skip files in the temp directory
		if (path.indexOf('temp/') === 0) {
			return false;
		}

		// Only allow .js, .json & .ejs files
		if (!allowed.exec(path)) {
			return false;
		}

		// This actually does not restart the server, it just kills it.
		// You should run it together with forever
		die('Killing server because "' + path.replace(cwd + '/', '').bold + '" has been modified');
	});
}<commit_msg>Allow user to configure the file extensions to watch for devwatch reset<commit_after>var chokidar = alchemy.use('chokidar'),
    cwd      = process.cwd(),
    allowed  = /\.js$|\.json$|\.ejs$|^((?!\.).)*$/, // Allow these extensions, or no extensions at all
    ignored  = /^app\/public\/views|^temp/,
    count    = 0,
    watcher,
    watchlog,
    watchtimer,
    allowedPattern,
    extensions = ['js', 'json', 'ejs'];

if (alchemy.settings.config.restartOnFileChange) {

	// Get the allowed extensions
	if (Array.isArray(alchemy.settings.config.restartExtensions)) {
		extensions = alchemy.settings.config.restartExtensions;
	}

	allowedPattern = '';

	// Go over every extensionin the array
	extensions.forEach(function(extension) {

		if (allowedPattern) {
			allowedPattern += '|';
		}

		allowedPattern += '\\.' + extension + '$';
	});

	// Finish the pattern
	allowedPattern += '|^((?!\\.).)*$';

	// Create the regex
	allowed = new RegExp(allowedPattern);

	watchlog = function watchlog() {
		log.warn(String(count).red.bold + ' files are being monitored for changes');
	};

	// Start watching all the files, starting with the current working directory
	watcher = chokidar.watch(cwd, {ignored: function ignoreThisPath(path) {

		var isAllowed,
		    file;

		if (watchtimer) {
			clearTimeout(watchtimer);
		}

		watchtimer = setTimeout(watchlog, 450);

		path = path.replace(cwd+'/', '');
		file = path.split('/');
		file = file[file.length-1];

		// Only allow the specified extensions
		isAllowed = allowed.exec(file);

		// If it's already false, return it
		if (!isAllowed) {
			return true;
		}

		// See if it's still allowed based on patterns to ignore
		isAllowed = !ignored.exec(path);

		// If it's still allowed, make sure it isn't 3 or more node_modules deep
		if (isAllowed && path.count('node_modules') > 2) {
			isAllowed = false;
		}

		// If it's still allowed, increase the watch count
		if (isAllowed) {
			count++;
		}

		// Return if it should be ignored or not
		return !isAllowed;

	}, persistent: true});

	// Kill the server when any of the files change
	watcher.on('change', function(path, stats) {

		// Don't do anything if alchemy hasn't finished loading
		if (!alchemy.loaded) {
			return false;
		}

		// Also skip files that have changed in the temporary target
		if (path.indexOf('app/public/views/') > -1) {
			return false;
		}

		// Also skip files in the temp directory
		if (path.indexOf('temp/') === 0) {
			return false;
		}

		// Only allow .js, .json & .ejs files
		if (!allowed.exec(path)) {
			return false;
		}

		// This actually does not restart the server, it just kills it.
		// You should run it together with forever
		die('Killing server because "' + path.replace(cwd + '/', '').bold + '" has been modified');
	});
}<|endoftext|>


<commit_before>/*
 * Represents a single IRC server.
 */
"use strict";
var irc = require("irc");
var q = require("q");
var log = require("../logging").get("irc-server");
var actions = require("../models/actions");

var hookFunction = function(client, server, fn, isBot) {
    if (isBot) {
        client.addListener("message", function(from, to, text) {
            fn(server, from, to, actions.irc.createMessage(text));
        });
        client.addListener("ctcp-privmsg", function(from, to, text) {
            if (text.indexOf("ACTION ") === 0) {
                fn(server, from, to, actions.irc.createEmote(
                    text.substring("ACTION ".length)
                ));
            }
        });
        client.addListener("notice", function(from, to, text) {
            if (from) { // ignore server notices
                fn(server, from, to, actions.irc.createNotice(text));
            }
        });
        client.addListener("topic", function(channel, topic, nick) {
            fn(server, nick, channel, actions.irc.createTopic(topic));
        });
    }
    else {
        // just listen for PMs for clients. If you listen for rooms, you'll get
        // duplicates since the bot will also invoke the callback fn!
        client.addListener("message", function(from, to, text) {
            if (to.indexOf("#") === 0) { return; }
            fn(server, from, to, actions.irc.createMessage(text));
        });
        client.addListener("notice", function(from, to, text) {
            if (!from || to.indexOf("#") === 0) { return; }
            fn(server, from, to, actions.irc.createNotice(text));
        });
        client.addListener("ctcp-privmsg", function(from, to, text) {
            if (to.indexOf("#") === 0) { return; }
            if (text.indexOf("ACTION ") === 0) {
                fn(
                    server, from, to, actions.irc.createEmote(
                        text.substring("ACTION ".length)
                    )
                );
            }
        });
    }
};

function IrcServer(domain, serviceConfig) {
    this.domain = domain;
    this.doNotTrackChannels = serviceConfig.dynamicChannels.exclude;
    this.nick = serviceConfig.botConfig.nick;
    this.nickTemplate = serviceConfig.ircClients.nickTemplate;
    this.allowNickChanges = serviceConfig.ircClients.allowNickChanges;
    this.aliasTemplate = serviceConfig.dynamicChannels.aliasTemplate;
    this.userTemplate = serviceConfig.matrixClients.userTemplate;
    this.whitelist = serviceConfig.dynamicChannels.whitelist;
    this.enablePrivateMessages = serviceConfig.privateMessages.enabled;
    this.enableDynamicChannels = serviceConfig.dynamicChannels.enabled;
    this.dynamicChannelVisibility = serviceConfig.dynamicChannels.visibility;
    this.port = serviceConfig.port;
    this.maxClients = serviceConfig.ircClients.maxClients;
    this.ssl = Boolean(serviceConfig.ssl);
    this.mirrorJoinPart = serviceConfig.matrixClients.mirrorJoinPart;
}

IrcServer.prototype.connect = function(channels, callbacks) {
    // auto-connect as a bot to channels being tracked.
    return this.connectAs(this.nick, channels,
                          callbacks);
};

IrcServer.prototype.connectAs = function(nick, channels, callbacks, existingDefer) {
    var defer = existingDefer || q.defer();
    var opts = {
        autoConnect: false,
        autoRejoin: true,
        floodProtection: true,
        floodProtectionDelay: 700,
        port: this.port,
        secure: this.ssl
    };

    // strip do not track channels
    if (channels) {
        for (var i=0; i<channels.length; i++) {
            if (this.doNotTrackChannels.indexOf(channels[i]) !== -1) {
                channels.splice(i, 1);
                i--;
            }
        }
    }

    log.info("Connecting to IRC server %s as %s - Joining channels %s",
        this.domain, nick, JSON.stringify(channels));
    var thisServer = this;
    var client = new irc.Client(this.domain, nick, opts);
    client.addListener("error", function(err) {
        log.error(
            "Server: %s (%s) Error: %s", thisServer.domain, nick,
            JSON.stringify(err)
        );
    });
    client.addListener("netError", function(err) {
        log.error(
            "Server: %s (%s) Network Error: %s", thisServer.domain, nick,
            // take up more lines to be more visible ^_________^
            JSON.stringify(err, undefined, 2)
        );
    });

    if (callbacks) {
        var clientIsBot = nick === this.nick;
        if (callbacks.onMessage) {
            hookFunction(
                client, thisServer, callbacks.onMessage, clientIsBot
            );
        }
        // make the bot listen for join/parts
        if (callbacks.onPart && clientIsBot) {
            client.addListener("part", function(chan, nick, reason, msg) {
                callbacks.onPart(thisServer, nick, chan, "part");
            });
            client.addListener("quit", function(nick, reason, chans, msg) {
                chans = chans || [];
                chans.forEach(function(chan) {
                    callbacks.onPart(thisServer, nick, chan, "quit");
                });
            });
            client.addListener("kick", function(chan, nick, by, reason, msg) {
                callbacks.onPart(thisServer, nick, chan, "kick");
            });
        }
        if (callbacks.onJoin && clientIsBot) {
            client.addListener("join", function(chan, nick, msg) {
                callbacks.onJoin(thisServer, nick, chan, "join");
            });
            client.addListener("names", function(chan, names, msg) {
                if (names) {
                    Object.keys(names).forEach(function(nick) {
                        var opsLevel = names[nick]; // + @ or empty string
                        // TODO do something with opsLevel
                        callbacks.onJoin(thisServer, nick, chan, "names");
                    });
                }
            });
        }
    }

    // Start a 10s timer to redo the connection if we haven't connected. This
    // has been added because in the wild it is possible to just never get
    // a connected callback, or a netError/error callback from the IRC lib :(
    // e.g. just the "Connecting to" line and that's it.
    var gotConnectedCallback = false;
    setTimeout(function() {
        if (!gotConnectedCallback) {
            log.error(
                "%s (%s) still not connected after 15s. Nudging connection...",
                thisServer.domain, nick
            );
            client.disconnect();
            thisServer.connectAs(nick, channels, callbacks, defer);
        }
    }, 15000);

    client.connect(function() {
        gotConnectedCallback = true;
        var localPort = -1;
        if (client.conn && client.conn.localPort) {
            localPort = client.conn.localPort;
        }
        log.info(
            "Server: %s (%s) connected (local port %s). Joining %s channels",
            thisServer.domain, nick, localPort,
            (channels ? channels.length : "0")
        );
        if (channels) {
            for (var i=0; i<channels.length; i++) {
                client.join(channels[i]);
            }
        }
        defer.resolve(client);
    });

    return defer.promise;
};

IrcServer.prototype.exposesChannelsPublicly = function() {
    return (
        this.enableDynamicChannels && this.dynamicChannelVisibility == "public"
    );
};

IrcServer.prototype.allowsPms = function() {
    return this.enablePrivateMessages;
};

IrcServer.prototype.shouldMirrorJoinParts = function() {
    return this.mirrorJoinPart;
};

IrcServer.prototype.getUserLocalpart = function(nick) {
    // the template is just a literal string with special vars; so find/replace
    // the vars and strip the @
    var uid = this.userTemplate.replace(/\$SERVER/g, this.domain);
    return uid.replace(/\$NICK/g, nick).substring(1);
};

IrcServer.prototype.claimsUserId = function(userId) {
    // the server claims the given user ID if the ID matches the user ID template.
    var regex = templateToRegex(
        this.userTemplate,
        {
            "$SERVER": this.domain
        },
        {
            "$NICK": "(.*)"
        },
        ":.*"
    );
    return new RegExp(regex).test(userId);
};

IrcServer.prototype.getNickFromUserId = function(userId) {
    // extract the nick from the given user ID
    var regex = templateToRegex(
        this.userTemplate,
        {
            "$SERVER": this.domain
        },
        {
            "$NICK": "(.*)"
        },
        ":.*"
    );
    var match = new RegExp(regex).exec(userId);
    if (!match) {
        return null;
    }
    return match[1];
};

IrcServer.prototype.claimsAlias = function(alias) {
    // the server claims the given alias if the alias matches the alias template
    var regex = templateToRegex(
        this.aliasTemplate,
        {
            "$SERVER": this.domain
        },
        {
            "$CHANNEL": "(.*)"
        },
        ":.*"
    );
    return new RegExp(regex).test(alias);
};

IrcServer.prototype.getChannelFromAlias = function(alias) {
    // extract the channel from the given alias
    var regex = templateToRegex(
        this.aliasTemplate,
        {
            "$SERVER": this.domain
        },
        {
            "$CHANNEL": "(.*)"
        },
        ":.*"
    );
    var match = new RegExp(regex).exec(alias);
    if (!match) {
        return null;
    }
    return match[1];
};

IrcServer.prototype.getNick = function(userId, displayName) {
    var localpart = userId.substring(1).split(":")[0];
    var display = displayName || localpart;
    var template = this.nickTemplate;
    var nick = template.replace(/\$USERID/g, userId);
    nick = nick.replace(/\$LOCALPART/g, localpart);
    nick = nick.replace(/\$DISPLAY/g, display);
    return nick;
};

IrcServer.prototype.getAliasRegex = function() {
    return templateToRegex(
        this.aliasTemplate,
        {
            "$SERVER": this.domain  // find/replace $server
        },
        {
            "$CHANNEL": ".*"  // the nick is unknown, so replace with a wildcard
        },
        // The regex applies to the entire alias, so add a wildcard after : to
        // match all domains.
        ":.*"
    );
};

IrcServer.prototype.getUserRegex = function() {
    return templateToRegex(
        this.userTemplate,
        {
            "$SERVER": this.domain  // find/replace $server
        },
        {
            "$NICK": ".*"  // the nick is unknown, so replace with a wildcard
        },
        // The regex applies to the entire user ID, so add a wildcard after : to
        // match all domains.
        ":.*"
    );
};

function templateToRegex(template, literalVars, regexVars, suffix) {
    // The 'template' is a literal string with some special variables which need
    // to be find/replaced.
    var regex = template;
    Object.keys(literalVars).forEach(function(varPlaceholder) {
        regex = regex.replace(
            new RegExp(escapeRegExp(varPlaceholder), 'g'),
            literalVars[varPlaceholder]
        );
    });

    // at this point the template is still a literal string, so escape it before
    // applying the regex vars.
    regex = escapeRegExp(regex);
    // apply regex vars
    Object.keys(regexVars).forEach(function(varPlaceholder) {
        regex = regex.replace(
            // double escape, because we bluntly escaped the entire string before
            // so our match is now escaped.
            new RegExp(escapeRegExp(escapeRegExp(varPlaceholder)), 'g'),
            regexVars[varPlaceholder]
        );
    });

    suffix = suffix || "";
    return regex + suffix;
}

function escapeRegExp(string) {
    // MDN -> https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
    return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

module.exports.IrcServer = IrcServer;<commit_msg>Set mapping to the nick temporarily.<commit_after>/*
 * Represents a single IRC server.
 */
"use strict";
var irc = require("irc");
var q = require("q");
var ident = require("./ident");
var log = require("../logging").get("irc-server");
var actions = require("../models/actions");

var hookFunction = function(client, server, fn, isBot) {
    if (isBot) {
        client.addListener("message", function(from, to, text) {
            fn(server, from, to, actions.irc.createMessage(text));
        });
        client.addListener("ctcp-privmsg", function(from, to, text) {
            if (text.indexOf("ACTION ") === 0) {
                fn(server, from, to, actions.irc.createEmote(
                    text.substring("ACTION ".length)
                ));
            }
        });
        client.addListener("notice", function(from, to, text) {
            if (from) { // ignore server notices
                fn(server, from, to, actions.irc.createNotice(text));
            }
        });
        client.addListener("topic", function(channel, topic, nick) {
            fn(server, nick, channel, actions.irc.createTopic(topic));
        });
    }
    else {
        // just listen for PMs for clients. If you listen for rooms, you'll get
        // duplicates since the bot will also invoke the callback fn!
        client.addListener("message", function(from, to, text) {
            if (to.indexOf("#") === 0) { return; }
            fn(server, from, to, actions.irc.createMessage(text));
        });
        client.addListener("notice", function(from, to, text) {
            if (!from || to.indexOf("#") === 0) { return; }
            fn(server, from, to, actions.irc.createNotice(text));
        });
        client.addListener("ctcp-privmsg", function(from, to, text) {
            if (to.indexOf("#") === 0) { return; }
            if (text.indexOf("ACTION ") === 0) {
                fn(
                    server, from, to, actions.irc.createEmote(
                        text.substring("ACTION ".length)
                    )
                );
            }
        });
    }
};

function IrcServer(domain, serviceConfig) {
    this.domain = domain;
    this.doNotTrackChannels = serviceConfig.dynamicChannels.exclude;
    this.nick = serviceConfig.botConfig.nick;
    this.nickTemplate = serviceConfig.ircClients.nickTemplate;
    this.allowNickChanges = serviceConfig.ircClients.allowNickChanges;
    this.aliasTemplate = serviceConfig.dynamicChannels.aliasTemplate;
    this.userTemplate = serviceConfig.matrixClients.userTemplate;
    this.whitelist = serviceConfig.dynamicChannels.whitelist;
    this.enablePrivateMessages = serviceConfig.privateMessages.enabled;
    this.enableDynamicChannels = serviceConfig.dynamicChannels.enabled;
    this.dynamicChannelVisibility = serviceConfig.dynamicChannels.visibility;
    this.port = serviceConfig.port;
    this.maxClients = serviceConfig.ircClients.maxClients;
    this.ssl = Boolean(serviceConfig.ssl);
    this.mirrorJoinPart = serviceConfig.matrixClients.mirrorJoinPart;
}

IrcServer.prototype.connect = function(channels, callbacks) {
    // auto-connect as a bot to channels being tracked.
    return this.connectAs(this.nick, channels,
                          callbacks);
};

IrcServer.prototype.connectAs = function(nick, channels, callbacks, existingDefer) {
    var defer = existingDefer || q.defer();
    var opts = {
        autoConnect: false,
        autoRejoin: true,
        floodProtection: true,
        floodProtectionDelay: 700,
        port: this.port,
        secure: this.ssl
    };

    // strip do not track channels
    if (channels) {
        for (var i=0; i<channels.length; i++) {
            if (this.doNotTrackChannels.indexOf(channels[i]) !== -1) {
                channels.splice(i, 1);
                i--;
            }
        }
    }

    log.info("Connecting to IRC server %s as %s - Joining channels %s",
        this.domain, nick, JSON.stringify(channels));
    var thisServer = this;
    var client = new irc.Client(this.domain, nick, opts);
    client.addListener("error", function(err) {
        log.error(
            "Server: %s (%s) Error: %s", thisServer.domain, nick,
            JSON.stringify(err)
        );
    });
    client.addListener("netError", function(err) {
        log.error(
            "Server: %s (%s) Network Error: %s", thisServer.domain, nick,
            // take up more lines to be more visible ^_________^
            JSON.stringify(err, undefined, 2)
        );
    });

    if (callbacks) {
        var clientIsBot = nick === this.nick;
        if (callbacks.onMessage) {
            hookFunction(
                client, thisServer, callbacks.onMessage, clientIsBot
            );
        }
        // make the bot listen for join/parts
        if (callbacks.onPart && clientIsBot) {
            client.addListener("part", function(chan, nick, reason, msg) {
                callbacks.onPart(thisServer, nick, chan, "part");
            });
            client.addListener("quit", function(nick, reason, chans, msg) {
                chans = chans || [];
                chans.forEach(function(chan) {
                    callbacks.onPart(thisServer, nick, chan, "quit");
                });
            });
            client.addListener("kick", function(chan, nick, by, reason, msg) {
                callbacks.onPart(thisServer, nick, chan, "kick");
            });
        }
        if (callbacks.onJoin && clientIsBot) {
            client.addListener("join", function(chan, nick, msg) {
                callbacks.onJoin(thisServer, nick, chan, "join");
            });
            client.addListener("names", function(chan, names, msg) {
                if (names) {
                    Object.keys(names).forEach(function(nick) {
                        var opsLevel = names[nick]; // + @ or empty string
                        // TODO do something with opsLevel
                        callbacks.onJoin(thisServer, nick, chan, "names");
                    });
                }
            });
        }
    }

    // Start a 10s timer to redo the connection if we haven't connected. This
    // has been added because in the wild it is possible to just never get
    // a connected callback, or a netError/error callback from the IRC lib :(
    // e.g. just the "Connecting to" line and that's it.
    var gotConnectedCallback = false;
    setTimeout(function() {
        if (!gotConnectedCallback) {
            log.error(
                "%s (%s) still not connected after 15s. Nudging connection...",
                thisServer.domain, nick
            );
            client.disconnect();
            thisServer.connectAs(nick, channels, callbacks, defer);
        }
    }, 15000);

    client.connect(function() {
        gotConnectedCallback = true;
        var localPort = -1;
        if (client.conn && client.conn.localPort) {
            localPort = client.conn.localPort;
        }
        log.info(
            "Server: %s (%s) connected (local port %s). Joining %s channels",
            thisServer.domain, nick, localPort,
            (channels ? channels.length : "0")
        );
        if (channels) {
            for (var i=0; i<channels.length; i++) {
                client.join(channels[i]);
            }
        }
        defer.resolve(client);
    });

    client.once("connect", function() {
        var localPort = -1;
        if (client.conn && client.conn.localPort) {
            localPort = client.conn.localPort;
        }
        if (localPort > 0) {
            // FIXME: should be user_id
            ident.setMapping(nick, localPort);
        }
    });

    return defer.promise;
};

IrcServer.prototype.exposesChannelsPublicly = function() {
    return (
        this.enableDynamicChannels && this.dynamicChannelVisibility == "public"
    );
};

IrcServer.prototype.allowsPms = function() {
    return this.enablePrivateMessages;
};

IrcServer.prototype.shouldMirrorJoinParts = function() {
    return this.mirrorJoinPart;
};

IrcServer.prototype.getUserLocalpart = function(nick) {
    // the template is just a literal string with special vars; so find/replace
    // the vars and strip the @
    var uid = this.userTemplate.replace(/\$SERVER/g, this.domain);
    return uid.replace(/\$NICK/g, nick).substring(1);
};

IrcServer.prototype.claimsUserId = function(userId) {
    // the server claims the given user ID if the ID matches the user ID template.
    var regex = templateToRegex(
        this.userTemplate,
        {
            "$SERVER": this.domain
        },
        {
            "$NICK": "(.*)"
        },
        ":.*"
    );
    return new RegExp(regex).test(userId);
};

IrcServer.prototype.getNickFromUserId = function(userId) {
    // extract the nick from the given user ID
    var regex = templateToRegex(
        this.userTemplate,
        {
            "$SERVER": this.domain
        },
        {
            "$NICK": "(.*)"
        },
        ":.*"
    );
    var match = new RegExp(regex).exec(userId);
    if (!match) {
        return null;
    }
    return match[1];
};

IrcServer.prototype.claimsAlias = function(alias) {
    // the server claims the given alias if the alias matches the alias template
    var regex = templateToRegex(
        this.aliasTemplate,
        {
            "$SERVER": this.domain
        },
        {
            "$CHANNEL": "(.*)"
        },
        ":.*"
    );
    return new RegExp(regex).test(alias);
};

IrcServer.prototype.getChannelFromAlias = function(alias) {
    // extract the channel from the given alias
    var regex = templateToRegex(
        this.aliasTemplate,
        {
            "$SERVER": this.domain
        },
        {
            "$CHANNEL": "(.*)"
        },
        ":.*"
    );
    var match = new RegExp(regex).exec(alias);
    if (!match) {
        return null;
    }
    return match[1];
};

IrcServer.prototype.getNick = function(userId, displayName) {
    var localpart = userId.substring(1).split(":")[0];
    var display = displayName || localpart;
    var template = this.nickTemplate;
    var nick = template.replace(/\$USERID/g, userId);
    nick = nick.replace(/\$LOCALPART/g, localpart);
    nick = nick.replace(/\$DISPLAY/g, display);
    return nick;
};

IrcServer.prototype.getAliasRegex = function() {
    return templateToRegex(
        this.aliasTemplate,
        {
            "$SERVER": this.domain  // find/replace $server
        },
        {
            "$CHANNEL": ".*"  // the nick is unknown, so replace with a wildcard
        },
        // The regex applies to the entire alias, so add a wildcard after : to
        // match all domains.
        ":.*"
    );
};

IrcServer.prototype.getUserRegex = function() {
    return templateToRegex(
        this.userTemplate,
        {
            "$SERVER": this.domain  // find/replace $server
        },
        {
            "$NICK": ".*"  // the nick is unknown, so replace with a wildcard
        },
        // The regex applies to the entire user ID, so add a wildcard after : to
        // match all domains.
        ":.*"
    );
};

function templateToRegex(template, literalVars, regexVars, suffix) {
    // The 'template' is a literal string with some special variables which need
    // to be find/replaced.
    var regex = template;
    Object.keys(literalVars).forEach(function(varPlaceholder) {
        regex = regex.replace(
            new RegExp(escapeRegExp(varPlaceholder), 'g'),
            literalVars[varPlaceholder]
        );
    });

    // at this point the template is still a literal string, so escape it before
    // applying the regex vars.
    regex = escapeRegExp(regex);
    // apply regex vars
    Object.keys(regexVars).forEach(function(varPlaceholder) {
        regex = regex.replace(
            // double escape, because we bluntly escaped the entire string before
            // so our match is now escaped.
            new RegExp(escapeRegExp(escapeRegExp(varPlaceholder)), 'g'),
            regexVars[varPlaceholder]
        );
    });

    suffix = suffix || "";
    return regex + suffix;
}

function escapeRegExp(string) {
    // MDN -> https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
    return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

module.exports.IrcServer = IrcServer;<|endoftext|>


<commit_before>
var Q = require('q');

// default valued
var MAX_FAILED_TIME = 10 * 24 * 60 * 60 * 1000; // 10 days
var MAX_ACTIVE_TIME = 2 * 60 * 60 * 1000; // 2 hours
var MAX_COMPLETE_TIME = 2 * 24 * 60 * 60 * 1000; // 2 days

var KueConcierge = function(config, options) {
	this.setOptions = function(options) {
		this.maxFailedTime = options.maxFailedTime || MAX_FAILED_TIME;
		this.maxActiveTime = options.maxActiveTime || MAX_ACTIVE_TIME;
		this.maxCompleteTime = options.maxCompleteTime || MAX_COMPLETE_TIME;
	};
	this.kue = require('kue');
	if (this.kue.singleton) {
		console.log('There is already a queue initialized. Redis connection string and options are not used. See here for details: https://github.com/LearnBoost/kue/blob/master/lib/kue.js#L69');
		this.queue = this.kue.singleton;
	} else {
		this.queue = this.kue.createQueue(config);
	}
	this.kue = require('kue');
	this.options = options || {};
	this.setOptions(this.options);
};

KueConcierge.prototype.isJobExpired = function(job, state) {
	var now = new Date();
	var created = new Date(parseInt(job.updated_at));
	var age = parseInt(now - created);
	var maxTime;
	switch(state) {
		case 'failed':
			maxTime = this.maxFailedTime;
			break;
		case 'active':
			maxTime = this.maxActiveTime;
			break;
		case 'complete':
			maxTime = this.maxCompleteTime;
			break;
	}
	return age > maxTime;
};

KueConcierge.prototype.cleanUp = function() {
	var instance = this;
	instance.restartStuck().then(function(){
		instance.clearComplete();
	}).then(function(){
		instance.clearFailed();
	});
};

KueConcierge.prototype.restartStuck = function() {
	var deferred = Q.defer();
	var instance = this;
	this.kue.Job.rangeByState('active', 0, 100, 'asc', function (err,jobs) {
		if (err) {
			deferred.reject(err);
		}
		jobs.forEach(function (job) {
			if (instance.isJobExpired(job,'active')) {
				job.log('Restarting job due to inactivity');
				console.log('Restarting job ' + job.id + ' due to inactivity');
				job.inactive();
			}
		});
		deferred.resolve();
	});
	return deferred.promise;
};

KueConcierge.prototype.clearComplete = function() {
	var deferred = Q.defer();
	var instance = this;
	this.kue.Job.rangeByState('complete', 0, 100, 'asc', function (err,jobs) {
		if (err) {
			deferred.reject(err);
		}
		jobs.forEach(function (job) {
			if (instance.isJobExpired(job,'complete')) {
				console.log('Removing job ' + job.id + ' (complete)');
				job.remove();
			}
		});
		deferred.resolve();
	});
	return deferred.promise;
};

KueConcierge.prototype.clearFailed = function() {
	var deferred = Q.defer();
	var instance = this;
	this.kue.Job.rangeByState('failed', 0, 100, 'asc', function (err,jobs) {
		if (err) {
			deferred.reject(err);
		}
		jobs.forEach(function (job) {
			if (instance.isJobExpired(job,'failed')) {
				console.log('Removing job ' + job.id + ' (failed)');
				job.remove();
			}
		});
		deferred.resolve();
	});
	return deferred.promise;
};

module.exports = KueConcierge;

<commit_msg>Remove duplicate require<commit_after>
var Q = require('q');

// default valued
var MAX_FAILED_TIME = 10 * 24 * 60 * 60 * 1000; // 10 days
var MAX_ACTIVE_TIME = 2 * 60 * 60 * 1000; // 2 hours
var MAX_COMPLETE_TIME = 2 * 24 * 60 * 60 * 1000; // 2 days

var KueConcierge = function(config, options) {
	this.setOptions = function(options) {
		this.maxFailedTime = options.maxFailedTime || MAX_FAILED_TIME;
		this.maxActiveTime = options.maxActiveTime || MAX_ACTIVE_TIME;
		this.maxCompleteTime = options.maxCompleteTime || MAX_COMPLETE_TIME;
	};
	this.kue = require('kue');
	if (this.kue.singleton) {
		console.log('There is already a queue initialized. Redis connection string and options are not used. See here for details: https://github.com/LearnBoost/kue/blob/master/lib/kue.js#L69');
		this.queue = this.kue.singleton;
	} else {
		this.queue = this.kue.createQueue(config);
	}
	this.options = options || {};
	this.setOptions(this.options);
};

KueConcierge.prototype.isJobExpired = function(job, state) {
	var now = new Date();
	var created = new Date(parseInt(job.updated_at));
	var age = parseInt(now - created);
	var maxTime;
	switch(state) {
		case 'failed':
			maxTime = this.maxFailedTime;
			break;
		case 'active':
			maxTime = this.maxActiveTime;
			break;
		case 'complete':
			maxTime = this.maxCompleteTime;
			break;
	}
	return age > maxTime;
};

KueConcierge.prototype.cleanUp = function() {
	var instance = this;
	instance.restartStuck().then(function(){
		instance.clearComplete();
	}).then(function(){
		instance.clearFailed();
	});
};

KueConcierge.prototype.restartStuck = function() {
	var deferred = Q.defer();
	var instance = this;
	this.kue.Job.rangeByState('active', 0, 100, 'asc', function (err,jobs) {
		if (err) {
			deferred.reject(err);
		}
		jobs.forEach(function (job) {
			if (instance.isJobExpired(job,'active')) {
				job.log('Restarting job due to inactivity');
				console.log('Restarting job ' + job.id + ' due to inactivity');
				job.inactive();
			}
		});
		deferred.resolve();
	});
	return deferred.promise;
};

KueConcierge.prototype.clearComplete = function() {
	var deferred = Q.defer();
	var instance = this;
	this.kue.Job.rangeByState('complete', 0, 100, 'asc', function (err,jobs) {
		if (err) {
			deferred.reject(err);
		}
		jobs.forEach(function (job) {
			if (instance.isJobExpired(job,'complete')) {
				console.log('Removing job ' + job.id + ' (complete)');
				job.remove();
			}
		});
		deferred.resolve();
	});
	return deferred.promise;
};

KueConcierge.prototype.clearFailed = function() {
	var deferred = Q.defer();
	var instance = this;
	this.kue.Job.rangeByState('failed', 0, 100, 'asc', function (err,jobs) {
		if (err) {
			deferred.reject(err);
		}
		jobs.forEach(function (job) {
			if (instance.isJobExpired(job,'failed')) {
				console.log('Removing job ' + job.id + ' (failed)');
				job.remove();
			}
		});
		deferred.resolve();
	});
	return deferred.promise;
};

module.exports = KueConcierge;

<|endoftext|>


<commit_before>'use babel'

import { getLanguageSettings } from '../helpers'

const name = 'CSS'
const language = {
	internalSettings: {
		selector: '.syntax--source.syntax--css'
	},
	userSettings: {
		active: true
	}
}
const properties = {
	'@import': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--at-rule.syntax--import'
		},
		userSettings: {
			active: true,
			emoji: '📦',
			pseudo: 'before',
			spacing: true
		}
	},
	'@media': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--at-rule.syntax--media'
		},
		userSettings: {
			active: true,
			emoji: '💻',
			pseudo: 'before',
			spacing: true
		}
	},
	'@keyframes': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--at-rule.syntax--keyframes'
		},
		userSettings: {
			active: true,
			emoji: '📹',
			pseudo: 'before',
			spacing: true
		}
	},
	'@font-face': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--at-rule.syntax--font-face'
		},
		userSettings: {
			active: true,
			emoji: '📖',
			pseudo: 'before',
			spacing: true
		}
	},
	'@namespace': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--at-rule.syntax--namespace'
		},
		userSettings: {
			active: true,
			emoji: '🏷',
			pseudo: 'after',
			spacing: false
		}
	},
	'@charset': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--at-rule.syntax--charset'
		},
		userSettings: {
			active: true,
			emoji: '🔣',
			pseudo: 'before',
			spacing: true
		}
	},
	'!important': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--important'
		},
		userSettings: {
			active: true,
			emoji: '🚨',
			pseudo: 'after',
			spacing: true
		}
	}
}

export default {
	name,
	internalSettings: Object.assign({},
		language.internalSettings,
		getLanguageSettings(properties, 'internalSettings')
	),
	userSettings: Object.assign({},
		language.userSettings,
		getLanguageSettings(properties, 'userSettings')
	)
}
<commit_msg>Updated CSS @namespace keyword<commit_after>'use babel'

import { getLanguageSettings } from '../helpers'

const name = 'CSS'
const language = {
	internalSettings: {
		selector: '.syntax--source.syntax--css'
	},
	userSettings: {
		active: true
	}
}
const properties = {
	'@import': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--at-rule.syntax--import'
		},
		userSettings: {
			active: true,
			emoji: '📦',
			pseudo: 'before',
			spacing: true
		}
	},
	'@media': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--at-rule.syntax--media'
		},
		userSettings: {
			active: true,
			emoji: '💻',
			pseudo: 'before',
			spacing: true
		}
	},
	'@keyframes': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--at-rule.syntax--keyframes'
		},
		userSettings: {
			active: true,
			emoji: '📹',
			pseudo: 'before',
			spacing: true
		}
	},
	'@font-face': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--at-rule.syntax--font-face'
		},
		userSettings: {
			active: true,
			emoji: '📖',
			pseudo: 'before',
			spacing: true
		}
	},
	'@namespace': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--at-rule.syntax--namespace'
		},
		userSettings: {
			active: true,
			emoji: '🏷',
			pseudo: 'before',
			spacing: true
		}
	},
	'@charset': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--at-rule.syntax--charset'
		},
		userSettings: {
			active: true,
			emoji: '🔣',
			pseudo: 'before',
			spacing: true
		}
	},
	'!important': {
		internalSettings: {
			selector: '.syntax--keyword.syntax--important'
		},
		userSettings: {
			active: true,
			emoji: '🚨',
			pseudo: 'after',
			spacing: true
		}
	}
}

export default {
	name,
	internalSettings: Object.assign({},
		language.internalSettings,
		getLanguageSettings(properties, 'internalSettings')
	),
	userSettings: Object.assign({},
		language.userSettings,
		getLanguageSettings(properties, 'userSettings')
	)
}
<|endoftext|>


<commit_before>'use babel';

import ModalPanelView from './modal-panel-view';

const path = require( 'path' );
const fs = require( 'fs' );
less = require("less");

class LessCompiler {

  constructor( state, less_input_path ) {

    this.less_file_extension = atom.config.get( 'atom-compile-less.lessFileExtension' );
    this.css_file_extension = atom.config.get( 'atom-compile-less.cssFileExtension' );
    this.sourcemap_file_extension = atom.config.get( 'atom-compile-less.sourcemapFileExtension' );
    this.display_time = atom.config.get( 'atom-compile-less.successDisplayTime' );
    this.should_compress = atom.config.get( 'atom-compile-less.shouldCompressCSS' );
    this.should_notify_success = atom.config.get( 'atom-compile-less.shouldNotifySuccess' );
    this.should_create_sourcemap = atom.config.get( 'atom-compile-less.shouldCreateSourcemap' );
    this.should_inline_sourcemap = atom.config.get( 'atom-compile-less.shouldInlineSourcemap' );

    this.less_input_path = less_input_path;
    this.directory = path.dirname( this.less_input_path );
    this.input_filename = path.basename( this.less_input_path );

    this.css_output_path = this.less_input_path.replace(this.less_file_extension, this.css_file_extension);
    this.sourcemap_output_path = this.css_output_path + this.sourcemap_file_extension;

    if ( this.less_input_path == this.css_output_path ) {
      this.valid = false;
      let error_message = this.less_input_path + " is not a .less file.";
      atom.notifications.addError( error_message, {
        dismissable: true
      } );
    }
    else {
      this.valid = true;
      this.modal_panel_view = new ModalPanelView(
                                state.ModalPanelViewState,
                                this.less_input_path,
                                this.css_output_path
                              );

      this.modal_panel = atom.workspace.addModalPanel({
        item:    this.modal_panel_view.getElement(),
        visible: false
      });
    }

  }

  destroy() {
    this.modalPanel.destroy();
    this.ModalPanelView.destroy();
  }

  compile() {
    delete this.output;
    return this.valid
         ? fs.readFile(
             this.less_input_path,
             'utf-8',
             ( error, less_content ) => this.parse( error, less_content )
           )
         : false;
  }

  parse( error, less_input ) {

    if ( error )
      atom.notifications.addError(
        error,
        {
          dismissable: true
        }
      );

    else {
      let options = {};
      if ( this.should_create_sourcemap )
        options["sourceMap"] = this.should_inline_sourcemap
                          ? { sourceMapFileInline: true }
                          : {};

      less.render( less_input, options ).then( ( output ) => {
        /*
           output.css = string of css
           output.map = string of sourcemap
           output.imports = array of string filenames of the imports referenced
        */
        this.output = output;

        fs.writeFile(
           this.css_output_path,
           output.css,
           ( error ) => this.notify( error )
         );

        if ( this.should_create_sourcemap && ! this.should_inline_sourcemap )
          fs.writeFile(
             this.sourcemap_output_path,
             output.map,
             ( error ) => this.notify( error )
           );
      } ).catch( ( error ) => {
        atom.notifications.addError(
          error.message,
          {
            dismissable: true
          }
        );
      });
    }

  }

  notify( error ) {

    if ( error )
      atom.notifications.addError(
        error,
        {
          dismissable: true
        }
      );

    else if ( this.should_notify_success ) {

      this.modal_panel.show();

      let modal_panel = this.modal_panel;
      setTimeout( function() { modal_panel.hide(); }, this.display_time );
    }

  }

}

export default LessCompiler;
<commit_msg>added options to notify parser of filename for proper path handling (imports were broken)<commit_after>'use babel';

import ModalPanelView from './modal-panel-view';

const path = require( 'path' );
const fs = require( 'fs' );
less = require("less");

class LessCompiler {

  constructor( state, less_input_path ) {

    this.less_file_extension = atom.config.get( 'atom-compile-less.lessFileExtension' );
    this.css_file_extension = atom.config.get( 'atom-compile-less.cssFileExtension' );
    this.sourcemap_file_extension = atom.config.get( 'atom-compile-less.sourcemapFileExtension' );
    this.display_time = atom.config.get( 'atom-compile-less.successDisplayTime' );
    this.should_compress = atom.config.get( 'atom-compile-less.shouldCompressCSS' );
    this.should_notify_success = atom.config.get( 'atom-compile-less.shouldNotifySuccess' );
    this.should_create_sourcemap = atom.config.get( 'atom-compile-less.shouldCreateSourcemap' );
    this.should_inline_sourcemap = atom.config.get( 'atom-compile-less.shouldInlineSourcemap' );

    this.less_input_path = less_input_path;
    this.directory = path.dirname( this.less_input_path );
    this.input_filename = path.basename( this.less_input_path );

    this.css_output_path = this.less_input_path.replace(this.less_file_extension, this.css_file_extension);
    this.sourcemap_output_path = this.css_output_path + this.sourcemap_file_extension;

    if ( this.less_input_path == this.css_output_path ) {
      this.valid = false;
      let error_message = this.less_input_path + " is not a .less file.";
      atom.notifications.addError( error_message, {
        dismissable: true
      } );
    }
    else {
      this.valid = true;
      this.modal_panel_view = new ModalPanelView(
                                state.ModalPanelViewState,
                                this.less_input_path,
                                this.css_output_path
                              );

      this.modal_panel = atom.workspace.addModalPanel({
        item:    this.modal_panel_view.getElement(),
        visible: false
      });
    }

  }

  destroy() {
    this.modalPanel.destroy();
    this.ModalPanelView.destroy();
  }

  compile() {
    delete this.output;
    return this.valid
         ? fs.readFile(
             this.less_input_path,
             'utf-8',
             ( error, less_content ) => this.parse( error, less_content )
           )
         : false;
  }

  parse( error, less_input ) {

    if ( error )
      atom.notifications.addError(
        error,
        {
          dismissable: true
        }
      );

    else {
      let options = {
        relativeUrls: true,
        filename:     this.less_input_path
      };
      if ( this.should_create_sourcemap )
        options["sourceMap"] = this.should_inline_sourcemap
                          ? { sourceMapFileInline: true }
                          : {};

      less.render( less_input, options ).then( ( output ) => {
        /*
           output.css = string of css
           output.map = string of sourcemap
           output.imports = array of string filenames of the imports referenced
        */
        this.output = output;

        fs.writeFile(
           this.css_output_path,
           output.css,
           ( error ) => this.notify( error )
         );

        if ( this.should_create_sourcemap && ! this.should_inline_sourcemap )
          fs.writeFile(
             this.sourcemap_output_path,
             output.map,
             ( error ) => this.notify( error )
           );
      } ).catch( ( error ) => {
        atom.notifications.addError(
          error.message,
          {
            dismissable: true
          }
        );
      });
    }

  }

  notify( error ) {

    if ( error )
      atom.notifications.addError(
        error,
        {
          dismissable: true
        }
      );

    else if ( this.should_notify_success ) {

      this.modal_panel.show();

      let modal_panel = this.modal_panel;
      setTimeout( function() { modal_panel.hide(); }, this.display_time );
    }

  }

}

export default LessCompiler;
<|endoftext|>


<commit_before>var contexts = {};
module.exports = contexts;

var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) {
    if (!original) { return; }

    for(var i = 0; i < propertiesToCopy.length; i++) {
        if (original.hasOwnProperty(propertiesToCopy[i])) {
            destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
        }
    }
};

/*
 parse is used whilst parsing
 */
var parseCopyProperties = [
    // options
    'paths',            // option - unmodified - paths to search for imports on
    'relativeUrls',     // option - whether to adjust URL's to be relative
    'rootpath',         // option - rootpath to append to URL's
    'strictImports',    // option -
    'insecure',         // option - whether to allow imports from insecure ssl hosts
    'dumpLineNumbers',  // option - whether to dump line numbers
    'compress',         // option - whether to compress
    'syncImport',       // option - whether to import synchronously
    'chunkInput',       // option - whether to chunk input. more performant but causes parse issues.
    'mime',             // browser only - mime type for sheet import
    'useFileCache',     // browser only - whether to use the per file session cache
    // context
    'processImports',   // option & context - whether to process imports. if false then imports will not be imported.
                        // Used by the import manager to stop multiple import visitors being created.
    'reference',        // Used to indicate that the contents are imported by reference
    'pluginManager'     // Used as the plugin manager for the session
];

contexts.Parse = function(options) {
    copyFromOriginal(options, this, parseCopyProperties);

    if (typeof this.paths === "string") { this.paths = [this.paths]; }
};

var evalCopyProperties = [
    'compress',       // whether to compress
    'ieCompat',       // whether to enforce IE compatibility (IE8 data-uri)
    'strictMath',     // whether math has to be within parenthesis
    'strictUnits',    // whether units need to evaluate correctly
    'sourceMap',      // whether to output a source map
    'importMultiple', // whether we are currently importing multiple copies
    'urlArgs',        // whether to add args into url tokens
    'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
    'pluginManager',  // Used as the plugin manager for the session
    'importantScope'  // used to bubble up !important statements
    ];

contexts.Eval = function(options, frames) {
    copyFromOriginal(options, this, evalCopyProperties);

    this.frames = frames || [];
    this.importantScope = this.importantScope || [];
};

contexts.Eval.prototype.inParenthesis = function () {
    if (!this.parensStack) {
        this.parensStack = [];
    }
    this.parensStack.push(true);
};

contexts.Eval.prototype.outOfParenthesis = function () {
    this.parensStack.pop();
};

contexts.Eval.prototype.isMathOn = function () {
    return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
};

contexts.Eval.prototype.isPathRelative = function (path) {
    return !/^(?:[a-z-]+:|\/i)/.test(path);
};

contexts.Eval.prototype.normalizePath = function( path ) {
    var
      segments = path.split("/").reverse(),
      segment;

    path = [];
    while (segments.length !== 0 ) {
        segment = segments.pop();
        switch( segment ) {
            case ".":
                break;
            case "..":
                if ((path.length === 0) || (path[path.length - 1] === "..")) {
                    path.push( segment );
                } else {
                    path.pop();
                }
                break;
            default:
                path.push( segment );
                break;
        }
    }

    return path.join("/");
};

//todo - do the same for the toCSS ?

<commit_msg>Fix typo<commit_after>var contexts = {};
module.exports = contexts;

var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) {
    if (!original) { return; }

    for(var i = 0; i < propertiesToCopy.length; i++) {
        if (original.hasOwnProperty(propertiesToCopy[i])) {
            destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
        }
    }
};

/*
 parse is used whilst parsing
 */
var parseCopyProperties = [
    // options
    'paths',            // option - unmodified - paths to search for imports on
    'relativeUrls',     // option - whether to adjust URL's to be relative
    'rootpath',         // option - rootpath to append to URL's
    'strictImports',    // option -
    'insecure',         // option - whether to allow imports from insecure ssl hosts
    'dumpLineNumbers',  // option - whether to dump line numbers
    'compress',         // option - whether to compress
    'syncImport',       // option - whether to import synchronously
    'chunkInput',       // option - whether to chunk input. more performant but causes parse issues.
    'mime',             // browser only - mime type for sheet import
    'useFileCache',     // browser only - whether to use the per file session cache
    // context
    'processImports',   // option & context - whether to process imports. if false then imports will not be imported.
                        // Used by the import manager to stop multiple import visitors being created.
    'reference',        // Used to indicate that the contents are imported by reference
    'pluginManager'     // Used as the plugin manager for the session
];

contexts.Parse = function(options) {
    copyFromOriginal(options, this, parseCopyProperties);

    if (typeof this.paths === "string") { this.paths = [this.paths]; }
};

var evalCopyProperties = [
    'compress',       // whether to compress
    'ieCompat',       // whether to enforce IE compatibility (IE8 data-uri)
    'strictMath',     // whether math has to be within parenthesis
    'strictUnits',    // whether units need to evaluate correctly
    'sourceMap',      // whether to output a source map
    'importMultiple', // whether we are currently importing multiple copies
    'urlArgs',        // whether to add args into url tokens
    'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
    'pluginManager',  // Used as the plugin manager for the session
    'importantScope'  // used to bubble up !important statements
    ];

contexts.Eval = function(options, frames) {
    copyFromOriginal(options, this, evalCopyProperties);

    this.frames = frames || [];
    this.importantScope = this.importantScope || [];
};

contexts.Eval.prototype.inParenthesis = function () {
    if (!this.parensStack) {
        this.parensStack = [];
    }
    this.parensStack.push(true);
};

contexts.Eval.prototype.outOfParenthesis = function () {
    this.parensStack.pop();
};

contexts.Eval.prototype.isMathOn = function () {
    return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
};

contexts.Eval.prototype.isPathRelative = function (path) {
    return !/^(?:[a-z-]+:|\/)/i.test(path);
};

contexts.Eval.prototype.normalizePath = function( path ) {
    var
      segments = path.split("/").reverse(),
      segment;

    path = [];
    while (segments.length !== 0 ) {
        segment = segments.pop();
        switch( segment ) {
            case ".":
                break;
            case "..":
                if ((path.length === 0) || (path[path.length - 1] === "..")) {
                    path.push( segment );
                } else {
                    path.pop();
                }
                break;
            default:
                path.push( segment );
                break;
        }
    }

    return path.join("/");
};

//todo - do the same for the toCSS ?

<|endoftext|>


<commit_before>let path = require("path");
let pkg = require("./package.json");

module.exports = {
    "source": __dirname,
    "target": path.join(__dirname, "www"),
    "theme": "@siimple/press-theme-siimple/config.js",
    "base": "/",
    "site": {
        //Global site links
        "links": {
            "documentation": "#",
            "privacy": "/privacy.html",
            "repository": "https://github.com/siimplie/siimple",
            "twitter": "https://twitter.com/siimplecss",
            "gitter": "https://gitter.im/siimple",
            "issues_bug": "https://github.com/siimple/siimple/issues/new?template=BUG_REPORT.md",
            "issues_feature": "https://github.com/siimple/siimple/issues/new?template=FEATURE_REQUEST.md"
        },
        //Navbar links
        "navbarLinks": [
            {"text": "Docs", "url": "#"}
        ]
    },
    //Custom head tags
    "head": [
        ["link", {"rel": "stylesheet", "href": "/assets/css/siimple-website.css"}]
    ],
    //Plugins
    "plugins": []
};

<commit_msg>website: updated website configuration<commit_after>let path = require("path");
let pkg = require("./package.json");

module.exports = {
    "source": __dirname,
    "target": path.join(__dirname, "www"),
    "theme": "@siimple/press-theme-siimple/config.js",
    "base": "/",
    "site": {
        "analytics": null
    },
    //Custom head tags
    "head": [
        ["link", {"rel": "stylesheet", "href": "/assets/css/siimple-website.css"}]
    ],
    //Plugins
    "plugins": []
};

<|endoftext|>


<commit_before>/*global window, document*/
/**
 * Fallback implementation.
 */

var prev = new Date().getTime();
var fallback = function (fn) {
    var curr = new Date().getTime();
    var ms = Math.max(0, 16 - (curr - prev));
    var req = setTimeout(fn, ms);
    prev = curr;
    return req;
};

var raf = window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    fallback;

// grab all our h* tags
var aTags = document.querySelectorAll('.nav-docs a');
var hTags = document.querySelector('.docs-content').querySelectorAll('a.anchor');

function markActive(selected) {
    var id = selected && selected.name;
    var found;
    if (id) {
        window.selected = selected;
        Array.prototype.forEach.call(aTags, function (a) {
            if (a.hash.slice(1) === id) {
                found = a;
                a.classList.add('active');
            } else {
                a.classList.remove('active');
            }
        });
    }
}

function selectCurrent() {
    var found;
    var i = 0;
    var l = hTags.length;
    var scroll = document.body.scrollTop || window.scrollY;
    if (scroll < hTags[0].offsetTop) {
        found = hTags[0];
    } else {
        for (; i < l; i++) {
            if (hTags[i].offsetTop > scroll) {
                found = hTags[i - 1];
                break;
            }
        }
    }
    if (found) markActive(found);
}

window.onscroll = function () {
    raf(selectCurrent);
};

document.addEventListener('click', function (e) {
    var target = e.target;
    if (target.className === 'toggle') {
        var next = target.nextElementSibling;
        if (next.style.display === 'none') {
            next.style.display = '';
            target.textContent = target.textContent.replace('Show', 'Collapse');
        } else {
            next.style.display = 'none';
            target.textContent = target.textContent.replace('Collapse', 'Show');
        }
    }
});

selectCurrent();
<commit_msg>ugly but working for docs site to fix nav scrolling in safari<commit_after>/*global window, document, navigator*/
/**
 * Fallback implementation.
 */

var prev = new Date().getTime();
var fallback = function (fn) {
    var curr = new Date().getTime();
    var ms = Math.max(0, 16 - (curr - prev));
    var req = setTimeout(fn, ms);
    prev = curr;
    return req;
};

var raf = window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    fallback;

// fix scrollbar issues in Safari
var isChrome = navigator.userAgent.indexOf('Chrome') > -1;
var isSafari = navigator.userAgent.indexOf('Safari') > -1;
if (isChrome && isSafari) {
    isSafari=false;
}
if (isSafari) {
    var navStyle = document.querySelector('.nav-docs').style;
    navStyle.position = 'relative';
    navStyle.float = 'left';
    navStyle.top = '-40px';
    document.querySelector('.nav-docs .logo-ampersand').style.display = 'none';
}

// grab all our h* tags
var aTags = document.querySelectorAll('.nav-docs a');
var hTags = document.querySelector('.docs-content').querySelectorAll('a.anchor');

function markActive(selected) {
    var id = selected && selected.name;
    var found;
    if (id) {
        window.selected = selected;
        Array.prototype.forEach.call(aTags, function (a) {
            if (a.hash.slice(1) === id) {
                found = a;
                a.classList.add('active');
            } else {
                a.classList.remove('active');
            }
        });
    }
}

function selectCurrent() {
    var found;
    var i = 0;
    var l = hTags.length;
    var scroll = document.body.scrollTop || window.scrollY;
    if (scroll < hTags[0].offsetTop) {
        found = hTags[0];
    } else {
        for (; i < l; i++) {
            if (hTags[i].offsetTop > scroll) {
                found = hTags[i - 1];
                break;
            }
        }
    }
    if (found) markActive(found);
}

if (!isSafari) {
    window.onscroll = function () {
        raf(selectCurrent);
    };
}

document.addEventListener('click', function (e) {
    var target = e.target;
    if (target.className === 'toggle') {
        var next = target.nextElementSibling;
        if (next.style.display === 'none') {
            next.style.display = '';
            target.textContent = target.textContent.replace('Show', 'Collapse');
        } else {
            next.style.display = 'none';
            target.textContent = target.textContent.replace('Collapse', 'Show');
        }
    }
});

if (!isSafari) {
    selectCurrent();
}

<|endoftext|>


<commit_before>/**
 * Copyright (c) 2017-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

// See https://docusaurus.io/docs/site-config for all the possible
// site configuration options.

const siteConfig = {
  title: 'Create React App', // Title for your website.
  tagline: 'Create React apps with no build configuration.',
  url: 'https://facebook.github.io', // Your website URL
  baseUrl: '/', // Base URL for your project */
  // For github.io type URLs, you would set the url and baseUrl like:
  //   url: 'https://facebook.github.io',
  //   baseUrl: '/test-site/',

  // Used for publishing and more
  projectName: 'create-react-app',
  organizationName: 'facebook',
  // For top-level user or org sites, the organization is still the same.
  // e.g., for the https://JoelMarcey.github.io site, it would be set like...
  //   organizationName: 'JoelMarcey'

  // For no header links in the top nav bar -> headerLinks: [],
  headerLinks: [
    { doc: 'getting-started', label: 'Getting started' },
    { href: 'https://reactjs.org/community/support.html', label: 'Help' },
    {
      href: 'https://www.github.com/facebook/create-react-app',
      label: 'GitHub',
    },
  ],

  /* path to images for header/footer */
  headerIcon: 'img/logo.svg',
  footerIcon: 'img/logo.svg',
  favicon: 'img/favicon/favicon.ico',

  /* Colors for website */
  colors: {
    primaryColor: '#20232a',
    secondaryColor: '#61dafb',
  },

  /* Custom fonts for website */
  /*
  fonts: {
    myFont: [
      "Times New Roman",
      "Serif"
    ],
    myOtherFont: [
      "-apple-system",
      "system-ui"
    ]
  },
  */

  // This copyright info is used in /core/Footer.js and blog RSS/Atom feeds.
  copyright: `Copyright © ${new Date().getFullYear()} Facebook`,

  highlight: {
    // Highlight.js theme to use for syntax highlighting in code blocks.
    theme: 'default',
  },

  // Add custom scripts here that would be placed in <script> tags.
  scripts: ['https://buttons.github.io/buttons.js'],

  // On page navigation for the current documentation page.
  onPageNav: 'separate',
  // No .html extensions for paths.
  cleanUrl: true,

  // Open Graph and Twitter card images.
  ogImage: 'img/logo-og.png',
  twitterImage: 'img/logo-og.png',

  // You may provide arbitrary config keys to be used as needed by your
  // template. For example, if you need your repo's URL...
  repoUrl: 'https://github.com/facebook/create-react-app',
};

module.exports = siteConfig;
<commit_msg>Add Inc to copyright notice<commit_after>/**
 * Copyright (c) 2017-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

// See https://docusaurus.io/docs/site-config for all the possible
// site configuration options.

const siteConfig = {
  title: 'Create React App', // Title for your website.
  tagline: 'Create React apps with no build configuration.',
  url: 'https://facebook.github.io', // Your website URL
  baseUrl: '/', // Base URL for your project */
  // For github.io type URLs, you would set the url and baseUrl like:
  //   url: 'https://facebook.github.io',
  //   baseUrl: '/test-site/',

  // Used for publishing and more
  projectName: 'create-react-app',
  organizationName: 'facebook',
  // For top-level user or org sites, the organization is still the same.
  // e.g., for the https://JoelMarcey.github.io site, it would be set like...
  //   organizationName: 'JoelMarcey'

  // For no header links in the top nav bar -> headerLinks: [],
  headerLinks: [
    { doc: 'getting-started', label: 'Getting started' },
    { href: 'https://reactjs.org/community/support.html', label: 'Help' },
    {
      href: 'https://www.github.com/facebook/create-react-app',
      label: 'GitHub',
    },
  ],

  /* path to images for header/footer */
  headerIcon: 'img/logo.svg',
  footerIcon: 'img/logo.svg',
  favicon: 'img/favicon/favicon.ico',

  /* Colors for website */
  colors: {
    primaryColor: '#20232a',
    secondaryColor: '#61dafb',
  },

  /* Custom fonts for website */
  /*
  fonts: {
    myFont: [
      "Times New Roman",
      "Serif"
    ],
    myOtherFont: [
      "-apple-system",
      "system-ui"
    ]
  },
  */

  // This copyright info is used in /core/Footer.js and blog RSS/Atom feeds.
  copyright: `Copyright © ${new Date().getFullYear()} Facebook Inc.`,

  highlight: {
    // Highlight.js theme to use for syntax highlighting in code blocks.
    theme: 'default',
  },

  // Add custom scripts here that would be placed in <script> tags.
  scripts: ['https://buttons.github.io/buttons.js'],

  // On page navigation for the current documentation page.
  onPageNav: 'separate',
  // No .html extensions for paths.
  cleanUrl: true,

  // Open Graph and Twitter card images.
  ogImage: 'img/logo-og.png',
  twitterImage: 'img/logo-og.png',

  // You may provide arbitrary config keys to be used as needed by your
  // template. For example, if you need your repo's URL...
  repoUrl: 'https://github.com/facebook/create-react-app',
};

module.exports = siteConfig;
<|endoftext|>


<commit_before>'use strict';
/* global describe, it */
let assert = require('assert');
let Rx = require('rx');
let CycleDOM = require('../../src/cycle-dom');
let mockDOMSource = require('../../src/mock-dom-source');

describe('mockDOMSource', function () {
  it('should be in accessible in the API', function () {
    assert.strictEqual(typeof CycleDOM.mockDOMSource, 'function');
  });

  it('should make an Observable for clicks on `.foo`', function (done) {
    const userEvents = mockDOMSource({
      '.foo': {
        'click': Rx.Observable.just(135)
      }
    });
    userEvents.select('.foo').events('click').subscribe(ev => {
      assert.strictEqual(ev, 135);
      done();
    })
  });

  it('should make multiple user event Observables', function (done) {
    const userEvents = mockDOMSource({
      '.foo': {
        'click': Rx.Observable.just(135)
      },
      '.bar': {
        'scroll': Rx.Observable.just(2)
      }
    });
    Rx.Observable.combineLatest(
      userEvents.select('.foo').events('click'),
      userEvents.select('.bar').events('scroll'),
      (a, b) => a * b
    ).subscribe(ev => {
      assert.strictEqual(ev, 270);
      done();
    })
  });

  it('should make multiple user event Observables on the same selector', function (done) {
    const userEvents = mockDOMSource({
      '.foo': {
        'click': Rx.Observable.just(135),
        'scroll': Rx.Observable.just(3)
      }
    });
    Rx.Observable.combineLatest(
      userEvents.select('.foo').events('click'),
      userEvents.select('.foo').events('scroll'),
      (a, b) => a * b
    ).subscribe(ev => {
      assert.strictEqual(ev, 405);
      done();
    })
  });

  it('should return an empty Observable if query does not match', function (done) {
    const userEvents = mockDOMSource({
      '.foo': {
        'click': Rx.Observable.just(135)
      }
    });
    let subscribeExecuted = false;
    userEvents.select('.impossible').events('scroll').subscribe(ev => {
      subscribeExecuted = true;
    });
    setTimeout(() => {
      assert.strictEqual(subscribeExecuted, false);
      done();
    }, 1000);
  });

  it('should return empty Observable for select().observable', function (done) {
    const userEvents = mockDOMSource({
      '.foo': {
        'click': Rx.Observable.just(135)
      }
    });
    let subscribeExecuted = false;
    userEvents.select('.foo').observable.subscribe(ev => {
      subscribeExecuted = true;
    });
    setTimeout(() => {
      assert.strictEqual(subscribeExecuted, false);
      done();
    }, 1000);
  });
});
<commit_msg>Speed up mockDOMSource tests a bit<commit_after>'use strict';
/* global describe, it */
let assert = require('assert');
let Rx = require('rx');
let CycleDOM = require('../../src/cycle-dom');
let mockDOMSource = require('../../src/mock-dom-source');

describe('mockDOMSource', function () {
  it('should be in accessible in the API', function () {
    assert.strictEqual(typeof CycleDOM.mockDOMSource, 'function');
  });

  it('should make an Observable for clicks on `.foo`', function (done) {
    const userEvents = mockDOMSource({
      '.foo': {
        'click': Rx.Observable.just(135)
      }
    });
    userEvents.select('.foo').events('click').subscribe(ev => {
      assert.strictEqual(ev, 135);
      done();
    })
  });

  it('should make multiple user event Observables', function (done) {
    const userEvents = mockDOMSource({
      '.foo': {
        'click': Rx.Observable.just(135)
      },
      '.bar': {
        'scroll': Rx.Observable.just(2)
      }
    });
    Rx.Observable.combineLatest(
      userEvents.select('.foo').events('click'),
      userEvents.select('.bar').events('scroll'),
      (a, b) => a * b
    ).subscribe(ev => {
      assert.strictEqual(ev, 270);
      done();
    })
  });

  it('should make multiple user event Observables on the same selector', function (done) {
    const userEvents = mockDOMSource({
      '.foo': {
        'click': Rx.Observable.just(135),
        'scroll': Rx.Observable.just(3)
      }
    });
    Rx.Observable.combineLatest(
      userEvents.select('.foo').events('click'),
      userEvents.select('.foo').events('scroll'),
      (a, b) => a * b
    ).subscribe(ev => {
      assert.strictEqual(ev, 405);
      done();
    })
  });

  it('should return an empty Observable if query does not match', function (done) {
    const userEvents = mockDOMSource({
      '.foo': {
        'click': Rx.Observable.just(135)
      }
    });
    let subscribeExecuted = false;
    userEvents.select('.impossible').events('scroll')
      .subscribe(assert.fail, assert.fail, done);
  });

  it('should return empty Observable for select().observable', function (done) {
    const userEvents = mockDOMSource({
      '.foo': {
        'click': Rx.Observable.just(135)
      }
    });
    let subscribeExecuted = false;
    userEvents.select('.foo').observable
      .subscribe(assert.fail, assert.fail, done);
  });
});
<|endoftext|>


<commit_before>define(["dojo/_base/lang", "../../util/oo", "../../manager/_registry", "../TextBlock"],
function(lang, oo, registry, TextBlock){

var Equation = oo.declare(
	// summary:
	//		Essentially the same as the TextBlock tool, but
	//		allows for a different icon and tooltip title.

	dojox.drawing.tools.TextBlock,
	function(options){

	},
	{
		customType:"equation"
	}

);

lang.setObject("dojox.drawing.tools.custom.Equation", Equation);
Equation.setup = {
	// summary:
	//		See stencil._Base ToolsSetup

	name:"dojox.drawing.tools.custom.Equation",
	tooltip:"Equation Tool",
	iconClass:"iconEq"
};
registry.register(Equation.setup, "tool");

return Equation;
});
<commit_msg>stray global variable accidentally left over during AMD conversion<commit_after>define(["dojo/_base/lang", "../../util/oo", "../../manager/_registry", "../TextBlock"],
function(lang, oo, registry, TextBlock){

var Equation = oo.declare(
	// summary:
	//		Essentially the same as the TextBlock tool, but
	//		allows for a different icon and tooltip title.

	TextBlock,
	function(options){

	},
	{
		customType:"equation"
	}

);

lang.setObject("dojox.drawing.tools.custom.Equation", Equation);
Equation.setup = {
	// summary:
	//		See stencil._Base ToolsSetup

	name:"dojox.drawing.tools.custom.Equation",
	tooltip:"Equation Tool",
	iconClass:"iconEq"
};
registry.register(Equation.setup, "tool");

return Equation;
});
<|endoftext|>


<commit_before>/**
 * Created by Luke Hardiman on 20/09/2015.
 *
 */
if (typeof jQuery == 'undefined') {
    throw exception("SSPaypalBasic.js jQuery not loaded");
}
console.log("SSPaypalBasic.js : loaded ");

var Settings = {
    cmd			: "_cart"
    , upload		: "1"
    , currency_code : 'NZD'
    , business		: 'luke@hardiman.co.nz'
    , rm			: 2
    , tax_cart		: 0//(0*1).toFixed(2)
    , handling_cart : 0//(0*1).toFixed(2)
    , charset		: "utf-8"
};

var  $shoppingCart = null;
var cartItems = {};

$(document).ready(function () {

    //declare our shopping cart element
    $shoppingCart = $("#shoppingCart");

    //check cart
    if ($shoppingCart.length < 0)
        throw new Error("paypal.js Error missing shopping cart container");

    if (!$shoppingCart.is(':visible'))
        console.log("paypal.js cart is hidden")

    //adding item to cart
    $(".addToCart").click(addToCart)

    $("#checkOut").click(checkOut);

    processCart();
    checkCart();

});

//bind to non existing class
$(document.body).on('click', '.cartRemove', function () {
    console.log("cartRemove Clicked")
    //get product item
    var item_code = $(this).data('item_code');

    $("#" + item_code).remove();

    delete(cartItems[item_code]);

    console.log("item_code",cartItems);
    saveCart();
    //process the cart 5
    processCart();

});

function addToCart() {
    console.log("addToCart clicked")
    //check if showing
    if (!$shoppingCart.is(':visible'))
        $(".shoppingCartContainer").show();

    //lets get the data
    var data = $(this).data();
    console.log("addToCart data",data)
    var qnty = parseInt($('.sslModuleQty').val());
    data.qty = qnty;

    //check the data
    if (typeof data.name != "string" || typeof data.price != "string") {
        console.log("data", data);
        throw new Error("Error missing data");
    }

    //check if we don't already exist
    if (typeof cartItems[data.code] == "undefined")
    cartItems[data.name] = data;
    else
        cartItems[data.name].qty =  parseInt(cartItems[data.code].qty) + parseInt(data.qty);

    //addRow(data);
    saveCart();
    //close cart
    //window.location.href=window.location.href
    //processCart();
}

/**
 * Process Shopping Cart from Storage
 */
function processCart(){
    $shoppingCart.find(".item-list").remove();
    var items = localStorage.getItem('cartItems');

    if (items != null && typeof items == "string")
        cartItems = JSON.parse(items);
        else
        cartItems = {};

    console.log("Loaded cartItems from storage",cartItems)
    //loop items
    $.map(cartItems,addRow);
}
/**
 * Adds a row to the shopping cart parsed my $.map
 * @param data
 */
function addRow(item,index){

    var item_code = (item.code.length > 1) ? item.code : index;

    var cartRow = '
' +
        '
' + item.name  + '
	' +
        '
' + item.qty + '
	' +
        '
$' + item.price + '
	' +
        '	' +
        '
';
    /*
     @see to checkOut
     */
    //append to last
    $("tr:last", $shoppingCart).after(cartRow);

}

/**
 * Saves cartItems state
 */
function saveCart(){
    localStorage.setItem('cartItems',JSON.stringify(cartItems));
    checkCart();
}
/**
 * Check if cart should be showing.
 */
function checkCart(){

    if ($('tr', $shoppingCart).length <= 1)   //hide shopping cart no items
        $(".shoppingCartContainer").hide();
}

/**
 * CheckOut Creates the form
 */
function checkOut(){
    var $form = $("<form></form>");

    $form.attr('style', 'display:none;');
    $form.attr('action', 'https://sandbox.paypal.com/cgi-bin/webscr');
    $form.attr('method', 'POST');

    var counter = 1;
    $.each(cartItems, function (index, item) {
      //name: "Chargrilled Eggplant Dip", price: "12.00", code: 5, qty: 1}

        console.log("item",item)
        var data = {};

        data["item_name_" + counter] = item.name;

        data["quantity_" + counter] = item.qty;
        data["amount_" + counter] = (item.price * 1).toFixed(2);
        data["item_number_" +counter++] = item.code;

        $form.append(createHiddenInput(data));

       // counter++;
    });
    $form.append(createHiddenInput(Settings));
    console.log("form",$form.html());
    //$shoppingCart.append(form);
    $("body").append($form);
    $form.submit();
}

function createHiddenInput(data){
    var emptyObj = $('
');
    $.each(data,function(item,val){
        console.log("adding item");
        var $input = $('<input>');

        emptyObj.append(
            $input.attr("type","hidden").attr("name",item).val(val)
        );

    });

    return emptyObj;
}
<commit_msg>fixed quanty issue<commit_after>/**
 * Created by Luke Hardiman on 20/09/2015.
 *
 */
if (typeof jQuery == 'undefined') {
    throw exception("SSPaypalBasic.js jQuery not loaded");
}
console.log("SSPaypalBasic.js : loaded ");

var Settings = {
    cmd			: "_cart"
    , upload		: "1"
    , currency_code : 'NZD'
    , business		: 'luke@hardiman.co.nz'
    , rm			: 2
    , tax_cart		: 0//(0*1).toFixed(2)
    , handling_cart : 0//(0*1).toFixed(2)
    , charset		: "utf-8"
};

var  $shoppingCart = null;
var cartItems = {};

$(document).ready(function () {

    //declare our shopping cart element
    $shoppingCart = $("#shoppingCart");

    //check cart
    if ($shoppingCart.length < 0)
        throw new Error("paypal.js Error missing shopping cart container");

    if (!$shoppingCart.is(':visible'))
        console.log("paypal.js cart is hidden")

    //adding item to cart
    $(".addToCart").click(addToCart)

    $("#checkOut").click(checkOut);

    processCart();
    checkCart();

});

//bind to non existing class
$(document.body).on('click', '.cartRemove', function () {
    console.log("cartRemove Clicked")
    //get product item
    var item_code = $(this).data('item_code');
    console.log("item_code",item_code)
    $("#" + item_code).remove();

    delete(cartItems[item_code]);

    console.log("item_code",cartItems);
    saveCart();
    //process the cart 5
    processCart();

});

function addToCart() {
    var $self = $(this);
    console.log("addToCart clicked")
    //check if showing
    if (!$shoppingCart.is(':visible'))
        $(".shoppingCartContainer").show();

    //lets get the data
    var data = $(this).data();
    console.log("addToCart data",data)
    console.log("addToCart this",$self.parent().html())
    var qnty = parseInt($('.sslModuleQty',$self.parent()).val());
    console.log("addToCart qnty",qnty)
    data.qty = qnty;

    //check the data
    if (typeof data.name != "string" || typeof data.price != "string") {
        console.log("data", data);
        throw new Error("Error missing data");
    }

    //check if we don't already exist
    if (typeof cartItems[data.code] == "undefined")
    cartItems[data.name] = data;
    else
        cartItems[data.name].qty =  parseInt(cartItems[data.code].qty) + parseInt(data.qty);

    //addRow(data);
    saveCart();
    //close cart
    //window.location.href=window.location.href
    //processCart();
}

/**
 * Process Shopping Cart from Storage
 */
function processCart(){
    $shoppingCart.find(".item-list").remove();
    var items = localStorage.getItem('cartItems');

    if (items != null && typeof items == "string")
        cartItems = JSON.parse(items);
        else
        cartItems = {};

    console.log("Loaded cartItems from storage",cartItems)
    //loop items
    $.map(cartItems,addRow);
}

/**
 * Adds a row to the shopping cart parsed my $.map
 * @param data
 */
function addRow(item,index){

    var item_code = (item.code.length > 1) ? item.code : index;

    var cartRow = '
' +
        '
' + item.name  + '
	' +
        '
' + item.qty + '
	' +
        '
$' + item.price + '
	' +
        '	' +
        '
';
    /*
     @see to checkOut
     */
    //append to last
    $("tr:last", $shoppingCart).after(cartRow);

}

/**
 * Saves cartItems state
 */
function saveCart(){
    localStorage.setItem('cartItems',JSON.stringify(cartItems));
    checkCart();
}
/**
 * Check if cart should be showing.
 */
function checkCart(){

    if ($('tr', $shoppingCart).length <= 1)   //hide shopping cart no items
        $(".shoppingCartContainer").hide();
}

/**
 * CheckOut Creates the form
 */
function checkOut(){
    var $form = $("<form></form>");

    $form.attr('style', 'display:none;');
    $form.attr('action', 'https://sandbox.paypal.com/cgi-bin/webscr');
    $form.attr('method', 'POST');

    var counter = 1;
    $.each(cartItems, function (index, item) {
      //name: "Chargrilled Eggplant Dip", price: "12.00", code: 5, qty: 1}

        console.log("item",item)
        var data = {};

        data["item_name_" + counter] = item.name;

        data["quantity_" + counter] = item.qty;
        data["amount_" + counter] = (item.price * 1).toFixed(2);
        data["item_number_" +counter++] = item.code;

        $form.append(createHiddenInput(data));

       // counter++;
    });
    $form.append(createHiddenInput(Settings));
    console.log("form",$form.html());
    //$shoppingCart.append(form);
    $("body").append($form);
    $form.submit();
}

function createHiddenInput(data){
    var emptyObj = $('
');
    $.each(data,function(item,val){
        console.log("adding item");
        var $input = $('<input>');

        emptyObj.append(
            $input.attr("type","hidden").attr("name",item).val(val)
        );

    });

    return emptyObj;
}
<|endoftext|>


<commit_before>/**
 * Counter used for uniquely identifying each Google map
 * @type {Number}
 */
var mappableMapCtr = 0;

/**
 * Create a google map pin from data provided
 * @param  {GoogleMap} map             Instance of a google map
 * @param  {Number} lat                Latitude of pin
 * @param  {Number} lng                Longitude of pin
 * @param  {String} html               HTML for information window
 * @param  {String} icon               URL of alternative map icon, or blank for default
 * @param  {boolean} useClusterer      Whether or not to use clusterer
 * @param  {boolean} enableWindowZoom  Whether or not to enable zoom on the rendered map
 * @param  {boolean} defaultHideMarker Whether or not to hide markers initially
 * @return {MapMarker}                 Google map pin object
 */
function createMarker(map, lat, lng, html, category, icon, useClusterer, enableWindowZoom,
	defaultHideMarker) {

	mapId = map.getDiv().getAttribute('id');

	var marker = new google.maps.Marker();
	marker.setPosition(new google.maps.LatLng(lat, lng));
	marker.mycategory = category;

	if (icon && icon !== '') {
		var image = new google.maps.MarkerImage(icon);
		marker.setIcon(image);
	}

	if (!useClusterer) {
		marker.setMap(map);
	}

	google.maps.event.addListener(marker, "click", function() {
		if (enableWindowZoom) {
			map.setCenter(new google.maps.LatLng(lat, lng), 12); // $InfoWindowZoom);
		}
		var infoWindow = infoWindows[mapId];
		infoWindow.setContent(html);
		infoWindow.open(map, this);
	});

	gmarkers[mapId].push(marker);

	if (defaultHideMarker) {
		marker.hide();
	}
	return marker;
}

/**
 * Get the current latitude
 * @return {float} Current latitude
 */
function getCurrentLat() {
	return current_lat;
}

/**
 * Get the current longitude
 * @return {float} The current longitude
 */
function getCurrentLng() {
	return current_lng;
}

/**
 * Convert JSON point data into google map markers
 * @param {GoogleMap} map             Google Map instance
 * @param {array} markers            point data loaded from JSON
 * @param {boolean} useClusterer      Whether or not to use the clusterer
 * @param {boolean} enableWindowZoom  Whether or not zoom is enabled
 * @param {boolean} defaultHideMarker Whether or not to hide markers
 * * @return array of Google map markers converted from the JSON data
 */
function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHideMarker) {
	var allmarkers = [];
	for (var i = 0; i < markers.length; i++) {
		var markerinfo = markers[i];
		var marker = createMarker(map, markerinfo.latitude, markerinfo.longitude, markerinfo.html,
			markerinfo.category, markerinfo.icon, useClusterer, enableWindowZoom,
			defaultHideMarker);
		allmarkers.push(marker);
	}
	return allmarkers;
}

/**
 * Add lines to a Google map
 * @param {googleMap} map   Google map instance
 * @param {array} lines Line data loaded from json, lat1,lon1 to lat2,lon2
 */
function addLines(map, lines) {
	for (i = 0; i < lines.length; i++) {
		var line = lines[i];
		var point1 = new google.maps.LatLng(line.lat1, line.lon1);
		var point2 = new google.maps.LatLng(line.lat2, line.lon2);
		var points = [point1, point2];
		var pl = new google.maps.Polyline({
			path: points,
			strokeColor: line.color,
			strokeWeight: 4,
			strokeOpacity: 0.8
		});
		pl.setMap(map);
	}
}

/**
 * Add one or more (max of 25) KML files to a Google map
 * @param {GoogleMap} map      A Google Map instance
 * @param {array} kmlFiles array of URLs for KML files
 */
function addKmlFiles(map, kmlFiles) {
	for (var i = 0; i < kmlFiles.length; i++) {
		var kmlFile = kmlFiles[i];
		var kmlLayer = new google.maps.KmlLayer(kmlFile, {
			suppressInfoWindows: true,
			map: map
		});

	}
}

function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLat, maxLng, mapType,
	markers, lines, kmlFiles, jsonMapStyles, enableAutomaticCenterZoom, useClusterer,
	allowFullScreen) {
	var newMap = [];
	newMap.googleMapID = googleMapID;
	newMap.zoom = zoom;
	newMap.centreCoordinates = centreCoordinates;
	newMap.minLat = minLat;
	newMap.minLng = minLng;
	newMap.maxLng = maxLng;
	newMap.maxLat = maxLat;
	newMap.markers = markers;
	newMap.googleMapID = googleMapID;
	newMap.mapType = mapType;
	newMap.lines = lines;
	newMap.kmlFiles = kmlFiles;
	newMap.jsonMapStyles = jsonMapStyles;
	newMap.enableAutomaticCenterZoom = enableAutomaticCenterZoom;
	newMap.useClusterer = useClusterer;
	newMap.allowFullScreen = allowFullScreen;
	mappableMaps[googleMapID] = newMap;

	// increment map counter
	mappableMapCtr++;

	// initialise gmarkers array for this map
	gmarkers[googleMapID] = [];
	var infoWindow = new google.maps.InfoWindow({
		content: 'test',
		maxWidth: 400
	});
	infoWindows[googleMapID] = infoWindow;

	mapLayers[googleMapID] = kmlFiles;
	mapLines[googleMapID] = lines;
}

/**
 * Callback function after the Google Maps API has been loaded - renders the maps along with
 * associated points of interest and layers
 */
function loadedGoogleMapsAPI() {
	for (var i = 1; i <= mappableMapCtr; i++) {
		var map_info = mappableMaps['google_map_' + i];
		var map = new google.maps.Map(document.getElementById(map_info.googleMapID));

		// initialise geocoder
		geocoder = new google.maps.Geocoder();

		// TODO
		// if (map_info.jsonMapStyles) {
		//		map.setOptions({styles: map_info.jsonMapStyles});
		//	};

		if (map_info.allowFullScreen) {
			map.controls[google.maps.ControlPosition.TOP_RIGHT].push(
				FullScreenControl(map, "Full Screen", "Original Size")
			);
		}
		if (map_info.enableAutomaticCenterZoom) {
			centre = map_info.centreCoordinates;
			map.setCenter(new google.maps.LatLng(centre.lat, centre.lng));

			var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map_info.minLat, map_info.minLng),
				new google.maps.LatLng(map_info.maxLat, map_info.maxLng));
			map.fitBounds(bds);
			map.setZoom(map_info.zoom);
		} else {
			var centre = map_info.centreCoordinates;
			map.setCenter(new google.maps.LatLng(centre.lat, centre.lng));
			map.setZoom(map_info.zoom);
		}

		if (map_info.mapType) {
			map.setMapTypeId(map_info.mapType);
		} else {
			map.setMapTypeId(google.maps.MapTypeId.ROADMAP);
		}

		var markers = addAllMarkers(map, map_info.markers, map_info.useClusterer,
			map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker);

		if (map_info.useClusterer) {
			var mcOptions = {gridSize: 50, maxZoom: 17};
			var markerCluster = new MarkerClusterer(map,markers,mcOptions);
		}

		addLines(map, map_info.lines);
		addKmlFiles(map, map_info.kmlFiles);
	}
}
<commit_msg>ENHANCEMENT: Calculate min/max bounds of markers using JavaScript instead of PHP<commit_after>/**
 * Counter used for uniquely identifying each Google map
 * @type {Number}
 */
var mappableMapCtr = 0;

/**
 * Create a google map pin from data provided
 * @param  {GoogleMap} map             Instance of a google map
 * @param  {Number} lat                Latitude of pin
 * @param  {Number} lng                Longitude of pin
 * @param  {String} html               HTML for information window
 * @param  {String} icon               URL of alternative map icon, or blank for default
 * @param  {boolean} useClusterer      Whether or not to use clusterer
 * @param  {boolean} enableWindowZoom  Whether or not to enable zoom on the rendered map
 * @param  {boolean} defaultHideMarker Whether or not to hide markers initially
 * @return {MapMarker}                 Google map pin object
 */
function createMarker(map, lat, lng, html, category, icon, useClusterer, enableWindowZoom,
	defaultHideMarker) {

	mapId = map.getDiv().getAttribute('id');

	var marker = new google.maps.Marker();
	marker.setPosition(new google.maps.LatLng(lat, lng));
	marker.mycategory = category;

	if (icon && icon !== '') {
		var image = new google.maps.MarkerImage(icon);
		marker.setIcon(image);
	}

	if (!useClusterer) {
		marker.setMap(map);
	}

	google.maps.event.addListener(marker, "click", function() {
		if (enableWindowZoom) {
			map.setCenter(new google.maps.LatLng(lat, lng), 12); // $InfoWindowZoom);
		}
		var infoWindow = infoWindows[mapId];
		infoWindow.setContent(html);
		infoWindow.open(map, this);
	});

	gmarkers[mapId].push(marker);

	if (defaultHideMarker) {
		marker.hide();
	}
	return marker;
}

/**
 * Get the current latitude
 * @return {float} Current latitude
 */
function getCurrentLat() {
	return current_lat;
}

/**
 * Get the current longitude
 * @return {float} The current longitude
 */
function getCurrentLng() {
	return current_lng;
}

/**
 * Convert JSON point data into google map markers
 * @param {GoogleMap} map             Google Map instance
 * @param {array} markers            point data loaded from JSON
 * @param {boolean} useClusterer      Whether or not to use the clusterer
 * @param {boolean} enableWindowZoom  Whether or not zoom is enabled
 * @param {boolean} defaultHideMarker Whether or not to hide markers
 * * @return array of Google map markers converted from the JSON data
 */
function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHideMarker) {

	// these will be altered by adding markers
	map.minLat = 1000000;
	map.minLng = 1000000;
	map.maxLat = -1000000;
	map.maxLng = -1000000;

	var allmarkers = [];
	for (var i = 0; i < markers.length; i++) {
		var markerinfo = markers[i];
		var marker = createMarker(map, markerinfo.latitude, markerinfo.longitude, markerinfo.html,
			markerinfo.category, markerinfo.icon, useClusterer, enableWindowZoom,
			defaultHideMarker);

		var latitude = parseFloat(markerinfo.latitude);
		var longitude = parseFloat(markerinfo.longitude);

		// update lat,lon min to lat,lon max
		if (latitude < map.minLat) {
			map.minLat = latitude;

		}
		if (latitude > map.maxLat) {
			map.maxLat = latitude;
		}

		if (longitude > map.maxLng) {
			map.maxLng = longitude;

		}

		if (longitude < map.minLng) {
			map.minLng = longitude;
		}

		allmarkers.push(marker);
	}

	var centreCoordinates = [];
	centreCoordinates.lat = (parseFloat(map.minLat)+parseFloat(map.maxLat))/2;;
	centreCoordinates.lng = (parseFloat(map.minLng)+parseFloat(map.maxLng))/2;
	map.centreCoordinates = centreCoordinates;
	return allmarkers;
}

/**
 * Add lines to a Google map
 * @param {googleMap} map   Google map instance
 * @param {array} lines Line data loaded from json, lat1,lon1 to lat2,lon2
 */
function addLines(map, lines) {
	for (i = 0; i < lines.length; i++) {
		var line = lines[i];
		var point1 = new google.maps.LatLng(line.lat1, line.lon1);
		var point2 = new google.maps.LatLng(line.lat2, line.lon2);
		var points = [point1, point2];
		var pl = new google.maps.Polyline({
			path: points,
			strokeColor: line.color,
			strokeWeight: 4,
			strokeOpacity: 0.8
		});
		pl.setMap(map);
	}
}

/**
 * Add one or more (max of 25) KML files to a Google map
 * @param {GoogleMap} map      A Google Map instance
 * @param {array} kmlFiles array of URLs for KML files
 */
function addKmlFiles(map, kmlFiles) {
	for (var i = 0; i < kmlFiles.length; i++) {
		var kmlFile = kmlFiles[i];
		var kmlLayer = new google.maps.KmlLayer(kmlFile, {
			suppressInfoWindows: true,
			map: map
		});

	}
}

function registerMap(googleMapID, centreCoordinates, zoom, mapType,
	markers, lines, kmlFiles, jsonMapStyles, enableAutomaticCenterZoom, useClusterer,
	allowFullScreen) {
	var newMap = [];
	newMap.googleMapID = googleMapID;
	newMap.zoom = zoom;
	newMap.centreCoordinates = centreCoordinates;

	newMap.markers = markers;
	newMap.googleMapID = googleMapID;
	newMap.mapType = mapType;
	newMap.lines = lines;
	newMap.kmlFiles = kmlFiles;
	newMap.jsonMapStyles = jsonMapStyles;
	newMap.enableAutomaticCenterZoom = enableAutomaticCenterZoom;
	newMap.useClusterer = useClusterer;
	newMap.allowFullScreen = allowFullScreen;
	mappableMaps[googleMapID] = newMap;

	// increment map counter
	mappableMapCtr++;

	// initialise gmarkers array for this map
	gmarkers[googleMapID] = [];
	var infoWindow = new google.maps.InfoWindow({
		content: 'test',
		maxWidth: 400
	});
	infoWindows[googleMapID] = infoWindow;

	mapLayers[googleMapID] = kmlFiles;
	mapLines[googleMapID] = lines;
}

/**
 * Callback function after the Google Maps API has been loaded - renders the maps along with
 * associated points of interest and layers
 */
function loadedGoogleMapsAPI() {
	for (var i = 1; i <= mappableMapCtr; i++) {
		var map_info = mappableMaps['google_map_' + i];
		var map = new google.maps.Map(document.getElementById(map_info.googleMapID));

		// initialise geocoder
		geocoder = new google.maps.Geocoder();

		// TODO
		// if (map_info.jsonMapStyles) {
		//		map.setOptions({styles: map_info.jsonMapStyles});
		//	};

		if (map_info.allowFullScreen) {
			map.controls[google.maps.ControlPosition.TOP_RIGHT].push(
				FullScreenControl(map, "Full Screen", "Original Size")
			);
		}

		var markers = addAllMarkers(map, map_info.markers, map_info.useClusterer,
			map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker);

		if (map_info.enableAutomaticCenterZoom) {
			centre = map.centreCoordinates;
			map.setCenter(new google.maps.LatLng(centre.lat, centre.lng));

			map_info.minLat = map.minLat;
			map_info.maxLat = map.maxLat;
			map_info.minLng = map.minLng;
			map_info.maxLng = map.maxLng;

			var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map_info.minLat, map_info.minLng),
				new google.maps.LatLng(map_info.maxLat, map_info.maxLng));
			map.fitBounds(bds);
		} else {
			var centre = map_info.centreCoordinates;
			map.setCenter(new google.maps.LatLng(centre.lat, centre.lng));
			map.setZoom(map_info.zoom);
		}

		if (map_info.mapType) {
			map.setMapTypeId(map_info.mapType);
		} else {
			map.setMapTypeId(google.maps.MapTypeId.ROADMAP);
		}

		if (map_info.useClusterer) {
			var mcOptions = {gridSize: 50, maxZoom: 17};
			var markerCluster = new MarkerClusterer(map,markers,mcOptions);
		}

		addLines(map, map_info.lines);
		addKmlFiles(map, map_info.kmlFiles);
	}
}
<|endoftext|>