<commit_before>// used in profile banner
Handlebars.registerHelper("formatPhoneNumber", function(str) {
    if (str.length == 10) {
        return str.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3');
    }
    return str;
});

Handlebars.registerHelper("formatStudentCredits", function(str) {
    return parseInt(str, 10);
});

(function() {

    function parse_date(str) {
        // MUWM-2505 - dates without times assume a time zone of UTC
        if (str.match(/^\d{4}-\d{2}-\d{2}$/)) {
            str = str + " 00:00:00";
        }
        // MUWM-2289.  Our dates come in looking like:
        // 2014-11-26 23:59:59
        // sometimes.  Rather than hunt all of those down, we can just convert
        // that to ISO-8601
        // If that's still invalid - write more code.
        var d = new Date(str);
        if (isNaN(d.getYear())) {
            d = new Date(str.replace(" ", "T"));
        }
        return d;
    }

    // used on course card
    Handlebars.registerHelper("toMonthDay", function(str) {
        return moment(parse_date(str)).format("MMM D");
    });

    // used on course card
    Handlebars.registerHelper("toMoreDay", function(str) {
        var d =  moment().from(moment(parse_date(str)), true);
        if (str.match(/^a [a-z]+/)) {
            return d.replace(/^a /, 'one more ');
        } else
            return d.replace(/ ([a-z]+)$/, ' more $1');
    });

    // used on Library card
    Handlebars.registerHelper("toFromNowDate", function(str) {
        return moment(parse_date(str)).fromNow();
    });

    // used on Grade, Library card
    Handlebars.registerHelper("toFriendlyDate", function(str) {
        return moment(parse_date(str)).format("ddd, MMM D");
    });

    Handlebars.registerHelper("toFriendlyDateVerbose", function(str) {
        return moment(parse_date(str)).format("dddd, MMMM D");
    });
})();

(function() {
    var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];
    var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

    function _get_date(d) {
        return new Date(d.replace(/-/g, "/") + " 00:00:00");
    }

    function _date_range(d1, d2) {
        var date1 = _get_date(d1);
        var date2 = _get_date(d2);

        if (date1.getMonth() == date2.getMonth() && date1.getYear() == date2.getYear()) {
            return [months[date1.getMonth()], date1.getDate(), "-", date2.getDate()].join(" ");
        }
        else {
            return [months[date1.getMonth()], date1.getDate(), "-", months[date2.getMonth()], date2.getDate()].join(" ");
        }
    }

    Handlebars.registerHelper('acal_banner_date_format', function(d1, d2) {
        if (typeof(d2) != 'string' || d1 == d2) {
            var date1 = _get_date(d1);
            return [months[date1.getMonth()], date1.getDate(), "("+days[date1.getDay()]+")"].join(" ");
        }
        else {
            return _date_range(d1, d2);
        }
    });
    Handlebars.registerHelper('acal_page_date_format', function(d1, d2) {
        if (typeof(d2) != 'string' || d1 == d2) {
            var date1 = _get_date(d1);
            return [months[date1.getMonth()], date1.getDate()].join(" ");
        }
        else {
            return _date_range(d1, d2);
        }

    });
})();

Handlebars.registerHelper("toUrlSafe", function(str) {
    return str.replace(/ /g, "%20");
});

Handlebars.registerHelper("toLowerCase", function(str) {
    if (str) {
        return str.toLowerCase();
    }
    return str;
});

// convert term string from "2013,summer,a-term" to "summer a-term"
Handlebars.registerHelper("termNoYear", function(term) {
    value = term.split(",");
    str = value[1];
    if (value[2]) {
        str += " " + value[2];
    }
    return str;
});

Handlebars.registerHelper("capitalizeString", function(str) {
    return capitalizeString(str);
});

// convert term string from "2013,summer,a-term" to "Summer 2013 A-Term"
Handlebars.registerHelper("titleFormatTerm", function(term) {
    value = term.split(",");
    str = capitalizeString(value[1]) + " " + value[0];
    if (value[2]) {
        str += " " + capitalizeString(value[2]);
    }
    return str;
});

// Google maps gets very confused by some characters in map urls
Handlebars.registerHelper("encodeForMaps", function(str) {
    str = str.replace(/ \(/g, " - ");
    str = str.replace(/[\)&]/g, "");
    str = encodeURIComponent(str);
    return str;
});

//probably extraneous
Handlebars.registerHelper("formatTime", function(time) {
    formatted = time.toString().split(":");
    formatted[0] = parseInt(formatted[0], 10);
    if (formatted[0] > 12) {
        formatted[0] -= 12;
    }
    return formatted.join(":");
});

//converts 24 hour time to 12 hour
Handlebars.registerHelper("formatTimeAMPM", function(time) {
    formatted = time.toString().split(":");
    formatted[0] = parseInt(formatted[0], 10);
    if (formatted[0] < 12) {
        formatted[1] += "AM";
    }
    else {
        formatted[1] += "PM";
    }

    if (formatted[0] > 12) {
        formatted[0] = formatted[0] - 12;
    }
    return formatted.join(":");
});

// converts date string into 12 hour display - no am/pm
Handlebars.registerHelper("formatDateAsTime", function(date_str) {
    var date = date_from_string(date_str);
    var hours = date.getHours();
    var minutes = date.getMinutes();
    if (minutes < 10) {
        minutes = "0"+minutes;
    }

    if (hours > 12) {
        hours = hours - 12;
    }
    return hours + ":" + minutes;
});

// converts date string into 12 hour am/pm display
Handlebars.registerHelper("formatDateAsTimeAMPM", function(date_str) {
    var date = date_from_string(date_str);
    var hours = date.getHours();
    var minutes = date.getMinutes();
    var am_pm;
    if (hours < 12) {
        am_pm = "AM";
    }
    else {
        am_pm = "PM";
    }

    if (minutes < 10) {
        minutes = "0"+minutes;
    }
    if (hours > 12) {
        hours = hours - 12;
    }
    return hours + ":" + minutes + am_pm;
});

// converts date string into a day display
Handlebars.registerHelper("formatDateAsDate", function(date_str) {
    var date = date_from_string(date_str);
    var day_of_week = date.getDay();
    var month_num = date.getMonth();
    var day_of_month = date.getDate();

    var day_names = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
    var month_names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

    return new Handlebars.SafeString(day_names[day_of_week] + ", " + month_names[month_num] + " " + day_of_month);
});

// converts date string into the label for the final exams schedule
Handlebars.registerHelper("formatDateAsFinalsDay", function(date_str, days_back) {
    var date = date_from_string(date_str);
    date.setDate(date.getDate() - days_back);
    var day_of_week = date.getDay();
    var month_num = date.getMonth();
    var day_of_month = date.getDate();

    var month_names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

    return month_names[month_num] + " " + day_of_month;
});

Handlebars.registerHelper("ucfirst", function(str) {
    return str.replace(/^([a-z])/, function(match) {
        return match.toUpperCase();
    });
});

Handlebars.registerHelper("formatPrice", function(price) {
    formatted = price.toString().split(".");
    if (formatted[1] && formatted[1].length == 1) {
        formatted[1] += "0";
    }
    if (!formatted[1] || formatted[1].length === 0) {
        formatted[1] = "00";
    }
    return formatted.join(".");
});

Handlebars.registerHelper('equal', function(value1, value2, options) {
    if (arguments.length < 3)
        throw new Error("Handlebars Helper equal needs 2 parameters");
    if(value1 != value2) {
        return options.inverse(this);
    }
    else {
        return options.fn(this);
    }
});

Handlebars.registerHelper("eachWithIndex", function(array, fn) {
    var buffer = "";
    for (var i = 0, j = array.length; i < j; i++) {
        var item = array[i];
        item.index = i;
        buffer += fn.fn(item);
    }
    return buffer;
});

Handlebars.registerHelper('format_schedule_hour', function(hour, position) {
    if (parseInt(hour, 10) === 12) {
        VisualSchedule.shown_am_marker = true;
        return hour + "p";
    }
    else if (hour > 12) {
        var shown_hour = hour - 12;
        if (position === 0) {
            return shown_hour + "p";
        }
        return shown_hour;
    }
    else if (hour < 12) {
        if (position === 0) {
            return hour + "a";
        }
    }
    return hour;
});

Handlebars.registerHelper('time_percentage', function(time, start, end) {
    return VisualSchedule.get_scaled_percentage(time, start, end);
});

Handlebars.registerHelper('time_percentage_height', function(start, end, min, max) {
    var top = VisualSchedule.get_scaled_percentage(start, min, max);
    var bottom = VisualSchedule.get_scaled_percentage(end, min, max);

    return bottom-top;
});

Handlebars.registerHelper('show_days_meetings', function(list, start_time, end_time) {
    if (!VisualSchedule.day_template) {
        var day_source = $("#visual_schedule_day").html();
        var _day_template = Handlebars.compile(day_source);

        VisualSchedule.day_template = _day_template;
    }

    return new Handlebars.SafeString(VisualSchedule.day_template({ meetings: list, start_time: start_time, end_time: end_time }));
});

Handlebars.registerHelper('show_days_finals', function(list, start_time, end_time, term) {
    if (!VisualSchedule.day_template) {
        var day_source = $("#finals_schedule_day").html();
        var _day_template = Handlebars.compile(day_source);

        VisualSchedule.day_template = _day_template;
    }

    return new Handlebars.SafeString(VisualSchedule.day_template({ meetings: list, start_time: start_time, end_time: end_time, term: term }));
});

Handlebars.registerHelper('show_card_days_meetings', function(list, start_time, end_time) {
    if (!VisualScheduleCard.day_template) {
        var day_source = $("#visual_schedule_card_day").html();
        var _day_template = Handlebars.compile(day_source);

        VisualScheduleCard.day_template = _day_template;
    }

    return new Handlebars.SafeString(VisualScheduleCard.day_template({ meetings: list, start_time: start_time, end_time: end_time }));
});

Handlebars.registerHelper('show_final_card_day', function(list, start_time, end_time, term) {
    if (!FinalExamCard.day_template) {
        var day_source = $("#final_exam_schedule_card_day").html();
        var _day_template = Handlebars.compile(day_source);

        FinalExamCard.day_template = _day_template;
    }

    return new Handlebars.SafeString(FinalExamCard.day_template({ meetings: list, start_time: start_time, end_time: end_time, term: term }));
});

Handlebars.registerHelper('pluralize', function(number, single, plural) {
    if (number === 1) {
        return single;
    }
    return plural;
});

Handlebars.registerHelper('pluralize_by_size', function(list, single, plural) {
    if (list.length === 1) {
        return single;
    }
    return plural;
});

Handlebars.registerHelper('greater_than', function(value1, value2, options) {
    if (arguments.length < 3)
        throw new Error("Handlebars Helper greater_than needs 2 parameters");
    if(value1 > value2) {
        return options.inverse(this);
    }
    else {
        return options.fn(this);
    }
});
<commit_msg>Handle case like an hour in toMoreDay.<commit_after>// used in profile banner
Handlebars.registerHelper("formatPhoneNumber", function(str) {
    if (str.length == 10) {
        return str.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3');
    }
    return str;
});

Handlebars.registerHelper("formatStudentCredits", function(str) {
    return parseInt(str, 10);
});

(function() {

    function parse_date(str) {
        // MUWM-2505 - dates without times assume a time zone of UTC
        if (str.match(/^\d{4}-\d{2}-\d{2}$/)) {
            str = str + " 00:00:00";
        }
        // MUWM-2289.  Our dates come in looking like:
        // 2014-11-26 23:59:59
        // sometimes.  Rather than hunt all of those down, we can just convert
        // that to ISO-8601
        // If that's still invalid - write more code.
        var d = new Date(str);
        if (isNaN(d.getYear())) {
            d = new Date(str.replace(" ", "T"));
        }
        return d;
    }

    // used on course card
    Handlebars.registerHelper("toMonthDay", function(str) {
        return moment(parse_date(str)).format("MMM D");
    });

    // used on course card
    Handlebars.registerHelper("toMoreDay", function(str) {
        var d =  moment().from(moment(parse_date(str)), true);
        if (str.match(/^an? [a-z]+/)) {
            return d.replace(/^an? /, 'one more ');
        } else
            return d.replace(/ ([a-z]+)$/, ' more $1');
    });

    // used on Library card
    Handlebars.registerHelper("toFromNowDate", function(str) {
        return moment(parse_date(str)).fromNow();
    });

    // used on Grade, Library card
    Handlebars.registerHelper("toFriendlyDate", function(str) {
        return moment(parse_date(str)).format("ddd, MMM D");
    });

    Handlebars.registerHelper("toFriendlyDateVerbose", function(str) {
        return moment(parse_date(str)).format("dddd, MMMM D");
    });
})();

(function() {
    var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];
    var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

    function _get_date(d) {
        return new Date(d.replace(/-/g, "/") + " 00:00:00");
    }

    function _date_range(d1, d2) {
        var date1 = _get_date(d1);
        var date2 = _get_date(d2);

        if (date1.getMonth() == date2.getMonth() && date1.getYear() == date2.getYear()) {
            return [months[date1.getMonth()], date1.getDate(), "-", date2.getDate()].join(" ");
        }
        else {
            return [months[date1.getMonth()], date1.getDate(), "-", months[date2.getMonth()], date2.getDate()].join(" ");
        }
    }

    Handlebars.registerHelper('acal_banner_date_format', function(d1, d2) {
        if (typeof(d2) != 'string' || d1 == d2) {
            var date1 = _get_date(d1);
            return [months[date1.getMonth()], date1.getDate(), "("+days[date1.getDay()]+")"].join(" ");
        }
        else {
            return _date_range(d1, d2);
        }
    });
    Handlebars.registerHelper('acal_page_date_format', function(d1, d2) {
        if (typeof(d2) != 'string' || d1 == d2) {
            var date1 = _get_date(d1);
            return [months[date1.getMonth()], date1.getDate()].join(" ");
        }
        else {
            return _date_range(d1, d2);
        }

    });
})();

Handlebars.registerHelper("toUrlSafe", function(str) {
    return str.replace(/ /g, "%20");
});

Handlebars.registerHelper("toLowerCase", function(str) {
    if (str) {
        return str.toLowerCase();
    }
    return str;
});

// convert term string from "2013,summer,a-term" to "summer a-term"
Handlebars.registerHelper("termNoYear", function(term) {
    value = term.split(",");
    str = value[1];
    if (value[2]) {
        str += " " + value[2];
    }
    return str;
});

Handlebars.registerHelper("capitalizeString", function(str) {
    return capitalizeString(str);
});

// convert term string from "2013,summer,a-term" to "Summer 2013 A-Term"
Handlebars.registerHelper("titleFormatTerm", function(term) {
    value = term.split(",");
    str = capitalizeString(value[1]) + " " + value[0];
    if (value[2]) {
        str += " " + capitalizeString(value[2]);
    }
    return str;
});

// Google maps gets very confused by some characters in map urls
Handlebars.registerHelper("encodeForMaps", function(str) {
    str = str.replace(/ \(/g, " - ");
    str = str.replace(/[\)&]/g, "");
    str = encodeURIComponent(str);
    return str;
});

//probably extraneous
Handlebars.registerHelper("formatTime", function(time) {
    formatted = time.toString().split(":");
    formatted[0] = parseInt(formatted[0], 10);
    if (formatted[0] > 12) {
        formatted[0] -= 12;
    }
    return formatted.join(":");
});

//converts 24 hour time to 12 hour
Handlebars.registerHelper("formatTimeAMPM", function(time) {
    formatted = time.toString().split(":");
    formatted[0] = parseInt(formatted[0], 10);
    if (formatted[0] < 12) {
        formatted[1] += "AM";
    }
    else {
        formatted[1] += "PM";
    }

    if (formatted[0] > 12) {
        formatted[0] = formatted[0] - 12;
    }
    return formatted.join(":");
});

// converts date string into 12 hour display - no am/pm
Handlebars.registerHelper("formatDateAsTime", function(date_str) {
    var date = date_from_string(date_str);
    var hours = date.getHours();
    var minutes = date.getMinutes();
    if (minutes < 10) {
        minutes = "0"+minutes;
    }

    if (hours > 12) {
        hours = hours - 12;
    }
    return hours + ":" + minutes;
});

// converts date string into 12 hour am/pm display
Handlebars.registerHelper("formatDateAsTimeAMPM", function(date_str) {
    var date = date_from_string(date_str);
    var hours = date.getHours();
    var minutes = date.getMinutes();
    var am_pm;
    if (hours < 12) {
        am_pm = "AM";
    }
    else {
        am_pm = "PM";
    }

    if (minutes < 10) {
        minutes = "0"+minutes;
    }
    if (hours > 12) {
        hours = hours - 12;
    }
    return hours + ":" + minutes + am_pm;
});

// converts date string into a day display
Handlebars.registerHelper("formatDateAsDate", function(date_str) {
    var date = date_from_string(date_str);
    var day_of_week = date.getDay();
    var month_num = date.getMonth();
    var day_of_month = date.getDate();

    var day_names = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
    var month_names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

    return new Handlebars.SafeString(day_names[day_of_week] + ", " + month_names[month_num] + " " + day_of_month);
});

// converts date string into the label for the final exams schedule
Handlebars.registerHelper("formatDateAsFinalsDay", function(date_str, days_back) {
    var date = date_from_string(date_str);
    date.setDate(date.getDate() - days_back);
    var day_of_week = date.getDay();
    var month_num = date.getMonth();
    var day_of_month = date.getDate();

    var month_names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

    return month_names[month_num] + " " + day_of_month;
});

Handlebars.registerHelper("ucfirst", function(str) {
    return str.replace(/^([a-z])/, function(match) {
        return match.toUpperCase();
    });
});

Handlebars.registerHelper("formatPrice", function(price) {
    formatted = price.toString().split(".");
    if (formatted[1] && formatted[1].length == 1) {
        formatted[1] += "0";
    }
    if (!formatted[1] || formatted[1].length === 0) {
        formatted[1] = "00";
    }
    return formatted.join(".");
});

Handlebars.registerHelper('equal', function(value1, value2, options) {
    if (arguments.length < 3)
        throw new Error("Handlebars Helper equal needs 2 parameters");
    if(value1 != value2) {
        return options.inverse(this);
    }
    else {
        return options.fn(this);
    }
});

Handlebars.registerHelper("eachWithIndex", function(array, fn) {
    var buffer = "";
    for (var i = 0, j = array.length; i < j; i++) {
        var item = array[i];
        item.index = i;
        buffer += fn.fn(item);
    }
    return buffer;
});

Handlebars.registerHelper('format_schedule_hour', function(hour, position) {
    if (parseInt(hour, 10) === 12) {
        VisualSchedule.shown_am_marker = true;
        return hour + "p";
    }
    else if (hour > 12) {
        var shown_hour = hour - 12;
        if (position === 0) {
            return shown_hour + "p";
        }
        return shown_hour;
    }
    else if (hour < 12) {
        if (position === 0) {
            return hour + "a";
        }
    }
    return hour;
});

Handlebars.registerHelper('time_percentage', function(time, start, end) {
    return VisualSchedule.get_scaled_percentage(time, start, end);
});

Handlebars.registerHelper('time_percentage_height', function(start, end, min, max) {
    var top = VisualSchedule.get_scaled_percentage(start, min, max);
    var bottom = VisualSchedule.get_scaled_percentage(end, min, max);

    return bottom-top;
});

Handlebars.registerHelper('show_days_meetings', function(list, start_time, end_time) {
    if (!VisualSchedule.day_template) {
        var day_source = $("#visual_schedule_day").html();
        var _day_template = Handlebars.compile(day_source);

        VisualSchedule.day_template = _day_template;
    }

    return new Handlebars.SafeString(VisualSchedule.day_template({ meetings: list, start_time: start_time, end_time: end_time }));
});

Handlebars.registerHelper('show_days_finals', function(list, start_time, end_time, term) {
    if (!VisualSchedule.day_template) {
        var day_source = $("#finals_schedule_day").html();
        var _day_template = Handlebars.compile(day_source);

        VisualSchedule.day_template = _day_template;
    }

    return new Handlebars.SafeString(VisualSchedule.day_template({ meetings: list, start_time: start_time, end_time: end_time, term: term }));
});

Handlebars.registerHelper('show_card_days_meetings', function(list, start_time, end_time) {
    if (!VisualScheduleCard.day_template) {
        var day_source = $("#visual_schedule_card_day").html();
        var _day_template = Handlebars.compile(day_source);

        VisualScheduleCard.day_template = _day_template;
    }

    return new Handlebars.SafeString(VisualScheduleCard.day_template({ meetings: list, start_time: start_time, end_time: end_time }));
});

Handlebars.registerHelper('show_final_card_day', function(list, start_time, end_time, term) {
    if (!FinalExamCard.day_template) {
        var day_source = $("#final_exam_schedule_card_day").html();
        var _day_template = Handlebars.compile(day_source);

        FinalExamCard.day_template = _day_template;
    }

    return new Handlebars.SafeString(FinalExamCard.day_template({ meetings: list, start_time: start_time, end_time: end_time, term: term }));
});

Handlebars.registerHelper('pluralize', function(number, single, plural) {
    if (number === 1) {
        return single;
    }
    return plural;
});

Handlebars.registerHelper('pluralize_by_size', function(list, single, plural) {
    if (list.length === 1) {
        return single;
    }
    return plural;
});

Handlebars.registerHelper('greater_than', function(value1, value2, options) {
    if (arguments.length < 3)
        throw new Error("Handlebars Helper greater_than needs 2 parameters");
    if(value1 > value2) {
        return options.inverse(this);
    }
    else {
        return options.fn(this);
    }
});
<|endoftext|>


<commit_before>var assert = chai.assert;

describe('addHeadingAnchors', function() {
    addHeadingAnchors.init('#testarea');

    it('added the header links beside each heading', function() {
        var headingsWithAnId = document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]');

        headingsWithAnId.forEach(function assertHasCopyLink(heading) {
            var anchor = heading.lastChild,
                href,
                clipboardDataAttribute;

            // is element
            assert.equal(anchor.nodeType, 1);

            // is an anchor
            assert.equal(anchor.nodeName, 'A');

            // href is to id

            href = anchor.getAttribute('href');

            assert.endsWith(href, "#" + heading.getAttribute('id'));

            // data-clipboard-text is the full url

            clipboardDataAttribute = anchor.getAttribute('data-clipboard-text');

            // this is a crappy/quick way to assert for the full url being in the data attribute.
            assert.startsWith(clipboardDataAttribute, 'http://');
            assert.endsWith(clipboardDataAttribute, "#" + heading.getAttribute('id'));
        });
    });

    it('copies to clipboard when clicked', function() {
        var clipboardAnchors = document.querySelectorAll('a[data-clipboard-text]');

        clipboardAnchors.forEach(function assertClipboardCopy(anchor) {

        });
    })
});<commit_msg>move some of the setup out, this will make it easier to refactor these tests later.<commit_after>var assert = chai.assert;

describe('addHeadingAnchors', function() {
    before(function() {
        addHeadingAnchors.init('#testarea');
        this.headingsWithAnId = document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]');
        this.clipboardAnchors = document.querySelectorAll('a[data-clipboard-text]');
    });

    it('added the header links beside each heading', function() {
        this.headingsWithAnId.forEach(function assertHasCopyLink(heading) {
            var anchor = heading.lastChild,
                href,
                clipboardDataAttribute;

            // is element
            assert.equal(anchor.nodeType, 1);

            // is an anchor
            assert.equal(anchor.nodeName, 'A');

            // href is to id

            href = anchor.getAttribute('href');

            assert.endsWith(href, "#" + heading.getAttribute('id'));

            // data-clipboard-text is the full url

            clipboardDataAttribute = anchor.getAttribute('data-clipboard-text');

            // this is a crappy/quick way to assert for the full url being in the data attribute.
            assert.startsWith(clipboardDataAttribute, 'http://');
            assert.endsWith(clipboardDataAttribute, "#" + heading.getAttribute('id'));
        });
    });

    it('copies to clipboard when clicked', function() {
        this.clipboardAnchors.forEach(function assertClipboardCopy(anchor) {

        });
    })
});<|endoftext|>


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

/*global exports:true*/

/**
 * Desugars ES6 Arrow functions to ES3 function expressions.
 * If the function contains `this` expression -- automatically
 * binds the function to current value of `this`.
 *
 * Single parameter, simple expression:
 *
 * [1, 2, 3].map(x => x * x);
 *
 * [1, 2, 3].map(function(x) { return x * x; });
 *
 * Several parameters, complex block:
 *
 * this.users.forEach((user, idx) => {
 *   return this.isActive(idx) && this.send(user);
 * });
 *
 * this.users.forEach(function(user, idx) {
 *   return this.isActive(idx) && this.send(user);
 * }.bind(this));
 *
 */
var restParamVisitors = require('./es6-rest-param-visitors');
var destructuringVisitors = require('./es6-destructuring-visitors');

var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');

/**
 * @public
 */
function visitArrowFunction(traverse, node, path, state) {
  var notInExpression = (path[0].type === Syntax.ExpressionStatement);

  // Wrap a function into a grouping operator, if it's not
  // in the expression position.
  if (notInExpression) {
    utils.append('(', state);
  }

  utils.append('function', state);
  renderParams(traverse, node, path, state);

  // Skip arrow.
  utils.catchupWhiteSpace(node.body.range[0], state);

  var renderBody = node.body.type == Syntax.BlockStatement
    ? renderStatementBody
    : renderExpressionBody;

  path.unshift(node);
  renderBody(traverse, node, path, state);
  path.shift();

  // Bind the function only if `this` value is used
  // inside it or inside any sub-expression.
  var containsBindingSyntax =
    utils.containsChildMatching(node.body, function(node) {
      return node.type === Syntax.ThisExpression
             || (node.type === Syntax.Identifier
                 && node.name === "super")
             || (node.type === Syntax.XJSIdentifier
                 && node.name === 'this');
    });

  if (containsBindingSyntax) {
    utils.append('.bind(this)', state);
  }

  utils.catchupWhiteSpace(node.range[1], state);

  // Close wrapper if not in the expression.
  if (notInExpression) {
    utils.append(')', state);
  }

  return false;
}

function renderParams(traverse, node, path, state) {
  // To preserve inline typechecking directives, we
  // distinguish between parens-free and paranthesized single param.
  if (isParensFreeSingleParam(node, state) || !node.params.length) {
    utils.append('(', state);
  }
  if (node.params.length !== 0) {
    path.unshift(node);
    traverse(node.params, path, state);
    path.unshift();
  }
  utils.append(')', state);
}

function isParensFreeSingleParam(node, state) {
  return node.params.length === 1 &&
    state.g.source[state.g.position] !== '(';
}

function renderExpressionBody(traverse, node, path, state) {
  // Wrap simple expression bodies into a block
  // with explicit return statement.
  utils.append('{', state);

  // Special handling of rest param.
  if (node.rest) {
    utils.append(
      restParamVisitors.renderRestParamSetup(node, state),
      state
    );
  }

  // Special handling of destructured params.
  destructuringVisitors.renderDestructuredComponents(
    node,
    utils.updateState(state, {
      localScope: {
        parentNode: state.parentNode,
        parentScope: state.parentScope,
        identifiers: state.identifiers,
        tempVarIndex: 0
      }
    })
  );

  utils.append('return ', state);
  renderStatementBody(traverse, node, path, state);
  utils.append(';}', state);
}

function renderStatementBody(traverse, node, path, state) {
  traverse(node.body, path, state);
  utils.catchup(node.body.range[1], state);
}

visitArrowFunction.test = function(node, path, state) {
  return node.type === Syntax.ArrowFunctionExpression;
};

exports.visitorList = [
  visitArrowFunction
];

<commit_msg>s/XJS/JSX/ in b370c99706f075c6dfea134db5049d0f5e1669a7<commit_after>/**
 * Copyright 2013 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*global exports:true*/

/**
 * Desugars ES6 Arrow functions to ES3 function expressions.
 * If the function contains `this` expression -- automatically
 * binds the function to current value of `this`.
 *
 * Single parameter, simple expression:
 *
 * [1, 2, 3].map(x => x * x);
 *
 * [1, 2, 3].map(function(x) { return x * x; });
 *
 * Several parameters, complex block:
 *
 * this.users.forEach((user, idx) => {
 *   return this.isActive(idx) && this.send(user);
 * });
 *
 * this.users.forEach(function(user, idx) {
 *   return this.isActive(idx) && this.send(user);
 * }.bind(this));
 *
 */
var restParamVisitors = require('./es6-rest-param-visitors');
var destructuringVisitors = require('./es6-destructuring-visitors');

var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');

/**
 * @public
 */
function visitArrowFunction(traverse, node, path, state) {
  var notInExpression = (path[0].type === Syntax.ExpressionStatement);

  // Wrap a function into a grouping operator, if it's not
  // in the expression position.
  if (notInExpression) {
    utils.append('(', state);
  }

  utils.append('function', state);
  renderParams(traverse, node, path, state);

  // Skip arrow.
  utils.catchupWhiteSpace(node.body.range[0], state);

  var renderBody = node.body.type == Syntax.BlockStatement
    ? renderStatementBody
    : renderExpressionBody;

  path.unshift(node);
  renderBody(traverse, node, path, state);
  path.shift();

  // Bind the function only if `this` value is used
  // inside it or inside any sub-expression.
  var containsBindingSyntax =
    utils.containsChildMatching(node.body, function(node) {
      return node.type === Syntax.ThisExpression
             || (node.type === Syntax.Identifier
                 && node.name === "super")
             || (node.type === Syntax.JSXIdentifier
                 && node.name === 'this');
    });

  if (containsBindingSyntax) {
    utils.append('.bind(this)', state);
  }

  utils.catchupWhiteSpace(node.range[1], state);

  // Close wrapper if not in the expression.
  if (notInExpression) {
    utils.append(')', state);
  }

  return false;
}

function renderParams(traverse, node, path, state) {
  // To preserve inline typechecking directives, we
  // distinguish between parens-free and paranthesized single param.
  if (isParensFreeSingleParam(node, state) || !node.params.length) {
    utils.append('(', state);
  }
  if (node.params.length !== 0) {
    path.unshift(node);
    traverse(node.params, path, state);
    path.unshift();
  }
  utils.append(')', state);
}

function isParensFreeSingleParam(node, state) {
  return node.params.length === 1 &&
    state.g.source[state.g.position] !== '(';
}

function renderExpressionBody(traverse, node, path, state) {
  // Wrap simple expression bodies into a block
  // with explicit return statement.
  utils.append('{', state);

  // Special handling of rest param.
  if (node.rest) {
    utils.append(
      restParamVisitors.renderRestParamSetup(node, state),
      state
    );
  }

  // Special handling of destructured params.
  destructuringVisitors.renderDestructuredComponents(
    node,
    utils.updateState(state, {
      localScope: {
        parentNode: state.parentNode,
        parentScope: state.parentScope,
        identifiers: state.identifiers,
        tempVarIndex: 0
      }
    })
  );

  utils.append('return ', state);
  renderStatementBody(traverse, node, path, state);
  utils.append(';}', state);
}

function renderStatementBody(traverse, node, path, state) {
  traverse(node.body, path, state);
  utils.catchup(node.body.range[1], state);
}

visitArrowFunction.test = function(node, path, state) {
  return node.type === Syntax.ArrowFunctionExpression;
};

exports.visitorList = [
  visitArrowFunction
];

<|endoftext|>


<commit_before>/* Inspired by JUITTER 1.0.0 BY RODRIGO FANTE -- Thanks Mr. Juitter :)*/

function init() {
  this.hooks = [];
}

function runMainLoop(){
  setTimeout('runMainLoop()', 5000);
  $("div.realtimedata").empty();
  $.getJSON("http://localbox.info:8080/ep/tag/?format=json?",
      function(data){
      doSomethingWithJSON(data);
  });
}

function doSomethingWithJSON(json){
  if (json != undefined && json != ""){
    var msgNb = 0;
    var maxNumMessages = 10;
    $("div.realtimedata").append('
')
    /* Mark up each of the matching pads. */
    jQuery.each(json.matchingPads, function(i,item){
            /* Inserting and marking up the items we obtained. */
            if (item.ID != undefined) {

                /* We collect all of the tags into one
                /* string -- better markup could be used
                /* to be consistent with the usual look &
                /* feel. */

                var tagString = "";

                for (j = 0; j < json.matchingPads[i].TAGS.length; j++){

                    tagString = tagString + '  ' +
                        '' + json.matchingPads[i].TAGS[j] + '';
                }

                $("ul.allmatches").append(
                   '
' +
                   '' +
                   item.ID + '' +
                   '  ' + item.lastWriteTime +
                   '
' + tagString);

                jQuery('matchingpad'+msgNb).hide();
                jQuery('matchingpad'+msgNb).show("slow");
                msgNb++; }});

    /* After dealing with the new stuff, remove any old
    /* entries (Do we have to deal with some kind of reset
    /* of msgNb?) Though frankly I find this somewhat
    /* confusing... Should get out a notebook and trace
    /* through the flow, I guess. */

    if(msgNb>=maxNumMessages){
        jQuery(".matchingpad").each(
            function(k,elemLI){
                if(k >= maxNumMessages)
                    jQuery(this).hide("slow");
            });}
}}

realTimeRecentChanges = new init();

// jQuery(document).ready(function () {
//     // do stuff to start things on client side
//     // (set up the start, timer, all that stuff)
//     console.log("Starting up");
//     runMainLoop();
// });
<commit_msg>resolving merge conflict<commit_after>/* Inspired by JUITTER 1.0.0 BY RODRIGO FANTE -- Thanks Mr. Juitter :)*/

function init() {
  this.hooks = [];
}

function runMainLoop(){
  setTimeout('runMainLoop()', 5000);
  $("div.realtimedata").empty();
  $.getJSON("http://localbox.info:8080/ep/tag/?format=json?",
      function(data){
      doSomethingWithJSON(data);
  });
}

function doSomethingWithJSON(json){
  if (json != undefined && json != ""){
    var msgNb = 0;
    var maxNumMessages = 10;
    $("div.realtimedata").append('
')

    /* Mark up each of the matching pads. */
    jQuery.each(json.matchingPads, function(i,item){
            /* Inserting and marking up the items we obtained. */
            if (item.ID != undefined) {

                /* We collect all of the tags into one
                /* string -- better markup could be used
                /* to be consistent with the usual look &
                /* feel. */

                var tagString = "";

                for (j = 0; j < json.matchingPads[i].TAGS.length; j++){

                    tagString = tagString + '  ' +
                        '' + json.matchingPads[i].TAGS[j] + '';
                }

                $("ul.allmatches").append(
                   '
' +
                   '' +
                   item.ID + '' +
                   '  ' + item.lastWriteTime +
                   '
' + tagString);

                jQuery('matchingpad'+msgNb).hide();
                jQuery('matchingpad'+msgNb).show("slow");
                msgNb++; }});

    /* After dealing with the new stuff, remove any old
    /* entries (Do we have to deal with some kind of reset
    /* of msgNb?) Though frankly I find this somewhat
    /* confusing... Should get out a notebook and trace
    /* through the flow, I guess. */

    if(msgNb>=maxNumMessages){
        jQuery(".matchingpad").each(
            function(k,elemLI){
                if(k >= maxNumMessages)
                    jQuery(this).hide("slow");
            });}
}}

realTimeRecentChanges = new init();

/* This gives me problems!  */

$(document).ready(function () {
  // do stuff to start things on client side
  // (set up the start, timer, all that stuff)
  jQuery("#realtimedata").html("");
  runMainLoop();
});
<|endoftext|>


<commit_before>// This is the JavaScript part of Judge, a client-side validation gem for Rails 3.
// You can find a guide and some more traditional API documentation at <http://joecorcoran.github.com/judge/>.
// Hopefully this page will help you understand what's happening under the hood.

/* http://raw.github.com/joecorcoran/judge/master/LICENSE.txt */

/*jshint curly: true, evil: true, newcap: true, noarg: true, strict: false */
/*global _: false, JSON: false */

// The judge namespace.
var judge = judge || {};

// A judge.Watcher is a DOM element wrapper that judge uses to store validation info and instance methods.
judge.Watcher = function (element) {

  // Throw dependency errors.
  if (typeof window._ === 'undefined') {
    throw {
      name: 'ReferenceError',
      message: '[judge][dependency] Underscore.js not found'
    };
  }
  if (_(window.JSON).isUndefined()) {
    throw {
      name: 'ReferenceError',
      message: '[judge][dependency] JSON global object not found'
    };
  }

  // Throw some constructor usage errors.
  if (_(element).isUndefined()) {
    throw {
      name: 'ReferenceError',
      message: '[judge][constructor] No DOM element passed to constructor'
    };
  }

  if (element.getAttribute('data-validate') === null) {
    throw {
      name: 'ReferenceError',
      message: '[judge][constructor] Cannot construct Watcher for this element, use judge form builders'
    };
  }

  // Watcher instance properties.
  this.element         = element;
  this.validators      = JSON.parse(this.element.getAttribute('data-validate'));
};

// The `validate` method returns an object which describes the current validity of the
// value of the watched element.
judge.Watcher.prototype.validate = function() {
  var allMessages = [];
  _(this.validators).each(function(v) {
    if (this.element.value.length || v.options.allow_blank !== true) {
      var messages = this.validates()[v.kind](this.element.value, v.options, v.messages);
      if (messages.length) {
        allMessages.push(messages);
      }
    }
  }, this);
  allMessages = _(allMessages).flatten();
  return {
    valid: (allMessages.length < 1),
    messages: allMessages,
    element: this.element
  };
};

// Ported ActiveModel validators.
// See http://api.rubyonrails.org/classes/ActiveModel/Validations.html for the originals.
judge.eachValidators = (function() {
  return {
    // ActiveModel::Validations::PresenceValidator
    presence: function(value, options, messages) {
      return (value.length) ? [] : [messages.blank];
    },

    // ActiveModel::Validations::LengthValidator
    length: function(value, options, messages) {
      var msgs = [],
          types = {
            minimum: { operator: '<',  message: 'too_short' },
            maximum: { operator: '>',  message: 'too_long' },
            is:      { operator: '!=', message: 'wrong_length' }
          };
      _(types).each(function(properties, type) {
        var invalid = judge.utils.operate(value.length, properties.operator, options[type]);
        if (options.hasOwnProperty(type) && invalid) {
          msgs.push(messages[properties.message]);
        }
      });
      return msgs;
    },

    // ActiveModel::Validations::ExclusionValidator
    exclusion: function(value, options, messages) {
      var stringIn = _(options['in']).map(function(o) { return o.toString(); });
      return (_(stringIn).include(value)) ? [messages.exclusion] : [];
    },

    // ActiveModel::Validations::InclusionValidator
    inclusion: function(value, options, messages) {
      var stringIn = _(options['in']).map(function(o) { return o.toString(); });
      return (!_(stringIn).include(value)) ? [messages.inclusion] : [];
    },

    // ActiveModel::Validations::NumericalityValidator
    numericality: function(value, options, messages) {
      var operators = {
            greater_than: '>',
            greater_than_or_equal_to: '>=',
            equal_to: '==',
            less_than: '<',
            less_than_or_equal_to: '<='
          },
          msgs = [],
          parsedValue = parseFloat(value, 10);

      if (isNaN(Number(value))) {
        msgs.push(messages.not_a_number);
      } else {
        if (options.odd && judge.utils.isEven(parsedValue)) {
          msgs.push(messages.odd);
        }
        if (options.even && judge.utils.isOdd(parsedValue)) {
          msgs.push(messages.even);
        }
        if (options.only_integer && !judge.utils.isInt(parsedValue)) {
          msgs.push(messages.not_an_integer);
        }
        _(operators).each(function(operator, key) {
          var valid = judge.utils.operate(parsedValue, operators[key], parseFloat(options[key], 10));
          if (options.hasOwnProperty(key) && !valid) {
            msgs.push(messages[key]);
          }
        });
      }
      return msgs;
    },

    // ActiveModel::Validations::FormatValidator
    format: function(value, options, messages) {
      var msgs  = [];
      if (options.hasOwnProperty('with')) {
        var withReg = judge.utils.convertRegExp(options['with']);
        if (!withReg.test(value)) {
          msgs.push(messages.invalid);
        }
      }
      if (options.hasOwnProperty('without')) {
        var withoutReg = judge.utils.convertRegExp(options.without);
        if (withoutReg.test(value)) {
          msgs.push(messages.invalid);
        }
      }
      return msgs;
    },

    // ActiveModel::Validations::AcceptanceValidator
    acceptance: function(value, options, messages) {
      return (this._element.checked === true) ? [] : [messages.accepted];
    },

    // ActiveModel::Validations::ConfirmationValidator
    confirmation: function(value, options, messages) {
      var id       = this._element.getAttribute('id'),
          confId   = id + '_confirmation',
          confElem = document.getElementById(confId);
      return (value === confElem.value) ? [] : [messages.confirmation];
    }
  };

}());

// This object should contain any judge validation methods
// that correspond to custom validators used in the model.
judge.customValidators = {};

// Return all validation methods, including those found in judge.customValidators.
// If you name a custom validation method the same as a default one, for example
//   `judge.customValidators.presence = function() {};`
// then the custom method _will overwrite_ the default one, so be careful!
judge.Watcher.prototype.validates = function() {
  return _.extend({_element: this.element}, judge.eachValidators, judge.customValidators);
};

// The judge store is now open :)
judge.store = (function() {
  var store   = {};

  return {

    // Stores watcher(s) for element(s) against a user defined key.
    save: function(key, element) {
      var elements = judge.utils.isCollection(element) ? element : [element];
      _(elements).each(function(element) {
        if (!store.hasOwnProperty(key)) { store[key] = []; }
        var watchedInstance = new judge.Watcher(element),
            currentStored   = _(store[key]).pluck('element');
        if (!_(currentStored).include(element)) {
          store[key].push(watchedInstance);
        }
      });
      return store;
    },

    // Removes stored watcher(s).
    remove: function(key, element) {
      if (!store.hasOwnProperty(key)) {
        return null;
      }
      var elements = judge.utils.isCollection(element) ? element : [element];
      store[key] = _(store[key]).reject(function(j) { return _(elements).include(j.element); });
      if (store[key].length === 0) {
        delete store[key];
      }
      return store[key];
    },

    // Returns the entire store object, or an array of watchers stored against
    // the given key.
    get: function(key) {
      if (_(key).isUndefined()) { return store; }
      return store.hasOwnProperty(key) ? store[key] : null;
    },

    // Returns the entire store object with watchers converted to elements,
    // or all DOM elements stored within all watchers stored against the given key.
    getDOM: function(key) {
      if (_(key).isUndefined()) {
        var convertedStore = {};
        _(store).each(function(array, key) {
          convertedStore[key] = _(array).pluck('element');
        });
        return convertedStore;
      }
      return store.hasOwnProperty(key) ? _(store[key]).pluck('element') : null;
    },

    // Shortcut for `judge.validate(judge.store.getDOM(key));`.
    // Returns null if no stored elements are found for the given key.
    validate: function(key) {
      var elements = judge.store.getDOM(key);
      return (key && !_(elements).isNull()) ? judge.validate(elements) : null;
    },

    // Wipes the entire store object, or wipes all watchers stored against
    // the given key.
    clear: function(key) {
      if (_(key).isUndefined()) {
        store = {};
      } else {
        if (!store.hasOwnProperty(key)) { return null; }
        delete store[key];
      }
      return store;
    }

  };
}());

// Validate all given element(s) without storing. Watcher creation is handled for you,
// but the created Watchers will not be returned, so it's less flexible.
judge.validate = function(elements) {
  var results = [];
  elements = judge.utils.isCollection(elements) ? elements : [elements];
  _(elements).each(function(element) {
    var j = new judge.Watcher(element);
    results.push(j.validate());
  });
  return results;
};

// The obligatory utilities.
judge.utils = {

  // A way of checking isArray, but including weird object types that are returned from collection queries.
  isCollection: function(object) {
    var type  = judge.utils.getObjectString(object),
        types = [
          'Array',
          'NodeList',
          'StaticNodeList',
          'HTMLCollection',
          'HTMLFormElement',
          'HTMLAllCollection'
        ];
    return _(types).include(type);
  },

  // Returns the object type as represented in `Object.prototype.toString`.
  getObjectString: function(object) {
    var string = Object.prototype.toString.call(object);
    return string.replace(/\[|\]/g, '').split(' ')[1];
  },

  // OMG! How can you use eval, you monster! It's totally evil!
  // (It's used here for stuff like `(3, '<', 4) => '3 < 4' => true`.)
  operate: function(input, operator, validInput) {
    return eval(input+' '+operator+' '+validInput);
  },

  // Some nifty numerical helpers.
  isInt: function(value) { return value === +value && value === (value|0); },
  isFloat: function(value) { return value === +value && value !== (value|0); },
  isEven: function(value) { return (value % 2 === 0) ? true : false; },
  isOdd: function(value) { return !judge.utils.isEven(value); },

  // Converts a Ruby regular expression, given as a string, into JavaScript.
  // This is rudimentary at best, as there are many, many differences between Ruby
  // and JavaScript when it comes to regexp-fu. Basically, if you validate your records
  // using complex regular expressions AND you hope that this will "just work"
  // on the client-side too… you are going to be pretty disappointed.
  // If you know of a better way (or an existing library) to convert regular expressions
  // from Ruby to JavaScript, I would really love to hear from you.
  convertRegExp: function(string) {
    var p = string.slice(1, -1).split(':'),
        o = p.shift(),
        r = p.join(':').replace(/\\\\/g, '\\');
    return new RegExp(r, judge.utils.convertFlags(o));
  },
  convertFlags: function(string) {
    var off    = new RegExp('-'),
        multi  = new RegExp('m');
    string = string.replace('?', '');
    if (off.test(string) || !multi.test(string)) {
      return '';
    } else {
      return 'm';
    }
  }
};
<commit_msg>add version and better comments to js<commit_after>// Judge 1.3.1
// (c) 2011–2012 Joe Corcoran
// http://raw.github.com/joecorcoran/judge/master/LICENSE.txt
// This is the JavaScript part of Judge, a client-side validation gem for Rails 3.
// You can find a guide and some more traditional API documentation at <http://joecorcoran.github.com/judge/>.
// Hopefully the comments here will help you understand what's happening under the hood.

/*jshint curly: true, evil: true, newcap: true, noarg: true, strict: false */
/*global _: false, JSON: false */

// The judge namespace.
var judge = {};

judge.VERSION = '1.2.0';

// A judge.Watcher is a DOM element wrapper that judge uses to store validation info and instance methods.
judge.Watcher = function (element) {

  // Throw dependency errors.
  if (typeof window._ === 'undefined') {
    throw {
      name: 'ReferenceError',
      message: '[judge][dependency] Underscore.js not found'
    };
  }
  if (_(window.JSON).isUndefined()) {
    throw {
      name: 'ReferenceError',
      message: '[judge][dependency] JSON global object not found'
    };
  }

  // Throw some constructor usage errors.
  if (_(element).isUndefined()) {
    throw {
      name: 'ReferenceError',
      message: '[judge][constructor] No DOM element passed to constructor'
    };
  }
  if (element.getAttribute('data-validate') === null) {
    throw {
      name: 'ReferenceError',
      message: '[judge][constructor] Cannot construct Watcher for this element, use judge form builders'
    };
  }

  // Watcher instance properties.
  this.element         = element;
  this.validators      = JSON.parse(this.element.getAttribute('data-validate'));
};

// The `validate` method returns an object which describes the current validity of the
// value of the watched element.
judge.Watcher.prototype.validate = function() {
  var allMessages = [];
  _(this.validators).each(function(v) {
    if (this.element.value.length || v.options.allow_blank !== true) {
      var messages = this.validates()[v.kind](this.element.value, v.options, v.messages);
      if (messages.length) {
        allMessages.push(messages);
      }
    }
  }, this);
  allMessages = _(allMessages).flatten();
  return {
    valid: (allMessages.length < 1),
    messages: allMessages,
    element: this.element
  };
};

// Ported ActiveModel validators.
// See <http://api.rubyonrails.org/classes/ActiveModel/Validations.html> for the originals.
judge.eachValidators = (function() {
  return {
    // ActiveModel::Validations::PresenceValidator
    presence: function(value, options, messages) {
      return (value.length) ? [] : [messages.blank];
    },

    // ActiveModel::Validations::LengthValidator
    length: function(value, options, messages) {
      var msgs = [],
          types = {
            minimum: { operator: '<',  message: 'too_short' },
            maximum: { operator: '>',  message: 'too_long' },
            is:      { operator: '!=', message: 'wrong_length' }
          };
      _(types).each(function(properties, type) {
        var invalid = judge.utils.operate(value.length, properties.operator, options[type]);
        if (options.hasOwnProperty(type) && invalid) {
          msgs.push(messages[properties.message]);
        }
      });
      return msgs;
    },

    // ActiveModel::Validations::ExclusionValidator
    exclusion: function(value, options, messages) {
      var stringIn = _(options['in']).map(function(o) { return o.toString(); });
      return (_(stringIn).include(value)) ? [messages.exclusion] : [];
    },

    // ActiveModel::Validations::InclusionValidator
    inclusion: function(value, options, messages) {
      var stringIn = _(options['in']).map(function(o) { return o.toString(); });
      return (!_(stringIn).include(value)) ? [messages.inclusion] : [];
    },

    // ActiveModel::Validations::NumericalityValidator
    numericality: function(value, options, messages) {
      var operators = {
            greater_than: '>',
            greater_than_or_equal_to: '>=',
            equal_to: '==',
            less_than: '<',
            less_than_or_equal_to: '<='
          },
          msgs = [],
          parsedValue = parseFloat(value, 10);

      if (isNaN(Number(value))) {
        msgs.push(messages.not_a_number);
      } else {
        if (options.odd && judge.utils.isEven(parsedValue)) {
          msgs.push(messages.odd);
        }
        if (options.even && judge.utils.isOdd(parsedValue)) {
          msgs.push(messages.even);
        }
        if (options.only_integer && !judge.utils.isInt(parsedValue)) {
          msgs.push(messages.not_an_integer);
        }
        _(operators).each(function(operator, key) {
          var valid = judge.utils.operate(parsedValue, operators[key], parseFloat(options[key], 10));
          if (options.hasOwnProperty(key) && !valid) {
            msgs.push(messages[key]);
          }
        });
      }
      return msgs;
    },

    // ActiveModel::Validations::FormatValidator
    format: function(value, options, messages) {
      var msgs  = [];
      if (options.hasOwnProperty('with')) {
        var withReg = judge.utils.convertRegExp(options['with']);
        if (!withReg.test(value)) {
          msgs.push(messages.invalid);
        }
      }
      if (options.hasOwnProperty('without')) {
        var withoutReg = judge.utils.convertRegExp(options.without);
        if (withoutReg.test(value)) {
          msgs.push(messages.invalid);
        }
      }
      return msgs;
    },

    // ActiveModel::Validations::AcceptanceValidator
    acceptance: function(value, options, messages) {
      return (this._element.checked === true) ? [] : [messages.accepted];
    },

    // ActiveModel::Validations::ConfirmationValidator
    confirmation: function(value, options, messages) {
      var id       = this._element.getAttribute('id'),
          confId   = id + '_confirmation',
          confElem = document.getElementById(confId);
      return (value === confElem.value) ? [] : [messages.confirmation];
    }
  };

}());

// This object should contain any judge validation methods
// that correspond to custom validators used in the model.
judge.customValidators = {};

// Return all validation methods, including those found in judge.customValidators.
// If you name a custom validation method the same as a default one, for example
//   `judge.customValidators.presence = function() {};`
// then the custom method _will overwrite_ the default one, so be careful!
judge.Watcher.prototype.validates = function() {
  return _.extend({_element: this.element}, judge.eachValidators, judge.customValidators);
};

// The judge store is now open :)
judge.store = (function() {
  var store   = {};

  return {

    // Stores watcher(s) for element(s) against a user defined key.
    save: function(key, element) {
      var elements = judge.utils.isCollection(element) ? element : [element];
      _(elements).each(function(element) {
        if (!store.hasOwnProperty(key)) { store[key] = []; }
        var watchedInstance = new judge.Watcher(element),
            currentStored   = _(store[key]).pluck('element');
        if (!_(currentStored).include(element)) {
          store[key].push(watchedInstance);
        }
      });
      return store;
    },

    // Removes stored watcher(s).
    remove: function(key, element) {
      if (!store.hasOwnProperty(key)) {
        return null;
      }
      var elements = judge.utils.isCollection(element) ? element : [element];
      store[key] = _(store[key]).reject(function(j) { return _(elements).include(j.element); });
      if (store[key].length === 0) {
        delete store[key];
      }
      return store[key];
    },

    // Returns the entire store object, or an array of watchers stored against
    // the given key.
    get: function(key) {
      if (_(key).isUndefined()) { return store; }
      return store.hasOwnProperty(key) ? store[key] : null;
    },

    // Returns the entire store object with watchers converted to elements,
    // or all DOM elements stored within all watchers stored against the given key.
    getDOM: function(key) {
      if (_(key).isUndefined()) {
        var convertedStore = {};
        _(store).each(function(array, key) {
          convertedStore[key] = _(array).pluck('element');
        });
        return convertedStore;
      }
      return store.hasOwnProperty(key) ? _(store[key]).pluck('element') : null;
    },

    // Shortcut for `judge.validate(judge.store.getDOM(key));`.
    // Returns null if no stored elements are found for the given key.
    validate: function(key) {
      var elements = judge.store.getDOM(key);
      return (key && !_(elements).isNull()) ? judge.validate(elements) : null;
    },

    // Wipes the entire store object, or wipes all watchers stored against
    // the given key.
    clear: function(key) {
      if (_(key).isUndefined()) {
        store = {};
      } else {
        if (!store.hasOwnProperty(key)) { return null; }
        delete store[key];
      }
      return store;
    }

  };
}());

// Validate all given element(s) without storing. Watcher creation is handled for you,
// but the created Watchers will not be returned, so it's less flexible.
judge.validate = function(elements) {
  var results = [];
  elements = judge.utils.isCollection(elements) ? elements : [elements];
  _(elements).each(function(element) {
    var j = new judge.Watcher(element);
    results.push(j.validate());
  });
  return results;
};

// The obligatory utilities.
judge.utils = {

  // A way of checking isArray, but including weird object types that are returned from collection queries.
  isCollection: function(object) {
    var type  = judge.utils.getObjectString(object),
        types = [
          'Array',
          'NodeList',
          'StaticNodeList',
          'HTMLCollection',
          'HTMLFormElement',
          'HTMLAllCollection'
        ];
    return _(types).include(type);
  },

  // Returns the object type as represented in `Object.prototype.toString`.
  getObjectString: function(object) {
    var string = Object.prototype.toString.call(object);
    return string.replace(/\[|\]/g, '').split(' ')[1];
  },

  // OMG! How can you use eval, you monster! It's totally evil!
  // (It's used here for stuff like `(3, '<', 4) => '3 < 4' => true`.)
  operate: function(input, operator, validInput) {
    return eval(input+' '+operator+' '+validInput);
  },

  // Some nifty numerical helpers.
  isInt: function(value) { return value === +value && value === (value|0); },
  isFloat: function(value) { return value === +value && value !== (value|0); },
  isEven: function(value) { return (value % 2 === 0) ? true : false; },
  isOdd: function(value) { return !judge.utils.isEven(value); },

  // Converts a Ruby regular expression, given as a string, into JavaScript.
  // This is rudimentary at best, as there are many, many differences between Ruby
  // and JavaScript when it comes to regexp-fu. Basically, if you validate your records
  // using complex regular expressions AND you hope that this will "just work"
  // on the client-side too… you are going to be pretty disappointed.
  // If you know of a better way (or an existing library) to convert regular expressions
  // from Ruby to JavaScript, I would really love to hear from you.
  convertRegExp: function(string) {
    var p = string.slice(1, -1).split(':'),
        o = p.shift(),
        r = p.join(':').replace(/\\\\/g, '\\');
    return new RegExp(r, judge.utils.convertFlags(o));
  },
  convertFlags: function(string) {
    var off    = new RegExp('-'),
        multi  = new RegExp('m');
    string = string.replace('?', '');
    if (off.test(string) || !multi.test(string)) {
      return '';
    } else {
      return 'm';
    }
  }
};<|endoftext|>


<commit_before>'use strict';

const chalk = require('chalk'),
	os = require('os'),
	merge = require('merge'),
	Spinner = require('cli-spinner').Spinner,
	msRestAzure = require('ms-rest-azure'),
	ResourceManagerClient = require('azure-arm-resource').ResourceManagementClient,
	BbPromise = require('bluebird'),
	fs = BbPromise.promisifyAll(require("fs-extra")),
	FError = require('./../Error'),
	env = require('./../utils/env');

class ProjectProvisionHandler {
	constructor() {
		this.context = 'project';
		this.action = 'provision';
	}

	process(options) {
		return BbPromise.try(() => {
			if (!this._checkOptions(options)) {
				throw new FError('resource group and/or location has not been specified.');
			}

			let resourceGroup = options.g || options.resourceGroup;
			let location = options.l || options.location;
			let deploymentName = options.d || options.deploymentName || 'FAZDeployment-' + Date.now();
			let armTemplate = require(path.join(env.rootFolder, 'f-resources-arm.json'));
			var deploymentParameters = {
				properties: {
					mode: 'Incremental',
					template: armTemplate
				},
				parameters: {
					siteLocation: {
						value: location
					},
					fazProjectName: {
						value: env.projectName
					}
				}
			}

			const avp = env.getAdminVars();
			let spinner = null;
			return avp.then((creds) => {
				let credentials = new msRestAzure.ApplicationTokenCredentials(creds.app_id, creds.tenant_id, creds.password);
				let client = new ResourceManagementClient(credentials, creds.subscription_id);
				client.deployments = BbPromise.promisifyAll(client.deployments);
				client.resourceGroups = BbPromise.promisifyAll(client.resourceGroups);

				console.log(chalk.cyan('Starting provisioning your project...' + os.EOL));
				spinner = new Spinner(chalk.cyan('Updating ARM template... %s'));
				spinner.setSpinnerString('|/-\\');
				spinner.start();
				this._updateARMTemplate()
					.then(() => {
						// Validation
						spinner.stop();
						console.log(chalk.green('DONE!'));
						spinner = new Spinner(chalk.cyan('Validating ARM template... %s'));
						spinner.setSpinnerString('|/-\\');
						spinner.start();
						client.deployments.validateAsync(resourceGroup, deploymentName, deploymentParameters)
					})
					.then(() => {
						// Create/Update Resource Group
						spinner.stop();
						console.log(chalk.green('DONE!'));
						spinner = new Spinner(chalk.cyan('Creating/Updating Resource Group... %s'));
						spinner.setSpinnerString('|/-\\');
						spinner.start();
						client.resourceGroups.createOrUpdateAsync(resourceGroup, { location: location });
					})
					.then(() => {
						// Provision template
						spinner.stop();
						console.log(chalk.green('DONE!'));
						spinner = new Spinner(chalk.cyan('Executing ARM template... %s'));
						spinner.setSpinnerString('|/-\\');
						spinner.start();
						return client.deployments.createOrUpdateAsync(resourceGroup, deploymentName, deploymentParameters);
					})
					.then((res) => {
						spinner.stop();
						console.log(chalk.green('DONE!'));
						console.log(chalk.cyan('******************************************************************'))
						console.log(chalk.cyan('All good. Your project has been successfully provisioned to Azure.'))
						// TODO: Write res to faz.log
					})
			})
				.catch((e) => {
					spinner.stop();
					console.log(chalk.red('FAILED!'));
					console.log(chalk.red('Ups. Something went wrong. See error message for details.'));
					throw new FError('Unable to provision project: ' + e.message);
				});
		});
	}

	_checkOptions(options) {
		return (options.g === undefined && options.resourceGroup === undefined) ||
		       (options.l === undefined && options.location === undefined);
	}

	/**
	 * Merges cloud.env variables with template
	 */
	_updateARMTemplate() {
		return BbPromise.all([
			env.getCloudVars(),
			fs.readFileAsync(path.join(env.rootFolder, 'f-resources-arm.json'))
		])
			.then((params) => {
				let cloudVars = params[0];
				let template = params[1];
				template.resources.forEach((r) => {
					if (r.type === 'Microsoft.Web/sites') {
						merge.recursive(r.resources.properties, enVars);
					}
					return template;
				})
			})
			.then((template) => {
				fs.writeFileAsync(path.join(env.rootFolder, 'f-resources-arm.json'), template);
			})
			.catch((e) => {
				throw new FError('Unable to update ARM template with environment variables. Aborting: ' + JSON.stringify(e));;
			});
	}

	help() {
		let help = 'Provisions or updates the current state of your project to your Azure subscription.' + os.EOL + os.EOL;
		help += '-g, --resourceGroup' + os.EOL;
		help += '      The resource group where this project is to be provisioned or updated.' + os.EOL;
		help += '-l, --location' + os.EOL;
		help += '      The location where this project is to be provisioned or updated.' + os.EOL;
		help += '-d, --deploymentName' + os.EOL;
		help += '      Optional - Set a dedicated deployment name. Default: <projectname>' + os.EOL;

		console.log(chalk.cyan(help));
	}
}

module.exports = ProjectProvisionHandler;<commit_msg>fixes<commit_after>'use strict';

const chalk = require('chalk'),
	os = require('os'),
	merge = require('merge'),
	Spinner = require('cli-spinner').Spinner,
	msRestAzure = require('ms-rest-azure'),
	ResourceManagementClient = require('azure-arm-resource').ResourceManagementClient,
	BbPromise = require('bluebird'),
	path = require('path'),
	fs = BbPromise.promisifyAll(require("fs-extra")),
	FError = require('./../Error'),
	env = require('./../utils/env');

class ProjectProvisionHandler {
	constructor() {
		this.context = 'project';
		this.action = 'provision';
	}

	process(options) {
		return BbPromise.try(() => {
			if (!this._checkOptions(options)) {
				throw new FError('resource group and/or location has not been specified.');
			}

			let resourceGroup = options.g || options.resourceGroup;
			let location = options.l || options.location;
			let deploymentName = options.d || options.deploymentName || 'FAZDeployment-' + Date.now();
			let deploymentParameters = {
				properties: {
					mode: 'Incremental'
				},
				parameters: {
					siteLocation: {
						value: location
					},
					fazProjectName: {
						value: env.projectName
					}
				}
			}

			console.log(chalk.cyan('Starting provisioning your project...' + os.EOL));
			let spinner = new Spinner(chalk.cyan('Updating ARM template... %s'));
			spinner.setSpinnerString('|/-\\');
			spinner.start();

			const avp = env.getAdminVars();
			return avp.then((creds) => {
				let credentials = new msRestAzure.ApplicationTokenCredentials(creds.app_id, creds.tenant_id, creds.password);
				let client = new ResourceManagementClient(credentials, creds.subscription_id);
				client.deployments = BbPromise.promisifyAll(client.deployments);
				client.resourceGroups = BbPromise.promisifyAll(client.resourceGroups);
				this._updateARMTemplate()
					.then(() => {
						let armTemplate = require(path.join(env.projectFolder, 'f-resources-arm.json'));
						deploymentParameters.properties.template = armTemplate;
						// Validation
						spinner.stop();
						console.log(chalk.green('DONE!'));
						spinner = new Spinner(chalk.cyan('Validating ARM template... %s'));
						spinner.setSpinnerString('|/-\\');
						spinner.start();
						return client.deployments.validateAsync(resourceGroup, deploymentName, deploymentParameters)
					})
					.then(() => {
						// Create/Update Resource Group
						spinner.stop();
						console.log(chalk.green('DONE!'));
						spinner = new Spinner(chalk.cyan('Creating/Updating Resource Group... %s'));
						spinner.setSpinnerString('|/-\\');
						spinner.start();
						return client.resourceGroups.createOrUpdateAsync(resourceGroup, { location: location });
					})
					.then(() => {
						// Provision template
						spinner.stop();
						console.log(chalk.green('DONE!'));
						spinner = new Spinner(chalk.cyan('Executing ARM template... %s'));
						spinner.setSpinnerString('|/-\\');
						spinner.start();
						return client.deployments.createOrUpdateAsync(resourceGroup, deploymentName, deploymentParameters);
					})
					.then((res) => {
						spinner.stop();
						console.log(chalk.green('DONE!'));
						console.log(chalk.cyan('******************************************************************'))
						console.log(chalk.cyan('All good. Your project has been successfully provisioned to Azure.'))
						// TODO: Write res to faz.log
					})
			})
				.catch((e) => {
					spinner.stop();
					console.log(chalk.red('FAILED!'));
					console.log(chalk.red('Ups. Something went wrong. See error message for details.'));
					throw new FError('Unable to provision project: ' + e.message);
				});
		});
	}

	_checkOptions(options) {
		return !((options.g === undefined && options.resourceGroup === undefined) ||
			(options.l === undefined && options.location === undefined));
	}

	/**
	 * Merges cloud.env variables with template
	 */
	_updateARMTemplate() {
		return env.getCloudVars()
			.then((cloudVars) => {
				let template = require(path.join(env.projectFolder, 'f-resources-arm.json'));
				template.resources.forEach((r) => {
					if (r.type === 'Microsoft.Web/sites') {
						merge.recursive(r.resources[0].properties, cloudVars);
					}
				});
				fs.writeFileAsync(path.join(env.projectFolder, 'f-resources-arm.json'), JSON.stringify(template, null, 2));
			})
			.catch((e) => {
				throw new FError('Unable to update ARM template with environment variables. Aborting: ' + JSON.stringify(e));;
			});
	}

	help() {
		let help = 'Provisions or updates the current state of your project to your Azure subscription.' + os.EOL + os.EOL;
		help += '-g, --resourceGroup' + os.EOL;
		help += '      The resource group where this project is to be provisioned or updated.' + os.EOL;
		help += '-l, --location' + os.EOL;
		help += '      The location where this project is to be provisioned or updated.' + os.EOL;
		help += '-d, --deploymentName' + os.EOL;
		help += '      Optional - Set a dedicated deployment name. Default: <projectname>' + os.EOL;

		console.log(chalk.cyan(help));
	}
}

module.exports = ProjectProvisionHandler;<|endoftext|>


<commit_before>// this file is automatically generated using scripts/updatePackageVersions.js
export default {
  "core": "1.0.0-beta.36",
  "route-active": "1.0.0-beta.6",
  "route-ancestors": "1.0.0-beta.6",
  "route-prefetch": "1.0.0-beta.10",
  "react": "1.0.0-beta.28",
  "react-native": "1.0.0-beta.8",
  "router": "1.0.0-beta.36",
  "side-effect-aria-live": "1.0.0-beta.0",
  "side-effect-scroll": "1.0.0-beta.9",
  "side-effect-title": "1.0.0-beta.10",
  "svelte": "1.0.0-beta.6",
  "vue": "1.0.0-beta.20"
};
<commit_msg>[docs] pkg versions<commit_after>// this file is automatically generated using scripts/updatePackageVersions.js
export default {
  "core": "1.0.0-beta.37",
  "route-active": "1.0.0-beta.7",
  "route-ancestors": "1.0.0-beta.6",
  "route-prefetch": "1.0.0-beta.11",
  "react": "1.0.0-beta.29",
  "react-native": "1.0.0-beta.8",
  "router": "1.0.0-beta.37",
  "side-effect-aria-live": "1.0.0-beta.0",
  "side-effect-scroll": "1.0.0-beta.9",
  "side-effect-title": "1.0.0-beta.10",
  "svelte": "1.0.0-beta.6",
  "vue": "1.0.0-beta.20"
};
<|endoftext|>


<commit_before>/* dataservice: data access and model management layer
 * relies on Angular injector to provide:
 *     $timeout - Angular equivalent of 'setTimeout'
 *     breeze - the Breeze.Angular service (which is breeze itself)
 *     logger - the application's logging facility
 */
(function() {

    angular.module('app').factory('dataservice',
    ['$http', '$q', '$timeout', 'breeze', 'logger', dataservice]);

    var jsonResultsAdapter = new breeze.JsonResultsAdapter({

        name: "nodeWebApi_default",

        visitNode: function (node, mappingContext, nodeContext) {
            if (node == null) return {};
            var entityTypeName = node.$type;
            var entityType = entityTypeName && mappingContext.entityManager.metadataStore._getEntityType(entityTypeName, true);
            var propertyName = nodeContext.propertyName;
            var ignore = propertyName && propertyName.substr(0, 1) === "$";

            return {
                entityType: entityType,
                nodeId: node.$id,
                nodeRefId: node.$ref,
                ignore: ignore
            };
        }

    });

    function dataservice($http, $q, $timeout, breeze, logger) {

        //TODO: document this difference
        breeze.core.config.initializeAdapterInstance("uriBuilder", "json");

        var serviceName = 'breeze/todos';
        var dataService = new breeze.DataService({ serviceName: serviceName, jsonResultsAdapter: jsonResultsAdapter });
        var manager = new breeze.EntityManager( { dataService: dataService });
        // old code
        // var manager = new breeze.EntityManager( serviceName );
        manager.enableSaveQueuing(true);

        var service = {
            addPropertyChangeHandler: addPropertyChangeHandler,
            createTodo: createTodo,
            deleteTodoAndSave: deleteTodoAndSave,
            getTodos: getTodos,
            hasChanges: hasChanges,
            purge: purge,
            reset: reset,
            removePropertyChangeHandler: removePropertyChangeHandler,
            saveChanges: saveChanges
        };
        return service;

        /*** implementation details ***/

        function addPropertyChangeHandler(handler) {
            // Actually adds any 'entityChanged' event handler
            // call handler when an entity property of any entity changes
            return manager.entityChanged.subscribe(function(changeArgs) {
                var action = changeArgs.entityAction;
                if (action === breeze.EntityAction.PropertyChange) {
                    handler(changeArgs);
                }
            });
        }

        function createTodo(initialValues) {
            return manager.createEntity('TodoItem', initialValues);
        }

        function deleteTodoAndSave(todoItem) {
            if (todoItem) {
                var aspect = todoItem.entityAspect;
                if (aspect.isBeingSaved && aspect.entityState.isAdded()) {
                    // wait to delete added entity while it is being saved
                    setTimeout(function () { deleteTodoAndSave (todoItem); }, 100);
                    return;
                }
                aspect.setDeleted();
                saveChanges();
            }
        }

        function getTodos(includeArchived) {
            var query = breeze.EntityQuery
                .from("Todos")
                .orderBy("CreatedAt");

            if (!includeArchived) { // if excluding archived Todos ...
                // add filter clause limiting results to non-archived Todos
                query = query.where("IsArchived", "==", false);
            }

            var promise = manager.executeQuery(query).catch(queryFailed);
            return promise;

            function queryFailed(error) {
                logger.error(error.message, "Query failed");
                return $q.reject(error); // so downstream promise users know it failed
            }
        }

        function hasChanges() {
            return manager.hasChanges();
        }

        function handleSaveValidationError(error) {
            var message = "Not saved due to validation error";
            try { // fish out the first error
                var firstErr = error.entityErrors[0];
                message += ": " + firstErr.errorMessage;
            } catch (e) { /* eat it for now */ }
            return message;
        }

        function removePropertyChangeHandler(handler) {
            // Actually removes any 'entityChanged' event handler
            return manager.entityChanged.unsubscribe(handler);
        }

        function saveChanges() {
            return manager.saveChanges()
                .then(saveSucceeded)
                .catch(saveFailed);

            function saveSucceeded(saveResult) {
                logger.success("# of Todos saved = " + saveResult.entities.length);
                logger.log(saveResult);
            }

            function saveFailed(error) {
                var reason = error.message;
                var detail = error.detail;

                if (error.entityErrors) {
                    reason = handleSaveValidationError(error);
                } else if (detail && detail.ExceptionType &&
                    detail.ExceptionType.indexOf('OptimisticConcurrencyException') !== -1) {
                    // Concurrency error
                    reason =
                        "Another user, perhaps the server, " +
                        "may have deleted one or all of the todos." +
                        " You may have to restart the app.";
                } else {
                    reason = "Failed to save changes: " + reason +
                        " You may have to restart the app.";
                }

                logger.error(error, reason);
                // DEMO ONLY: discard all pending changes
                // Let them see the error for a second before rejecting changes
                $timeout(function() {
                    manager.rejectChanges();
                }, 1000);
                return $q.reject(error); // so downstream promise users know it failed
            }

        }

        //#region demo operations
        function purge(callback) {
            return $http.post(serviceName + '/purge')
            .then(function () {
                logger.success("database purged.");
                manager.clear();
                if (callback) callback();
            })
            .catch(function (error) {
                logger.error("database purge failed: " + error);
                return $q.reject(error); // so downstream promise users know it failed
            });
        }

        function reset(callback) {
            return $http.post(serviceName + '/reset')
            .then(function () {
                logger.success("database reset.");
                manager.clear();
                if (callback) callback();
            })
            .catch(function (error) {
                logger.error("database reset failed: " + error);
                return $q.reject(error); // so downstream promise users know it failed
            });
        }
        //#endregion
    }
})();<commit_msg>remove workaround for entityType mapping bug. fixed with breeze.js 1.5.4<commit_after>/* dataservice: data access and model management layer
 * relies on Angular injector to provide:
 *     $timeout - Angular equivalent of 'setTimeout'
 *     breeze - the Breeze.Angular service (which is breeze itself)
 *     logger - the application's logging facility
 */
(function() {

    angular.module('app').factory('dataservice',
    ['$http', '$q', '$timeout', 'breeze', 'logger', dataservice]);

    function dataservice($http, $q, $timeout, breeze, logger) {

        //TODO: document this difference
        breeze.core.config.initializeAdapterInstance("uriBuilder", "json");

        var serviceName = 'breeze/todos';
        var manager = new breeze.EntityManager( serviceName );
        manager.enableSaveQueuing(true);

        var service = {
            addPropertyChangeHandler: addPropertyChangeHandler,
            createTodo: createTodo,
            deleteTodoAndSave: deleteTodoAndSave,
            getTodos: getTodos,
            hasChanges: hasChanges,
            purge: purge,
            reset: reset,
            removePropertyChangeHandler: removePropertyChangeHandler,
            saveChanges: saveChanges
        };
        return service;

        /*** implementation details ***/

        function addPropertyChangeHandler(handler) {
            // Actually adds any 'entityChanged' event handler
            // call handler when an entity property of any entity changes
            return manager.entityChanged.subscribe(function(changeArgs) {
                var action = changeArgs.entityAction;
                if (action === breeze.EntityAction.PropertyChange) {
                    handler(changeArgs);
                }
            });
        }

        function createTodo(initialValues) {
            return manager.createEntity('TodoItem', initialValues);
        }

        function deleteTodoAndSave(todoItem) {
            if (todoItem) {
                var aspect = todoItem.entityAspect;
                if (aspect.isBeingSaved && aspect.entityState.isAdded()) {
                    // wait to delete added entity while it is being saved
                    setTimeout(function () { deleteTodoAndSave (todoItem); }, 100);
                    return;
                }
                aspect.setDeleted();
                saveChanges();
            }
        }

        function getTodos(includeArchived) {
            var query = breeze.EntityQuery
                .from("Todos")
                .orderBy("CreatedAt");

            if (!includeArchived) { // if excluding archived Todos ...
                // add filter clause limiting results to non-archived Todos
                query = query.where("IsArchived", "==", false);
            }

            var promise = manager.executeQuery(query).catch(queryFailed);
            return promise;

            function queryFailed(error) {
                logger.error(error.message, "Query failed");
                return $q.reject(error); // so downstream promise users know it failed
            }
        }

        function hasChanges() {
            return manager.hasChanges();
        }

        function handleSaveValidationError(error) {
            var message = "Not saved due to validation error";
            try { // fish out the first error
                var firstErr = error.entityErrors[0];
                message += ": " + firstErr.errorMessage;
            } catch (e) { /* eat it for now */ }
            return message;
        }

        function removePropertyChangeHandler(handler) {
            // Actually removes any 'entityChanged' event handler
            return manager.entityChanged.unsubscribe(handler);
        }

        function saveChanges() {
            return manager.saveChanges()
                .then(saveSucceeded)
                .catch(saveFailed);

            function saveSucceeded(saveResult) {
                logger.success("# of Todos saved = " + saveResult.entities.length);
                logger.log(saveResult);
            }

            function saveFailed(error) {
                var reason = error.message;
                var detail = error.detail;

                if (error.entityErrors) {
                    reason = handleSaveValidationError(error);
                } else if (detail && detail.ExceptionType &&
                    detail.ExceptionType.indexOf('OptimisticConcurrencyException') !== -1) {
                    // Concurrency error
                    reason =
                        "Another user, perhaps the server, " +
                        "may have deleted one or all of the todos." +
                        " You may have to restart the app.";
                } else {
                    reason = "Failed to save changes: " + reason +
                        " You may have to restart the app.";
                }

                logger.error(error, reason);
                // DEMO ONLY: discard all pending changes
                // Let them see the error for a second before rejecting changes
                $timeout(function() {
                    manager.rejectChanges();
                }, 1000);
                return $q.reject(error); // so downstream promise users know it failed
            }

        }

        //#region demo operations
        function purge(callback) {
            return $http.post(serviceName + '/purge')
            .then(function () {
                logger.success("database purged.");
                manager.clear();
                if (callback) callback();
            })
            .catch(function (error) {
                logger.error("database purge failed: " + error);
                return $q.reject(error); // so downstream promise users know it failed
            });
        }

        function reset(callback) {
            return $http.post(serviceName + '/reset')
            .then(function () {
                logger.success("database reset.");
                manager.clear();
                if (callback) callback();
            })
            .catch(function (error) {
                logger.error("database reset failed: " + error);
                return $q.reject(error); // so downstream promise users know it failed
            });
        }
        //#endregion
    }
})();<|endoftext|>