<commit_before>/*
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint anon:true, sloppy:true, nomen:true, regexp:true, stupid:true,
  continue: true*/

var libpath = require('path'),
    utils = require(libpath.join(__dirname, '../../management/utils')),
    Mojito = require(libpath.join(__dirname, '../../mojito')),
    fs = require('fs'),
    libqs = require('querystring'),
    MODE_755 = parseInt('755', 8),
    getSpecURL,
    mkdirP,
    rmdirR,
    writeWebPagesToFiles,
    YUI = require('yui').YUI,
    Y = YUI({useSync: true}).use('json-parse', 'json-stringify', 'escape');

Y.applyConfig({useSync: false});

/**
 * The usage string for this command.
 */
exports.usage = 'mojito build {type} [destination]\n' +
    "\t- type: 'html5app'\n" +
    '\t- destination: (optional) the directory where the build output goes.\n' +
    "\t  By default this is the type i.e. './artifacts/builds/<type>'\n" +
    '\nOPTIONS: \n' +
    '\t --replace: Tells the build system to delete the destination' +
    ' directory and replace it.\n' +
    '\t        -r: Short for --replace\n' +
    '\t --context: Tells the build system what context to build with' +
    ' i.e. device=iphone&lang=en-GB.\n' +
    '\t        -c: Short for --context\n';

/**
 * The options list for this command.
 */
exports.options = [
    {
        shortName: 'r',
        longName: 'replace',
        hasValue: false
    },
    {
        shortName: 'm',
        longName: 'mojit',
        hasValue: true
    },
    {
        shortName: 'c',
        longName: 'context',
        hasValue: true
    }
];

/**
 * The run() method which handles processing for this command.
 * @param {Array} params Optional parameters to the command.
 * @param {Object} options Optional options for the command.
 * @param {Function} callback Function to invoke on commmand completion.
 */
exports.run = function(params, options, callback) {
    var store,
        type = 'html5app',
        cwd = process.cwd(),
        destination,
        appConfig,
        config = {},
        context = {};

    Y.applyConfig({
        useSync: true,
        modules: {
            'mojito-util': {
                fullpath: libpath.join(__dirname, '../../app/autoload/util.common.js')
            },
            'mojito-resource-store': {
                fullpath: libpath.join(__dirname, '../../store.server.js')
            }
        }
    });

    if (typeof options.context === 'string') {
        // Parse the context into an object
        context = utils.contextCsvToObject(options.context);
    }

    Y.use('mojito-util', 'mojito-resource-store');
    Y.applyConfig({useSync: false});
    store = new Y.mojito.ResourceStore({
        root: cwd,
        context: context
    });

    if (!params[0]) {
        params[0] = '';
    }

    switch (params[0].toUpperCase()) {
    case 'HTML5APP':
        type = 'html5app';
        break;
    default:
        utils.error("Can only build type '" + type + "'", exports.usage);
        return;
    }

    // Set the destination for the generated files
    if (params[1] && params[1][0] === '/') {
        destination = libpath.join(params[1]);
    } else if (params[1]) {
        destination = libpath.join(cwd, params[1]);
    } else {
        destination = libpath.join(cwd, 'artifacts/builds', type);
    }

    // Are we in a Mojito App?
    utils.isMojitoApp(cwd, exports.usage, true);

    appConfig = store.getStaticAppConfig();
    // Is there a "builds" section for this "type" in the appConfig
    if (appConfig.builds && appConfig.builds[type]) {
        config = appConfig.builds[type];
    }

    if (options.replace) {
        try {
            rmdirR(destination);
            console.log('Removing all files in folder "' + destination + '"');
        } catch (err2) {    // err2 to get past JSLint
            // Dirty way to check dir
        }
    }

    exports['build' + type](options, store, config, destination, callback);
};

/**
 * The build command itself.
 * @param {Object} cmdOptions Options for the command.
 * @param {Object} store The relevant resource store reference.
 * @param {Object} config Configuration data for the context.
 * @param {string} destination A path to target with build output.
 * @param {Function} callback A function to invoke on completion.
 */
exports.buildhtml5app = function(cmdOptions, store, config, destination,
    callback) {

    var type = 'HTML5 Application',
        i,
        from,
        to,
        // files to fetch through the server
        serverFiles = { css: true, js: true, json: true },
        extension,
        manifest = 'CACHE MANIFEST\n',
        indexJs = "module.exports = require('express')." +
            "createServer(require('express')['static'](__dirname));",
        url,
        storeURLs,
        urls = {}, // from: to
        app,
        context = '',
        contextObj = libqs.parse(cmdOptions.context),
        appConfig,
        tunnelPrefix,
        dynamicURLs = {},
        mr,
        mojitRes,
        mojitRess,
        sr,
        specRes,
        specRess,
        id;

    if (typeof cmdOptions.context === 'string') {
        // Parse the context into an object
        config.context = utils.contextCsvToObject(cmdOptions.context);
        // Stringify the context object into query string
        context = '?' + libqs.stringify(config.context);
    }

    urls['/' + context] = '/index.html';

    if (config.urls && config.urls.length) {
        for (i = 0; i < config.urls.length; i += 1) {
            urls[config.urls[i] + context] = config.urls[i];
        }
    }

    store.preload();

    appConfig = store.getAppConfig(contextObj);
    tunnelPrefix = appConfig.tunnelPrefix || '/tunnel';

    console.log('Building a "' + type + '" of the Mojito application at "' +
        store._config.root + '"');

    console.log('...');

    // Set the cachable files in the manifest
    manifest += 'CACHE:\n';

    // Copy all the files into the destination directory
    storeURLs = store.getAllURLs();
    for (url in storeURLs) {
        if (storeURLs.hasOwnProperty(url)) {
            from = storeURLs[url];  // filesystem path
            to = libpath.join(destination, url);
            extension = from.split('.').pop();

            mkdirP(libpath.dirname(to), MODE_755);

            manifest += url + '\n';

            if (serverFiles[extension]) {
                urls[url + context] = url;
                mkdirP(libpath.dirname(libpath.join(destination, url)),
                    MODE_755);
            } else {
                fs.writeFileSync(to, fs.readFileSync(from), 'utf8');
            }
        }
    }

    mojitRess = store.getResources('client', contextObj, {type: 'mojit'});
    for (mr = 0; mr < mojitRess.length; mr += 1) {
        mojitRes = mojitRess[mr];
        if (!mojitRes.url) {
            continue;
        }
        url = mojitRes.url + '/definition.json';
        dynamicURLs[url] = true;

        specRess = store.getResources('client', contextObj, {type: 'spec', mojit: mojitRes.name});
        for (sr = 0; sr < specRess.length; sr += 1) {
            specRes = specRess[sr];
            dynamicURLs[specRes.url] = true;
        }
    }

    for (id in appConfig.specs) {
        if (appConfig.specs.hasOwnProperty(id)) {
            url = getSpecURL(appConfig, id);
            dynamicURLs[url] = true;
        }
    }

    // Get all the dynamic URLs we have to call via the "tunnel"
    for (url in dynamicURLs) {
        if (dynamicURLs.hasOwnProperty(url)) {
            urls[tunnelPrefix + url + context] = url;
            mkdirP(libpath.dirname(libpath.join(destination, url)), MODE_755);
        }
    }

    for (url in urls) {
        if (urls.hasOwnProperty(url)) {
            manifest += (urls[url] === '/' ? '/index.html' : urls[url]) + '\n';
        }
    }

    // Write the "cache.manifest" file
    fs.writeFileSync(libpath.join(destination, 'cache.manifest'), manifest,
        'utf8');

    // Write a "quick test" index.js file
    fs.writeFileSync(libpath.join(destination, 'index.js'), indexJs, 'utf8');

    // Now use the server to generate some of the files
    writeWebPagesToFiles(type, store, destination, urls, config, callback);
};

getSpecURL = function(appConfig, id) {
    var prefix = '/static',
        parts = id.split(':'),
        typeName = parts[0],
        specName = parts[1] || 'default',
        ns = typeName.replace(/\./g, '_'),
        url;

    if (appConfig && appConfig.staticHandling &&
            appConfig.staticHandling.hasOwnProperty('prefix')) {
        prefix = (appConfig.staticHandling.prefix ? '/' +
            appConfig.staticHandling.prefix : '');
    }
    url = prefix + '/' + typeName + '/specs/' + specName + '.json';
    return url;
};

mkdirP = function(p, mode) {
    var ps = libpath.normalize(p).split('/'),
        i;

    for (i = 0; i <= ps.length; i += 1) {
        try {
            fs.mkdirSync(ps.slice(0, i).join('/'), mode);
        } catch (err) {
            // Dirty way to check dir
        }
    }
};

rmdirR = function(path) {
    var files = fs.readdirSync(path),
        currDir = path,
        i,
        currFile;

    /* Loop through and delete everything in the sub-tree after checking it */
    for (i = 0; i < files.length; i += 1) {
        currFile = fs.statSync(currDir + '/' + files[i]);

        if (currFile.isDirectory()) {
            // Recursive function back to the beginning
            rmdirR(currDir + '/' + files[i]);
        } else if (currFile.isSymbolicLink()) {
            // Unlink symlinks
            fs.unlinkSync(currDir + '/' + files[i]);
        } else {
            // Assume it's a file - perhaps a try/catch belongs here?
            fs.unlinkSync(currDir + '/' + files[i]);
        }
    }

    /*
     * Now that we know everything in the sub-tree has been deleted,
     * we can delete the main directory.
     */
    return fs.rmdirSync(path);
};

/**
 * Calculates the filesystem path to get from source to destination.
 *
 * @private
 * @param {string} dst The destination directory.
 * @param {string} src The source directory.
 * @return {string} The path from source to destination.
 */
function pathTo(dst, src) {
    var dstParts,
        srcParts,
        i,
        j,
        path = [];

    // trimming leading and trailing slashes removes nasty corner cases from the
    // algorithm
    if ('/' === dst.charAt(0)) {
        dst = dst.substring(1);
    }
    if ('/' === dst.charAt(dst.length - 1)) {
        dst = dst.substring(0, dst.length - 1);
    }
    if ('/' === src.charAt(0)) {
        src = src.substring(1);
    }
    if ('/' === src.charAt(src.length - 1)) {
        src = src.substring(0, src.length - 1);
    }

    dstParts = dst ? dst.split('/') : [];
    srcParts = src ? src.split('/') : [];
    path = [];
    for (i = 0; i < dstParts.length && i < srcParts.length; i += 1) {
        if (dstParts[i] !== srcParts[i]) {
            break;
        }
    }
    for (j = i; j < srcParts.length; j += 1) {
        path.push('..');
    }
    for (j = i; j < dstParts.length; j += 1) {
        path.push(dstParts[j]);
    }
    return libpath.join.apply(libpath, path) || '.';
}

/**
 * Adds the HTML5 cache manifest to the <html>
 *
 * @private
 * @param {string} root The directory where html5app is being built into.
 * @param {string} relativePath The file path, relative to root.
 * @param {string} content The body of the file.
 * @param {Boolean} force Whether to fixup the content, even though the path
 *     might not end in .html.
 * @return {string} The fixed up content.
 */
function attachManifest(root, relativePath, content, force) {
    var extname = libpath.extname(relativePath),
        dirname,
        pathToRoot;

    if (force || '.html' === extname) {
        dirname = libpath.dirname(relativePath);
        pathToRoot = pathTo('/', dirname);

        content = content.replace(/<html>/, '<html manifest="' + pathToRoot +
            '/cache.manifest">');
    }

    return content;
}

/**
 * Changes server-relative paths to file-relative paths.
 *
 * @private
 * @param {string} root The directory where html5app is being built into.
 * @param {string} relativePath The file path, relative to root.
 * @param {string} content The body of the file.
 * @param {Boolean} force Whether to fixup the content, even though the path
 *     might not end in .html.
 * @return {string} The fixed up content.
 */
function forceRelativePaths(root, relativePath, content, force) {
    var extname = libpath.extname(relativePath),
        dirname;

    if (force || '.html' === extname) {
        dirname = libpath.dirname(relativePath);

        content = content.replace(/(src|href)="([^"]+)"/g,
            function(all, name, val) {
                // FUTURE:  once the "/" aren't escaped, we can do this easier
                var fixed = utils.decodeHTMLEntities(val);
                if ('/' === fixed.charAt(0)) {
                    fixed = libpath.join(pathTo(libpath.dirname(fixed), dirname),
                        libpath.basename(fixed));
                }
                fixed = Y.Escape.html(fixed);
                return name + '="' + fixed + '"';
            });
    }

    return content;
}

writeWebPagesToFiles = function(type, store, destination, urls, config,
        callback) {
    var options,
        userPages = {},
        i,
        app;

    if (config.urls) {
        // If the user is using builds.html5app.attachManifest or
        // .forceRelativePaths, we explicitly want to fix those URLs which are
        // pages (the ones they've specified).
        for (i = 0; i < config.urls.length; i += 1) {
            userPages[config.urls[i]] = true;
        }
    }

    options = {
        port: 11111,
        context: config.context
    };

    app = Mojito.createServer(options);

    app.start(function(err) {
        var got = 0,
            need;

        if (err) {
            utils.error(err);
            return;
        }
        need = Object.keys(urls).length;

        Object.keys(urls).forEach(function(u) {
            var opts = {
                    headers: {
                        'x-mojito-build': 'html5app',
                        'x-mojito-build-path-to-root': pathTo('/',
                            libpath.dirname(urls[u]))
                    }
                };
            app.getWebPage(u, opts, function(err, url, content) {
                var dest;

                got += 1;

                if (err) {
                    utils.error('FAILED to get ' + url + ' with error:\n' +
                                err, null, true);
                } else {
                    if (config.attachManifest) {
                        content = attachManifest(destination, urls[url],
                            content,
                            userPages[url]);
                    }
                    if (config.forceRelativePaths) {
                        content = forceRelativePaths(destination, urls[url],
                            content,
                            userPages[url]
                            );
                    }
                    dest = libpath.join(destination, urls[url]);
                    mkdirP(libpath.dirname(dest), MODE_755);
                    fs.writeFileSync(dest, content, 'utf8');
                }

                if (got === need) {
                    app.close();
                    callback();
                }
            });
        });
    });
};
<commit_msg>rm usused params<commit_after>/*
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint anon:true, sloppy:true, nomen:true, regexp:true, stupid:true,
  continue: true*/

var libpath = require('path'),
    utils = require(libpath.join(__dirname, '../../management/utils')),
    Mojito = require(libpath.join(__dirname, '../../mojito')),
    fs = require('fs'),
    libqs = require('querystring'),
    MODE_755 = parseInt('755', 8),
    getSpecURL,
    mkdirP,
    rmdirR,
    writeWebPagesToFiles,
    YUI = require('yui').YUI,
    Y = YUI({useSync: true}).use('json-parse', 'json-stringify', 'escape');

Y.applyConfig({useSync: false});

/**
 * The usage string for this command.
 */
exports.usage = 'mojito build {type} [destination]\n' +
    "\t- type: 'html5app'\n" +
    '\t- destination: (optional) the directory where the build output goes.\n' +
    "\t  By default this is the type i.e. './artifacts/builds/<type>'\n" +
    '\nOPTIONS: \n' +
    '\t --replace: Tells the build system to delete the destination' +
    ' directory and replace it.\n' +
    '\t        -r: Short for --replace\n' +
    '\t --context: Tells the build system what context to build with' +
    ' i.e. device=iphone&lang=en-GB.\n' +
    '\t        -c: Short for --context\n';

/**
 * The options list for this command.
 */
exports.options = [
    {
        shortName: 'r',
        longName: 'replace',
        hasValue: false
    },
    {
        shortName: 'm',
        longName: 'mojit',
        hasValue: true
    },
    {
        shortName: 'c',
        longName: 'context',
        hasValue: true
    }
];

/**
 * The run() method which handles processing for this command.
 * @param {Array} params Optional parameters to the command.
 * @param {Object} options Optional options for the command.
 * @param {Function} callback Function to invoke on commmand completion.
 */
exports.run = function(params, options, callback) {
    var store,
        type = 'html5app',
        cwd = process.cwd(),
        destination,
        appConfig,
        config = {},
        context = {};

    Y.applyConfig({
        useSync: true,
        modules: {
            'mojito-util': {
                fullpath: libpath.join(__dirname, '../../app/autoload/util.common.js')
            },
            'mojito-resource-store': {
                fullpath: libpath.join(__dirname, '../../store.server.js')
            }
        }
    });

    if (typeof options.context === 'string') {
        // Parse the context into an object
        context = utils.contextCsvToObject(options.context);
    }

    Y.use('mojito-util', 'mojito-resource-store');
    Y.applyConfig({useSync: false});
    store = new Y.mojito.ResourceStore({
        root: cwd,
        context: context
    });

    if (!params[0]) {
        params[0] = '';
    }

    switch (params[0].toUpperCase()) {
    case 'HTML5APP':
        type = 'html5app';
        break;
    default:
        utils.error("Can only build type '" + type + "'", exports.usage);
        return;
    }

    // Set the destination for the generated files
    if (params[1] && params[1][0] === '/') {
        destination = libpath.join(params[1]);
    } else if (params[1]) {
        destination = libpath.join(cwd, params[1]);
    } else {
        destination = libpath.join(cwd, 'artifacts/builds', type);
    }

    // Are we in a Mojito App?
    utils.isMojitoApp(cwd, exports.usage, true);

    appConfig = store.getStaticAppConfig();
    // Is there a "builds" section for this "type" in the appConfig
    if (appConfig.builds && appConfig.builds[type]) {
        config = appConfig.builds[type];
    }

    if (options.replace) {
        try {
            rmdirR(destination);
            console.log('Removing all files in folder "' + destination + '"');
        } catch (err2) {    // err2 to get past JSLint
            // Dirty way to check dir
        }
    }

    exports['build' + type](options, store, config, destination, callback);
};

/**
 * The build command itself.
 * @param {Object} cmdOptions Options for the command.
 * @param {Object} store The relevant resource store reference.
 * @param {Object} config Configuration data for the context.
 * @param {string} destination A path to target with build output.
 * @param {Function} callback A function to invoke on completion.
 */
exports.buildhtml5app = function(cmdOptions, store, config, destination,
    callback) {

    var type = 'HTML5 Application',
        i,
        from,
        to,
        // files to fetch through the server
        serverFiles = { css: true, js: true, json: true },
        extension,
        manifest = 'CACHE MANIFEST\n',
        indexJs = "module.exports = require('express')." +
            "createServer(require('express')['static'](__dirname));",
        url,
        storeURLs,
        urls = {}, // from: to
        app,
        context = '',
        contextObj = libqs.parse(cmdOptions.context),
        appConfig,
        tunnelPrefix,
        dynamicURLs = {},
        mr,
        mojitRes,
        mojitRess,
        sr,
        specRes,
        specRess,
        id;

    if (typeof cmdOptions.context === 'string') {
        // Parse the context into an object
        config.context = utils.contextCsvToObject(cmdOptions.context);
        // Stringify the context object into query string
        context = '?' + libqs.stringify(config.context);
    }

    urls['/' + context] = '/index.html';

    if (config.urls && config.urls.length) {
        for (i = 0; i < config.urls.length; i += 1) {
            urls[config.urls[i] + context] = config.urls[i];
        }
    }

    store.preload();

    appConfig = store.getAppConfig(contextObj);
    tunnelPrefix = appConfig.tunnelPrefix || '/tunnel';

    console.log('Building a "' + type + '" of the Mojito application at "' +
        store._config.root + '"');

    console.log('...');

    // Set the cachable files in the manifest
    manifest += 'CACHE:\n';

    // Copy all the files into the destination directory
    storeURLs = store.getAllURLs();
    for (url in storeURLs) {
        if (storeURLs.hasOwnProperty(url)) {
            from = storeURLs[url];  // filesystem path
            to = libpath.join(destination, url);
            extension = from.split('.').pop();

            mkdirP(libpath.dirname(to), MODE_755);

            manifest += url + '\n';

            if (serverFiles[extension]) {
                urls[url + context] = url;
                mkdirP(libpath.dirname(libpath.join(destination, url)),
                    MODE_755);
            } else {
                fs.writeFileSync(to, fs.readFileSync(from), 'utf8');
            }
        }
    }

    mojitRess = store.getResources('client', contextObj, {type: 'mojit'});
    for (mr = 0; mr < mojitRess.length; mr += 1) {
        mojitRes = mojitRess[mr];
        if (!mojitRes.url) {
            continue;
        }
        url = mojitRes.url + '/definition.json';
        dynamicURLs[url] = true;

        specRess = store.getResources('client', contextObj, {type: 'spec', mojit: mojitRes.name});
        for (sr = 0; sr < specRess.length; sr += 1) {
            specRes = specRess[sr];
            dynamicURLs[specRes.url] = true;
        }
    }

    for (id in appConfig.specs) {
        if (appConfig.specs.hasOwnProperty(id)) {
            url = getSpecURL(appConfig, id);
            dynamicURLs[url] = true;
        }
    }

    // Get all the dynamic URLs we have to call via the "tunnel"
    for (url in dynamicURLs) {
        if (dynamicURLs.hasOwnProperty(url)) {
            urls[tunnelPrefix + url + context] = url;
            mkdirP(libpath.dirname(libpath.join(destination, url)), MODE_755);
        }
    }

    for (url in urls) {
        if (urls.hasOwnProperty(url)) {
            manifest += (urls[url] === '/' ? '/index.html' : urls[url]) + '\n';
        }
    }

    // Write the "cache.manifest" file
    fs.writeFileSync(libpath.join(destination, 'cache.manifest'), manifest,
        'utf8');

    // Write a "quick test" index.js file
    fs.writeFileSync(libpath.join(destination, 'index.js'), indexJs, 'utf8');

    // Now use the server to generate some of the files
    writeWebPagesToFiles(destination, urls, config, callback);
};

getSpecURL = function(appConfig, id) {
    var prefix = '/static',
        parts = id.split(':'),
        typeName = parts[0],
        specName = parts[1] || 'default',
        ns = typeName.replace(/\./g, '_'),
        url;

    if (appConfig && appConfig.staticHandling &&
            appConfig.staticHandling.hasOwnProperty('prefix')) {
        prefix = (appConfig.staticHandling.prefix ? '/' +
            appConfig.staticHandling.prefix : '');
    }
    url = prefix + '/' + typeName + '/specs/' + specName + '.json';
    return url;
};

mkdirP = function(p, mode) {
    var ps = libpath.normalize(p).split('/'),
        i;

    for (i = 0; i <= ps.length; i += 1) {
        try {
            fs.mkdirSync(ps.slice(0, i).join('/'), mode);
        } catch (err) {
            // Dirty way to check dir
        }
    }
};

rmdirR = function(path) {
    var files = fs.readdirSync(path),
        currDir = path,
        i,
        currFile;

    /* Loop through and delete everything in the sub-tree after checking it */
    for (i = 0; i < files.length; i += 1) {
        currFile = fs.statSync(currDir + '/' + files[i]);

        if (currFile.isDirectory()) {
            // Recursive function back to the beginning
            rmdirR(currDir + '/' + files[i]);
        } else if (currFile.isSymbolicLink()) {
            // Unlink symlinks
            fs.unlinkSync(currDir + '/' + files[i]);
        } else {
            // Assume it's a file - perhaps a try/catch belongs here?
            fs.unlinkSync(currDir + '/' + files[i]);
        }
    }

    /*
     * Now that we know everything in the sub-tree has been deleted,
     * we can delete the main directory.
     */
    return fs.rmdirSync(path);
};

/**
 * Calculates the filesystem path to get from source to destination.
 *
 * @private
 * @param {string} dst The destination directory.
 * @param {string} src The source directory.
 * @return {string} The path from source to destination.
 */
function pathTo(dst, src) {
    var dstParts,
        srcParts,
        i,
        j,
        path = [];

    // trimming leading and trailing slashes removes nasty corner cases from the
    // algorithm
    if ('/' === dst.charAt(0)) {
        dst = dst.substring(1);
    }
    if ('/' === dst.charAt(dst.length - 1)) {
        dst = dst.substring(0, dst.length - 1);
    }
    if ('/' === src.charAt(0)) {
        src = src.substring(1);
    }
    if ('/' === src.charAt(src.length - 1)) {
        src = src.substring(0, src.length - 1);
    }

    dstParts = dst ? dst.split('/') : [];
    srcParts = src ? src.split('/') : [];
    path = [];
    for (i = 0; i < dstParts.length && i < srcParts.length; i += 1) {
        if (dstParts[i] !== srcParts[i]) {
            break;
        }
    }
    for (j = i; j < srcParts.length; j += 1) {
        path.push('..');
    }
    for (j = i; j < dstParts.length; j += 1) {
        path.push(dstParts[j]);
    }
    return libpath.join.apply(libpath, path) || '.';
}

/**
 * Adds the HTML5 cache manifest to the <html>
 *
 * @private
 * @param {string} root The directory where html5app is being built into.
 * @param {string} relativePath The file path, relative to root.
 * @param {string} content The body of the file.
 * @param {Boolean} force Whether to fixup the content, even though the path
 *     might not end in .html.
 * @return {string} The fixed up content.
 */
function attachManifest(root, relativePath, content, force) {
    var extname = libpath.extname(relativePath),
        dirname,
        pathToRoot;

    if (force || '.html' === extname) {
        dirname = libpath.dirname(relativePath);
        pathToRoot = pathTo('/', dirname);

        content = content.replace(/<html>/, '<html manifest="' + pathToRoot +
            '/cache.manifest">');
    }

    return content;
}

/**
 * Changes server-relative paths to file-relative paths.
 *
 * @private
 * @param {string} root The directory where html5app is being built into.
 * @param {string} relativePath The file path, relative to root.
 * @param {string} content The body of the file.
 * @param {Boolean} force Whether to fixup the content, even though the path
 *     might not end in .html.
 * @return {string} The fixed up content.
 */
function forceRelativePaths(root, relativePath, content, force) {
    var extname = libpath.extname(relativePath),
        dirname;

    if (force || '.html' === extname) {
        dirname = libpath.dirname(relativePath);

        content = content.replace(/(src|href)="([^"]+)"/g,
            function(all, name, val) {
                // FUTURE:  once the "/" aren't escaped, we can do this easier
                var fixed = utils.decodeHTMLEntities(val);
                if ('/' === fixed.charAt(0)) {
                    fixed = libpath.join(pathTo(libpath.dirname(fixed), dirname),
                        libpath.basename(fixed));
                }
                fixed = Y.Escape.html(fixed);
                return name + '="' + fixed + '"';
            });
    }

    return content;
}

writeWebPagesToFiles = function(destination, urls, config, callback) {
    var options,
        userPages = {},
        i,
        app;

    if (config.urls) {
        // If the user is using builds.html5app.attachManifest or
        // .forceRelativePaths, we explicitly want to fix those URLs which are
        // pages (the ones they've specified).
        for (i = 0; i < config.urls.length; i += 1) {
            userPages[config.urls[i]] = true;
        }
    }

    options = {
        port: 11111,
        context: config.context
    };

    app = Mojito.createServer(options);

    app.start(function(err) {
        var got = 0,
            need;

        if (err) {
            utils.error(err);
            return;
        }
        need = Object.keys(urls).length;

        Object.keys(urls).forEach(function(u) {
            var opts = {
                    headers: {
                        'x-mojito-build': 'html5app',
                        'x-mojito-build-path-to-root': pathTo('/',
                            libpath.dirname(urls[u]))
                    }
                };
            app.getWebPage(u, opts, function(err, url, content) {
                var dest;

                got += 1;

                if (err) {
                    utils.error('FAILED to get ' + url + ' with error:\n' +
                                err, null, true);
                } else {
                    if (config.attachManifest) {
                        content = attachManifest(destination, urls[url],
                            content,
                            userPages[url]);
                    }
                    if (config.forceRelativePaths) {
                        content = forceRelativePaths(destination, urls[url],
                            content,
                            userPages[url]
                            );
                    }
                    dest = libpath.join(destination, urls[url]);
                    mkdirP(libpath.dirname(dest), MODE_755);
                    fs.writeFileSync(dest, content, 'utf8');
                }

                if (got === need) {
                    app.close();
                    callback();
                }
            });
        });
    });
};
<|endoftext|>


<commit_before>/**
 * @license ng-bs-daterangepicker v0.0.1
 * @forked by StartAppDev (Nadav Sinai,misterBit)
 @original Author* (c) 2013 Luis Farzati http://github.com/luisfarzati/ng-bs-daterangepicker
 * License: MIT
 */
(function (angular) {
    'use strict';

    angular.module('ngDateRange', []).directive('ngDateRange', function ($compile, $parse) {
        return {
            restrict: 'EA',
            require: '?ngModel',
            link: function ($scope, $element, $attributes, ngModel) {
                if (ngModel === null) return;
                var datepickerObj = null;
                $element.css('position', 'relative');
                var options = {};
                options.invalidText = $attributes.invalidText || 'date format is invalid';
                options.format = $attributes.format || 'YYYY-MM-DD';
                options.showDropdowns = $attributes.showDropdowns || false;
                options.parentEl = $attributes.parentEl || 'body';
                options.separator = $attributes.separator || ' - ';
                options.minDate = ($attributes.minDate) ? moment($parse($attributes.minDate)($scope), options.format) : undefined;
                options.maxDate = ($attributes.maxDate) ? moment($parse($attributes.maxDate)($scope), options.format) : undefined;
                options.dateLimit = $attributes.limit && moment.duration.apply(this, $attributes.limit.split(' ').map(function (elem, index) {
                    return index === 0 && parseInt(elem, 10) || elem;
                }));
                options.ranges = $attributes.ranges && $parse($attributes.ranges)($scope);
                options.locale = $attributes.locale && $parse($attributes.locale)($scope);
                options.opens = $attributes.opens && $parse($attributes.opens)($scope);

                function format(date) {
                    return date.format(options.format);
                }

                function formatted(dates) {
                    return [format(dates.startDate), format(dates.endDate)].join(options.separator);
                }

                var dateValid = function (dateObj) {
                    if (!(dateObj.startDate.isValid() && dateObj.endDate.isValid() &&
                        (dateObj.startDate.isBefore(dateObj.endDate) || dateObj.startDate.isSame(dateObj.endDate, 'day')))) {
                        return false;
                    }
                    if (options.minDate && options.minDate.isValid()) {
                        if (dateObj.startDate.isBefore(options.minDate)) {
                            return false;
                        }
                    }
                    if (options.maxDate && options.maxDate.isValid()) {
                        if (dateObj.endDate.isAfter(options.maxDate)) {
                            return false;
                        }
                    }
                    return true;
                };

                var buildValidation = function () {
                    var html = $('').text(options.invalidText);

                };

                var afterDateChoosen = function (start, end) {
                    $scope.$apply(function () {
                        //if (dateValid({startDate: start, endDate: end})) {
                        ngModel.$setViewValue({startDate: start, endDate: end});
                        ngModel.$render();
                        //  }
                        //else {
                        //       buildValidation();
                        //   }
                    });
                };
                // invoke the jquery plugin and supply the callback, cache the controller object
                datepickerObj = $element.daterangepicker(options, afterDateChoosen).data('daterangepicker');

                $scope.$on('ng.bs.daterangepicker.updateModel', function(e,edata){
                    if (!(edata && edata.startDate && edata.endDate)){
                        edata =ngModel.$modelValue;
                    }
                    updateCalender(edata);
                });

                var updateCalender = function (modelValue) {
                    if (!modelValue || (!modelValue.startDate)) {
                        ngModel.$setViewValue({
                            startDate: options.minDate || moment().startOf('day'),
                            endDate: options.maxDate || moment().startOf('day')
                        });
                    }
                    datepickerObj.setStartDate(modelValue.startDate);
                    datepickerObj.setEndDate(modelValue.endDate);
                    datepickerObj.updateView();
                    datepickerObj.updateCalendars();
                    datepickerObj.updateInputText();
                    return modelValue;
                };

                $element.click(function () {
                    datepickerObj.showCalendars();
                });
                ngModel.$render = function () {
                    if (!ngModel.$viewValue || !ngModel.$viewValue.startDate) return;
                    if ($element[0].tagName == 'input')
                        $element.val(formatted(ngModel.$viewValue));
                };

                ngModel.$formatters.unshift(updateCalender);
                ngModel.$parsers.unshift(updateCalender);

                if ($element[0].tagName == 'input') {
                    ngModel.$parsers.unshift(updateCalender);

                }
            }
        };
    });

})(angular);
<commit_msg>Update ng-bs-daterangepicker.js<commit_after>/**
 * @license ng-bs-daterangepicker v0.0.1
 * @forked by StartAppDev (Nadav Sinai,misterBit)
 @original Author* (c) 2013 Luis Farzati http://github.com/luisfarzati/ng-bs-daterangepicker
 * License: MIT
 */
(function (angular) {
    'use strict';

    angular.module('ngDateRange', []).directive('ngDateRange',["$compile", "$parse",function ($compile, $parse) {
        return {
            restrict: 'EA',
            require: '?ngModel',
            link: function ($scope, $element, $attributes, ngModel) {
                if (ngModel === null) return;
                var datepickerObj = null;
                $element.css('position', 'relative');
                var options = {};
                options.invalidText = $attributes.invalidText || 'date format is invalid';
                options.format = $attributes.format || 'YYYY-MM-DD';
                options.showDropdowns = $attributes.showDropdowns || false;
                options.parentEl = $attributes.parentEl || 'body';
                options.separator = $attributes.separator || ' - ';
                options.minDate = ($attributes.minDate) ? moment($parse($attributes.minDate)($scope), options.format) : undefined;
                options.maxDate = ($attributes.maxDate) ? moment($parse($attributes.maxDate)($scope), options.format) : undefined;
                options.dateLimit = $attributes.limit && moment.duration.apply(this, $attributes.limit.split(' ').map(function (elem, index) {
                    return index === 0 && parseInt(elem, 10) || elem;
                }));
                options.ranges = $attributes.ranges && $parse($attributes.ranges)($scope);
                options.locale = $attributes.locale && $parse($attributes.locale)($scope);
                options.opens = $attributes.opens && $parse($attributes.opens)($scope);

                function format(date) {
                    return date.format(options.format);
                }

                function formatted(dates) {
                    return [format(dates.startDate), format(dates.endDate)].join(options.separator);
                }

                var dateValid = function (dateObj) {
                    if (!(dateObj.startDate.isValid() && dateObj.endDate.isValid() &&
                        (dateObj.startDate.isBefore(dateObj.endDate) || dateObj.startDate.isSame(dateObj.endDate, 'day')))) {
                        return false;
                    }
                    if (options.minDate && options.minDate.isValid()) {
                        if (dateObj.startDate.isBefore(options.minDate)) {
                            return false;
                        }
                    }
                    if (options.maxDate && options.maxDate.isValid()) {
                        if (dateObj.endDate.isAfter(options.maxDate)) {
                            return false;
                        }
                    }
                    return true;
                };

                var buildValidation = function () {
                    var html = $('').text(options.invalidText);

                };

                var afterDateChoosen = function (start, end) {
                    $scope.$apply(function () {
                        //if (dateValid({startDate: start, endDate: end})) {
                        ngModel.$setViewValue({startDate: start, endDate: end});
                        ngModel.$render();
                        //  }
                        //else {
                        //       buildValidation();
                        //   }
                    });
                };
                // invoke the jquery plugin and supply the callback, cache the controller object
                datepickerObj = $element.daterangepicker(options, afterDateChoosen).data('daterangepicker');

                $scope.$on('ng.bs.daterangepicker.updateModel', function(e,edata){
                    if (!(edata && edata.startDate && edata.endDate)){
                        edata =ngModel.$modelValue;
                    }
                    updateCalender(edata);
                });

                var updateCalender = function (modelValue) {
                    if (!modelValue || (!modelValue.startDate)) {
                        ngModel.$setViewValue({
                            startDate: options.minDate || moment().startOf('day'),
                            endDate: options.maxDate || moment().startOf('day')
                        });
                    }
                    datepickerObj.setStartDate(modelValue.startDate);
                    datepickerObj.setEndDate(modelValue.endDate);
                    datepickerObj.updateView();
                    datepickerObj.updateCalendars();
                    datepickerObj.updateInputText();
                    return modelValue;
                };

                $element.click(function () {
                    datepickerObj.showCalendars();
                });
                ngModel.$render = function () {
                    if (!ngModel.$viewValue || !ngModel.$viewValue.startDate) return;
                    if ($element[0].tagName == 'input')
                        $element.val(formatted(ngModel.$viewValue));
                };

                ngModel.$formatters.unshift(updateCalender);
                ngModel.$parsers.unshift(updateCalender);

                if ($element[0].tagName == 'input') {
                    ngModel.$parsers.unshift(updateCalender);

                }
            }
        };
    }]);

})(angular);
<|endoftext|>


<commit_before>(function() {
  'use strict';

  angular.module("firebase")

    // The factory returns an object containing the value of the data at
    // the Firebase location provided, as well as several methods. It
    // takes one or two arguments:
    //
    //   * `ref`: A Firebase reference. Queries or limits may be applied.
    //   * `config`: An object containing any of the advanced config options explained in API docs
    .factory("$firebase", [ "$firebaseUtils", "$firebaseConfig",
      function ($firebaseUtils, $firebaseConfig) {
        function AngularFire(ref, config) {
          // make the new keyword optional
          if (!(this instanceof AngularFire)) {
            return new AngularFire(ref, config);
          }
          this._config = $firebaseConfig(config);
          this._ref = ref;
          this._arraySync = null;
          this._objectSync = null;
          this._assertValidConfig(ref, this._config);
        }

        AngularFire.prototype = {
          $ref: function () {
            return this._ref;
          },

          $push: function (data) {
            var def = $firebaseUtils.defer();
            var ref = this._ref.ref().push();
            var done = this._handle(def, ref);
            if (arguments.length > 0) {
              ref.set(data, done);
            }
            else {
              done();
            }
            return def.promise;
          },

          $set: function (key, data) {
            var ref = this._ref;
            var def = $firebaseUtils.defer();
            if (arguments.length > 1) {
              ref = ref.ref().child(key);
            }
            else {
              data = key;
            }
            if( angular.isFunction(ref.set) || !angular.isObject(data) ) {
              // this is not a query, just do a flat set
              ref.ref().set(data, this._handle(def, ref));
            }
            else {
              var dataCopy = angular.extend({}, data);
              // this is a query, so we will replace all the elements
              // of this query with the value provided, but not blow away
              // the entire Firebase path
              ref.once('value', function(snap) {
                snap.forEach(function(ss) {
                  if( !dataCopy.hasOwnProperty(ss.name()) ) {
                    dataCopy[ss.name()] = null;
                  }
                });
                ref.ref().update(dataCopy, this._handle(def, ref));
              }, this);
            }
            return def.promise;
          },

          $remove: function (key) {
            var ref = this._ref, self = this;
            if (arguments.length > 0) {
              ref = ref.ref().child(key);
            }
            var def = $firebaseUtils.defer();
            if( angular.isFunction(ref.remove) ) {
              // self is not a query, just do a flat remove
              ref.remove(self._handle(def, ref));
            }
            else {
              var promises = [];
              // self is a query so let's only remove the
              // items in the query and not the entire path
              ref.once('value', function(snap) {
                snap.forEach(function(ss) {
                  var d = $firebaseUtils.defer();
                  promises.push(d);
                  ss.ref().remove(self._handle(d, ss.ref()));
                }, self);
              });
              self._handle($firebaseUtils.allPromises(promises), ref);
            }
            return def.promise;
          },

          $update: function (key, data) {
            var ref = this._ref.ref();
            var def = $firebaseUtils.defer();
            if (arguments.length > 1) {
              ref = ref.child(key);
            }
            else {
              data = key;
            }
            ref.update(data, this._handle(def, ref));
            return def.promise;
          },

          $transaction: function (key, valueFn, applyLocally) {
            var ref = this._ref.ref();
            if( angular.isFunction(key) ) {
              applyLocally = valueFn;
              valueFn = key;
            }
            else {
              ref = ref.child(key);
            }
            if( angular.isUndefined(applyLocally) ) {
              applyLocally = false;
            }

            var def = $firebaseUtils.defer();
            ref.transaction(valueFn, function(err, committed, snap) {
               if( err ) {
                 def.reject(err);
               }
               else {
                 def.resolve(committed? snap : null);
               }
            }, applyLocally);
            return def.promise;
          },

          $asObject: function () {
            if (!this._objectSync || this._objectSync.isDestroyed) {
              this._objectSync = new SyncObject(this, this._config.objectFactory);
            }
            return this._objectSync.getObject();
          },

          $asArray: function () {
            if (!this._arraySync || this._arraySync.isDestroyed) {
              this._arraySync = new SyncArray(this, this._config.arrayFactory);
            }
            return this._arraySync.getArray();
          },

          _handle: function (def) {
            var args = Array.prototype.slice.call(arguments, 1);
            return function (err) {
              if (err) {
                def.reject(err);
              }
              else {
                def.resolve.apply(def, args);
              }
            };
          },

          _assertValidConfig: function (ref, cnf) {
            $firebaseUtils.assertValidRef(ref, 'Must pass a valid Firebase reference ' +
              'to $firebase (not a string or URL)');
            if (!angular.isFunction(cnf.arrayFactory)) {
              throw new Error('config.arrayFactory must be a valid function');
            }
            if (!angular.isFunction(cnf.objectFactory)) {
              throw new Error('config.arrayFactory must be a valid function');
            }
          }
        };

        function SyncArray($inst, ArrayFactory) {
          function destroy(err) {
            self.isDestroyed = true;
            var ref = $inst.$ref();
            ref.off('child_added', created);
            ref.off('child_moved', moved);
            ref.off('child_changed', updated);
            ref.off('child_removed', removed);
            array = null;
            resolve(err||'destroyed');
          }

          function init() {
            var ref = $inst.$ref();

            // listen for changes at the Firebase instance
            ref.on('child_added', created, error);
            ref.on('child_moved', moved, error);
            ref.on('child_changed', updated, error);
            ref.on('child_removed', removed, error);

            // determine when initial load is completed
            ref.once('value', resolve.bind(null, null), resolve);
          }

          function resolve(err) {
            if( def ) {
              if( err ) { def.reject(err); }
              else { def.resolve(array); }
              def = null;
            }
          }

          var def = $firebaseUtils.defer();
          var array = new ArrayFactory($inst, destroy, def.promise);
          var batch = $firebaseUtils.batch();
          var created = batch(array.$$added, array);
          var updated = batch(array.$$updated, array);
          var moved = batch(array.$$moved, array);
          var removed = batch(array.$$removed, array);
          var error = batch(array.$$error, array);

          var self = this;
          self.isDestroyed = false;
          self.getArray = function() { return array; };
          init();
        }

        function SyncObject($inst, ObjectFactory) {
          function destroy(err) {
            self.isDestroyed = true;
            ref.off('value', applyUpdate);
            obj = null;
            resolve(err||'destroyed');
          }

          function init() {
            ref.on('value', applyUpdate, error);
            ref.once('value', batch(resolve.bind(null, null)), resolve);
          }

          function resolve(err) {
            if( def ) {
              if( err ) { def.reject(err); }
              else { def.resolve(obj); }
              def = null;
            }
          }

          var def = $firebaseUtils.defer();
          var obj = new ObjectFactory($inst, destroy, def.promise);
          var ref = $inst.$ref();
          var batch = $firebaseUtils.batch();
          var applyUpdate = batch(obj.$$updated, obj);
          var error = batch(obj.$$error, obj);

          var self = this;
          self.isDestroyed = false;
          self.getObject = function() { return obj; };
          init();
        }

        return AngularFire;
      }
    ]);
})();<commit_msg>Fixed $bindTo regression in $FirebaseArray<commit_after>(function() {
  'use strict';

  angular.module("firebase")

    // The factory returns an object containing the value of the data at
    // the Firebase location provided, as well as several methods. It
    // takes one or two arguments:
    //
    //   * `ref`: A Firebase reference. Queries or limits may be applied.
    //   * `config`: An object containing any of the advanced config options explained in API docs
    .factory("$firebase", [ "$firebaseUtils", "$firebaseConfig",
      function ($firebaseUtils, $firebaseConfig) {
        function AngularFire(ref, config) {
          // make the new keyword optional
          if (!(this instanceof AngularFire)) {
            return new AngularFire(ref, config);
          }
          this._config = $firebaseConfig(config);
          this._ref = ref;
          this._arraySync = null;
          this._objectSync = null;
          this._assertValidConfig(ref, this._config);
        }

        AngularFire.prototype = {
          $ref: function () {
            return this._ref;
          },

          $push: function (data) {
            var def = $firebaseUtils.defer();
            var ref = this._ref.ref().push();
            var done = this._handle(def, ref);
            if (arguments.length > 0) {
              ref.set(data, done);
            }
            else {
              done();
            }
            return def.promise;
          },

          $set: function (key, data) {
            var ref = this._ref;
            var def = $firebaseUtils.defer();
            if (arguments.length > 1) {
              ref = ref.ref().child(key);
            }
            else {
              data = key;
            }
            if( angular.isFunction(ref.set) || !angular.isObject(data) ) {
              // this is not a query, just do a flat set
              ref.ref().set(data, this._handle(def, ref));
            }
            else {
              var dataCopy = angular.extend({}, data);
              // this is a query, so we will replace all the elements
              // of this query with the value provided, but not blow away
              // the entire Firebase path
              ref.once('value', function(snap) {
                snap.forEach(function(ss) {
                  if( !dataCopy.hasOwnProperty(ss.name()) ) {
                    dataCopy[ss.name()] = null;
                  }
                });
                ref.ref().update(dataCopy, this._handle(def, ref));
              }, this);
            }
            return def.promise;
          },

          $remove: function (key) {
            var ref = this._ref, self = this;
            if (arguments.length > 0) {
              ref = ref.ref().child(key);
            }
            var def = $firebaseUtils.defer();
            if( angular.isFunction(ref.remove) ) {
              // self is not a query, just do a flat remove
              ref.remove(self._handle(def, ref));
            }
            else {
              var promises = [];
              // self is a query so let's only remove the
              // items in the query and not the entire path
              ref.once('value', function(snap) {
                snap.forEach(function(ss) {
                  var d = $firebaseUtils.defer();
                  promises.push(d);
                  ss.ref().remove(self._handle(d, ss.ref()));
                }, self);
              });
              self._handle($firebaseUtils.allPromises(promises), ref);
            }
            return def.promise;
          },

          $update: function (key, data) {
            var ref = this._ref.ref();
            var def = $firebaseUtils.defer();
            if (arguments.length > 1) {
              ref = ref.child(key);
            }
            else {
              data = key;
            }
            ref.update(data, this._handle(def, ref));
            return def.promise;
          },

          $transaction: function (key, valueFn, applyLocally) {
            var ref = this._ref.ref();
            if( angular.isFunction(key) ) {
              applyLocally = valueFn;
              valueFn = key;
            }
            else {
              ref = ref.child(key);
            }
            if( angular.isUndefined(applyLocally) ) {
              applyLocally = false;
            }

            var def = $firebaseUtils.defer();
            ref.transaction(valueFn, function(err, committed, snap) {
               if( err ) {
                 def.reject(err);
               }
               else {
                 def.resolve(committed? snap : null);
               }
            }, applyLocally);
            return def.promise;
          },

          $asObject: function () {
            if (!this._objectSync || this._objectSync.isDestroyed) {
              this._objectSync = new SyncObject(this, this._config.objectFactory);
            }
            return this._objectSync.getObject();
          },

          $asArray: function () {
            if (!this._arraySync || this._arraySync.isDestroyed) {
              this._arraySync = new SyncArray(this, this._config.arrayFactory);
            }
            return this._arraySync.getArray();
          },

          _handle: function (def) {
            var args = Array.prototype.slice.call(arguments, 1);
            return function (err) {
              if (err) {
                def.reject(err);
              }
              else {
                def.resolve.apply(def, args);
              }
            };
          },

          _assertValidConfig: function (ref, cnf) {
            $firebaseUtils.assertValidRef(ref, 'Must pass a valid Firebase reference ' +
              'to $firebase (not a string or URL)');
            if (!angular.isFunction(cnf.arrayFactory)) {
              throw new Error('config.arrayFactory must be a valid function');
            }
            if (!angular.isFunction(cnf.objectFactory)) {
              throw new Error('config.arrayFactory must be a valid function');
            }
          }
        };

        function SyncArray($inst, ArrayFactory) {
          function destroy(err) {
            self.isDestroyed = true;
            var ref = $inst.$ref();
            ref.off('child_added', created);
            ref.off('child_moved', moved);
            ref.off('child_changed', updated);
            ref.off('child_removed', removed);
            array = null;
            resolve(err||'destroyed');
          }

          function init() {
            var ref = $inst.$ref();

            // listen for changes at the Firebase instance
            ref.on('child_added', created, error);
            ref.on('child_moved', moved, error);
            ref.on('child_changed', updated, error);
            ref.on('child_removed', removed, error);

            // determine when initial load is completed
            ref.once('value', batch(resolve.bind(null, null)), resolve);
          }

          function resolve(err) {
            if( def ) {
              if( err ) { def.reject(err); }
              else { def.resolve(array); }
              def = null;
            }
          }

          var def = $firebaseUtils.defer();
          var array = new ArrayFactory($inst, destroy, def.promise);
          var batch = $firebaseUtils.batch();
          var created = batch(array.$$added, array);
          var updated = batch(array.$$updated, array);
          var moved = batch(array.$$moved, array);
          var removed = batch(array.$$removed, array);
          var error = batch(array.$$error, array);

          var self = this;
          self.isDestroyed = false;
          self.getArray = function() { return array; };
          init();
        }

        function SyncObject($inst, ObjectFactory) {
          function destroy(err) {
            self.isDestroyed = true;
            ref.off('value', applyUpdate);
            obj = null;
            resolve(err||'destroyed');
          }

          function init() {
            ref.on('value', applyUpdate, error);
            ref.once('value', batch(resolve.bind(null, null)), resolve);
          }

          function resolve(err) {
            if( def ) {
              if( err ) { def.reject(err); }
              else { def.resolve(obj); }
              def = null;
            }
          }

          var def = $firebaseUtils.defer();
          var obj = new ObjectFactory($inst, destroy, def.promise);
          var ref = $inst.$ref();
          var batch = $firebaseUtils.batch();
          var applyUpdate = batch(obj.$$updated, obj);
          var error = batch(obj.$$error, obj);

          var self = this;
          self.isDestroyed = false;
          self.getObject = function() { return obj; };
          init();
        }

        return AngularFire;
      }
    ]);
})();<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict'

const Metalsmith = require('metalsmith')
const autoprefixer = require('autoprefixer-stylus')
const collections = require('metalsmith-collections')
const feed = require('metalsmith-feed')
const layouts = require('metalsmith-layouts')
const markdown = require('metalsmith-markdown')
const prism = require('metalsmith-prism')
const stylus = require('metalsmith-stylus')
const permalinks = require('metalsmith-permalinks')
const marked = require('marked')
const path = require('path')
const fs = require('fs')
const ncp = require('ncp')

const filterStylusPartials = require('./scripts/plugins/filter-stylus-partials')
const mapHandlebarsPartials = require('./scripts/plugins/map-handlebars-partials')
const anchorMarkdownHeadings = require('./scripts/plugins/anchor-markdown-headings')
const loadVersions = require('./scripts/load-versions')

/** Build **/

// load template.json for given language, but use default language as fallback
// for properties which are not present in the given language
const DEFAULT_LANG = 'en'

const renderer = new marked.Renderer()
renderer.heading = anchorMarkdownHeadings

const markedOptions = {
  langPrefix: 'language-',
  renderer: renderer
}

function i18nJSON (lang) {
  var defaultJSON = require(`./locale/${DEFAULT_LANG}/site.json`)
  var templateJSON = require(`./locale/${lang}/site.json`)
  var finalJSON = JSON.parse(JSON.stringify(defaultJSON))
  var merge = function (targetJSON, customJSON) {
    Object.keys(customJSON).forEach(function (key) {
      let value = customJSON[key]
      if (typeof value === 'object') {
        merge(targetJSON[key], value)
      } else {
        targetJSON[key] = value
      }
    })
  }
  merge(finalJSON, templateJSON)
  return finalJSON
}

function buildlocale (source, locale) {
  console.time('[metalsmith] build/' + locale + ' finished')
  const siteJSON = path.join(__dirname, 'locale', locale, 'site.json')
  const metalsmith = Metalsmith(__dirname)
  metalsmith
    .metadata({
      site: require(siteJSON),
      project: source.project,
      i18n: i18nJSON(locale)
    })
    .source(path.join(__dirname, 'locale', locale))
    .use(collections({
      blog: {
        pattern: 'blog/**/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      blogAnnounce: {
        pattern: 'blog/announcements/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      blogReleases: {
        pattern: 'blog/release/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      blogVulnerability: {
        pattern: 'blog/vulnerability/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      lastWeekly: {
        pattern: 'blog/weekly-updates/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false,
        limit: 1
      },
      tscMinutes: {
        pattern: 'foundation/tsc/minutes/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      }
    }))
    .use(markdown(markedOptions))
    .use(prism())
    .use(filterStylusPartials())
    .use(stylus({
      compress: true,
      paths: [path.join(__dirname, 'layouts', 'css')],
      use: [autoprefixer()]
    }))
    .use(permalinks({
      relative: false
    }))
    .use(feed({
      collection: 'blog',
      destination: 'feed/blog.xml',
      title: 'Node.js Blog'
    }))
    .use(feed({
      collection: 'blogAnnounce',
      destination: 'feed/announce.xml',
      title: 'Node.js Announcements'
    }))
    .use(feed({
      collection: 'blogReleases',
      destination: 'feed/releases.xml',
      title: 'Node.js Blog: Releases'
    }))
    .use(feed({
      collection: 'blogVulnerability',
      destination: 'feed/vulnerability.xml',
      title: 'Node.js Blog: Vulnerability Reports'
    }))
    .use(feed({
      collection: 'tscMinutes',
      destination: 'feed/tsc-minutes.xml',
      title: 'Node.js Technical Steering Committee meetings'
    }))
    .use(layouts({
      engine: 'handlebars',
      pattern: '**/*.html',
      partials: mapHandlebarsPartials(metalsmith, 'layouts', 'partials'),
      helpers: {
        equals: require('./scripts/helpers/equals.js'),
        startswith: require('./scripts/helpers/startswith.js'),
        i18n: require('./scripts/helpers/i18n.js'),
        changeloglink: require('./scripts/helpers/changeloglink.js'),
        strftime: require('./scripts/helpers/strftime.js'),
        apidocslink: require('./scripts/helpers/apidocslink.js')
      }
    }))
    .destination(path.join(__dirname, 'build', locale))

  metalsmith.build(function (err) {
    if (err) { throw err }
    console.timeEnd('[metalsmith] build/' + locale + ' finished')
  })
}

function copystatic () {
  console.time('[metalsmith] build/static finished')
  fs.mkdir(path.join(__dirname, 'build'), function () {
    fs.mkdir(path.join(__dirname, 'build', 'static'), function () {
      ncp(path.join(__dirname, 'static'), path.join(__dirname, 'build', 'static'), function (err) {
        if (err) { return console.error(err) }
        console.timeEnd('[metalsmith] build/static finished')
      })
    })
  })
}

function fullbuild () {
  copystatic()
  loadVersions(function (err, versions) {
    if (err) { throw err }
    const source = {
      project: {
        versions,
        currentVersion: versions[0].version,
        banner: {
          visible: false,
          content: 'Important security release, please update now!'
        }
      }
    }

    fs.readdir(path.join(__dirname, 'locale'), function (e, locales) {
      locales.forEach(function (locale) {
        buildlocale(source, locale)
      })
    })
  })
}

function server () {
  /** Static file server **/
  const st = require('st')
  const http = require('http')
  const mount = st({
    path: path.join(__dirname, 'build'),
    cache: false,
    index: 'index.html'
  })
  http.createServer(
    function (req, res) { mount(req, res) }
  ).listen(8080,
    function () { console.log('http://localhost:8080/en/') }
  )

  /** File Watches for Re-Builds **/
  const chokidar = require('chokidar')
  const opts = {
    persistent: true,
    ignoreInitial: true,
    followSymlinks: true,
    usePolling: true,
    alwaysStat: false,
    depth: undefined,
    interval: 100,
    ignorePermissionErrors: false,
    atomic: true
  }
  const locales = chokidar.watch(path.join(__dirname, 'locale'), opts)
  const layouts = chokidar.watch(path.join(__dirname, 'layouts'), opts)
  const staticf = chokidar.watch(path.join(__dirname, 'static'), opts)

  function getlocale (p) {
    const pre = path.join(__dirname, 'locale')
    return p.slice(pre.length + 1, p.indexOf('/', pre.length + 1))
  }
  locales.on('change', function (p) {
    buildlocale(p, getlocale(p))
  })
  locales.on('add', function (p) {
    buildlocale(p, getlocale(p))
    locales.add(p)
  })

  layouts.on('change', fullbuild)
  layouts.on('add', function (p) { layouts.add(p); fullbuild() })

  staticf.on('change', copystatic)
  staticf.on('add', function (p) { staticf.add(p); copystatic() })
}

fullbuild()

if (process.argv[2] === 'serve') {
  server()
}
<commit_msg>Homepage: Enable security update banner for v4.1.2<commit_after>#!/usr/bin/env node

'use strict'

const Metalsmith = require('metalsmith')
const autoprefixer = require('autoprefixer-stylus')
const collections = require('metalsmith-collections')
const feed = require('metalsmith-feed')
const layouts = require('metalsmith-layouts')
const markdown = require('metalsmith-markdown')
const prism = require('metalsmith-prism')
const stylus = require('metalsmith-stylus')
const permalinks = require('metalsmith-permalinks')
const marked = require('marked')
const path = require('path')
const fs = require('fs')
const ncp = require('ncp')

const filterStylusPartials = require('./scripts/plugins/filter-stylus-partials')
const mapHandlebarsPartials = require('./scripts/plugins/map-handlebars-partials')
const anchorMarkdownHeadings = require('./scripts/plugins/anchor-markdown-headings')
const loadVersions = require('./scripts/load-versions')

/** Build **/

// load template.json for given language, but use default language as fallback
// for properties which are not present in the given language
const DEFAULT_LANG = 'en'

const renderer = new marked.Renderer()
renderer.heading = anchorMarkdownHeadings

const markedOptions = {
  langPrefix: 'language-',
  renderer: renderer
}

function i18nJSON (lang) {
  var defaultJSON = require(`./locale/${DEFAULT_LANG}/site.json`)
  var templateJSON = require(`./locale/${lang}/site.json`)
  var finalJSON = JSON.parse(JSON.stringify(defaultJSON))
  var merge = function (targetJSON, customJSON) {
    Object.keys(customJSON).forEach(function (key) {
      let value = customJSON[key]
      if (typeof value === 'object') {
        merge(targetJSON[key], value)
      } else {
        targetJSON[key] = value
      }
    })
  }
  merge(finalJSON, templateJSON)
  return finalJSON
}

function buildlocale (source, locale) {
  console.time('[metalsmith] build/' + locale + ' finished')
  const siteJSON = path.join(__dirname, 'locale', locale, 'site.json')
  const metalsmith = Metalsmith(__dirname)
  metalsmith
    .metadata({
      site: require(siteJSON),
      project: source.project,
      i18n: i18nJSON(locale)
    })
    .source(path.join(__dirname, 'locale', locale))
    .use(collections({
      blog: {
        pattern: 'blog/**/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      blogAnnounce: {
        pattern: 'blog/announcements/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      blogReleases: {
        pattern: 'blog/release/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      blogVulnerability: {
        pattern: 'blog/vulnerability/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      lastWeekly: {
        pattern: 'blog/weekly-updates/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false,
        limit: 1
      },
      tscMinutes: {
        pattern: 'foundation/tsc/minutes/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      }
    }))
    .use(markdown(markedOptions))
    .use(prism())
    .use(filterStylusPartials())
    .use(stylus({
      compress: true,
      paths: [path.join(__dirname, 'layouts', 'css')],
      use: [autoprefixer()]
    }))
    .use(permalinks({
      relative: false
    }))
    .use(feed({
      collection: 'blog',
      destination: 'feed/blog.xml',
      title: 'Node.js Blog'
    }))
    .use(feed({
      collection: 'blogAnnounce',
      destination: 'feed/announce.xml',
      title: 'Node.js Announcements'
    }))
    .use(feed({
      collection: 'blogReleases',
      destination: 'feed/releases.xml',
      title: 'Node.js Blog: Releases'
    }))
    .use(feed({
      collection: 'blogVulnerability',
      destination: 'feed/vulnerability.xml',
      title: 'Node.js Blog: Vulnerability Reports'
    }))
    .use(feed({
      collection: 'tscMinutes',
      destination: 'feed/tsc-minutes.xml',
      title: 'Node.js Technical Steering Committee meetings'
    }))
    .use(layouts({
      engine: 'handlebars',
      pattern: '**/*.html',
      partials: mapHandlebarsPartials(metalsmith, 'layouts', 'partials'),
      helpers: {
        equals: require('./scripts/helpers/equals.js'),
        startswith: require('./scripts/helpers/startswith.js'),
        i18n: require('./scripts/helpers/i18n.js'),
        changeloglink: require('./scripts/helpers/changeloglink.js'),
        strftime: require('./scripts/helpers/strftime.js'),
        apidocslink: require('./scripts/helpers/apidocslink.js')
      }
    }))
    .destination(path.join(__dirname, 'build', locale))

  metalsmith.build(function (err) {
    if (err) { throw err }
    console.timeEnd('[metalsmith] build/' + locale + ' finished')
  })
}

function copystatic () {
  console.time('[metalsmith] build/static finished')
  fs.mkdir(path.join(__dirname, 'build'), function () {
    fs.mkdir(path.join(__dirname, 'build', 'static'), function () {
      ncp(path.join(__dirname, 'static'), path.join(__dirname, 'build', 'static'), function (err) {
        if (err) { return console.error(err) }
        console.timeEnd('[metalsmith] build/static finished')
      })
    })
  })
}

function fullbuild () {
  copystatic()
  loadVersions(function (err, versions) {
    if (err) { throw err }
    const source = {
      project: {
        versions,
        currentVersion: versions[0].version,
        banner: {
          visible: true,
          content: 'Important security release, please update now!'
        }
      }
    }

    fs.readdir(path.join(__dirname, 'locale'), function (e, locales) {
      locales.forEach(function (locale) {
        buildlocale(source, locale)
      })
    })
  })
}

function server () {
  /** Static file server **/
  const st = require('st')
  const http = require('http')
  const mount = st({
    path: path.join(__dirname, 'build'),
    cache: false,
    index: 'index.html'
  })
  http.createServer(
    function (req, res) { mount(req, res) }
  ).listen(8080,
    function () { console.log('http://localhost:8080/en/') }
  )

  /** File Watches for Re-Builds **/
  const chokidar = require('chokidar')
  const opts = {
    persistent: true,
    ignoreInitial: true,
    followSymlinks: true,
    usePolling: true,
    alwaysStat: false,
    depth: undefined,
    interval: 100,
    ignorePermissionErrors: false,
    atomic: true
  }
  const locales = chokidar.watch(path.join(__dirname, 'locale'), opts)
  const layouts = chokidar.watch(path.join(__dirname, 'layouts'), opts)
  const staticf = chokidar.watch(path.join(__dirname, 'static'), opts)

  function getlocale (p) {
    const pre = path.join(__dirname, 'locale')
    return p.slice(pre.length + 1, p.indexOf('/', pre.length + 1))
  }
  locales.on('change', function (p) {
    buildlocale(p, getlocale(p))
  })
  locales.on('add', function (p) {
    buildlocale(p, getlocale(p))
    locales.add(p)
  })

  layouts.on('change', fullbuild)
  layouts.on('add', function (p) { layouts.add(p); fullbuild() })

  staticf.on('change', copystatic)
  staticf.on('add', function (p) { staticf.add(p); copystatic() })
}

fullbuild()

if (process.argv[2] === 'serve') {
  server()
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

var chalk = require('chalk'),
    path = require('path'),
    fs = require('fs-extra'),
    // zlib = require('zlib'),
    browserify = require('browserify');

var EXAMPLES_DIR = 'examples',
    BUILD_DIR = 'build';

// var gzip = zlib.createGzip();

console.log(chalk.green('Bundling examples...'));

fs.emptydirSync(BUILD_DIR);
fs.copySync(EXAMPLES_DIR, BUILD_DIR);
fs.readdirSync(BUILD_DIR)
  .map((dir) => path.join(BUILD_DIR, dir, 'index.js'))
  .filter((path) => {
    try { return fs.statSync(path).isFile(); }
    catch (err) { return false; }
  })
  .map((file) => {
    var dir = path.dirname(file);

    console.log(chalk.yellow('  ⇢  %s/bundle.js'), dir);

    var bundle = browserify()
      .add(file)
      .bundle();
    bundle
      .pipe(fs.createWriteStream(path.join(dir, 'bundle.js')));
    // bundle
    //   .pipe(gzip)
    //   .pipe(fs.createWriteStream(path.join(dir, 'bundle.js.gz')));
  });

process.on('exit', () => console.log(chalk.black.bgGreen('  Success! 🍻   ')));
<commit_msg>Remove gzip stuff for now.<commit_after>#!/usr/bin/env node

var chalk = require('chalk'),
    path = require('path'),
    fs = require('fs-extra'),
    browserify = require('browserify');

var EXAMPLES_DIR = 'examples',
    BUILD_DIR = 'build';

console.log(chalk.green('Bundling examples...'));

fs.emptydirSync(BUILD_DIR);
fs.copySync(EXAMPLES_DIR, BUILD_DIR);
fs.readdirSync(BUILD_DIR)
  .map((dir) => path.join(BUILD_DIR, dir, 'index.js'))
  .filter((path) => {
    try { return fs.statSync(path).isFile(); }
    catch (err) { return false; }
  })
  .map((file) => {
    var dir = path.dirname(file);

    console.log(chalk.yellow('  ⇢  %s/bundle.js'), dir);

    var bundle = browserify()
      .add(file)
      .bundle();
    bundle
      .pipe(fs.createWriteStream(path.join(dir, 'bundle.js')));
  });

process.on('exit', () => console.log(chalk.black.bgGreen('  Success! 🍻   ')));
<|endoftext|>


<commit_before>
if( typeof module !== 'undefined' )
require( 'wFiles' )

var _ = wTools;

if( _.FileRecord )
{

  debugger;

  //var fileRecord = wFileRecord( 'tmp' );
  //var fileRecord = wFileRecord( 'tmp/sample/FilesPathTest/tmp/copy/test_original.txt' );

  var f1 = wFileRecord({ dir : _.baseDir(), file : '../proto/dwtools/amid/l4_files/Uses.ss' });
  var f2 = wFileRecord({ dir : _.baseDir(), file : '../proto/amid/l4_files/Uses.ss' });
  var filesSame = _.filesSame( f1,f2 );
  console.log( 'filesSame :',filesSame );

}

/* qqq : rewrite samples */
<commit_msg>Fix sample `Sample2.js`, use valid routines<commit_after>
if( typeof module !== 'undefined' )
require( 'wFiles' )

var _ = wTools;

var dir = _.path.join( __dirname, '../proto/wtools/Tools.s' );
var o =
{
  defaultProvider : _.fileProvider,
  filter : null,
  allowingMissed : 1,
};
var factory = _.FileRecordFactory.TolerantFrom( o, { dirPath : dir } ).form();

if( _.FileRecord )
{

  debugger;

  //var fileRecord = wFileRecord( 'tmp' );
  //var fileRecord = wFileRecord( 'tmp/sample/FilesPathTest/tmp/copy/test_original.txt' );

  // var f1 = wFileRecord({ dir : _.baseDir(), file : '../proto/dwtools/amid/l4_files/Uses.ss' });
  // var f2 = wFileRecord({ dir : _.baseDir(), file : '../proto/amid/l4_files/Uses.ss' });
  var f1 = _.FileRecord({ input : dir, factory });
  var f2 = _.FileRecord({ input : dir, factory });
  var filesSame = _.fileProvider.filesAreSameForSure( f1, f2 );
  console.log( 'filesSame :', filesSame );

}

/* qqq : rewrite samples */
<|endoftext|>


<commit_before>module.exports = ({
    baseUrl: '.',
    name: 'lib/client/main',
    out: '.bin/main.js',
    optimize: 'none',
    absolutise: true,
    cjsTranslate: true,
    paths: {
        'async': 'async/lib/async',
        'chalk': 'chalk/index',
        'jquery': 'jquery/dist/jquery'
    },
    map: {
        // Use no conflict jQuery.
        '*': { 'jquery': 'lib/client/vendor/jquery' },
        'lib/client/vendor/jquery': { 'jquery': 'jquery' }
    }
});<commit_msg>Add chalk dependencies<commit_after>module.exports = ({
    baseUrl: '.',
    name: 'lib/client/main',
    out: '.bin/main.js',
    optimize: 'none',
    absolutise: true,
    cjsTranslate: true,
    paths: {
        'async': 'async/lib/async',
        'chalk': 'chalk/index',
        'escape-string-regexp': 'chalk/node_modules/escape-string-regexp/index',
        'ansi-styles': 'chalk/node_modules/ansi-styles/index',
        'strip-ansi': 'chalk/node_modules/strip-ansi/index',
        'ansi-regex': 'chalk/node_modules/strip-ansi/node_modules/ansi-regex/index',
        'has-ansi': 'chalk/node_modules/has-ansi/index',
        'supports-color': 'chalk/node_modules/supports-color/index',
        'jquery': 'jquery/dist/jquery'
    },
    map: {
        // Use no conflict jQuery.
        '*': { 'jquery': 'lib/client/vendor/jquery' },
        'lib/client/vendor/jquery': { 'jquery': 'jquery' }
    }
});<|endoftext|>


<commit_before>import FaShield from 'react-icons/lib/fa/shield'
import { Component } from 'react'

class Member extends Component {

render() {

    return (

        //
        // TODO: Create Member Component
        //

    )
}
}

export default Member<commit_msg>ret: a challenge - add a Member component<commit_after>import FaShield from 'react-icons/lib/fa/shield'
import { Component } from 'react'

class Member extends Component {

    render() {

        const{name, thumbnail, email, admin, makeAdmin} = this.props

        return (

 {name} {(admin) ? <FaShield /> : null}

                Make Admin

{email}

        )
    }
}

export default Member<|endoftext|>