<commit_before>/**
*
*	Schedule Dialog creation methods.
*
*/

//dateText mm-dd-yy
function startDpSelect(dateText, inst) {
	var time, date;

	time = dateText.split("-");
	date = new Date(time[0], time[1] - 1, time[2]);

	$("#add_show_end_date").datepicker("option", "minDate", date);
    $('input[name^="add_show_rebroadcast_absolute_date"]').datepicker("option", "minDate", date);
}

function endDpSelect(dateText, inst) {
	var time, date;

	time = dateText.split("-");
	date = new Date(time[0], time[1] - 1, time[2]);

	$("#add_show_start_date").datepicker( "option", "maxDate", date);
}

function createDateInput(el, onSelect) {
	var date;

	el.datepicker({
			minDate: new Date(),
			onSelect: onSelect,
			dateFormat: 'yy-mm-dd'
		});
}

function autoSelect(event, ui) {

	$("#add_show_hosts-"+ui.item.value).attr("checked", "checked");
	event.preventDefault();
}

function findHosts(request, callback) {
	var search, url;

	url = "/User/get-hosts";
	search = request.term;

	$.post(url,
		{format: "json", term: search},

		function(json) {
			callback(json.hosts);
		});

}

function setAddShowEvents() {

    var form = $("#add-show-form");

	form.find("h3").click(function(){
        $(this).next().toggle();
    });

    if(!form.find("#add_show_repeats").attr('checked')) {
        form.find("#schedule-show-when > fieldset:last").hide();
        $("#add_show_rebroadcast_relative").hide();
    }
    else {
        $("#add_show_rebroadcast_absolute").hide();
    }

    if(!form.find("#add_show_record").attr('checked')) {
        form.find("#add_show_rebroadcast").hide();
    }

    if(!form.find("#add_show_rebroadcast").attr('checked')) {
        form.find("#schedule-record-rebroadcast > fieldset:not(:first-child)").hide();
    }

    form.find("#add_show_repeats").click(function(){
        $(this).blur();
        form.find("#schedule-show-when > fieldset:last").toggle();

        //must switch rebroadcast displays
        if(form.find("#add_show_rebroadcast").attr('checked')) {

            if($(this).attr('checked')){
                form.find("#add_show_rebroadcast_absolute").hide();
                form.find("#add_show_rebroadcast_relative").show();
            }
            else {
                form.find("#add_show_rebroadcast_absolute").show();
                form.find("#add_show_rebroadcast_relative").hide();
            }
        }
    });

    form.find("#add_show_record").click(function(){
        $(this).blur();
        form.find("#add_show_rebroadcast").toggle();
    });

    form.find("#add_show_rebroadcast").click(function(){
        $(this).blur();
        if($(this).attr('checked') && !form.find("#add_show_repeats").attr('checked')) {
            form.find("#add_show_rebroadcast_absolute").show();
        }
        else if($(this).attr('checked') && form.find("#add_show_repeats").attr('checked')) {
            form.find("#add_show_rebroadcast_relative").show();
        }
        else {
            form.find("#schedule-record-rebroadcast > fieldset:not(:first-child)").hide();
        }
    });

    form.find("#add_show_repeat_type").change(function(){
        if($(this).val() == 2) {
            form.find("#add_show_day_check-label, #add_show_day_check-element").hide();
        }
        else {
            form.find("#add_show_day_check-label, #add_show_day_check-element").show();
        }
    });

    form.find("#add_show_day_check-label").addClass("block-display");
    form.find("#add_show_day_check-element").addClass("block-display clearfix");
    form.find("#add_show_day_check-element label").addClass("wrapp-label");
    form.find("#add_show_day_check-element br").remove();

    function endDateVisibility(){
        if(form.find("#add_show_no_end").is(':checked')){
            form.find("#add_show_end_date").hide();
        } else {
            form.find("#add_show_end_date").show();
        }
    }
    endDateVisibility();
    form.find("#add_show_no_end").click(endDateVisibility);

	createDateInput(form.find("#add_show_start_date"), startDpSelect);
	createDateInput(form.find("#add_show_end_date"), endDpSelect);

    form.find("#add_show_start_time").timepicker({
        amPmText: ['', '']
    });
    form.find("#add_show_duration").timepicker({
        amPmText: ['', ''],
        defaultTime: '01:00'
    });

    form.find('input[name^="add_show_rebroadcast_date_absolute"]').datepicker({
		minDate: new Date(),
		dateFormat: 'yy-mm-dd'
	});
    form.find('input[name^="add_show_rebroadcast_time"]').timepicker({
        amPmText: ['', ''],
        defaultTime: ''
    });

    form.find(".add_absolute_rebroadcast_day").click(function(){
        var li = $(this).parent().find("ul.formrow-repeat > li:visible:last").next();

        li.show();
        li = li.next();
        if(li.length === 0) {
            $(this).hide();
        }
    });

    form.find('a[id^="remove_rebroadcast"]').click(function(){
        var list = $(this).parent().parent();
        var li_num = $(this).parent().index();
        var num = list.find("li").length;
        var count = num - li_num;

        var curr = $(this).parent();
        var next = curr.next();

        for(var i=0; i<=count; i++) {
            var date = next.find('[name^="add_show_rebroadcast_date"]').val();
            curr.find('[name^="add_show_rebroadcast_date"]').val(date);
            var time = next.find('[name^="add_show_rebroadcast_time"]').val();
            curr.find('[name^="add_show_rebroadcast_time"]').val(time);

            curr = next;
            next = curr.next();
        }

        list.find("li:visible:last")
                .find('[name^="add_show_rebroadcast_date"]')
                    .val('')
                .end()
                .find('[name^="add_show_rebroadcast_time"]')
                    .val('')
                .end()
            .hide();

        list.next().show();
    });

	form.find("#add_show_hosts_autocomplete").autocomplete({
		source: findHosts,
		select: autoSelect,
        delay: 200
	});

	form.find("#schedule-show-style input").ColorPicker({
        onChange: function (hsb, hex, rgb, el) {
		    $(el).val(hex);
	    },
		onSubmit: function(hsb, hex, rgb, el) {
			$(el).val(hex);
			$(el).ColorPickerHide();
		},
		onBeforeShow: function () {
			$(this).ColorPickerSetColor(this.value);
		}
	});

    form.find("#add-show-close")
		.click(function(event){
            event.stopPropagation();
            event.preventDefault();

            var y = $("#schedule_calendar").width();
            var z = $("#schedule-add-show").width();

            $("#schedule_calendar").width(y+z+50)
                .fullCalendar('render');

			$("#add-show-form").hide();
            $.get("/Schedule/get-form", {format:"json"}, function(json){
                $("#add-show-form")
                    .empty()
                    .append(json.form);

                setAddShowEvents();
            });
            makeAddShowButton();
		});

	form.find(".add-show-submit")
		.click(function(event){
            var addShowButton = $(this);
            if (!addShowButton.hasClass("disabled")){
                addShowButton.addClass("disabled");
            }
            else {
                return;
            }

            event.preventDefault();

			var data = $("form").serializeArray();

            var hosts = $('#add_show_hosts-element input').map(function() {
                if($(this).attr("checked")) {
                    return $(this).val();
                }
            }).get();

            var days = $('#add_show_day_check-element input').map(function() {
                if($(this).attr("checked")) {
                    return $(this).val();
                }
            }).get();

            var start_date = $("#add_show_start_date").val();
            var end_date = $("#add_show_end_date").val();

            $.post("/Schedule/add-show", {format: "json", data: data, hosts: hosts, days: days}, function(json){
                addShowButton.removeClass("disabled");
                if(json.form) {
                    $("#add-show-form")
                        .empty()
                        .append(json.form);

                    setAddShowEvents();

                    $("#add_show_end_date").val(end_date);
                    $("#add_show_start_date").val(start_date);
                    showErrorSections();
                }
                else {
                     $("#add-show-form")
                        .empty()
                        .append(json.newForm);

                    setAddShowEvents();
                    scheduleRefetchEvents();
                }
            });
		});
}

function showErrorSections() {

    if($("#schedule-show-what .errors").length > 0) {
        $("#schedule-show-what").show();
    }
    if($("#schedule-show-when .errors").length > 0) {
        $("#schedule-show-when").show();
    }
    if($("#schedule-show-who .errors").length > 0) {
        $("#schedule-show-who").show();
    }
    if($("#schedule-show-style .errors").length > 0) {
        $("#schedule-show-style").show();
    }
    if($("#add_show_rebroadcast_absolute .errors").length > 0) {
        $("#schedule-record-rebroadcast").show();
        $("#add_show_rebroadcast_absolute").show();
    }
    if($("#add_show_rebroadcast_relative .errors").length > 0) {
        $("#schedule-record-rebroadcast").show();
        $("#add_show_rebroadcast_relative").show();
    }
}

$(document).ready(function() {

	//setAddShowEvents();
});

$(window).load(function() {

	setAddShowEvents();
});
<commit_msg>CC-2155:Can open the rebroadcast options without the 'Record'checkbox being selected - fixed<commit_after>/**
*
*	Schedule Dialog creation methods.
*
*/

//dateText mm-dd-yy
function startDpSelect(dateText, inst) {
	var time, date;

	time = dateText.split("-");
	date = new Date(time[0], time[1] - 1, time[2]);

	$("#add_show_end_date").datepicker("option", "minDate", date);
    $('input[name^="add_show_rebroadcast_absolute_date"]').datepicker("option", "minDate", date);
}

function endDpSelect(dateText, inst) {
	var time, date;

	time = dateText.split("-");
	date = new Date(time[0], time[1] - 1, time[2]);

	$("#add_show_start_date").datepicker( "option", "maxDate", date);
}

function createDateInput(el, onSelect) {
	var date;

	el.datepicker({
			minDate: new Date(),
			onSelect: onSelect,
			dateFormat: 'yy-mm-dd'
		});
}

function autoSelect(event, ui) {

	$("#add_show_hosts-"+ui.item.value).attr("checked", "checked");
	event.preventDefault();
}

function findHosts(request, callback) {
	var search, url;

	url = "/User/get-hosts";
	search = request.term;

	$.post(url,
		{format: "json", term: search},

		function(json) {
			callback(json.hosts);
		});

}

function setAddShowEvents() {

    var form = $("#add-show-form");

	form.find("h3").click(function(){
        $(this).next().toggle();
    });

    if(!form.find("#add_show_repeats").attr('checked')) {
        form.find("#schedule-show-when > fieldset:last").hide();
        $("#add_show_rebroadcast_relative").hide();
    }
    else {
        $("#add_show_rebroadcast_absolute").hide();
    }

    if(!form.find("#add_show_record").attr('checked')) {
        form.find("#add_show_rebroadcast").hide();
    }

    if(!form.find("#add_show_rebroadcast").attr('checked')) {
        form.find("#schedule-record-rebroadcast > fieldset:not(:first-child)").hide();
    }

    form.find("#add_show_repeats").click(function(){
        $(this).blur();
        form.find("#schedule-show-when > fieldset:last").toggle();

        //must switch rebroadcast displays
        if(form.find("#add_show_rebroadcast").attr('checked')) {

            if($(this).attr('checked')){
                form.find("#add_show_rebroadcast_absolute").hide();
                form.find("#add_show_rebroadcast_relative").show();
            }
            else {
                form.find("#add_show_rebroadcast_absolute").show();
                form.find("#add_show_rebroadcast_relative").hide();
            }
        }
    });

    form.find("#add_show_record").click(function(){
        $(this).blur();
        form.find("#add_show_rebroadcast").toggle();
    });

    form.find("#add_show_rebroadcast").click(function(){
        $(this).blur();
        if(form.find("#add_show_record").attr('checked')){
	        if($(this).attr('checked') && !form.find("#add_show_repeats").attr('checked')) {
	            form.find("#add_show_rebroadcast_absolute").show();
	        }
	        else if($(this).attr('checked') && form.find("#add_show_repeats").attr('checked')) {
	            form.find("#add_show_rebroadcast_relative").show();
	        }
	        else {
	            form.find("#schedule-record-rebroadcast > fieldset:not(:first-child)").hide();
	        }
    	}
    });

    form.find("#add_show_repeat_type").change(function(){
        if($(this).val() == 2) {
            form.find("#add_show_day_check-label, #add_show_day_check-element").hide();
        }
        else {
            form.find("#add_show_day_check-label, #add_show_day_check-element").show();
        }
    });

    form.find("#add_show_day_check-label").addClass("block-display");
    form.find("#add_show_day_check-element").addClass("block-display clearfix");
    form.find("#add_show_day_check-element label").addClass("wrapp-label");
    form.find("#add_show_day_check-element br").remove();

    function endDateVisibility(){
        if(form.find("#add_show_no_end").is(':checked')){
            form.find("#add_show_end_date").hide();
        } else {
            form.find("#add_show_end_date").show();
        }
    }
    endDateVisibility();
    form.find("#add_show_no_end").click(endDateVisibility);

	createDateInput(form.find("#add_show_start_date"), startDpSelect);
	createDateInput(form.find("#add_show_end_date"), endDpSelect);

    form.find("#add_show_start_time").timepicker({
        amPmText: ['', '']
    });
    form.find("#add_show_duration").timepicker({
        amPmText: ['', ''],
        defaultTime: '01:00'
    });

    form.find('input[name^="add_show_rebroadcast_date_absolute"]').datepicker({
		minDate: new Date(),
		dateFormat: 'yy-mm-dd'
	});
    form.find('input[name^="add_show_rebroadcast_time"]').timepicker({
        amPmText: ['', ''],
        defaultTime: ''
    });

    form.find(".add_absolute_rebroadcast_day").click(function(){
        var li = $(this).parent().find("ul.formrow-repeat > li:visible:last").next();

        li.show();
        li = li.next();
        if(li.length === 0) {
            $(this).hide();
        }
    });

    form.find('a[id^="remove_rebroadcast"]').click(function(){
        var list = $(this).parent().parent();
        var li_num = $(this).parent().index();
        var num = list.find("li").length;
        var count = num - li_num;

        var curr = $(this).parent();
        var next = curr.next();

        for(var i=0; i<=count; i++) {
            var date = next.find('[name^="add_show_rebroadcast_date"]').val();
            curr.find('[name^="add_show_rebroadcast_date"]').val(date);
            var time = next.find('[name^="add_show_rebroadcast_time"]').val();
            curr.find('[name^="add_show_rebroadcast_time"]').val(time);

            curr = next;
            next = curr.next();
        }

        list.find("li:visible:last")
                .find('[name^="add_show_rebroadcast_date"]')
                    .val('')
                .end()
                .find('[name^="add_show_rebroadcast_time"]')
                    .val('')
                .end()
            .hide();

        list.next().show();
    });

	form.find("#add_show_hosts_autocomplete").autocomplete({
		source: findHosts,
		select: autoSelect,
        delay: 200
	});

	form.find("#schedule-show-style input").ColorPicker({
        onChange: function (hsb, hex, rgb, el) {
		    $(el).val(hex);
	    },
		onSubmit: function(hsb, hex, rgb, el) {
			$(el).val(hex);
			$(el).ColorPickerHide();
		},
		onBeforeShow: function () {
			$(this).ColorPickerSetColor(this.value);
		}
	});

    form.find("#add-show-close")
		.click(function(event){
            event.stopPropagation();
            event.preventDefault();

            var y = $("#schedule_calendar").width();
            var z = $("#schedule-add-show").width();

            $("#schedule_calendar").width(y+z+50)
                .fullCalendar('render');

			$("#add-show-form").hide();
            $.get("/Schedule/get-form", {format:"json"}, function(json){
                $("#add-show-form")
                    .empty()
                    .append(json.form);

                setAddShowEvents();
            });
            makeAddShowButton();
		});

	form.find(".add-show-submit")
		.click(function(event){
            var addShowButton = $(this);
            if (!addShowButton.hasClass("disabled")){
                addShowButton.addClass("disabled");
            }
            else {
                return;
            }

            event.preventDefault();

			var data = $("form").serializeArray();

            var hosts = $('#add_show_hosts-element input').map(function() {
                if($(this).attr("checked")) {
                    return $(this).val();
                }
            }).get();

            var days = $('#add_show_day_check-element input').map(function() {
                if($(this).attr("checked")) {
                    return $(this).val();
                }
            }).get();

            var start_date = $("#add_show_start_date").val();
            var end_date = $("#add_show_end_date").val();

            $.post("/Schedule/add-show", {format: "json", data: data, hosts: hosts, days: days}, function(json){
                addShowButton.removeClass("disabled");
                if(json.form) {
                    $("#add-show-form")
                        .empty()
                        .append(json.form);

                    setAddShowEvents();

                    $("#add_show_end_date").val(end_date);
                    $("#add_show_start_date").val(start_date);
                    showErrorSections();
                }
                else {
                     $("#add-show-form")
                        .empty()
                        .append(json.newForm);

                    setAddShowEvents();
                    scheduleRefetchEvents();
                }
            });
		});
}

function showErrorSections() {

    if($("#schedule-show-what .errors").length > 0) {
        $("#schedule-show-what").show();
    }
    if($("#schedule-show-when .errors").length > 0) {
        $("#schedule-show-when").show();
    }
    if($("#schedule-show-who .errors").length > 0) {
        $("#schedule-show-who").show();
    }
    if($("#schedule-show-style .errors").length > 0) {
        $("#schedule-show-style").show();
    }
    if($("#add_show_rebroadcast_absolute .errors").length > 0) {
        $("#schedule-record-rebroadcast").show();
        $("#add_show_rebroadcast_absolute").show();
    }
    if($("#add_show_rebroadcast_relative .errors").length > 0) {
        $("#schedule-record-rebroadcast").show();
        $("#add_show_rebroadcast_relative").show();
    }
}

$(document).ready(function() {

	//setAddShowEvents();
});

$(window).load(function() {

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


<commit_before>/*
 * Parsing.
 *
 * Copyright (c) 2014 Tiancheng "Timothy" Gu <timothygu99@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * @file Parsing summary and report files.
 *
 * Summary files has three lines: one with header, one with config, one
 * with stats. They were generated by fate-recv.sh. The first two lines were
 * copied straight from uploaded report file. The contents of the third line
 * was calculated by iterating over all tests in report file.
 *
 * @module lib/parse
 */

'use strict'

var fs = require('fs')
  , readline = require('readline')
  , path = require('path')
  , xz = require('xz-pipe')
  , debug = require('debug')('f:l:parse')

var util       = require('./util')
  , sort       = require('./sort')
  , cache      = require('./cache')
  , cacheLock  = require('./cache').lock

/**
 * Parses a `header` line in a report file or old-style summary file.
 *
 * @param  {string} line Line to be splitted
 * @return {object|null} The resulting object, or null if an error occurred.
 *
 * @private
 */
function splitHeader (line) {
  var s = line.split(':')
    , header = { version: +s[1], date   : +s[2]
               , slot   :  s[3], rev    :  s[4]
               , status : +s[5], errstr :  s[6]
               , branch :  s[7], comment:  s[8]
               }
  if (header.version === 0) {
    header.branch = 'master'
    header.comment = s[7]
  }
  return header
}

/**
 * Parses a `config` line in a report file or old-style summary file.
 *
 * @param  {string} line Line to be splitted
 * @return {object|null} The resulting object, or null if an error occurred.
 *
 * @private
 */
function splitConfig (line) {
  var c = line.split(':')
  return { arch: c[1], subarch: c[2]
         , cpu : c[3], os     : c[4]
         , cc  : c[5], config : c[6]
         }
}

/**
 * Parses a `stats` line in a old-style summary file.
 *
 * @param  {string} line Line to be splitted
 * @return {object|null} The resulting object, or null if an error occurred.
 *
 * @private
 */
function splitStats (line) {
  var s = line.split(':')
  return { ntests: +s[1]
         , npass : +s[2]
         , nwarn : +s[3]
         }
}

/**
 * This type of object is used for a single test record.
 *
 * @typedef Record
 *
 * @prop {string} name   Name of the test.
 * @prop {string} status Return code of the test.
 * @prop {(string|undefined)} diff   Base64-encoded diff.
 * @prop {(string|undefined)} stderr Base64-encoded stderr output.
 */

/**
 * Parses a record line in a report file.
 *
 * @param  {string} line Line to be splitted
 * @param  {number} errdiff Verbosity of the record. 0 - no stderr or diff.
 *                          1 - stderr and diff only if the status is < 0.
 *                          2 - always include stderr and diff
 * @return {?Record} The resulting {@link Record} object, or null if an error
 *                  occurred.
 */
// TODO add errdiff doc to all the other places too
function splitRec (line, errdiff) {
  errdiff = errdiff === undefined ? 1 : errdiff
  var r = line.split(':')
  switch (errdiff) {
    case 0: return { name: r[0], status: +r[1] }
    case 1: return +r[1] === 0
                 ? { name: r[0], status:  0    }
                 : { name: r[0], status: +r[1], diff: r[2], stderr: r[3] }
    case 2: return { name: r[0], status: +r[1], diff: r[2], stderr: r[3] }
  }
}

/**
 * Summaries are the metadata associated with a FATE run. This function
 * creates a Summary object with header, config, and stats merged into a
 * single object.
 *
 * @param {header} h Header object as returned by splitHeader()
 * @param {config} c Configuration object as returned by splitConfig()
 * @param {stats}  s Stats object as returned by splitStats() or created from
 *                   the report file.
 *
 * @prop {number} version The version of the report file.
 * @prop {Timestamp} date The timestamp.
 * @prop {string} slot    The slot.
 * @prop {string} rev     Revision of the source.
 * @prop {number} status  Final status of the building process.
 * @prop {string} errstr  Error description, if there is an error.
 * @prop {string} comment Any additional comments from the slot owner.
 *
 * @prop {string} arch    The architecture.
 * @prop {string} subarch The variant of the architecture.
 * @prop {string} cpu     The selected CPU.
 * @prop {string} os      The operating system.
 * @prop {string} cc      The compiler.
 * @prop {string} config  The build-time configure flags.
 *
 * @prop {number} ntests  Number of tests run.
 * @prop {number} npass   Number of tests passed.
 * @prop {number} nfail   Number of failed tests
 * @prop {number} nwarn   Number of compiler warnings.
 *
 * @prop {string} rclass  HTML class of the run
 * @prop {string} rtext   HTML text of the run
 *
 * @constructor
 */
module.exports.Summary = function (h, c, s) {
  var prop
  for (prop in h) {
    if (h.hasOwnProperty(prop)) this[prop] = h[prop]
  }
  for (prop in c) {
    if (c.hasOwnProperty(prop)) this[prop] = c[prop]
  }
  for (prop in s) {
    if (s.hasOwnProperty(prop)) this[prop] = s[prop]
  }

  // Postprocessing
  // Calculate nfail property
  this.nfail = this.ntests - this.npass

  // Calculate rclass and rtext for HTML display
  if (this.npass) {
    this.rclass = this.nfail ? 'warn' : 'pass'
    this.rtext  = this.npass + ' / ' + this.ntests
  } else if (!this.ntests && !this.status) {
    this.rclass = 'pass'
    this.rtext  = 'build only'
  } else {
    this.rclass = 'fail'
    this.rtext  = this.errstr
  }

  // Always make subarch non-empty
  if (!this.subarch) this.subarch = this.arch
}
var Summary = module.exports.Summary

/**
 * This type of callback is used for {@link loadSummary}() that returns an
 * {@link Summary} object.
 *
 * @callback summaryCallback
 * @param {Error}   err The error occurred in the function if any.
 * @param {Summary} rep The resulting {@link Summary} object. (rep = report)
 */

/**
 * Load a summary file.
 *
 * @param slot     {string}         The slot.
 * @param date     {Timestamp}      The timestamp.
 * @param callback {summaryCallback} Callback receiving the Summary object.
 * @private
 */
function loadSummary (slot, date, callback) {
  var header = null, config = null, stats = null
  var repdir = path.join(util.dir, slot, date)

  var summary = fs.createReadStream(
    path.join(repdir, 'summary')
  ).on('error', function (err) {
    err.HTMLMessage = 'Slot "' + slot + '" does not exist.'
    err.status = 404
    return callback(err)
  })

  var err = null

  var lr = new readline.createInterface({
    input: summary
  , terminal: false
  })

  lr.on('line', function handleLine (line) {
    function checkLine (obj, name) {
      if (!obj) {
        var msg = "Could not parse " + name + " correctly:\n" + line
        if (err) {
          err.message += '\n'
          err.message += msg
          // Don't append stack trace as the stack trace
          // is always going to be the same
        } else {
          err = new Error(msg)
          err.status = 500
        }
      }
    }
    switch (line.split(':')[0]) {
    case 'fate':
      if (!header) {
        header = splitHeader(line)
        checkLine(header, 'header')
      }
      break
    case 'config':
      if (!config) {
        config = splitConfig(line)
        checkLine(config, 'config')
      }
      break
    case 'stats':
      if (!stats) {
        stats = splitStats(line)
        checkLine(stats, 'stats')
      }
      break
    }
  }).on('close', function close () {
    callback(err, new Summary(header, config, stats))
  })
}
module.exports.loadSummary = loadSummary

/**
 * This type of callback is used for functions that return an array of
 * {@link Record} object(s).
 *
 * @callback recCallback
 *
 * @param {?Error}          err   The error occurred in the function, if any.
 *                                Beware that sometimes even if there is an
 *                                error this argument might be `null`.
 * @param {?Array.<Record>} recs  The resulting array of {@link Record} object
 *                                or `null` if there is any error.
 */

/**
 * Load the test report of an entry. It loads the xz-compressed report.xz and
 * splits it into {@link Record}s, then calls the callback with the completed
 * data.
 *
 * @param slot     {string}          The slot.
 * @param date     {number}          The timestamp.
 * @param callback {recCallback}     Callback receiving the array of
 *                                   {@link Record} objects.
 */
module.exports.loadReport = function loadReport (slot, date, errdiff, cb) {
  var repFile = path.join(slot, date, 'report.xz')
    , repPath = path.join(util.dir, repFile)
    , cacheName = ['report', slot, date, errdiff].join('_')
    , cacheHasIt = false

  cacheLock.readLock(function (r) {
    if (cache.has(cacheName)) {
      debug('cache hit')
      var data = cache.get(cacheName)
      cacheHasIt = true
      cb(null, data)
    }
    r()
  })
  if (cacheHasIt) return
  debug('cache miss')

  var fileStream = fs.createReadStream(repPath)
                     .on('error', function noReport (err) {
    err.status = 404
    err.HTMLMessage = 'Report "' + repFile + '" not found.'
    return cb(err)
  })

  // Have to use an empty error handler to prevent uncaught exception in
  // .pipe().
  // Don't explicitly handle errors as it is hard to avoid race conditions
  // and the stream's close event might be emitted before error signal
  // here.
  var stream = fileStream.pipe(xz.d().on('error', function () {}))
  var recs = []
  var lr = new readline.createInterface({
    input: stream
  , terminal: false
  })

  lr.on('line', function handleLine (line) {
    var rec = splitRec(line, errdiff)
    // Don't care to check if the line is indeed a test record.
    // Depend on splitRec()'s judgment.
    if (rec && rec.name != 'fate' && rec.name != 'config') {
      recs[recs.length] = rec
    }
  }).on('close', function close () {
    var data = recs.sort(sort.by('name'))
    cacheLock.writeLock(function (r) {
      cache.set(cacheName, data)
      r()
    })
    cb(null, data)
  })
}

/**
 * Get the slot owner name.
 * The owner of a slot is stored in <fatedir>/<slot>/owner.
 *
 * @param slot {string} The slot.
 * @return     {string} The owner of the slot.
 */
module.exports.getSlotOwner = function (slot, callback) {
  fs.readFile(path.join(util.dir, slot, 'owner') , 'utf8', function (e, d) {
    // ignore error
    // at worst the page will display "owner: null" anyway
    // no need to handle it
    callback(null, d.trim())
  })
}

module.exports.loadLastPass = function (slot, callback) {
  var debug = require('debug')('f:parse:loadLastPass')

  var repdir = path.join(util.dir, slot)

  var lastPass = fs.createReadStream(
    path.join(repdir, 'lastpass')
  ).on('error', function noSlotLastpass (err) {
    err.HTMLMessage = 'Slot "' + slot + '" does not exist.'
    err.status = 404
    return callback(err)
  })

  var recs = []

  var lr = new readline.createInterface({
    input: lastPass
  , terminal: false
  })
  lr.on('line', function handleLine (line) {
    var splittedLine = line.split(':')
    if (splittedLine.length !== 3) return
    recs[splittedLine[0]] = {
      date: Number(splittedLine[1])
    , rev:  splittedLine[2]
    }
  }).on('close', function close () {
    callback(null, recs)
  })
}
<commit_msg>Fix a corner case where no tests failed but status still > 0<commit_after>/*
 * Parsing.
 *
 * Copyright (c) 2014 Tiancheng "Timothy" Gu <timothygu99@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * @file Parsing summary and report files.
 *
 * Summary files has three lines: one with header, one with config, one
 * with stats. They were generated by fate-recv.sh. The first two lines were
 * copied straight from uploaded report file. The contents of the third line
 * was calculated by iterating over all tests in report file.
 *
 * @module lib/parse
 */

'use strict'

var fs = require('fs')
  , readline = require('readline')
  , path = require('path')
  , xz = require('xz-pipe')
  , debug = require('debug')('f:l:parse')

var util       = require('./util')
  , sort       = require('./sort')
  , cache      = require('./cache')
  , cacheLock  = require('./cache').lock

/**
 * Parses a `header` line in a report file or old-style summary file.
 *
 * @param  {string} line Line to be splitted
 * @return {object|null} The resulting object, or null if an error occurred.
 *
 * @private
 */
function splitHeader (line) {
  var s = line.split(':')
    , header = { version: +s[1], date   : +s[2]
               , slot   :  s[3], rev    :  s[4]
               , status : +s[5], errstr :  s[6]
               , branch :  s[7], comment:  s[8]
               }
  if (header.version === 0) {
    header.branch = 'master'
    header.comment = s[7]
  }
  return header
}

/**
 * Parses a `config` line in a report file or old-style summary file.
 *
 * @param  {string} line Line to be splitted
 * @return {object|null} The resulting object, or null if an error occurred.
 *
 * @private
 */
function splitConfig (line) {
  var c = line.split(':')
  return { arch: c[1], subarch: c[2]
         , cpu : c[3], os     : c[4]
         , cc  : c[5], config : c[6]
         }
}

/**
 * Parses a `stats` line in a old-style summary file.
 *
 * @param  {string} line Line to be splitted
 * @return {object|null} The resulting object, or null if an error occurred.
 *
 * @private
 */
function splitStats (line) {
  var s = line.split(':')
  return { ntests: +s[1]
         , npass : +s[2]
         , nwarn : +s[3]
         }
}

/**
 * This type of object is used for a single test record.
 *
 * @typedef Record
 *
 * @prop {string} name   Name of the test.
 * @prop {string} status Return code of the test.
 * @prop {(string|undefined)} diff   Base64-encoded diff.
 * @prop {(string|undefined)} stderr Base64-encoded stderr output.
 */

/**
 * Parses a record line in a report file.
 *
 * @param  {string} line Line to be splitted
 * @param  {number} errdiff Verbosity of the record. 0 - no stderr or diff.
 *                          1 - stderr and diff only if the status is < 0.
 *                          2 - always include stderr and diff
 * @return {?Record} The resulting {@link Record} object, or null if an error
 *                  occurred.
 */
// TODO add errdiff doc to all the other places too
function splitRec (line, errdiff) {
  errdiff = errdiff === undefined ? 1 : errdiff
  var r = line.split(':')
  switch (errdiff) {
    case 0: return { name: r[0], status: +r[1] }
    case 1: return +r[1] === 0
                 ? { name: r[0], status:  0    }
                 : { name: r[0], status: +r[1], diff: r[2], stderr: r[3] }
    case 2: return { name: r[0], status: +r[1], diff: r[2], stderr: r[3] }
  }
}

/**
 * Summaries are the metadata associated with a FATE run. This function
 * creates a Summary object with header, config, and stats merged into a
 * single object.
 *
 * @param {header} h Header object as returned by splitHeader()
 * @param {config} c Configuration object as returned by splitConfig()
 * @param {stats}  s Stats object as returned by splitStats() or created from
 *                   the report file.
 *
 * @prop {number} version The version of the report file.
 * @prop {Timestamp} date The timestamp.
 * @prop {string} slot    The slot.
 * @prop {string} rev     Revision of the source.
 * @prop {number} status  Final status of the building process.
 * @prop {string} errstr  Error description, if there is an error.
 * @prop {string} comment Any additional comments from the slot owner.
 *
 * @prop {string} arch    The architecture.
 * @prop {string} subarch The variant of the architecture.
 * @prop {string} cpu     The selected CPU.
 * @prop {string} os      The operating system.
 * @prop {string} cc      The compiler.
 * @prop {string} config  The build-time configure flags.
 *
 * @prop {number} ntests  Number of tests run.
 * @prop {number} npass   Number of tests passed.
 * @prop {number} nfail   Number of failed tests
 * @prop {number} nwarn   Number of compiler warnings.
 *
 * @prop {string} rclass  HTML class of the run
 * @prop {string} rtext   HTML text of the run
 *
 * @constructor
 */
module.exports.Summary = function (h, c, s) {
  var prop
  for (prop in h) {
    if (h.hasOwnProperty(prop)) this[prop] = h[prop]
  }
  for (prop in c) {
    if (c.hasOwnProperty(prop)) this[prop] = c[prop]
  }
  for (prop in s) {
    if (s.hasOwnProperty(prop)) this[prop] = s[prop]
  }

  // Postprocessing
  // Calculate nfail property
  this.nfail = this.ntests - this.npass

  // Calculate rclass and rtext for HTML display
  if (this.npass) {
    this.rclass = this.status ? 'warn' : 'pass'
    this.rtext  = this.npass + ' / ' + this.ntests
  } else if (!this.ntests && !this.status) {
    this.rclass = 'pass'
    this.rtext  = 'build only'
  } else {
    this.rclass = 'fail'
    this.rtext  = this.errstr
  }

  // Always make subarch non-empty
  if (!this.subarch) this.subarch = this.arch
}
var Summary = module.exports.Summary

/**
 * This type of callback is used for {@link loadSummary}() that returns an
 * {@link Summary} object.
 *
 * @callback summaryCallback
 * @param {Error}   err The error occurred in the function if any.
 * @param {Summary} rep The resulting {@link Summary} object. (rep = report)
 */

/**
 * Load a summary file.
 *
 * @param slot     {string}         The slot.
 * @param date     {Timestamp}      The timestamp.
 * @param callback {summaryCallback} Callback receiving the Summary object.
 * @private
 */
function loadSummary (slot, date, callback) {
  var header = null, config = null, stats = null
  var repdir = path.join(util.dir, slot, date)

  var summary = fs.createReadStream(
    path.join(repdir, 'summary')
  ).on('error', function (err) {
    err.HTMLMessage = 'Slot "' + slot + '" does not exist.'
    err.status = 404
    return callback(err)
  })

  var err = null

  var lr = new readline.createInterface({
    input: summary
  , terminal: false
  })

  lr.on('line', function handleLine (line) {
    function checkLine (obj, name) {
      if (!obj) {
        var msg = "Could not parse " + name + " correctly:\n" + line
        if (err) {
          err.message += '\n'
          err.message += msg
          // Don't append stack trace as the stack trace
          // is always going to be the same
        } else {
          err = new Error(msg)
          err.status = 500
        }
      }
    }
    switch (line.split(':')[0]) {
    case 'fate':
      if (!header) {
        header = splitHeader(line)
        checkLine(header, 'header')
      }
      break
    case 'config':
      if (!config) {
        config = splitConfig(line)
        checkLine(config, 'config')
      }
      break
    case 'stats':
      if (!stats) {
        stats = splitStats(line)
        checkLine(stats, 'stats')
      }
      break
    }
  }).on('close', function close () {
    callback(err, new Summary(header, config, stats))
  })
}
module.exports.loadSummary = loadSummary

/**
 * This type of callback is used for functions that return an array of
 * {@link Record} object(s).
 *
 * @callback recCallback
 *
 * @param {?Error}          err   The error occurred in the function, if any.
 *                                Beware that sometimes even if there is an
 *                                error this argument might be `null`.
 * @param {?Array.<Record>} recs  The resulting array of {@link Record} object
 *                                or `null` if there is any error.
 */

/**
 * Load the test report of an entry. It loads the xz-compressed report.xz and
 * splits it into {@link Record}s, then calls the callback with the completed
 * data.
 *
 * @param slot     {string}          The slot.
 * @param date     {number}          The timestamp.
 * @param callback {recCallback}     Callback receiving the array of
 *                                   {@link Record} objects.
 */
module.exports.loadReport = function loadReport (slot, date, errdiff, cb) {
  var repFile = path.join(slot, date, 'report.xz')
    , repPath = path.join(util.dir, repFile)
    , cacheName = ['report', slot, date, errdiff].join('_')
    , cacheHasIt = false

  cacheLock.readLock(function (r) {
    if (cache.has(cacheName)) {
      debug('cache hit')
      var data = cache.get(cacheName)
      cacheHasIt = true
      cb(null, data)
    }
    r()
  })
  if (cacheHasIt) return
  debug('cache miss')

  var fileStream = fs.createReadStream(repPath)
                     .on('error', function noReport (err) {
    err.status = 404
    err.HTMLMessage = 'Report "' + repFile + '" not found.'
    return cb(err)
  })

  // Have to use an empty error handler to prevent uncaught exception in
  // .pipe().
  // Don't explicitly handle errors as it is hard to avoid race conditions
  // and the stream's close event might be emitted before error signal
  // here.
  var stream = fileStream.pipe(xz.d().on('error', function () {}))
  var recs = []
  var lr = new readline.createInterface({
    input: stream
  , terminal: false
  })

  lr.on('line', function handleLine (line) {
    var rec = splitRec(line, errdiff)
    // Don't care to check if the line is indeed a test record.
    // Depend on splitRec()'s judgment.
    if (rec && rec.name != 'fate' && rec.name != 'config') {
      recs[recs.length] = rec
    }
  }).on('close', function close () {
    var data = recs.sort(sort.by('name'))
    cacheLock.writeLock(function (r) {
      cache.set(cacheName, data)
      r()
    })
    cb(null, data)
  })
}

/**
 * Get the slot owner name.
 * The owner of a slot is stored in <fatedir>/<slot>/owner.
 *
 * @param slot {string} The slot.
 * @return     {string} The owner of the slot.
 */
module.exports.getSlotOwner = function (slot, callback) {
  fs.readFile(path.join(util.dir, slot, 'owner') , 'utf8', function (e, d) {
    // ignore error
    // at worst the page will display "owner: null" anyway
    // no need to handle it
    callback(null, d.trim())
  })
}

module.exports.loadLastPass = function (slot, callback) {
  var debug = require('debug')('f:parse:loadLastPass')

  var repdir = path.join(util.dir, slot)

  var lastPass = fs.createReadStream(
    path.join(repdir, 'lastpass')
  ).on('error', function noSlotLastpass (err) {
    err.HTMLMessage = 'Slot "' + slot + '" does not exist.'
    err.status = 404
    return callback(err)
  })

  var recs = []

  var lr = new readline.createInterface({
    input: lastPass
  , terminal: false
  })
  lr.on('line', function handleLine (line) {
    var splittedLine = line.split(':')
    if (splittedLine.length !== 3) return
    recs[splittedLine[0]] = {
      date: Number(splittedLine[1])
    , rev:  splittedLine[2]
    }
  }).on('close', function close () {
    callback(null, recs)
  })
}
<|endoftext|>


<commit_before>import React from 'react'
import { Button, Form, Header, Icon, Message, Modal } from 'semantic-ui-react'
import steem from 'steem'

export default class LoginModal extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      warningOpen: false,
      loginOpen: false,
      error: false,
      loading: false,
      account: '',
      key: ''
    }
  }

  handleOpen = (e) => this.setState({
    warningOpen: true,
    loginOpen: false
  })

  handleSwap = (e) => this.setState({
    warningOpen: false,
    loginOpen: true
  })

  handleClose = (e) => this.setState({
    warningOpen: false,
    loginOpen: false
  })

  handleChange = (e, { name, value }) => this.setState({ [name]: value })

  handleSubmit = e => {
    e.preventDefault()
    const { account, key } = this.state,
          t = this
    let isValidKey = steem.auth.isWif(key),
        isValidForAccount = false
    // Indicate we're loading
    t.setState({
      loading: true,
      error: false
    })
    if(isValidKey) {
      steem.api.getAccounts([account], function(err, result) {
        if(!result.length) {
          t.setState({
            loading: false,
            error: 'Invalid account name'
          })
          return
        }
        if(result) {
          let public_key = steem.auth.wifToPublic(key),
              key_auths = result[0].posting.key_auths
          for(var i=0; i < key_auths.length; i++) {
            if(key_auths[i][0] === public_key) {
              isValidForAccount = true
            }
          }
        }
        if(isValidForAccount) {
          t.props.actions.signinAccount(account, key, result[0])
          t.handleClose()
        } else {
          t.setState({
            loading: false,
            error: 'Invalid WIF Key for Account'
          })
        }
      })
    } else {
      t.setState({
        loading: false,
        error: 'Invalid WIF Key'
      })
    }

  }

  render() {
    let modal = (
      <Modal
        trigger={<Button fluid onClick={this.handleOpen}>Sign-in</Button>}
        open={this.state.warningOpen}
        onOpen={this.open}
        onClose={this.close}
        basic
        size='small'
        className='modal-warning'
      >

        <Modal.Content>

Please use your use your keys responsibly.

This is beta software and you use it at your own risk. Please ensure you are only using your posting key within the site to ensure your account balance is safe.

          {/*
For more information and best practices, please <Link to='#' rel='nofollow' target='_blank'>read our post about security and your keys</Link>.
*/}
        </Modal.Content>
        <Modal.Actions>
          <Button color='orange' onClick={this.handleClose}>Cancel</Button>
          <Button color='green' icon onClick={this.handleSwap}>Proceed <Icon name='right chevron' /></Button>
        </Modal.Actions>
      </Modal>
    )
    if(this.state.loginOpen) {
      modal = (
        <Modal
          open={this.state.loginOpen}
          size='small'
        >

          <Modal.Content>
            <Message>
              <Message.Header>Before you login, please note:</Message.Header>
              <Message.List>
                <Message.Item>chainBB uses Steem‘s Post Beneficiaries feature to support itself, at a rate of 15% on all posts created.</Message.Item>
                <Message.Item>chainBB will only accept posting keys (WIF) for login, which are currently stored unencrypted in local storage.</Message.Item>
                <Message.Item>chainBB is currently in BETA and still may contain bugs.</Message.Item>
              </Message.List>
            </Message>
            <Form
              error={(this.state.error) ? true : false}
              loading={this.state.loading}>
              <Form.Input placeholder='Account Name' name='account' value={this.state.account} onChange={this.handleChange} />
              <Form.Input placeholder='Posting Key' type='password' name='key' value={this.state.key} onChange={this.handleChange} />

                Need help finding your Posting Key?
                {' '}

                  Read this post by @rgeddes on steemit.com

              <Message
                error
                header='Error'
                content={this.state.error}
              />
            </Form>
          </Modal.Content>
          <Modal.Actions>
            <Button color='orange' onClick={this.handleClose}>Cancel</Button>
            <Button color='blue' icon onClick={this.handleSubmit}>Sign-in <Icon name='right chevron' /></Button>
          </Modal.Actions>
        </Modal>
      )
    }
    return modal
  }
}
<commit_msg>Clarification on login<commit_after>import React from 'react'
import { Button, Form, Header, Icon, Message, Modal } from 'semantic-ui-react'
import steem from 'steem'

export default class LoginModal extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      warningOpen: false,
      loginOpen: false,
      error: false,
      loading: false,
      account: '',
      key: ''
    }
  }

  handleOpen = (e) => this.setState({
    warningOpen: true,
    loginOpen: false
  })

  handleSwap = (e) => this.setState({
    warningOpen: false,
    loginOpen: true
  })

  handleClose = (e) => this.setState({
    warningOpen: false,
    loginOpen: false
  })

  handleChange = (e, { name, value }) => this.setState({ [name]: value })

  handleSubmit = e => {
    e.preventDefault()
    const { account, key } = this.state,
          t = this
    let isValidKey = steem.auth.isWif(key),
        isValidForAccount = false
    // Indicate we're loading
    t.setState({
      loading: true,
      error: false
    })
    if(isValidKey) {
      steem.api.getAccounts([account], function(err, result) {
        if(!result.length) {
          t.setState({
            loading: false,
            error: 'Invalid account name'
          })
          return
        }
        if(result) {
          let public_key = steem.auth.wifToPublic(key),
              key_auths = result[0].posting.key_auths
          for(var i=0; i < key_auths.length; i++) {
            if(key_auths[i][0] === public_key) {
              isValidForAccount = true
            }
          }
        }
        if(isValidForAccount) {
          t.props.actions.signinAccount(account, key, result[0])
          t.handleClose()
        } else {
          t.setState({
            loading: false,
            error: 'Invalid WIF Key for Account'
          })
        }
      })
    } else {
      t.setState({
        loading: false,
        error: 'Invalid WIF Key'
      })
    }

  }

  render() {
    let modal = (
      <Modal
        trigger={<Button fluid onClick={this.handleOpen}>Sign-in</Button>}
        open={this.state.warningOpen}
        onOpen={this.open}
        onClose={this.close}
        basic
        size='small'
        className='modal-warning'
      >

        <Modal.Content>

Please use your use your keys responsibly.

This is beta software and you use it at your own risk. Please ensure you are only using your posting key within the site to ensure your account balance is safe.

          {/*
For more information and best practices, please <Link to='#' rel='nofollow' target='_blank'>read our post about security and your keys</Link>.
*/}
        </Modal.Content>
        <Modal.Actions>
          <Button color='orange' onClick={this.handleClose}>Cancel</Button>
          <Button color='green' icon onClick={this.handleSwap}>Proceed <Icon name='right chevron' /></Button>
        </Modal.Actions>
      </Modal>
    )
    if(this.state.loginOpen) {
      modal = (
        <Modal
          open={this.state.loginOpen}
          size='small'
        >

          <Modal.Content>
            <Message>
              <Message.Header>Before you login, please note:</Message.Header>
              <Message.List>
                <Message.Item>chainBB uses Steem‘s Post Beneficiaries feature to support itself, at a rate of 15% on all posts created.</Message.Item>
                <Message.Item>chainBB will only accept posting keys (WIF) for login, which are currently stored unencrypted in local storage.</Message.Item>
                <Message.Item>chainBB is currently in BETA and still may contain bugs.</Message.Item>
              </Message.List>
            </Message>
            <Form
              error={(this.state.error) ? true : false}
              loading={this.state.loading}>
              <Form.Input placeholder='Account Name' name='account' value={this.state.account} onChange={this.handleChange} />
              <Form.Input placeholder='Posting (Private Key)' type='password' name='key' value={this.state.key} onChange={this.handleChange} />

                Need help finding your Posting (Private Key)?
                {' '}

                  Read this post by @rgeddes on steemit.com.

              <Message
                error
                header='Error'
                content={this.state.error}
              />
            </Form>
          </Modal.Content>
          <Modal.Actions>
            <Button color='orange' onClick={this.handleClose}>Cancel</Button>
            <Button color='blue' icon onClick={this.handleSubmit}>Sign-in <Icon name='right chevron' /></Button>
          </Modal.Actions>
        </Modal>
      )
    }
    return modal
  }
}
<|endoftext|>


<commit_before>/**
 * Appcelerator Common Library for Node.js
 * Copyright (c) 2012 by Appcelerator, Inc. All Rights Reserved.
 * Please see the LICENSE file for information about licensing.
 */

var fs = require('fs'),
	path = require('path'),
	afs = require('./fs'),
	wrench = require('wrench'),
	xml = require('./xml'),
	DOMParser = require('xmldom').DOMParser;

function PlistType(type, value) {
	this.className = 'PlistType';
	this.type = type;
	this.value = type == 'real' && value == Math.round(value) ? value.toFixed(1) : value;
}

function toXml(dom, parent, it, indent) {
	var i = indent || 0,
		p,
		q = parent,
		type = Object.prototype.toString.call(it);

	while (q.parentNode) {
		i++;
		q = q.parentNode;
	}

	switch (type) {
		case '[object Object]':
			if (it.className == 'PlistType') {
				dom.create(it.type, { nodeValue: it.value }, parent);
			} else {
				p = dom.create('dict', null, parent);
				Object.keys(it).forEach(function (name) {
					dom.create('key', { nodeValue: name }, p);
					toXml(dom, p, it[name], indent);
				});
				p.appendChild(dom.createTextNode('\r\n' + new Array(i).join('\t')));
			}
			break;

		case '[object Array]':
			p = dom.create('array', null, parent);
			it.forEach(function (val) {
				toXml(dom, p, val, indent);
			});
			p.appendChild(dom.createTextNode('\r\n' + new Array(i).join('\t')));
			break;

		case '[object Date]':
			dom.create('date', { nodeValue: it.toISOString() }, parent);
			break;

		case '[object Boolean]':
			p = dom.create(!!it ? 'true' : 'false', null, parent);
			break;

		case '[object Null]':
			dom.create('string', { nodeValue: '' }, parent);
			break;

		case '[object String]':
			dom.create('string', { nodeValue: it }, parent);
			break;
	}
}

function walkDict(obj, node) {
	var key, next;

	while (node) {
		if (node.nodeType == xml.ELEMENT_NODE && node.tagName == 'key') {
			key = (node.firstChild.data || '').trim();

			next = node.nextSibling;
			while (next && next.nodeType != xml.ELEMENT_NODE) {
				next = next.nextSibling;
			}

			if (next) {
				if (next.tagName == 'true') {
					obj[key] = true;
					node = next;
				} else if (next.tagName == 'false') {
					obj[key] = false;
					node = next;
				} else if (next.tagName == 'string') {
					obj[key] = '' + (next.firstChild.data || '').trim(); // cast all values as strings
					node = next;
				} else if (next.tagName == 'integer') {
					obj[key] = parseInt(next.firstChild.data) || 0;
					node = next;
				} else if (next.tagName == 'real') {
					obj[key] = parseFloat(next.firstChild.data) || 0;
					node = next;
				} else if (next.tagName == 'date') {
					var d = (next.firstChild.data || '').trim();
					obj[key] = d ? new Date(d) : null; // note: toXml() can't convert a null date back to a <date> tag
					node = next;
				} else if (next.tagName == 'array') {
					walkArray(obj[key] = [], next.firstChild);
					node = next;
				} else if (next.tagName == 'dict') {
					walkDict(obj[key] = {}, next.firstChild);
				}
			}
		}
		node = node.nextSibling;
	}
}

function walkArray(arr, node) {
	while (node) {
		if (node.nodeType == xml.ELEMENT_NODE) {
			switch (node.tagName) {
				case 'string':
					arr.push('' + (node.firstChild.data || '').trim());
					break;

				case 'integer':
					arr.push(parseInt(node.firstChild.data) || 0);
					break;

				case 'array':
					var a = [];
					walkArray(a, node.firstChild);
					arr.push(a);
					break;

				case 'date':
					var d = (node.firstChild.data || '').trim();
					arr.push(d ? new Date(d) : null);
					break;

				case 'dict':
					var obj = {};
					walkDict(obj, node.firstChild);
					arr.push(obj);
					break;

				case 'data':
					arr.push((node.firstChild.data || '').replace(/\s*/g, ''));
			}
		}
		node = node.nextSibling;
	}
}

function toJS(obj, doc) {
	var node = doc.firstChild;

	// the first child should be a <dict> element
	while (node) {
		if (node.nodeType == xml.ELEMENT_NODE && node.tagName == 'dict') {
			node = node.firstChild;
			break;
		}
		node = node.nextSibling;
	}

	node && walkDict(obj, node);
}

function plist(filename) {
	Object.defineProperty(this, 'load', {
		value: function (file) {
			if (!afs.exists(file)) {
				throw new Error('plist file does not exist');
			}
			toJS(this, (new DOMParser().parseFromString(fs.readFileSync(file).toString(), 'text/xml')).documentElement);
			return this;
		}
	});

	Object.defineProperty(this, 'parse', {
		value: function (str) {
			toJS(this, (new DOMParser().parseFromString(str, 'text/xml')).documentElement);
			return this;
		}
	});

	Object.defineProperty(this, 'toXml', {
		value: function (indent) {
			var dom = new DOMParser().parseFromString('<plist version="1.0"/>');

			dom.create = function (tag, attrs, parent, callback) {
				var node = dom.createElement(tag),
					i = indent || 0,
					p = parent;

				attrs && Object.keys(attrs).forEach(function (attr) {
					if (attr == 'nodeValue') {
						node.appendChild(dom.createTextNode(''+attrs[attr]));
					} else {
						attrs[attr] != void 0 && node.setAttribute(attr, ''+attrs[attr]);
					}
				});

				if (p) {
					while (p.parentNode) {
						i++;
						p = p.parentNode;
					}
					parent.appendChild(dom.createTextNode('\r\n' + new Array(i).join('\t')));
				}

				parent && parent.appendChild(node);
				if (callback) {
					callback(node);
					node.appendChild(dom.createTextNode('\r\n' + new Array(i).join('\t')));
				}
				return node;
			};

			toXml(dom, dom.documentElement, this, indent);

			dom.documentElement.appendChild(dom.createTextNode('\r\n'));

			return dom.documentElement;
		}
	});

	Object.defineProperty(this, 'type', {
		value: function (type, value) {
			return new PlistType(type, value);
		}
	});

	Object.defineProperty(this, 'toString', {
		value: function (fmt) {
			if (fmt == 'xml') {
				return '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n' + this.toXml().toString();
			} else if (fmt == 'pretty-json') {
				return JSON.stringify(this, null, '\t');
			} else if (fmt == 'json') {
				return JSON.stringify(this);
			}
			return Object.prototype.toString.call(this);
		}
	});

	Object.defineProperty(this, 'save', {
		value: function (file) {
			if (file) {
				wrench.mkdirSyncRecursive(path.dirname(file));
				fs.writeFileSync(file, this.toString('xml'));
			}
			return this;
		}
	});

	filename && this.load(filename);
}

module.exports = plist;
<commit_msg>[TIMOB-11982] Fixed ISO string formatting in plist date fields.<commit_after>/**
 * Appcelerator Common Library for Node.js
 * Copyright (c) 2012 by Appcelerator, Inc. All Rights Reserved.
 * Please see the LICENSE file for information about licensing.
 */

var fs = require('fs'),
	path = require('path'),
	afs = require('./fs'),
	wrench = require('wrench'),
	xml = require('./xml'),
	DOMParser = require('xmldom').DOMParser;

function PlistType(type, value) {
	this.className = 'PlistType';
	this.type = type;
	this.value = type == 'real' && value == Math.round(value) ? value.toFixed(1) : value;
}

function toXml(dom, parent, it, indent) {
	var i = indent || 0,
		p,
		q = parent,
		type = Object.prototype.toString.call(it);

	while (q.parentNode) {
		i++;
		q = q.parentNode;
	}

	switch (type) {
		case '[object Object]':
			if (it.className == 'PlistType') {
				dom.create(it.type, { nodeValue: it.value }, parent);
			} else {
				p = dom.create('dict', null, parent);
				Object.keys(it).forEach(function (name) {
					dom.create('key', { nodeValue: name }, p);
					toXml(dom, p, it[name], indent);
				});
				p.appendChild(dom.createTextNode('\r\n' + new Array(i).join('\t')));
			}
			break;

		case '[object Array]':
			p = dom.create('array', null, parent);
			it.forEach(function (val) {
				toXml(dom, p, val, indent);
			});
			p.appendChild(dom.createTextNode('\r\n' + new Array(i).join('\t')));
			break;

		case '[object Date]':
			dom.create('date', { nodeValue: it.toISOString().replace(/\.\d+Z$/, 'Z') }, parent);
			break;

		case '[object Boolean]':
			p = dom.create(!!it ? 'true' : 'false', null, parent);
			break;

		case '[object Null]':
			dom.create('string', { nodeValue: '' }, parent);
			break;

		case '[object String]':
			dom.create('string', { nodeValue: it }, parent);
			break;
	}
}

function walkDict(obj, node) {
	var key, next;

	while (node) {
		if (node.nodeType == xml.ELEMENT_NODE && node.tagName == 'key') {
			key = (node.firstChild.data || '').trim();

			next = node.nextSibling;
			while (next && next.nodeType != xml.ELEMENT_NODE) {
				next = next.nextSibling;
			}

			if (next) {
				if (next.tagName == 'true') {
					obj[key] = true;
					node = next;
				} else if (next.tagName == 'false') {
					obj[key] = false;
					node = next;
				} else if (next.tagName == 'string') {
					obj[key] = '' + (next.firstChild.data || '').trim(); // cast all values as strings
					node = next;
				} else if (next.tagName == 'integer') {
					obj[key] = parseInt(next.firstChild.data) || 0;
					node = next;
				} else if (next.tagName == 'real') {
					obj[key] = parseFloat(next.firstChild.data) || 0;
					node = next;
				} else if (next.tagName == 'date') {
					var d = (next.firstChild.data || '').trim();
					obj[key] = d ? new Date(d) : null; // note: toXml() can't convert a null date back to a <date> tag
					node = next;
				} else if (next.tagName == 'array') {
					walkArray(obj[key] = [], next.firstChild);
					node = next;
				} else if (next.tagName == 'dict') {
					walkDict(obj[key] = {}, next.firstChild);
				}
			}
		}
		node = node.nextSibling;
	}
}

function walkArray(arr, node) {
	while (node) {
		if (node.nodeType == xml.ELEMENT_NODE) {
			switch (node.tagName) {
				case 'string':
					arr.push('' + (node.firstChild.data || '').trim());
					break;

				case 'integer':
					arr.push(parseInt(node.firstChild.data) || 0);
					break;

				case 'array':
					var a = [];
					walkArray(a, node.firstChild);
					arr.push(a);
					break;

				case 'date':
					var d = (node.firstChild.data || '').trim();
					arr.push(d ? new Date(d) : null);
					break;

				case 'dict':
					var obj = {};
					walkDict(obj, node.firstChild);
					arr.push(obj);
					break;

				case 'data':
					arr.push((node.firstChild.data || '').replace(/\s*/g, ''));
			}
		}
		node = node.nextSibling;
	}
}

function toJS(obj, doc) {
	var node = doc.firstChild;

	// the first child should be a <dict> element
	while (node) {
		if (node.nodeType == xml.ELEMENT_NODE && node.tagName == 'dict') {
			node = node.firstChild;
			break;
		}
		node = node.nextSibling;
	}

	node && walkDict(obj, node);
}

function plist(filename) {
	Object.defineProperty(this, 'load', {
		value: function (file) {
			if (!afs.exists(file)) {
				throw new Error('plist file does not exist');
			}
			toJS(this, (new DOMParser().parseFromString(fs.readFileSync(file).toString(), 'text/xml')).documentElement);
			return this;
		}
	});

	Object.defineProperty(this, 'parse', {
		value: function (str) {
			toJS(this, (new DOMParser().parseFromString(str, 'text/xml')).documentElement);
			return this;
		}
	});

	Object.defineProperty(this, 'toXml', {
		value: function (indent) {
			var dom = new DOMParser().parseFromString('<plist version="1.0"/>');

			dom.create = function (tag, attrs, parent, callback) {
				var node = dom.createElement(tag),
					i = indent || 0,
					p = parent;

				attrs && Object.keys(attrs).forEach(function (attr) {
					if (attr == 'nodeValue') {
						node.appendChild(dom.createTextNode(''+attrs[attr]));
					} else {
						attrs[attr] != void 0 && node.setAttribute(attr, ''+attrs[attr]);
					}
				});

				if (p) {
					while (p.parentNode) {
						i++;
						p = p.parentNode;
					}
					parent.appendChild(dom.createTextNode('\r\n' + new Array(i).join('\t')));
				}

				parent && parent.appendChild(node);
				if (callback) {
					callback(node);
					node.appendChild(dom.createTextNode('\r\n' + new Array(i).join('\t')));
				}
				return node;
			};

			toXml(dom, dom.documentElement, this, indent);

			dom.documentElement.appendChild(dom.createTextNode('\r\n'));

			return dom.documentElement;
		}
	});

	Object.defineProperty(this, 'type', {
		value: function (type, value) {
			return new PlistType(type, value);
		}
	});

	Object.defineProperty(this, 'toString', {
		value: function (fmt) {
			if (fmt == 'xml') {
				return '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n' + this.toXml().toString();
			} else if (fmt == 'pretty-json') {
				return JSON.stringify(this, null, '\t');
			} else if (fmt == 'json') {
				return JSON.stringify(this);
			}
			return Object.prototype.toString.call(this);
		}
	});

	Object.defineProperty(this, 'save', {
		value: function (file) {
			if (file) {
				wrench.mkdirSyncRecursive(path.dirname(file));
				fs.writeFileSync(file, this.toString('xml'));
			}
			return this;
		}
	});

	filename && this.load(filename);
}

module.exports = plist;
<|endoftext|>


<commit_before>/**
 * KIRKI CONTROL: TYPOGRAPHY
 */
wp.customize.controlConstructor.typography = wp.customize.Control.extend({

	ready: function() {

		var control               = this,
		    fontFamilySelector    = control.selector + ' .font-family select',
		    variantSelector       = control.selector + ' .variant select',
		    subsetSelector        = control.selector + ' .subset select',
		    textTransformSelector = control.selector + ' .text-transform select',
		    hasDefault            = false,
		    firstAvailable        = false,
		    activeItem,
		    value = {},
		    renderSubControl,
		    picker;

		// Make sure everything we're going to need exists.
		_.each( control.params['default'], function( defaultParamValue, param ) {
			if ( false !== defaultParamValue ) {
				value[ param ] = defaultParamValue;
				if ( undefined !== control.setting._value[ param ] ) {
					value[ param ] = control.setting._value[ param ];
				}
			}
		});

		// Renders and refreshes selectize sub-controls.
		renderSubControl = function( fontFamily, sub, startValue ) {

			var subSelector = ( 'variant' === sub ) ? variantSelector : subsetSelector,
			    isStandard = false,
			    subList = {},
			    subValue,
			    subsetValues,
			    subsetValuesArray,
			    subSelectize;

			// Destroy the selectize instance.
			if ( undefined !== jQuery( subSelector ).selectize()[0] ) {
				jQuery( subSelector ).selectize()[0].selectize.destroy();
			}

			// Get all items in the sub-list for the active font-family.
			_.each( kirkiAllFonts, function( font, i ) {

				// Find the font-family we've selected in the global array of fonts.
				if ( fontFamily === font.family ) {

					// Check if this is a standard font or a google-font.
					if ( undefined !== font.isStandard && true === font.isStandard ) {
						isStandard = true;
					}

					subList = font[ sub + 's' ]; // The 's' is for plural (variant/variants, subset/subsets)

				}

			});

			// This is a googlefont, or we're talking subsets.
			if ( false === isStandard || 'subset' !== sub ) {

				// Determine the initial value we have to use
				if ( null === startValue  ) {

					if ( 'variant' === sub ) { // The context here is variants

						// Loop the variants.
						_.each( subList, function( variant ) {

							var defaultValue;

							if ( undefined !== variant.id ) {

								activeItem = value.variant;

							} else {

								defaultValue = 'regular';

								if ( defaultValue === variant.id ) {
									hasDefault = true;
								} else if ( false === firstAvailable ) {
									firstAvailable = variant.id;
								}

							}

						});

					} else if ( 'subset' === sub ) { // The context here is subsets

						subsetValues = {};

						_.each( subList, function( subSet ) {
							if ( null !== value.subset ) {
								_.each( value.subset, function( item ) {
									if ( undefined !== subSet && item === subSet.id ) {
										subsetValues[ item ] = item;
									}
								});
							}
						});

						if ( 0 !== subsetValues.length ) {
							subsetValuesArray = jQuery.map( subsetValues, function( value, index ) {
								return [value];
							});
							activeItem = subsetValuesArray;
						}

					}

					// If we have a valid setting, use it.
					// If not, check if the default value exists.
					// If not, then use the 1st available option.
					subValue = ( undefined !== activeItem ) ? activeItem : ( false !== hasDefault ) ? 'regular' : firstAvailable;

				} else {

					subValue = startValue;

				}

				// Create
				subSelectize = jQuery( subSelector ).selectize({
					maxItems:    ( 'variant' === sub ) ? 1 : null,
					valueField:  'id',
					labelField:  'label',
					searchField: ['label'],
					options:     subList,
					items:       ( 'variant' === sub ) ? [ subValue ] : subValue,
					create:      false,
					plugins:     ( 'variant' === sub ) ? '' : ['remove_button'],
					render: {
						item: function( item, escape ) {
							return '
' + escape( item.label ) + '
';
						},
						option: function( item, escape ) {
							return '
' + escape( item.label ) + '
';
						}
					}
				}).data( 'selectize' );

			}

			if ( true === isStandard ) {

				// Hide unrelated fields on standard fonts.
				control.container.find( '.hide-on-standard-fonts' ).css( 'display', 'none' );
			} else {

				if ( 2 > subList.length ) {

					// If only 1 option is available then there's no reason to show this.
					control.container.find( '.kirki-' + sub + '-wrapper' ).css( 'display', 'none' );
				} else {
					control.container.find( '.kirki-' + sub + '-wrapper' ).css( 'display', 'block' );
				}

			}

		};

		// Render the font-family
		jQuery( fontFamilySelector ).selectize({
			options:     kirkiAllFonts,
			items:       [ control.setting._value['font-family'] ],
			persist:     false,
			maxItems:    1,
			valueField:  'family',
			labelField:  'label',
			searchField: ['family', 'label', 'subsets'],
			create:      false,
			render: {
				item: function( item, escape ) {
					return '
' + escape( item.label ) + '
';
				},
				option: function( item, escape ) {
					return '
' + escape( item.label ) + '
';
				}
			}
		});

		// Render the variants
		// Please note that when the value of font-family changes,
		// this will be destroyed and re-created.
		renderSubControl( value['font-family'], 'variant', value.variant );

		// Render the subsets
		// Please note that when the value of font-family changes,
		// this will be destroyed and re-created.
		renderSubControl( value['font-family'], 'subset', value.subset );

		this.container.on( 'change', '.font-family select', function() {

			// Add the value to the array and set the setting's value
			value['font-family'] = jQuery( this ).val();
			control.setting.set( value );

			// Trigger changes to variants & subsets
			renderSubControl( jQuery( this ).val(), 'variant', null );
			renderSubControl( jQuery( this ).val(), 'subset', null );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		this.container.on( 'change', '.variant select', function() {

			// Add the value to the array and set the setting's value
			value.variant = jQuery( this ).val();
			control.setting.set( value );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		this.container.on( 'change', '.subset select', function() {

			// Add the value to the array and set the setting's value.
			value.subset = jQuery( this ).val();
			control.setting.set( value );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		this.container.on( 'change keyup paste', '.font-size input', function() {

			// Add the value to the array and set the setting's value
			value['font-size'] = jQuery( this ).val();
			control.setting.set( value );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		this.container.on( 'change keyup paste', '.line-height input', function() {

			// Add the value to the array and set the setting's value
			value['line-height'] = jQuery( this ).val();
			control.setting.set( value );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		this.container.on( 'change keyup paste', '.letter-spacing input', function() {

			// Add the value to the array and set the setting's value
			value['letter-spacing'] = jQuery( this ).val();
			control.setting.set( value );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		this.container.on( 'change', '.text-align input', function() {

			// Add the value to the array and set the setting's value.
			value['text-align'] = jQuery( this ).val();
			control.setting.set( value );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		// Text-transform
		jQuery( textTransformSelector ).selectize();
		this.container.on( 'change', '.text-transform select', function() {

			// Add the value to the array and set the setting's value.
			value['text-transform'] = jQuery( this ).val();
			control.setting.set( value );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		picker = this.container.find( '.kirki-color-control' );

		// Change color
		picker.wpColorPicker({
			change: function() {

				setTimeout( function() {

					// Add the value to the array and set the setting's value
					value.color = picker.val();
					control.setting.set( value );

					// Refresh the preview
					wp.customize.previewer.refresh();

				}, 100 );

			}

		});

	}

});
<commit_msg>initial solution (needs tweaking)<commit_after>/**
 * KIRKI CONTROL: TYPOGRAPHY
 */
wp.customize.controlConstructor.typography = wp.customize.Control.extend({

	ready: function() {

		var control               = this,
		    fontFamilySelector    = control.selector + ' .font-family select',
		    variantSelector       = control.selector + ' .variant select',
		    subsetSelector        = control.selector + ' .subset select',
		    textTransformSelector = control.selector + ' .text-transform select',
		    hasDefault            = false,
		    firstAvailable        = false,
		    activeItem,
		    value = {},
		    renderSubControl,
		    picker,
		    newValue;

		// Make sure everything we're going to need exists.
		_.each( control.params['default'], function( defaultParamValue, param ) {
			if ( false !== defaultParamValue ) {
				value[ param ] = defaultParamValue;
				if ( undefined !== control.setting._value[ param ] ) {
					value[ param ] = control.setting._value[ param ];
				}
			}
		});

		// Renders and refreshes selectize sub-controls.
		renderSubControl = function( fontFamily, sub, startValue ) {

			var subSelector = ( 'variant' === sub ) ? variantSelector : subsetSelector,
			    isStandard = false,
			    subList = {},
			    subValue,
			    subsetValues,
			    subsetValuesArray,
			    subSelectize;

			// Destroy the selectize instance.
			if ( undefined !== jQuery( subSelector ).selectize()[0] ) {
				jQuery( subSelector ).selectize()[0].selectize.destroy();
			}

			// Get all items in the sub-list for the active font-family.
			_.each( kirkiAllFonts, function( font, i ) {

				// Find the font-family we've selected in the global array of fonts.
				if ( fontFamily === font.family ) {

					// Check if this is a standard font or a google-font.
					if ( undefined !== font.isStandard && true === font.isStandard ) {
						isStandard = true;
					}

					subList = font[ sub + 's' ]; // The 's' is for plural (variant/variants, subset/subsets)

				}

			});

			// This is a googlefont, or we're talking subsets.
			if ( false === isStandard || 'subset' !== sub ) {

				// Determine the initial value we have to use
				if ( null === startValue  ) {

					if ( 'variant' === sub ) { // The context here is variants

						// Loop the variants.
						_.each( subList, function( variant ) {

							var defaultValue;

							if ( undefined !== variant.id ) {

								activeItem = value.variant;

							} else {

								defaultValue = 'regular';

								if ( defaultValue === variant.id ) {
									hasDefault = true;
								} else if ( false === firstAvailable ) {
									firstAvailable = variant.id;
								}

							}

						});

					} else if ( 'subset' === sub ) { // The context here is subsets

						subsetValues = {};

						_.each( subList, function( subSet ) {
							if ( null !== value.subset ) {
								_.each( value.subset, function( item ) {
									if ( undefined !== subSet && item === subSet.id ) {
										subsetValues[ item ] = item;
									}
								});
							}
						});

						if ( 0 !== subsetValues.length ) {
							subsetValuesArray = jQuery.map( subsetValues, function( value, index ) {
								return [value];
							});
							activeItem = subsetValuesArray;
						}

					}

					// If we have a valid setting, use it.
					// If not, check if the default value exists.
					// If not, then use the 1st available option.
					subValue = ( undefined !== activeItem ) ? activeItem : ( false !== hasDefault ) ? 'regular' : firstAvailable;

				} else {

					subValue = startValue;

				}

				// Create
				subSelectize = jQuery( subSelector ).selectize({
					maxItems:    ( 'variant' === sub ) ? 1 : null,
					valueField:  'id',
					labelField:  'label',
					searchField: ['label'],
					options:     subList,
					items:       ( 'variant' === sub ) ? [ subValue ] : subValue,
					create:      false,
					plugins:     ( 'variant' === sub ) ? '' : ['remove_button'],
					render: {
						item: function( item, escape ) {
							return '
' + escape( item.label ) + '
';
						},
						option: function( item, escape ) {
							return '
' + escape( item.label ) + '
';
						}
					}
				}).data( 'selectize' );

			}

			if ( true === isStandard ) {

				// Hide unrelated fields on standard fonts.
				control.container.find( '.hide-on-standard-fonts' ).css( 'display', 'none' );
			} else {

				if ( 2 > subList.length ) {

					// If only 1 option is available then there's no reason to show this.
					control.container.find( '.kirki-' + sub + '-wrapper' ).css( 'display', 'none' );
				} else {
					control.container.find( '.kirki-' + sub + '-wrapper' ).css( 'display', 'block' );
				}

			}

		};

		// Render the font-family
		jQuery( fontFamilySelector ).selectize({
			options:     kirkiAllFonts,
			items:       [ control.setting._value['font-family'] ],
			persist:     false,
			maxItems:    1,
			valueField:  'family',
			labelField:  'label',
			searchField: ['family', 'label', 'subsets'],
			create:      false,
			render: {
				item: function( item, escape ) {
					return '
' + escape( item.label ) + '
';
				},
				option: function( item, escape ) {
					return '
' + escape( item.label ) + '
';
				}
			}
		});

		// Render the variants
		// Please note that when the value of font-family changes,
		// this will be destroyed and re-created.
		renderSubControl( value['font-family'], 'variant', value.variant );

		// Render the subsets
		// Please note that when the value of font-family changes,
		// this will be destroyed and re-created.
		renderSubControl( value['font-family'], 'subset', value.subset );

		this.container.on( 'change', '.font-family select', function() {

			// Add the value to the array and set the setting's value
			value['font-family'] = jQuery( this ).val();

			newValue = {};
			_.each( value , function( newSubValue, i ) {
				newValue[ i ] = newSubValue;
			});

			control.setting.set( newValue );

			// Trigger changes to variants & subsets
			renderSubControl( jQuery( this ).val(), 'variant', null );
			renderSubControl( jQuery( this ).val(), 'subset', null );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		this.container.on( 'change', '.variant select', function() {

			// Add the value to the array and set the setting's value
			value.variant = jQuery( this ).val();

			newValue = {};
			_.each( value , function( newSubValue, i ) {
				newValue[ i ] = newSubValue;
			});

			control.setting.set( newValue );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		this.container.on( 'change', '.subset select', function() {

			// Add the value to the array and set the setting's value.
			value.subset = jQuery( this ).val();

			newValue = {};
			_.each( value , function( newSubValue, i ) {
				newValue[ i ] = newSubValue;
			});

			control.setting.set( newValue );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		this.container.on( 'change keyup paste', '.font-size input', function() {

			// Add the value to the array and set the setting's value
			value['font-size'] = jQuery( this ).val();

			newValue = {};
			_.each( value , function( newSubValue, i ) {
				newValue[ i ] = newSubValue;
			});

			control.setting.set( newValue );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		this.container.on( 'change keyup paste', '.line-height input', function() {

			// Add the value to the array and set the setting's value
			value['line-height'] = jQuery( this ).val();

			newValue = {};
			_.each( value , function( newSubValue, i ) {
				newValue[ i ] = newSubValue;
			});

			control.setting.set( newValue );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		this.container.on( 'change keyup paste', '.letter-spacing input', function() {

			// Add the value to the array and set the setting's value
			value['letter-spacing'] = jQuery( this ).val();

			newValue = {};
			_.each( value , function( newSubValue, i ) {
				newValue[ i ] = newSubValue;
			});

			control.setting.set( newValue );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		this.container.on( 'change', '.text-align input', function() {

			// Add the value to the array and set the setting's value.
			value['text-align'] = jQuery( this ).val();

			newValue = {};
			_.each( value , function( newSubValue, i ) {
				newValue[ i ] = newSubValue;
			});

			control.setting.set( newValue );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		// Text-transform
		jQuery( textTransformSelector ).selectize();
		this.container.on( 'change', '.text-transform select', function() {

			// Add the value to the array and set the setting's value.
			value['text-transform'] = jQuery( this ).val();

			newValue = {};
			_.each( value , function( newSubValue, i ) {
				newValue[ i ] = newSubValue;
			});

			control.setting.set( newValue );

			// Refresh the preview
			wp.customize.previewer.refresh();

		});

		picker = this.container.find( '.kirki-color-control' );

		// Change color
		picker.wpColorPicker({
			change: function() {

				setTimeout( function() {

					// Add the value to the array and set the setting's value
					value.color = picker.val();

					newValue = {};
					_.each( value , function( newSubValue, i ) {
						newValue[ i ] = newSubValue;
					});

					control.setting.set( newValue );

					// Refresh the preview
					wp.customize.previewer.refresh();

				}, 100 );

			}

		});

	}

});
<|endoftext|>