<commit_before>(function() {

    var displayProjection = "EPSG:4326";

    var hover;
    var renderers = [ "SVG" ]; // Canvas

    var status,
        loader,
        loading,
        overpassAPI,
        changesetRegex;

    function addBaseLayers(map) {

        //OpenLayers.Layer.OSM.wrapDateLine = false;

        // reuse Bing resolutions 0-21 as client layer resolutions
        var resolutions = OpenLayers.Layer.Bing.prototype.serverResolutions;
        // OSM zoom levels: 0-18
        var serverResolutions = resolutions.slice(0, 19);

        var osm = new OpenLayers.Layer.OSM(null, null, {
            wrapDateLine: false,
            opacity : 0.2,
            resolutions : resolutions,
            serverResolutions : serverResolutions
        });
        osm.attribution = 'tiles ' + osm.attribution;
        map.addLayer(osm);

        // empty layer (~ no base layer)
        map.addLayer(new OpenLayers.Layer("blank", {
            isBaseLayer : true
        }));
    }

    function init() {
        OpenLayers.ImgPath = "lib/openlayers/img/";

        var styleMaps;
        var options = {
            div : "map_div",
            // disable theme CSS auto-loading, causes error with OpenLayers.js.gz
            // (requires exact name match of OpenLayers.js script URL)
            theme : null,
            projection : "EPSG:900913",
            displayProjection : displayProjection,
            controls : []
        };

        map = new OpenLayers.Map(options);

        var cAttribution = new OpenLayers.Control.Attribution();
        cAttribution.template = "achavi [attic]&nbsp&nbsp-&nbsp&nbsp${layers}";
        map.addControl(cAttribution);
        // map.addControl(new OpenLayers.Control.LayerSwitcher());
        map.addControl(new OpenLayers.Control.LayerSwitcherBorder());
        map.addControl(new OpenLayers.Control.Navigation());
        map.addControl(new OpenLayers.Control.Zoom());
        // map.addControl(new OpenLayers.Control.PanZoomBar());
        map.addControl(new OpenLayers.Control.Permalink());
        map.addControl(new OpenLayers.Control.MousePosition());

        addBaseLayers(map);

        var styler = new OSMChangeStyle(map);
        styleMaps = styler.getStyleMaps();

        changesets = new OpenLayers.Layer.Vector("changeset", {
            projection : map.displayProjection,
            visibility : true,
            styleMap : styleMaps.changes,
            renderers : renderers
        });

        old = new OpenLayers.Layer.Vector("old", {
            projection : map.displayProjection,
            visibility : true,
            styleMap : styleMaps.old,
            renderers : renderers,
            rendererOptions: { zIndexing: true }
        });

        changes = new OpenLayers.Layer.Vector("changes", {
            projection : map.displayProjection,
            visibility : true,
            styleMap : styleMaps.changes,
            renderers : renderers,
            rendererOptions: { zIndexing: true },
            attribution: 'data © OpenStreetMap contributors, '
                + 'licensed under ODbL'
        });

        status = new Status();
        loading = new Loading();
        loader = new Loader(map, { changes: changes, old: old, changesets: changesets }, status);
        var parameters = OpenLayers.Control.ArgParser.prototype.getParameters();
        if (parameters.url) {
            // load augmented change file passed as 'url' parameter
            loader.GET({url: parameters.url, zoomToExtent: true});
        }
        if (parameters.changeset) {
            loadChangeset(parameters.changeset);
        }

        map.addLayer(changesets);
        map.addLayer(old);
        map.addLayer(changes);

        var vectorLayers = [ old, changes, changesets ];
        addControls(map, vectorLayers);

        // hover + select
        var handler = new PopupHandler(map, old, changes);
        var options = {
            onSelect : function(feature) {
                // also pass mouse position of the event
                // (note: OpenLayers.Handler.evt is not an API property)
                // see http://trac.osgeo.org/openlayers/ticket/2089
                handler.onFeatureSelect(feature, this.handlers.feature.evt.xy, this.hover);
            },
            onUnselect : handler.onFeatureUnselect
        };

        hover = new OpenLayers.Control.HoverAndSelectFeature(vectorLayers, options);
        map.addControl(hover);
        hover.activate();

        if (!map.getCenter()) {
            if (old.features.length > 0) {
                map.zoomToExtent(old.getDataExtent());
            } else if (changes.features.length > 0) {
                map.zoomToExtent(changes.getDataExtent());
            } else {
                //map.zoomToMaxExtent();
                map.setCenter(new OpenLayers.LonLat(0,0), 2);
            }

            // map.setCenter(new OpenLayers.LonLat(lon,
            // lat).transform("EPSG:4326", map.getProjectionObject()), zoom);
        }
    }

    function addBottomControls() {
        var toggle = function(e) {
            var prefix = this.id.substring(0, this.id.indexOf('_')) + '_';
            document.getElementById(prefix + 'content').classList.toggle('hidden');
            document.getElementById(prefix + 'button').classList.toggle('hidden');
        };

        var bottomButtons = ['legend', 'help', 'about'];
        bottomButtons.forEach(function(id) {
            document.getElementById(id + '_minimize').onclick = toggle;
            document.getElementById(id + '_button').onclick = toggle;
        });
    }

    function addBBoxControl(map, bboxChangeCallback) {

        var col = 'rgba(255, 255, 255, 0.4)';
        bbox.style['default'].strokeColor = col;
        bbox.style['transform'].strokeColor = col;
        bbox.style['temporary'].strokeColor = col;

        // bbox vector layer for drawing
        var bboxLayer = new OpenLayers.Layer.Vector("bbox", {
            styleMap: bbox.createStyleMap(),
            // for now, hide layer by default, because bbox select control disables/interferes with main select control
            visibility: false
        });
        map.addLayer(bboxLayer);

        // bbox control
        bbox.addControls(map, bboxLayer, {
            update : bboxChangeCallback,
            activate : function() {
                // reset when new box is drawn (set bbox to null)
                bboxChangeCallback(null);
                document.getElementById('bbox_button').classList.add('button_active');
            },
            deactivate : function() {
                document.getElementById('bbox_button').classList.remove('button_active');
            }
        });

        var onBBoxClick = function(e) {
            bbox.switchActive();
        };
        document.getElementById('bbox_button').onclick = onBBoxClick;

        return bbox;
    }

    function addControls(map, layers) {

        addBottomControls();

        var bboxChangeHandler = function(bbox) {
            overpassAPI.bbox = bbox;
        };
        var bboxControl = addBBoxControl(map, bboxChangeHandler);

        overpassAPI = new OverpassAPI(loader, bboxControl);
        new Live(overpassAPI, status);
        new Diff(overpassAPI, loading, status);

        var onClearClick = function(e) {
            for (var i = 0; i < layers.length; i++) {
                layers[i].removeAllFeatures();
            }
            status.reset();
        };
        document.getElementById('clear_button').onclick = onClearClick;

        var fileReaderControl = new FileReaderControl(_.bind(loader.handleLoad, loader));
        fileReaderControl.addUrlHandler(overpassAPI.sequenceUrlRegex, _.bind(overpassAPI.loadByUrl, overpassAPI));
        changesetRegex = /.*\/changeset\/([0-9]*)/;
        fileReaderControl.addUrlHandler(changesetRegex, loadChangesetByUrl);
        fileReaderControl.activate();
    }

    function loadChangesetByUrl(url) {
        var id = parseInt(url.replace(changesetRegex, "$1"));
        loadChangeset(id);
    }

    function loadChangeset(id) {

        function handleDiff() {
            loading.loadEnd();
        }

        function handleChangeset() {
            var csFeature, cs, bbox, xhr, from, to;

            csFeature = changesets.getFeatureByFid('changeset.' + id);
            if (csFeature) {
                cs = csFeature.attributes;
                from = cs.created_at;
                to = cs.closed_at;
                bbox = new OpenLayers.Bounds(cs.min_lon, cs.min_lat, cs.max_lon, cs.max_lat);
                overpassAPI.bbox = bbox;

                // TODO relations (checkbox?)
                xhr = overpassAPI.loadDiff(from, to, false, handleDiff);
                loading.loadStart(xhr);
            }
        }

        loader.GET({
            url: 'http://www.openstreetmap.org/api/0.6/changeset/' + id,
            zoomToExtent: true,
            postLoadCallback: handleChangeset
        });
    }

    init();
})();
<commit_msg>workaround for equal start/end time in changesets (subtract on second)<commit_after>(function() {

    var displayProjection = "EPSG:4326";

    var hover;
    var renderers = [ "SVG" ]; // Canvas

    var status,
        loader,
        loading,
        overpassAPI,
        changesetRegex;

    function addBaseLayers(map) {

        //OpenLayers.Layer.OSM.wrapDateLine = false;

        // reuse Bing resolutions 0-21 as client layer resolutions
        var resolutions = OpenLayers.Layer.Bing.prototype.serverResolutions;
        // OSM zoom levels: 0-18
        var serverResolutions = resolutions.slice(0, 19);

        var osm = new OpenLayers.Layer.OSM(null, null, {
            wrapDateLine: false,
            opacity : 0.2,
            resolutions : resolutions,
            serverResolutions : serverResolutions
        });
        osm.attribution = 'tiles ' + osm.attribution;
        map.addLayer(osm);

        // empty layer (~ no base layer)
        map.addLayer(new OpenLayers.Layer("blank", {
            isBaseLayer : true
        }));
    }

    function init() {
        OpenLayers.ImgPath = "lib/openlayers/img/";

        var styleMaps;
        var options = {
            div : "map_div",
            // disable theme CSS auto-loading, causes error with OpenLayers.js.gz
            // (requires exact name match of OpenLayers.js script URL)
            theme : null,
            projection : "EPSG:900913",
            displayProjection : displayProjection,
            controls : []
        };

        map = new OpenLayers.Map(options);

        var cAttribution = new OpenLayers.Control.Attribution();
        cAttribution.template = "achavi [attic]&nbsp&nbsp-&nbsp&nbsp${layers}";
        map.addControl(cAttribution);
        // map.addControl(new OpenLayers.Control.LayerSwitcher());
        map.addControl(new OpenLayers.Control.LayerSwitcherBorder());
        map.addControl(new OpenLayers.Control.Navigation());
        map.addControl(new OpenLayers.Control.Zoom());
        // map.addControl(new OpenLayers.Control.PanZoomBar());
        map.addControl(new OpenLayers.Control.Permalink());
        map.addControl(new OpenLayers.Control.MousePosition());

        addBaseLayers(map);

        var styler = new OSMChangeStyle(map);
        styleMaps = styler.getStyleMaps();

        changesets = new OpenLayers.Layer.Vector("changeset", {
            projection : map.displayProjection,
            visibility : true,
            styleMap : styleMaps.changes,
            renderers : renderers
        });

        old = new OpenLayers.Layer.Vector("old", {
            projection : map.displayProjection,
            visibility : true,
            styleMap : styleMaps.old,
            renderers : renderers,
            rendererOptions: { zIndexing: true }
        });

        changes = new OpenLayers.Layer.Vector("changes", {
            projection : map.displayProjection,
            visibility : true,
            styleMap : styleMaps.changes,
            renderers : renderers,
            rendererOptions: { zIndexing: true },
            attribution: 'data © OpenStreetMap contributors, '
                + 'licensed under ODbL'
        });

        status = new Status();
        loading = new Loading();
        loader = new Loader(map, { changes: changes, old: old, changesets: changesets }, status);
        var parameters = OpenLayers.Control.ArgParser.prototype.getParameters();
        if (parameters.url) {
            // load augmented change file passed as 'url' parameter
            loader.GET({url: parameters.url, zoomToExtent: true});
        }
        if (parameters.changeset) {
            loadChangeset(parameters.changeset);
        }

        map.addLayer(changesets);
        map.addLayer(old);
        map.addLayer(changes);

        var vectorLayers = [ old, changes, changesets ];
        addControls(map, vectorLayers);

        // hover + select
        var handler = new PopupHandler(map, old, changes);
        var options = {
            onSelect : function(feature) {
                // also pass mouse position of the event
                // (note: OpenLayers.Handler.evt is not an API property)
                // see http://trac.osgeo.org/openlayers/ticket/2089
                handler.onFeatureSelect(feature, this.handlers.feature.evt.xy, this.hover);
            },
            onUnselect : handler.onFeatureUnselect
        };

        hover = new OpenLayers.Control.HoverAndSelectFeature(vectorLayers, options);
        map.addControl(hover);
        hover.activate();

        if (!map.getCenter()) {
            if (old.features.length > 0) {
                map.zoomToExtent(old.getDataExtent());
            } else if (changes.features.length > 0) {
                map.zoomToExtent(changes.getDataExtent());
            } else {
                //map.zoomToMaxExtent();
                map.setCenter(new OpenLayers.LonLat(0,0), 2);
            }

            // map.setCenter(new OpenLayers.LonLat(lon,
            // lat).transform("EPSG:4326", map.getProjectionObject()), zoom);
        }
    }

    function addBottomControls() {
        var toggle = function(e) {
            var prefix = this.id.substring(0, this.id.indexOf('_')) + '_';
            document.getElementById(prefix + 'content').classList.toggle('hidden');
            document.getElementById(prefix + 'button').classList.toggle('hidden');
        };

        var bottomButtons = ['legend', 'help', 'about'];
        bottomButtons.forEach(function(id) {
            document.getElementById(id + '_minimize').onclick = toggle;
            document.getElementById(id + '_button').onclick = toggle;
        });
    }

    function addBBoxControl(map, bboxChangeCallback) {

        var col = 'rgba(255, 255, 255, 0.4)';
        bbox.style['default'].strokeColor = col;
        bbox.style['transform'].strokeColor = col;
        bbox.style['temporary'].strokeColor = col;

        // bbox vector layer for drawing
        var bboxLayer = new OpenLayers.Layer.Vector("bbox", {
            styleMap: bbox.createStyleMap(),
            // for now, hide layer by default, because bbox select control disables/interferes with main select control
            visibility: false
        });
        map.addLayer(bboxLayer);

        // bbox control
        bbox.addControls(map, bboxLayer, {
            update : bboxChangeCallback,
            activate : function() {
                // reset when new box is drawn (set bbox to null)
                bboxChangeCallback(null);
                document.getElementById('bbox_button').classList.add('button_active');
            },
            deactivate : function() {
                document.getElementById('bbox_button').classList.remove('button_active');
            }
        });

        var onBBoxClick = function(e) {
            bbox.switchActive();
        };
        document.getElementById('bbox_button').onclick = onBBoxClick;

        return bbox;
    }

    function addControls(map, layers) {

        addBottomControls();

        var bboxChangeHandler = function(bbox) {
            overpassAPI.bbox = bbox;
        };
        var bboxControl = addBBoxControl(map, bboxChangeHandler);

        overpassAPI = new OverpassAPI(loader, bboxControl);
        new Live(overpassAPI, status);
        new Diff(overpassAPI, loading, status);

        var onClearClick = function(e) {
            for (var i = 0; i < layers.length; i++) {
                layers[i].removeAllFeatures();
            }
            status.reset();
        };
        document.getElementById('clear_button').onclick = onClearClick;

        var fileReaderControl = new FileReaderControl(_.bind(loader.handleLoad, loader));
        fileReaderControl.addUrlHandler(overpassAPI.sequenceUrlRegex, _.bind(overpassAPI.loadByUrl, overpassAPI));
        changesetRegex = /.*\/changeset\/([0-9]*)/;
        fileReaderControl.addUrlHandler(changesetRegex, loadChangesetByUrl);
        fileReaderControl.activate();
    }

    function loadChangesetByUrl(url) {
        var id = parseInt(url.replace(changesetRegex, "$1"));
        loadChangeset(id);
    }

    function loadChangeset(id) {

        function handleDiff() {
            loading.loadEnd();
        }

        function handleChangeset() {
            var csFeature, cs, bbox, xhr, from, to;

            csFeature = changesets.getFeatureByFid('changeset.' + id);
            if (csFeature) {
                cs = csFeature.attributes;
                from = cs.created_at;
                to = cs.closed_at;
                if (from) {
                    // workaround: no result when same start and end date (e.g. changeset 23168078), not sure if needed generally
                    // https://github.com/drolbr/Overpass-API/issues/113
                    from = moment(cs.closed_at, 'YYYY-MM-DDTHH:mm:ss\\Z').subtract('seconds', 1).format('YYYY-MM-DDTHH:mm:ss\\Z');
                }
                bbox = new OpenLayers.Bounds(cs.min_lon, cs.min_lat, cs.max_lon, cs.max_lat);
                overpassAPI.bbox = bbox;

                // TODO relations (checkbox?)
                xhr = overpassAPI.loadDiff(from, to, false, handleDiff);
                loading.loadStart(xhr);
            }
        }

        loader.GET({
            url: 'http://www.openstreetmap.org/api/0.6/changeset/' + id,
            zoomToExtent: true,
            postLoadCallback: handleChangeset
        });
    }

    init();
})();
<|endoftext|>


<commit_before>'use strict';

const Discord = require('discord.js');

const { readRss } = require('../../../utils/rss');
const { convertHtmlToMarkdown } = require('../../../utils/markdown');

const Worker = require('../../../../bot/modules/Worker');

const thumbnailFolder = './resources/';
const thumbnailFile = 'blog-post.png';
const rssFeed = 'https://www.guildwars2.com/en/feed/';

class WorkerBlogPostChecker extends Worker {
    constructor(bot) {
        super(bot, 'blog-post-checker');
        this._localizerNamespaces = 'module.guildwars2';
    }

    async check() {
        try {
            const [oldBlogUrl, blog] = await Promise.all([
                this.getLatestBlog(),
                this._checkBlog()
            ]);

            const allBlogs = [];
            for (let i = 0; i < blog.length; i++) {
                if (blog[i].link === oldBlogUrl) {
                    break;
                }
                allBlogs.push(blog[i]);
            }

            if (allBlogs.length > 0) {
                // Set the latest blog to the newest blog
                await this.setLatestBlog(allBlogs[0].link);
                if (oldBlogUrl) {
                    // Signal all new blog posts, in reversed order
                    for (let i = allBlogs.length - 1; i >= 0; i--) {
                        await this.onNewBlog(allBlogs[i]); // eslint-disable-line no-await-in-loop
                    }
                }
            }
        } catch (err) {
            this.log(`Error while checking for new blog posts: ${err.message}`, 'error');
        }
    }

    async _checkBlog() {
        const feed = await readRss(rssFeed);
        this.log(`Got ${feed.items.length} blog posts`, 'log');
        return feed.items;
    }

    async onNewBlog(blog) {
        const bot = this.getBot();
        const config = this.getConfig();
        const client = bot.getClient();
        const l = bot.getLocalizer();

        const time = new Date(blog.pubDate);
        this.log(`New blog post: ${blog.link}`);
        this.emit('new-blog', { blog, time });

        const channelId = config.get('channel-id');
        let channel;
        if (channelId && (channel = client.channels.get(channelId)) && channel.type === 'text') {
            const description = convertHtmlToMarkdown(blog.summary, 'feed')
                .replace(/\[read more]\([^)]+\)/i, '');

            return channel.send('', {
                embed: new Discord.RichEmbed()
                    .setColor(config.get('richcolor'))
                    .attachFile(`${thumbnailFolder}${thumbnailFile}`)
                    .setThumbnail(`attachment://${thumbnailFile}`)
                    .setAuthor(blog.author)
                    .setURL(blog.link)
                    .setTitle(l.t('module.guildwars2:blog-post-checker.post-title', { title: blog.title }))
                    .setDescription(l.t('module.guildwars2:blog-post-checker.post-description', { description }))
                    .setTimestamp(time)
            });
        }

        this.log(`Invalid channel ${channelId}`, 'error');
    }

    getLatestBlog() {
        return this.getBot().getCache().get(this.getId(), 'blog');
    }

    setLatestBlog(blog) {
        return this.getBot().getCache().set(this.getId(), 'blog', undefined, blog);
    }

    async enableWorker() {
        this._intervalId = setInterval(this.check.bind(this), 600000);
        setTimeout(this.check.bind(this), 1000);
    }

    async disableWorker() {
        if (this._intervalId) {
            clearInterval(this._intervalId);
        }
    }
}

module.exports = WorkerBlogPostChecker;
<commit_msg>Change blog post checking based on date instead of URL<commit_after>'use strict';

const Discord = require('discord.js');

const { readRss } = require('../../../utils/rss');
const { convertHtmlToMarkdown } = require('../../../utils/markdown');

const Worker = require('../../../../bot/modules/Worker');

const thumbnailFolder = './resources/';
const thumbnailFile = 'blog-post.png';
const rssFeed = 'https://www.guildwars2.com/en/feed/';

class WorkerBlogPostChecker extends Worker {
    constructor(bot) {
        super(bot, 'blog-post-checker');
        this._localizerNamespaces = 'module.guildwars2';
    }

    async check() {
        try {
            const [oldBlogDate, blog] = await Promise.all([
                this.getLatestBlog().then(d => new Date(d)),
                this._checkBlog()
            ]);

            const posts = blog.filter(b => new Date(b.pubDate) > oldBlogDate);
            if (posts.length > 0) {
                // Set the latest blog to the newest blog
                const latestDate = Math.max(...posts.map(p => new Date(p.pubDate).getTime()));
                await this.setLatestBlog(latestDate);
                if (oldBlogDate) {
                    // Signal all new blog posts, in reversed order
                    for (let i = posts.length - 1; i >= 0; i--) {
                        await this.onNewBlog(posts[i]); // eslint-disable-line no-await-in-loop
                    }
                }
            }
        } catch (err) {
            this.log(`Error while checking for new blog posts: ${err.message}`, 'error');
        }
    }

    async _checkBlog() {
        const feed = await readRss(rssFeed);
        this.log(`Got ${feed.items.length} blog posts`, 'log');
        return feed.items;
    }

    async onNewBlog(blog) {
        const bot = this.getBot();
        const config = this.getConfig();
        const client = bot.getClient();
        const l = bot.getLocalizer();

        const time = new Date(blog.pubDate);
        this.log(`New blog post: ${blog.link}`);
        this.emit('new-blog', { blog, time });

        const channelId = config.get('channel-id');
        let channel;
        if (channelId && (channel = client.channels.get(channelId)) && channel.type === 'text') {
            const description = convertHtmlToMarkdown(blog.summary, 'feed')
                .replace(/\[read more]\([^)]+\)/i, '');

            return channel.send('', {
                embed: new Discord.RichEmbed()
                    .setColor(config.get('richcolor'))
                    .attachFile(`${thumbnailFolder}${thumbnailFile}`)
                    .setThumbnail(`attachment://${thumbnailFile}`)
                    .setAuthor(blog.author)
                    .setURL(blog.link)
                    .setTitle(l.t('module.guildwars2:blog-post-checker.post-title', { title: blog.title }))
                    .setDescription(l.t('module.guildwars2:blog-post-checker.post-description', { description }))
                    .setTimestamp(time)
            });
        }

        this.log(`Invalid channel ${channelId}`, 'error');
    }

    async getLatestBlog() {
        return parseInt(await this.getBot().getCache().get(this.getId(), 'blog'), 10);
    }

    setLatestBlog(blog) {
        return this.getBot().getCache().set(this.getId(), 'blog', undefined, blog);
    }

    async enableWorker() {
        this._intervalId = setInterval(this.check.bind(this), 600000);
        setTimeout(this.check.bind(this), 1000);
    }

    async disableWorker() {
        if (this._intervalId) {
            clearInterval(this._intervalId);
        }
    }
}

module.exports = WorkerBlogPostChecker;
<|endoftext|>


<commit_before>/**
 * Sockpress Module
 *
 * Inspired by https://gist.github.com/bobbydavid/2640463
 */

'use strict';

var express = require('express'),
    session = require('express-session'),
    io      = require('socket.io'),
    Routing = require('./routing'),
    Route   = require('./route');

module.exports.init = function (app, options) {
  if(!app && !options) {
    options = {};
  }
  else if(!options) {
    options = app;
    app = undefined;
  }

  var _app, _server;

  //Defaults values for options
  options.name = options.name || 'sockpress.id';
  options.store = options.store || new session.MemoryStore();
  options.resave = !!options.resave;
  options.saveUninitialized = !!options.saveUninitialized;

  _app = app || express(); // Load an express instance
  _app.express = express; // Expose raw express object for middlewares

  // Load http or https nodejs server
  if (!options.https) {
    _server = require('http').createServer(_app);
  } else {
    _server = require('https').createServer(options.https, _app);
  }

  if (!options.disableSession) {
    _app.use(session(options)); // Enable session support in express
  }

  _app.io = io(_server);
  _app.rawServer = _server;

  // Init IO Routing
  var _routing = new Routing(_app, options);

  // Expose new methods :

  /**
   * Start express and socket.io applications.
   * @params : see http://nodejs.org/api/http.html#http_server_listen_port_hostname_backlog_callback
   */
  _app.listen = function () {
    if (!_app.io.hasListeners) {
      _routing.addListeners(_app.io);
      _app.io.hasListeners = true;
    }

    return _server.listen.apply(_server, arguments);
  };

  /**
   * Add a new socket.io route (convenience method)
   * @param  {String}   [namespace] socket.io namespace. Defaults to "/".
   * @param  {String}   eventName   the event name (should be unique)
   * @param  {Function} fn          callback function or instance of Router
   */
  _app.io.route = function (namespace, eventName, fn) {
    _routing.add(namespace, eventName, fn);
  };

  // Expose Route object to create mountable sub-routes
  _app.io.Route = Route;

  return _app;

};
<commit_msg>Revert "fix incorrect boolean check"<commit_after>/**
 * Sockpress Module
 *
 * Inspired by https://gist.github.com/bobbydavid/2640463
 */

'use strict';

var express = require('express'),
    session = require('express-session'),
    io      = require('socket.io'),
    Routing = require('./routing'),
    Route   = require('./route');

module.exports.init = function (app, options) {
  if(!app && !options) {
    options = {};
  }
  else if(!options) {
    options = app;
    app = undefined;
  }

  var _app, _server;

  //Defaults values for options
  options.name = options.name || 'sockpress.id';
  options.store = options.store || new session.MemoryStore();
  options.resave = options.resave === undefined ? true : false;
  options.saveUninitialized = options.saveUninitialized === undefined ? true : false;

  _app = app || express(); // Load an express instance
  _app.express = express; // Expose raw express object for middlewares

  // Load http or https nodejs server
  if (!options.https) {
    _server = require('http').createServer(_app);
  } else {
    _server = require('https').createServer(options.https, _app);
  }

  if (!options.disableSession) {
    _app.use(session(options)); // Enable session support in express
  }

  _app.io = io(_server);
  _app.rawServer = _server;

  // Init IO Routing
  var _routing = new Routing(_app, options);

  // Expose new methods :

  /**
   * Start express and socket.io applications.
   * @params : see http://nodejs.org/api/http.html#http_server_listen_port_hostname_backlog_callback
   */
  _app.listen = function () {
    if (!_app.io.hasListeners) {
      _routing.addListeners(_app.io);
      _app.io.hasListeners = true;
    }

    return _server.listen.apply(_server, arguments);
  };

  /**
   * Add a new socket.io route (convenience method)
   * @param  {String}   [namespace] socket.io namespace. Defaults to "/".
   * @param  {String}   eventName   the event name (should be unique)
   * @param  {Function} fn          callback function or instance of Router
   */
  _app.io.route = function (namespace, eventName, fn) {
    _routing.add(namespace, eventName, fn);
  };

  // Expose Route object to create mountable sub-routes
  _app.io.Route = Route;

  return _app;

};
<|endoftext|>


<commit_before>/**
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

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

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
*/

var fs            = require('fs'),
    path          = require('path'),
    et            = require('elementtree'),
    xml           = require('cordova-common').xmlHelpers,
    util          = require('../util'),
    events        = require('cordova-common').events,
    shell         = require('shelljs'),
    Q             = require('q'),
    Parser        = require('./parser'),
    ConfigParser = require('cordova-common').ConfigParser,
    CordovaError = require('cordova-common').CordovaError;

function android_parser(project) {
    if (!fs.existsSync(path.join(project, 'AndroidManifest.xml'))) {
        throw new CordovaError('The provided path "' + project + '" is not an Android project.');
    }

    // Call the base class constructor
    Parser.call(this, 'android', project);

    this.path = project;
    this.strings = path.join(this.path, 'res', 'values', 'strings.xml');
    this.manifest = path.join(this.path, 'AndroidManifest.xml');
    this.android_config = path.join(this.path, 'res', 'xml', 'config.xml');
}

require('util').inherits(android_parser, Parser);

module.exports = android_parser;

android_parser.prototype.findAndroidLaunchModePreference = function(config) {
    var launchMode = config.getPreference('AndroidLaunchMode');
    if (!launchMode) {
        // Return a default value
        return 'singleTop';
    }

    var expectedValues = ['standard', 'singleTop', 'singleTask', 'singleInstance'];
    var valid = expectedValues.indexOf(launchMode) !== -1;
    if (!valid) {
        events.emit('warn', 'Unrecognized value for AndroidLaunchMode preference: ' + launchMode);
        events.emit('warn', '  Expected values are: ' + expectedValues.join(', '));
        // Note: warn, but leave the launch mode as developer wanted, in case the list of options changes in the future
    }

    return launchMode;
};

// remove the default resource name from all drawable folders
// return the array of the densities in this project
android_parser.prototype.deleteDefaultResource = function(name) {
    var res = path.join(this.path, 'res');
    var dirs = fs.readdirSync(res);

    for (var i=0; i<dirs.length; i++) {
        var filename = dirs[i];
        if (filename.indexOf('drawable-') === 0) {
            var imgPath = path.join(res, filename, name);
            if (fs.existsSync(imgPath)) {
                shell.chmod('u+w', imgPath);
                fs.unlinkSync(imgPath);
                events.emit('verbose', 'deleted: ' + imgPath);
            }
            imgPath = imgPath.replace(/\.png$/, '.9.png');
            if (fs.existsSync(imgPath)) {
                shell.chmod('u+w', imgPath);
                fs.unlinkSync(imgPath);
                events.emit('verbose', 'deleted: ' + imgPath);
            }
        }
    }
};

android_parser.prototype.copyImage = function(src, density, name) {
    var destFolder = path.join(this.path, 'res', (density ? 'drawable-': 'drawable') + density);
    var isNinePatch = !!/\.9\.png$/.exec(src);
    var ninePatchName = name.replace(/\.png$/, '.9.png');

    // default template does not have default asset for this density
    if (!fs.existsSync(destFolder)) {
        fs.mkdirSync(destFolder);
    }

    var destFilePath = path.join(destFolder, isNinePatch ? ninePatchName : name);
    events.emit('verbose', 'copying image from ' + src + ' to ' + destFilePath);
    shell.cp('-f', src, destFilePath);
};

android_parser.prototype.handleSplashes = function(config) {
    var resources = config.getSplashScreens('android');
    var me = this;
    // if there are "splash" elements in config.xml
    if (resources.length > 0) {
        this.deleteDefaultResource('screen.png');
        events.emit('verbose', 'splash screens: ' + JSON.stringify(resources));

        var projectRoot = util.isCordova(this.path);

        var hadMdpi = false;
        resources.forEach(function (resource) {
            if (!resource.density) {
                return;
            }
            if (resource.density == 'mdpi') {
                hadMdpi = true;
            }
            me.copyImage(path.join(projectRoot, resource.src), resource.density, 'screen.png');
        });
        // There's no "default" drawable, so assume default == mdpi.
        if (!hadMdpi && resources.defaultResource) {
            me.copyImage(path.join(projectRoot, resources.defaultResource.src), 'mdpi', 'screen.png');
        }
    }
};

android_parser.prototype.handleIcons = function(config) {
    var icons = config.getIcons('android');

    // if there are icon elements in config.xml
    if (icons.length === 0) {
        events.emit('verbose', 'This app does not have launcher icons defined');
        return;
    }

    this.deleteDefaultResource('icon.png');

    var android_icons = {};
    var default_icon;
    // http://developer.android.com/design/style/iconography.html
    var sizeToDensityMap = {
        36: 'ldpi',
        48: 'mdpi',
        72: 'hdpi',
        96: 'xhdpi',
        144: 'xxhdpi',
        192: 'xxxhdpi'
    };
    // find the best matching icon for a given density or size
    // @output android_icons
    var parseIcon = function(icon, icon_size) {
        // do I have a platform icon for that density already
        var density = icon.density || sizeToDensityMap[icon_size];
        if (!density) {
            // invalid icon defition ( or unsupported size)
            return;
        }
        var previous = android_icons[density];
        if (previous && previous.platform) {
            return;
        }
        android_icons[density] = icon;
    };

    // iterate over all icon elements to find the default icon and call parseIcon
    for (var i=0; i<icons.length; i++) {
        var icon = icons[i];
        var size = icon.width;
        if (!size) {
            size = icon.height;
        }
        if (!size && !icon.density) {
            if (default_icon) {
                events.emit('verbose', 'more than one default icon: ' + JSON.stringify(icon));
            } else {
                default_icon = icon;
            }
        } else {
            parseIcon(icon, size);
        }
    }
    var projectRoot = util.isCordova(this.path);
    for (var density in android_icons) {
        this.copyImage(path.join(projectRoot, android_icons[density].src), density, 'icon.png');
    }
    // There's no "default" drawable, so assume default == mdpi.
    if (default_icon && !android_icons.mdpi) {
        this.copyImage(path.join(projectRoot, default_icon.src), 'mdpi', 'icon.png');
    }
};

android_parser.prototype.update_from_config = function(config) {
    // TODO: share code for this func with Android. Or fix it and remove
    // the below JSHint hacks line.
    // jshint unused:false, indent:false, undef:true, loopfunc:true, shadow:true, quotmark:false
    if (config instanceof ConfigParser) {
    } else throw new Error('update_from_config requires a ConfigParser object');

    // Update app name by editing res/values/strings.xml
    var name = config.name();
    var strings = xml.parseElementtreeSync(this.strings);
    strings.find('string[@name="app_name"]').text = name;
    fs.writeFileSync(this.strings, strings.write({indent: 4}), 'utf-8');
    events.emit('verbose', 'Wrote out Android application name to "' + name + '"');

    this.handleSplashes(config);
    this.handleIcons(config);

    var manifest = xml.parseElementtreeSync(this.manifest);
    // Update the version by changing the AndroidManifest android:versionName
    var version = config.version();
    var versionCode = config.android_versionCode() || default_versionCode(version);
    manifest.getroot().attrib["android:versionName"] = version;
    manifest.getroot().attrib["android:versionCode"] = versionCode;

    // Update package name by changing the AndroidManifest id and moving the entry class around to the proper package directory
    var pkg = config.android_packageName() || config.packageName();
    pkg = pkg.replace(/-/g, '_'); // Java packages cannot support dashes
    var orig_pkg = manifest.getroot().attrib.package;
    manifest.getroot().attrib.package = pkg;

    var act = manifest.getroot().find('./application/activity');

    // Set the android:screenOrientation in the AndroidManifest
    var orientation = this.helper.getOrientation(config);

    if (orientation && !this.helper.isDefaultOrientation(orientation)) {
        act.attrib['android:screenOrientation'] = orientation;
    } else {
        delete act.attrib['android:screenOrientation'];
    }

    // Set android:launchMode in AndroidManifest
    var androidLaunchModePref = this.findAndroidLaunchModePreference(config);
    if (androidLaunchModePref) {
        act.attrib["android:launchMode"] = androidLaunchModePref;
    } else { // User has (explicitly) set an invalid value for AndroidLaunchMode preference
        delete act.attrib["android:launchMode"]; // use Android default value (standard)
    }

    // Set min/max/target SDK version
    //<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="19" ... />
    var usesSdk = manifest.getroot().find('./uses-sdk');
    ['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion'].forEach(function(sdkPrefName) {
        var sdkPrefValue = config.getPreference('android-' + sdkPrefName, 'android');
        if (!sdkPrefValue) return;

        if (!usesSdk) { // if there is no required uses-sdk element, we should create it first
            usesSdk = new et.Element('uses-sdk');
            manifest.getroot().append(usesSdk);
        }
        usesSdk.attrib['android:' + sdkPrefName] = sdkPrefValue;
    });

    // Write out AndroidManifest.xml
    fs.writeFileSync(this.manifest, manifest.write({indent: 4}), 'utf-8');

    var orig_pkgDir = path.join(this.path, 'src', path.join.apply(null, orig_pkg.split('.')));
    var java_files = fs.readdirSync(orig_pkgDir).filter(function(f) {
      return f.indexOf('.svn') == -1 && f.indexOf('.java') >= 0 && fs.readFileSync(path.join(orig_pkgDir, f), 'utf-8').match(/extends\s+CordovaActivity/);
    });
    if (java_files.length === 0) {
      throw new Error('No Java files found which extend CordovaActivity.');
    } else if(java_files.length > 1) {
      events.emit('log', 'Multiple candidate Java files (.java files which extend CordovaActivity) found. Guessing at the first one, ' + java_files[0]);
    }

    var orig_java_class = java_files[0];
    var pkgDir = path.join(this.path, 'src', path.join.apply(null, pkg.split('.')));
    shell.mkdir('-p', pkgDir);
    var orig_javs = path.join(orig_pkgDir, orig_java_class);
    var new_javs = path.join(pkgDir, orig_java_class);
    var javs_contents = fs.readFileSync(orig_javs, 'utf-8');
    javs_contents = javs_contents.replace(/package [\w\.]*;/, 'package ' + pkg + ';');
    events.emit('verbose', 'Wrote out Android package name to "' + pkg + '"');
    fs.writeFileSync(new_javs, javs_contents, 'utf-8');
    // remove the original if different from the new.
    if(orig_pkgDir !== pkgDir){
      shell.rm('-Rf',orig_javs);
      // remove any empty directories
      var curDir = path.dirname(orig_javs);
      while(curDir !== path.resolve(this.path, 'src')) {
          if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
              fs.rmdirSync(curDir);
              curDir = path.resolve(curDir, '..');
            } else {
              break;
            }
          }
    }
};

// Returns the platform-specific www directory.
android_parser.prototype.www_dir = function() {
    return path.join(this.path, 'assets', 'www');
};

android_parser.prototype.config_xml = function(){
    return this.android_config;
};

// Used for creating platform_www in projects created by older versions.
android_parser.prototype.cordovajs_path = function(libDir) {
    var jsPath = path.join(libDir, 'framework', 'assets', 'www', 'cordova.js');
    return path.resolve(jsPath);
};

android_parser.prototype.cordovajs_src_path = function(libDir) {
    var jsPath = path.join(libDir, 'cordova-js-src');
    return path.resolve(jsPath);
};

// Replace the www dir with contents of platform_www and app www.
android_parser.prototype.update_www = function() {
    var projectRoot = util.isCordova(this.path);
    var app_www = util.projectWww(projectRoot);
    var platform_www = path.join(this.path, 'platform_www');

    // Clear the www dir
    shell.rm('-rf', this.www_dir());
    shell.mkdir(this.www_dir());
    // Copy over all app www assets
    shell.cp('-rf', path.join(app_www, '*'), this.www_dir());
    // Copy over stock platform www assets (cordova.js)
    shell.cp('-rf', path.join(platform_www, '*'), this.www_dir());
};

// update the overrides folder into the www folder
android_parser.prototype.update_overrides = function() {
    var projectRoot = util.isCordova(this.path);
    var merges_path = path.join(util.appDir(projectRoot), 'merges', 'android');
    if (fs.existsSync(merges_path)) {
        var overrides = path.join(merges_path, '*');
        shell.cp('-rf', overrides, this.www_dir());
    }
};

// Returns a promise.
android_parser.prototype.update_project = function(cfg) {
    var platformWww = path.join(this.path, 'assets');
    try {
        this.update_from_config(cfg);
        this.update_overrides();
    } catch(e) {
        return Q.reject(e);
    }
    // delete any .svn folders copied over
    util.deleteSvnFolders(platformWww);
    return Q();
};

// Consturct the default value for versionCode as
// PATCH + MINOR * 100 + MAJOR * 10000
// see http://developer.android.com/tools/publishing/versioning.html
function default_versionCode(version) {
    var nums = version.split('-')[0].split('.');
    var versionCode = 0;
    if (+nums[0]) {
        versionCode += +nums[0] * 10000;
    }
    if (+nums[1]) {
        versionCode += +nums[1] * 100;
    }
    if (+nums[2]) {
        versionCode += +nums[2];
    }
    return versionCode;
}
<commit_msg>CB-8320: We look for a build.gradle to make sure it's Android, not an AndroidManifest, because it moved<commit_after>/**
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

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

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
*/

var fs            = require('fs'),
    path          = require('path'),
    et            = require('elementtree'),
    xml           = require('cordova-common').xmlHelpers,
    util          = require('../util'),
    events        = require('cordova-common').events,
    shell         = require('shelljs'),
    Q             = require('q'),
    Parser        = require('./parser'),
    ConfigParser = require('cordova-common').ConfigParser,
    CordovaError = require('cordova-common').CordovaError;

function android_parser(project) {
    if (!fs.existsSync(path.join(project, 'build.gradle'))) {
        throw new CordovaError('The provided path "' + project + '" is not an Android project.');
    }

    // Call the base class constructor
    Parser.call(this, 'android', project);

    this.path = project;

    //If we have a gradle file but not a manifest, this is an Android Studio project
    if(!fs.existsSync(path.join(project, "AndroidManifest.xml"))) {
      this.path = path.join(project, 'app', 'src', 'main');
    }

    this.strings = path.join(this.path, 'res', 'values', 'strings.xml');
    this.manifest = path.join(this.path, 'AndroidManifest.xml');
    this.android_config = path.join(this.path, 'res', 'xml', 'config.xml');
}

require('util').inherits(android_parser, Parser);

module.exports = android_parser;

android_parser.prototype.findAndroidLaunchModePreference = function(config) {
    var launchMode = config.getPreference('AndroidLaunchMode');
    if (!launchMode) {
        // Return a default value
        return 'singleTop';
    }

    var expectedValues = ['standard', 'singleTop', 'singleTask', 'singleInstance'];
    var valid = expectedValues.indexOf(launchMode) !== -1;
    if (!valid) {
        events.emit('warn', 'Unrecognized value for AndroidLaunchMode preference: ' + launchMode);
        events.emit('warn', '  Expected values are: ' + expectedValues.join(', '));
        // Note: warn, but leave the launch mode as developer wanted, in case the list of options changes in the future
    }

    return launchMode;
};

// remove the default resource name from all drawable folders
// return the array of the densities in this project
android_parser.prototype.deleteDefaultResource = function(name) {
    var res = path.join(this.path, 'res');
    var dirs = fs.readdirSync(res);

    for (var i=0; i<dirs.length; i++) {
        var filename = dirs[i];
        if (filename.indexOf('drawable-') === 0) {
            var imgPath = path.join(res, filename, name);
            if (fs.existsSync(imgPath)) {
                shell.chmod('u+w', imgPath);
                fs.unlinkSync(imgPath);
                events.emit('verbose', 'deleted: ' + imgPath);
            }
            imgPath = imgPath.replace(/\.png$/, '.9.png');
            if (fs.existsSync(imgPath)) {
                shell.chmod('u+w', imgPath);
                fs.unlinkSync(imgPath);
                events.emit('verbose', 'deleted: ' + imgPath);
            }
        }
    }
};

android_parser.prototype.copyImage = function(src, density, name) {
    var destFolder = path.join(this.path, 'res', (density ? 'drawable-': 'drawable') + density);
    var isNinePatch = !!/\.9\.png$/.exec(src);
    var ninePatchName = name.replace(/\.png$/, '.9.png');

    // default template does not have default asset for this density
    if (!fs.existsSync(destFolder)) {
        fs.mkdirSync(destFolder);
    }

    var destFilePath = path.join(destFolder, isNinePatch ? ninePatchName : name);
    events.emit('verbose', 'copying image from ' + src + ' to ' + destFilePath);
    shell.cp('-f', src, destFilePath);
};

android_parser.prototype.handleSplashes = function(config) {
    var resources = config.getSplashScreens('android');
    var me = this;
    // if there are "splash" elements in config.xml
    if (resources.length > 0) {
        this.deleteDefaultResource('screen.png');
        events.emit('verbose', 'splash screens: ' + JSON.stringify(resources));

        var projectRoot = util.isCordova(this.path);

        var hadMdpi = false;
        resources.forEach(function (resource) {
            if (!resource.density) {
                return;
            }
            if (resource.density == 'mdpi') {
                hadMdpi = true;
            }
            me.copyImage(path.join(projectRoot, resource.src), resource.density, 'screen.png');
        });
        // There's no "default" drawable, so assume default == mdpi.
        if (!hadMdpi && resources.defaultResource) {
            me.copyImage(path.join(projectRoot, resources.defaultResource.src), 'mdpi', 'screen.png');
        }
    }
};

android_parser.prototype.handleIcons = function(config) {
    var icons = config.getIcons('android');

    // if there are icon elements in config.xml
    if (icons.length === 0) {
        events.emit('verbose', 'This app does not have launcher icons defined');
        return;
    }

    this.deleteDefaultResource('icon.png');

    var android_icons = {};
    var default_icon;
    // http://developer.android.com/design/style/iconography.html
    var sizeToDensityMap = {
        36: 'ldpi',
        48: 'mdpi',
        72: 'hdpi',
        96: 'xhdpi',
        144: 'xxhdpi',
        192: 'xxxhdpi'
    };
    // find the best matching icon for a given density or size
    // @output android_icons
    var parseIcon = function(icon, icon_size) {
        // do I have a platform icon for that density already
        var density = icon.density || sizeToDensityMap[icon_size];
        if (!density) {
            // invalid icon defition ( or unsupported size)
            return;
        }
        var previous = android_icons[density];
        if (previous && previous.platform) {
            return;
        }
        android_icons[density] = icon;
    };

    // iterate over all icon elements to find the default icon and call parseIcon
    for (var i=0; i<icons.length; i++) {
        var icon = icons[i];
        var size = icon.width;
        if (!size) {
            size = icon.height;
        }
        if (!size && !icon.density) {
            if (default_icon) {
                events.emit('verbose', 'more than one default icon: ' + JSON.stringify(icon));
            } else {
                default_icon = icon;
            }
        } else {
            parseIcon(icon, size);
        }
    }
    var projectRoot = util.isCordova(this.path);
    for (var density in android_icons) {
        this.copyImage(path.join(projectRoot, android_icons[density].src), density, 'icon.png');
    }
    // There's no "default" drawable, so assume default == mdpi.
    if (default_icon && !android_icons.mdpi) {
        this.copyImage(path.join(projectRoot, default_icon.src), 'mdpi', 'icon.png');
    }
};

android_parser.prototype.update_from_config = function(config) {
    // TODO: share code for this func with Android. Or fix it and remove
    // the below JSHint hacks line.
    // jshint unused:false, indent:false, undef:true, loopfunc:true, shadow:true, quotmark:false
    if (config instanceof ConfigParser) {
    } else throw new Error('update_from_config requires a ConfigParser object');

    // Update app name by editing res/values/strings.xml
    var name = config.name();
    var strings = xml.parseElementtreeSync(this.strings);
    strings.find('string[@name="app_name"]').text = name;
    fs.writeFileSync(this.strings, strings.write({indent: 4}), 'utf-8');
    events.emit('verbose', 'Wrote out Android application name to "' + name + '"');

    this.handleSplashes(config);
    this.handleIcons(config);

    var manifest = xml.parseElementtreeSync(this.manifest);
    // Update the version by changing the AndroidManifest android:versionName
    var version = config.version();
    var versionCode = config.android_versionCode() || default_versionCode(version);
    manifest.getroot().attrib["android:versionName"] = version;
    manifest.getroot().attrib["android:versionCode"] = versionCode;

    // Update package name by changing the AndroidManifest id and moving the entry class around to the proper package directory
    var pkg = config.android_packageName() || config.packageName();
    pkg = pkg.replace(/-/g, '_'); // Java packages cannot support dashes
    var orig_pkg = manifest.getroot().attrib.package;
    manifest.getroot().attrib.package = pkg;

    var act = manifest.getroot().find('./application/activity');

    // Set the android:screenOrientation in the AndroidManifest
    var orientation = this.helper.getOrientation(config);

    if (orientation && !this.helper.isDefaultOrientation(orientation)) {
        act.attrib['android:screenOrientation'] = orientation;
    } else {
        delete act.attrib['android:screenOrientation'];
    }

    // Set android:launchMode in AndroidManifest
    var androidLaunchModePref = this.findAndroidLaunchModePreference(config);
    if (androidLaunchModePref) {
        act.attrib["android:launchMode"] = androidLaunchModePref;
    } else { // User has (explicitly) set an invalid value for AndroidLaunchMode preference
        delete act.attrib["android:launchMode"]; // use Android default value (standard)
    }

    // Set min/max/target SDK version
    //<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="19" ... />
    var usesSdk = manifest.getroot().find('./uses-sdk');
    ['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion'].forEach(function(sdkPrefName) {
        var sdkPrefValue = config.getPreference('android-' + sdkPrefName, 'android');
        if (!sdkPrefValue) return;

        if (!usesSdk) { // if there is no required uses-sdk element, we should create it first
            usesSdk = new et.Element('uses-sdk');
            manifest.getroot().append(usesSdk);
        }
        usesSdk.attrib['android:' + sdkPrefName] = sdkPrefValue;
    });

    // Write out AndroidManifest.xml
    fs.writeFileSync(this.manifest, manifest.write({indent: 4}), 'utf-8');

    var orig_pkgDir = path.join(this.path, 'src', path.join.apply(null, orig_pkg.split('.')));
    var java_files = fs.readdirSync(orig_pkgDir).filter(function(f) {
      return f.indexOf('.svn') == -1 && f.indexOf('.java') >= 0 && fs.readFileSync(path.join(orig_pkgDir, f), 'utf-8').match(/extends\s+CordovaActivity/);
    });
    if (java_files.length === 0) {
      throw new Error('No Java files found which extend CordovaActivity.');
    } else if(java_files.length > 1) {
      events.emit('log', 'Multiple candidate Java files (.java files which extend CordovaActivity) found. Guessing at the first one, ' + java_files[0]);
    }

    var orig_java_class = java_files[0];
    var pkgDir = path.join(this.path, 'src', path.join.apply(null, pkg.split('.')));
    shell.mkdir('-p', pkgDir);
    var orig_javs = path.join(orig_pkgDir, orig_java_class);
    var new_javs = path.join(pkgDir, orig_java_class);
    var javs_contents = fs.readFileSync(orig_javs, 'utf-8');
    javs_contents = javs_contents.replace(/package [\w\.]*;/, 'package ' + pkg + ';');
    events.emit('verbose', 'Wrote out Android package name to "' + pkg + '"');
    fs.writeFileSync(new_javs, javs_contents, 'utf-8');
    // remove the original if different from the new.
    if(orig_pkgDir !== pkgDir){
      shell.rm('-Rf',orig_javs);
      // remove any empty directories
      var curDir = path.dirname(orig_javs);
      while(curDir !== path.resolve(this.path, 'src')) {
          if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
              fs.rmdirSync(curDir);
              curDir = path.resolve(curDir, '..');
            } else {
              break;
            }
          }
    }
};

// Returns the platform-specific www directory.
android_parser.prototype.www_dir = function() {
    return path.join(this.path, 'assets', 'www');
};

android_parser.prototype.config_xml = function(){
    return this.android_config;
};

// Used for creating platform_www in projects created by older versions.
android_parser.prototype.cordovajs_path = function(libDir) {
    var jsPath = path.join(libDir, 'framework', 'assets', 'www', 'cordova.js');
    return path.resolve(jsPath);
};

android_parser.prototype.cordovajs_src_path = function(libDir) {
    var jsPath = path.join(libDir, 'cordova-js-src');
    return path.resolve(jsPath);
};

// Replace the www dir with contents of platform_www and app www.
android_parser.prototype.update_www = function() {
    var projectRoot = util.isCordova(this.path);
    var app_www = util.projectWww(projectRoot);
    var platform_www = path.join(this.path, 'platform_www');

    // Clear the www dir
    shell.rm('-rf', this.www_dir());
    shell.mkdir(this.www_dir());
    // Copy over all app www assets
    shell.cp('-rf', path.join(app_www, '*'), this.www_dir());
    // Copy over stock platform www assets (cordova.js)
    shell.cp('-rf', path.join(platform_www, '*'), this.www_dir());
};

// update the overrides folder into the www folder
android_parser.prototype.update_overrides = function() {
    var projectRoot = util.isCordova(this.path);
    var merges_path = path.join(util.appDir(projectRoot), 'merges', 'android');
    if (fs.existsSync(merges_path)) {
        var overrides = path.join(merges_path, '*');
        shell.cp('-rf', overrides, this.www_dir());
    }
};

// Returns a promise.
android_parser.prototype.update_project = function(cfg) {
    var platformWww = path.join(this.path, 'assets');
    try {
        this.update_from_config(cfg);
        this.update_overrides();
    } catch(e) {
        return Q.reject(e);
    }
    // delete any .svn folders copied over
    util.deleteSvnFolders(platformWww);
    return Q();
};

// Consturct the default value for versionCode as
// PATCH + MINOR * 100 + MAJOR * 10000
// see http://developer.android.com/tools/publishing/versioning.html
function default_versionCode(version) {
    var nums = version.split('-')[0].split('.');
    var versionCode = 0;
    if (+nums[0]) {
        versionCode += +nums[0] * 10000;
    }
    if (+nums[1]) {
        versionCode += +nums[1] * 100;
    }
    if (+nums[2]) {
        versionCode += +nums[2];
    }
    return versionCode;
}
<|endoftext|>


<commit_before>(function (window, undefined) {

    // test for REM unit support
    var cssremunit =  function() {
        var div = document.createElement( 'div' );
            div.style.cssText = 'font-size: 1rem;';

        return (/rem/).test(div.style.fontSize);
    },

    // filter returned link nodes for stylesheets
    isStyleSheet = function () {
        var styles = document.getElementsByTagName('link'),
            filteredStyles = [];

        for (i = 0; i < styles.length; i++) {
             if ( styles[i].rel === 'stylesheet' )
                filteredStyles.push( styles[i] );
        }

        return filteredStyles;
    },

    processSheets = function () {
        var links = [];
        sheets = isStyleSheet(); //search for link tags and confirm it's a stylesheet
        sheets.og = sheets.length; //store the original length of sheets as a property
        for( var i = 0; i < sheets.length; i++ ){
            links[i] = sheets[i].href;
            xhr( links[i], matchcss, i );
        }
    },

    matchcss = function ( response, i ) { //collect all of the rules from the xhr response texts and match them to a pattern
        var pattern = /[\w\d\.\,\:\[\]\^\>\/\<\+\~\|\-\_\$\#\"\'\/\*\\\=\s]+\{[\w\d\.\,\-\(\)\%\#\:\;\'\"\/\*\\\s]+\d*\.{0,1}\d+rem[\w\d\.\,\-\(\)\%\#\:\;\'\"\/\*\\\s]+\}/g, //find selectors that use rem in one or more of their rules
            current = response.responseText.match(pattern),
            remPattern =/\d*\.{0,1}\d+rem/g,
            remCurrent = response.responseText.match(remPattern);
        if( current !== null && current.length !== 0 ){
            found = found.concat( current ); //save all of the blocks of rules with rem in a property
            foundProps = foundProps.concat( remCurrent ); //save all of the properties with rem
        }
        if( i === sheets.og-1 ){
            buildIt();
        }
    },

    buildIt = function () { //first build each individual rule from elements in the found array and then add it to the string of rules.
        var pattern = /[\w\d\.\,\-\(\)\%\#\:\'\"\/\*\\\s]+\d*\.{0,1}\d+rem[\w\d\.\,\-\(\)\%\#\:\'\"\/\*\\\s]*;/g; //find properties with rem values in them
        for( var i = 0; i < found.length; i++ ){
            rules = rules + found[i].substr(0,found[i].indexOf("{")+1); //save the selector portion of each rule with a rem value
            var current = found[i].match( pattern );
            for( var j = 0; j<current.length; j++ ){ //build a new set of with only the selector and properties that have rem in the value
                rules = rules + current[j];
                if( j === current.length-1 ){
                    rules = rules + "\n}";
                }
            }
        }

        parseIt();
    },

    parseIt = function () { //replace each set of parentheses with evaluated content
        for( var i = 0; i < foundProps.length; i++ ){
            css[i] = Math.round( eval(foundProps[i].substr(0,foundProps[i].length-3)*fontSize) ) + 'px';
        }
        loadCSS();
    },

    loadCSS = function () { //replace and load the new rules
        for( var i = 0; i < css.length; i++ ){ //only run this loop as many times as css has entries
            if( css[i] ){
                rules = rules.replace( foundProps[i],css[i] ); //replace old rules with our processed rules
            }
        }
        rules = removeComments( rules );
        var remcss = document.createElement( 'style' );
        remcss.setAttribute( 'type', 'text/css' );
        remcss.id = 'remReplace';
        document.getElementsByTagName( 'head' )[0].appendChild( remcss );   //create the new element
        remcss.styleSheet.cssText = rules; // IE8 will not support innerHTML on read-only elements, such as STYLE
    },

    xhr = function ( url, callback, i ) { //create new XMLHttpRequest object and run it
        var xhr = getXMLHttpRequest();
        xhr.open( 'GET', url, true );
        xhr.send();
        xhr.onreadystatechange = function() {
            if ( xhr.readyState === 4 ){
                callback(xhr, i);
            } else { //callback function on AJAX error

            }
        };
    },

    removeComments =  function ( css ) {
        return css.replace(/\/\*[\w\d\.\,\[\]\^\>\<\+\~\|\-\_\$\#\"\'\/\*\\\=\s\{\}\(\)]*\*\//g, "");
    },

    getXMLHttpRequest = function () { //we're gonna check if our browser will let us use AJAX
        if(window.XMLHttpRequest){
            return new XMLHttpRequest();
        }
    };

    if( !cssremunit() ){ //this checks if the rem value is supported
        var rules = '', //initialize the rules variable in this scope so it can be used later
            sheets = [], //initialize the array holding the sheets for use later
            found = [], //initialize the array holding the found rules for use later
            foundProps = [], //initialize the array holding the found properties for use later
            css = [], //initialize the array holding the parsed rules for use later
            body = document.getElementsByTagName('body')[0],
            fontSize = '';
        if (body.currentStyle) {
            fontSize = (body.currentStyle['fontSize'].replace('%', '') / 100) * 16; //IE8 returns the percentage while other browsers return the computed value
        } else if (window.getComputedStyle)
            fontSize = document.defaultView.getComputedStyle(body, null).getPropertyValue('font-size').replace('px', ''); //find font-size in body element
        processSheets();
    } else {
        // do nothing, you are awesome and have REM support
    }

})(window);<commit_msg>Cleaned regular expressions and improved comment removal algorithm<commit_after>(function (window, undefined) {

    // test for REM unit support
    var cssremunit =  function() {
        var div = document.createElement( 'div' );
            div.style.cssText = 'font-size: 1rem;';

        return (/rem/).test(div.style.fontSize);
    },

    // filter returned link nodes for stylesheets
    isStyleSheet = function () {
        var styles = document.getElementsByTagName('link'),
            filteredStyles = [];

        for (i = 0; i < styles.length; i++) {
             if ( styles[i].rel === 'stylesheet' )
                filteredStyles.push( styles[i] );
        }

        return filteredStyles;
    },

    processSheets = function () {
        var links = [];
        sheets = isStyleSheet(); //search for link tags and confirm it's a stylesheet
        sheets.og = sheets.length; //store the original length of sheets as a property
        for( var i = 0; i < sheets.length; i++ ){
            links[i] = sheets[i].href;
            xhr( links[i], matchcss, i );
        }
    },

    matchcss = function ( response, i ) { //collect all of the rules from the xhr response texts and match them to a pattern
        var pattern = /[\w\d\s\-\/\\\[\]:,.'"*()<>+~%#^$_=|]+\{[\w\d\s\-\/\\%#:;,.'"*()]+\d*\.{0,1}\d+rem[\w\d\s\-\/\\%#:;,.'"*()]+\}/g, //find selectors that use rem in one or more of their rules
            current = response.responseText.match(pattern),
            remPattern =/\d*\.{0,1}\d+rem/g,
            remCurrent = response.responseText.match(remPattern);
        if( current !== null && current.length !== 0 ){
            found = found.concat( current ); //save all of the blocks of rules with rem in a property
            foundProps = foundProps.concat( remCurrent ); //save all of the properties with rem
        }
        if( i === sheets.og-1 ){
            buildIt();
        }
    },

    buildIt = function () { //first build each individual rule from elements in the found array and then add it to the string of rules.
        var pattern = /[\w\d\s\-\/\\%#:,.'"*()]+\d*\.{0,1}\d+rem[\w\d\s\-\/\\%#:,.'"*()]*;/g; //find properties with rem values in them
        for( var i = 0; i < found.length; i++ ){
            rules = rules + found[i].substr(0,found[i].indexOf("{")+1); //save the selector portion of each rule with a rem value
            var current = found[i].match( pattern );
            for( var j = 0; j<current.length; j++ ){ //build a new set of with only the selector and properties that have rem in the value
                rules = rules + current[j];
                if( j === current.length-1 ){
                    rules = rules + "\n}";
                }
            }
        }

        parseIt();
    },

    parseIt = function () { //replace each set of parentheses with evaluated content
        for( var i = 0; i < foundProps.length; i++ ){
            css[i] = Math.round( eval(foundProps[i].substr(0,foundProps[i].length-3)*fontSize) ) + 'px';
        }
        loadCSS();
    },

    loadCSS = function () { //replace and load the new rules
        for( var i = 0; i < css.length; i++ ){ //only run this loop as many times as css has entries
            if( css[i] ){
                rules = rules.replace( foundProps[i],css[i] ); //replace old rules with our processed rules
            }
        }
        rules = removeComments( rules );
        var remcss = document.createElement( 'style' );
        remcss.setAttribute( 'type', 'text/css' );
        remcss.id = 'remReplace';
        document.getElementsByTagName( 'head' )[0].appendChild( remcss );   //create the new element
        remcss.styleSheet.cssText = rules; // IE8 will not support innerHTML on read-only elements, such as STYLE
    },

    xhr = function ( url, callback, i ) { //create new XMLHttpRequest object and run it
        var xhr = getXMLHttpRequest();
        xhr.open( 'GET', url, true );
        xhr.send();
        xhr.onreadystatechange = function() {
            if ( xhr.readyState === 4 ){
                callback(xhr, i);
            } else { //callback function on AJAX error

            }
        };
    },

    removeComments = function ( css ) {
        var start = css.search(/\/\*/),
            end = css.search(/\*\//);
        if ( (start > -1) && (end > start) ) {
            css = css.substring(0, start) + css.substring(end + 2);
            return removeComments(css);
        }
        else {
            return css;
        }
    },

    getXMLHttpRequest = function () { //we're gonna check if our browser will let us use AJAX
        if(window.XMLHttpRequest){
            return new XMLHttpRequest();
        }
    };

    if( !cssremunit() ){ //this checks if the rem value is supported
        var rules = '', //initialize the rules variable in this scope so it can be used later
            sheets = [], //initialize the array holding the sheets for use later
            found = [], //initialize the array holding the found rules for use later
            foundProps = [], //initialize the array holding the found properties for use later
            css = [], //initialize the array holding the parsed rules for use later
            body = document.getElementsByTagName('body')[0],
            fontSize = '';
        if (body.currentStyle) {
            fontSize = (body.currentStyle['fontSize'].replace('%', '') / 100) * 16; //IE8 returns the percentage while other browsers return the computed value
        } else if (window.getComputedStyle)
            fontSize = document.defaultView.getComputedStyle(body, null).getPropertyValue('font-size').replace('px', ''); //find font-size in body element
        processSheets();
    } else {
        // do nothing, you are awesome and have REM support
    }

})(window);<|endoftext|>


<commit_before>/*!
 * Copyright 2013 Apereo Foundation (AF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

var fs = require('fs');
var Path = require('path');
var S3 = require('awssum-amazon-s3').S3;
var ShortId = require('shortid');
var util = require('util');

var Cassandra = require('oae-util/lib/cassandra');
var Config = require('oae-config').config('oae-content');
var IO = require('oae-util/lib/io');
var log = require('oae-logger').logger('amazon-storage');
var Revision = require('oae-content/lib/model').Revision;
var TempFile = require('oae-util/lib/tempfile');
var Validator = require('oae-util/lib/validator').Validator;

var BackendUtil = require('./util');
var ContentConstants = require('../constants').ContentConstants;
var DownloadStrategy = require('../model').DownloadStrategy;

//////////////////////
// Storage methods. //
//////////////////////

/**
 * @borrows Interface.store as Amazons3.store
 */
var store = module.exports.store = function(ctx, file, options, callback) {
    // Generate the uri for this file.
    // We can use this uri later to retrieve it.
    var uri = BackendUtil.generateUri(file, options);

    log().trace('Uploading %s to S3.', uri);

    var stream = fs.createReadStream(file.path);
    stream.once('error', function(err){
        IO.destroyStream(stream);
        log().error({'err': err}, 'Could not upload %s to S3', uri);
        callback({'code': 500, 'msg': err});
    });

    options = {
        'BucketName': _getBucketName(ctx),
        'ObjectName': uri,
        'ContentLength': file.size,
        'Body':  stream
    };

    // Uploads a file to S3.
    // See:
    //  * awssum: http://awssum.io/amazon/s3/put-object.html
    //  * Amazon S3 doc: http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectPUT.html
    _getClient(ctx).PutObject(options, function(err, data) {
        // Remove the file on disk.
        fs.unlink(file.path, function(unlinkError) {
            if (unlinkError) {
                log().warn({'err': unlinkError}, 'Could not remove the temporary file.');
                // We ignore the unlink error, as the file might've actually ended up on S3.
            }

            // Deal with the amazon response.
            if (err) {
                log().error({'err': err}, 'Could not upload to S3.');
                return callback(err);
            }
            callback(null, 'amazons3:' + uri);
        });
    });
};

/**
 * @borrows Interface.get as Amazons3.get
 */
var get = module.exports.get = function(ctx, uri, callback) {
    // Download it to a temp folder.
    var uriObj = BackendUtil.splitUri(uri);

    var filename = Path.basename(uriObj.location);
    var tmp = TempFile.createTempFile({'suffix': filename});
    var writeStream = fs.createWriteStream(tmp.path);

    writeStream.once('error', function(err){
        IO.destroyStream(writeStream);
        log().error({'err': err}, 'Could not save %s to disk', uri);
        callback({'code': 500, 'msg': err});
    });

    log().trace('Downloading %s from S3.', uriObj.location);

    var options = {
        'BucketName': _getBucketName(ctx),
        'ObjectName': uriObj.location
    };

    // Download an "object"(=file) to a temporary folder.
    // See:
    //  * Awssum doc: http://awssum.io/amazon/s3/get-object.html
    //  * Amazon S3 doc: http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGET.html
    // TODO: Replace with upcoming streaming download
    _getClient(ctx).GetObject(options, function(err, data) {
        if (err) {
            IO.destroyStream(writeStream);
            log().error({'err': err}, 'Failed to download %s from S3.', path);
            return callback({'code': 500, 'msg': err});
        }

        writeStream.on('close', function() {
            tmp.size = data.Headers['content-length'];
            return callback(null, tmp);
        });

        // Pump the data to disk.
        writeStream.end(data.Body);
    });
};

/**
 * @borrows Interface.remove as Amazons3.remove
 */
var remove = module.exports.remove = function(ctx, uri, callback) {
    var uriObj = BackendUtil.splitUri(uri);
    var options = {
        'BucketName': _getBucketName(ctx),
        'ObjectName': uriObj.location
    };

    log().trace('Removing %s from S3.', uriObj.location);

    // Delete it from Amazon S3
    _getClient(ctx).DeleteObject(options, function(err, data) {
        if (err) {
            log().error({'err': err}, 'Error removing %s', uriObj.location);
            return callback({'code': 500, 'msg': 'Unable to remove the file: ' + err});
        }
        callback(null);
    });
};

/**
 * We create a signed URL that allows the user to retrieve a file directly from S3
 * This URL is valid for 5 minutes and can only be used once
 * This involves adding a Signature parameter that signs a string with:
 *  * the HTTP method (GET)
 *  * an Expires request parameter in seconds since epoch
 *  * our public key
 *
 * @borrows Interface.getDownloadStrategy as Amazons3.getDownloadStrategy
 */
var getDownloadStrategy = module.exports.getDownloadStrategy = function(ctx, uri) {
    // Date.now returns the milliseconds since epoch, so divide/round it by a thousand
    var expires = Math.round((Date.now() + 5*60000) / 1000);

    // Construct a proper signature
    var s3 = _getClient(ctx);
    var bucketName = _getBucketName(ctx);
    var amazonUri = BackendUtil.splitUri(uri).location;
    var stringToSign = util.format('GET\n\n\n%d\n/%s/%s', expires, bucketName, amazonUri);
    var signature = encodeURIComponent(s3.signature(stringToSign));

    // Construct the signed URL
    var keyId = s3.accessKeyId();
    var url = util.format('https://%s.s3.amazonaws.com/%s?AWSAccessKeyId=%s&Signature=%s&Expires=%s', bucketName, amazonUri, keyId, signature, expires);

    // The user can download the file with the REDIRECT strategy and the signed url
    return new DownloadStrategy(ContentConstants.backend.DOWNLOAD_STRATEGY_REDIRECT, url);
};

/**
 * Returns the bucket we'll be using for storing files in.
 * @param  {Context}    ctx     The current execution context.
 * @return {String}             The bucket name
 */
var _getBucketName = function(ctx) {
    return Config.getValue(ctx.tenant().alias, 'storage', 'amazons3-bucket');
};

/**
 * Gets a client that can connect to S3 and is configured via the admin interface.
 * @param  {Context}    ctx     The current execution context.
 * @return {S3}                 An S3 client.
 */
var _getClient = function(ctx) {

    var accessKey = Config.getValue(ctx.tenant().alias, 'storage', 'amazons3-access-key');
    var secretKey = Config.getValue(ctx.tenant().alias, 'storage', 'amazons3-secret-key');
    var region = Config.getValue(ctx.tenant().alias, 'storage', 'amazons3-region');

    return new S3({
        'accessKeyId': accessKey,
        'secretAccessKey': secretKey,
        'region': region
    });
};
<commit_msg>Added the ContentType when storing a file with the Amazon S3 storage backend.<commit_after>/*!
 * Copyright 2013 Apereo Foundation (AF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

var fs = require('fs');
var mime = require('mime');
var Path = require('path');
var S3 = require('awssum-amazon-s3').S3;
var ShortId = require('shortid');
var util = require('util');

var Cassandra = require('oae-util/lib/cassandra');
var Config = require('oae-config').config('oae-content');
var IO = require('oae-util/lib/io');
var log = require('oae-logger').logger('amazon-storage');
var Revision = require('oae-content/lib/model').Revision;
var TempFile = require('oae-util/lib/tempfile');
var Validator = require('oae-util/lib/validator').Validator;

var BackendUtil = require('./util');
var ContentConstants = require('../constants').ContentConstants;
var DownloadStrategy = require('../model').DownloadStrategy;

//////////////////////
// Storage methods. //
//////////////////////

/**
 * @borrows Interface.store as Amazons3.store
 */
var store = module.exports.store = function(ctx, file, options, callback) {
    // Generate the uri for this file.
    // We can use this uri later to retrieve it.
    var uri = BackendUtil.generateUri(file, options);

    log().trace('Uploading %s to S3.', uri);

    var stream = fs.createReadStream(file.path);
    stream.once('error', function(err){
        IO.destroyStream(stream);
        log().error({'err': err}, 'Could not upload %s to S3', uri);
        callback({'code': 500, 'msg': err});
    });

    options = {
        'Body':  stream,
        'BucketName': _getBucketName(ctx),
        'ContentLength': file.size,
        'ContentType': mime.lookup(file.path),
        'ObjectName': uri
    };

    // Uploads a file to S3.
    // See:
    //  * awssum: http://awssum.io/amazon/s3/put-object.html
    //  * Amazon S3 doc: http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectPUT.html
    _getClient(ctx).PutObject(options, function(err, data) {
        // Remove the file on disk.
        fs.unlink(file.path, function(unlinkError) {
            if (unlinkError) {
                log().warn({'err': unlinkError}, 'Could not remove the temporary file.');
                // We ignore the unlink error, as the file might've actually ended up on S3.
            }

            // Deal with the amazon response.
            if (err) {
                log().error({'err': err}, 'Could not upload to S3.');
                return callback(err);
            }
            callback(null, 'amazons3:' + uri);
        });
    });
};

/**
 * @borrows Interface.get as Amazons3.get
 */
var get = module.exports.get = function(ctx, uri, callback) {
    // Download it to a temp folder.
    var uriObj = BackendUtil.splitUri(uri);

    var filename = Path.basename(uriObj.location);
    var tmp = TempFile.createTempFile({'suffix': filename});
    var writeStream = fs.createWriteStream(tmp.path);

    writeStream.once('error', function(err){
        IO.destroyStream(writeStream);
        log().error({'err': err}, 'Could not save %s to disk', uri);
        callback({'code': 500, 'msg': err});
    });

    log().trace('Downloading %s from S3.', uriObj.location);

    var options = {
        'BucketName': _getBucketName(ctx),
        'ObjectName': uriObj.location
    };

    // Download an "object"(=file) to a temporary folder.
    // See:
    //  * Awssum doc: http://awssum.io/amazon/s3/get-object.html
    //  * Amazon S3 doc: http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGET.html
    // TODO: Replace with upcoming streaming download
    _getClient(ctx).GetObject(options, function(err, data) {
        if (err) {
            IO.destroyStream(writeStream);
            log().error({'err': err}, 'Failed to download %s from S3.', path);
            return callback({'code': 500, 'msg': err});
        }

        writeStream.on('close', function() {
            tmp.size = data.Headers['content-length'];
            return callback(null, tmp);
        });

        // Pump the data to disk.
        writeStream.end(data.Body);
    });
};

/**
 * @borrows Interface.remove as Amazons3.remove
 */
var remove = module.exports.remove = function(ctx, uri, callback) {
    var uriObj = BackendUtil.splitUri(uri);
    var options = {
        'BucketName': _getBucketName(ctx),
        'ObjectName': uriObj.location
    };

    log().trace('Removing %s from S3.', uriObj.location);

    // Delete it from Amazon S3
    _getClient(ctx).DeleteObject(options, function(err, data) {
        if (err) {
            log().error({'err': err}, 'Error removing %s', uriObj.location);
            return callback({'code': 500, 'msg': 'Unable to remove the file: ' + err});
        }
        callback(null);
    });
};

/**
 * We create a signed URL that allows the user to retrieve a file directly from S3
 * This URL is valid for 5 minutes and can only be used once
 * This involves adding a Signature parameter that signs a string with:
 *  * the HTTP method (GET)
 *  * an Expires request parameter in seconds since epoch
 *  * our public key
 *
 * @borrows Interface.getDownloadStrategy as Amazons3.getDownloadStrategy
 */
var getDownloadStrategy = module.exports.getDownloadStrategy = function(ctx, uri) {
    // Date.now returns the milliseconds since epoch, so divide/round it by a thousand
    var expires = Math.round((Date.now() + 5*60000) / 1000);

    // Construct a proper signature
    var s3 = _getClient(ctx);
    var bucketName = _getBucketName(ctx);
    var amazonUri = BackendUtil.splitUri(uri).location;
    var stringToSign = util.format('GET\n\n\n%d\n/%s/%s', expires, bucketName, amazonUri);
    var signature = encodeURIComponent(s3.signature(stringToSign));

    // Construct the signed URL
    var keyId = s3.accessKeyId();
    var url = util.format('https://%s.s3.amazonaws.com/%s?AWSAccessKeyId=%s&Signature=%s&Expires=%s', bucketName, amazonUri, keyId, signature, expires);

    // The user can download the file with the REDIRECT strategy and the signed url
    return new DownloadStrategy(ContentConstants.backend.DOWNLOAD_STRATEGY_REDIRECT, url);
};

/**
 * Returns the bucket we'll be using for storing files in.
 * @param  {Context}    ctx     The current execution context.
 * @return {String}             The bucket name
 */
var _getBucketName = function(ctx) {
    return Config.getValue(ctx.tenant().alias, 'storage', 'amazons3-bucket');
};

/**
 * Gets a client that can connect to S3 and is configured via the admin interface.
 * @param  {Context}    ctx     The current execution context.
 * @return {S3}                 An S3 client.
 */
var _getClient = function(ctx) {

    var accessKey = Config.getValue(ctx.tenant().alias, 'storage', 'amazons3-access-key');
    var secretKey = Config.getValue(ctx.tenant().alias, 'storage', 'amazons3-secret-key');
    var region = Config.getValue(ctx.tenant().alias, 'storage', 'amazons3-region');

    return new S3({
        'accessKeyId': accessKey,
        'secretAccessKey': secretKey,
        'region': region
    });
};
<|endoftext|>


<commit_before>function SwatExpandableCheckboxTree(id, dependent_boxes)
{
	var self = this;
	this.id = id;

	// This flag sets the behaviour of checkboxes. If it is true then checking
	// a parent will check its children and checking all children of a parent
	// will check the parent.
	if (dependent_boxes) {
		// get all checkboxes in this tree
		this.check_list = document.getElementsByName(id + '[]');

		for (var i = 0; i < this.check_list.length; i++) {
			YAHOO.util.Event.addListener(this.check_list[i], 'change',
				handleClick);
		}
	}

	function handleClick(event)
	{
		self.handleClick(this);
	}
}

SwatExpandableCheckboxTree.open_text = 'open';
SwatExpandableCheckboxTree.close_text = 'close';

SwatExpandableCheckboxTree.prototype.handleClick = function(checkbox)
{
	// get path of checkbox from id
	var path = checkbox.id.substr(this.id.length + 1);

	branch = document.getElementById(this.id + '_' + path + '_branch');

	// check all sub-elements
	// ignore leaves
	if (branch) {

		checkboxes = branch.getElementsByTagName('input');

		for (var i = 0; i < checkboxes.length; i++) {
			if (checkboxes[i].getAttribute('type') == 'checkbox') {
				checkboxes[i].checked = checkbox.checked;
			}
		}

	}

	// check parent elements
	// split path into pieces
	var path_exp = path.split('/');

	// skip the root
	var root = path_exp.shift();

	var count = path_exp.length;

	// for each parent, check if all direct children are checked
	for (var i = 0; i < count - 1; i++) {
		path_exp.pop();

		var parent_path = root + '/' + path_exp.join('/');

		// get parent checkbox element
		var parent_checkbox =
			document.getElementById(this.id + '_' + parent_path);

		// get parent branch
		var branch =
			document.getElementById(this.id + '_' + parent_path + '_branch');

		var checkboxes = branch.getElementsByTagName('input');
		var all_checked = true;

		// get state of all checkboxes below parent
		for (var j = 0; j < checkboxes.length; j++) {
			if (checkboxes[j].getAttribute('type') == 'checkbox') {
				if (!checkboxes[j].checked) {
					all_checked = false;
					break;
				}
			}
		}

		parent_checkbox.checked = all_checked;
	}
}

SwatExpandableCheckboxTree.prototype.toggleBranch = function(branch_id)
{
	branch = document.getElementById(this.id + '_' + branch_id + '_branch');
	image = document.getElementById(this.id + '_' + branch_id + '_img');

	opened = YAHOO.util.Dom.hasClass(branch,
		'swat-expandable-checkbox-tree-opened');

	if (opened) {
		YAHOO.util.Dom.addClass(branch, 'swat-expandable-checkbox-tree-closed');
		YAHOO.util.Dom.removeClass(branch,
			'swat-expandable-checkbox-tree-opened');

		image.src = 'packages/swat/images/swat-disclosure-closed.png';
		image.alt = SwatExpandableCheckboxTree.open_text;
	} else {
		YAHOO.util.Dom.addClass(branch, 'swat-expandable-checkbox-tree-opened');
		YAHOO.util.Dom.removeClass(branch,
			'swat-expandable-checkbox-tree-closed');

		image.src = 'packages/swat/images/swat-disclosure-open.png';
		image.alt = SwatExpandableCheckboxTree.closed_text;
	}
}
<commit_msg>- add animation for expandable checkbox tree nodes (Fixes #300) - refactor events so self is not needed - add missing var declarations - whitespace<commit_after>function SwatExpandableCheckboxTree(id, dependent_boxes)
{
	this.id = id;

	// This flag sets the behaviour of checkboxes. If it is true then checking
	// a parent will check its children and checking all children of a parent
	// will check the parent.
	if (dependent_boxes) {
		// get all checkboxes in this tree
		this.check_list = document.getElementsByName(id + '[]');

		for (var i = 0; i < this.check_list.length; i++) {
			YAHOO.util.Event.addListener(this.check_list[i], 'change',
				SwatExpandableCheckboxTree.handleClick, this);
		}
	}
}

SwatExpandableCheckboxTree.open_text = 'open';
SwatExpandableCheckboxTree.close_text = 'close';

SwatExpandableCheckboxTree.handleClick = function(event, tree)
{
	var checkbox = YAHOO.util.Event.getTarget(event);
	tree.handleClick(checkbox);
}

SwatExpandableCheckboxTree.prototype.handleClick = function(checkbox)
{
	// get path of checkbox from id
	var path = checkbox.id.substr(this.id.length + 1);
	var branch = document.getElementById(this.id + '_' + path + '_branch');

	// check all sub-elements
	// ignore leaves
	if (branch) {
		var checkboxes = branch.getElementsByTagName('input');

		for (var i = 0; i < checkboxes.length; i++) {
			if (checkboxes[i].getAttribute('type') == 'checkbox') {
				checkboxes[i].checked = checkbox.checked;
			}
		}
	}

	// check parent elements
	// split path into pieces
	var path_exp = path.split('/');

	// skip the root
	var root = path_exp.shift();

	var count = path_exp.length;

	// for each parent, check if all direct children are checked
	for (var i = 0; i < count - 1; i++) {
		path_exp.pop();

		var parent_path = root + '/' + path_exp.join('/');

		// get parent checkbox element
		var parent_checkbox =
			document.getElementById(this.id + '_' + parent_path);

		// get parent branch
		var branch =
			document.getElementById(this.id + '_' + parent_path + '_branch');

		var checkboxes = branch.getElementsByTagName('input');
		var all_checked = true;

		// get state of all checkboxes below parent
		for (var j = 0; j < checkboxes.length; j++) {
			if (checkboxes[j].getAttribute('type') == 'checkbox') {
				if (!checkboxes[j].checked) {
					all_checked = false;
					break;
				}
			}
		}

		parent_checkbox.checked = all_checked;
	}
}

SwatExpandableCheckboxTree.prototype.toggleBranch = function(branch_id)
{
	var branch = document.getElementById(this.id + '_' + branch_id + '_branch');
	var opened = YAHOO.util.Dom.hasClass(branch,
		'swat-expandable-checkbox-tree-opened');

	if (opened) {
		this.closeBranch(branch_id);
	} else {
		this.openBranch(branch_id);
	}
}

SwatExpandableCheckboxTree.prototype.openBranch = function(branch_id)
{
	var branch = document.getElementById(this.id + '_' + branch_id + '_branch');
	var image = document.getElementById(this.id + '_' + branch_id + '_img');

	YAHOO.util.Dom.removeClass(branch, 'swat-expandable-checkbox-tree-closed');
	YAHOO.util.Dom.addClass(branch, 'swat-expandable-checkbox-tree-opened');

	// get display height
	branch.parentNode.style.overflow = 'hidden';
	branch.parentNode.style.height = '0';
	branch.style.visibility = 'hidden';
	branch.style.overflow = 'hidden';
	branch.style.display = 'block';
	branch.style.height = '';
	var height = branch.offsetHeight;
	branch.style.height = '0';
	branch.style.visibility = 'visible';
	branch.parentNode.style.height = '';
	branch.parentNode.style.overflow = 'visible';

	var attributes = { height: { to: height, from: 0 } };
	var animation = new YAHOO.util.Anim(branch, attributes, 0.25,
		YAHOO.util.Easing.easeOut);

	animation.onComplete.subscribe(
		SwatExpandableCheckboxTree.handleBranchOpen, [this, branch, image]);

	animation.animate();
}

SwatExpandableCheckboxTree.prototype.closeBranch = function(branch_id)
{
	var branch = document.getElementById(this.id + '_' + branch_id + '_branch');
	var image = document.getElementById(this.id + '_' + branch_id + '_img');

	branch.style.overflow = 'hidden';
	branch.style.height = '';

	var attributes = { height: { to: 0 } };
	var animation = new YAHOO.util.Anim(branch, attributes, 0.25,
		YAHOO.util.Easing.easingIn);

	animation.onComplete.subscribe(
		SwatExpandableCheckboxTree.handleBranchClose, [this, branch, image]);

	animation.animate();
}

SwatExpandableCheckboxTree.handleBranchOpen = function(type, args, data)
{
	var tree = data[0];
	var branch = data[1];
	var image = data[2];

	branch.style.height = '';

	image.src = 'packages/swat/images/swat-disclosure-open.png';
	image.alt = SwatExpandableCheckboxTree.closed_text;
}

SwatExpandableCheckboxTree.handleBranchClose = function(type, args, data)
{
	var tree = data[0];
	var branch = data[1];
	var image = data[2];

	image.src = 'packages/swat/images/swat-disclosure-closed.png';
	image.alt = SwatExpandableCheckboxTree.open_text;

	YAHOO.util.Dom.addClass(branch, 'swat-expandable-checkbox-tree-closed');
	YAHOO.util.Dom.removeClass(branch, 'swat-expandable-checkbox-tree-opened');
}
<|endoftext|>