<commit_before>angular.module('ps.inputTime', [])
.value('psInputTimeConfig', {
        minuteStep : 5,
        minDate : null,
        maxDate : null,
        fixedDay: true,
        format: 'hh:mma'
    })
.directive("psInputTime", ['$filter', 'psInputTimeConfig', '$parse', function($filter, psInputTimeConfig, $parse) {
    var temp12hr = '((0?[0-9])|(1[0-2]))(:|\s)([0-5][0-9])[apAP][mM]',
        temp12noColon = '((0?[0-9])|(1[0-2]))([0-5][0-9])[apAP][mM]',
        temp24hr = '([01]?[0-9]|2[0-3])[:;][0-5][0-9]',
        temp24noColon = '(2[0-3]|[01]?[0-9])([0-5][0-9])';
    var customFloor = function(value, roundTo) {
        return Math.floor(value / psInputConfig.minuteStep) * psInputConfig.minuteStep;
    };
    var timeTest12hr = new RegExp('^' + temp12hr + '$', ['i']),
        timeTest12noColon = new RegExp('^' + temp12noColon + '$', ['i']),
        timeTest24hr = new RegExp('^' + temp24hr + '$', ['i']),
        timeTest24noColon = new RegExp('^' + temp24noColon + '$', ['i']);
    return {
        restrict: "A",
        require: '?^ngModel',
        scope: {},
        link: function(scope, element, attrs, ngModel) {
            if (!ngModel) return; // do nothing if no ng-model

            ngModel.$render = function () {
               element.val(formatter(ngModel.$modelValue));
            };

            var minuteStep = getValue(attrs.minuteStep, psInputTimeConfig.minuteStep),
                fixedDay = getValue(attrs.fixedDay, psInputTimeConfig.fixedDay),
                timeFormat = attrs.format || psInputTimeConfig.format,
                maxDate = null,
                minDate = null;

            function getValue(value, defaultValue) {
                return angular.isDefined(value) ? scope.$parent.$eval(value) : defaultValue;
            }

            if(attrs.min || attrs.max){
                fixedDay = false;
            }

            function checkMinMaxValid(){
              if(minDate !== null && ngModel.$modelValue < minDate){
                  ngModel.$setValidity('time-min', false);
              }else if (minDate !== null) ngModel.$setValidity('time-min', true);

              if(maxDate !== null && ngModel.$modelValue > maxDate){
                  ngModel.$setValidity('time-max', false);
              } else if (maxDate !== null) ngModel.$setValidity('time-max', true);

            }

            if (attrs.max) {
                scope.$parent.$watch($parse(attrs.max), function(value) {
                  maxDate = value ? new Date(value) : null;
                  checkMinMaxValid();
                });
            }

            if (attrs.min) {
                scope.$parent.$watch($parse(attrs.min), function(value) {
                  minDate = value ? new Date(value) : null;
                  checkMinMaxValid();
                });
            }

            var reservedKey = false;

            element.on('keydown', function(e) {
                reservedKey = false;
                switch (e.keyCode) {
                    case 37:
                        // left button hit
                        if(verifyFormat()){
                            tabBackward(e);
                            reservedKey = true;
                        }
                        break;
                    case 38:
                        // up button hit
                        if(verifyFormat()) {
                            addTime();
                            reservedKey = true;
                        }
                        break;
                    case 39:
                        // right button hit
                        if(verifyFormat()){
                            tabForward(e);
                            reservedKey = true;

                        }
                        break;
                    case 40:
                        // down button hit
                        if(verifyFormat()) {
                            subtractTime();
                            reservedKey = true;
                        }
                        break;
                    case 9:
                        // TAB

                        if(verifyFormat()){
                            if(e.shiftKey){
                                if(getSelectionPoint() != 'hour') {
                                    reservedKey = true;
                                    tabBackward(e);
                                }
                            } else{
                                if(getSelectionPoint() != 'meridian') {
                                    reservedKey = true;
                                    tabForward(e);
                                }
                            }
                        }

                        break;
                    default:
                        // e.preventDefault();
                        break;
                }
                if(reservedKey){
                    e.preventDefault();
                }
            }).on('keyup blur', function(){
                if(checkTimeFormat(element.val()) != 'invalid' && !reservedKey){
                    scope.$apply(function (){
                        ngModel.$setViewValue(createDateFromTime(element.val(), ngModel.$modelValue));
                    });
                }

            }).on('click', function() {

                selectTime(getSelectionPoint());

            });

            function verifyFormat(){
                if(checkTimeFormat( element.val() ) == '12hr') return true;
                else if (element.val() === ''){
                    element.val(formatter(getDefaultDate()));
                    ngModel.$setViewValue(getDefaultDate());
                    setTimeout(function() {
                        selectTime('hour');
                    }, 0);
                    return true;
                }
                else if (checkTimeFormat( element.val() ) != 'invalid') {
                    element.val(formatter(ngModel.$modelValue));
                    ngModel.$setViewValue(getDefaultDate());
                    setTimeout(function() {
                        selectTime('hour');
                    }, 0);
                    return true;
                } else return false;
            }

            function selectTime(part) {
                if (part == 'hour') {
                    setTimeout(function() {
                        element[0].setSelectionRange(0, 2);
                    }, 0);
                } else if (part == 'minute') {
                    setTimeout(function() {
                        element[0].setSelectionRange(3, 5);
                    }, 0);
                } else {
                    setTimeout(function() {
                        element[0].setSelectionRange(5, 7);
                    }, 0);
                }
            }

            function getSelectionPoint() {
                var pos = element.prop("selectionStart");
                if(element.val().length < 1){
                    return 'hour';
                }
                if (pos < 3) {
                    return 'hour';
                } else if (pos < 5) {
                    return 'minute';
                } else if (pos < 8) {
                    return 'meridian';
                } else return 'unknown';
            }

            function tabForward() {
                var cspot = getSelectionPoint();
                if (cspot == 'hour') {
                    selectTime('minute');
                } else if (cspot == 'minute') {
                    selectTime('meridian');
                } else {
                    selectTime('hour');
                }
            }

            function tabBackward(e) {
                var cspot = getSelectionPoint();
                if (cspot == 'meridian') {
                    selectTime('minute');
                    e.preventDefault();
                } else if (cspot == 'minute') {
                    selectTime('hour');
                    e.preventDefault();
                } else {
                    selectTime('meridian');
                }
            }

            function getDefaultDate(){
                if(minDate !== null) return new Date(minDate);
                else if (maxDate !== null) return new Date(maxDate);
                else return new Date();
            }

            function parser(value) {

                if(value){
                    if(angular.isDate(value)){
                        checkMinMaxValid();
                        ngModel.$setValidity('time', true);

                        if(minDate !== null && value < minDate) value = minDate;
                        if(maxDate !== null && value > maxDate) value = maxDate;

                        return value;

                    } else{

                        ngModel.$setValidity('time', false);
                        return ngModel.$modelValue;
                    }

                }

            }

            ngModel.$parsers.push(parser);

            function formatter(value) {

                if (value) {
                    return $filter('date')(value, timeFormat);
                }
            }

            ngModel.$formatters.push(formatter);

            function createDateFromTime(time,cdate){
                if(isNaN(cdate)){
                    cdate = getDefaultDate();
                }
                var ct = checkTimeFormat(time),
                        minutes, hours, ampm, sHours, sMinutes;
                if(ct == '12hr'){
                    hours = Number(time.match(/^(\d+)/)[1]);
                    minutes = Number(time.match(/:(\d+)/)[1]);
                    AMPM = time.match(/[apAP][mM]/)[0];
                    if(AMPM.toUpperCase() == "PM" && hours<12) hours = hours+12;
                    if(AMPM.toUpperCase() == "AM" && hours==12) hours = hours-12;
                } else if (ct == '24hr'){
                    hours = time.split(/[;:]/)[0];
                    minutes = time.split(/[;:]/)[1];
                } else if (ct == '24nc') {
                    hours = time.length == 4 ? time.substr(0,2) : time.substr(0,1);
                    minutes = time.substr(-2);
                } else if (ct == '12nc') {
                    timeAsNumber = Number(time.match(/^(\d+)/)[1]).toString();
                    hours = timeAsNumber.length == 4 ? Number(timeAsNumber.substr(0,2)) : Number(timeAsNumber.substr(0,1));
                    minutes = Number(timeAsNumber.substr(-2));
                    AMPM = time.match(/[apAP][mM]/)[0];
                    if(AMPM.toUpperCase() == "PM" && hours<12) hours = hours+12;
                    if(AMPM.toUpperCase() == "AM" && hours==12) hours = hours-12;
                } else {
                    return 'invalid';
                }
                sHours = hours.toString();
                sMinutes = minutes.toString();
                console.log('sMinutes', sMinutes, 'sHours', sHours);
                if(hours<10) sHours = "0" + sHours;
                if(minutes<10) sMinutes = "0" + sMinutes;
                cdate.setHours(sHours,sMinutes);
                return new Date(cdate);
            }

            function checkTimeFormat(value){
                if(timeTest12hr.test(value)) return '12hr';
                else if (timeTest24hr.test(value)) return '24hr';
                else if (timeTest24noColon.test(value)) return '24nc';
                else if (timeTest12noColon.test(value)) return '12nc';
                else return 'invalid';
            }

            function addTime() {
                var cPoint = getSelectionPoint();
                if (cPoint == 'hour') {
                    addMinutes(60);
                } else if (cPoint == 'minute') {
                     addMinutes(minuteStep);
                } else if (cPoint == 'meridian') {
                    if ((ngModel.$modelValue ? ngModel.$modelValue : getDefaultDate()).getHours > 12) {
                        addMinutes(-720);
                    } else {
                        addMinutes(720);
                    }
                }
                selectTime(cPoint);
            }

            function subtractTime() {
                var cPoint = getSelectionPoint();
                if (cPoint == 'hour') {
                    addMinutes(-60);
                } else if (cPoint == 'minute') {
                    addMinutes(-1);
                } else if (cPoint == 'meridian') {
                    if ((ngModel.$modelValue ? ngModel.$modelValue : getDefaultDate()).getHours > 12) {
                        addMinutes(720);
                    } else {
                        addMinutes(-720);
                    }
                }
                selectTime(cPoint);
            }

            function addMinutes(minutes){
                selected = ngModel.$modelValue ? new Date(ngModel.$modelValue) : getDefaultDate();
                dt = new Date(selected.getTime() + minutes * 60000);
                if(fixedDay === true || fixedDay == 'true'){
                    dt = selected.setHours(dt.getHours(), dt.getMinutes());
                    dt = new Date(dt);
                }
                scope.$apply(function (){
                    ngModel.$setViewValue(dt);
                });
                element.val(formatter(ngModel.$modelValue));
            }

        }
    };
}]);
<commit_msg>Update ps-input-time.js<commit_after>  angular.module('ps.inputTime', [])
    .value('psInputTimeConfig', {
      minuteStep: 5,
      minDate: null,
      maxDate: null,
      fixedDay: true,
      format: 'hh:mma'
    })
    .directive("psInputTime", ['$filter', 'psInputTimeConfig', '$parse', function ($filter,
        psInputTimeConfig, $parse) {
        //var temp12hr = '((0?[0-9]{1})|(1[0-2]{1}))(:|\s)([0-5][0-9])[apAP][mM]',
        var temp12hr = '((0[1-9])|(1[0-2]))(:)([0-5][0-9])[apAP][mM]',
        temp12hr2 = '(([])|(0[1-9]?)|(1[0-2]?))(:)([0-5][0-9]?)[apAP][mM]',
          //var temp12hr ='^(00|0[0-9]|1[012]):[0-5][0-9] ?((a|p)m|(A|P)M)$',
          temp12noColon = '((0?[0-9])|(1[0-2]))([0-5][0-9])[apAP][mM]',
          temp24hr = '([01]?[0-9]|2[0-3])[:;][0-5][0-9]',
          temp24noColon = '(2[0-3]|[01]?[0-9])([0-5][0-9])';
        var customFloor = function (value, roundTo) {
          return Math.floor(value / psInputConfig.minuteStep) * psInputConfig.minuteStep;
        };
        var timeTest12hr = new RegExp('^' + temp12hr + '$', ['i']),
          timeTest12noColon = new RegExp('^' + temp12noColon + '$', ['i']),
          timeTest24hr = new RegExp('^' + temp24hr + '$', ['i']),
          timeTest24noColon = new RegExp('^' + temp24noColon + '$', ['i']),
          timeTest12hr2 = new RegExp('^' + temp12hr2 + '$', ['i']);
        return {
          restrict: "A",
          require: '?^ngModel',
          scope: {},
          link: function (scope, element, attrs, ngModel) {
            if (!ngModel) return; // do nothing if no ng-model

            ngModel.$render = function () {
              element.val(formatter(ngModel.$modelValue));
            };

            var minuteStep = getValue(attrs.minuteStep, psInputTimeConfig.minuteStep),
              fixedDay = getValue(attrs.fixedDay, psInputTimeConfig.fixedDay),
              timeFormat = attrs.format || psInputTimeConfig.format,
              maxDate = null,
              minDate = null;

            function getValue(value, defaultValue) {
              return angular.isDefined(value) ? scope.$parent.$eval(value) : defaultValue;
            }

            if (attrs.min || attrs.max) {
              fixedDay = false;
            }

            function checkMinMaxValid() {
              if (minDate !== null && ngModel.$modelValue < minDate) {
                ngModel.$setValidity('time-min', false);
              } else if (minDate !== null) ngModel.$setValidity('time-min', true);

              if (maxDate !== null && ngModel.$modelValue > maxDate) {
                ngModel.$setValidity('time-max', false);
              } else if (maxDate !== null) ngModel.$setValidity('time-max', true);

            }

            if (attrs.max) {
              scope.$parent.$watch($parse(attrs.max), function (value) {
                maxDate = value ? new Date(value) : null;
                checkMinMaxValid();
              });
            }

            if (attrs.min) {
              scope.$parent.$watch($parse(attrs.min), function (value) {
                minDate = value ? new Date(value) : null;
                checkMinMaxValid();
              });
            }

            var reservedKey = false;

            element.on('keydown', function (e) {

              reservedKey = false;
              switch (e.keyCode) {
                case 37:
                  // left button hit
                  tabBackward(e);
                  break;
                case 38:
                  // up button hit
                  addTime();
                  break;
                case 39:
                  // right button hit
                  tabForward(e);
                  break;
                case 40:
                  // down button hit
                  subtractTime();
                  break;
                case 9:
                  // TAB
                  if (e.shiftKey) {
                    if (getSelectionPoint() != 'hour') {
                      reservedKey = true;
                      tabBackward(e);
                    }
                  } else {
                    if (getSelectionPoint() != 'meridian') {
                      reservedKey = true;
                      tabForward(e);
                    }
                  }
                  break;
              }

              setTimeout(function () {
                var verified = false;
                switch (e.keyCode) {
                  case 37:
                    // left button hit
                    if (verifyFormat()) {
                      verified = true;
                      reservedKey = true;
                    }
                    break;
                  case 38:
                    // up button hit
                    if (verifyFormat()) {
                      verified = true;
                      reservedKey = true;
                    }
                    break;
                  case 39:
                    // right button hit
                    if (verifyFormat()) {
                      verified = true;
                      reservedKey = true;
                    }
                    break;
                  case 40:
                    // down button hit
                    if (verifyFormat()) {
                      verified = true;
                      reservedKey = true;
                    }
                    break;
                  case 9:
                    // TAB
                    if (verifyFormat()) {
                      verified = true;
                    }
                    break;
                  default:
                    // this is the value after the keypress
                    var afterVal = element.val();

                    if (afterVal.length > 7) {
                      verified = false;
                    } else if (afterVal.length === 7) {
                      if (timeTest12hr.test(afterVal)) {
                        verified = true;
                      } else {
                        verified = false;
                      }
                    } else {
                      if(timeTest12hr2.test(afterVal)){
                        verified = true;
                      } else {
                        verified = false;
                      }
                    }
                    //e.preventDefault();
                    break;
                }
                if (!verified) {
                  element.val(formatter(ngModel.$modelValue));
                } else{
                  ngModel.$setViewValue(createDateFromTime(element.val(), ngModel.$modelValue));
                }
              }, 0);
              if (reservedKey) {
                e.preventDefault();
              }
            }).on('blur', function () {
              setTimeout(function () {
                if (checkTimeFormat(element.val()) != 'invalid' && !reservedKey) {
                  scope.$apply(function () {
                    ngModel.$setViewValue(createDateFromTime(element.val(), ngModel.$modelValue));
                  });
                } else {
                  element.val(formatter(ngModel.$modelValue));
                }

              }, 0);

            }).on('click', function () {

              selectTime(getSelectionPoint());

            });

            function verifyFormat() {
              if (checkTimeFormat(element.val()) == '12hr')
                return true;
              else if (element.val() === '') {
                element.val(formatter(getDefaultDate()));
                ngModel.$setViewValue(getDefaultDate());
                setTimeout(function () {
                  selectTime('hour');
                }, 0);
                return true;
              }
              else if (checkTimeFormat(element.val()) != 'invalid') {
                element.val(formatter(ngModel.$modelValue));
                ngModel.$setViewValue(getDefaultDate());
                setTimeout(function () {
                  selectTime('hour');
                }, 0);
                return true;
              } else return false;
            }

            function selectTime(part) {
              if (part == 'hour') {
                setTimeout(function () {
                  element[0].setSelectionRange(0, 2);
                }, 0);
             } else if (part == 'minute') {
                setTimeout(function () {
                  element[0].setSelectionRange(3, 5);
                }, 0);
              } else {
                setTimeout(function () {
                  element[0].setSelectionRange(5, 7);
                }, 0);
              }
            }

            function getSelectionPoint() {
              var pos = element.prop("selectionStart");
              if (element.val().length < 1) {
                return 'hour';
              }
              if (pos < 3) {
                return 'hour';
              } else if (pos < 5) {
                return 'minute';
              } else if (pos < 8) {
                return 'meridian';
              } else return 'unknown';
            }

            function tabForward() {
              var cspot = getSelectionPoint();
              if (cspot == 'hour') {
                selectTime('minute');
              } else if (cspot == 'minute') {
                selectTime('meridian');
              } else {
                selectTime('hour');
              }
            }

            function tabBackward(e) {
              var cspot = getSelectionPoint();
              if (cspot == 'meridian') {
                selectTime('minute');
                e.preventDefault();
              } else if (cspot == 'minute') {
                selectTime('hour');
                e.preventDefault();
              } else {
                selectTime('meridian');
             }
            }

            function getDefaultDate() {
              if (minDate !== null) return new Date(minDate);
              else if (maxDate !== null) return new Date(maxDate);
              else return new Date();
            }

            function parser(value) {

              if (value) {
                if (angular.isDate(value)) {
                  checkMinMaxValid();
                  ngModel.$setValidity('time', true);

                  if (minDate !== null && value < minDate) value = minDate;
                  if (maxDate !== null && value > maxDate) value = maxDate;

                  return value;

                } else {

                  ngModel.$setValidity('time', false);
                  return ngModel.$modelValue;
                }

              }

            }

            ngModel.$parsers.push(parser);

            function formatter(value) {

              if (value) {
                return $filter('date')(value, timeFormat);
              }
            }

            ngModel.$formatters.push(formatter);

            function createDateFromTime(time, cdate) {
              if (isNaN(cdate)) {
                cdate = getDefaultDate();
              }
              var ct = checkTimeFormat(time),
                minutes, hours, ampm, sHours, sMinutes;
              if (ct == '12hr') {
                hours = Number(time.match(/^(\d+)/)[1]);
                minutes = Number(time.match(/:(\d+)/)[1]);
                AMPM = time.match(/[apAP][mM]/)[0];
                if (AMPM.toUpperCase() == "PM" && hours < 12) hours = hours + 12;
                if (AMPM.toUpperCase() == "AM" && hours == 12) hours = hours - 12;
              } else if (ct == '24hr') {
                hours = time.split(/[;:]/)[0];
                minutes = time.split(/[;:]/)[1];
              } else if (ct == '24nc') {
                hours = time.length == 4 ? time.substr(0, 2) : time.substr(0, 1);
                minutes = time.substr(-2);
              } else if (ct == '12nc') {
                timeAsNumber = Number(time.match(/^(\d+)/)[1]).toString();
                hours = timeAsNumber.length == 4 ? Number(timeAsNumber.substr(0, 2)) : Number(
                  timeAsNumber.substr(0, 1));
                minutes = Number(timeAsNumber.substr(-2));
                AMPM = time.match(/[apAP][mM]/)[0];
                if (AMPM.toUpperCase() == "PM" && hours < 12) hours = hours + 12;
                if (AMPM.toUpperCase() == "AM" && hours == 12) hours = hours - 12;
              } else {
                return 'invalid';
              }
              sHours = hours.toString();
              sMinutes = minutes.toString();
              console.log('sMinutes', sMinutes, 'sHours', sHours);
              if (hours < 10) sHours = "0" + sHours;
              if (minutes < 10) sMinutes = "0" + sMinutes;
              cdate.setHours(sHours, sMinutes);
              return new Date(cdate);
            }

            function checkTimeFormat(value) {
              if (timeTest12hr.test(value)) return '12hr';
              else if (timeTest24hr.test(value)) return '24hr';
              else if (timeTest24noColon.test(value)) return '24nc';
              else if (timeTest12noColon.test(value)) return '12nc';
              else return 'invalid';
            }

            function addTime() {
              var cPoint = getSelectionPoint();
              if (cPoint == 'hour') {
                addMinutes(60);
              } else if (cPoint == 'minute') {
                addMinutes(minuteStep);
              } else if (cPoint == 'meridian') {
                if ((ngModel.$modelValue ? ngModel.$modelValue : getDefaultDate()).getHours > 12) {
                  addMinutes(-720);
                } else {
                  addMinutes(720);
                }
              }
              selectTime(cPoint);
            }

            function subtractTime() {
              var cPoint = getSelectionPoint();
              if (cPoint == 'hour') {
                addMinutes(-60);
              } else if (cPoint == 'minute') {
                addMinutes(-1);
              } else if (cPoint == 'meridian') {
                if ((ngModel.$modelValue ? ngModel.$modelValue : getDefaultDate()).getHours > 12) {
                  addMinutes(720);
                } else {
                  addMinutes(-720);
                }
              }
              selectTime(cPoint);
            }

            function addMinutes(minutes) {
              selected = ngModel.$modelValue ? new Date(ngModel.$modelValue) : getDefaultDate();
              dt = new Date(selected.getTime() + minutes * 60000);
              if (fixedDay === true || fixedDay == 'true') {
                dt = selected.setHours(dt.getHours(), dt.getMinutes());
                dt = new Date(dt);
              }
              scope.$apply(function () {
                ngModel.$setViewValue(dt);
              });
              element.val(formatter(ngModel.$modelValue));
            }

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


<commit_before>var Plugin = {
	host: 'http://localhost:8888'
}

Plugin.getConfig = function() {
	var hashes = window.location.hash.split('#')[1].split('&'),
		args = {attr:{}}

	hashes.forEach(function(val) {
		var v = val.split('=')
		args[v[0]] = JSON.parse(decodeURIComponent(v[1]))
	})

	return args
}

Plugin.sendConfig = function(attr) {
	var data = {attr: attr}
	console.log(data)
	parent.postMessage(JSON.stringify(data), Plugin.host)
}<commit_msg>Small fix<commit_after>var Plugin = {
	host: 'http://35cb0e44.ngrok.com'
}

Plugin.getConfig = function() {
	var hashes = window.location.hash.split('#')[1].split('&'),
		args = {attr:{}}

	hashes.forEach(function(val) {
		var v = val.split('=')
		args[v[0]] = JSON.parse(decodeURIComponent(v[1]))
	})

	return args
}

Plugin.sendConfig = function(attr) {
	var data = {attr: attr}
	console.log(data)
	parent.postMessage(JSON.stringify(data), Plugin.host)
}<|endoftext|>


<commit_before>function MYSYSTEMSTATE(state, timestamp) {
	this.type = "mysystem";
	this.data = state;

	if(timestamp == null) {
		this.timestamp = new Date().getTime();
	} else {
		this.timestamp = timestamp;
	}
};

/**
 * Gets the xml format for the student data
 * @return an xml string with the student data
 */
MYSYSTEMSTATE.prototype.getDataXML = function() {
	var dataXML = "<data>" + this.data + "</data>";
	dataXML += "<timestamp>" + this.timestamp + "</timestamp>";
	return dataXML;
}

/**
 * Creates a state object from an xml object
 * @param stateXML an xml object
 * @return an MYSYSTEMSTATE object
 */
MYSYSTEMSTATE.prototype.parseDataXML = function(stateXML) {
	//obtain the data element
	var dataElement = stateXML.getElementsByTagName("data")[0];

	//obtain the timestamp element
	var timestampElement = stateXML.getElementsByTagName("timestamp")[0];

	//check if both elements exist
	if(dataElement != null && timestampElement != null) {
		//obtain the values for the data and timestamp
		var data = dataElement.textContent;
		var timestamp = timestampElement.textContent;

		//create an MYSYSTEMSTATE
		var state = new MYSYSTEMSTATE(data, timestamp);
		return state;
	} else {
		return null;
	}
}

/**
 * Takes in a state JSON object and returns an MYSYSTEMSTATE object
 * @param stateJSONObj a state JSON object
 * @return a MYSYSTEMSTATE object
 */
MYSYSTEMSTATE.prototype.parseDataJSONObj = function(stateJSONObj) {
	//create a new MYSYSTEMSTATE object
	var mysystemState = new MYSYSTEMSTATE();

	//set the attributes of the MYSYSTEMSTATE object
	mysystemState.data = stateJSONObj.data;
	mysystemState.timestamp = stateJSONObj.timestamp;

	//return the MYSYSTEMSTATE object
	return mysystemState;
}

/**
 * Get the student work.
 * @return the student's work
 */
MYSYSTEMSTATE.prototype.getStudentWork = function() {
	return this.data;
}

//used to notify scriptloader that this script has finished loading
if(typeof eventManager != 'undefined'){
	eventManager.fire('scriptLoaded', 'vle/node/mysystem/mysystemstate.js');
};<commit_msg>ReJigger namespace.<commit_after>function MySystemSCState(state, timestamp) {
	this.type = "mysystem";
	this.data = state;

	if(timestamp == null) {
		this.timestamp = new Date().getTime();
	} else {
		this.timestamp = timestamp;
	}
};

/**
 * Gets the xml format for the student data
 * @return an xml string with the student data
 */
MySystemSCState.prototype.getDataXML = function() {
	var dataXML = "<data>" + this.data + "</data>";
	dataXML += "<timestamp>" + this.timestamp + "</timestamp>";
	return dataXML;
}

/**
 * Creates a state object from an xml object
 * @param stateXML an xml object
 * @return an MySystemSCState object
 */
MySystemSCState.prototype.parseDataXML = function(stateXML) {
	//obtain the data element
	var dataElement = stateXML.getElementsByTagName("data")[0];

	//obtain the timestamp element
	var timestampElement = stateXML.getElementsByTagName("timestamp")[0];

	//check if both elements exist
	if(dataElement != null && timestampElement != null) {
		//obtain the values for the data and timestamp
		var data = dataElement.textContent;
		var timestamp = timestampElement.textContent;

		//create an MySystemSCState
		var state = new MySystemSCState(data, timestamp);
		return state;
	} else {
		return null;
	}
}

/**
 * Takes in a state JSON object and returns an MySystemSCState object
 * @param stateJSONObj a state JSON object
 * @return a MySystemSCState object
 */
MySystemSCState.prototype.parseDataJSONObj = function(stateJSONObj) {
	//create a new MySystemSCState object
	var MySystemSCState = new MySystemSCState();

	//set the attributes of the MySystemSCState object
	MySystemSCState.data = stateJSONObj.data;
	MySystemSCState.timestamp = stateJSONObj.timestamp;

	//return the MySystemSCState object
	return MySystemSCState;
}

/**
 * Get the student work.
 * @return the student's work
 */
MySystemSCState.prototype.getStudentWork = function() {
	return this.data;
}

//used to notify scriptloader that this script has finished loading
if(typeof eventManager != 'undefined'){
	eventManager.fire('scriptLoaded', 'vle/node/mysystem/MySystemSCState.js');
};
<|endoftext|>


<commit_before>function smf_fileUpload(oOptions)
{
	var previewNode = document.querySelector('#au-template');
	previewNode.id = '';
	var previewTemplate = previewNode.parentNode.innerHTML;
	previewNode.parentNode.removeChild(previewNode);

	// Default values in case oOptions isn't defined.
	var dOptions = {
		url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=add;' + smf_session_var + '=' + smf_session_id,
		parallelUploads : 1,
		filesizeBase:1000,
		paramName: 'attachment',
		uploadMultiple:true,
		previewsContainer: '#au-previews',
		previewTemplate: previewTemplate,
		acceptedFiles: '.doc,.gif,.jpg,.pdf,.png,.txt,.zip',
		thumbnailWidth: 100,
		thumbnailHeight: null,
		autoQueue: false,
		clickable: '.fileinput-button',
		accept: function(file, done) {

			// Need to check if the added file doesn't surpass the total max size setting.
			myDropzone.options.totalMaxSize = myDropzone.options.totalMaxSize + file.size;

			if (myDropzone.options.totalMaxSize > myDropzone.options.limitMultiFileUploadSize){
				done(myDropzone.options.text_totalMaxSize.replace('{currentTotal}', myDropzone.options.limitMultiFileUploadSize * 0.001).replace('{currentRemain}', myDropzone.options.totalMaxSize * 0.001));
			}
			else{
				done();
			}
		},
		totalMaxSize: 0
	};

	$.extend(true, dOptions, oOptions);

	var myDropzone = new Dropzone('div#attachUpload', dOptions);

	myDropzone.on('addedfile', function(file) {

		_thisElement = $(file.previewElement);

		// Create a generic thumb for non image files.
		if (!file.type.match(/image.*/)) {
			myDropzone.emit('thumbnail', file, smf_images_url +'/generic_attach.png');
		}

		// Show the file info.
		_thisElement.find('.attach-ui').fadeIn();

		// Create a function to insert the BBC attach tag.
		file.insertAttachment = function (_innerElement, attachmentId){

			_innerElement.find('.insertBBC').on('click', function (e) {
				e.preventDefault();
				$('#' + oEditorID).data('sceditor').sourceEditorInsertText('[attach]' + attachmentId + '[/attach]');
			});
		};

		// Replace the field with a message when the attachment is deleted.
		file.deleteAttachment = function (_innerElement, attachmentId){

			deleteButton = $('')
			.addClass('button_submit')
			.prop('disabled', false)
			.text(myDropzone.options.text_deleteAttach)
			.one('click', function (e) {

				$this = $(this);

				// Perform the action only after receiving the confirmation.
				if (!confirm(smf_you_sure)){
					return;
				}

				// Let the server know you want to delete the file you just recently uploaded...
				$.ajax({
					url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=delete;attach='+ attachmentId +';' + smf_session_var + '=' + smf_session_id,
					type: 'GET',
					dataType: 'json',
					beforeSend: function(){
						ajax_indicator(true);
					},
					complete: function(jqXHR, textStatus){
						ajax_indicator(false);

						// Delete the button.
						$this.fadeOutAndRemove('slow');
					},
					success: function (data, textStatus, xhr) {

						// Remove the text field and show a nice confirmation message.
						_innerElement.find('.attached_BBC').text(myDropzone.options.text_attachDeleted);
					},
					error: function (xhr, textStatus, errorThrown) {

						// Tell the user something horrible happen!
						_thisElement.find('p.error').append(textStatus.error.join('
'));

						// For dramatic purposes only!
						_thisElement.removeClass('infobox').addClass('errorbox');
					}
				});
			})
			.appendTo(_innerElement.find('.attach-ui'));
		};

		// Hookup the start button.
		_thisElement.find('.start').on( 'click', function() {
			myDropzone.enqueueFile(file);
		});

		// Show the main stuff!
		_thisElement.addClass('descbox');

		// Show the upload and cancel all buttons only if there is something to cancel/upload.
		if (myDropzone.getFilesWithStatus(Dropzone.ADDED).length == 1){
			$('div#attachUpload').find('#attach-cancelAll, #attach-uploadAll').fadeIn();
		}
	});

	// Stuff to do when a file gets cancel.
	myDropzone.on('removedfile', function(file) {

		// Need to remove the file size to make sure theres plenty of room for another one.
		myDropzone.options.totalMaxSize = myDropzone.options.totalMaxSize - file.size;

		// Hide the cancel and upload all buttons if there is nothing to cancel/upload anymore.
		if (myDropzone.getFilesWithStatus(Dropzone.ADDED).length == 0){
			$('div#attachUpload').find('#attach-cancelAll, #attach-uploadAll').fadeOut();
		}
	});

	// Update the total progress bar.
	myDropzone.on('totaluploadprogress', function(progress) {
		$('#total-progress span').width(progress + '%');
	});

	myDropzone.on('error', function(file, errorMessage, xhr) {

		_thisElement = $(file.previewElement);

		// Remove the 'start' button.
		_thisElement.find('.start').fadeOutAndRemove('slow');

		// Set a nice css class to make it more obvious theres an error.
		_thisElement.addClass('errorbox').removeClass('descbox');
	});

	myDropzone.on('success', function(file, responseText, e) {

		_thisElement = $(file.previewElement);

		// Remove the 'start' button.
		_thisElement.find('.start').fadeOutAndRemove('slow');

		// Don't do anything if there is no response from server.
		if (!responseText){
			return;
		}

		// There is a general error.
		if (responseText.generalErrors){
			_thisElement.find('p.error').append(responseText.generalErrors.join('
'));
			return;
		}

		// Server returns an array.
		response = responseText.files[0];

		// Show the input field.
		_thisElement.find('.attach-info p.attached_BBC').fadeIn();

		// The request was complete but the server returned an error.
		if (typeof response.errors !== 'undefined' && response.errors.length > 0){

			_thisElement.addClass('errorbox').removeClass('descbox');

			// Show the server error.
			_thisElement.find('p.error').append(response.errors.join('
'));
			return;
		}

		// If there wasn't any error, change the current cover.
		_thisElement.addClass('infobox').removeClass('descbox');

		// Append the BBC.
		_thisElement.find('input[name="attachBBC"]').val('[attach]' + response.attachID + '[/attach]');

		file.insertAttachment(_thisElement, response.attachID);

		// You have already loaded this attachment, to prevent abuse, you cannot cancel it and upload a new one.
		_thisElement.find('a.delete').fadeOutAndRemove('slow');

		// Fire up the delete button.
		file.deleteAttachment(_thisElement, response.attachID);
	});

	myDropzone.on('uploadprogress', function(file, progress, bytesSent) {

		_thisElement = $(file.previewElement);

		// Get the current file box progress bar, set its inner span's width accordingly.
		_thisElement.find('p.progressBar span').width(progress + '%');
	});

	myDropzone.on('complete', function(file, progress, bytesSent) {

		_thisElement = $(file.previewElement);

		// Hide the progress bar.
		_thisElement.find('p.progressBar').fadeOut();

		// Finishing up mocking!
		if (typeof file.isMock !== "undefined" && typeof file.attachID !== "undefined"){
			// Show the input field.
			_thisElement.find('.attach-info p.attached_BBC').fadeIn();

			// If there wasn't any error, change the current cover.
			_thisElement.addClass('infobox').removeClass('descbox');

			// Remove the 'start' button.
			_thisElement.find('.start').fadeOutAndRemove('slow');

			// Append the BBC.
			_thisElement.find('input[name="attachBBC"]').val('[attach]' + file.attachID + '[/attach]');

			file.insertAttachment(_thisElement, file.attachID);

			// You have already loaded this attachment, to prevent abuse, you cannot cancel it and upload a new one.
			_thisElement.find('a.delete').fadeOutAndRemove('slow');

			// Fire up the delete button.
			file.deleteAttachment(_thisElement, file.attachID);
		}
	});

	// Show each individual's progress bar.
	myDropzone.on('sending', function(file) {

		_thisElement = $(file.previewElement);

		// Show the progress bar when upload starts.
		_thisElement.find('p.progressBar').fadeIn();

		// Show the total progress bar when upload starts.
		$("#total-progress").fadeIn();
	});

	// Update the total progress bar.
	myDropzone.on("totaluploadprogress", function(progress) {
		$("#total-progress span").width(progress + '%');
	});

	// Hide the total progress bar when nothing's uploading anymore.
	myDropzone.on("queuecomplete", function(progress) {
		$("#total-progress").fadeOut();
	});

	// Add an event for uploading and cancelling all files.
	$('a#attach-cancelAll' ).on('click', function() {

		if (!confirm(smf_you_sure)){
			return;
		}

		myDropzone.removeAllFiles(true);
	});

	$('a#attach-uploadAll' ).on('click', function() {

		if (!confirm(smf_you_sure)){
			return;
		}

		myDropzone.enqueueFiles(myDropzone.getFilesWithStatus(Dropzone.ADDED));
	});

	// Need to tell the user they cannot post until all files are either uploaded or canceled.
	$("input[name ='post']").on('click', function(e) {

		attachAdded = myDropzone.getFilesWithStatus(Dropzone.ADDED).length;
		attachQueued = myDropzone.getFilesWithStatus(Dropzone.QUEUED).length;

		if (attachAdded > 0 || attachQueued > 0 ){
			alert(myDropzone.options.text_attachLeft);
			e.preventDefault();
			e.stopPropagation();
			return false;
		}
	});

	// Hide the default way to show already atachments.
	$('#postAttachment').fadeOutAndRemove('slow');

	// Show any attachments already uploaded.
	if (typeof current_attachments !== "undefined"){
		$.each(current_attachments, function(key, mock) {

			// Tell the world this is a mock file!
			mock.isMock = true;

			myDropzone.emit("addedfile", mock);

			// If the attachment is an image and has a thumbnail, show it. Otherwise fallback to the generic thumbfile.
			if (!mock.type.match(/image.*/)) {
				myDropzone.emit('thumbnail', mock, smf_images_url +'/generic_attach.png');
			}

			// Build a preview image.
			else if (typeof mock.thumbID !== "undefined"){
				myDropzone.emit('thumbnail', mock, smf_prepareScriptUrl(smf_scripturl) +'action=dlattach;attach='+ mock.thumbID + ';type=preview');
			}

			// This file is "completed".
			myDropzone.emit("complete", mock);
		});
	}

}
<commit_msg>If the file is too small, it won't have a thumbnail, show the regular file.<commit_after>function smf_fileUpload(oOptions)
{
	var previewNode = document.querySelector('#au-template');
	previewNode.id = '';
	var previewTemplate = previewNode.parentNode.innerHTML;
	previewNode.parentNode.removeChild(previewNode);

	// Default values in case oOptions isn't defined.
	var dOptions = {
		url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=add;' + smf_session_var + '=' + smf_session_id,
		parallelUploads : 1,
		filesizeBase:1000,
		paramName: 'attachment',
		uploadMultiple:true,
		previewsContainer: '#au-previews',
		previewTemplate: previewTemplate,
		acceptedFiles: '.doc,.gif,.jpg,.pdf,.png,.txt,.zip',
		thumbnailWidth: 100,
		thumbnailHeight: null,
		autoQueue: false,
		clickable: '.fileinput-button',
		accept: function(file, done) {

			// Need to check if the added file doesn't surpass the total max size setting.
			myDropzone.options.totalMaxSize = myDropzone.options.totalMaxSize + file.size;

			if (myDropzone.options.totalMaxSize > myDropzone.options.limitMultiFileUploadSize){
				done(myDropzone.options.text_totalMaxSize.replace('{currentTotal}', myDropzone.options.limitMultiFileUploadSize * 0.001).replace('{currentRemain}', myDropzone.options.totalMaxSize * 0.001));
			}
			else{
				done();
			}
		},
		totalMaxSize: 0
	};

	$.extend(true, dOptions, oOptions);

	var myDropzone = new Dropzone('div#attachUpload', dOptions);

	myDropzone.on('addedfile', function(file) {

		_thisElement = $(file.previewElement);

		// Create a generic thumb for non image files.
		if (!file.type.match(/image.*/)) {
			myDropzone.emit('thumbnail', file, smf_images_url +'/generic_attach.png');
		}

		// Show the file info.
		_thisElement.find('.attach-ui').fadeIn();

		// Create a function to insert the BBC attach tag.
		file.insertAttachment = function (_innerElement, attachmentId){

			_innerElement.find('.insertBBC').on('click', function (e) {
				e.preventDefault();
				$('#' + oEditorID).data('sceditor').sourceEditorInsertText('[attach]' + attachmentId + '[/attach]');
			});
		};

		// Replace the field with a message when the attachment is deleted.
		file.deleteAttachment = function (_innerElement, attachmentId){

			deleteButton = $('')
			.addClass('button_submit')
			.prop('disabled', false)
			.text(myDropzone.options.text_deleteAttach)
			.one('click', function (e) {

				$this = $(this);

				// Perform the action only after receiving the confirmation.
				if (!confirm(smf_you_sure)){
					return;
				}

				// Let the server know you want to delete the file you just recently uploaded...
				$.ajax({
					url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=delete;attach='+ attachmentId +';' + smf_session_var + '=' + smf_session_id,
					type: 'GET',
					dataType: 'json',
					beforeSend: function(){
						ajax_indicator(true);
					},
					complete: function(jqXHR, textStatus){
						ajax_indicator(false);

						// Delete the button.
						$this.fadeOutAndRemove('slow');
					},
					success: function (data, textStatus, xhr) {

						// Remove the text field and show a nice confirmation message.
						_innerElement.find('.attached_BBC').text(myDropzone.options.text_attachDeleted);
					},
					error: function (xhr, textStatus, errorThrown) {

						// Tell the user something horrible happen!
						_thisElement.find('p.error').append(textStatus.error.join('
'));

						// For dramatic purposes only!
						_thisElement.removeClass('infobox').addClass('errorbox');
					}
				});
			})
			.appendTo(_innerElement.find('.attach-ui'));
		};

		// Hookup the start button.
		_thisElement.find('.start').on( 'click', function() {
			myDropzone.enqueueFile(file);
		});

		// Show the main stuff!
		_thisElement.addClass('descbox');

		// Show the upload and cancel all buttons only if there is something to cancel/upload.
		if (myDropzone.getFilesWithStatus(Dropzone.ADDED).length == 1){
			$('div#attachUpload').find('#attach-cancelAll, #attach-uploadAll').fadeIn();
		}
	});

	// Stuff to do when a file gets cancel.
	myDropzone.on('removedfile', function(file) {

		// Need to remove the file size to make sure theres plenty of room for another one.
		myDropzone.options.totalMaxSize = myDropzone.options.totalMaxSize - file.size;

		// Hide the cancel and upload all buttons if there is nothing to cancel/upload anymore.
		if (myDropzone.getFilesWithStatus(Dropzone.ADDED).length == 0){
			$('div#attachUpload').find('#attach-cancelAll, #attach-uploadAll').fadeOut();
		}
	});

	// Update the total progress bar.
	myDropzone.on('totaluploadprogress', function(progress) {
		$('#total-progress span').width(progress + '%');
	});

	myDropzone.on('error', function(file, errorMessage, xhr) {

		_thisElement = $(file.previewElement);

		// Remove the 'start' button.
		_thisElement.find('.start').fadeOutAndRemove('slow');

		// Set a nice css class to make it more obvious theres an error.
		_thisElement.addClass('errorbox').removeClass('descbox');
	});

	myDropzone.on('success', function(file, responseText, e) {

		_thisElement = $(file.previewElement);

		// Remove the 'start' button.
		_thisElement.find('.start').fadeOutAndRemove('slow');

		// Don't do anything if there is no response from server.
		if (!responseText){
			return;
		}

		// There is a general error.
		if (responseText.generalErrors){
			_thisElement.find('p.error').append(responseText.generalErrors.join('
'));
			return;
		}

		// Server returns an array.
		response = responseText.files[0];

		// Show the input field.
		_thisElement.find('.attach-info p.attached_BBC').fadeIn();

		// The request was complete but the server returned an error.
		if (typeof response.errors !== 'undefined' && response.errors.length > 0){

			_thisElement.addClass('errorbox').removeClass('descbox');

			// Show the server error.
			_thisElement.find('p.error').append(response.errors.join('
'));
			return;
		}

		// If there wasn't any error, change the current cover.
		_thisElement.addClass('infobox').removeClass('descbox');

		// Append the BBC.
		_thisElement.find('input[name="attachBBC"]').val('[attach]' + response.attachID + '[/attach]');

		file.insertAttachment(_thisElement, response.attachID);

		// You have already loaded this attachment, to prevent abuse, you cannot cancel it and upload a new one.
		_thisElement.find('a.delete').fadeOutAndRemove('slow');

		// Fire up the delete button.
		file.deleteAttachment(_thisElement, response.attachID);
	});

	myDropzone.on('uploadprogress', function(file, progress, bytesSent) {

		_thisElement = $(file.previewElement);

		// Get the current file box progress bar, set its inner span's width accordingly.
		_thisElement.find('p.progressBar span').width(progress + '%');
	});

	myDropzone.on('complete', function(file, progress, bytesSent) {

		_thisElement = $(file.previewElement);

		// Hide the progress bar.
		_thisElement.find('p.progressBar').fadeOut();

		// Finishing up mocking!
		if (typeof file.isMock !== "undefined" && typeof file.attachID !== "undefined"){
			// Show the input field.
			_thisElement.find('.attach-info p.attached_BBC').fadeIn();

			// If there wasn't any error, change the current cover.
			_thisElement.addClass('infobox').removeClass('descbox');

			// Remove the 'start' button.
			_thisElement.find('.start').fadeOutAndRemove('slow');

			// Append the BBC.
			_thisElement.find('input[name="attachBBC"]').val('[attach]' + file.attachID + '[/attach]');

			file.insertAttachment(_thisElement, file.attachID);

			// You have already loaded this attachment, to prevent abuse, you cannot cancel it and upload a new one.
			_thisElement.find('a.delete').fadeOutAndRemove('slow');

			// Fire up the delete button.
			file.deleteAttachment(_thisElement, file.attachID);
		}
	});

	// Show each individual's progress bar.
	myDropzone.on('sending', function(file) {

		_thisElement = $(file.previewElement);

		// Show the progress bar when upload starts.
		_thisElement.find('p.progressBar').fadeIn();

		// Show the total progress bar when upload starts.
		$("#total-progress").fadeIn();
	});

	// Update the total progress bar.
	myDropzone.on("totaluploadprogress", function(progress) {
		$("#total-progress span").width(progress + '%');
	});

	// Hide the total progress bar when nothing's uploading anymore.
	myDropzone.on("queuecomplete", function(progress) {
		$("#total-progress").fadeOut();
	});

	// Add an event for uploading and cancelling all files.
	$('a#attach-cancelAll' ).on('click', function() {

		if (!confirm(smf_you_sure)){
			return;
		}

		myDropzone.removeAllFiles(true);
	});

	$('a#attach-uploadAll' ).on('click', function() {

		if (!confirm(smf_you_sure)){
			return;
		}

		myDropzone.enqueueFiles(myDropzone.getFilesWithStatus(Dropzone.ADDED));
	});

	// Need to tell the user they cannot post until all files are either uploaded or canceled.
	$("input[name ='post']").on('click', function(e) {

		attachAdded = myDropzone.getFilesWithStatus(Dropzone.ADDED).length;
		attachQueued = myDropzone.getFilesWithStatus(Dropzone.QUEUED).length;

		if (attachAdded > 0 || attachQueued > 0 ){
			alert(myDropzone.options.text_attachLeft);
			e.preventDefault();
			e.stopPropagation();
			return false;
		}
	});

	// Hide the default way to show already atachments.
	$('#postAttachment').fadeOutAndRemove('slow');

	// Show any attachments already uploaded.
	if (typeof current_attachments !== "undefined"){
		$.each(current_attachments, function(key, mock) {

			// Tell the world this is a mock file!
			mock.isMock = true;

			myDropzone.emit("addedfile", mock);

			// If the attachment is an image and has a thumbnail, show it. Otherwise fallback to the generic thumbfile.
			if (!mock.type.match(/image.*/)) {
				myDropzone.emit('thumbnail', mock, smf_images_url +'/generic_attach.png');
			}

			// Build a preview image.
			else if (typeof mock.thumbID !== "undefined"){

				// If the file is too small, it won't have a thumbnail, show the regular file.
				myDropzone.emit('thumbnail', mock, smf_prepareScriptUrl(smf_scripturl) +'action=dlattach;attach='+ (mock.thumbID > 0 ? mock.thumbID : mock.attachID) + ';type=preview');
			}

			// This file is "completed".
			myDropzone.emit("complete", mock);
		});
	}

}
<|endoftext|>


<commit_before>/**
 * sortClassNames - Default-sorts through the an element's class list to more easily compare against other class name lists.
 *
 * @param  {array} classList - The element's list of classes.
 * @return {array}             - A sorted list the class names.
 */
function sortClassNames(classList) {
  return classList.split(' ').sort();
}

/**
 * hasChanged - description
 *
 * @param  {array} oldClassNames - The target element's previous class names.
 * @param  {array} newClassNames - The target element's new list of class names.
 * @return {boolean}             - Evaluator for whether or not the comparable class names are different from eachother.
 */
function hasChanged(oldClassNames, newClassNames) {
  if (oldClassNames !== newClassNames) return true;

  return false;
}

/**
 * findClassNameDifference - Compares two lists of class names, returning all differences.
 *
 * @param  {array} firstClassList   - The (longer=) class list to compare against the second class list.
 * @param  {string} secondClassList - The (shorter=) class list to compare with the first class list.
 * @return {array}                  - The different class between the two class lists.
 */
function findClassNameDifference(firstClassList, secondClassList) {
  return firstClassList.filter((className) => secondClassList.indexOf(className) === -1);
}

/**
 * orderClassLists - Determines which list to use to compare against the other.
 *
 * @param  {array} oldClassNames - The target element's previous class names.
 * @param  {array} newClassNames - The target element's new list of class names.
 * @return {array}               - The correctly ordered list of class lists.
 */
function orderClassLists(oldClassNames, newClassNames) {

  // Removed a class.
  if (oldClassNames.length > newClassNames.length) {
    return [oldClassNames, newClassNames];
  }

  // Added or changed a class.
  return [newClassNames, oldClassNames];
}
/**
 * getClassChange - Determines how to filter through each class list to find the difference between the two.
 *
 * @param  {array} oldClassNames - The target element's previous class names.
 * @param  {array} newClassNames - The target element's new list of class names.
 * @return {@findClassNameDifference}
 */
function getClassChange(oldClassNames, newClassNames) {
  const comparableClassLists = orderClassLists(oldClassNames, newClassNames);

  return findClassNameDifference(comparableClassLists[0], comparableClassLists[1]);
}

/**
 * classNameChange - Uses a mutation observer to watch for when an element's class
 * names has been added to/removed from.
 *
 * @param  {string} selector  - The css selector to query the DOM for the element.
 * @param  {object} config={attributes: true} - Target node's default configuration is set to be for target element but could be adjusted to watch on a parent element and listen to changes in children elements.
 * @param  {function} callback - The function to run once target element's name has changed.
 *
 * @return {undefined}
 */
export default function classNameChange(selector, callback, config={attributes: true}) {
  const target = document.querySelector(selector);

  const oldClassNames = sortClassNames(target.className);

  const observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
      const newClassNames = sortClassNames(target.className);

      if (hasChanged(oldClassNames, newClassNames)) {
        observer.disconnect();
        // send back all class name differences w/ original element & observer (if user wants to make change and keep observing).
        callback(getClassChange(oldClassNames, newClassNames), target, observer);
      }
    });
  });

  observer.observe(target, config);
}
<commit_msg>adds contingency for all class names being added/removed<commit_after>/**
 * sortClassNames - Default-sorts through the an element's class list to more easily compare against other class name lists.
 *
 * @param  {array} classList - The element's list of classes.
 * @return {array}             - A sorted list the class names.
 */
function sortClassNames(classList) {
  return classList.split(' ').sort();
}

/**
 * allClassesChanged - description
 *
 * @param  {type} oldClassNames description
 * @param  {type} newClassNames description
 * @return {type}               description
 */
function allClassesChanged(oldClassNames, newClassNames) {
  const classLists = orderClassLists(oldClassNames, newClassNames);

  if (!classLists[0].length) {
    return classLists[1];
  } else if (!classLists[1].length) {
    return classLists[0];
  }
}

/**
 * hasChanged - description
 *
 * @param  {array} oldClassNames - The target element's previous class names.
 * @param  {array} newClassNames - The target element's new list of class names.
 * @return {boolean}             - Evaluator for whether or not the comparable class names are different from eachother.
 */
function hasChanged(oldClassNames, newClassNames) {
  if (oldClassNames !== newClassNames) return true;

  return false;
}

/**
 * findClassNameDifference - Compares two lists of class names, returning all differences.
 *
 * @param  {array} firstClassList   - The (longer=) class list to compare against the second class list.
 * @param  {string} secondClassList - The (shorter=) class list to compare with the first class list.
 * @return {array}                  - The different class between the two class lists.
 */
function findClassNameDifference(firstClassList, secondClassList) {
 return firstClassList.filter((className) => secondClassList.indexOf(className) === -1);
}

/**
 * orderClassLists - Determines which list to use to compare against the other.
 *
 * @param  {array} oldClassNames - The target element's previous class names.
 * @param  {array} newClassNames - The target element's new list of class names.
 * @return {array}               - The correctly ordered list of class lists.
 */
function orderClassLists(oldClassNames, newClassNames) {

  // Removed a class.
  if (oldClassNames.length > newClassNames.length) {
    return [oldClassNames, newClassNames];
  }

  // Added or changed a class.
  return [newClassNames, oldClassNames];
}
/**
 * getClassChange - Determines how to filter through each class list to find the difference between the two.
 *
 * @param  {array} oldClassNames - The target element's previous class names.
 * @param  {array} newClassNames - The target element's new list of class names.
 * @return {@findClassNameDifference}
 */
function getClassChange(oldClassNames, newClassNames) {
  const comparableClassLists = orderClassLists(oldClassNames, newClassNames);

  return findClassNameDifference(comparableClassLists[0], comparableClassLists[1]);
}

/**
 * classNameChange - Uses a mutation observer to watch for when an element's class
 * names has been added to/removed from.
 *
 * @param  {string} selector  - The css selector to query the DOM for the element.
 * @param  {object} config={attributes: true} - Target node's default configuration is set to be for target element but could be adjusted to watch on a parent element and listen to changes in children elements.
 * @param  {function} callback - The function to run once target element's name has changed.
 *
 * @return {undefined}
 */
export default function classNameChange(selector, callback, config={attributes: true}) {
  var target = document.querySelector(selector);

  var oldClassNames = sortClassNames(target.className);

  var observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
      var newClassNames = sortClassNames(target.className);

      // If all classes were removed, just send back the original class list
      if (!!allClassesChanged(oldClassNames, newClassNames)) {
        observer.disconnect();

        callback(allClassesChanged(oldClassNames, newClassNames), target);
      }

      if (hasChanged(oldClassNames, newClassNames)) {
        observer.disconnect();
        // send back all class name differences w/ original element & observer (if user wants to make change and keep observing).

        if (getClassChange(oldClassNames, newClassNames).length > 0) {
          callback(getClassChange(oldClassNames, newClassNames), target);
        }
      }
    });
  });

  observer.observe(target, config);
}
<|endoftext|>


<commit_before>// TODO: switch to requireJS or AMD modules
// TODO: convert to backbone?

$(function(){
	mf.init()
	mf.load()
})

var mf = {}

mf.init = function() {
	// put in constructor
	mf.nav.skip    = new mf.controllers.button('#skip')
	mf.nav.discard = new mf.controllers.button('#discard')
	mf.nav.publish = new mf.controllers.button('#publish')
	mf.nav.inspect = new mf.controllers.button('#inspect')
	mf.nav.undo    = new mf.controllers.button('#undo')

	mf.nav.skip.bind('right').bind('j').bind('s')
	mf.nav.discard.bind('x').bind('d').bind('space')
	mf.nav.publish.bind('w').bind('p')
	mf.nav.inspect.bind('i')
	mf.nav.undo.bind('u').bind('ctrl+z')
	mf.nav.undo.disable()

	mf.display = new mf.controllers.display('article')
	mf.display.wait()

	mf.pending = new mf.controllers.counter('#pending')
	// queue refill should load article
	mf.pending.change(function(from, to){
		if (from == 0 && to > 0)
			mf.load()

		document.title =  '('+to+') Megafilter'
	})

	mf.nav.skip.action(mf.skip)
	mf.nav.discard.action(mf.discard)
	mf.nav.publish.action(mf.publish)
	mf.nav.inspect.action(mf.inspect)
	//mf.nav.undo.action(mf.undo)

	setInterval(mf.check_pending,5000)
}

mf.updatePending = function(count) {
	$('#pending').text(count)
}

// render the next article from cache and begin to use the
mf.next = function() {}

mf.controllers = {}

mf.controllers.display = function(selector) {
	var ele = $(selector)

	// currently displayed article, if any
	this.article = null

	// render a given node-feedparser article to the page
	this.render = function(article) {
		this.article = article
		$('#error').hide()
		$('#loading').hide()
		ele.css('visibility','visible')
		$('section.description',ele).html(article.description)
		$('> h1 a',ele).text(article.title).attr('href',article.origlink)
		$('time',ele).attr('datetime',article.pubdate)
		$('.note',ele).html(article.author).prepend(' by ')
		return this
	}

	var discard = this.discard = function() {
		this.article = null
		ele.css('visibility','hidden')
		return this
	}

	// show loading animation
	this.wait = function(){
		$('#loading').show()
		discard()
		$('#error').hide()
		return this
	}

	// show 'error' message
	this.error = function(msg) {
		$('#loading').hide()
		discard()
		$('#error').show().text(msg)
		return this
	}
}

// positive article counter
mf.controllers.counter = function(selector) {
	ele = $(selector)
	var value = 0

	// callback for when number changes
	var change = function(from,to) {}
	this.change = function(fn) {
		change = fn
	}

	this.set = function(number) {
		if (number == value) return;
		// from, to
		change(value,number)
		ele.text(number)
		value = number
		return this
	}

	// initialise
	this.set(value)

	this.get = function() {
		return value
	}

	this.decrement = function() {
		if (value > 0)
			ele.text(--value)

		return this
	}
}

// navigation buttons (sort of like a view controller)
mf.controllers.button = function(selector){
	var ele = $(selector)
	var enabled = true;

	// callback for when button is clicked or key is pressed
	var action = function() {}
	this.action = function(fn) {
		action = fn
	}

	ele.click(function() {
		if (enabled)
			action()
	})

	this.enable = function() {
		enabled = true
		ele.removeClass('disabled')
		return this
	}

	this.disable = function() {
		enabled = false
		ele.addClass('disabled')
		return this
	}

	// bind a key
	this.bind = function(key) {

		// press
		$(document).bind('keydown',key,function() {
			if (!enabled) return false

			ele.addClass('depressed')
			return false
		})
		// release
		$(document).bind('keyup',key,function() {
			if (!enabled) return false

			ele.removeClass('depressed')
			action()
			return false
		})
		return this
	}
}

// -------model?

// download and display the next article (or current on first load)
mf.load = mf.skip = function() {
	mf.display.wait()
	mf.nav.disable()
	$.ajax({
		url: mf.display.article?'/next':'/current',
		type:'GET',
		error:function() {
			mf.display.error('No more articles')
		},
		success:function(article) {
			if (!article.pending) {
				mf.display.error('None left in queue')
				ml.article = null
			} else {
				mf.display.render(article)
				mf.nav.enable()
			}

			mf.pending.set(article.pending)
		}

	})
}

mf.publish = function() {
	mf.pending.decrement()
	$.ajax({
		url:'/publish/'+mf.display.article.id,
		type:'GET',
		error:function() {
			mf.display.error("None left!")
		},
		success:function() {
			console.log('Published article')
		}

	})
	mf.load()
}

mf.discard = function() {
	mf.pending.decrement()
	$.ajax({
		url:'/'+mf.display.article.id,
		type:'DELETE',
		error:function() {
			mf.display.error("That's it!")
		},
		success:function() {
			console.log('Deleted article')
		}

	})
	mf.load()
}

mf.inspect = function() {
	window.open(mf.display.article.link)
}

mf.nav = {}
mf.nav.enable = function() {
	mf.nav.skip.enable()
	mf.nav.discard.enable()
	mf.nav.publish.enable()
	mf.nav.inspect.enable()
}

mf.nav.disable = function() {
	mf.nav.skip.disable()
	mf.nav.discard.disable()
	mf.nav.publish.disable()
	mf.nav.inspect.disable()
}

mf.check_pending = function () {
	$.ajax({
		url: '/pending',
		type: 'GET',
		success: function(d) {
			mf.pending.set(d.pending)
		}
	})
}
<commit_msg>fix pendign counter update to title<commit_after>// TODO: switch to requireJS or AMD modules
// TODO: convert to backbone?

$(function(){
	mf.init()
	mf.load()
})

var mf = {}

mf.init = function() {
	// put in constructor
	mf.nav.skip    = new mf.controllers.button('#skip')
	mf.nav.discard = new mf.controllers.button('#discard')
	mf.nav.publish = new mf.controllers.button('#publish')
	mf.nav.inspect = new mf.controllers.button('#inspect')
	mf.nav.undo    = new mf.controllers.button('#undo')

	mf.nav.skip.bind('right').bind('j').bind('s')
	mf.nav.discard.bind('x').bind('d').bind('space')
	mf.nav.publish.bind('w').bind('p')
	mf.nav.inspect.bind('i')
	mf.nav.undo.bind('u').bind('ctrl+z')
	mf.nav.undo.disable()

	mf.display = new mf.controllers.display('article')
	mf.display.wait()

	mf.pending = new mf.controllers.counter('#pending')
	// queue refill should load article
	mf.pending.change(function(from, to){
		if (from == 0 && to > 0)
			mf.load()

		document.title =  '('+to+') Megafilter'
		console.log('change to',to,'from',from)
	})

	mf.nav.skip.action(mf.skip)
	mf.nav.discard.action(mf.discard)
	mf.nav.publish.action(mf.publish)
	mf.nav.inspect.action(mf.inspect)
	//mf.nav.undo.action(mf.undo)

	setInterval(mf.check_pending,5000)
}

// render the next article from cache and begin to use the
mf.next = function() {}

mf.controllers = {}

mf.controllers.display = function(selector) {
	var ele = $(selector)

	// currently displayed article, if any
	this.article = null

	// render a given node-feedparser article to the page
	this.render = function(article) {
		this.article = article
		$('#error').hide()
		$('#loading').hide()
		ele.css('visibility','visible')
		$('section.description',ele).html(article.description)
		$('> h1 a',ele).text(article.title).attr('href',article.origlink)
		$('time',ele).attr('datetime',article.pubdate)
		$('.note',ele).html(article.author).prepend(' by ')
		return this
	}

	var discard = this.discard = function() {
		this.article = null
		ele.css('visibility','hidden')
		return this
	}

	// show loading animation
	this.wait = function(){
		$('#loading').show()
		discard()
		$('#error').hide()
		return this
	}

	// show 'error' message
	this.error = function(msg) {
		$('#loading').hide()
		discard()
		$('#error').show().text(msg)
		return this
	}
}

// positive article counter
mf.controllers.counter = function(selector) {
	ele = $(selector)
	var value = 0

	// callback for when number changes
	var change = function(from,to) {}
	this.change = function(fn) {
		change = fn
	}

	this.set = function(number) {
		if (number == value) return;
		// from, to
		change(value,number)
		ele.text(number)
		value = number
		return this
	}

	// initialise
	this.set(value)

	this.get = function() {
		return value
	}

	this.decrement = function() {
		if (value > 0) {
			ele.text(--value)
			change(value+1,value)
		}

		return this
	}
}

// navigation buttons (sort of like a view controller)
mf.controllers.button = function(selector){
	var ele = $(selector)
	var enabled = true;

	// callback for when button is clicked or key is pressed
	var action = function() {}
	this.action = function(fn) {
		action = fn
	}

	ele.click(function() {
		if (enabled)
			action()
	})

	this.enable = function() {
		enabled = true
		ele.removeClass('disabled')
		return this
	}

	this.disable = function() {
		enabled = false
		ele.addClass('disabled')
		return this
	}

	// bind a key
	this.bind = function(key) {

		// press
		$(document).bind('keydown',key,function() {
			if (!enabled) return false

			ele.addClass('depressed')
			return false
		})
		// release
		$(document).bind('keyup',key,function() {
			if (!enabled) return false

			ele.removeClass('depressed')
			action()
			return false
		})
		return this
	}
}

// -------model?

// download and display the next article (or current on first load)
mf.load = mf.skip = function() {
	mf.display.wait()
	mf.nav.disable()
	$.ajax({
		url: mf.display.article?'/next':'/current',
		type:'GET',
		error:function() {
			mf.display.error('No more articles')
		},
		success:function(article) {
			if (!article.pending) {
				mf.display.error('None left in queue')
				ml.article = null
			} else {
				mf.display.render(article)
				mf.nav.enable()
			}

			mf.pending.set(article.pending)
		}

	})
}

mf.publish = function() {
	mf.pending.decrement()
	$.ajax({
		url:'/publish/'+mf.display.article.id,
		type:'GET',
		error:function() {
			mf.display.error("None left!")
		},
		success:function() {
			console.log('Published article')
		}

	})
	mf.load()
}

mf.discard = function() {
	mf.pending.decrement()
	$.ajax({
		url:'/'+mf.display.article.id,
		type:'DELETE',
		error:function() {
			mf.display.error("That's it!")
		},
		success:function() {
			console.log('Deleted article')
		}

	})
	mf.load()
}

mf.inspect = function() {
	window.open(mf.display.article.link)
}

mf.nav = {}
mf.nav.enable = function() {
	mf.nav.skip.enable()
	mf.nav.discard.enable()
	mf.nav.publish.enable()
	mf.nav.inspect.enable()
}

mf.nav.disable = function() {
	mf.nav.skip.disable()
	mf.nav.discard.disable()
	mf.nav.publish.disable()
	mf.nav.inspect.disable()
}

mf.check_pending = function () {
	$.ajax({
		url: '/pending',
		type: 'GET',
		success: function(d) {
			mf.pending.set(d.pending)
		}
	})
}
<|endoftext|>


<commit_before>// (C) Copyright 2015 Martin Dougiamas
//
// 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.

angular.module('mm.core.sidemenu', [])

.config(function($stateProvider) {

    $stateProvider

    .state('site', {
        url: '/site',
        templateUrl: 'core/components/sidemenu/templates/menu.html',
        controller: 'mmSideMenuCtrl',
        abstract: true,
        cache: false,
        onEnter: function($ionicHistory, $state, $mmSite) {
            // Remove the login page from the history stack.
            $ionicHistory.clearHistory();

            // Go to login if user is not logged in.
            if (!$mmSite.isLoggedIn()) {
                $state.go('mm_login.init');
            }
        }
    });

});
<commit_msg>MOBILE-1062 core: Close app with Android back button after login<commit_after>// (C) Copyright 2015 Martin Dougiamas
//
// 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.

angular.module('mm.core.sidemenu', [])

.config(function($stateProvider) {

    $stateProvider

    .state('site', {
        url: '/site',
        templateUrl: 'core/components/sidemenu/templates/menu.html',
        controller: 'mmSideMenuCtrl',
        abstract: true,
        cache: false,
        onEnter: function($ionicHistory, $state, $mmSite, $timeout) {
            // Remove the login page from the history stack.
            // MOBILE-1062: If we don't use $timeout, clearHistory leaves the backView on the history stack.
            $timeout(function() {
                $ionicHistory.clearHistory();
            });

            // Go to login if user is not logged in.
            if (!$mmSite.isLoggedIn()) {
                $state.go('mm_login.init');
            }
        }
    });

});
<|endoftext|>


<commit_before>const logContainer = document.getElementById("honkpad-log"),
      logMaxSize = 500,
      roomlist = new Set(),
      roomlistContainer = document.getElementById("honkpad-roomlist-container"),
      createRoomButton = document.getElementById("honkpad-roomlist-createbutton"),
      compileButton = document.getElementById("honkpad-compile"),
      runButton = document.getElementById("honkpad-run"),
      aboutButton = document.getElementById("honkpad-about"),
      resizeEl = document.getElementById("honkpad-log-resize"),
      resizeWidthMin = 0,
      resizeWidthMax = 100,
      firepadContainer = document.getElementById("firepad"),
      codemirror = CodeMirror(firepadContainer, {
        lineNumbers: true,
        matchBrackets: true,
        autoCloseBrackets: true,
        mode: "text/x-c++src",
        lineWrapping: true
      }),
      decoder = new TextDecoder(),
      newline = /\r\n|\n/,
      disallowedCharsRe = /[^A-Za-z0-9_-]/g,
      socket = io.connect(location.origin, {path: "/honkpad/socket.io"});

var firepad,
    currentRoom = localStorage.getItem("currentRoom") || undefined;

firepadContainer.style.width = localStorage.getItem("firepadContainer.style.width") || "50%";
logContainer.style.width = localStorage.getItem("logContainer.style.width") || "50%";

socket.on("compiler:begin", data => {
  clearLog();
  log("Compiling...", "info");
});
socket.on("compiler:out", data => log(data));
socket.on("compiler:error", data => log(data || "There was an error.", "warning"));
socket.on("compiler:fail", data => log(data || "There was an error.", "error"));
socket.on("compiler:success", data => log("Finished compiling successfully.", "success"));

socket.on("exec:begin", data => log("Running...", "info"));
socket.on("exec:out", data => {
  const str = decoder.decode(data);
  for(const line of str.split(newline)){
    if(line) log(line);
  }
});
socket.on("exec:error", data => log(data || "There was an error.", "warning"));
socket.on("exec:fail", data => log("Exited with code: " + data, "error"));
socket.on("exec:success", data => log(data || "Finished running successfully.", "success"));

socket.on("meta:about", data => log(data));

socket.on("disconnect", () => {
  leaveRoom();
  while(roomlistContainer.firstChild){
    roomlistContainer.removeChild(roomlistContainer.firstChild);
  }
  log("Disconnected.");
});

compileButton.addEventListener("click", e => {
  socket.emit("compiler:compile");
});
runButton.addEventListener("click", e => {
  socket.emit("exec:execute");
});
createRoomButton.addEventListener("click", e => {
  joinRoom(prompt("What should the name of the new room be? Name must be alphanumeric."));
});
aboutButton.addEventListener("click", e => {
  socket.emit("meta:about");
});

resizeEl.addEventListener("mousedown", e => {
  var mousedown = true;
  document.body.style.cursor = "col-resize";

  function onmouseup(){
    document.body.style.cursor = "";
    mousedown = false;
    window.removeEventListener("mousemove", onmousemove);
    window.removeEventListener("mouseup", onmouseup);
  }

  function onmousemove(e){
    if(mousedown){
      const screenMin = 40,
            screenMax = window.innerWidth,
            width = (e.clientX - screenMin) / (screenMax - screenMin) * (resizeWidthMax - resizeWidthMin) + resizeWidthMin;
      firepadContainer.style.width = width.toFixed(3) + "%";
      logContainer.style.width = (resizeWidthMax - width).toFixed(3) + "%";
    }
  }

  window.addEventListener("mouseup", onmouseup);
  window.addEventListener("mousemove", onmousemove);
});

window.addEventListener("beforeunload", e => {
  localStorage.setItem("firepadContainer.style.width", firepadContainer.style.width);
  localStorage.setItem("logContainer.style.width", logContainer.style.width);

  localStorage.setItem("currentRoom", currentRoom);
});

function log(text, style = "default"){
  const isScrolledToBottom = logContainer.scrollHeight - logContainer.clientHeight <= logContainer.scrollTop + 1,
        lineEl = document.createElement("div"),
        timeEl = document.createElement("div"),
        textEl = document.createElement("div");

  lineEl.className = "honkpad-log-line";

  timeEl.className = "honkpad-log-time";
  timeEl.textContent = `${Date().slice(16, 24)}`;

  textEl.className = "honkpad-log-text " + style;
  textEl.textContent = text;

  lineEl.appendChild(timeEl);
  lineEl.appendChild(textEl);
  logContainer.appendChild(lineEl);

  if(logContainer.childElementCount > logMaxSize){
    logContainer.removeChild(logContainer.firstChild);
  }

  if(isScrolledToBottom){
    logContainer.scrollTop = logContainer.scrollHeight - logContainer.clientHeight;
  }
}

function clearLog(){
  while(logContainer.firstChild){
    logContainer.removeChild(logContainer.firstChild);
  }
}

const getFirebaseConfig = (() => {
  const firebaseConfigName = "firebase-config.json";
  var firebaseConfig;

  return function(){
    if(firebaseConfig){
      return new Promise(resolve => resolve(firebaseConfig));
    }else{
      return fetch(firebaseConfigName)
              .then(res => {
                firebaseConfig = res.json();
                return firebaseConfig;
              });
    }
  }
})();

function firebaseAuth(){
  return new Promise((resolve, reject) => {
    firebase.auth().onAuthStateChanged(user => {
      if(user){
        resolve(user);
      }else{
        firebase.auth().signInAnonymously().catch(reject);
      }
    })
  });
}

getFirebaseConfig().then(config => {
  firebase.initializeApp(config);
  return firebaseAuth();
}).then(() => {
  joinRoom(currentRoom);
  const ref = firebase.database().ref().orderByKey();
  ref.on("child_added", (data) => {
    const roomname = data.getKey();
    if(roomlist.size < roomlist.add(roomname).size){
      const roomEl = document.createElement("div");
      const roomText = document.createElement("div");
      roomEl.id = "honkpad-roomlist-item-" + roomname;
      roomEl.className = "honkpad-roomlist-item";
      roomText.className = "honkpad-roomlist-item-text";
      roomText.textContent = roomname;
      roomText.title = "Join room";
      roomText.addEventListener("click", e => {
        joinRoom(roomname);
      });

      if(roomname === currentRoom){
        roomEl.classList.add("honkpad-roomlist-item-active");
      }

      const deleteButton = document.createElement("div");
      deleteButton.className = "honkpad-roomlist-item-deletebutton";
      deleteButton.textContent = "✖";
      deleteButton.title = "Delete room";
      deleteButton.addEventListener("click", e => {
        if(confirm(`Are you sure you want to delete the room '${roomname}'?\nAll data from room '${roomname}' will be lost.`)) deleteRoom(roomname);
      });

      roomEl.appendChild(deleteButton);
      roomEl.appendChild(roomText);
      roomlistContainer.insertBefore(roomEl, createRoomButton);
    }
  });
  ref.on("child_removed", (data) => {
    const roomname = data.getKey();
    if(roomlist.delete(roomname)){
      if(roomname === currentRoom){
        joinRoom();
      }
      const roomEl = document.getElementById("honkpad-roomlist-item-" + roomname);
      roomlistContainer.removeChild(roomEl);
    }
  });
}).catch(err => console.error(err));

function leaveRoom(){
  const roomEl = document.getElementById("honkpad-roomlist-item-" + currentRoom);
  if(roomEl){
    roomEl.classList.remove("honkpad-roomlist-item-active");
  }

  if(firepad) firepad.dispose();

  clearLog();
  codemirror.setValue("");
  codemirror.clearHistory();
}

function joinRoom(roomname = "default"){
  if(typeof roomname === "string"){
    roomname = roomname.replace(disallowedCharsRe, "");
    if(roomname){
      leaveRoom();
      currentRoom = roomname;

      const roomEl = document.getElementById("honkpad-roomlist-item-" + currentRoom);
      if(roomEl){
        roomEl.classList.add("honkpad-roomlist-item-active");
      }

      socket.emit("meta:join", roomname);

      const firebaseRef = firebase.database().ref(roomname);
      firepad = Firepad.fromCodeMirror(firebaseRef, codemirror, {
        defaultText: '#include <iostream>\nusing namespace std;\n\nint main(){\n  cout << "Hello World!";\n}'
      });
    }
  }
}

function deleteRoom(roomname){
  if(typeof roomname === "string"){
    roomname = roomname.replace(disallowedCharsRe, "");
    if(roomname){
      if(roomname === currentRoom) joinRoom();

      roomlist.delete(roomname);

      const roomEl = document.getElementById("honkpad-roomlist-item-" + roomname);
      roomlistContainer.removeChild(roomEl);

      firebase.database().ref().child(roomname).remove();
    }
  }
}
<commit_msg>Display room name in document title.<commit_after>const logContainer = document.getElementById("honkpad-log"),
      logMaxSize = 500,
      roomlist = new Set(),
      roomlistContainer = document.getElementById("honkpad-roomlist-container"),
      createRoomButton = document.getElementById("honkpad-roomlist-createbutton"),
      compileButton = document.getElementById("honkpad-compile"),
      runButton = document.getElementById("honkpad-run"),
      aboutButton = document.getElementById("honkpad-about"),
      resizeEl = document.getElementById("honkpad-log-resize"),
      resizeWidthMin = 0,
      resizeWidthMax = 100,
      firepadContainer = document.getElementById("firepad"),
      codemirror = CodeMirror(firepadContainer, {
        lineNumbers: true,
        matchBrackets: true,
        autoCloseBrackets: true,
        mode: "text/x-c++src",
        lineWrapping: true
      }),
      decoder = new TextDecoder(),
      newline = /\r\n|\n/,
      disallowedCharsRe = /[^A-Za-z0-9_-]/g,
      socket = io.connect(location.origin, {path: "/honkpad/socket.io"});

var firepad,
    currentRoom = localStorage.getItem("currentRoom") || undefined;

firepadContainer.style.width = localStorage.getItem("firepadContainer.style.width") || "50%";
logContainer.style.width = localStorage.getItem("logContainer.style.width") || "50%";

socket.on("compiler:begin", data => {
  clearLog();
  log("Compiling...", "info");
});
socket.on("compiler:out", data => log(data));
socket.on("compiler:error", data => log(data || "There was an error.", "warning"));
socket.on("compiler:fail", data => log(data || "There was an error.", "error"));
socket.on("compiler:success", data => log("Finished compiling successfully.", "success"));

socket.on("exec:begin", data => log("Running...", "info"));
socket.on("exec:out", data => {
  const str = decoder.decode(data);
  for(const line of str.split(newline)){
    if(line) log(line);
  }
});
socket.on("exec:error", data => log(data || "There was an error.", "warning"));
socket.on("exec:fail", data => log("Exited with code: " + data, "error"));
socket.on("exec:success", data => log(data || "Finished running successfully.", "success"));

socket.on("meta:about", data => log(data));

socket.on("disconnect", () => {
  leaveRoom();
  while(roomlistContainer.firstChild){
    roomlistContainer.removeChild(roomlistContainer.firstChild);
  }
  log("Disconnected.");
});

compileButton.addEventListener("click", e => {
  socket.emit("compiler:compile");
});
runButton.addEventListener("click", e => {
  socket.emit("exec:execute");
});
createRoomButton.addEventListener("click", e => {
  joinRoom(prompt("What should the name of the new room be? Name must be alphanumeric."));
});
aboutButton.addEventListener("click", e => {
  socket.emit("meta:about");
});

resizeEl.addEventListener("mousedown", e => {
  var mousedown = true;
  document.body.style.cursor = "col-resize";

  function onmouseup(){
    document.body.style.cursor = "";
    mousedown = false;
    window.removeEventListener("mousemove", onmousemove);
    window.removeEventListener("mouseup", onmouseup);
  }

  function onmousemove(e){
    if(mousedown){
      const screenMin = 40,
            screenMax = window.innerWidth,
            width = (e.clientX - screenMin) / (screenMax - screenMin) * (resizeWidthMax - resizeWidthMin) + resizeWidthMin;
      firepadContainer.style.width = width.toFixed(3) + "%";
      logContainer.style.width = (resizeWidthMax - width).toFixed(3) + "%";
    }
  }

  window.addEventListener("mouseup", onmouseup);
  window.addEventListener("mousemove", onmousemove);
});

window.addEventListener("beforeunload", e => {
  localStorage.setItem("firepadContainer.style.width", firepadContainer.style.width);
  localStorage.setItem("logContainer.style.width", logContainer.style.width);

  localStorage.setItem("currentRoom", currentRoom);
});

function log(text, style = "default"){
  const isScrolledToBottom = logContainer.scrollHeight - logContainer.clientHeight <= logContainer.scrollTop + 1,
        lineEl = document.createElement("div"),
        timeEl = document.createElement("div"),
        textEl = document.createElement("div");

  lineEl.className = "honkpad-log-line";

  timeEl.className = "honkpad-log-time";
  timeEl.textContent = `${Date().slice(16, 24)}`;

  textEl.className = "honkpad-log-text " + style;
  textEl.textContent = text;

  lineEl.appendChild(timeEl);
  lineEl.appendChild(textEl);
  logContainer.appendChild(lineEl);

  if(logContainer.childElementCount > logMaxSize){
    logContainer.removeChild(logContainer.firstChild);
  }

  if(isScrolledToBottom){
    logContainer.scrollTop = logContainer.scrollHeight - logContainer.clientHeight;
  }
}

function clearLog(){
  while(logContainer.firstChild){
    logContainer.removeChild(logContainer.firstChild);
  }
}

const getFirebaseConfig = (() => {
  const firebaseConfigName = "firebase-config.json";
  var firebaseConfig;

  return function(){
    if(firebaseConfig){
      return new Promise(resolve => resolve(firebaseConfig));
    }else{
      return fetch(firebaseConfigName)
              .then(res => {
                firebaseConfig = res.json();
                return firebaseConfig;
              });
    }
  }
})();

function firebaseAuth(){
  return new Promise((resolve, reject) => {
    firebase.auth().onAuthStateChanged(user => {
      if(user){
        resolve(user);
      }else{
        firebase.auth().signInAnonymously().catch(reject);
      }
    })
  });
}

getFirebaseConfig().then(config => {
  firebase.initializeApp(config);
  return firebaseAuth();
}).then(() => {
  joinRoom(currentRoom);
  const ref = firebase.database().ref().orderByKey();
  ref.on("child_added", (data) => {
    const roomname = data.getKey();
    if(roomlist.size < roomlist.add(roomname).size){
      const roomEl = document.createElement("div");
      const roomText = document.createElement("div");
      roomEl.id = "honkpad-roomlist-item-" + roomname;
      roomEl.className = "honkpad-roomlist-item";
      roomText.className = "honkpad-roomlist-item-text";
      roomText.textContent = roomname;
      roomText.title = "Join room";
      roomText.addEventListener("click", e => {
        joinRoom(roomname);
      });

      if(roomname === currentRoom){
        roomEl.classList.add("honkpad-roomlist-item-active");
      }

      const deleteButton = document.createElement("div");
      deleteButton.className = "honkpad-roomlist-item-deletebutton";
      deleteButton.textContent = "✖";
      deleteButton.title = "Delete room";
      deleteButton.addEventListener("click", e => {
        if(confirm(`Are you sure you want to delete the room '${roomname}'?\nAll data from room '${roomname}' will be lost.`)) deleteRoom(roomname);
      });

      roomEl.appendChild(deleteButton);
      roomEl.appendChild(roomText);
      roomlistContainer.insertBefore(roomEl, createRoomButton);
    }
  });
  ref.on("child_removed", (data) => {
    const roomname = data.getKey();
    if(roomlist.delete(roomname)){
      if(roomname === currentRoom){
        joinRoom();
      }
      const roomEl = document.getElementById("honkpad-roomlist-item-" + roomname);
      roomlistContainer.removeChild(roomEl);
    }
  });
}).catch(err => console.error(err));

function leaveRoom(){
  const roomEl = document.getElementById("honkpad-roomlist-item-" + currentRoom);
  if(roomEl){
    roomEl.classList.remove("honkpad-roomlist-item-active");
  }

  if(firepad) firepad.dispose();

  clearLog();
  codemirror.setValue("");
  codemirror.clearHistory();
}

function joinRoom(roomname = "default"){
  if(typeof roomname === "string"){
    roomname = roomname.replace(disallowedCharsRe, "");
    if(roomname){
      leaveRoom();
      currentRoom = roomname;

      const roomEl = document.getElementById("honkpad-roomlist-item-" + currentRoom);
      if(roomEl){
        roomEl.classList.add("honkpad-roomlist-item-active");
      }

      document.title = currentRoom + " | Honkpad";

      socket.emit("meta:join", roomname);

      const firebaseRef = firebase.database().ref(roomname);
      firepad = Firepad.fromCodeMirror(firebaseRef, codemirror, {
        defaultText: '#include <iostream>\nusing namespace std;\n\nint main(){\n  cout << "Hello World!";\n}'
      });
    }
  }
}

function deleteRoom(roomname){
  if(typeof roomname === "string"){
    roomname = roomname.replace(disallowedCharsRe, "");
    if(roomname){
      if(roomname === currentRoom) joinRoom();

      roomlist.delete(roomname);

      const roomEl = document.getElementById("honkpad-roomlist-item-" + roomname);
      roomlistContainer.removeChild(roomEl);

      firebase.database().ref().child(roomname).remove();
    }
  }
}
<|endoftext|>


<commit_before>/* Config
*/

var DEV = 1;
var DEBUG = DEV;

var BASE_URI = !DEV ? 'geo.media.mit.edu/' : 'localhost:3000/';
var BASE_URL = 'http://' + BASE_URI;

var MapStatus = {
	PRIVATE: 'P',
	PUBLIC: 'A'
}

var DataStatus = {
	IMPORTING: 'I',
	UNREDUCED: 'U',
	REDUCING: 'R',
	DONE: 'D'
};

var FeatureType = {
	POINTS: 'P',
	CELLS: 'C',
	BUBBLES: 'B'
};

FeatureType.DEFAULT = FeatureType.CELLS;

var ColorType = {
	SOLID: 'S',
	LINEAR_GRADIENT: 'L'
}

var UnitFormat = {
	LEGEND: '%(value)s'
}

ColorType.DEFAULT = ColorType.SOLID;

var DEG_PER_PX_AT_ZOOM_0 = 0.7111111112100985
var GRID_SIZES = {
//	'-1': 2,
	'0': DEG_PER_PX_AT_ZOOM_0 * 4
};
for (var zoom = 1; zoom <= 15; zoom++) {
	GRID_SIZES[zoom] = GRID_SIZES[zoom - 1] / 2;
}

var HISTOGRAM_SIZES = [222, 100, 30];

if (!DEBUG || typeof console == "undefined" || typeof console.log == "undefined") var console = { log: function() {} };

var COLOR_GRADIENT_STEP = null; // 1 / 500.0;

var DEFAULT_FEATURE_OPACITY = .75;
var DEFAULT_SELECTED_FEATURE_OPACITY = DEFAULT_FEATURE_OPACITY + (1 - DEFAULT_FEATURE_OPACITY) / 2;

// the higher this ratio, the more cropping of extreme high values in histograms if
// cropDistribution == true
var CROP_DISTRIBUTION_RATIO = 1 / 10;

var _panelLoaded = false;
var _firstLoad = true;

var _mapCollections = [];
var _commentArray = [];

var pointCollection = new Array();
var timeBasedPointCollection = new Array();

var _defaultMapStyle = 'dark';
var _defaultMapLocation = 'Fukushima';
var _defaultMapZoom = 6;

var _settingsVisible = false;
var _graphVisible = false;
var _dataLibraryVisible = false;
var _chatVisible = false;

if (module) {
	module.exports = {
		MapStatus: MapStatus,
		FeatureType: FeatureType,
		DataStatus: DataStatus,
		ColorType: ColorType,
		GRID_SIZES: GRID_SIZES,
		HISTOGRAM_SIZES: HISTOGRAM_SIZES
	};
}

<commit_msg>Minor fixes<commit_after>/* Config
*/

var DEV = 1;
var DEBUG = DEV;

var BASE_URI = !DEV ? 'geo.media.mit.edu/' : 'localhost:3000/';
var BASE_URL = 'http://' + BASE_URI;

var MapStatus = {
	PRIVATE: 'P',
	PUBLIC: 'A'
}

var DataStatus = {
	IMPORTING: 'I',
	UNREDUCED: 'U',
	REDUCING: 'R',
	DONE: 'D'
};

var FeatureType = {
	POINTS: 'P',
	CELLS: 'C',
	BUBBLES: 'B'
};

FeatureType.DEFAULT = FeatureType.CELLS;

var ColorType = {
	SOLID: 'S',
	LINEAR_GRADIENT: 'L'
}

var UnitFormat = {
	LEGEND: '%(value)s'
}

ColorType.DEFAULT = ColorType.SOLID;

var DEG_PER_PX_AT_ZOOM_0 = 0.7111111112100985
var GRID_SIZES = {
//	'-1': 2,
	'0': DEG_PER_PX_AT_ZOOM_0 * 4
};
for (var zoom = 1; zoom <= 15; zoom++) {
	GRID_SIZES[zoom] = GRID_SIZES[zoom - 1] / 2;
}

var HISTOGRAM_SIZES = [222, 100, 30];

if (!DEBUG || typeof console == "undefined" || typeof console.log == "undefined") var console = { log: function() {} };

var COLOR_GRADIENT_STEP = null; // 1 / 500.0;

var DEFAULT_FEATURE_OPACITY = .75;
var DEFAULT_SELECTED_FEATURE_OPACITY = DEFAULT_FEATURE_OPACITY + (1 - DEFAULT_FEATURE_OPACITY) / 2;

// the higher this ratio, the more cropping of extreme high values in histograms if
// cropDistribution == true
var CROP_DISTRIBUTION_RATIO = 1 / 10;

var _panelLoaded = false;
var _firstLoad = true;

var _mapCollections = [];
var _commentArray = [];

var pointCollection = new Array();
var timeBasedPointCollection = new Array();

var _defaultMapStyle = 'dark';
var _defaultMapLocation = 'Fukushima';
var _defaultMapZoom = 6;

var _settingsVisible = false;
var _graphVisible = false;
var _dataLibraryVisible = false;
var _chatVisible = false;

if (!this.Document) {
	module.exports = {
		MapStatus: MapStatus,
		FeatureType: FeatureType,
		DataStatus: DataStatus,
		ColorType: ColorType,
		GRID_SIZES: GRID_SIZES,
		HISTOGRAM_SIZES: HISTOGRAM_SIZES
	};
}

<|endoftext|>