<commit_before>// Dependencies
var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");
var Utils = require("github/jillix/utils");

/**
 *  Mono module for jxMaps application
 */
module.exports = function(config) {

    // get module
    var self = this;

    // call events
    Events.call(self, config);

    // binds
    config.binds = config.binds || [];
    config.options = Object(config.options);
    config.options.ui = Object(config.options.ui);

    // run the binds
    for (var i = 0; i < config.binds.length; ++i) {
        Bind.call(self, config.binds[i]);
    }

    // set config in self
    self.config = config;
    self._maps = {};

    // ui
    self._$ = {
        map: $(self.config.options.ui.map),
        waiter: $(self.config.options.ui.waiter),
        error: $(self.config.options.ui.error)
    };

    // crud operations
    var operations = ["create", "read", "update", "delete"];

    // generate client methods
    for (var i = 0; i < operations.length; ++i) {
        (function(op) {
            self[op] = function(options, callback) {
                self.link(op, {
                    data: options
                }, callback);
            };
        })(operations[i]);
    }

    /**
     * embed
     * Gets the map to embed from database
     *
     * @param options: object containing
     *  - mapId: the map id
     * @param callback
     * @return
     */
    self.embed = function(options, callback) {

        // update ui
        self._$.map.hide();
        self._$.waiter.show();

        // default value for callback
        callback = callback || function() {};

        // call server operation
        self.link("embed", {
            data: options
        }, function(err, mapData) {

            // handle error
            if (err) {

                // update UI
                self._$.waiter.fadeOut();
                self._$.error.fadeIn().text(err);

                return callback(err);
            }

            handleMapData(mapData);
        });
    };

    /**
     * handleMapData
     * This function attaches the map data to the module instance
     *
     * @param mapData
     * @return
     */
    function handleMapData(mapData) {

        // cache map data
        self._maps[mapData._id] = mapData;

        // get google maps script
        $.getScript("https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=__initializeMap");
    }

    /**
     * __initializeMap
     * This is a global function that will be called after
     * loading the Google library.
     *
     * @param mapData
     * @return
     */
    window.__initializeMap = function(mapData) {

        function updateAndReload(lat, lng) {
            Url.updateSearchParam("options.center.lat", lat);
            Url.updateSearchParam("options.center.lng", lng);
            Url.updateSearchParam("address");
            if (Url.queryString("displayMarker")) {
                Url.updateSearchParam("markers.0.position.lat", lat);
                Url.updateSearchParam("markers.0.position.lng", lng);
            }
            location.reload();
        }

        // handle ?address parameter
        if (self._maps._addressMap) {
            var address = Url.queryString("address");
            var match = address.match(/^([-+]?\d{1,2}([.]\d+)?),\s*([-+]?\d{1,3}([.]\d+)?)$/);

            // if we have a match
            if (match) {
                // both coordinates must be valid numbers
                lat = Number(match[1]);
                lng = Number(match[3]);

                if (!isNaN(lat) && !isNaN(lng)) {
                    return updateAndReload(lat, lng);
                }
            }

            var geocoder = self._geocoder = new google.maps.Geocoder();
            geocoder.geocode({
                address: Url.queryString("address")
            }, function(results, status) {
                if (!results[0] || !results[0].geometry.location) {
                    return console.error("No location found");
                }
                var strLoc = results[0].geometry.location.toString().replace(/\(|\)/g, "").split(", ");
                updateAndReload(strLoc[0], strLoc[1]);
            });
            return;
        }

        // no map data
        if (!mapData) {
            for (var mapId in self._maps) {
                __initializeMap(self._maps[mapId]);
            }
            return;
        }

        // get map element
        var mapEl = self._$.map[0];
        if (!mapEl) {
            return console.error("No map element found.");
        }

        // map options
        var mapOptions = {
            center: new google.maps.LatLng(
                mapData.options.center.lat, mapData.options.center.lng
            ),
            zoom: mapData.options.zoom,
            mapTypeId: google.maps.MapTypeId[mapData.options.type]
        };

        // save google maps instance in _gmap
        self._gmap = new google.maps.Map(mapEl, mapOptions);

        // default value for markers
        var markers = mapData.markers = mapData.markers || [];
        var allMarkers = [];
        var lastInfoWindow = null;

        // each marker
        for (var i = 0; i < markers.length; ++i) {

            (function(cMarker) {

                if (!cMarker.position) {
                    return;
                }

                // icon exists
                if (cMarker.icon) {
                    cMarker.icon = !cMarker.icon.path ? undefined : new google.maps.MarkerImage(
                        cMarker.icon.path, new google.maps.Size(
                            cMarker.icon.size.w, cMarker.icon.size.h
                        ), new google.maps.Point(
                            cMarker.icon.origin.x, cMarker.icon.origin.y
                        ), new google.maps.Point(
                            cMarker.icon.anchor.x, cMarker.icon.anchor.y
                        )
                    );
                }

                // create google marker
                var marker = new google.maps.Marker({
                    position: new google.maps.LatLng(cMarker.position.lat, cMarker.position.lng),
                    map: self._gmap,
                    title: cMarker.title || "",
                    icon: cMarker.icon || undefined,
                    visible: cMarker.visible
                });

                // infowin exists
                if (cMarker.infowin) {
                    cMarker.infowin = new google.maps.InfoWindow({
                        content: cMarker.infowin.content
                    });
                }

                // push the new marker
                allMarkers.push(marker);

                // add click event for info window
                google.maps.event.addListener(marker, "click", function() {
                    if (!cMarker.infowin) return;
                    if (lastInfoWindow) {
                        lastInfoWindow.close();
                    }

                    (lastInfoWindow = cMarker.infowin).open(self._gmap, marker);
                });
            })(markers[i]);
        }

        // marker clusterer
        if (mapData.options.clustering) {
            self._gmarkerClusterer = new MarkerClusterer(self._gmap, allMarkers, mapData.options.clustering.options);
        }

        // update ui
        self._$.map.fadeIn();
        self._$.waiter.fadeOut();
    };

    // PostMesage API
    $(window).on("message", function(event) {
        event.data = event.originalEvent.data;
        if (!event.data || !event.data.method) {
            throw new Error("No method defined.");
        }

        var methods = {
            search: function(d) {
                d = Object(d);
                self._geocoder.geocode(d.geocode, function(res) {

                    if (!res || !res[0]) {
                        return;
                    }

                    var p = res[0].geometry.location;

                    google.maps.event.trigger(self._gmap, "resize");
                    self._gmap.setCenter(p || self.center);

                    if (p) {
                        self._gmap.setZoom(11);
                    } else {
                        self._gmap.setZoom(8);
                    }
                });
            }
        };

        if (typeof methods[event.data.method] !== "function") {
            throw new Error("Provided method is not a function.");
        }

        methods[event.data.method](event.data.data);
    });

    // emit ready
    self.emit("ready", self);

    // we are on the embed page
    if (
        location.pathname + location.hash === self.config.options.embedPage && self.config.options.automaticallyEmbed !== false
    ) {

        // get the map id
        var mapId = Url.queryString("mapId");
        var dataUrl = Url.queryString("data");
        var lat = Url.queryString("options.center.lat");
        var lng = Url.queryString("options.center.lng");
        var address = Url.queryString("address");

        // map id was provided
        if (mapId) {
            return self.embed({
                mapId: mapId
            });
        }

        if (dataUrl) {
            return self.embed({
                data: dataUrl
            });
        }

        if (address) {
            return handleMapData({
                address: address,
                _id: "_addressMap"
            });
        }

        // querystring api
        if (lat && lng) {

            var validators = {
                number: function(val) {
                    return isNaN(val) ? undefined : Number(val);
                }
            };

            var fields = {
                "name": {
                    default: "No name"
                },
                "options.center.lat": {
                    validator: validators.number
                },
                "options.center.lng": {
                    validator: validators.number
                },
                "options.zoom": {
                    validator: validators.number,
                    default: 15
                },
                "options.type": {
                    default: "ROADMAP"
                },
                "markers.0.label": {},
                "markers.0.title": {},
                "markers.0.position.lat": {
                    validator: validators.number
                },
                "markers.0.position.lng": {
                    validator: validators.number
                },
                "markers.0.icon.path": {},
                "markers.0.icon.label": {},
                "markers.0.icon.size.w": {
                    default: 100,
                    validator: validators.number
                },
                "markers.0.icon.size.h": {
                    default: 100,
                    validator: validators.number
                },
                "markers.0.icon.origin.x": {
                    default: 0,
                    validator: validators.number
                },
                "markers.0.icon.origin.y": {
                    default: 0,
                    validator: validators.number
                },
                "markers.0.icon.anchor.x": {
                    default: 0,
                    validator: validators.number
                },
                "markers.0.icon.anchor.y": {
                    default: 0,
                    validator: validators.number
                },
                "markers.0.infowin.content": {},
                "markers.0.visible": {
                    default: true
                }
            };

            function iconLoaded() {
                // set map data
                var mapData = {};
                var searchQueryObj = Url.parseSearchQuery();
                for (var field in fields) {
                    if (!fields.hasOwnProperty(field)) continue;
                    var cFieldVal = fields[field];
                    var paramValue = searchQueryObj[field];
                    var valueToSet = paramValue || cFieldVal.default;
                    if (cFieldVal.validator) {
                        valueToSet = cFieldVal.validator(valueToSet);
                    }
                    mapData[field] = valueToSet;
                    if (mapData[field] === undefined) {
                        delete mapData[field];
                    }
                }

                mapData = Utils.unflattenObject(mapData);
                if (mapData.markers) {
                    mapData.markers.length = 1;
                }

                handleMapData(mapData);
            }

            var iconImgSrc = Url.queryString("markers.0.icon.path");
            if (iconImgSrc) {
                var iconImg = new Image();
                iconImg.src = iconImgSrc;
                return $(iconImg).load(function() {
                    fields["markers.0.icon.anchor.x"]["default"] = iconImg.width / 2;
                    fields["markers.0.icon.anchor.y"]["default"] = iconImg.height / 2 + iconImg.height / 3;
                    iconLoaded();
                });
            } else {
                return iconLoaded();
            }
        }

        // invalid request
        self._$.map.hide();
        self._$.waiter.hide();
        self._$.error.text("Please provide a map id or use the query string API.");
    }
};

<commit_msg>Fixed #19. Create the geocoder object always<commit_after>// Dependencies
var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");
var Utils = require("github/jillix/utils");

/**
 *  Mono module for jxMaps application
 */
module.exports = function(config) {

    // get module
    var self = this;

    // call events
    Events.call(self, config);

    // binds
    config.binds = config.binds || [];
    config.options = Object(config.options);
    config.options.ui = Object(config.options.ui);

    // run the binds
    for (var i = 0; i < config.binds.length; ++i) {
        Bind.call(self, config.binds[i]);
    }

    // set config in self
    self.config = config;
    self._maps = {};

    // ui
    self._$ = {
        map: $(self.config.options.ui.map),
        waiter: $(self.config.options.ui.waiter),
        error: $(self.config.options.ui.error)
    };

    // crud operations
    var operations = ["create", "read", "update", "delete"];

    // generate client methods
    for (var i = 0; i < operations.length; ++i) {
        (function(op) {
            self[op] = function(options, callback) {
                self.link(op, {
                    data: options
                }, callback);
            };
        })(operations[i]);
    }

    /**
     * embed
     * Gets the map to embed from database
     *
     * @param options: object containing
     *  - mapId: the map id
     * @param callback
     * @return
     */
    self.embed = function(options, callback) {

        // update ui
        self._$.map.hide();
        self._$.waiter.show();

        // default value for callback
        callback = callback || function() {};

        // call server operation
        self.link("embed", {
            data: options
        }, function(err, mapData) {

            // handle error
            if (err) {

                // update UI
                self._$.waiter.fadeOut();
                self._$.error.fadeIn().text(err);

                return callback(err);
            }

            handleMapData(mapData);
        });
    };

    /**
     * handleMapData
     * This function attaches the map data to the module instance
     *
     * @param mapData
     * @return
     */
    function handleMapData(mapData) {

        // cache map data
        self._maps[mapData._id] = mapData;

        // get google maps script
        $.getScript("https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=__initializeMap");
    }

    /**
     * __initializeMap
     * This is a global function that will be called after
     * loading the Google library.
     *
     * @param mapData
     * @return
     */
    window.__initializeMap = function(mapData) {

        function updateAndReload(lat, lng) {
            Url.updateSearchParam("options.center.lat", lat);
            Url.updateSearchParam("options.center.lng", lng);
            Url.updateSearchParam("address");
            if (Url.queryString("displayMarker")) {
                Url.updateSearchParam("markers.0.position.lat", lat);
                Url.updateSearchParam("markers.0.position.lng", lng);
            }
            location.reload();
        }

        var geocoder = self._geocoder = new google.maps.Geocoder();

        // handle ?address parameter
        if (self._maps._addressMap) {
            var address = Url.queryString("address");
            var match = address.match(/^([-+]?\d{1,2}([.]\d+)?),\s*([-+]?\d{1,3}([.]\d+)?)$/);

            // if we have a match
            if (match) {
                // both coordinates must be valid numbers
                lat = Number(match[1]);
                lng = Number(match[3]);

                if (!isNaN(lat) && !isNaN(lng)) {
                    return updateAndReload(lat, lng);
                }
            }

            geocoder.geocode({
                address: Url.queryString("address")
            }, function(results, status) {
                if (!results[0] || !results[0].geometry.location) {
                    return console.error("No location found");
                }
                var strLoc = results[0].geometry.location.toString().replace(/\(|\)/g, "").split(", ");
                updateAndReload(strLoc[0], strLoc[1]);
            });
            return;
        }

        // no map data
        if (!mapData) {
            for (var mapId in self._maps) {
                __initializeMap(self._maps[mapId]);
            }
            return;
        }

        // get map element
        var mapEl = self._$.map[0];
        if (!mapEl) {
            return console.error("No map element found.");
        }

        // map options
        var mapOptions = {
            center: new google.maps.LatLng(
                mapData.options.center.lat, mapData.options.center.lng
            ),
            zoom: mapData.options.zoom,
            mapTypeId: google.maps.MapTypeId[mapData.options.type]
        };

        // save google maps instance in _gmap
        self._gmap = new google.maps.Map(mapEl, mapOptions);

        // default value for markers
        var markers = mapData.markers = mapData.markers || [];
        var allMarkers = [];
        var lastInfoWindow = null;

        // each marker
        for (var i = 0; i < markers.length; ++i) {

            (function(cMarker) {

                if (!cMarker.position) {
                    return;
                }

                // icon exists
                if (cMarker.icon) {
                    cMarker.icon = !cMarker.icon.path ? undefined : new google.maps.MarkerImage(
                        cMarker.icon.path, new google.maps.Size(
                            cMarker.icon.size.w, cMarker.icon.size.h
                        ), new google.maps.Point(
                            cMarker.icon.origin.x, cMarker.icon.origin.y
                        ), new google.maps.Point(
                            cMarker.icon.anchor.x, cMarker.icon.anchor.y
                        )
                    );
                }

                // create google marker
                var marker = new google.maps.Marker({
                    position: new google.maps.LatLng(cMarker.position.lat, cMarker.position.lng),
                    map: self._gmap,
                    title: cMarker.title || "",
                    icon: cMarker.icon || undefined,
                    visible: cMarker.visible
                });

                // infowin exists
                if (cMarker.infowin) {
                    cMarker.infowin = new google.maps.InfoWindow({
                        content: cMarker.infowin.content
                    });
                }

                // push the new marker
                allMarkers.push(marker);

                // add click event for info window
                google.maps.event.addListener(marker, "click", function() {
                    if (!cMarker.infowin) return;
                    if (lastInfoWindow) {
                        lastInfoWindow.close();
                    }

                    (lastInfoWindow = cMarker.infowin).open(self._gmap, marker);
                });
            })(markers[i]);
        }

        // marker clusterer
        if (mapData.options.clustering) {
            self._gmarkerClusterer = new MarkerClusterer(self._gmap, allMarkers, mapData.options.clustering.options);
        }

        // update ui
        self._$.map.fadeIn();
        self._$.waiter.fadeOut();
    };

    // PostMesage API
    $(window).on("message", function(event) {
        event.data = event.originalEvent.data;
        if (!event.data || !event.data.method) {
            throw new Error("No method defined.");
        }

        var methods = {
            search: function(d) {
                d = Object(d);
                self._geocoder.geocode(d.geocode, function(res) {

                    if (!res || !res[0]) {
                        return;
                    }

                    var p = res[0].geometry.location;

                    google.maps.event.trigger(self._gmap, "resize");
                    self._gmap.setCenter(p || self.center);

                    if (p) {
                        self._gmap.setZoom(11);
                    } else {
                        self._gmap.setZoom(8);
                    }
                });
            }
        };

        if (typeof methods[event.data.method] !== "function") {
            throw new Error("Provided method is not a function.");
        }

        methods[event.data.method](event.data.data);
    });

    // emit ready
    self.emit("ready", self);

    // we are on the embed page
    if (
        location.pathname + location.hash === self.config.options.embedPage && self.config.options.automaticallyEmbed !== false
    ) {

        // get the map id
        var mapId = Url.queryString("mapId");
        var dataUrl = Url.queryString("data");
        var lat = Url.queryString("options.center.lat");
        var lng = Url.queryString("options.center.lng");
        var address = Url.queryString("address");

        // map id was provided
        if (mapId) {
            return self.embed({
                mapId: mapId
            });
        }

        if (dataUrl) {
            return self.embed({
                data: dataUrl
            });
        }

        if (address) {
            return handleMapData({
                address: address,
                _id: "_addressMap"
            });
        }

        // querystring api
        if (lat && lng) {

            var validators = {
                number: function(val) {
                    return isNaN(val) ? undefined : Number(val);
                }
            };

            var fields = {
                "name": {
                    default: "No name"
                },
                "options.center.lat": {
                    validator: validators.number
                },
                "options.center.lng": {
                    validator: validators.number
                },
                "options.zoom": {
                    validator: validators.number,
                    default: 15
                },
                "options.type": {
                    default: "ROADMAP"
                },
                "markers.0.label": {},
                "markers.0.title": {},
                "markers.0.position.lat": {
                    validator: validators.number
                },
                "markers.0.position.lng": {
                    validator: validators.number
                },
                "markers.0.icon.path": {},
                "markers.0.icon.label": {},
                "markers.0.icon.size.w": {
                    default: 100,
                    validator: validators.number
                },
                "markers.0.icon.size.h": {
                    default: 100,
                    validator: validators.number
                },
                "markers.0.icon.origin.x": {
                    default: 0,
                    validator: validators.number
                },
                "markers.0.icon.origin.y": {
                    default: 0,
                    validator: validators.number
                },
                "markers.0.icon.anchor.x": {
                    default: 0,
                    validator: validators.number
                },
                "markers.0.icon.anchor.y": {
                    default: 0,
                    validator: validators.number
                },
                "markers.0.infowin.content": {},
                "markers.0.visible": {
                    default: true
                }
            };

            function iconLoaded() {
                // set map data
                var mapData = {};
                var searchQueryObj = Url.parseSearchQuery();
                for (var field in fields) {
                    if (!fields.hasOwnProperty(field)) continue;
                    var cFieldVal = fields[field];
                    var paramValue = searchQueryObj[field];
                    var valueToSet = paramValue || cFieldVal.default;
                    if (cFieldVal.validator) {
                        valueToSet = cFieldVal.validator(valueToSet);
                    }
                    mapData[field] = valueToSet;
                    if (mapData[field] === undefined) {
                        delete mapData[field];
                    }
                }

                mapData = Utils.unflattenObject(mapData);
                if (mapData.markers) {
                    mapData.markers.length = 1;
                }

                handleMapData(mapData);
            }

            var iconImgSrc = Url.queryString("markers.0.icon.path");
            if (iconImgSrc) {
                var iconImg = new Image();
                iconImg.src = iconImgSrc;
                return $(iconImg).load(function() {
                    fields["markers.0.icon.anchor.x"]["default"] = iconImg.width / 2;
                    fields["markers.0.icon.anchor.y"]["default"] = iconImg.height / 2 + iconImg.height / 3;
                    iconLoaded();
                });
            } else {
                return iconLoaded();
            }
        }

        // invalid request
        self._$.map.hide();
        self._$.waiter.hide();
        self._$.error.text("Please provide a map id or use the query string API.");
    }
};

<|endoftext|>


<commit_before>
////////////// login stettings
Accounts.ui.config({
	passwordSignupFields: 'USERNAME_AND_OPTIONAL_EMAIL'
});

////////////// loading-indicator
Meteor.startup(function () {
  Session.setDefault('coursesLoaded', false);
  var region = localStorage.getItem("region")
  if (!region) region = 'all';
  Session.set("region", region);
});

////////////// db-subscriptions:

//Meteor.subscribe('courses', Session.get('region'))
Meteor.subscribe('categories');
Meteor.subscribe('comments');
Meteor.subscribe('events');
Meteor.subscribe('discussions');
Meteor.subscribe('locations');
Meteor.subscribe('messages');
Meteor.subscribe('regions');
Meteor.subscribe('roles');
Meteor.subscribe('votings');
Meteor.subscribe('users');
return Meteor.subscribe('currentUser');

<commit_msg>housekeeping<commit_after>
////////////// login stettings
Accounts.ui.config({
	passwordSignupFields: 'USERNAME_AND_OPTIONAL_EMAIL'
});

////////////// loading-indicator
Meteor.startup(function () {
  Session.setDefault('coursesLoaded', false);
  var region = localStorage.getItem("region")
  if (!region) region = 'all';
  Session.set("region", region);
});

////////////// db-subscriptions:

Meteor.subscribe('categories');
Meteor.subscribe('comments');
Meteor.subscribe('events');
Meteor.subscribe('discussions');
Meteor.subscribe('locations');
Meteor.subscribe('messages');
Meteor.subscribe('regions');
Meteor.subscribe('roles');
Meteor.subscribe('votings');
Meteor.subscribe('users');
return Meteor.subscribe('currentUser');

<|endoftext|>


<commit_before>var automate = require("guide-automation");
var isWindowsCI = require("is-appveyor");
var join = require("path").join;
var rimraf = require("rimraf").sync;
var streamWhen = require("stream-when");
var nodeVersion = +process.version.substr(1).split(".")[0];

// Only run in AppVeyor if version >= 5
if(isWindowsCI && nodeVersion < 5) {
    process.exit(0);
}

var guide = automate({ spinner: true, log: true });
var wait = function(){
    var ms = isWindowsCI ? 5000 : 2000;
	return guide.wait(ms);
};

/**
 * @Step 1
 */
guide.step("Remove existing dependencies", function(){
  rimraf(join("node_modules", "donejs-cli"));
  rimraf(join("node_modules", "generator-donejs"));
});

guide.step("Install donejs", function(){
  var branch = process.env.TRAVIS_BRANCH || process.env.APPVEYOR_REPO_BRANCH || 'master';

	return guide.executeCommand("npm", ["install", "donejs/donejs#" + branch, "-g"]);
});

// Move to a temp folder for the rest of the guide
guide.moveToTmp();

/**
 * @Step 2
 */
guide.step("Run donejs add app", function(){
	var init = guide.answerPrompts("donejs", ["add", "app", "donejs-chat", "--skip-install"]);
	var answer = init.answer;

	answer(/Project name/, "\n");
	answer(/Project main folder/, "src\n");
	answer(/Description/, "\n");
	answer(/Project homepage url/, "\n");
	answer(/GitHub username or organization/, "\n");
	answer(/Author's Name/, "\n");
	answer(/Author's Email/, "\n");
	answer(/Author's Homepage/, "\n");
	answer(/Application keywords/, "\n");
	answer(/NPM version/, "\n");

	return init.promise;
});

/**
 * @Step 3
 */
guide.step("Move to donejs-chat folder", function(){
	process.chdir("donejs-chat");
	return guide.injectSpy("src/index.stache");
});

guide.step("Run NPM install", function() {
	return guide.executeCommand("npm", ["install"]).then(guide.wait(10000));
});

/**
 * @Step 4
 */
guide.step("Start donejs develop", function(){
	var child = guide.canServe = guide.executeCommand("donejs", ["develop"]).childProcess;
  console.log("done-serve running as", child.pid);

	var server = streamWhen(child.stdout, /done-serve starting on/);
	var liveReload = streamWhen(child.stderr, /Live-reload server/);
	return Promise.all([server, liveReload]).then(wait);
});

guide.test(function(){
	return guide.nodeTest(join(__dirname, "steps", "3-donejs-develop", "test.js"));
});

guide.launchBrowser("http://localhost:8080");

/**
 * @Step 5
 */
guide.step("Install bootstrap", function(){
	return guide.executeCommand("npm", ["install", "bootstrap", "--save"])
		.then(wait)
		.then(function(){
			return guide.replaceFile(join("src", "index.stache"),
									 join(__dirname, "steps", "4-bootstrap", "index.stache"));
		})
    .then(function(){
      return guide.injectSpy("src/index.stache");
    })
    .then(wait)
		.then(wait);
});

guide.test(function(){
  console.log("Running bootstrap tests");
	return guide.functionalTest(join(__dirname, "steps", "4-bootstrap", "test.js"))
		.then(wait);
});

/**
 * @Step 6
 */
guide.step("Generate custom elements", function(){
	return guide.executeCommand("donejs", ["add","component","home.component","chat-home"])
		.then(function(){
			return guide.executeCommand("donejs", ["add","component","messages",
								  "chat-messages"]);
		}).then(wait);
});

/**
 * @Step 7
 */
guide.step("Navigate between pages", function(){
	return guide.replaceFile(join("src", "home.component"),
					   join(__dirname, "steps", "7-navigate", "home.component"))
		.then(function(){
			return guide.replaceFile(join("src", "messages", "messages.stache"),
							   join(__dirname, "steps", "7-navigate", "messages.stache"));
		})
		.then(function(){
			return guide.replaceFile(join("src", "app.js"),
							   join(__dirname, "steps", "7-navigate", "app.js"));
		})
		.then(function(){
			return guide.replaceFile(join("src", "index.stache"),
							   join(__dirname, "steps", "7-navigate", "index.stache"));
		})
    .then(function(){
      return guide.injectSpy("src/index.stache");
    })
    .then(wait);
});

guide.test(function(){
	return guide.functionalTest(join(__dirname, "steps", "7-navigate", "test.js"));
});

guide.test(function(){
	return guide.nodeTest(join(__dirname, "steps", "7-navigate", "ssr_test.js"));
});

/**
 * @Step 8
 */
guide.step("Install and use bit-tabs", function(){
  function installWait(){
    return guide.wait(10000);
  }

	return guide.executeCommand("npm", ["install", "bit-tabs@alpha", "--save"])
    .then(installWait)
		.then(function(){
			return guide.replaceFile(join("src", "home.component"),
									 join(__dirname, "steps", "8-bit-tabs", "home.component"));
		})
		.then(wait);
});

guide.test(function(){
  console.log("Running bit-tabs tests");
	// Check that the tabs have appeared.
	return guide.functionalTest(join(__dirname, "steps", "8-bit-tabs", "tabs_test.js"))
		.then(wait);
});

/**
 * @Step 9
 */
guide.step("Generate the Message model", function(){
	var supermodel = guide.answerPrompts("donejs", ["add", "supermodel", "message"]);
	var answer = supermodel.answer;

	answer(/URL endpoint/, "http://chat.donejs.com/api/messages\n");
	answer(/property name/, "\n");
  answer(/service URL/, "Yes\n");

	return supermodel.promise;
});

/**
 * @Step 10
 */
guide.step("Use the connection", function(){
	return guide.replaceFile(join("src", "messages", "messages.stache"),
							 join(__dirname, "steps", "10-use-connection", "messages.stache"))
    .then(function() {
      return guide.replaceFile(join("src", "messages", "messages.js"),
                   join(__dirname, "steps", "10-use-connection", "messages.js"))
    })
		.then(wait);
});

guide.test(function(){
	return guide.functionalTest(join(__dirname, "steps", "10-use-connection", "test.js"));
});

/**
 * @Step 11
 */
guide.step("Create messages", function(){
  return guide.replaceFile(join("src", "messages", "messages.js"),
               join(__dirname, "steps", "11-create-messages", "messages.js"))
		.then(function(){
      return guide.replaceFile(join("src", "messages", "messages.stache"),
    							 join(__dirname, "steps", "11-create-messages", "messages.stache"));
		}).then(wait);
});

guide.test(function(){
	return guide.functionalTest(join(__dirname, "steps", "11-create-messages", "test.js"));
});

/**
 * @Step 12
 */
guide.step("Enable a real-time connection", function(){
	return guide.executeCommand("npm", ["install", "steal-socket.io@2", "--save"])
		.then(wait)
		.then(function(){
			return guide.replaceFile(join("src", "models", "message.js"),
									 join(__dirname, "steps", "12-real-time", "message.js"));
		});

});

guide.test(function(){
	return guide.functionalTest(join(__dirname, "steps", "12-real-time", "test.js"));
});

/**
 * @Step 13
 */
guide.closeBrowser();

guide.step("Stop development mode", function(){
  return guide.kill(guide.canServe)
    .then(function(){
      guide.canServe = null;
      return guide.wait(2000);
    });
});

/**
 * @ Step 14
 */

guide.step("Production build", function(){
	return guide.executeCommand("donejs", ["build"]);
});

/**
 * @ Step 15
 */
guide.step("Open in production", function(){
	process.env["NODE_ENV"] = "production";

	var child = guide.canServe = guide.executeCommand("donejs", ["start"])
		.childProcess;

	var server = streamWhen(child.stdout, /done-serve starting on/);
	return server.then(wait);
});

guide.test(function(){
	return guide.nodeTest(join(__dirname, "steps", "15-production", "test.js"));
});

guide.step("Stop production mode", function(){
	return guide.kill(guide.canServe)
		.then(function(){
			guide.canServe = null;
			return guide.wait(2000);
		});
});

/**
 * @Step 16
 */
guide.stepIf("Deploy to CDN", function() {
	return !!guide.options.app;
}, function(){
  return guide.executeCommand("donejs", ["add", "firebase"])
		.then(function(){
			return guide.executeCommand("donejs", ["build"]);
		})
		.then(function(){
			return guide.executeCommand("donejs", ["deploy"]);
		});
});

/**
 * @Step 17
 */
guide.stepIf("Desktop and mobile apps: Cordova", function() {
	return process.platform === 'darwin';
}, function(){
	return guide.executeCommand("npm", ["install", "-g", "ios-sim"])
		.then(function(){
			var proc = guide.answerPrompts("donejs", ["add", "cordova"]);
			var answer = proc.answer;

			answer(/Name of project/, "donejs chat\n");
			answer(/ID of project/, "com.donejs.donejschat\n");
      answer(/service layer/, "\n");
			answer(/What platforms/, "\n");

			return proc.promise;
		})
		.then(function(){
			return guide.executeCommand("donejs", ["build", "cordova"]);
		});
});

/**
 * @Step 18
 */
guide.step("Desktop and mobile apps: NW.js", function(){
	var proc = guide.answerPrompts("donejs", ["add", "nw"]);
	var answer = proc.answer;

	answer(/Main HTML file/, "\n");
	answer(/The nw.js version/, "\n");
  answer(/service layer/, "\n");
	answer(/Width of/, "\n");
	answer(/Height of/, "\n");
	answer(/What platforms/, "\n");

	return proc.promise
		.then(function(){
			return guide.executeCommand("donejs", ["build", "nw"]);
		});
});

/**
 *  Run the test
 */

guide.run().then(
	function(){
		console.log("All done!");
		return 0;
	},
	function(err){
		console.error("Oh no", err.message, err.stack, err);
		return 1;
	}
).then(function(exitCode){
	if(guide.canServe) {
		guide.canServe.kill();
	}
	return exitCode;
}).then(function(exitCode){
	process.exit(exitCode);
});
<commit_msg>updating quickstart guide tests to use Electron<commit_after>var automate = require("guide-automation");
var isWindowsCI = require("is-appveyor");
var join = require("path").join;
var rimraf = require("rimraf").sync;
var streamWhen = require("stream-when");
var nodeVersion = +process.version.substr(1).split(".")[0];

// Only run in AppVeyor if version >= 5
if(isWindowsCI && nodeVersion < 5) {
    process.exit(0);
}

var guide = automate({ spinner: true, log: true });
var wait = function(){
    var ms = isWindowsCI ? 5000 : 2000;
	return guide.wait(ms);
};

/**
 * @Step 1
 */
guide.step("Remove existing dependencies", function(){
  rimraf(join("node_modules", "donejs-cli"));
  rimraf(join("node_modules", "generator-donejs"));
});

guide.step("Install donejs", function(){
  var branch = process.env.TRAVIS_BRANCH || process.env.APPVEYOR_REPO_BRANCH || 'master';

	return guide.executeCommand("npm", ["install", "donejs/donejs#" + branch, "-g"]);
});

// Move to a temp folder for the rest of the guide
guide.moveToTmp();

/**
 * @Step 2
 */
guide.step("Run donejs add app", function(){
	var init = guide.answerPrompts("donejs", ["add", "app", "donejs-chat", "--skip-install"]);
	var answer = init.answer;

	answer(/Project name/, "\n");
	answer(/Project main folder/, "src\n");
	answer(/Description/, "\n");
	answer(/Project homepage url/, "\n");
	answer(/GitHub username or organization/, "\n");
	answer(/Author's Name/, "\n");
	answer(/Author's Email/, "\n");
	answer(/Author's Homepage/, "\n");
	answer(/Application keywords/, "\n");
	answer(/NPM version/, "\n");

	return init.promise;
});

/**
 * @Step 3
 */
guide.step("Move to donejs-chat folder", function(){
	process.chdir("donejs-chat");
	return guide.injectSpy("src/index.stache");
});

guide.step("Run NPM install", function() {
	return guide.executeCommand("npm", ["install"]).then(guide.wait(10000));
});

/**
 * @Step 4
 */
guide.step("Start donejs develop", function(){
	var child = guide.canServe = guide.executeCommand("donejs", ["develop"]).childProcess;
  console.log("done-serve running as", child.pid);

	var server = streamWhen(child.stdout, /done-serve starting on/);
	var liveReload = streamWhen(child.stderr, /Live-reload server/);
	return Promise.all([server, liveReload]).then(wait);
});

guide.test(function(){
	return guide.nodeTest(join(__dirname, "steps", "3-donejs-develop", "test.js"));
});

guide.launchBrowser("http://localhost:8080");

/**
 * @Step 5
 */
guide.step("Install bootstrap", function(){
	return guide.executeCommand("npm", ["install", "bootstrap", "--save"])
		.then(wait)
		.then(function(){
			return guide.replaceFile(join("src", "index.stache"),
									 join(__dirname, "steps", "4-bootstrap", "index.stache"));
		})
    .then(function(){
      return guide.injectSpy("src/index.stache");
    })
    .then(wait)
		.then(wait);
});

guide.test(function(){
  console.log("Running bootstrap tests");
	return guide.functionalTest(join(__dirname, "steps", "4-bootstrap", "test.js"))
		.then(wait);
});

/**
 * @Step 6
 */
guide.step("Generate custom elements", function(){
	return guide.executeCommand("donejs", ["add","component","home.component","chat-home"])
		.then(function(){
			return guide.executeCommand("donejs", ["add","component","messages",
								  "chat-messages"]);
		}).then(wait);
});

/**
 * @Step 7
 */
guide.step("Navigate between pages", function(){
	return guide.replaceFile(join("src", "home.component"),
					   join(__dirname, "steps", "7-navigate", "home.component"))
		.then(function(){
			return guide.replaceFile(join("src", "messages", "messages.stache"),
							   join(__dirname, "steps", "7-navigate", "messages.stache"));
		})
		.then(function(){
			return guide.replaceFile(join("src", "app.js"),
							   join(__dirname, "steps", "7-navigate", "app.js"));
		})
		.then(function(){
			return guide.replaceFile(join("src", "index.stache"),
							   join(__dirname, "steps", "7-navigate", "index.stache"));
		})
    .then(function(){
      return guide.injectSpy("src/index.stache");
    })
    .then(wait);
});

guide.test(function(){
	return guide.functionalTest(join(__dirname, "steps", "7-navigate", "test.js"));
});

guide.test(function(){
	return guide.nodeTest(join(__dirname, "steps", "7-navigate", "ssr_test.js"));
});

/**
 * @Step 8
 */
guide.step("Install and use bit-tabs", function(){
  function installWait(){
    return guide.wait(10000);
  }

	return guide.executeCommand("npm", ["install", "bit-tabs@alpha", "--save"])
    .then(installWait)
		.then(function(){
			return guide.replaceFile(join("src", "home.component"),
									 join(__dirname, "steps", "8-bit-tabs", "home.component"));
		})
		.then(wait);
});

guide.test(function(){
  console.log("Running bit-tabs tests");
	// Check that the tabs have appeared.
	return guide.functionalTest(join(__dirname, "steps", "8-bit-tabs", "tabs_test.js"))
		.then(wait);
});

/**
 * @Step 9
 */
guide.step("Generate the Message model", function(){
	var supermodel = guide.answerPrompts("donejs", ["add", "supermodel", "message"]);
	var answer = supermodel.answer;

	answer(/URL endpoint/, "http://chat.donejs.com/api/messages\n");
	answer(/property name/, "\n");
  answer(/service URL/, "Yes\n");

	return supermodel.promise;
});

/**
 * @Step 10
 */
guide.step("Use the connection", function(){
	return guide.replaceFile(join("src", "messages", "messages.stache"),
							 join(__dirname, "steps", "10-use-connection", "messages.stache"))
    .then(function() {
      return guide.replaceFile(join("src", "messages", "messages.js"),
                   join(__dirname, "steps", "10-use-connection", "messages.js"))
    })
		.then(wait);
});

guide.test(function(){
	return guide.functionalTest(join(__dirname, "steps", "10-use-connection", "test.js"));
});

/**
 * @Step 11
 */
guide.step("Create messages", function(){
  return guide.replaceFile(join("src", "messages", "messages.js"),
               join(__dirname, "steps", "11-create-messages", "messages.js"))
		.then(function(){
      return guide.replaceFile(join("src", "messages", "messages.stache"),
    							 join(__dirname, "steps", "11-create-messages", "messages.stache"));
		}).then(wait);
});

guide.test(function(){
	return guide.functionalTest(join(__dirname, "steps", "11-create-messages", "test.js"));
});

/**
 * @Step 12
 */
guide.step("Enable a real-time connection", function(){
	return guide.executeCommand("npm", ["install", "steal-socket.io@2", "--save"])
		.then(wait)
		.then(function(){
			return guide.replaceFile(join("src", "models", "message.js"),
									 join(__dirname, "steps", "12-real-time", "message.js"));
		});

});

guide.test(function(){
	return guide.functionalTest(join(__dirname, "steps", "12-real-time", "test.js"));
});

/**
 * @Step 13
 */
guide.closeBrowser();

guide.step("Stop development mode", function(){
  return guide.kill(guide.canServe)
    .then(function(){
      guide.canServe = null;
      return guide.wait(2000);
    });
});

/**
 * @ Step 14
 */

guide.step("Production build", function(){
	return guide.executeCommand("donejs", ["build"]);
});

/**
 * @ Step 15
 */
guide.step("Open in production", function(){
	process.env["NODE_ENV"] = "production";

	var child = guide.canServe = guide.executeCommand("donejs", ["start"])
		.childProcess;

	var server = streamWhen(child.stdout, /done-serve starting on/);
	return server.then(wait);
});

guide.test(function(){
	return guide.nodeTest(join(__dirname, "steps", "15-production", "test.js"));
});

guide.step("Stop production mode", function(){
	return guide.kill(guide.canServe)
		.then(function(){
			guide.canServe = null;
			return guide.wait(2000);
		});
});

/**
 * @Step 16
 */
guide.stepIf("Deploy to CDN", function() {
	return !!guide.options.app;
}, function(){
  return guide.executeCommand("donejs", ["add", "firebase"])
		.then(function(){
			return guide.executeCommand("donejs", ["build"]);
		})
		.then(function(){
			return guide.executeCommand("donejs", ["deploy"]);
		});
});

/**
 * @Step 17
 */
guide.stepIf("Desktop and mobile apps: Cordova", function() {
	return process.platform === 'darwin';
}, function(){
	return guide.executeCommand("npm", ["install", "-g", "ios-sim"])
		.then(function(){
			var proc = guide.answerPrompts("donejs", ["add", "cordova"]);
			var answer = proc.answer;

			answer(/Name of project/, "donejs chat\n");
			answer(/ID of project/, "com.donejs.donejschat\n");
      answer(/service layer/, "\n");
			answer(/What platforms/, "\n");

			return proc.promise;
		})
		.then(function(){
			return guide.executeCommand("donejs", ["build", "cordova"]);
		});
});

/**
 * @Step 18
 */
guide.step("Desktop and mobile apps: Electron", function(){
	var proc = guide.answerPrompts("donejs", ["add", "electron"]);
	var answer = proc.answer;

	answer(/Main HTML file/, "\n");
  answer(/service layer/, "\n");
	answer(/What platforms/, "\n");
	answer(/What architectures/, "\n");

	return proc.promise
		.then(function(){
			return guide.executeCommand("donejs", ["build", "electron"]);
		});
});

/**
 *  Run the test
 */

guide.run().then(
	function(){
		console.log("All done!");
		return 0;
	},
	function(err){
		console.error("Oh no", err.message, err.stack, err);
		return 1;
	}
).then(function(exitCode){
	if(guide.canServe) {
		guide.canServe.kill();
	}
	return exitCode;
}).then(function(exitCode){
	process.exit(exitCode);
});
<|endoftext|>


<commit_before>(function () {

  describe('Function Exercises', function () {

      var ACTUAL;

      // This resets the value of ACTUAL (to null) before each test is run
      beforeEach(function () {
        ACTUAL = null;
      });

      it('add a top to an array', function () {
        var fn = function (mytop) {
          var tops = mytop;
          ACTUAL = tops;
        };
        fn("shirt");
        expect(ACTUAL === 'shirt').to.be.true;
      });

      it('add a second top to an array', function () {
        var fn = function (mytop) {
          var tops = ['white shirt'];
          tops.push(mytop);
          ACTUAL = tops;
        };
        fn('blue shirt');
        alert(ACTUAL);
        expect(ACTUAL === ['white shirt','blue shirt']).to.be.true;
      });

      it('a function has access to the variables contained within the same scope that function was created in', function () {
        var name = 'outer';
        var fn = function () {
          ACTUAL = name;
        };
        fn();
        expect(ACTUAL === '???').to.be.true;
      });

      it('a function\'s local scope variables are not available anywhere outside that function', function () {
        var firstFn = function () {
          var localToFirstFn = 'inner';
        };
        firstFn();
        expect(function () {
          ACTUAL = localToFirstFn;
        }).to.throw();
        expect(ACTUAL === '???').to.be.true;
      });

      it('a function\'s local scope variables are not available anywhere outside that function, regardless of the context it\'s called in', function () {
        var firstFn = function () {
          var localToFirstFn = 'first';
          // Although false, it might seem reasonable to think that the secondFn (which mentions the localToFirstFn variable), should have access to the localToFirstFn variable, since it's being called here from within the scope where that variable is declared.
          secondFn();
        };
        var secondFn = function () {
          ACTUAL = localToFirstFn;
        };
        expect(function () {
          // of course, calling the secondFn should throw an error in this case, since secondFn does not have access to the localToFirstFn variable
          secondFn();
        }).to.throw();
        expect(function () {
          // in addition, calling the firstFn (which in turn calls the secondFn) should also throw, since it the calling context of secondFn has no influence over its scope access rules
          firstFn();
        }).to.throw();
        expect(ACTUAL === '???').to.be.true;
      });

      it('if an inner and an outer variable share the same name, and the name is referenced in the inner scope, the inner scope variable masks the variable from the outer scope with the same name. This renders the outer scope variables inaccassible from anywhere within the inner function block', function () {
        var sameName = 'outer';
        var fn = function () {
          var sameName = 'inner';
          ACTUAL = sameName;
        };
        fn();
        expect(ACTUAL === '???').to.be.true;
      });

      it('if an inner and an outer variable share the same name, and the name is referenced in the outer scope, the outer value binding will be used', function () {
        var sameName = 'outer';
        var fn = function () {
          var sameName = 'inner';
        };
        fn();
        ACTUAL = sameName;
        expect(ACTUAL === '???').to.be.true;
      });

      it('a new variable scope is created for every call to a function, as exemplified with a counter', function () {
        var fn = function () {
          // the `||` symbol here is being used to set a default value for innerCounter. If innerCounter already contains a truthy value, then the value in that variable will be unchanged. If it is falsey however (such as if it were completely uninitialized), then this line will set it to the default value of 10.
          var innerCounter = innerCounter || 10;
          innerCounter = innerCounter + 1;
          ACTUAL = innerCounter;
        };

        fn();
        expect(ACTUAL === '???').to.be.true;
        fn();
        expect(ACTUAL === '???').to.be.true;
      });

      it('a new variable scope is created for each call to a function, as exemplified with uninitialized string variables', function () {
        // this is a longer form of the same observation as above, using strings in stead of numbers.
        var fn = function () {
          var localVariable;
          if (localVariable === undefined) {
            // the variable will be initialized for the first time during this call to fn
            ACTUAL = 'alpha';
          } else if (localVariable === 'initialized') {
            // the variable has already been initialized by a previous call to fn
            ACTUAL = 'omega';
          }
          // now that actual has been set, we will initialize localVariable to refer to a string
          localVariable = 'initialized';
        };

        fn();
        expect(ACTUAL === '???').to.be.true;
        fn();
        expect(ACTUAL === '???').to.be.true;
      });

      it('an inner function can access both its local scope variables and variables in its containing scope, provided the variables have different names', function () {
        var outerName = 'outer';
        var fn = function () {
          var innerName = 'inner';
          ACTUAL = innerName + outerName;
        };
        fn();
        expect(ACTUAL === '???').to.be.true;
      });

      it('between calls to an inner function, that inner function retains access to a variable in an outer scope. Modifying those variables has a lasting effect between calls to the inner function.', function () {
        var outerCounter = 10;

        var fn = function () {
          outerCounter = outerCounter + 1;
          ACTUAL = outerCounter;
        };

        fn();
        expect(ACTUAL === '???').to.be.true;
        fn();
        expect(ACTUAL === '???').to.be.true;
      });

      it('the rule about retaining access to variables from an outer scope still applies, even after the outer function call (that created the outer scope) has returned', function () {
        var outerFn = function () {
          // NOTE: the contents of this function is the same as the entire body of the previous test
          var counterInOuterScope = 10;

          var innerIncrementingFn = function () {
            counterInOuterScope = counterInOuterScope + 1;
            ACTUAL = counterInOuterScope;
          };

          innerIncrementingFn();
          expect(ACTUAL === '???').to.be.true;
          innerIncrementingFn();
          expect(ACTUAL === '???').to.be.true;
          // Here, we retain a reference to the newly created inner function for later, by assigning it to the global scope (window)
          window.retainedInnerFn = innerIncrementingFn;

        };

        // before we run outerFn, there will be no innerFn exported to the global scope
        expect(window.retainedInnerFn).to.equal.undefined;
        // running this outer function should have the same effect as running the whole previous test, with the addition of placing the innerFn somewhere that we can reach it after outerFn has returned
        outerFn();
        expect(window.retainedInnerFn).to.be.a('function');
        // even though the outerFn has returned once the only call to it was completed a couple of lines above, the inner function remains available in the global scope, and still has access to the variables of that containing scope where it was first created.
        window.retainedInnerFn();
        expect(ACTUAL === '???').to.be.true;
      });

  });

})();
<commit_msg>update test<commit_after>(function () {

  describe('Function Exercises', function () {

      var ACTUAL;

      // This resets the value of ACTUAL (to null) before each test is run
      beforeEach(function () {
        ACTUAL = null;
      });

      it('add a top to an array', function () {
        var fn = function (mytop) {
          var tops = mytop;
          ACTUAL = tops;
        };
        fn("shirt");
        expect(ACTUAL === 'shirt').to.be.true;
      });

      it('add a second top to an array', function () {
        var fn = function (mytop) {
          var tops = ['white shirt'];
          tops.push(mytop);
          ACTUAL = tops;
        };
        fn('blue shirt');
        alert(ACTUAL);
        assert(Array.isArray([ACTUAL]))
        expect(ACTUAL === [['white shirt,blue shirt']]).to.be.true;
      });

      it('a function has access to the variables contained within the same scope that function was created in', function () {
        var name = 'outer';
        var fn = function () {
          ACTUAL = name;
        };
        fn();
        expect(ACTUAL === '???').to.be.true;
      });

      it('a function\'s local scope variables are not available anywhere outside that function', function () {
        var firstFn = function () {
          var localToFirstFn = 'inner';
        };
        firstFn();
        expect(function () {
          ACTUAL = localToFirstFn;
        }).to.throw();
        expect(ACTUAL === '???').to.be.true;
      });

      it('a function\'s local scope variables are not available anywhere outside that function, regardless of the context it\'s called in', function () {
        var firstFn = function () {
          var localToFirstFn = 'first';
          // Although false, it might seem reasonable to think that the secondFn (which mentions the localToFirstFn variable), should have access to the localToFirstFn variable, since it's being called here from within the scope where that variable is declared.
          secondFn();
        };
        var secondFn = function () {
          ACTUAL = localToFirstFn;
        };
        expect(function () {
          // of course, calling the secondFn should throw an error in this case, since secondFn does not have access to the localToFirstFn variable
          secondFn();
        }).to.throw();
        expect(function () {
          // in addition, calling the firstFn (which in turn calls the secondFn) should also throw, since it the calling context of secondFn has no influence over its scope access rules
          firstFn();
        }).to.throw();
        expect(ACTUAL === '???').to.be.true;
      });

      it('if an inner and an outer variable share the same name, and the name is referenced in the inner scope, the inner scope variable masks the variable from the outer scope with the same name. This renders the outer scope variables inaccassible from anywhere within the inner function block', function () {
        var sameName = 'outer';
        var fn = function () {
          var sameName = 'inner';
          ACTUAL = sameName;
        };
        fn();
        expect(ACTUAL === '???').to.be.true;
      });

      it('if an inner and an outer variable share the same name, and the name is referenced in the outer scope, the outer value binding will be used', function () {
        var sameName = 'outer';
        var fn = function () {
          var sameName = 'inner';
        };
        fn();
        ACTUAL = sameName;
        expect(ACTUAL === '???').to.be.true;
      });

      it('a new variable scope is created for every call to a function, as exemplified with a counter', function () {
        var fn = function () {
          // the `||` symbol here is being used to set a default value for innerCounter. If innerCounter already contains a truthy value, then the value in that variable will be unchanged. If it is falsey however (such as if it were completely uninitialized), then this line will set it to the default value of 10.
          var innerCounter = innerCounter || 10;
          innerCounter = innerCounter + 1;
          ACTUAL = innerCounter;
        };

        fn();
        expect(ACTUAL === '???').to.be.true;
        fn();
        expect(ACTUAL === '???').to.be.true;
      });

      it('a new variable scope is created for each call to a function, as exemplified with uninitialized string variables', function () {
        // this is a longer form of the same observation as above, using strings in stead of numbers.
        var fn = function () {
          var localVariable;
          if (localVariable === undefined) {
            // the variable will be initialized for the first time during this call to fn
            ACTUAL = 'alpha';
          } else if (localVariable === 'initialized') {
            // the variable has already been initialized by a previous call to fn
            ACTUAL = 'omega';
          }
          // now that actual has been set, we will initialize localVariable to refer to a string
          localVariable = 'initialized';
        };

        fn();
        expect(ACTUAL === '???').to.be.true;
        fn();
        expect(ACTUAL === '???').to.be.true;
      });

      it('an inner function can access both its local scope variables and variables in its containing scope, provided the variables have different names', function () {
        var outerName = 'outer';
        var fn = function () {
          var innerName = 'inner';
          ACTUAL = innerName + outerName;
        };
        fn();
        expect(ACTUAL === '???').to.be.true;
      });

      it('between calls to an inner function, that inner function retains access to a variable in an outer scope. Modifying those variables has a lasting effect between calls to the inner function.', function () {
        var outerCounter = 10;

        var fn = function () {
          outerCounter = outerCounter + 1;
          ACTUAL = outerCounter;
        };

        fn();
        expect(ACTUAL === '???').to.be.true;
        fn();
        expect(ACTUAL === '???').to.be.true;
      });

      it('the rule about retaining access to variables from an outer scope still applies, even after the outer function call (that created the outer scope) has returned', function () {
        var outerFn = function () {
          // NOTE: the contents of this function is the same as the entire body of the previous test
          var counterInOuterScope = 10;

          var innerIncrementingFn = function () {
            counterInOuterScope = counterInOuterScope + 1;
            ACTUAL = counterInOuterScope;
          };

          innerIncrementingFn();
          expect(ACTUAL === '???').to.be.true;
          innerIncrementingFn();
          expect(ACTUAL === '???').to.be.true;
          // Here, we retain a reference to the newly created inner function for later, by assigning it to the global scope (window)
          window.retainedInnerFn = innerIncrementingFn;

        };

        // before we run outerFn, there will be no innerFn exported to the global scope
        expect(window.retainedInnerFn).to.equal.undefined;
        // running this outer function should have the same effect as running the whole previous test, with the addition of placing the innerFn somewhere that we can reach it after outerFn has returned
        outerFn();
        expect(window.retainedInnerFn).to.be.a('function');
        // even though the outerFn has returned once the only call to it was completed a couple of lines above, the inner function remains available in the global scope, and still has access to the variables of that containing scope where it was first created.
        window.retainedInnerFn();
        expect(ACTUAL === '???').to.be.true;
      });

  });

})();
<|endoftext|>


<commit_before>/*-----------------------------------------------------------------------------
 **
 ** - Fennel Card-/CalDAV -
 **
 ** Copyright 2014 by
 ** SwordLord - the coding crew - http://www.swordlord.com
 ** and contributing authors
 **
 -----------------------------------------------------------------------------*/

var xml = require("libxmljs");
var rh = require("../libs/responsehelper");
var xh = require("../libs/xmlhelper");
var log = require('../libs/log').log;

// Exporting.
module.exports = {
    propfind: propfind,
    proppatch: proppatch,
    report: report,
    options: options
};

function propfind(request)
{
    log.debug("principal.propfind called");

    rh.setStandardHeaders(request);
    rh.setDAVHeaders(request);

    var res = request.getRes();
    res.writeHead(207);
    res.write(xh.getXMLHead());

    var body = request.getBody();
    var xmlDoc = xml.parseXml(body);

    var node = xmlDoc.get('/A:propfind/A:prop', {
        A: 'DAV:',
        B: "urn:ietf:params:xml:ns:caldav",
        C: 'http://calendarserver.org/ns/',
        D: "http://apple.com/ns/ical/",
        E: "http://me.com/_namespace/"
    });
    var childs = node.childNodes();

    var response = "";

    var len = childs.length;
    for (var i=0; i < len; ++i)
    {
        var child = childs[i];
        var name = child.name();
        switch(name)
        {
            case 'checksum-versions':
                response += "";
                break;

            case 'sync-token':
                response += "<d:sync-token>http://sabredav.org/ns/sync/5</d:sync-token>";
                break;

            case 'supported-report-set':
                response += getSupportedReportSet(request);
                break;

            case 'principal-URL':
                response += "<d:principal-URL><d:href>/p/" + request.getUser().getUserName() + "/</d:href></d:principal-URL>\r\n";
                break;

            case 'displayname':
                response += "<d:displayname>" + request.getUser().getUserName() + "</d:displayname>";
                break;

            case 'principal-collection-set':
                response += "<d:principal-collection-set><d:href>/p/</d:href></d:principal-collection-set>";
                break;

            case 'current-user-principal':
                response += "<d:current-user-principal><d:href>/p/" + request.getUser().getUserName() + "/</d:href></d:current-user-principal>";
                break;

            case 'calendar-home-set':
                response += "<cal:calendar-home-set><d:href>/cal/" + request.getUser().getUserName() + "</d:href></cal:calendar-home-set>";
                break;

            case 'schedule-outbox-URL':
                response += "<cal:schedule-outbox-URL><d:href>/cal/" + request.getUser().getUserName() + "/outbox</d:href></cal:schedule-outbox-URL>";
                break;

            case 'calendar-user-address-set':
                response += getCalendarUserAddressSet(request);
                break;

            case 'notification-URL':
                response += "<cs:notification-URL><d:href>/cal/" + request.getUser().getUserName() + "/notifications/</d:href></cs:notification-URL>";
                break;

            case 'getcontenttype':
                response += "";
                break;

            case 'addressbook-home-set':
                response += "<card:addressbook-home-set><d:href>/card/" + request.getUser().getUserName() + "/</d:href></card:addressbook-home-set>";
                break;

            case 'directory-gateway':
                response += "";
                break;
            case 'email-address-set':
                response += "";
                break;
            case 'resource-id':
                response += "";
                break;

            default:
                if(name != 'text') log.warn("P-PF: not handled: " + name);
                break;
        }
    }

    res.write("<d:multistatus xmlns:d=\"DAV:\" xmlns:cal=\"urn:ietf:params:xml:ns:caldav\" xmlns:cs=\"http://calendarserver.org/ns/\" xmlns:card=\"urn:ietf:params:xml:ns:carddav\">");
    res.write("<d:response>");
    res.write("<d:propstat>");
    res.write("<d:prop>");
    res.write(response);
    res.write("</d:prop>");
    res.write("<d:status>HTTP/1.1 200 OK</d:status>");
    res.write("</d:propstat>");
    res.write("</d:response>");
    res.write("</d:multistatus>");
}

function getCalendarUserAddressSet(request)
{
    var response = "";

    response += "        <cal:calendar-user-address-set>\r\n";
    response += "        	<d:href>mailto:lord test at swordlord.com</d:href>\r\n";
    response += "        	<d:href>/p/" + request.getUser().getUserName() + "/</d:href>\r\n";
    response += "        </cal:calendar-user-address-set>\r\n";

    return response;
}

function getSupportedReportSet(request)
{
    var response = "";
    response += "        <d:supported-report-set>\r\n";
    response += "        	<d:supported-report>\r\n";
    response += "        		<d:report>\r\n";
    response += "        			<d:expand-property/>\r\n";
    response += "        		</d:report>\r\n";
    response += "        	</d:supported-report>\r\n";
    response += "        	<d:supported-report>\r\n";
    response += "        		<d:report>\r\n";
    response += "        			<d:principal-property-search/>\r\n";
    response += "        		</d:report>\r\n";
    response += "        	</d:supported-report>\r\n";
    response += "        	<d:supported-report>\r\n";
    response += "        		<d:report>\r\n";
    response += "        			<d:principal-search-property-set/>\r\n";
    response += "        		</d:report>\r\n";
    response += "        	</d:supported-report>\r\n";
    response += "        </d:supported-report-set>\r\n";

    return response;
}

function options(request)
{
    log.debug("principal.options called");

    var res = request.getRes();
    res.setHeader("Content-Type", "text/html");
    res.setHeader("Server", "Fennel");

    rh.setDAVHeaders(request);
    rh.setAllowHeader(request);

    var res = request.getRes();
    res.writeHead(200);
}

function report(request)
{
    log.debug("principal.report called");

    rh.setStandardHeaders(request);

    var res = request.getRes();
    res.writeHead(200);
    res.write(xh.getXMLHead());

    var body = request.getBody();
    var xmlDoc = xml.parseXml(body);

    var node = xmlDoc.get('/A:propfind/A:prop', {
        A: 'DAV:',
        B: "urn:ietf:params:xml:ns:caldav",
        C: 'http://calendarserver.org/ns/',
        D: "http://apple.com/ns/ical/",
        E: "http://me.com/_namespace/"
    });

    var response = "";

    if(node != undefined)
    {
        var childs = node.childNodes();

        var len = childs.length;
        for (var i=0; i < len; ++i)
        {
            var child = childs[i];
            var name = child.name();
            switch(name)
            {
                case 'principal-search-property-set':
                    response += getPrincipalSearchPropertySet(request);
                    break;

                default:
                    if(name != 'text') log.warn("P-R: not handled: " + name);
                    break;
            }
        }
    }

    node = xmlDoc.get('/A:principal-search-property-set', {
        A: 'DAV:',
        B: "urn:ietf:params:xml:ns:caldav",
        C: 'http://calendarserver.org/ns/',
        D: "http://apple.com/ns/ical/",
        E: "http://me.com/_namespace/"
    });

    if(node != undefined)
    {
        var name = node.name();
        switch(name)
        {
            case 'principal-search-property-set':
                response += getPrincipalSearchPropertySet(request);
                break;

            default:
                if(name != 'text') log.warn("P-R: not handled: " + name);
                break;
        }
    }

    // TODO: clean up
    res.write(response);

    if(isReportPropertyCalendarProxyWriteFor(request))
    {
        replyPropertyCalendarProxyWriteFor(request);
    }
}

function getPrincipalSearchPropertySet(request)
{
    var response = "";
    response += "<d:principal-search-property-set xmlns:d=\"DAV:\" xmlns:cal=\"urn:ietf:params:xml:ns:caldav\" xmlns:cs=\"http://calendarserver.org/ns/\" xmlns:card=\"urn:ietf:params:xml:ns:carddav\">\r\n";
    response += "  <d:principal-search-property>\r\n";
    response += "    <d:prop>\r\n";
    response += "      <d:displayname/>\r\n";
    response += "    </d:prop>\r\n";
    response += "    <d:description xml:lang=\"en\">Display name</d:description>\r\n";
    response += "  </d:principal-search-property>\r\n";
//    response += "  <d:principal-search-property>\r\n";
//    response += "    <d:prop>\r\n";
//    response += "      <s:email-address/>\r\n";
//    response += "    </d:prop>\r\n";
//    response += "    <d:description xml:lang=\"en\">Email address</d:description>\r\n";
//    response += "  </d:principal-search-property>\r\n";
    response += "</d:principal-search-property-set>\r\n";

    return response;
}

function isReportPropertyCalendarProxyWriteFor(request)
{
    var body = request.getBody();
    var xmlDoc = xml.parseXml(body);

    var node = xmlDoc.get('/A:expand-property/A:property[@name=\'calendar-proxy-write-for\']', { A: 'DAV:', C: 'http://calendarserver.org/ns/'});

    return typeof node != 'undefined';
}

function replyPropertyCalendarProxyWriteFor(request)
{
    var url = request.getURL();
    var res = request.getRes();
    res.write("<d:multistatus xmlns:d=\"DAV:\" xmlns:cal=\"urn:ietf:params:xml:ns:caldav\" xmlns:cs=\"http://calendarserver.org/ns/\" xmlns:card=\"urn:ietf:params:xml:ns:carddav\">\r\n");
    res.write("<d:response>");
    res.write("    <d:href>" + url + "</d:href>");
    res.write("    <d:propstat>");
    res.write("       <d:prop>");
    res.write("           <cs:calendar-proxy-read-for/>");
    res.write("           <cs:calendar-proxy-write-for/>");
    res.write("       </d:prop>");
    res.write("        <d:status>HTTP/1.1 200 OK</d:status>");
    res.write("    </d:propstat>");
    res.write("</d:response>");
    res.write("</d:multistatus>\r\n");
}

function proppatch(request)
{
    log.debug("principal.proppatch called");

    rh.setStandardHeaders(request);

    var url = request.getURL();
    var res = request.getRes();
    res.writeHead(200);

    res.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
    res.write("<d:multistatus xmlns:d=\"DAV:\" xmlns:cal=\"urn:ietf:params:xml:ns:caldav\" xmlns:cs=\"http://calendarserver.org/ns/\" xmlns:card=\"urn:ietf:params:xml:ns:carddav\">\r\n");
    res.write("	<d:response>\r\n");
    res.write("		<d:href>" + url + "</d:href>\r\n");
    res.write("		<d:propstat>\r\n");
    res.write("			<d:prop>\r\n");
    res.write("				<cal:default-alarm-vevent-date/>\r\n");
    res.write("			</d:prop>\r\n");
    res.write("			<d:status>HTTP/1.1 403 Forbidden</d:status>\r\n");
    res.write("		</d:propstat>\r\n");
    res.write("	</d:response>\r\n");
    res.write("</d:multistatus>\r\n");
}

<commit_msg>removed an unneeded line<commit_after>/*-----------------------------------------------------------------------------
 **
 ** - Fennel Card-/CalDAV -
 **
 ** Copyright 2014-15 by
 ** SwordLord - the coding crew - http://www.swordlord.com
 ** and contributing authors
 **
 -----------------------------------------------------------------------------*/

var xml = require("libxmljs");
var rh = require("../libs/responsehelper");
var xh = require("../libs/xmlhelper");
var log = require('../libs/log').log;

// Exporting.
module.exports = {
    propfind: propfind,
    proppatch: proppatch,
    report: report,
    options: options
};

function propfind(request)
{
    log.debug("principal.propfind called");

    rh.setStandardHeaders(request);
    rh.setDAVHeaders(request);

    var res = request.getRes();
    res.writeHead(207);
    res.write(xh.getXMLHead());

    var body = request.getBody();
    var xmlDoc = xml.parseXml(body);

    var node = xmlDoc.get('/A:propfind/A:prop', {
        A: 'DAV:',
        B: "urn:ietf:params:xml:ns:caldav",
        C: 'http://calendarserver.org/ns/',
        D: "http://apple.com/ns/ical/",
        E: "http://me.com/_namespace/"
    });
    var childs = node.childNodes();

    var response = "";

    var len = childs.length;
    for (var i=0; i < len; ++i)
    {
        var child = childs[i];
        var name = child.name();
        switch(name)
        {
            case 'checksum-versions':
                response += "";
                break;

            case 'sync-token':
                response += "<d:sync-token>http://sabredav.org/ns/sync/5</d:sync-token>";
                break;

            case 'supported-report-set':
                response += getSupportedReportSet(request);
                break;

            case 'principal-URL':
                response += "<d:principal-URL><d:href>/p/" + request.getUser().getUserName() + "/</d:href></d:principal-URL>\r\n";
                break;

            case 'displayname':
                response += "<d:displayname>" + request.getUser().getUserName() + "</d:displayname>";
                break;

            case 'principal-collection-set':
                response += "<d:principal-collection-set><d:href>/p/</d:href></d:principal-collection-set>";
                break;

            case 'current-user-principal':
                response += "<d:current-user-principal><d:href>/p/" + request.getUser().getUserName() + "/</d:href></d:current-user-principal>";
                break;

            case 'calendar-home-set':
                response += "<cal:calendar-home-set><d:href>/cal/" + request.getUser().getUserName() + "</d:href></cal:calendar-home-set>";
                break;

            case 'schedule-outbox-URL':
                response += "<cal:schedule-outbox-URL><d:href>/cal/" + request.getUser().getUserName() + "/outbox</d:href></cal:schedule-outbox-URL>";
                break;

            case 'calendar-user-address-set':
                response += getCalendarUserAddressSet(request);
                break;

            case 'notification-URL':
                response += "<cs:notification-URL><d:href>/cal/" + request.getUser().getUserName() + "/notifications/</d:href></cs:notification-URL>";
                break;

            case 'getcontenttype':
                response += "";
                break;

            case 'addressbook-home-set':
                response += "<card:addressbook-home-set><d:href>/card/" + request.getUser().getUserName() + "/</d:href></card:addressbook-home-set>";
                break;

            case 'directory-gateway':
                response += "";
                break;
            case 'email-address-set':
                response += "";
                break;
            case 'resource-id':
                response += "";
                break;

            default:
                if(name != 'text') log.warn("P-PF: not handled: " + name);
                break;
        }
    }

    res.write("<d:multistatus xmlns:d=\"DAV:\" xmlns:cal=\"urn:ietf:params:xml:ns:caldav\" xmlns:cs=\"http://calendarserver.org/ns/\" xmlns:card=\"urn:ietf:params:xml:ns:carddav\">");
    res.write("<d:response>");
    res.write("<d:propstat>");
    res.write("<d:prop>");
    res.write(response);
    res.write("</d:prop>");
    res.write("<d:status>HTTP/1.1 200 OK</d:status>");
    res.write("</d:propstat>");
    res.write("</d:response>");
    res.write("</d:multistatus>");
}

function getCalendarUserAddressSet(request)
{
    var response = "";

    response += "        <cal:calendar-user-address-set>\r\n";
    response += "        	<d:href>mailto:lord test at swordlord.com</d:href>\r\n";
    response += "        	<d:href>/p/" + request.getUser().getUserName() + "/</d:href>\r\n";
    response += "        </cal:calendar-user-address-set>\r\n";

    return response;
}

function getSupportedReportSet(request)
{
    var response = "";
    response += "        <d:supported-report-set>\r\n";
    response += "        	<d:supported-report>\r\n";
    response += "        		<d:report>\r\n";
    response += "        			<d:expand-property/>\r\n";
    response += "        		</d:report>\r\n";
    response += "        	</d:supported-report>\r\n";
    response += "        	<d:supported-report>\r\n";
    response += "        		<d:report>\r\n";
    response += "        			<d:principal-property-search/>\r\n";
    response += "        		</d:report>\r\n";
    response += "        	</d:supported-report>\r\n";
    response += "        	<d:supported-report>\r\n";
    response += "        		<d:report>\r\n";
    response += "        			<d:principal-search-property-set/>\r\n";
    response += "        		</d:report>\r\n";
    response += "        	</d:supported-report>\r\n";
    response += "        </d:supported-report-set>\r\n";

    return response;
}

function options(request)
{
    log.debug("principal.options called");

    var res = request.getRes();
    res.setHeader("Content-Type", "text/html");
    res.setHeader("Server", "Fennel");

    rh.setDAVHeaders(request);
    rh.setAllowHeader(request);

    res.writeHead(200);
}

function report(request)
{
    log.debug("principal.report called");

    rh.setStandardHeaders(request);

    var res = request.getRes();
    res.writeHead(200);
    res.write(xh.getXMLHead());

    var body = request.getBody();
    var xmlDoc = xml.parseXml(body);

    var node = xmlDoc.get('/A:propfind/A:prop', {
        A: 'DAV:',
        B: "urn:ietf:params:xml:ns:caldav",
        C: 'http://calendarserver.org/ns/',
        D: "http://apple.com/ns/ical/",
        E: "http://me.com/_namespace/"
    });

    var response = "";

    if(node != undefined)
    {
        var childs = node.childNodes();

        var len = childs.length;
        for (var i=0; i < len; ++i)
        {
            var child = childs[i];
            var name = child.name();
            switch(name)
            {
                case 'principal-search-property-set':
                    response += getPrincipalSearchPropertySet(request);
                    break;

                default:
                    if(name != 'text') log.warn("P-R: not handled: " + name);
                    break;
            }
        }
    }

    node = xmlDoc.get('/A:principal-search-property-set', {
        A: 'DAV:',
        B: "urn:ietf:params:xml:ns:caldav",
        C: 'http://calendarserver.org/ns/',
        D: "http://apple.com/ns/ical/",
        E: "http://me.com/_namespace/"
    });

    if(node != undefined)
    {
        var name = node.name();
        switch(name)
        {
            case 'principal-search-property-set':
                response += getPrincipalSearchPropertySet(request);
                break;

            default:
                if(name != 'text') log.warn("P-R: not handled: " + name);
                break;
        }
    }

    // TODO: clean up
    res.write(response);

    if(isReportPropertyCalendarProxyWriteFor(request))
    {
        replyPropertyCalendarProxyWriteFor(request);
    }
}

function getPrincipalSearchPropertySet(request)
{
    var response = "";
    response += "<d:principal-search-property-set xmlns:d=\"DAV:\" xmlns:cal=\"urn:ietf:params:xml:ns:caldav\" xmlns:cs=\"http://calendarserver.org/ns/\" xmlns:card=\"urn:ietf:params:xml:ns:carddav\">\r\n";
    response += "  <d:principal-search-property>\r\n";
    response += "    <d:prop>\r\n";
    response += "      <d:displayname/>\r\n";
    response += "    </d:prop>\r\n";
    response += "    <d:description xml:lang=\"en\">Display name</d:description>\r\n";
    response += "  </d:principal-search-property>\r\n";
//    response += "  <d:principal-search-property>\r\n";
//    response += "    <d:prop>\r\n";
//    response += "      <s:email-address/>\r\n";
//    response += "    </d:prop>\r\n";
//    response += "    <d:description xml:lang=\"en\">Email address</d:description>\r\n";
//    response += "  </d:principal-search-property>\r\n";
    response += "</d:principal-search-property-set>\r\n";

    return response;
}

function isReportPropertyCalendarProxyWriteFor(request)
{
    var body = request.getBody();
    var xmlDoc = xml.parseXml(body);

    var node = xmlDoc.get('/A:expand-property/A:property[@name=\'calendar-proxy-write-for\']', { A: 'DAV:', C: 'http://calendarserver.org/ns/'});

    return typeof node != 'undefined';
}

function replyPropertyCalendarProxyWriteFor(request)
{
    var url = request.getURL();
    var res = request.getRes();
    res.write("<d:multistatus xmlns:d=\"DAV:\" xmlns:cal=\"urn:ietf:params:xml:ns:caldav\" xmlns:cs=\"http://calendarserver.org/ns/\" xmlns:card=\"urn:ietf:params:xml:ns:carddav\">\r\n");
    res.write("<d:response>");
    res.write("    <d:href>" + url + "</d:href>");
    res.write("    <d:propstat>");
    res.write("       <d:prop>");
    res.write("           <cs:calendar-proxy-read-for/>");
    res.write("           <cs:calendar-proxy-write-for/>");
    res.write("       </d:prop>");
    res.write("        <d:status>HTTP/1.1 200 OK</d:status>");
    res.write("    </d:propstat>");
    res.write("</d:response>");
    res.write("</d:multistatus>\r\n");
}

function proppatch(request)
{
    log.debug("principal.proppatch called");

    rh.setStandardHeaders(request);

    var url = request.getURL();
    var res = request.getRes();
    res.writeHead(200);

    res.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
    res.write("<d:multistatus xmlns:d=\"DAV:\" xmlns:cal=\"urn:ietf:params:xml:ns:caldav\" xmlns:cs=\"http://calendarserver.org/ns/\" xmlns:card=\"urn:ietf:params:xml:ns:carddav\">\r\n");
    res.write("	<d:response>\r\n");
    res.write("		<d:href>" + url + "</d:href>\r\n");
    res.write("		<d:propstat>\r\n");
    res.write("			<d:prop>\r\n");
    res.write("				<cal:default-alarm-vevent-date/>\r\n");
    res.write("			</d:prop>\r\n");
    res.write("			<d:status>HTTP/1.1 403 Forbidden</d:status>\r\n");
    res.write("		</d:propstat>\r\n");
    res.write("	</d:response>\r\n");
    res.write("</d:multistatus>\r\n");
}

<|endoftext|>


<commit_before>class FileControl {
  constructor (params) {
    this.name = 'File Control'
    this.section = 'File Stuff'
    this.fields = ['input', 'format', 'filename', 'filepath', 'filetask', 'input2', 'togglestatus']
  }

  subtitle (data) {
    const filetasks = ['Create', 'Write', 'Append into', 'Delete', 'Insert into']
    return `${filetasks[parseInt(data.filetask)]} ${data.filename}${data.format}`
  }
  html () {
    return `

          Format:

          <select id="format" class="round">
            <option value="" selected>OTHER</option>
            <option value=".log">LOG</option>
            <option value=".txt">TXT</option>
            <option value=".json">JSON</option>
            <option value=".js">JS</option>
          </select>

          Task:

          <select id="filetask" title="99% of the time, you want append" class="round">
            <option value="0" title="Only makes the file">Create</option>
            <option value="1" title="Overwrites the files contents with yours">Write</option>
            <option value="2" title="Add the content to the end of the file" selected>Append</option>
            <option value="4" title="Inserts a line in a specific place in the file">Insert</option>
            <option value="3" title="Deletes a file, Be VERY carefull with this option">Delete</option>
          </select>

          File Name:

          <textarea id="filename" title="If 'Other' add the file .format to the end of the file name" placeholder="Example file name 'myreallycoollog'" class="round" type="textarea" rows="1"></textarea>

          File Path:

          <textarea class="round col-100" id="filepath" title="./ represents the bots root directory. Use instead of an absolute path > C:/path/to/bot/" placeholder="Example Path = ./logs/date/example-date/" class="round" type="textarea" rows="3"></textarea>

        If you would like to create a directory, leave the filename section empty while setting format to 'OTHER' and task to 'Create'.

        Input Text:

        <textarea id="input" placeholder="Leave Blank For None." class="round" type="textarea" rows="10"></textarea>

          <input onclick="if(checked){value='yes'}else{value='no'}" type="checkbox" id="togglestatus" value='no'>

        Toogle this if your data contains Objects (Json, array, etc...)

        Line to Insert at:

        <input id="input2" placeholder="1 Adds content at the first line." class="round"></input>

`
  }
  init () {
    const { document } = this
    const selector = document.getElementById('filetask')
    const selector2 = document.getElementById('format')
    const targetfield = document.getElementById('inputArea')
    const targetfield2 = document.getElementById('lineInsert')
    var vall = document.getElementById('togglestatus').value
    if (vall === 'yes') {
      document.getElementById('togglestatus').checked = true
    };

    selector.onclick = () => showInput()

    function showInput () {
      const selected = selector[selector.selectedIndex].value
      const selected2 = selector2[selector2.selectedIndex].value
      if (selected2 === '.json') {
        document.getElementById('visibot').style.display = 'block'
      } else {
        document.getElementById('visibot').style.display = 'none'
      }
      if (selected === '0' || selected === '3') { // Hides "Input Text"
        targetfield.classList.add('hidden')
      } else {
        targetfield.classList.remove('hidden')
      }
      if (selected === '0' || selected === '1' || selected === '2' || selected === '3') { // Hides "Line to Insert at"
        targetfield2.classList.add('hidden')
      } else {
        targetfield2.classList.remove('hidden')
      }
    }
  }
  action (cache) {
    const fs = require('fs')
    const path = require('path')
    const Mods = this.getMods()
    const mkdirp = Mods.require('mkdirp')
    const insertLine = Mods.require('insert-line')
    const data = cache.actions[cache.index]
    const dirName = path.normalize(this.evalMessage(data.filepath, cache))
    const fileName = this.evalMessage(data.filename, cache)
    const line = parseInt(this.evalMessage(data.input2, cache))
    const togglestat = data.togglestatus

    const fpath = path.join(dirName, fileName + data.format)
    const task = parseInt(data.filetask)
    const itext = this.evalMessage(data.input, cache)
    const lmg = 'Something went wrong while'
    let result
    switch (task) {
      case 0: // Create File
        result = async () => {
          if (fileName === '') return this.callNextAction(cache)
          fs.writeFileSync(fpath, '', (err) => {
            if (err) return console.log(`${lmg} creating: [${err}]`)
          })
        }
        break
      case 1: // Write File
        result = () => {
          if (fileName === '') throw new Error('File Name not Provided:')
          fs.writeFileSync(fpath, itext, (err) => {
            if (err) return console.log(`${lmg} writing: [${err}]`)
          })
          if (togglestat === 'yes') {
            fs.writeFileSync(fpath, JSON.stringify(itext), (err) => {
              if (err) return console.log(`${lmg} writing: [${err}]`)
            })
          } else {
            fs.writeFileSync(fpath, itext, (err) => {
              if (err) return console.log(`${lmg} writing: [${err}]`)
            })
          }
        }
        break

      case 2: // Append File
        result = () => {
          if (fileName === '') throw new Error('File Name not Provided:')
          fs.appendFileSync(fpath, `${itext}\r\n`, (err) => {
            if (err) return console.log(`${lmg} appending: [${err}]`)
          })
          if (togglestat === 'yes') {
            fs.appendFileSync(fpath, `${JSON.stringify(itext)}\r\n`, (err) => {
              if (err) return console.log(`${lmg} appending: [${err}]`)
            })
          } else {
            fs.appendFileSync(fpath, `${itext}\r\n`, (err) => {
              if (err) return console.log(`${lmg} appending: [${err}]`)
            })
          }
        }
        break

      case 4: // Insert Line to File
        result = () => {
          if (fileName === '') throw new Error('File Name not Provided:')
          insertLine(fpath).content(itext).at(line).then((err) => {
            if (err) return console.log(`${lmg} inserting: [${err}]`)
          })
        }
        break
      case 3: // Delete File
        result = () => fs.unlink(fpath, (err) => {
          if (!fs.existsSync(dirName)) this.callNextAction(cache)
          if (err) return console.log(`${lmg} deleting: [${err}]`)
        })
        break
    }
    function ensureDirExists (dirPath, cb) {
      const dirname = path.normalize(dirPath)
      if (!fs.existsSync(dirname)) {
        mkdirp(dirname, { recursive: true }, cb)
        return true
      }
      cb(null, '')
      return false
    }
    try {
      if (dirName) {
        ensureDirExists(dirName, result)
      } else {
        throw new Error('you did not set a file path, please go back and check your work.')
      }
    } catch (err) {
      return console.error(`ERROR ${err.stack || err}`)
    }
    this.callNextAction(cache)
  }
  mod () {}
}
module.exports = new FileControl()
<commit_msg>Update file_control_MOD.js<commit_after>class FileControl {
  constructor (params) {
    this.name = 'File Control'
    this.section = 'File Stuff'
    this.fields = ['input', 'format', 'filename', 'filepath', 'filetask', 'input2', 'togglestatus']
  }

  subtitle (data) {
    const filetasks = ['Create', 'Write', 'Append into', 'Delete', 'Insert into']
    return `${filetasks[parseInt(data.filetask)]} ${data.filename}${data.format}`
  }
  html () {
    return `

          Format:

          <select id="format" class="round">
            <option value="" selected>OTHER</option>
            <option value=".log">LOG</option>
            <option value=".txt">TXT</option>
            <option value=".json">JSON</option>
            <option value=".js">JS</option>
          </select>

          Task:

          <select id="filetask" title="99% of the time, you want append" class="round">
            <option value="0" title="Only makes the file">Create</option>
            <option value="1" title="Overwrites the files contents with yours">Write</option>
            <option value="2" title="Add the content to the end of the file" selected>Append</option>
            <option value="4" title="Inserts a line in a specific place in the file">Insert</option>
            <option value="3" title="Deletes a file, Be VERY carefull with this option">Delete</option>
          </select>

          File Name:

          <textarea id="filename" title="If 'Other' add the file .format to the end of the file name" placeholder="Example file name 'myreallycoollog'" class="round" type="textarea" rows="1"></textarea>

          File Path:

          <textarea class="round col-100" id="filepath" title="./ represents the bots root directory. Use instead of an absolute path > C:/path/to/bot/" placeholder="Example Path = ./logs/date/example-date/" class="round" type="textarea" rows="3"></textarea>

        If you would like to create a directory, leave the filename section empty while setting format to 'OTHER' and task to 'Create'.

        Input Text:

        <textarea id="input" placeholder="Leave Blank For None." class="round" type="textarea" rows="10"></textarea>

          <input onclick="if(checked){value='yes'}else{value='no'}" type="checkbox" id="togglestatus" value='no'>

        Toogle this if your data contains Objects (Json, array, etc...)

        Line to Insert at:

        <input id="input2" placeholder="1 Adds content at the first line." class="round"></input>

`
  }
  init () {
    const { document } = this
    const selector = document.getElementById('filetask')
    const selector2 = document.getElementById('format')
    const targetfield = document.getElementById('inputArea')
    const targetfield2 = document.getElementById('lineInsert')
    var vall = document.getElementById('togglestatus').value
    if (vall === 'yes') {
      document.getElementById('togglestatus').checked = true
    };

    selector.onclick = () => showInput()

    function showInput () {
      const selected = selector[selector.selectedIndex].value
      const selected2 = selector2[selector2.selectedIndex].value
      if (selected2 === '.json') {
        document.getElementById('visibot').style.display = 'block'
      } else {
        document.getElementById('visibot').style.display = 'none'
      }
      if (selected === '0' || selected === '3') { // Hides "Input Text"
        targetfield.classList.add('hidden')
      } else {
        targetfield.classList.remove('hidden')
      }
      if (selected === '0' || selected === '1' || selected === '2' || selected === '3') { // Hides "Line to Insert at"
        targetfield2.classList.add('hidden')
      } else {
        targetfield2.classList.remove('hidden')
      }
    }
  }
  action (cache) {
    const fs = require('fs')
    const path = require('path')
    const Mods = this.getMods()
    const mkdirp = Mods.require('mkdirp')
    const insertLine = Mods.require('insert-line')
    const data = cache.actions[cache.index]
    const dirName = path.normalize(this.evalMessage(data.filepath, cache))
    const fileName = this.evalMessage(data.filename, cache)
    const line = parseInt(this.evalMessage(data.input2, cache))
    const togglestat = data.togglestatus

    const fpath = path.join(dirName, fileName + data.format)
    const task = parseInt(data.filetask)
    const itext = this.evalMessage(data.input, cache)
    const lmg = 'Something went wrong while'
    let result
    switch (task) {
      case 0: // Create File
        result = async () => {
          if (fileName === '') return this.callNextAction(cache)
          fs.writeFileSync(fpath, '', (err) => {
            if (err) return console.log(`${lmg} creating: [${err}]`)
          })
        }
        break
      case 1: // Write File
        result = () => {
          if (fileName === '') throw new Error('File Name not Provided:')
          fs.writeFileSync(fpath, itext, (err) => {
            if (err) return console.log(`${lmg} writing: [${err}]`)
          })
          if (togglestat === 'yes') {
            fs.writeFileSync(fpath, JSON.stringify(itext), (err) => {
              if (err) return console.log(`${lmg} writing: [${err}]`)
            })
          } else {
            fs.writeFileSync(fpath, itext, (err) => {
              if (err) return console.log(`${lmg} writing: [${err}]`)
            })
          }
        }
        break

      case 2: // Append File
        result = () => {
          if (fileName === '') throw new Error('File Name not Provided:')
          fs.appendFileSync(fpath, `${itext}\r\n`, (err) => {
            if (err) return console.log(`${lmg} appending: [${err}]`)
          })
          if (togglestat === 'yes') {
            fs.appendFileSync(fpath, `${JSON.stringify(itext)}\r\n`, (err) => {
              if (err) return console.log(`${lmg} appending: [${err}]`)
            })
          } else {
            fs.appendFileSync(fpath, `${itext}\r\n`, (err) => {
              if (err) return console.log(`${lmg} appending: [${err}]`)
            })
          }
        }
        break

      case 4: // Insert Line to File
        result = () => {
          if (fileName === '') throw new Error('File Name not Provided:')
          insertLine(fpath).content(itext).at(line).then((err) => {
            if (err) return console.log(`${lmg} inserting: [${err}]`)
          })
        }
        break
      case 3: // Delete File
        result = () => fs.unlink(fpath, (err) => {
          if (!fs.existsSync(dirName)) this.callNextAction(cache)
          if (err) return console.log(`${lmg} deleting: [${err}]`)
        })
        break
    }
    function ensureDirExists (dirPath, cb) {
      const dirname = path.normalize(dirPath)
      if (!fs.existsSync(dirname)) {
        mkdirp(dirname, { recursive: true }, cb)
        return true
      }
      cb(null, '')
      return false
    }
    try {
      if (dirName) {
        ensureDirExists(dirName, result)
      } else {
        throw new Error('you did not set a file path, please go back and check your work.')
      }
    } catch (err) {
      return console.error(`ERROR ${err.stack || err}`)
    }
    this.callNextAction(cache)
  }

  mod () {}
}

module.exports = new FileControl()
<|endoftext|>