<commit_before>define(["vendors/go-dashboard/dashboard","vendors/go-dashboard/jquery.qrcode.min"], function(Dashboard, qrcode) {

    $.fn.dashboard = function (options) {
        var el = $(this).find(".dashboard");
        if (!$(el).data("dashboard")) {
            $(el).data("dashboard", true);

            var DashboardViewModel = new Dashboard();
            var __has_loaded = false;
            // catch click tab title
            $(el).find(".dashboard-scene>ul").click(function (event) {
                if ($(event.target).is("a")) {

                    if (!__has_loaded) {
                        var base_dir = window.location.pathname;
                        base_dir = base_dir.substring(0, base_dir.lastIndexOf("/") + 1)
                        $("html script[src]").each(function (i, e) {
                            var src = $(e).attr("src");
                            if (src) {
                                src = src.replace(/\\/g,"/");
                                src = src.replace("//","/");
                                src = src.replace("//","/");
                                if (src.substr(0, 1) != "/") {
                                    src = base_dir + src;
                                }
                                DashboardViewModel.availableScripts.push(src);
                            }
                        });
                        $("html link[href]").each(function (i, e) {
                            if ($(e).attr("rel") == "stylesheet" || $(e).attr("type") == "text/css") {
                                var src = $(e).attr("href");
                                if (src) {
                                    src = src.replace(/\\/g,"/");
                                    src = src.replace("//","/");
                                    src = src.replace("//","/");
                                    if (src.substr(0, 1) != "/") {
                                        src = base_dir + src;
                                    }
                                    DashboardViewModel.availableStyles.push(src);
                                }
                            }
                        });

                        var clean_html = function(_content){
                            var re = /").appendTo(el);
        }
        return $._delay(function (dfd) {
            /*
             //     anon       true, if the space may be omitted in anonymous function declarations
             //     bitwise    true, if bitwise operators should be allowed
             //     browser    true, if the standard browser globals should be predefined
             //     'continue' true, if the continuation statement should be tolerated
             //     css        true, if CSS workarounds should be tolerated
             //     debug      true, if debugger statements should be allowed
             //     devel      true, if logging should be allowed (console, alert, etc.)
             //     eqeq       true, if == should be allowed
             //     es5        true, if ES5 syntax should be allowed
             //     evil       true, if eval should be allowed
             //     forin      true, if for in statements need not filter
             //     fragment   true, if HTML fragments should be allowed
             //     indent     the indentation factor
             //     maxerr     the maximum number of errors to allow
             //     maxlen     the maximum length of a source line
             //     newcap     true, if constructor names capitalization is ignored
             //     node       true, if Node.js globals should be predefined
             //     nomen      true, if names may have dangling _
             //     on         true, if HTML event handlers should be allowed
             //     passfail   true, if the scan should stop on first error
             //     plusplus   true, if increment/decrement should be allowed
             //     properties true, if all property names must be declared with / *properties* /
             //     regexp     true, if the . should be allowed in regexp literals
             //     rhino      true, if the Rhino environment globals should be predefined
             //     undef      true, if variables can be declared out of order
             //     unparam    true, if unused parameters should be tolerated
             //     sloppy     true, if the 'use strict'; pragma is optional
             //     stupid     true, if really stupid practices are tolerated
             //     sub        true, if all forms of subscript notation are tolerated
             //     todo       true, if TODO comments are tolerated
             //     vars       true, if multiple var statements per function should be allowed
             //     white      true, if sloppy whitespace is tolerated
             //     windows    true, if MS Windows-specific globals should be predefined

             //      to be configured
             */

            var myResult = JSLINT(script_content, {"predef": []});
            if (myResult == false) {
                var myReport = JSLINT.errors;
                //var myErrorReport = JSLINT.error_report(data);
                //var myPropertyReport = JSLINT.properties_report(JSLINT.property);

                var messages = new Array();
                for (i = 0, len = myReport.length; i < len; i++) {
                    var m = myReport[i];
                    if (m != null) {
                        messages.push({
                            "message": m.reason,
                            "id": m.id,
                            "line": m.line,
                            "col": m.character,
                            "evidence": m.evidence
                        });
                    }
                }
                dfd.resolve(messages);
            } else {
                dfd.resolve([]);
            }
        });
    };
    var create_jshint_report = function(el, script_content, callback){
        require(["vendors/go-jshint/jshint-1.1.0"], function(  ){
            var options = {};
            var globals = {};
            var success = JSHINT(script_content, options, globals);
            if (success == false) {
                var myReport = JSHINT.errors;
                //var myErrorReport = JSLINT.error_report(data);
                //var myPropertyReport = JSLINT.properties_report(JSLINT.property);

                var messages = new Array();
                for (i = 0, len = myReport.length; i < len; i++) {
                    var m = myReport[i];
                    if (m != null) {
                        messages.push({
                            "message": m.reason,
                            "id": m.id,
                            "line": m.line,
                            "col": m.character,
                            "evidence": m.evidence
                        });
                    }
                }
                callback(messages);
            }
        });
    }
    var create_csslint_report = function (el, css_content) {

        var lib_src = "/js/vendors/go-csslint/csslint.js";
        if ($("body").find("script[src='" + lib_src + "']").length == 0) {
            $("").appendTo(el);
        }
        return $._delay(function (dfd) {

            var results = CSSLint.verify(css_content);
            var messages = new Array();

            for (i = 0, len = results.messages.length; i < len; i++) {
                var m = results.messages[i];
                messages.push({
                    "message": m.message + " (line " + m.line + ", col " + m.col + ")",
                    "level": m.type
                });
            }

            dfd.resolve(messages);
        });
    };

    return $
});
<commit_msg>Better QrCode display<commit_after>define(["vendors/go-dashboard/dashboard","vendors/go-dashboard/jquery.qrcode.min"], function(Dashboard, qrcode) {

    $.fn.dashboard = function (options) {
        var el = $(this).find(".dashboard");
        if (!$(el).data("dashboard")) {
            $(el).data("dashboard", true);

            var DashboardViewModel = new Dashboard();
            var __has_loaded = false;
            // catch click tab title
            $(el).find(".dashboard-scene>ul").click(function (event) {
                if ($(event.target).is("a")) {

                    if (!__has_loaded) {
                        var base_dir = window.location.pathname;
                        base_dir = base_dir.substring(0, base_dir.lastIndexOf("/") + 1)
                        $("html script[src]").each(function (i, e) {
                            var src = $(e).attr("src");
                            if (src) {
                                src = src.replace(/\\/g,"/");
                                src = src.replace("//","/");
                                src = src.replace("//","/");
                                if (src.substr(0, 1) != "/") {
                                    src = base_dir + src;
                                }
                                DashboardViewModel.availableScripts.push(src);
                            }
                        });
                        $("html link[href]").each(function (i, e) {
                            if ($(e).attr("rel") == "stylesheet" || $(e).attr("type") == "text/css") {
                                var src = $(e).attr("href");
                                if (src) {
                                    src = src.replace(/\\/g,"/");
                                    src = src.replace("//","/");
                                    src = src.replace("//","/");
                                    if (src.substr(0, 1) != "/") {
                                        src = base_dir + src;
                                    }
                                    DashboardViewModel.availableStyles.push(src);
                                }
                            }
                        });

                        var clean_html = function(_content){
                            var re = /").appendTo(el);
        }
        return $._delay(function (dfd) {
            /*
             //     anon       true, if the space may be omitted in anonymous function declarations
             //     bitwise    true, if bitwise operators should be allowed
             //     browser    true, if the standard browser globals should be predefined
             //     'continue' true, if the continuation statement should be tolerated
             //     css        true, if CSS workarounds should be tolerated
             //     debug      true, if debugger statements should be allowed
             //     devel      true, if logging should be allowed (console, alert, etc.)
             //     eqeq       true, if == should be allowed
             //     es5        true, if ES5 syntax should be allowed
             //     evil       true, if eval should be allowed
             //     forin      true, if for in statements need not filter
             //     fragment   true, if HTML fragments should be allowed
             //     indent     the indentation factor
             //     maxerr     the maximum number of errors to allow
             //     maxlen     the maximum length of a source line
             //     newcap     true, if constructor names capitalization is ignored
             //     node       true, if Node.js globals should be predefined
             //     nomen      true, if names may have dangling _
             //     on         true, if HTML event handlers should be allowed
             //     passfail   true, if the scan should stop on first error
             //     plusplus   true, if increment/decrement should be allowed
             //     properties true, if all property names must be declared with / *properties* /
             //     regexp     true, if the . should be allowed in regexp literals
             //     rhino      true, if the Rhino environment globals should be predefined
             //     undef      true, if variables can be declared out of order
             //     unparam    true, if unused parameters should be tolerated
             //     sloppy     true, if the 'use strict'; pragma is optional
             //     stupid     true, if really stupid practices are tolerated
             //     sub        true, if all forms of subscript notation are tolerated
             //     todo       true, if TODO comments are tolerated
             //     vars       true, if multiple var statements per function should be allowed
             //     white      true, if sloppy whitespace is tolerated
             //     windows    true, if MS Windows-specific globals should be predefined

             //      to be configured
             */

            var myResult = JSLINT(script_content, {"predef": []});
            if (myResult == false) {
                var myReport = JSLINT.errors;
                //var myErrorReport = JSLINT.error_report(data);
                //var myPropertyReport = JSLINT.properties_report(JSLINT.property);

                var messages = new Array();
                for (i = 0, len = myReport.length; i < len; i++) {
                    var m = myReport[i];
                    if (m != null) {
                        messages.push({
                            "message": m.reason,
                            "id": m.id,
                            "line": m.line,
                            "col": m.character,
                            "evidence": m.evidence
                        });
                    }
                }
                dfd.resolve(messages);
            } else {
                dfd.resolve([]);
            }
        });
    };
    var create_jshint_report = function(el, script_content, callback){
        require(["vendors/go-jshint/jshint-1.1.0"], function(  ){
            var options = {};
            var globals = {};
            var success = JSHINT(script_content, options, globals);
            if (success == false) {
                var myReport = JSHINT.errors;
                //var myErrorReport = JSLINT.error_report(data);
                //var myPropertyReport = JSLINT.properties_report(JSLINT.property);

                var messages = new Array();
                for (i = 0, len = myReport.length; i < len; i++) {
                    var m = myReport[i];
                    if (m != null) {
                        messages.push({
                            "message": m.reason,
                            "id": m.id,
                            "line": m.line,
                            "col": m.character,
                            "evidence": m.evidence
                        });
                    }
                }
                callback(messages);
            }
        });
    }
    var create_csslint_report = function (el, css_content) {

        var lib_src = "/js/vendors/go-csslint/csslint.js";
        if ($("body").find("script[src='" + lib_src + "']").length == 0) {
            $("").appendTo(el);
        }
        return $._delay(function (dfd) {

            var results = CSSLint.verify(css_content);
            var messages = new Array();

            for (i = 0, len = results.messages.length; i < len; i++) {
                var m = results.messages[i];
                messages.push({
                    "message": m.message + " (line " + m.line + ", col " + m.col + ")",
                    "level": m.type
                });
            }

            dfd.resolve(messages);
        });
    };

    return $
});
<|endoftext|>


<commit_before>import { uniqueId } from "lodash";
import { gql } from "graphql-tag";

/* eslint graphql/template-strings:0 */
export default (model, fields, fragments = []) =>
  gql`
  fragment __${model}FragmentGenerated${uniqueId()} on ${model} {
    ${Array.isArray(fields) ? fields.join("\n") : fields}
  }
  ${Array.isArray(fragments)
    ? fragments.map(f => f.loc.source.body).join("\n")
    : fragments}
  `;
<commit_msg>fixing gql import<commit_after>import { uniqueId } from "lodash";
import gql from "graphql-tag";

/* eslint graphql/template-strings:0 */
export default (model, fields, fragments = []) =>
  gql`
  fragment __${model}FragmentGenerated${uniqueId()} on ${model} {
    ${Array.isArray(fields) ? fields.join("\n") : fields}
  }
  ${Array.isArray(fragments)
    ? fragments.map(f => f.loc.source.body).join("\n")
    : fragments}
  `;
<|endoftext|>


<commit_before>var Selectors = function () {
    "use strict";

    var Selector = function () { }

    Selector.prototype.toInput = function(elem) {
        if(elem && elem.length) {
            return elem.map(oneToOneToInput).filter(function(e) { return e !== null; })
        } else {
            return oneToOneToInput(elem)
        }
    }

    var oneToOneToInput = function (elem) {
        return elem && elem.tagName === "INPUT" ? elem : null
    }

    Selector.prototype.toDiv = function(elem) {
        if(elem && elem.length) {
            return elem.map(oneToOneToDiv).filter(function (e) { return e !== null })
        } else {
            return oneToOneToDiv(elem)
        }
    }

    var oneToOneToDiv = function (elem) {
        return elem && elem.tagName === "DIV" ? elem : null
    }

    Selector.prototype.toSpan = function(elem) {
        if(elem && elem.length) {
            return elem.map(oneToOneToSpan).filter(function (e) { return e !== null })
        } else {
            return oneToOneToSpan(elem)
        }
    }

    var oneToOneToSpan = function (elem) {
        return elem && elem.tagName === "SPAN" ? elem : null
    }

    Selector.prototype.toDocument = function(elem) {
        return elem ? elem instanceof HTMLDocument ? elem : null : null
    }

    Selector.prototype.idEq = function(el_a, idS) {
        return el_a.id === idS ? $UHC.$Base.$True__ : $UHC.$Base.$False__
    }

    Selector.prototype.clEq = function(el_a, clsName) {
        if(el_a instanceof HTMLElement && el_a.classList.contains(clsName)) {
            return $UHC.$Base.$True__
        } else {
            return $UHC.$Base.$False__
        }
    }

    Selector.prototype.tag = function(node, tagName) {
        if(node.tagName === tagName.toUpperCase())
            return $UHC.$Base.$True__
        else
            return $UHC.$Base.$False__
    }

    Selector.prototype.children = function(el) {

        var ret = [], fringe = Array.prototype.slice.call(el.childNodes)
        while(fringe.length > 0) {
            if(fringe[0] instanceof HTMLElement) ret.push(fringe[0])
            fringe.push.apply(fringe, fringe[0].childNodes)
            fringe = fringe.slice(1)
        }

        return Lists.toUHCList(ret)
    }

    Selector.prototype.runSelector = function(sel) {
        window.debug && console.log("how exactly do I run ", sel)
        return Lists.emptyUHCList();
    }

    return new Selector()
}()
<commit_msg>Boolean interop, default to js if needed<commit_after>var Selectors = function () {
    "use strict";

    var Selector = function () { }

    Selector.prototype.toInput = function(elem) {
        if(elem && elem.length) {
            return elem.map(oneToOneToInput).filter(function(e) { return e !== null; })
        } else {
            return oneToOneToInput(elem)
        }
    }

    var oneToOneToInput = function (elem) {
        return elem && elem.tagName === "INPUT" ? elem : null
    }

    Selector.prototype.toDiv = function(elem) {
        if(elem && elem.length) {
            return elem.map(oneToOneToDiv).filter(function (e) { return e !== null })
        } else {
            return oneToOneToDiv(elem)
        }
    }

    var oneToOneToDiv = function (elem) {
        return elem && elem.tagName === "DIV" ? elem : null
    }

    Selector.prototype.toSpan = function(elem) {
        if(elem && elem.length) {
            return elem.map(oneToOneToSpan).filter(function (e) { return e !== null })
        } else {
            return oneToOneToSpan(elem)
        }
    }

    var oneToOneToSpan = function (elem) {
        return elem && elem.tagName === "SPAN" ? elem : null
    }

    Selector.prototype.toDocument = function(elem) {
        return elem ? elem instanceof HTMLDocument ? elem : null : null
    }

    Selector.prototype.idEq = function(el_a, idS) {
        return el_a.id === idS ? $UHC.$Base.$True__ || true : $UHC.$Base.$False__ || false
    }

    Selector.prototype.clEq = function(el_a, clsName) {
        if(el_a instanceof HTMLElement && el_a.classList.contains(clsName)) {
            return $UHC.$Base.$True__ || true
        } else {
            return $UHC.$Base.$False__ || false
        }
    }

    Selector.prototype.tag = function(node, tagName) {
        if(node.tagName === tagName.toUpperCase())
            return $UHC.$Base.$True__ || true
        else
            return $UHC.$Base.$False__ || false
    }

    Selector.prototype.children = function(el) {

        var ret = [], fringe = Array.prototype.slice.call(el.childNodes)
        while(fringe.length > 0) {
            if(fringe[0] instanceof HTMLElement) ret.push(fringe[0])
            fringe.push.apply(fringe, fringe[0].childNodes)
            fringe = fringe.slice(1)
        }

        return Lists.toUHCList(ret)
    }

    Selector.prototype.runSelector = function(sel) {
        window.debug && console.log("how exactly do I run ", sel)
        return Lists.emptyUHCList();
    }

    return new Selector()
}()
<|endoftext|>


<commit_before>export default class Command {
    /* --- globals --- */

    /* --- constants --- */

    /* --- constructor --- */

    /**
     * Initializes the cli command
     *
     * @param {String} name
     * @param {String} description
     * @param {Function} execute
     * @return {Command}
     */
    constructor(name, description='', execute=null) {
        this.name = name;
        this.description = description;

        if (execute !== null) {
            this.execute = execute;
        }
    }

    /* --- protected --- */

    /* --- public --- */
}
<commit_msg>feat(command): add prompt function directly to base command<commit_after>import Prompt from './prompt';

export default class Command {
    /* --- globals --- */

    /* --- constants --- */

    /* --- constructor --- */

    /**
     * Initializes the cli command
     *
     * @param {String} name
     * @param {String} description
     * @param {Function} execute
     * @return {Command}
     */
    constructor(name, description='', execute=null) {
        this.name = name;
        this.description = description;

        if (execute !== null) {
            this.execute = execute;
        }
    }

    /* --- protected --- */

    /**
     * Fetches necessary data from user
     *
     * @param {Array}
     * @return {Promise}
     */
    async prompt(questions=[]) {
        try {
            // create new prompt instance
            const prompt = new Prompt();

            // set questions to prompt
            prompt.ask(questions);

            // start prompting for answers
            return await prompt.start();
        } catch(err) {
            console.error(err);
            process.exit(1);
        }
    }

    /* --- public --- */
}
<|endoftext|>


<commit_before>/* @flow */
'use strict'
/**
 * Gets a set of OGR Parameters for an export
 *
 * @param {object} options - potentially contains a fields object
 */
function create (format, options) {
  const ogrFormat = ogrFormats[format]
  // shapefiles cannot be streamed out of ogr2ogr
  const output = format === 'zip' ? `${options.path || '.'}/${options.name}` : '/vsistdout/'
  const input = options.input || 'layer.vrt'

  let cmd = ['--config', 'SHAPE_ENCODING', 'UTF-8', '-f', ogrFormat, output, input]

  options.geometry = options.geometry && options.geometry.toUpperCase() || 'NONE'
  if (format === 'csv') cmd = csvParams(cmd, options)
  if (format === 'zip') cmd = shapefileParams(cmd, options)
  return finishOgrParams(cmd)
}

/**
 * Add parameters specific to a csv export
 *
 * @param {array} cmd - an array of OGR command parts to modify
 * @param {object} options - may contain fields
 * @return {array}
 * @private
 */
function csvParams (cmd, options) {
  cmd.push('-lco', 'WRITE_BOM=YES')
  const hasPointGeom = options.geometry === 'POINT'
  const fields = options.fields.join('|').toLowerCase().split('|')
  const hasXY = fields.indexOf('x') > -1 && fields.indexOf('y') > -1
  if (hasPointGeom && !hasXY) cmd = cmd.concat(['-lco', 'GEOMETRY=AS_XY'])
  return cmd
}

/**
 * Add parameters specific to a csv export
 *
 * @param {string} cmd - an array of OGR command parts to modify
 * @param {object} options - may contain a wkid or srs
 * @param {function} callback - calls back back with a modified command array or an error
 * @private
 */
function shapefileParams (cmd, options) {
  // make sure geometries are still written even if the first is null
  cmd.push('-nlt', options.geometry.toUpperCase())
  cmd.push('-fieldmap', 'identity')
  if (options.srs) cmd.push('-t_srs', options.srs)
  return cmd
}

/**
 * Adds final parameters to the OGR function call
 * @param {array} cmd - an array of OGR command parts to modify
 * @return {string} the final OGR command
 */
function finishOgrParams (cmd) {
  cmd.push('-append')
  cmd.push('-skipfailures')
  cmd.push('-lco', 'ENCODING=UTF-8')
  return cmd
}

const ogrFormats = {
  kml: 'KML',
  zip: 'ESRI Shapefile',
  csv: 'CSV',
  json: 'GeoJSON',
  geojson: 'GeoJSON',
  gpkg: 'GPKG'
}

module.exports = {create}
<commit_msg>respect 2GB shapefile dbf limit<commit_after>/* @flow */
'use strict'
/**
 * Gets a set of OGR Parameters for an export
 *
 * @param {object} options - potentially contains a fields object
 */
function create (format, options) {
  const ogrFormat = ogrFormats[format]
  // shapefiles cannot be streamed out of ogr2ogr
  const output = format === 'zip' ? `${options.path || '.'}/${options.name}` : '/vsistdout/'
  const input = options.input || 'layer.vrt'

  let cmd = ['--config', 'SHAPE_ENCODING', 'UTF-8', '-f', ogrFormat, output, input]

  options.geometry = options.geometry && options.geometry.toUpperCase() || 'NONE'
  if (format === 'csv') cmd = csvParams(cmd, options)
  if (format === 'zip') cmd = shapefileParams(cmd, options)
  return finishOgrParams(cmd)
}

/**
 * Add parameters specific to a csv export
 *
 * @param {array} cmd - an array of OGR command parts to modify
 * @param {object} options - may contain fields
 * @return {array}
 * @private
 */
function csvParams (cmd, options) {
  cmd.push('-lco', 'WRITE_BOM=YES')
  const hasPointGeom = options.geometry === 'POINT'
  const fields = options.fields.join('|').toLowerCase().split('|')
  const hasXY = fields.indexOf('x') > -1 && fields.indexOf('y') > -1
  if (hasPointGeom && !hasXY) cmd = cmd.concat(['-lco', 'GEOMETRY=AS_XY'])
  return cmd
}

/**
 * Add parameters specific to a csv export
 *
 * @param {string} cmd - an array of OGR command parts to modify
 * @param {object} options - may contain a wkid or srs
 * @param {function} callback - calls back back with a modified command array or an error
 * @private
 */
function shapefileParams (cmd, options) {
  // make sure geometries are still written even if the first is null
  cmd.push('-nlt', options.geometry.toUpperCase())
  cmd.push('-fieldmap', 'identity')
  if (!options.ignoreShpLimit) cmd.push('-lco', '2GB_LIMIT=yes')
  if (options.srs) cmd.push('-t_srs', options.srs)
  return cmd
}

/**
 * Adds final parameters to the OGR function call
 * @param {array} cmd - an array of OGR command parts to modify
 * @return {string} the final OGR command
 */
function finishOgrParams (cmd) {
  cmd.push('-append')
  cmd.push('-skipfailures')
  cmd.push('-lco', 'ENCODING=UTF-8')
  return cmd
}

const ogrFormats = {
  kml: 'KML',
  zip: 'ESRI Shapefile',
  csv: 'CSV',
  json: 'GeoJSON',
  geojson: 'GeoJSON',
  gpkg: 'GPKG'
}

module.exports = {create}
<|endoftext|>


<commit_before>'use strict';

/**
 * @file Reads Canvas calendar and retrieves events to integrate in our planner
 * @module canvas
 */
const _ = require('underscore');
const aliases = require(__dirname + '/aliases.js');
const checkedEvents = require(__dirname + '/checkedEvents.js');
const feeds = require(__dirname + '/feeds.js');
const htmlParser = require(__dirname + '/htmlParser.js');
const ical = require('ical');
const prisma = require('prisma');
const querystring = require('querystring');
const request = require('request');
const url = require('url');
const users = require(__dirname + '/users.js');

// URL Calendars come from
const urlPrefix = 'https://micds.instructure.com/feeds/calendars/';

/**
 * Makes sure a given url is valid and it points to a Canvas calendar feed
 * @function verifyURL
 *
 * @param {string} canvasURL - URI to iCal feed
 * @callback {verifyURLCallback} callback - Callback
 */

/**
 * Returns whether url is valid or not
 * @callback verifyURLCallback
 *
 * @param {Object} err - Null if success, error object if failure.
 * @param {Boolean|string} isValid - True if valid URL, string describing problem if not valid. Null if error.
 * @param {string} url - Valid and formatted URL to our likings. Null if error or invalid url.
 */

function verifyURL(canvasURL, callback) {

	if(typeof callback !== 'function') return;

	if(typeof canvasURL !== 'string') {
		callback(new Error('Invalid URL!'), null, null);
		return;
	}

	// Parse URL first
	const parsedURL = url.parse(canvasURL);

	// Check if pathname is valid
	if(!parsedURL.pathname || !parsedURL.pathname.startsWith('/feeds/calendars/')) {
		// Not a valid URL!
		callback(null, 'Invalid URL path!', null);
		return;
	}

	const pathParts = parsedURL.path.split('/');
	const userCalendar = pathParts[pathParts.length - 1];

	const validURL = urlPrefix + userCalendar;

	// Not lets see if we can actually get any data from here
	request(validURL, (err, response) => {
		if(err) {
			callback(new Error('There was a problem fetching calendar data from the URL!'), null, null);
			return;
		}
		if(response.statusCode !== 200) {
			callback(null, 'Invalid URL!', null);
			return;
		}

		callback(null, true, validURL);
	});
}

/**
 * Sets a user's calendar URL if valid
 * @function setUrl
 *
 * @param {Object} db - Database connection
 * @param {string} user - Username
 * @param {string} url - Calendar url
 * @param {setUrlCallback} callback - Callback
 */

/**
 * Returns the valid url that was inserted into database
 * @callback setUrlCallback
 *
 * @param {Object} err - Null if success, error object if failure
 * @param {Boolean|string} isValid - True if valid URL, string describing problem if not valid. Null if error.
 * @param {string} validURL - Valid url that was inserted into database. Null if error or url invalid.
 */

function setURL(db, user, url, callback) {
	if(typeof callback !== 'function') {
		callback = () => {};
	}

	if(typeof db !== 'object') {
		callback(new Error('Invalid database connection!'), null, null);
		return;
	}

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err, null, null);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'), null, null);
			return;
		}

		verifyURL(url, (err, isValid, validURL) => {
			if(err) {
				callback(err, null, null);
				return;
			} else if(isValid !== true) {
				callback(null, isValid, null);
				return;
			}

			const userdata = db.collection('users');

			userdata.update({ _id: userDoc['_id'] }, { $set: { canvasURL: validURL }}, { upsert: true }, err => {
				if(err) {
					callback(new Error('There was a problem updating the URL to the database!'), null, null);
					return;
				}

				feeds.updateCanvasCache(db, user, err => {
					if(err) {
						callback(err, null, null);
						return;
					}

					callback(null, true, validURL);
				});
			});
		});
	});
}

/**
 * Retrieves a user's events on Canvas from their URL
 * @function getUserCal
 *
 * @param {Object} db - Database connection
 * @param {string} user - Username to get schedule
 * @param {getUserCalCallback} callback - Callback
 */

/**
 * Returns a user's schedule for that day
 * @callback getUserCalCallback
 *
 * @param {Object} err - Null if success, error object if failure.
 * @param {Boolean} hasURL - Whether user has set a valid portal URL. Null if failure.
 * @param {Object} events - Array of all the events in the month. Null if failure.
 */

function getUserCal(db, user, callback) {
	if(typeof callback !== 'function') return;

	if(typeof db !== 'object') {
		callback(new Error('Invalid database connection!'));
		return;
	}

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err, null, null);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'), null, null);
			return;
		}

		if(typeof userDoc.canvasURL !== 'string') {
			callback(null, false, null);
			return;
		}

		request(userDoc.canvasURL, (err, response, body) => {
			if(err) {
				callback(new Error('There was a problem fetching portal data from the URL!'), null, null);
				return;
			}
			if(response.statusCode !== 200) {
				callback(new Error('Invalid URL!'), null, null);
				return;
			}

			callback(null, true, Object.values(ical.parseICS(body)));
		});
	});
}

/**
 * Parses a Canvas assignment title into class name and teacher's name.
 * @function parseCanvasTitle
 *
 * @param {string} title - Canvas assignment title
 * @returns {Object}
 */

function parseCanvasTitle(title) {
	const classTeacherRegex = /\[.+]/g;
	const teacherRegex = /:[A-Z]{5}$/g;
	const firstLastBrackets = /(^\[)|(]$)/g;

	// Get what's in the square brackets, including square brackets
	const classTeacher = _.last(title.match(classTeacherRegex)) || '';
	const classTeacherNoBrackets = classTeacher.replace(firstLastBrackets, '');
	// Subtract the class/teacher from the Canvas title
	const assignmentName = title.replace(classTeacherRegex, '').trim();

	// Also check if there's a teacher, typically separated by a colon
	const teacher = (_.last(classTeacherNoBrackets.match(teacherRegex)) || '').replace(/^:/g, '');
	const teacherFirstName = teacher[0] || '';
	const teacherLastName = (teacher[1] || '') + teacher.substring(2).toLowerCase();

	// Subtract teacher from classTeacher to get the class
	const className = classTeacher.replace(teacher, '').replace(/\[|]/g, '').replace(/:$/g, '');

	return {
		assignment: assignmentName,
		class: {
			raw: classTeacherNoBrackets,
			name: className,
			teacher: {
				raw: teacher,
				firstName: teacherFirstName,
				lastName: teacherLastName
			}
		}
	};
}

/**
 * Parses a Canvas calendar link into an assignment/event link.
 * @function calendarToEvent
 *
 * @param {string} calLink - Calendar link
 * @returns {string}
 */

function calendarToEvent(calLink) {
	// Example calendar link: https://micds.instructure.com/calendar?include_contexts=course_XXXXXXX&month=XX&year=XXXX#assignment_XXXXXXX
	// 'assignment' can also be 'calendar_event'
	const calObject = url.parse(calLink);

	const courseId = querystring.parse(calObject.query)['include_contexts'].replace('_', 's/');

	// Remove hash sign and switch to event URL format
	const eventString = calObject.hash.slice(1);
	let eventId;
	if(eventString.includes('assignment')) {
		eventId = eventString.replace('assignment_', 'assignments/');
	} else if(eventString.includes('calendar_event')) {
		eventId = eventString.replace('calendar_event_', 'calendar_events/');
	}

	return 'https://micds.instructure.com/' + courseId + '/' + eventId;
}

/**
 * Iterates through all of the user's events and get their classes from Canvas
 * @function getClasses
 *
 * @param {Object} db - Database object
 * @param {string} user - Username
 * @param {getClassesCallback} callback - Callback
 */

/**
 * Returns array of classes from canvas
 * @callback getClassesCallback
 *
 * @param {Object} err - Null if success, error object if failure
 * @param {Boolean} hasURL - Whether or not user has a Canvas URL set. Null if error.
 * @param {Array} classes - Array of classes from canvas. Null if error or no Canvas URL set.
 */

function getClasses(db, user, callback) {
	if(typeof callback !== 'function') return;

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err, null, null, null);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'), null, null);
			return;
		}
		if(typeof userDoc['canvasURL'] !== 'string') {
			callback(null, false, null);
			return;
		}

		function parseEvents(eventsToParse) {
			const classes = [];

			for(const calEvent of eventsToParse) {
				// If event doesn't have a summary, skip
				if(typeof calEvent.summary !== 'string') continue;

				const parsedEvent = parseCanvasTitle(calEvent.summary);

				// If not already in classes array, push to array
				if(parsedEvent.class.raw.length > 0 && !_.contains(classes, parsedEvent.class.raw)) {
					classes.push(parsedEvent.class.raw);
				}
			}

			callback(null, true, classes);
		}

		const canvasdata = db.collection('canvasFeeds');

		canvasdata.find({ user: userDoc._id }).toArray((err, events) => {
			if(err) {
				callback(new Error('There was an error retrieving Canvas events!'), null, null);
				return;
			}

			// If cache is empty, update it
			if (events.length > 0) {
				parseEvents(events);
			} else {
				feeds.updateCanvasCache(db, user, err => {
					if (err) {
						callback(err, null, null);
						return;
					}

					canvasdata.find({ user: userDoc._id }).toArray((err, retryEvents) => {
						if(err) {
							callback(new Error('There was an error retrieving Canvas events!'), null, null);
							return;
						}
						parseEvents(retryEvents);
					});
				});
			}
		});
	});
}

/**
 * Get Canvas events from the cache
 * @param {Object} db - Database object
 * @param {string} user - Username
 * @param {getFromCacheCallback} callback - Callback
 */

/**
 * Returns array containing Canvas events
 * @callback getFromCacheCallback
 *
 * @param {Object} err - Null if success, error object if failure
 * @param {Boolean} hasURL - Whether or not user has a Canvas URL set. Null if error.
 * @param {Array} events - Array of events if success, null if failure.
 */

function getFromCache(db, user, callback) {
	if(typeof callback !== 'function') return;

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err, null, null, null);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'), null, null);
			return;
		}
		if(typeof userDoc['canvasURL'] !== 'string') {
			callback(null, false, null);
			return;
		}

		const canvasdata = db.collection('canvasFeeds');

		canvasdata.find({ user: userDoc._id }).toArray((err, events) => {
			if(err) {
				callback(new Error('There was an error retrieving Canvas events!'), null, null);
				return;
			}

			// Get which events are checked
			checkedEvents.list(db, user, (err, checkedEventsList) => {
				if(err) {
					callback(err, null, null);
					return;
				}

				// Loop through all of the events in the calendar feed and push events within month to validEvents
				const validEvents = [];
				// Cache class aliases
				const classAliases = {};

				// Function for getting class to insert according to canvas name
				function getCanvasClass(parsedEvent, classCallback) {
					const name = parsedEvent.class.raw;

					// Check if alias is already cached
					if(typeof classAliases[name] !== 'undefined') {
						classCallback(null, classAliases[name]);
						return;
					}

					// Query aliases to see if possible class object exists
					aliases.getClass(db, user, 'canvas', name, (err, hasAlias, aliasClassObject) => {
						if(err) {
							classCallback(err, null);
							return;
						}

						// Backup object if Canvas class doesn't have alias
						const defaultColor = '#34444F';
						const canvasClass = {
							_id: null,
							canvas: true,
							user,
							name: parsedEvent.class.name,
							teacher: {
								_id: null,
								prefix: '',
								firstName: parsedEvent.class.teacher.firstName,
								lastName: parsedEvent.class.teacher.lastName
							},
							type: 'other',
							block: 'other',
							color: defaultColor,
							textDark: prisma.shouldTextBeDark(defaultColor)
						};

						if(hasAlias) {
							classAliases[name] = aliasClassObject;
						} else {
							classAliases[name] = canvasClass;
						}

						classCallback(null, classAliases[name]);
					});
				}

				// Function to iterate over classes asynchronously
				function checkEvent(i) {
					if(i < events.length) {
						const canvasEvent = events[i];
						const parsedEvent = parseCanvasTitle(canvasEvent.summary);

						// Check if alias for class first
						getCanvasClass(parsedEvent, (err, canvasClass) => {
							if(err) {
								callback(err, null, null);
								return;
							}

							const start = new Date(canvasEvent.start);
							const end = new Date(canvasEvent.end);

							// class will be null if error in getting class name.
							const insertEvent = {
								_id: canvasEvent.uid,
								user: userDoc.user,
								class: canvasClass,
								title: parsedEvent.assignment,
								start,
								end,
								link: calendarToEvent(canvasEvent.url) || '',
								checked: _.contains(checkedEventsList, canvasEvent.uid)
							};

							if(typeof canvasEvent['ALT-DESC'] === 'object') {
								insertEvent.desc = canvasEvent['ALT-DESC'].val;
								insertEvent.descPlaintext = htmlParser.htmlToText(insertEvent.desc);
							} else {
								insertEvent.desc = '';
								insertEvent.descPlaintext = '';
							}

							validEvents.push(insertEvent);
							checkEvent(++i);
						});
					} else {
						// All done! Call callback
						callback(null, true, validEvents);
					}
				}
				checkEvent(0);

			});
		});
	});
}

module.exports.verifyURL    = verifyURL;
module.exports.setURL       = setURL;
module.exports.getFromCache = getFromCache;
module.exports.getUserCal   = getUserCal;
module.exports.getClasses   = getClasses;
<commit_msg>Re-add `canvas: true` so front-end can distinguish from other events<commit_after>'use strict';

/**
 * @file Reads Canvas calendar and retrieves events to integrate in our planner
 * @module canvas
 */
const _ = require('underscore');
const aliases = require(__dirname + '/aliases.js');
const checkedEvents = require(__dirname + '/checkedEvents.js');
const feeds = require(__dirname + '/feeds.js');
const htmlParser = require(__dirname + '/htmlParser.js');
const ical = require('ical');
const prisma = require('prisma');
const querystring = require('querystring');
const request = require('request');
const url = require('url');
const users = require(__dirname + '/users.js');

// URL Calendars come from
const urlPrefix = 'https://micds.instructure.com/feeds/calendars/';

/**
 * Makes sure a given url is valid and it points to a Canvas calendar feed
 * @function verifyURL
 *
 * @param {string} canvasURL - URI to iCal feed
 * @callback {verifyURLCallback} callback - Callback
 */

/**
 * Returns whether url is valid or not
 * @callback verifyURLCallback
 *
 * @param {Object} err - Null if success, error object if failure.
 * @param {Boolean|string} isValid - True if valid URL, string describing problem if not valid. Null if error.
 * @param {string} url - Valid and formatted URL to our likings. Null if error or invalid url.
 */

function verifyURL(canvasURL, callback) {

	if(typeof callback !== 'function') return;

	if(typeof canvasURL !== 'string') {
		callback(new Error('Invalid URL!'), null, null);
		return;
	}

	// Parse URL first
	const parsedURL = url.parse(canvasURL);

	// Check if pathname is valid
	if(!parsedURL.pathname || !parsedURL.pathname.startsWith('/feeds/calendars/')) {
		// Not a valid URL!
		callback(null, 'Invalid URL path!', null);
		return;
	}

	const pathParts = parsedURL.path.split('/');
	const userCalendar = pathParts[pathParts.length - 1];

	const validURL = urlPrefix + userCalendar;

	// Not lets see if we can actually get any data from here
	request(validURL, (err, response) => {
		if(err) {
			callback(new Error('There was a problem fetching calendar data from the URL!'), null, null);
			return;
		}
		if(response.statusCode !== 200) {
			callback(null, 'Invalid URL!', null);
			return;
		}

		callback(null, true, validURL);
	});
}

/**
 * Sets a user's calendar URL if valid
 * @function setUrl
 *
 * @param {Object} db - Database connection
 * @param {string} user - Username
 * @param {string} url - Calendar url
 * @param {setUrlCallback} callback - Callback
 */

/**
 * Returns the valid url that was inserted into database
 * @callback setUrlCallback
 *
 * @param {Object} err - Null if success, error object if failure
 * @param {Boolean|string} isValid - True if valid URL, string describing problem if not valid. Null if error.
 * @param {string} validURL - Valid url that was inserted into database. Null if error or url invalid.
 */

function setURL(db, user, url, callback) {
	if(typeof callback !== 'function') {
		callback = () => {};
	}

	if(typeof db !== 'object') {
		callback(new Error('Invalid database connection!'), null, null);
		return;
	}

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err, null, null);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'), null, null);
			return;
		}

		verifyURL(url, (err, isValid, validURL) => {
			if(err) {
				callback(err, null, null);
				return;
			} else if(isValid !== true) {
				callback(null, isValid, null);
				return;
			}

			const userdata = db.collection('users');

			userdata.update({ _id: userDoc['_id'] }, { $set: { canvasURL: validURL }}, { upsert: true }, err => {
				if(err) {
					callback(new Error('There was a problem updating the URL to the database!'), null, null);
					return;
				}

				feeds.updateCanvasCache(db, user, err => {
					if(err) {
						callback(err, null, null);
						return;
					}

					callback(null, true, validURL);
				});
			});
		});
	});
}

/**
 * Retrieves a user's events on Canvas from their URL
 * @function getUserCal
 *
 * @param {Object} db - Database connection
 * @param {string} user - Username to get schedule
 * @param {getUserCalCallback} callback - Callback
 */

/**
 * Returns a user's schedule for that day
 * @callback getUserCalCallback
 *
 * @param {Object} err - Null if success, error object if failure.
 * @param {Boolean} hasURL - Whether user has set a valid portal URL. Null if failure.
 * @param {Object} events - Array of all the events in the month. Null if failure.
 */

function getUserCal(db, user, callback) {
	if(typeof callback !== 'function') return;

	if(typeof db !== 'object') {
		callback(new Error('Invalid database connection!'));
		return;
	}

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err, null, null);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'), null, null);
			return;
		}

		if(typeof userDoc.canvasURL !== 'string') {
			callback(null, false, null);
			return;
		}

		request(userDoc.canvasURL, (err, response, body) => {
			if(err) {
				callback(new Error('There was a problem fetching portal data from the URL!'), null, null);
				return;
			}
			if(response.statusCode !== 200) {
				callback(new Error('Invalid URL!'), null, null);
				return;
			}

			callback(null, true, Object.values(ical.parseICS(body)));
		});
	});
}

/**
 * Parses a Canvas assignment title into class name and teacher's name.
 * @function parseCanvasTitle
 *
 * @param {string} title - Canvas assignment title
 * @returns {Object}
 */

function parseCanvasTitle(title) {
	const classTeacherRegex = /\[.+]/g;
	const teacherRegex = /:[A-Z]{5}$/g;
	const firstLastBrackets = /(^\[)|(]$)/g;

	// Get what's in the square brackets, including square brackets
	const classTeacher = _.last(title.match(classTeacherRegex)) || '';
	const classTeacherNoBrackets = classTeacher.replace(firstLastBrackets, '');
	// Subtract the class/teacher from the Canvas title
	const assignmentName = title.replace(classTeacherRegex, '').trim();

	// Also check if there's a teacher, typically separated by a colon
	const teacher = (_.last(classTeacherNoBrackets.match(teacherRegex)) || '').replace(/^:/g, '');
	const teacherFirstName = teacher[0] || '';
	const teacherLastName = (teacher[1] || '') + teacher.substring(2).toLowerCase();

	// Subtract teacher from classTeacher to get the class
	const className = classTeacher.replace(teacher, '').replace(/\[|]/g, '').replace(/:$/g, '');

	return {
		assignment: assignmentName,
		class: {
			raw: classTeacherNoBrackets,
			name: className,
			teacher: {
				raw: teacher,
				firstName: teacherFirstName,
				lastName: teacherLastName
			}
		}
	};
}

/**
 * Parses a Canvas calendar link into an assignment/event link.
 * @function calendarToEvent
 *
 * @param {string} calLink - Calendar link
 * @returns {string}
 */

function calendarToEvent(calLink) {
	// Example calendar link: https://micds.instructure.com/calendar?include_contexts=course_XXXXXXX&month=XX&year=XXXX#assignment_XXXXXXX
	// 'assignment' can also be 'calendar_event'
	const calObject = url.parse(calLink);

	const courseId = querystring.parse(calObject.query)['include_contexts'].replace('_', 's/');

	// Remove hash sign and switch to event URL format
	const eventString = calObject.hash.slice(1);
	let eventId;
	if(eventString.includes('assignment')) {
		eventId = eventString.replace('assignment_', 'assignments/');
	} else if(eventString.includes('calendar_event')) {
		eventId = eventString.replace('calendar_event_', 'calendar_events/');
	}

	return 'https://micds.instructure.com/' + courseId + '/' + eventId;
}

/**
 * Iterates through all of the user's events and get their classes from Canvas
 * @function getClasses
 *
 * @param {Object} db - Database object
 * @param {string} user - Username
 * @param {getClassesCallback} callback - Callback
 */

/**
 * Returns array of classes from canvas
 * @callback getClassesCallback
 *
 * @param {Object} err - Null if success, error object if failure
 * @param {Boolean} hasURL - Whether or not user has a Canvas URL set. Null if error.
 * @param {Array} classes - Array of classes from canvas. Null if error or no Canvas URL set.
 */

function getClasses(db, user, callback) {
	if(typeof callback !== 'function') return;

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err, null, null, null);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'), null, null);
			return;
		}
		if(typeof userDoc['canvasURL'] !== 'string') {
			callback(null, false, null);
			return;
		}

		function parseEvents(eventsToParse) {
			const classes = [];

			for(const calEvent of eventsToParse) {
				// If event doesn't have a summary, skip
				if(typeof calEvent.summary !== 'string') continue;

				const parsedEvent = parseCanvasTitle(calEvent.summary);

				// If not already in classes array, push to array
				if(parsedEvent.class.raw.length > 0 && !_.contains(classes, parsedEvent.class.raw)) {
					classes.push(parsedEvent.class.raw);
				}
			}

			callback(null, true, classes);
		}

		const canvasdata = db.collection('canvasFeeds');

		canvasdata.find({ user: userDoc._id }).toArray((err, events) => {
			if(err) {
				callback(new Error('There was an error retrieving Canvas events!'), null, null);
				return;
			}

			// If cache is empty, update it
			if (events.length > 0) {
				parseEvents(events);
			} else {
				feeds.updateCanvasCache(db, user, err => {
					if (err) {
						callback(err, null, null);
						return;
					}

					canvasdata.find({ user: userDoc._id }).toArray((err, retryEvents) => {
						if(err) {
							callback(new Error('There was an error retrieving Canvas events!'), null, null);
							return;
						}
						parseEvents(retryEvents);
					});
				});
			}
		});
	});
}

/**
 * Get Canvas events from the cache
 * @param {Object} db - Database object
 * @param {string} user - Username
 * @param {getFromCacheCallback} callback - Callback
 */

/**
 * Returns array containing Canvas events
 * @callback getFromCacheCallback
 *
 * @param {Object} err - Null if success, error object if failure
 * @param {Boolean} hasURL - Whether or not user has a Canvas URL set. Null if error.
 * @param {Array} events - Array of events if success, null if failure.
 */

function getFromCache(db, user, callback) {
	if(typeof callback !== 'function') return;

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err, null, null, null);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'), null, null);
			return;
		}
		if(typeof userDoc['canvasURL'] !== 'string') {
			callback(null, false, null);
			return;
		}

		const canvasdata = db.collection('canvasFeeds');

		canvasdata.find({ user: userDoc._id }).toArray((err, events) => {
			if(err) {
				callback(new Error('There was an error retrieving Canvas events!'), null, null);
				return;
			}

			// Get which events are checked
			checkedEvents.list(db, user, (err, checkedEventsList) => {
				if(err) {
					callback(err, null, null);
					return;
				}

				// Loop through all of the events in the calendar feed and push events within month to validEvents
				const validEvents = [];
				// Cache class aliases
				const classAliases = {};

				// Function for getting class to insert according to canvas name
				function getCanvasClass(parsedEvent, classCallback) {
					const name = parsedEvent.class.raw;

					// Check if alias is already cached
					if(typeof classAliases[name] !== 'undefined') {
						classCallback(null, classAliases[name]);
						return;
					}

					// Query aliases to see if possible class object exists
					aliases.getClass(db, user, 'canvas', name, (err, hasAlias, aliasClassObject) => {
						if(err) {
							classCallback(err, null);
							return;
						}

						// Backup object if Canvas class doesn't have alias
						const defaultColor = '#34444F';
						const canvasClass = {
							_id: null,
							canvas: true,
							user,
							name: parsedEvent.class.name,
							teacher: {
								_id: null,
								prefix: '',
								firstName: parsedEvent.class.teacher.firstName,
								lastName: parsedEvent.class.teacher.lastName
							},
							type: 'other',
							block: 'other',
							color: defaultColor,
							textDark: prisma.shouldTextBeDark(defaultColor)
						};

						if(hasAlias) {
							classAliases[name] = aliasClassObject;
						} else {
							classAliases[name] = canvasClass;
						}

						classCallback(null, classAliases[name]);
					});
				}

				// Function to iterate over classes asynchronously
				function checkEvent(i) {
					if(i < events.length) {
						const canvasEvent = events[i];
						const parsedEvent = parseCanvasTitle(canvasEvent.summary);

						// Check if alias for class first
						getCanvasClass(parsedEvent, (err, canvasClass) => {
							if(err) {
								callback(err, null, null);
								return;
							}

							const start = new Date(canvasEvent.start);
							const end = new Date(canvasEvent.end);

							// class will be null if error in getting class name.
							const insertEvent = {
								_id: canvasEvent.uid,
								canvas: true,
								user: userDoc.user,
								class: canvasClass,
								title: parsedEvent.assignment,
								start,
								end,
								link: calendarToEvent(canvasEvent.url) || '',
								checked: _.contains(checkedEventsList, canvasEvent.uid)
							};

							if(typeof canvasEvent['ALT-DESC'] === 'object') {
								insertEvent.desc = canvasEvent['ALT-DESC'].val;
								insertEvent.descPlaintext = htmlParser.htmlToText(insertEvent.desc);
							} else {
								insertEvent.desc = '';
								insertEvent.descPlaintext = '';
							}

							validEvents.push(insertEvent);
							checkEvent(++i);
						});
					} else {
						// All done! Call callback
						callback(null, true, validEvents);
					}
				}
				checkEvent(0);

			});
		});
	});
}

module.exports.verifyURL    = verifyURL;
module.exports.setURL       = setURL;
module.exports.getFromCache = getFromCache;
module.exports.getUserCal   = getUserCal;
module.exports.getClasses   = getClasses;
<|endoftext|>


<commit_before>// this directive is a mess! HELP!
module.exports = ['$sce', '$window', function($sce, $window) {
  return {
    restrict: 'A', // only activate on element attribute
    require: '?ngModel', // get a hold of NgModelController
    link: ($scope, element, attributes, ngModelx) => {
      let ngModel = ngModelx;
      if (!ngModel) return; // do nothing if no ng-model

      let settings = attempt(() => $scope.$eval(attributes['editableType']), {type: 'number', default: 1});

      if (settings) {
        if (settings.type === 'int') {
          ngModel.$parsers.unshift(transformInt);
          ngModel.$validators.number = (modelValue, viewValue) => {
            return typeof modelValue === 'number';
          };
        }
        else if (settings.type === 'number') {
          ngModel.$parsers.unshift(transformFloat);
          ngModel.$validators.number = (modelValue, viewValue) => {
            return typeof modelValue === 'number';
          };
        }
      }

      console.log({attributes});

      if (attributes.selectOnFocus === 'true') element.on('focus', selectText);

      element.on('blur change keypress', () => {
        $scope.$evalAsync(read);
      });

      ngModel.$render = () => {
        element.html(ngModel.$viewValue);
        // element.html($sce.getTrustedHtml(ngModel.$viewValue));
      };

      read();

      function read() {
        let html = element.html(),
            oldValue = ngModel.$viewValue;

        // When we clear the content editable the browser leaves a
 behind
        // If strip-br attribute is provided then we strip this out
        if (attributes.stripBr && html === '
' ) {
          html = '';
        }

        ngModel.$setViewValue(html);

        if (!ngModel.$valid) ngModel.$setViewValue(oldValue);

        ngModel.$render();
      }

      function transformInt(value) {
        value = parseInt(value, 10);
        return isNaN(value) ? settings['default'] : value;
      }

      function transformFloat(value) {
        value = parseFloat(value);
        return isNaN(value) ? settings['default'] : value;
      }

      function selectText() {
        if (document.selection) {
          let range = document.body.createTextRange();
          range.moveToElementText(element[0]);
          range.select();
        }
        else if ($window.getSelection) {
          let selection = $window.getSelection(),
              range = document.createRange();

          range.selectNodeContents(element[0]);
          selection.removeAllRanges();
          selection.addRange(range);
        }
      }
    }
  };
}];

function attempt(fn, defaultValue) {
  let value = defaultValue;
  try {
    value = fn();
  }
  catch (error) {
    value = defaultValue;
  }

  return value;
}

<commit_msg>Fixed a bug, and makes more sense.<commit_after>// this directive is a mess! HELP!
module.exports = ['$sce', '$window', function($sce, $window) {
  return {
    restrict: 'A', // only activate on element attribute
    require: '?ngModel', // get a hold of NgModelController
    link: ($scope, element, attributes, ngModelx) => {
      let ngModel = ngModelx;
      if (!ngModel) return; // do nothing if no ng-model

      let settings = attempt(() => $scope.$eval(attributes['editableType']), {type: 'number', default: 1});

      if (settings) {
        if (settings.type === 'int') {
          ngModel.$parsers.unshift(transformInt);
          ngModel.$validators.number = (modelValue, viewValue) => {
            return typeof modelValue === 'number';
          };
        }
        else if (settings.type === 'number') {
          ngModel.$parsers.unshift(transformFloat);
          ngModel.$validators.number = (modelValue, viewValue) => {
            return typeof modelValue === 'number';
          };
        }
      }

      if (attributes.selectOnFocus === 'true') element.on('focus', selectText);

      element.on('blur change keyup', () => $scope.$evalAsync(read));

      ngModel.$render = () => {
        element.html(ngModel.$viewValue);
        // element.html($sce.getTrustedHtml(ngModel.$viewValue));
      };

      read();

      function read() {
        let html = element.html(),
            oldValue = ngModel.$viewValue;

        // When we clear the content editable the browser leaves a
 behind
        // If strip-br attribute is provided then we strip this out
        if (attributes.stripBr && html === '
' ) {
          html = '';
        }

        ngModel.$setViewValue(html);

        if (!ngModel.$valid) ngModel.$setViewValue(oldValue);
      }

      function transformInt(value) {
        value = parseInt(value, 10);
        return isNaN(value) ? settings['default'] : value;
      }

      function transformFloat(value) {
        value = parseFloat(value);
        return isNaN(value) ? settings['default'] : value;
      }

      function selectText() {
        if (document.selection) {
          let range = document.body.createTextRange();
          range.moveToElementText(element[0]);
          range.select();
        }
        else if ($window.getSelection) {
          let selection = $window.getSelection(),
              range = document.createRange();

          range.selectNodeContents(element[0]);
          selection.removeAllRanges();
          selection.addRange(range);
        }
      }
    }
  };
}];

function attempt(fn, defaultValue) {
  let value = defaultValue;
  try {
    value = fn();
  }
  catch (error) {
    value = defaultValue;
  }

  return value;
}

<|endoftext|>


<commit_before>(function() {

    tinymce.PluginManager.requireLangPack('grappelli');
    var DOM = tinymce.DOM;

    tinymce.create("tinymce.plugins.Grappelli", {
        init: function(ed, url) {
            var t = this;
            tb = ed.getParam("grappelli_adv_toolbar", "toolbar2");
            documentstructure_css = url + "../../../themes/advanced/skins/grappelli/content_documentstructure_" + ed.settings.language + ".css";
            cookie_date = new Date();
            var year = cookie_date.getFullYear();
            cookie_date.setYear(year + 1);

            // get/set cookie
            cookie_grappelli_show_documentstructure = tinymce.util.Cookie.get('grappelli_show_documentstructure');
            if (cookie_grappelli_show_documentstructure != null) {
                ed.settings.grappelli_show_documentstructure = cookie_grappelli_show_documentstructure;
            } else {
                tinymce.util.Cookie.set('grappelli_show_documentstructure', ed.settings.grappelli_show_documentstructure, cookie_date, '/');
            }

            ed.onInit.add(function() {
                if ("mce_fullscreen" == ed.id) {
                    ed.dom.addClass(ed.dom.select('body'), 'fullscreen');
                }
                if (ed.settings.grappelli_adv_hidden) {
                    t._hide_adv_menu(ed);
                } else {
                    t._show_adv_menu(ed);
                }
                if (ed.settings.grappelli_show_documentstructure == "on") {
                    t._show_documentstructure(ed);
                } else {
                    t._hide_documentstructure(ed);
                }
            });

            // ADD COMMAND for SHOW/HIDE ADVANCED MENU
            ed.addCommand("Grappelli_Adv", function() {
                if (DOM.isHidden(ed.controlManager.get(tb).id)) {
                    t._show_adv_menu(ed);
                } else {
                    t._hide_adv_menu(ed);
                }
            });

            // ADD COMMAND for SHOW/HIDE DOCUMENTSTRUCTURE
            ed.addCommand("Grappelli_DocumentStructure", function() {
                i = ed.controlManager;
                if (ed.settings.grappelli_show_documentstructure == "on") {
                    t._hide_documentstructure(ed);
                } else {
                    t._show_documentstructure(ed);
                }
            });

            // ADD BUTTON: ADVANCED MENU
            ed.addButton("grappelli_adv", {
                title: "grappelli.grappelli_adv_desc",
                cmd: "Grappelli_Adv"
            });

            // ADD BUTTON: DOCUMENT STRUCTURE
            ed.addButton("grappelli_documentstructure", {
                title: "grappelli.grappelli_documentstructure_desc",
                cmd: "Grappelli_DocumentStructure"
            });

            /// FULLSCREEN
            ed.onBeforeExecCommand.add(function(ed, cmd, ui, val) {
                if ("mceFullScreen" != cmd) {
                    return;
                };
                if ("mce_fullscreen" == ed.id) {
                    base_ed = tinyMCE.get(ed.settings.fullscreen_editor_id);

                    /// ADVANCED MENU
                    if (!ed.settings.grappelli_adv_hidden) {
                        t._show_adv_menu(base_ed);
                    } else {
                        t._hide_adv_menu(base_ed);
                    }

                    /// DOCUMENT STRUCTURE
                    if (ed.settings.grappelli_show_documentstructure == "on") {
                        t._show_documentstructure(base_ed);
                    } else {
                        t._hide_documentstructure(base_ed);
                    }

                }
            });

            ed.addShortcut("alt+shift+z", ed.getLang("grappelli_adv_desc"), "Grappelli_Adv");

            // CONTENT HAS TO BE WITHIN A BLOCK-LEVEL-ELEMENT
            // ed.onNodeChange.add(function(ed, cm, e) {
            //     if (e.nodeName == "TD" || e.nodeName == "BODY") {
            //         cn = e.childNodes;
            //         for (i = cn.length - 1; i >= 0; i--) {
            //             c_cn = cn[i];
            //             if (c_cn.nodeType == 3 || (!ed.dom.isBlock(c_cn) && c_cn.nodeType != 8)) {
            //                 if (c_cn.nodeType != 3 || /[^\s]/g.test(c_cn.nodeValue)) {
            //                     bl = ed.dom.create('p');
            //                     bl.appendChild(c_cn.cloneNode(1));
            //                     new_cn = c_cn.parentNode.replaceChild(bl, c_cn);
            //                     // move caret
            //                     r = ed.getDoc().createRange();
            //                     r.setStart(bl, bl.nodeValue ? bl.nodeValue.length : 0);
            //                     r.setEnd(bl, bl.nodeValue ? bl.nodeValue.length : 0);
            //                     ed.selection.setRng(r);
            //                 }
            //             }
            //         }
            //     }
            // });

        },

        // INTERNAL: RESIZE
        _resizeIframe: function(ed, tb, b) {
            var iframe = ed.getContentAreaContainer().firstChild;
            DOM.setStyle(iframe, "height", iframe.clientHeight + b);
            ed.theme.deltaHeight += b
        },

        // INTERNAL: SHOW/HIDE DOCUMENT STRUCTURE
        _show_documentstructure: function(ed) {
            head = ed.getBody().previousSibling;
            if (head.lastChild.getAttribute('href') != documentstructure_css) {
                var vs_link = document.createElement("link");
                vs_link.rel="stylesheet";
                vs_link.mce_href = documentstructure_css;
                vs_link.href = documentstructure_css;
                head.appendChild(vs_link);
                ed.settings.grappelli_show_documentstructure = 'on';
                tinymce.util.Cookie.set('grappelli_show_documentstructure', 'on', cookie_date, '/');
                ed.controlManager.setActive('grappelli_documentstructure', true);
            }
        },
        _hide_documentstructure: function(ed) {
            head = ed.getBody().previousSibling;
            if (head.lastChild.getAttribute('href') == documentstructure_css) {
                vs_link = head.lastChild;
                head.removeChild(vs_link);
                ed.settings.grappelli_show_documentstructure = 'off';
                tinymce.util.Cookie.set('grappelli_show_documentstructure', 'off', cookie_date, '/');
                ed.controlManager.setActive('grappelli_documentstructure', false);
            }
        },

        // INTERNAL: SHOW/HIDE ADVANCED MENU
        _show_adv_menu: function(ed) {
            if (ed.controlManager.get(tb)) {
                ed.controlManager.setActive("grappelli_adv", 1);
                DOM.show(ed.controlManager.get(tb).id);
                this._resizeIframe(ed, tb, -28);
                ed.settings.grappelli_adv_hidden = 0;
            }
        },
        _hide_adv_menu: function(ed) {
            if (ed.controlManager.get(tb)) {
                ed.controlManager.setActive("grappelli_adv", 0);
                DOM.hide(ed.controlManager.get(tb).id);
                this._resizeIframe(ed, tb, 28);
                ed.settings.grappelli_adv_hidden = 1;
            }
        },

        // GET INFO
        getInfo: function() {
            return {
                longname: "Grappelli Plugin",
                author: "vonautomatisch (patrick kranzlmueller)",
                authorurl: "http://vonautomatisch.at",
                infourl: "http://code.google.com/p/django-grappelli/",
                version: "1.0"
            }
        }

    });

    tinymce.PluginManager.add("grappelli", tinymce.plugins.Grappelli)

})();
<commit_msg>fixes issue 54 in trunk<commit_after>(function() {

    tinymce.PluginManager.requireLangPack('grappelli');
    var DOM = tinymce.DOM;

    tinymce.create("tinymce.plugins.Grappelli", {
        init: function(ed, url) {
            var t = this;
            tb = ed.getParam("grappelli_adv_toolbar", "toolbar2");
            documentstructure_css = url + "../../../themes/advanced/skins/grappelli/content_documentstructure_" + ed.settings.language + ".css";
            cookie_date = new Date();
            var year = cookie_date.getFullYear();
            cookie_date.setYear(year + 1);

            // get/set cookie
            cookie_grappelli_show_documentstructure = tinymce.util.Cookie.get('grappelli_show_documentstructure');
            if (cookie_grappelli_show_documentstructure != null) {
                ed.settings.grappelli_show_documentstructure = cookie_grappelli_show_documentstructure;
            } else {
                tinymce.util.Cookie.set('grappelli_show_documentstructure', ed.settings.grappelli_show_documentstructure, cookie_date, '/');
            }

            ed.onInit.add(function() {
                if ("mce_fullscreen" == ed.id) {
                    ed.dom.addClass(ed.dom.select('body'), 'fullscreen');
                }
                if (ed.settings.grappelli_adv_hidden) {
                    t._hide_adv_menu(ed);
                } else {
                    t._show_adv_menu(ed);
                }
                if (ed.settings.grappelli_show_documentstructure == "on") {
                    t._show_documentstructure(ed);
                } else {
                    t._hide_documentstructure(ed);
                }
            });

            // ADD COMMAND for SHOW/HIDE ADVANCED MENU
            ed.addCommand("Grappelli_Adv", function() {
                if (DOM.isHidden(ed.controlManager.get(tb).id)) {
                    t._show_adv_menu(ed);
                } else {
                    t._hide_adv_menu(ed);
                }
            });

            // ADD COMMAND for SHOW/HIDE DOCUMENTSTRUCTURE
            ed.addCommand("Grappelli_DocumentStructure", function() {
                i = ed.controlManager;
                if (ed.settings.grappelli_show_documentstructure == "on") {
                    t._hide_documentstructure(ed);
                } else {
                    t._show_documentstructure(ed);
                }
            });

            // ADD BUTTON: ADVANCED MENU
            ed.addButton("grappelli_adv", {
                title: "grappelli.grappelli_adv_desc",
                cmd: "Grappelli_Adv"
            });

            // ADD BUTTON: DOCUMENT STRUCTURE
            ed.addButton("grappelli_documentstructure", {
                title: "grappelli.grappelli_documentstructure_desc",
                cmd: "Grappelli_DocumentStructure"
            });

            /// FULLSCREEN
            ed.onBeforeExecCommand.add(function(ed, cmd, ui, val) {
                if ("mceFullScreen" != cmd) {
                    return;
                };
                if ("mce_fullscreen" == ed.id) {
                    base_ed = tinyMCE.get(ed.settings.fullscreen_editor_id);

                    /// ADVANCED MENU
                    if (!ed.settings.grappelli_adv_hidden) {
                        t._show_adv_menu(base_ed);
                    } else {
                        t._hide_adv_menu(base_ed);
                    }

                    /// DOCUMENT STRUCTURE
                    if (ed.settings.grappelli_show_documentstructure == "on") {
                        t._show_documentstructure(base_ed);
                    } else {
                        t._hide_documentstructure(base_ed);
                    }

                }
            });

            ed.addShortcut("alt+shift+z", ed.getLang("grappelli_adv_desc"), "Grappelli_Adv");

            // CONTENT HAS TO BE WITHIN A BLOCK-LEVEL-ELEMENT
            // ed.onNodeChange.add(function(ed, cm, e) {
            //     if (e.nodeName == "TD" || e.nodeName == "BODY") {
            //         cn = e.childNodes;
            //         for (i = cn.length - 1; i >= 0; i--) {
            //             c_cn = cn[i];
            //             if (c_cn.nodeType == 3 || (!ed.dom.isBlock(c_cn) && c_cn.nodeType != 8)) {
            //                 if (c_cn.nodeType != 3 || /[^\s]/g.test(c_cn.nodeValue)) {
            //                     bl = ed.dom.create('p');
            //                     bl.appendChild(c_cn.cloneNode(1));
            //                     new_cn = c_cn.parentNode.replaceChild(bl, c_cn);
            //                     // move caret
            //                     r = ed.getDoc().createRange();
            //                     r.setStart(bl, bl.nodeValue ? bl.nodeValue.length : 0);
            //                     r.setEnd(bl, bl.nodeValue ? bl.nodeValue.length : 0);
            //                     ed.selection.setRng(r);
            //                 }
            //             }
            //         }
            //     }
            // });

        },

        // INTERNAL: RESIZE
        _resizeIframe: function(ed, tb, b) {
            var iframe = ed.getContentAreaContainer().firstChild;
            DOM.setStyle(iframe, "height", iframe.clientHeight + b);
            ed.theme.deltaHeight += b
        },

        // INTERNAL: SHOW/HIDE DOCUMENT STRUCTURE
        _show_documentstructure: function(ed) {
            head = ed.getBody().previousSibling;
            var headChilds = head.childNodes;

            for (var i = 0; i < headChilds.length; i++) {
                if (headChilds[i].nodeName == "LINK"
                    && headChilds[i].getAttribute('href') == documentstructure_css) {
                    // documentstructure_css is already set so...
                    return;
                }
            }
            var vs_link = document.createElement("link");
            vs_link.rel="stylesheet";
            vs_link.mce_href = documentstructure_css;
            vs_link.href = documentstructure_css;
            head.appendChild(vs_link);
            ed.settings.grappelli_show_documentstructure = 'on';
            tinymce.util.Cookie.set('grappelli_show_documentstructure', 'on', cookie_date, '/');
            ed.controlManager.setActive('grappelli_documentstructure', true);
        },
        _hide_documentstructure: function(ed) {
            head = ed.getBody().previousSibling;
            vs_link = null;

            var headChilds = head.childNodes;

            for (var i = 0; i < headChilds.length; i++) {
                if (headChilds[i].nodeName == "LINK"
                    && headChilds[i].getAttribute('href') == documentstructure_css) {
                    // found the node with documentstructure_css
                    vs_link = headChilds[i];
                    break;
                }
            }

            if (vs_link !== null) {
                // if we found the node with documentstructure_css, delete it
                head.removeChild(vs_link);
                ed.settings.grappelli_show_documentstructure = 'off';
                tinymce.util.Cookie.set('grappelli_show_documentstructure', 'off', cookie_date, '/');
                ed.controlManager.setActive('grappelli_documentstructure', false);
            }
        },

        // INTERNAL: SHOW/HIDE ADVANCED MENU
        _show_adv_menu: function(ed) {
            if (ed.controlManager.get(tb)) {
                ed.controlManager.setActive("grappelli_adv", 1);
                DOM.show(ed.controlManager.get(tb).id);
                this._resizeIframe(ed, tb, -28);
                ed.settings.grappelli_adv_hidden = 0;
            }
        },
        _hide_adv_menu: function(ed) {
            if (ed.controlManager.get(tb)) {
                ed.controlManager.setActive("grappelli_adv", 0);
                DOM.hide(ed.controlManager.get(tb).id);
                this._resizeIframe(ed, tb, 28);
                ed.settings.grappelli_adv_hidden = 1;
            }
        },

        // GET INFO
        getInfo: function() {
            return {
                longname: "Grappelli Plugin",
                author: "vonautomatisch (patrick kranzlmueller)",
                authorurl: "http://vonautomatisch.at",
                infourl: "http://code.google.com/p/django-grappelli/",
                version: "1.0"
            }
        }

    });

    tinymce.PluginManager.add("grappelli", tinymce.plugins.Grappelli)

})();
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

 foam.CLASS({
   package: 'foam.nanos.boot',
   name: 'DAOConfigSummaryView',
   extends: 'foam.u2.Controller',

   documentation: 'Data Management UI for browsing all DAOs.',

   classes: [
     {
       name: 'DAOUpdateControllerView',
       extends: 'foam.comics.DAOUpdateControllerView',

       documentation: 'Same as regular UpdateController except it starts in EDIT mode',

       properties: [
         {
           name: 'controllerMode',
           factory: function() {
             return this.ControllerMode.EDIT;
           }
         }
       ]
     },

     {
        name: 'BackBorder',
        extends: 'foam.u2.Element',

       imports: [ 'stack' ],

        css: `
          ^title {
            padding: 25px;
            font-size: 26px;
          }
          ^title a {
            color: blue;
            text-decoration: underline;
          }
        `,

        properties: [
          'title',
          {
            class: 'foam.u2.ViewSpec',
            name: 'inner'
          }
        ],

        methods: [
          function initE() {
            this.SUPER();

            var self = this;

            this.
              start().
                addClass(this.myClass('title')).
                start('a').
                  add('Data Management').on('click', function() { self.stack.back(); }).
                  // The next line should work but doesn't.
                  // add('Data Management').on('click', this.back).
                end().
                add(' / ', this.title).
              end().
              tag(this.inner);
          }
        ]/*,

        actions: [
          function back() {
            debugger;
            this.stack.back();
          }
        ]*/
     }
   ],

   css: `
     ^ {
       padding: 6px;
     }
     ^dao, ^header {
       display: inline-block;
       font-size: smaller;
       margin: 2px;
       padding: 2px;
       width: 220px;
     }
     ^dao {
       color: #555;
     }
     ^dao:hover {
       background: lightgray;
     }
     ^section {
       display: inline-grid;
     }
     ^header {
       background: /*%BLACK%*/ #1e1f21;
       color: white;
       font-weight: 800;
     }
   `,

   requires: [ 'foam.comics.BrowserView', 'foam.nanos.boot.NSpec' ],

   implements: [ 'foam.mlang.Expressions' ],

   imports: [ 'nSpecDAO', 'stack' ],

   properties: [
     {
       name: 'data',
       factory: function() {
         return this.nSpecDAO;
       }
     },
     {
       name: 'memento'
     },
     {
       name: 'filteredDAO',
       factory: function() {
         return this.data.where(
           this.AND(
             this.ENDS_WITH(foam.nanos.boot.NSpec.ID, 'DAO'),
             this.EQ(foam.nanos.boot.NSpec.SERVE,     true)
           ));
       }
     },
     {
       class: 'String',
       name: 'search',
       view: {
         class: 'foam.u2.TextField',
         type: 'search',
         onKey: true
       },
       value: ''
     }
   ],

   methods: [
     function initE() {
       this.SUPER();

       var self          = this;
       var currentLetter = '';
       var section;

       this.addClass(this.myClass()).
        start().
          style({ 'height': '56px', 'padding-left': '16px', 'padding-top': '8px'}).
          start().
            style({ 'font-size': '26px', 'width': 'fit-content', 'float': 'left', 'padding-top': '10px' }).
            add('Data Management').
          end()
          .start()
            .style({ 'width': 'fit-content', 'float': 'right', 'margin-right': '4%' })
              .add(this.SEARCH)
              .addClass('foam-u2-search-TextSearchView')
              .addClass(this.myClass('foam-u2-search-TextSearchView'))
            .end()
          .end()
        .end();

       var x = this.__subContext__.createSubContext();
       x.register(this.DAOUpdateControllerView, 'foam.comics.DAOUpdateControllerView');

       this.memento$.sub(function() {
         self.stack.push({
           class: self.BackBorder,
           title: self.memento,
           inner: {
             class: self.BrowserView,
             data: self.__context__[self.memento],
             stack: self.stack
           }
         }, x);
       });

       var updateSections = [];
       var i = 0;

       this.filteredDAO.select().then(function(specs) {
         specs.array.sort(function(o1, o2) { return foam.String.compare(o1.id.toUpperCase(), o2.id.toUpperCase())}).forEach(function(spec) {
           var label = foam.String.capitalize(spec.id.substring(0, spec.id.length-3));
           var l     = label.charAt(0);

           if ( l != currentLetter ) {
            var lSection;
             var showSection = foam.core.SimpleSlot.create({value: true});
             i = updateSections.length;

             var updateSlot = foam.core.SimpleSlot.create({value: false});
             updateSections.push(updateSlot);

             updateSections[i].sub(function() {
               var doNeedToShow = false;
               //first child is a header
               for (var j = 1; j <  lSection.instance_.childNodes.length; j++) {
                if ( lSection.instance_.childNodes[j].shown ) {
                  doNeedToShow = true;
                  break;
                }
               }
                showSection.set(doNeedToShow);
             });
             currentLetter = l;

             section = self.start('span')
              .show(showSection.map(function(s) {
                return s;
              }))
              .addClass(self.myClass('section'))
              .start('span')
                .addClass(self.myClass('header'))
                .add(l)
              .end();

              lSection = Object.assign({}, section);
           }

           var localI = i.valueOf();

           var localShow = foam.core.SimpleSlot.create({value: true});
           section
            .start('span')
              .show(localShow.map(function(s) {
                return s;
              }))
              .addClass(self.myClass('dao'))
              .add(label)
              .attrs({title: spec.description})
              .on('click', function() {
                self.memento = spec.id;
              });

              self.search$.sub(function() {
                var contains = false;
                if ( ! self.search )
                  contains = true;
                if ( label.toLowerCase().includes(self.search.toLowerCase()) )
                  contains =  true;
                if ( ! contains && spec.keywords && spec.keywords.length > 0 ) {
                  for(var k in spec.keywords) {
                    if ( k.toLowerCase().includes(self.search.toLowerCase()) ) {
                      contains  = true;
                      break;
                    }
                  }
                }

                localShow.set(contains);
                updateSections[localI].set(! updateSections[localI].get());
              });
         });
       });
     }
   ]
 });
<commit_msg>Fixing code<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

 foam.CLASS({
   package: 'foam.nanos.boot',
   name: 'DAOConfigSummaryView',
   extends: 'foam.u2.Controller',

   documentation: 'Data Management UI for browsing all DAOs.',

   classes: [
     {
       name: 'DAOUpdateControllerView',
       extends: 'foam.comics.DAOUpdateControllerView',

       documentation: 'Same as regular UpdateController except it starts in EDIT mode',

       properties: [
         {
           name: 'controllerMode',
           factory: function() {
             return this.ControllerMode.EDIT;
           }
         }
       ]
     },

     {
        name: 'BackBorder',
        extends: 'foam.u2.Element',

       imports: [ 'stack' ],

        css: `
          ^title {
            padding: 25px;
            font-size: 26px;
          }
          ^title a {
            color: blue;
            text-decoration: underline;
          }
        `,

        properties: [
          'title',
          {
            class: 'foam.u2.ViewSpec',
            name: 'inner'
          }
        ],

        methods: [
          function initE() {
            this.SUPER();

            var self = this;

            this.
              start().
                addClass(this.myClass('title')).
                start('a').
                  add('Data Management').on('click', function() { self.stack.back(); }).
                  // The next line should work but doesn't.
                  // add('Data Management').on('click', this.back).
                end().
                add(' / ', this.title).
              end().
              tag(this.inner);
          }
        ]/*,

        actions: [
          function back() {
            debugger;
            this.stack.back();
          }
        ]*/
     }
   ],

   css: `
     ^ {
       padding: 6px;
     }
     ^dao, ^header {
       display: inline-block;
       font-size: smaller;
       margin: 2px;
       padding: 2px;
       width: 220px;
     }
     ^dao {
       color: #555;
     }
     ^dao:hover {
       background: lightgray;
     }
     ^section {
       display: inline-grid;
     }
     ^header {
       background: /*%BLACK%*/ #1e1f21;
       color: white;
       font-weight: 800;
     }
   `,

   requires: [ 'foam.comics.BrowserView', 'foam.nanos.boot.NSpec' ],

   implements: [ 'foam.mlang.Expressions' ],

   imports: [ 'nSpecDAO', 'stack' ],

   properties: [
     {
       name: 'data',
       factory: function() {
         return this.nSpecDAO;
       }
     },
     {
       name: 'memento'
     },
     {
       name: 'filteredDAO',
       factory: function() {
         return this.data.where(
           this.AND(
             this.ENDS_WITH(foam.nanos.boot.NSpec.ID, 'DAO'),
             this.EQ(foam.nanos.boot.NSpec.SERVE,     true)
           ));
       }
     },
     {
       class: 'String',
       name: 'search',
       view: {
         class: 'foam.u2.TextField',
         type: 'search',
         onKey: true
       },
     }
   ],

   methods: [
     function initE() {
       this.SUPER();

       var self          = this;
       var currentLetter = '';
       var section;

       this.addClass(this.myClass()).
        start().
          style({ 'height': '56px', 'padding-left': '16px', 'padding-top': '8px'}).
          start().
            style({ 'font-size': '26px', 'width': 'fit-content', 'float': 'left', 'padding-top': '10px' }).
            add('Data Management').
          end()
          .start()
            .style({ 'width': 'fit-content', 'float': 'right', 'margin-right': '4%' })
              .add(this.SEARCH)
              .addClass('foam-u2-search-TextSearchView')
              .addClass(this.myClass('foam-u2-search-TextSearchView'))
            .end()
          .end()
        .end();

       var x = this.__subContext__.createSubContext();
       x.register(this.DAOUpdateControllerView, 'foam.comics.DAOUpdateControllerView');

       this.memento$.sub(function() {
         self.stack.push({
           class: self.BackBorder,
           title: self.memento,
           inner: {
             class: self.BrowserView,
             data: self.__context__[self.memento],
             stack: self.stack
           }
         }, x);
       });

       var updateSections = [];
       var i = 0;

       this.filteredDAO.select().then(function(specs) {
         specs.array.sort(function(o1, o2) { return foam.String.compare(o1.id.toUpperCase(), o2.id.toUpperCase())}).forEach(function(spec) {
           var label = foam.String.capitalize(spec.id.substring(0, spec.id.length-3));
           var l     = label.charAt(0);

           if ( l != currentLetter ) {
            var lSection;
             var showSection = foam.core.SimpleSlot.create({value: true});
             i = updateSections.length;

             var updateSlot = foam.core.SimpleSlot.create({value: false});
             updateSections.push(updateSlot);

             updateSections[i].sub(function() {
               var doNeedToShow = false;
               //first child is a header
               for (var j = 1 ; j <  lSection.instance_.childNodes.length ; j++) {
                if ( lSection.instance_.childNodes[j].shown ) {
                  doNeedToShow = true;
                  break;
                }
               }
                showSection.set(doNeedToShow);
             });
             currentLetter = l;

             section = self.start('span')
              .show(showSection)
              .addClass(self.myClass('section'))
              .start('span')
                .addClass(self.myClass('header'))
                .add(l)
              .end();

              lSection = Object.assign({}, section);
           }

           var localI = i.valueOf();

           var localShow = foam.core.SimpleSlot.create({value: true});
           section
            .start('span')
              .show(localShow)
              .addClass(self.myClass('dao'))
              .add(label)
              .attrs({title: spec.description})
              .on('click', function() {
                self.memento = spec.id;
              });

              self.search$.sub(function() {
                var contains = false;
                if ( ! self.search )
                  contains = true;
                if ( label.toLowerCase().includes(self.search.toLowerCase()) )
                  contains =  true;
                if ( ! contains && spec.keywords && spec.keywords.length > 0 ) {
                  for ( var k in spec.keywords ) {
                    if ( k.toLowerCase().includes(self.search.toLowerCase()) ) {
                      contains  = true;
                      break;
                    }
                  }
                }

                localShow.set(contains);
                updateSections[localI].set(! updateSections[localI].get());
              });
         });
       });
     }
   ]
 });
<|endoftext|>