<commit_before>angular.module('rifiuti.services.utili', [])

.factory('Conf', function() {

    return {
        showNews: function() {
            return SHOW_NEWS;
        }
    };
})

.factory('ConnectionErrorService', function($ionicPopup,$filter) {
  return {
    show : function() {
        var popup = $ionicPopup.show({
            title: ''+$filter("translate")("dev_mode")+'',
            template: $filter("translate")("connection_error"),
            buttons: [
                {
                    text: 'OK'
                }
            ]
        });
     }
   }
})

.factory('LoaderService', function($rootScope, $ionicLoading, $filter) {
  return {

        showToastByTime : function(alertMsg, time){
            $ionicLoading.show({
                template:  $filter("translate")(alertMsg),
                noBackdrop: true,
                duration: time });
        },

        showToast : function(alertMsg){
            $ionicLoading.show({
                template:  $filter("translate")(alertMsg),
                noBackdrop: true,
                duration: 3000 });
        },

        show : function() {

            $rootScope.loading = $ionicLoading.show({

              // The text to display in the loading indicator
              content: ' Loading',

              // The animation to use
              animation: 'fade-in',

              // Will a dark overlay or backdrop cover the entire view
              showBackdrop: true,

              // The maximum width of the loading indicator
              // Text will be wrapped if longer than maxWidth
              maxWidth: 200,

              // The delay in showing the indicator
              showDelay: 10
            });
        },

        hide : function(){
            if ($rootScope.loading) {
                $ionicLoading.hide();
            }
        }
    }
})

.factory('Utili', function () {
  var mesi = ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"];
  var giorni = ["DOM", "LUN", "MAR", "MER", "GIO", "VEN", "SAB"];
  var giorniC = ["domenica", "lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato"];
  var giorniShort = ["dom", "lun", "mar", "mer", "gio", "ven", "sab"];

  var DOW = {"DOM":6, "LUN":0, "MAR":1, "MER":2, "GIO":3, "VEN":4, "SAB":5};

  var daysInMonth = function(month, year) {
      return new Date(year, month + 1, 0).getDate();
  };
  var dayIndex = function(day) {
          return DOW[day];
  };

  var format = function(date) {
    return date.getFullYear()+'-'+(date.getMonth() < 9 ? '0'+(date.getMonth()+1):(date.getMonth()+1))+'-'+(date.getDate() < 10 ? '0'+date.getDate():date.getDate());
  };

  var generateDOWDates = function(da, a, dow, ecc) {
      var res = [];
      var currFrom = new Date(da.getTime());
      currFrom.setDate(currFrom.getDate()-dayIndex(giorni[currFrom.getDay()])+dow);
      while (currFrom.getTime()  da.getTime()) {
          var dStr = format(currFrom);
          if (!ecc || ecc.indexOf(dStr) < 0) res.push(dStr);
        }
        // move to next week
        currFrom.setDate(currFrom.getDate()+7);
      }
      return res;
  };

  /**
   * generate a list of date string for the specified period, date, definition, and list of exceptions (format dd/MM[/yyyy])
   * definition is space-separated elements, where element is either DOW (e.g., lunedi) or date (format dd/MM[/yyyy])
   */
  var calToDates = function(da, a, il, ecc) {
    if (!il) return [];

    var arr = il.trim().split(' ');
    var dates = [];
    var eccDates = calToDates(da, a, ecc, null);
    for (var i = 0; i < arr.length; i++) {
      var elem = arr[i];
      var idx = giorniC.indexOf(elem);
      // DOW case
      if (idx >= 0) {
        dates = dates.concat(generateDOWDates(da, a, DOW[giorni[idx]], eccDates));
      // date case
      } else {
        var dElems = elem.split('/');
        if (dElems.length < 2 || dElems.length > 3) return;
        if (dElems.length == 2) dElems.push(''+da.getFullYear());
        var dStr = format(new Date(dElems[2], dElems[1]-1, dElems[0]));
        if (!ecc || ecc.indexOf(dStr) < 0) dates.push(dStr);
      }
    }
    dates.sort();
    return dates;
  };

  var iconType = function(tipologia) {
    if (tipologia in ICON_POINT_MAP) return ICON_POINT_MAP[tipologia];
    return null;
  };

  var rgbColor = function(colore) {
      if (colore in ICON_COLOR_MAP) return ICON_COLOR_MAP[colore];
      return 'grey';
  }

  return {
    jsDOWToShortText: function(dow) {
      return giorni[dow];
    },
    jsDOWToDOW: function(dow) {
      return DOW[giorni[dow]];
    },
    DOWTextToDOW: function(dow) {
      return DOW[dow];
    },
    dayIndex: dayIndex,
    textToDOW: function(txt) {
      return DOW[giorni[giorniC.indexOf(txt)]];
    },
    dayToDOW: function(day) {
      return dayIndex(giorni[day]);
    },
    monthYear: function(a, b) {
        return mesi[a] + " " + b
    },
    isLastDayInMonth : function(dt) {
        return new Date(dt.getTime() + 86400000).getDate() === 1;
    },
    lastDateOfMonth: function(date) {
        return new Date(date.getFullYear(), date.getMonth() + 1, 0);
    },
    calToDates: calToDates,
    expandOrarioApertura: function(cal) {
      if (!cal.il || cal.dates) return;
      var max = new Date(Date.parse(cal.dataA));
      var min = new Date(Date.parse(cal.dataDa));
      cal.dates = calToDates(min,max, cal.il, cal.eccezione);
    },

    getRGBColor: function(rgbColor) {
        var c = rgbColor;
        if (c == 'white') {
            return 'grey';
        }
        return c;
    },
    iconFromRegola: function(regola) {
      return this.icon(regola.tipologiaPuntoRaccolta, regola.colore);
    },
//    icon: function(tipologia, colore) {
//      var icona = iconType(tipologia);
//      return (!!icona?'img/ic_'+icona+'_'+this.getRGBColor(colore)+'.png':null);
//    },
    icon: function(icona,colorById) {
      //var icona = iconType(tipologia);
      if (colorById=='white') {
        return icona + '-outline';
      }
      return icona;
    },
    poiIcon: function(icona, colore) {
      //var icona = iconType(tipologia);
      return (!!icona?'img/ic_poi_'+icona+'.png':null);
    },
    belongsTo: function(pr, area, profile) {
      return profile.aree.indexOf(area) != -1
          //&& (pr.tipologiaPuntiRaccolta=='CRM' || pr.tipologiaPuntiRaccolta=='CRZ' || !pr.zona || profile.comuni.indexOf(pr.zona)!=-1)
        ;
    },
    isPaP: function(tipoPuntoRaccolta) {
      return !!tipoPuntoRaccolta && !!tipoPuntoRaccolta.type && (tipoPuntoRaccolta.type == 'PP' || !!CUSTOM_PAP && CUSTOM_PAP.indexOf(tipoPuntoRaccolta.type) >= 0);
    },
      //return !!tipologia && (tipologia.toLowerCase().indexOf('porta a porta') == 0 || !!CUSTOM_PAP && CUSTOM_PAP.indexOf(tipologia) >= 0);
	fullDateFormat: function(d,transf) {
		return transf(giorniShort[d.getDay()])+ ' ' +d.getDate()+' '+transf(mesi[d.getMonth()])+' '+d.getFullYear();
	},
//    filterCharacteristics: function(pdr) {
//        if (!ISOLA_MAPPING) return false;
//        if (!pdr.caratteristiche) return false;
//        for (var key in pdr.caratteristiche) {
//            if (pdr.caratteristiche[key]) return true;
//        }
//        return false;
//    },
    pdrCharacteristicApplies: function(pdr, tipo) {
        var mapping = null;
        try {
            mapping = ISOLA_MAPPING;
        }catch(e) {
        }
        if (mapping != null && pdr.caratteristiche && !pdr.caratteristiche[mapping[tipo]]) return false;
        return true;
    }
  }
})
<commit_msg>issue #49, The span for collection calendar dates is intended as minor instead of minor or equals, fixed<commit_after>angular.module('rifiuti.services.utili', [])

.factory('Conf', function() {

    return {
        showNews: function() {
            return SHOW_NEWS;
        }
    };
})

.factory('ConnectionErrorService', function($ionicPopup,$filter) {
  return {
    show : function() {
        var popup = $ionicPopup.show({
            title: ''+$filter("translate")("dev_mode")+'',
            template: $filter("translate")("connection_error"),
            buttons: [
                {
                    text: 'OK'
                }
            ]
        });
     }
   }
})

.factory('LoaderService', function($rootScope, $ionicLoading, $filter) {
  return {

        showToastByTime : function(alertMsg, time){
            $ionicLoading.show({
                template:  $filter("translate")(alertMsg),
                noBackdrop: true,
                duration: time });
        },

        showToast : function(alertMsg){
            $ionicLoading.show({
                template:  $filter("translate")(alertMsg),
                noBackdrop: true,
                duration: 3000 });
        },

        show : function() {

            $rootScope.loading = $ionicLoading.show({

              // The text to display in the loading indicator
              content: ' Loading',

              // The animation to use
              animation: 'fade-in',

              // Will a dark overlay or backdrop cover the entire view
              showBackdrop: true,

              // The maximum width of the loading indicator
              // Text will be wrapped if longer than maxWidth
              maxWidth: 200,

              // The delay in showing the indicator
              showDelay: 10
            });
        },

        hide : function(){
            if ($rootScope.loading) {
                $ionicLoading.hide();
            }
        }
    }
})

.factory('Utili', function () {
  var mesi = ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"];
  var giorni = ["DOM", "LUN", "MAR", "MER", "GIO", "VEN", "SAB"];
  var giorniC = ["domenica", "lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato"];
  var giorniShort = ["dom", "lun", "mar", "mer", "gio", "ven", "sab"];

  var DOW = {"DOM":6, "LUN":0, "MAR":1, "MER":2, "GIO":3, "VEN":4, "SAB":5};

  var daysInMonth = function(month, year) {
      return new Date(year, month + 1, 0).getDate();
  };
  var dayIndex = function(day) {
          return DOW[day];
  };

  var format = function(date) {
    return date.getFullYear()+'-'+(date.getMonth() < 9 ? '0'+(date.getMonth()+1):(date.getMonth()+1))+'-'+(date.getDate() < 10 ? '0'+date.getDate():date.getDate());
  };

  var generateDOWDates = function(da, a, dow, ecc) {
      var res = [];
      var currFrom = new Date(da.getTime());
      currFrom.setDate(currFrom.getDate()-dayIndex(giorni[currFrom.getDay()])+dow);
      while (currFrom.getTime() <= a.getTime()) {
        if (currFrom.getTime() >= da.getTime()) {
          var dStr = format(currFrom);
          if (!ecc || ecc.indexOf(dStr) < 0) res.push(dStr);
        }
        // move to next week
        currFrom.setDate(currFrom.getDate()+7);
      }
      return res;
  };

  /**
   * generate a list of date string for the specified period, date, definition, and list of exceptions (format dd/MM[/yyyy])
   * definition is space-separated elements, where element is either DOW (e.g., lunedi) or date (format dd/MM[/yyyy])
   */
  var calToDates = function(da, a, il, ecc) {
    if (!il) return [];

    var arr = il.trim().split(' ');
    var dates = [];
    var eccDates = calToDates(da, a, ecc, null);
    for (var i = 0; i < arr.length; i++) {
      var elem = arr[i];
      var idx = giorniC.indexOf(elem);
      // DOW case
      if (idx >= 0) {
        dates = dates.concat(generateDOWDates(da, a, DOW[giorni[idx]], eccDates));
      // date case
      } else {
        var dElems = elem.split('/');
        if (dElems.length < 2 || dElems.length > 3) return;
        if (dElems.length == 2) dElems.push(''+da.getFullYear());
        var dStr = format(new Date(dElems[2], dElems[1]-1, dElems[0]));
        if (!ecc || ecc.indexOf(dStr) < 0) dates.push(dStr);
      }
    }
    dates.sort();
    return dates;
  };

  var iconType = function(tipologia) {
    if (tipologia in ICON_POINT_MAP) return ICON_POINT_MAP[tipologia];
    return null;
  };

  var rgbColor = function(colore) {
      if (colore in ICON_COLOR_MAP) return ICON_COLOR_MAP[colore];
      return 'grey';
  }

  return {
    jsDOWToShortText: function(dow) {
      return giorni[dow];
    },
    jsDOWToDOW: function(dow) {
      return DOW[giorni[dow]];
    },
    DOWTextToDOW: function(dow) {
      return DOW[dow];
    },
    dayIndex: dayIndex,
    textToDOW: function(txt) {
      return DOW[giorni[giorniC.indexOf(txt)]];
    },
    dayToDOW: function(day) {
      return dayIndex(giorni[day]);
    },
    monthYear: function(a, b) {
        return mesi[a] + " " + b
    },
    isLastDayInMonth : function(dt) {
        return new Date(dt.getTime() + 86400000).getDate() === 1;
    },
    lastDateOfMonth: function(date) {
        return new Date(date.getFullYear(), date.getMonth() + 1, 0);
    },
    calToDates: calToDates,
    expandOrarioApertura: function(cal) {
      if (!cal.il || cal.dates) return;
      var max = new Date(Date.parse(cal.dataA));
      var min = new Date(Date.parse(cal.dataDa));
      cal.dates = calToDates(min,max, cal.il, cal.eccezione);
    },

    getRGBColor: function(rgbColor) {
        var c = rgbColor;
        if (c == 'white') {
            return 'grey';
        }
        return c;
    },
    iconFromRegola: function(regola) {
      return this.icon(regola.tipologiaPuntoRaccolta, regola.colore);
    },
//    icon: function(tipologia, colore) {
//      var icona = iconType(tipologia);
//      return (!!icona?'img/ic_'+icona+'_'+this.getRGBColor(colore)+'.png':null);
//    },
    icon: function(icona,colorById) {
      //var icona = iconType(tipologia);
      if (colorById=='white') {
        return icona + '-outline';
      }
      return icona;
    },
    poiIcon: function(icona, colore) {
      //var icona = iconType(tipologia);
      return (!!icona?'img/ic_poi_'+icona+'.png':null);
    },
    belongsTo: function(pr, area, profile) {
      return profile.aree.indexOf(area) != -1
          //&& (pr.tipologiaPuntiRaccolta=='CRM' || pr.tipologiaPuntiRaccolta=='CRZ' || !pr.zona || profile.comuni.indexOf(pr.zona)!=-1)
        ;
    },
    isPaP: function(tipoPuntoRaccolta) {
      return !!tipoPuntoRaccolta && !!tipoPuntoRaccolta.type && (tipoPuntoRaccolta.type == 'PP' || !!CUSTOM_PAP && CUSTOM_PAP.indexOf(tipoPuntoRaccolta.type) >= 0);
    },
      //return !!tipologia && (tipologia.toLowerCase().indexOf('porta a porta') == 0 || !!CUSTOM_PAP && CUSTOM_PAP.indexOf(tipologia) >= 0);
	fullDateFormat: function(d,transf) {
		return transf(giorniShort[d.getDay()])+ ' ' +d.getDate()+' '+transf(mesi[d.getMonth()])+' '+d.getFullYear();
	},
//    filterCharacteristics: function(pdr) {
//        if (!ISOLA_MAPPING) return false;
//        if (!pdr.caratteristiche) return false;
//        for (var key in pdr.caratteristiche) {
//            if (pdr.caratteristiche[key]) return true;
//        }
//        return false;
//    },
    pdrCharacteristicApplies: function(pdr, tipo) {
        var mapping = null;
        try {
            mapping = ISOLA_MAPPING;
        }catch(e) {
        }
        if (mapping != null && pdr.caratteristiche && !pdr.caratteristiche[mapping[tipo]]) return false;
        return true;
    }
  }
})
<|endoftext|>


<commit_before>const {RetryCountExceedError} = require("./exceptions");
const {ProposerError} = require("../../../../src/Proposer");

/////////

function msg(id) {
    return {"id": id};
}

function log() {
    return new Log([]);
}

class Log {
    constructor(core) {
        this.core = core;
    }
    append(item) {
        return new Log(this.core.concat(item));
    }
}

function updateChange(x) {
    return function (state) {
        if (state==null) {
            return [{
                version: x.version,
                value: x.value
            }, null];
        }
        if (state.version != x.version) {
            return [state, log().append(msg("ERRNO014"))]
        } else {
            return [{
                version: state.version+1,
                value: x.value
            }, null]
        }
    }
}

function initChange(x) {
    return function (state) {
        if (state==null) {
            return [{
                version: 0,
                value: x
            }, null]
        } else {
            return [state, null]
        }
    }
}

async function loopOnError(timer, action, errors) {
    while (true) {
        // to put each while's iteration as a new event in the event loop
        await timer.yield();
        try {
            return await action();
        } catch(e) {
            if (errors.some(isError => isError(e))) {
                continue;
            }
            throw e;
        }
    }
}

const typedRespondAbstractFactory = respondType => details => ({ "status": respondType, "details": details });

const OK = typedRespondAbstractFactory("OK");
const NO = typedRespondAbstractFactory("NO");
const UNKNOWN = typedRespondAbstractFactory("UNKNOWN");

async function change(core, key, update, extra) {
    try {
        return OK(await core.change(key, x => {
            var [val, err] = update(x);
            if (err != null) {
                throw err;
            } else {
                return val;
            }
        }, extra)).details;
    } catch (e) {
        if (e instanceof ProposerError) {
            if (e.code == "ConcurrentRequestError") {
                return NO(log().append(msg("ERRNO002")).core);
            }
            if (e.code == "PrepareError") {
                return NO(log().append(msg("ERRNO009")).append(msg("ERRNO003")).core);
            }
            if (e.code == "CommitError") {
                return UNKNOWN(log().append(msg("ERRNO009")).append(msg("ERRNO004")).core);
            }
            if (e.code == "UpdateError") {
                return NO(e.err.append(msg("ERRNO005")).core);
            }
            throw e;
        } else {
            throw e;
        }
    }
}

/////////

function getErrorChecker(status, errors) {
    return function(e) {
        if (!e) return false;
        if (e.status!=status) return false;
        if (!e.details) return false;
        if (e.details.length!=errors.length) return false;
        for (const id of errors) {
            if (!e.details.some(x => x.id==id)) return false;
        }
        return true;
    };
}

const isAcceptUnknownError = getErrorChecker("UNKNOWN", ["ERRNO004","ERRNO009"]);
const isProposeNoError = getErrorChecker("NO", ["ERRNO003","ERRNO009"]);
const isConcurrentNoError = getErrorChecker("NO", ["ERRNO002"]);

function isUpdateChangeNoError(e) {
    if (!e) return false;
    if (e.status!="NO") return false;
    if (!e.details) return false;
    if (e.details.length!=2) return false;
    for (const id of ["ERRNO014","ERRNO005"]) {
        if (!e.details.some(x => x.id==id)) return false;
    }
    return true;
}

function isRetryCountExceedError(e) {
    if (!e) return false;
    return (e instanceof RetryCountExceedError)
}

const recoverableErrors = [
    isConcurrentNoError, isAcceptUnknownError, isProposeNoError, isUpdateChangeNoError
]

class ReadAllKeysClient {
    static spawn({ctx, id, proposers, keys, consistencyChecker}) {
        const c1 = new ReadAllKeysClient(ctx, consistencyChecker, id, keys);
        c1.proposers = [...proposers];
        c1.thread = c1.start();
        return c1;
    }
    constructor(ctx, consistencyChecker, id, keys) {
        this.ctx = ctx;
        this.consistencyChecker = consistencyChecker;
        this.id = id;
        this.keys = keys;
        this.proposers = [];
        this.error = null;
        this.stat = {
            tries: 0,
            writes: 0
        };
        this.thread = null;
    }
    async start() {
        try {
            for (const key of this.keys) {
                await loopOnError(this.ctx.timer, async () => {
                    const proposer = this.ctx.random.anyOf(this.proposers);

                    await (async function(timer, action, errors, times) {
                        while (times > 0) {
                            // to put each while's iteration as a new event in the event loop
                            await timer.yield();
                            try {
                                times--;
                                return await action();
                            } catch(e) {
                                if (errors.some(isError => isError(e))) {
                                    continue;
                                }
                                throw e;
                            }
                        }
                        throw new RetryCountExceedError();
                    })(this.ctx.timer, async () => {
                        this.stat.tries++;

                        let tx = this.consistencyChecker.tx(key);
                        const read = await change(proposer, key, initChange(0), this.id+":r");
                        tx.seen(read);

                        this.stat.writes++;
                    }, recoverableErrors, 2);

                }, [...recoverableErrors, isRetryCountExceedError]);
            }
        } catch (e) {
            this.error = e;
            throw e;
        }
        if (this.error) {
            throw this.error;
        }
    }
}

exports.ReadAllKeysClient = ReadAllKeysClient;<commit_msg>collapsing trash<commit_after>const {RetryCountExceedError} = require("./exceptions");
const {ProposerError} = require("../../../../src/Proposer");

/////////

function msg(id) {
    return {"id": id};
}

function log() {
    return new Log([]);
}

class Log {
    constructor(core) {
        this.core = core;
    }
    append(item) {
        return new Log(this.core.concat(item));
    }
}

function initChange(x) {
    return function (state) {
        if (state==null) {
            return [{
                version: 0,
                value: x
            }, null]
        } else {
            return [state, null]
        }
    }
}

const typedRespondAbstractFactory = respondType => details => ({ "status": respondType, "details": details });

const NO = typedRespondAbstractFactory("NO");
const UNKNOWN = typedRespondAbstractFactory("UNKNOWN");

async function change(core, key, update, extra) {
    return await core.change(key, x => {
        var [val, err] = update(x);
        if (err != null) {
            throw err;
        } else {
            return val;
        }
    }, extra);
}

/////////

function getErrorChecker(status, errors) {
    return function(e) {
        if (!e) return false;
        if (e.status!=status) return false;
        if (!e.details) return false;
        if (e.details.length!=errors.length) return false;
        for (const id of errors) {
            if (!e.details.some(x => x.id==id)) return false;
        }
        return true;
    };
}

const isAcceptUnknownError = getErrorChecker("UNKNOWN", ["ERRNO004","ERRNO009"]);
const isProposeNoError = getErrorChecker("NO", ["ERRNO003","ERRNO009"]);
const isConcurrentNoError = getErrorChecker("NO", ["ERRNO002"]);
const isUpdateChangeNoError = getErrorChecker("NO", ["ERRNO014", "ERRNO005"]);

const recoverableErrors = [
    isConcurrentNoError, isAcceptUnknownError, isProposeNoError, isUpdateChangeNoError
]

class ReadAllKeysClient {
    static spawn({ctx, id, proposers, keys, consistencyChecker}) {
        const c1 = new ReadAllKeysClient(ctx, consistencyChecker, id, keys);
        c1.proposers = [...proposers];
        c1.thread = c1.start();
        return c1;
    }
    constructor(ctx, consistencyChecker, id, keys) {
        this.ctx = ctx;
        this.consistencyChecker = consistencyChecker;
        this.id = id;
        this.keys = keys;
        this.proposers = [];
        this.error = null;
        this.stat = {
            tries: 0,
            writes: 0
        };
        this.thread = null;
    }
    async start() {
        try {
            for (const key of this.keys) {
                await (async () => {
                    while (true) {
                        const proposer = this.ctx.random.anyOf(this.proposers);
                        for (let i=0;i<2;i++) {
                            await this.ctx.timer.yield();
                            try {
                                this.stat.tries++;

                                let tx = this.consistencyChecker.tx(key);
                                const read = await change(proposer, key, initChange(0), this.id+":r");
                                tx.seen(read);

                                this.stat.writes++;

                                return;
                            } catch(e) {
                                if (e instanceof ProposerError) {
                                    if (e.code == "ConcurrentRequestError") {
                                        continue;
                                    }
                                    if (e.code == "PrepareError") {
                                        continue;
                                    }
                                    if (e.code == "CommitError") {
                                        continue;
                                    }
                                    if (e.code == "UpdateError") {
                                        continue;
                                    }
                                    throw e;
                                } else {
                                    throw e;
                                }
                                throw e;
                            }
                        }
                    }
                })();
            }
        } catch (e) {
            this.error = e;
            throw e;
        }
        if (this.error) {
            throw this.error;
        }
    }
}

exports.ReadAllKeysClient = ReadAllKeysClient;<|endoftext|>


<commit_before>
/**
 *  KitScript - A User Script Safari Extension
 *
 *  MainPanel.js - Javascript file containing functions for the main panel
 *  of the extension.
 *
 *  @author Seraf Dos Santos
 *  @copyright 2011-2012 Seraf Dos Santos - All rights reserved.
 *  @license MIT License
 *  @version 0.1
 */

// Run on load
jQuery(document).ready(function ($) {

    var _btnsSels = ['#ks-topmenu * a',
                        'ul.ks-vertmenu * a',
                        '#ks-gs-form * a',
                        '#ks-aus-form * a',
                        '#ks-alert-modal * a'];

    //
    $(_btnsSels.join(',')).click(function (evt) {

        var _req = $(this).attr("href");

        switch (_req) {
            case '#null':
                break;
            case '#enable-ks':
                ks.setEnable();
                break;
            case '#disable-ks':
                ks.setDisable();
                break;

            // Global Settings Buttons
            case '#ks-gs-btn-add':
                //alert(1);
                //ks.mainPanel.globalSettingsForm.addExclude();
                break;
            case '#ks-gs-btn-edit':
                //ks.mainPanel.globalSettingsForm.editExclude();
                break;
            case '#ks-gs-btn-remove':
                ks.mainPanel.globalSettingsForm.removeExclude();
                break;

            // New User Script
            case '#ks-aus-btn-add':
                ks.mainPanel.newUserScriptForm.addUserScript();
                break;

            // User Script Settings
            case '#':
                break;

            // Alert Modal
            case '#ks-alert-close':
                //ks.mainPanel.hideAlert();
                break;

            default:
                ks.mainPanel.contentManager.transitContent(_req);
        }
    });
});

/*

{{us_name}}
 {{us_desc}}

<button type="button" onclick="ks.mainPanel.managePanel.openUserScriptSettings({{us_id}})" class="btn">Settings</button>

<button type="button" onclick="ks.mainPanel.managePanel.disableUserScript({{us_id}})" class="btn">Disable</button>

<button type="button" onclick="ks.mainPanel.managePanel.deleteUserScript({{us_id}})" class="btn">Delete</button>

*/
<commit_msg>Renaming variable<commit_after>
/**
 *  KitScript - A User Script Safari Extension
 *
 *  MainPanel.js - Javascript file containing functions for the main panel
 *  of the extension.
 *
 *  @author Seraf Dos Santos <webmaster@cyb3r.ca>
 *  @copyright 2011-2012 Seraf Dos Santos - All rights reserved.
 *  @license MIT License
 *  @version 0.1
 */

// Run on load
jQuery(document).ready(function ($) {

    var _btnsSelectors = ['#ks-topmenu * a',
                            'ul.ks-vertmenu * a',
                            '#ks-gs-form * a',
                            '#ks-aus-form * a',
                            '#ks-alert-modal * a'
                        ];

    //
    $(_btnsSelectors.join(',')).click(function (evt) {

        var _req = $(this).attr("href");

        switch (_req) {
            case '#null':
                break;
            case '#enable-ks':
                ks.setEnable();
                break;
            case '#disable-ks':
                ks.setDisable();
                break;

            // Global Settings Buttons
            case '#ks-gs-btn-add':
                //alert(1);
                //ks.mainPanel.globalSettingsForm.addExclude();
                break;
            case '#ks-gs-btn-edit':
                //ks.mainPanel.globalSettingsForm.editExclude();
                break;
            case '#ks-gs-btn-remove':
                ks.mainPanel.globalSettingsForm.removeExclude();
                break;

            // New User Script
            case '#ks-aus-btn-add':
                ks.mainPanel.newUserScriptForm.addUserScript();
                break;

            // User Script Settings
            case '#':
                break;

            // Alert Modal
            case '#ks-alert-close':
                //ks.mainPanel.hideAlert();
                break;

            default:
                ks.mainPanel.contentManager.transitContent(_req);
        }
    });
});

/*

{{us_name}}
 {{us_desc}}

<button type="button" onclick="ks.mainPanel.managePanel.openUserScriptSettings({{us_id}})" class="btn">Settings</button>

<button type="button" onclick="ks.mainPanel.managePanel.disableUserScript({{us_id}})" class="btn">Disable</button>

<button type="button" onclick="ks.mainPanel.managePanel.deleteUserScript({{us_id}})" class="btn">Delete</button>

*/
<|endoftext|>


<commit_before>'use strict';

var ejs = require('elastic.js');
var client = require('../services/elasticsearch.js');
var Boom = require('boom');

module.exports = function (params, request, cb) {

  var err;
  var supported_query_re = new RegExp('^[0-9a-zA-Z#\*\.\_\:\(\)\"\\[\\]\{\}\\-\\+\>\<\= ]+$');

  // Build Elastic Search Query
  var q = ejs.Request();

  if (params.search) {
    if (!supported_query_re.test(params.search)) {
      err = Boom.create(400, 'Search not supported: ' + params.search, { timestamp: Date.now() });
      return cb(err);
    }

    q.query(ejs.QueryStringQuery(params.search));
  } else if (params.count) {
    q.facet(ejs.TermsFacet('count').fields([params.count]).size(request.limit));
  } else {
    q.query(ejs.MatchAllQuery()).sort('acquisitionDate', 'desc');
  }

  //Legacy support for skip parameter
  if (params.skip) {
    request.page = Math.floor(parseInt(params.skip) / request.limit);
  }

  // Decide from
  var from = (request.page - 1) * request.limit;

  var search_params = {
    index: process.env.ES_INDEX || 'landsat',
    body: q
  }

  if (!params.count) {
    search_params.from = from;
    search_params.size = request.limit;
  }

  client.search(search_params).then(function (body) {

    var response = [];
    var count = 0;

    // Process Facets
    if (params.count) {

      // Term facet count
      if (body.facets.count.terms.length != 0) {
        response = body.facets.count.terms;
        count = body.facets.count.total;
      } else {
        return cb(Boom.notFound('Nothing to count!'));
      }

    // Process search
    } else {
      if (body.hits.hits.length === 0) {
        return cb(Boom.notFound('No matches found!'));
      }
      count = body.hits.total;

      for (var i = 0; i < body.hits.hits.length; i++) {
        response.push(body.hits.hits[i]._source);
      }
    }

    return cb(err, response, count);
  }, function(err) {
      return cb(Boom.badRequest(err));
  });

};
<commit_msg>move legacy parameters to a separate function<commit_after>'use strict';

var ejs = require('elastic.js');
var client = require('../services/elasticsearch.js');
var Boom = require('boom');

var legacyParams = function (params, q, limit) {
  var err;
  var supported_query_re = new RegExp('^[0-9a-zA-Z#\*\.\_\:\(\)\"\\[\\]\{\}\\-\\+\>\<\= ]+$');

  if (params.search) {
    if (!supported_query_re.test(params.search)) {
      err = Boom.create(400, 'Search not supported: ' + params.search, { timestamp: Date.now() });
      throw err;
    }

    q.query(ejs.QueryStringQuery(params.search));
  } else if (params.count) {
    q.facet(ejs.TermsFacet('count').fields([params.count]).size(limit));
  }

  return q;
};

module.exports = function (params, request, cb) {
  var err;

  // Build Elastic Search Query
  var q = ejs.Request();

  if (Object.keys(params).length === 0) {
    q.query(ejs.MatchAllQuery()).sort('acquisitionDate', 'desc');
  }

  // Do legacy search
  if (params.search || params.count) {
    q = legacyParams(params, q, request.limit);
  }

  // Legacy support for skip parameter
  if (params.skip) {
    request.page = Math.floor(parseInt(params.skip, 10) / request.limit);
  }

  // Decide from
  var from = (request.page - 1) * request.limit;

  var search_params = {
    index: process.env.ES_INDEX || 'landsat',
    body: q
  };

  if (!params.count) {
    search_params.from = from;
    search_params.size = request.limit;
  }

  client.search(search_params).then(function (body) {
    var response = [];
    var count = 0;

    // Process Facets
    if (params.count) {
      // Term facet count
      if (body.facets.count.terms.length !== 0) {
        response = body.facets.count.terms;
        count = body.facets.count.total;
      } else {
        return cb(Boom.notFound('Nothing to count!'));
      }

    // Process search
    } else {
      if (body.hits.hits.length === 0) {
        return cb(Boom.notFound('No matches found!'));
      }
      count = body.hits.total;

      for (var i = 0; i < body.hits.hits.length; i++) {
        response.push(body.hits.hits[i]._source);
      }
    }

    return cb(err, response, count);
  }, function (err) {
    return cb(Boom.badRequest(err));
  });
};
<|endoftext|>


<commit_before>/**
 *
 * @copyright Copyright (c) 2020, ownCloud GmbH
 *
 * This code is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License, version 3,
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

/**
 * This gets only loaded if there is a grace period active
 */
OC.License = {
	NOTIFICATION_TEMPLATE:
		'
' +
		'
' +
		'    {{#each msgs}}' +
		'
' +
		'      {{this}}' +
		'
' +
		'    {{/each}}' +
		'
' +
		'
{{clickable_link_text}}
' +
		'
' +
		'
{{time_remaining_msg}}
' +
		'
' +
		'
' +
		'  <input id="license" type="text" placeholder="{{placeholder}}" style="flex:1 1 auto"/>' +
		'  <button id="license_button">{{license_button_text}}</button>' +
		'
' +
		'
',

	showNotification: function() {
		if (!this._template) {
			this._template = Handlebars.compile(this.NOTIFICATION_TEMPLATE);
		}

		var tmpl = this._template;
		var self = this;

		$.get(OC.generateUrl('/license/graceperiod'))
			.done(function(data) {
				if (!data || data.apps < 1) {
					return;
				}
				// adjust moment's time thresholds for better accuracy:
				// it will show "24 hours" instead of "a day" and "89 minutes" instead of "2 hours"
				// we'll have to restore the defaults afterwards.
				var thresholdH = moment.relativeTimeThreshold('h');
				var thresholdM = moment.relativeTimeThreshold('m');
				moment.relativeTimeThreshold('h', 25);
				moment.relativeTimeThreshold('m', 90);
				var relativeTime = moment(data.end * 1000).fromNow(true);
				moment.relativeTimeThreshold('h', thresholdH);
				moment.relativeTimeThreshold('m', thresholdM);

				var paragraphs = [
					t('core', 'You have enabled one or more ownCloud Enterprise apps but your installation does not have a valid license yet.'),
					t('core', 'A grace period of 24 hours has started to allow you to get going right away. Once the grace period ends, all Enterprise apps will become disabled unless you supply a valid license key.'),
					t('core', 'To try ownCloud Enterprise, just start a 30-day demo and enter the provided license key below.')
				];

				var dialog = $(tmpl({
					title: t('core', 'Upgrade to ownCloud Enterprise'),
					msgs: paragraphs,
					time_remaining_msg: t('core', 'Remaining time: {rtime}', {
						rtime: relativeTime
					}),
					clickable_link: 'https://marketplace.owncloud.com/demo-key',
					clickable_link_text: t('core', 'Get your demo key'),
					placeholder: t('core', 'Enter license key'),
					license_button_text: t('core', 'Set new key')
				}))
					.appendTo('body')
					.ocdialog({
						modal: true
					});

				self._initializeDialogEvents(dialog);
			});
	},

	_initializeDialogEvents: function(jqDialog) {
		jqDialog.on("ocdialogclose", function() {
			jqDialog.remove();
		});

		jqDialog.find('#license_button').click(function () {
			var license = jqDialog.find('#license').val();
			$.post(OC.generateUrl('/license/license'), {
				licenseString: license
			}).done(function () {
				jqDialog.ocdialog('close');
			})
		});
	}
}

$(document).ready(function(){
	OC.License.showNotification();
});
<commit_msg>Change clickable link and open it in new tab<commit_after>/**
 *
 * @copyright Copyright (c) 2020, ownCloud GmbH
 *
 * This code is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License, version 3,
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

/**
 * This gets only loaded if there is a grace period active
 */
OC.License = {
	NOTIFICATION_TEMPLATE:
		'
' +
		'
' +
		'    {{#each msgs}}' +
		'
' +
		'      {{this}}' +
		'
' +
		'    {{/each}}' +
		'
' +
		'
{{clickable_link_text}}
' +
		'
' +
		'
{{time_remaining_msg}}
' +
		'
' +
		'
' +
		'  <input id="license" type="text" placeholder="{{placeholder}}" style="flex:1 1 auto"/>' +
		'  <button id="license_button">{{license_button_text}}</button>' +
		'
' +
		'
',

	showNotification: function() {
		if (!this._template) {
			this._template = Handlebars.compile(this.NOTIFICATION_TEMPLATE);
		}

		var tmpl = this._template;
		var self = this;

		$.get(OC.generateUrl('/license/graceperiod'))
			.done(function(data) {
				if (!data || data.apps < 1) {
					return;
				}
				// adjust moment's time thresholds for better accuracy:
				// it will show "24 hours" instead of "a day" and "89 minutes" instead of "2 hours"
				// we'll have to restore the defaults afterwards.
				var thresholdH = moment.relativeTimeThreshold('h');
				var thresholdM = moment.relativeTimeThreshold('m');
				moment.relativeTimeThreshold('h', 25);
				moment.relativeTimeThreshold('m', 90);
				var relativeTime = moment(data.end * 1000).fromNow(true);
				moment.relativeTimeThreshold('h', thresholdH);
				moment.relativeTimeThreshold('m', thresholdM);

				var paragraphs = [
					t('core', 'You have enabled one or more ownCloud Enterprise apps but your installation does not have a valid license yet.'),
					t('core', 'A grace period of 24 hours has started to allow you to get going right away. Once the grace period ends, all Enterprise apps will become disabled unless you supply a valid license key.'),
					t('core', 'To try ownCloud Enterprise, just start a 30-day demo and enter the provided license key below.')
				];

				var dialog = $(tmpl({
					title: t('core', 'Upgrade to ownCloud Enterprise'),
					msgs: paragraphs,
					time_remaining_msg: t('core', 'Remaining time: {rtime}', {
						rtime: relativeTime
					}),
					clickable_link: 'https://owncloud.com/try-enterprise/',
					clickable_link_text: t('core', 'Get your demo key'),
					placeholder: t('core', 'Enter license key'),
					license_button_text: t('core', 'Set new key')
				}))
					.appendTo('body')
					.ocdialog({
						modal: true
					});

				self._initializeDialogEvents(dialog);
			});
	},

	_initializeDialogEvents: function(jqDialog) {
		jqDialog.on("ocdialogclose", function() {
			jqDialog.remove();
		});

		jqDialog.find('#license_button').click(function () {
			var license = jqDialog.find('#license').val();
			$.post(OC.generateUrl('/license/license'), {
				licenseString: license
			}).done(function () {
				jqDialog.ocdialog('close');
			})
		});
	}
}

$(document).ready(function(){
	OC.License.showNotification();
});
<|endoftext|>


<commit_before>'use strict';

const Bot = require('slackbots');
const fs = require('fs');
const CronJob = require('cron').CronJob;
const Model = require('./model');
const MessageService = require('./messageservice');
const AttendanceEnum = require('./participant').AttendanceEnum;

const States = Object.freeze({
  IDLE: 'idle',
  ASKED_RESPONSBILE: 'asked responsible',
  FOUND_RESPONSIBLE: 'found responsible',
  NO_RESPONSIBLE: 'no responsible',
  AWAITING_MEETING: 'awaiting meeting',
  SKIPPED: 'skipped'
});

class RoundpiecesBot extends Bot {
  constructor(settings) {
    super(settings);
    this.settings = settings;
    this.state = States.IDLE;
    this.model = new Model(this.settings.adminUserName);
    this.messageService = new MessageService((userName, message) => this.postMessageToUser(userName, message), this.model);
  }

  run() {
    this.on('start', this._onStart);
    this.on('message', this._onMessage);
  }

  get state() {
    return this._state;
  }

  set state(state) {
    this._state = state;
  }

  _onStart() {
    this.startTime = Date.now();

    fs.readFile(this.settings.listPath, 'utf8', (error, data) => this._setup(error, data));
  }

  _setup(error, data) {
    if (error) {
      this._reportError(error);
    }
    else {
      this.model.users = this.users;
      this.model.participants = data;
      if (this.model.getParticipantCount() < 1) {
        this._reportError('No participants in list');
        return;
      }
      this.model.setResponsible(this.model.participants[0]);
      this._setupCronJobs();

      this.messageService.botActivated(this.settings.name);
    }
  }

  _setupCronJobs() {
    const cronRanges = this.settings.cronRanges;
    //TODO also let administrator invoke script directly in case of virtual Friday
    new CronJob(cronRanges.start, () => this._startResponsibleSearch(), null, true);
    new CronJob(cronRanges.end, () => this._endResponsibleSearch(), null, true);
    new CronJob(cronRanges.reset, () => this._reset(), null, true);
  }

  _startResponsibleSearch() {
    if (this.state !== States.SKIPPED) {
      this.state = States.ASKED_RESPONSBILE;
      this._notifyParticipants();
    }
  }

  _endResponsibleSearch() {
    switch (this.state) {
      case States.FOUND_RESPONSIBLE:
        this.messageService.participationList();
        break;
      case States.ASKED_RESPONSBILE:
        this.messageService.noResponsibleResponse();
        break;
      case States.NO_RESPONSIBLE:
      case States.SKIPPED:
        //No attendance - don't do anything
        break;
    }
    this.state = States.AWAITING_MEETING;
  }

  _reset() {
    this.model.reset();
    this.state = States.IDLE;
  }

  _onMessage(message) {
    if (message.type === 'message' && message.user) {
      const participant = this.model.getParticipantFromId(message.user);
      if (!participant) {
        this._reportError(`Unknown user id: ${message.user}`);
        console.log(message);
        return;
      }

      if (message.text.startsWith('admin')) {
        if (participant.admin) {
          const messageParts = message.text.split(' ');
          switch (messageParts[1]) {
            case 'help':
              this.messageService.adminHelp();
              break;
            case 'setResponsible':
              this._changeResponsible(messageParts[2]);
              break;
            case 'skip':
              this._skipNextMeeting();
              break;
            default:
              this.messageService.unknownCommand(participant.username);
              break;
          }
        }
        else {
          this.messageService.notAdmin(participant.username);
        }
      }
      else {
        switch (message.text.toLowerCase()) {
          case 'help':
          case '?':
            this.messageService.help(participant.username);
            break;
          case 'status':
            this._printStatus(participant.username);
            break;
          case 'next':
            this.messageService.next(participant.username);
            break;
          case 'list':
            this.messageService.list(participant.username);
            break;
          case 'accept':
            this._accept(participant);
            break;
          case 'reject':
            this._reject(participant);
            break;
          case 'yes':
            this._attending(participant);
            break;
          case 'no':
            this._notAttending(participant);
            break;
          default:
            this.messageService.unknownCommand(participant.username);
            break;
        }
      }
    }
  }

  _printStatus(userName) {
    const uptime = Date.now() - this.startTime;
    this.messageService.status(userName, uptime);
  }

  _accept(participant) {
    if (!participant.responsible) {
      this.messageService.notResponsible(participant.username);
      return;
    }

    if (this.state !== States.ASKED_RESPONSBILE) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    this.messageService.accepted(participant.username);
    participant.attending = AttendanceEnum.ATTENDING;
    this.state = States.FOUND_RESPONSIBLE;
    this._updateList();
  }

  _updateList() {
    this.model.updateList();
    //Persist list
    const list = this.model.getParticipantUserNames().join('\n');
    fs.writeFile(this.settings.listPath, list, (error) => {
      if (error) {
        this._reportError(`Failed to save list due to ${error}. List is currently: ${list}`);
      }
    });
  }

  _reject(participant) {
    if (!participant.responsible) {
      this.messageService.notResponsible(participant.username);
      return;
    }

    if (this.state !== States.ASKED_RESPONSBILE) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    this.messageService.rejected(participant.username);
    participant.attending = AttendanceEnum.NOT_ATTENDING;
    const nextUser = this.model.getNextParticipant(participant);
    if (!nextUser) {
      this.state = States.NO_RESPONSIBLE;
      this.messageService.nobodyAttending();
      this.model.setResponsible(this.model.participants[0]);
    }
    else {
      this.model.setResponsible(nextUser);
      this.messageService.notifyResponsible();
    }
  }

  _attending(participant) {
    if (participant.responsible) {
      this.messageService.isResponsible();
      return;
    }

    if (!this._canChangeAttendanceStatus()) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    participant.attending = AttendanceEnum.ATTENDING;
    this.messageService.attending(participant.username);
  }

  _notAttending(participant) {
    if (participant.responsible) {
      this.messageService.isResponsible();
      return;
    }

    if (!this._canChangeAttendanceStatus()) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    participant.attending = AttendanceEnum.NOT_ATTENDING;
    this.messageService.notAttending(participant.username);
  }

  _canChangeAttendanceStatus() {
    return this.state === States.ASKED_RESPONSBILE || this.state === States.FOUND_RESPONSIBLE;
  }

  _changeResponsible(newResposibleUserName) {
    const responsible = this.model.getParticipantFromUserName(newResposibleUserName);
    if (responsible) {
      this.messageService.noLongerResponsible();
      this.model.setResponsible(responsible);
      this._updateList();
      this.messageService.participationList();
      this.messageService.responsibleChanged(responsible.username);
    }
    else {
      this.messageService.notParticipant(newResposibleUserName);
    }
  }

  _skipNextMeeting() {
    this.state = States.SKIPPED;
    this.messageService.skipping();
  }

  _notifyParticipants() {
    this.messageService.notifyResponsible();
    this.messageService.queryForAttendance();
  }

  _reportError(error) {
    console.error(error);
    this.messageService.error(error);
  }
}

module.exports = RoundpiecesBot;
<commit_msg>Only cancel previous responsible's message if responsible actually changes. Forcing accept for the current responsible caused confusion.<commit_after>'use strict';

const Bot = require('slackbots');
const fs = require('fs');
const CronJob = require('cron').CronJob;
const Model = require('./model');
const MessageService = require('./messageservice');
const AttendanceEnum = require('./participant').AttendanceEnum;

const States = Object.freeze({
  IDLE: 'idle',
  ASKED_RESPONSBILE: 'asked responsible',
  FOUND_RESPONSIBLE: 'found responsible',
  NO_RESPONSIBLE: 'no responsible',
  AWAITING_MEETING: 'awaiting meeting',
  SKIPPED: 'skipped'
});

class RoundpiecesBot extends Bot {
  constructor(settings) {
    super(settings);
    this.settings = settings;
    this.state = States.IDLE;
    this.model = new Model(this.settings.adminUserName);
    this.messageService = new MessageService((userName, message) => this.postMessageToUser(userName, message), this.model);
  }

  run() {
    this.on('start', this._onStart);
    this.on('message', this._onMessage);
  }

  get state() {
    return this._state;
  }

  set state(state) {
    this._state = state;
  }

  _onStart() {
    this.startTime = Date.now();

    fs.readFile(this.settings.listPath, 'utf8', (error, data) => this._setup(error, data));
  }

  _setup(error, data) {
    if (error) {
      this._reportError(error);
    }
    else {
      this.model.users = this.users;
      this.model.participants = data;
      if (this.model.getParticipantCount() < 1) {
        this._reportError('No participants in list');
        return;
      }
      this.model.setResponsible(this.model.participants[0]);
      this._setupCronJobs();

      this.messageService.botActivated(this.settings.name);
    }
  }

  _setupCronJobs() {
    const cronRanges = this.settings.cronRanges;
    //TODO also let administrator invoke script directly in case of virtual Friday
    new CronJob(cronRanges.start, () => this._startResponsibleSearch(), null, true);
    new CronJob(cronRanges.end, () => this._endResponsibleSearch(), null, true);
    new CronJob(cronRanges.reset, () => this._reset(), null, true);
  }

  _startResponsibleSearch() {
    if (this.state !== States.SKIPPED) {
      this.state = States.ASKED_RESPONSBILE;
      this._notifyParticipants();
    }
  }

  _endResponsibleSearch() {
    switch (this.state) {
      case States.FOUND_RESPONSIBLE:
        this.messageService.participationList();
        break;
      case States.ASKED_RESPONSBILE:
        this.messageService.noResponsibleResponse();
        break;
      case States.NO_RESPONSIBLE:
      case States.SKIPPED:
        //No attendance - don't do anything
        break;
    }
    this.state = States.AWAITING_MEETING;
  }

  _reset() {
    this.model.reset();
    this.state = States.IDLE;
  }

  _onMessage(message) {
    if (message.type === 'message' && message.user) {
      const participant = this.model.getParticipantFromId(message.user);
      if (!participant) {
        this._reportError(`Unknown user id: ${message.user}`);
        console.log(message);
        return;
      }

      if (message.text.startsWith('admin')) {
        if (participant.admin) {
          const messageParts = message.text.split(' ');
          switch (messageParts[1]) {
            case 'help':
              this.messageService.adminHelp();
              break;
            case 'setResponsible':
              this._changeResponsible(messageParts[2]);
              break;
            case 'skip':
              this._skipNextMeeting();
              break;
            default:
              this.messageService.unknownCommand(participant.username);
              break;
          }
        }
        else {
          this.messageService.notAdmin(participant.username);
        }
      }
      else {
        switch (message.text.toLowerCase()) {
          case 'help':
          case '?':
            this.messageService.help(participant.username);
            break;
          case 'status':
            this._printStatus(participant.username);
            break;
          case 'next':
            this.messageService.next(participant.username);
            break;
          case 'list':
            this.messageService.list(participant.username);
            break;
          case 'accept':
            this._accept(participant);
            break;
          case 'reject':
            this._reject(participant);
            break;
          case 'yes':
            this._attending(participant);
            break;
          case 'no':
            this._notAttending(participant);
            break;
          default:
            this.messageService.unknownCommand(participant.username);
            break;
        }
      }
    }
  }

  _printStatus(userName) {
    const uptime = Date.now() - this.startTime;
    this.messageService.status(userName, uptime);
  }

  _accept(participant) {
    if (!participant.responsible) {
      this.messageService.notResponsible(participant.username);
      return;
    }

    if (this.state !== States.ASKED_RESPONSBILE) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    this.messageService.accepted(participant.username);
    participant.attending = AttendanceEnum.ATTENDING;
    this.state = States.FOUND_RESPONSIBLE;
    this._updateList();
  }

  _updateList() {
    this.model.updateList();
    //Persist list
    const list = this.model.getParticipantUserNames().join('\n');
    fs.writeFile(this.settings.listPath, list, (error) => {
      if (error) {
        this._reportError(`Failed to save list due to ${error}. List is currently: ${list}`);
      }
    });
  }

  _reject(participant) {
    if (!participant.responsible) {
      this.messageService.notResponsible(participant.username);
      return;
    }

    if (this.state !== States.ASKED_RESPONSBILE) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    this.messageService.rejected(participant.username);
    participant.attending = AttendanceEnum.NOT_ATTENDING;
    const nextUser = this.model.getNextParticipant(participant);
    if (!nextUser) {
      this.state = States.NO_RESPONSIBLE;
      this.messageService.nobodyAttending();
      this.model.setResponsible(this.model.participants[0]);
    }
    else {
      this.model.setResponsible(nextUser);
      this.messageService.notifyResponsible();
    }
  }

  _attending(participant) {
    if (participant.responsible) {
      this.messageService.isResponsible();
      return;
    }

    if (!this._canChangeAttendanceStatus()) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    participant.attending = AttendanceEnum.ATTENDING;
    this.messageService.attending(participant.username);
  }

  _notAttending(participant) {
    if (participant.responsible) {
      this.messageService.isResponsible();
      return;
    }

    if (!this._canChangeAttendanceStatus()) {
      this.messageService.wrongTime(participant.username);
      return;
    }

    participant.attending = AttendanceEnum.NOT_ATTENDING;
    this.messageService.notAttending(participant.username);
  }

  _canChangeAttendanceStatus() {
    return this.state === States.ASKED_RESPONSBILE || this.state === States.FOUND_RESPONSIBLE;
  }

  _changeResponsible(newResposibleUserName) {
    const responsible = this.model.getParticipantFromUserName(newResposibleUserName);
    if (responsible) {
      if (responsible !== this.model.getResponsible()) {
        this.messageService.noLongerResponsible();
        this.model.setResponsible(responsible);
      }
      this._updateList();
      this.messageService.participationList();
      this.messageService.responsibleChanged(responsible.username);
    }
    else {
      this.messageService.notParticipant(newResposibleUserName);
    }
  }

  _skipNextMeeting() {
    this.state = States.SKIPPED;
    this.messageService.skipping();
  }

  _notifyParticipants() {
    this.messageService.notifyResponsible();
    this.messageService.queryForAttendance();
  }

  _reportError(error) {
    console.error(error);
    this.messageService.error(error);
  }
}

module.exports = RoundpiecesBot;
<|endoftext|>


<commit_before>import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import { next } from '@ember/runloop';
import { render, click, triggerEvent } from '@ember/test-helpers';

module(
  'component',
  'Integration | Component | click outside',
  function (hooks) {
    setupRenderingTest(hooks);

    test('smoke test', async function (assert) {
      assert.expect(2);

      this.set('didClickOutside', (e) => {
        assert.ok('`didClickOutside` fired only once');
        assert.equal(
          e.target.className,
          'outside',
          'the event object was passed and is correct'
        );
      });

      await render(hbs`

Somewhere, over the rainbow...

      {{#click-outside onClickOutside=(action didClickOutside)}}

We're in

      {{/click-outside}}
    `);

      // It's important to fire the actions in the next run loop. Failing to do so
      // would make the outside click not to fire. The reason for this is more
      // often than not the component is rendered as a result of some user
      // interaction, mainly a click. If the component attached the outside click
      // event handler in the same loop, the handler would catch the event and send
      // the action immediately.
      await next(async () => {
        await click('.inside');
        await click('.outside');
      });
    });

    test('real-world scenario', async function (assert) {
      this.isOpened = false;

      this.open = () => {
        this.set('isOpened', true);
      };

      this.close = () => {
        assert.ok(true, 'The close handler was called');
        this.set('isOpened', false);
      };

      await render(hbs`
      <button data-test-open onclick={{action this.open}}>
        Toggle popover
      </button>

Outside

      {{#if this.isOpened}}
        {{#click-outside onClickOutside=(action this.close)}}
          Popover is opened.
        {{/click-outside}}
      {{/if}}
    `);

      await click('[data-test-open]');
      await click('[data-test-outside]');
    });

    test(`it doesn't throw without an onClickOutside handler`, async function (assert) {
      assert.expect(0);

      await render(hbs`

Somewhere, over the rainbow...

      {{#click-outside}}

We're in

      {{/click-outside}}
    `);

      await click('.outside');
    });

    test('except selector', async function (assert) {
      assert.expect(1);

      this.set('didClickOutside', () => {
        assert.ok('`didClickOutside` fired only once');
      });

      await render(hbs`

Somewhere, over the rainbow...

        Somewhere, under the rainbow...

      {{#click-outside exceptSelector=".except-outside" onClickOutside=(action didClickOutside)}}
      {{/click-outside}}
    `);

      // It's important to fire the actions in the next run loop. Failing to do so
      // would make the outside click not to fire. The reason for this is more
      // often than not the component is rendered as a result of some user
      // interaction, mainly a click. If the component attached the outside click
      // event handler in the same loop, the handler would catch the event and send
      // the action immediately.
      await next(async () => {
        await click('.outside');
        await click('.except-outside');
      });
    });

    test('deprecated `except-selector` still works', async function (assert) {
      assert.expect(2);

      this.set('didClickOutside', () => {
        assert.ok('`didClickOutside` fired only once');
      });

      await render(hbs`

Somewhere, over the rainbow...

        Somewhere, under the rainbow...

      {{#click-outside except-selector=".except-outside" onClickOutside=(action didClickOutside)}}
      {{/click-outside}}
    `);

      // It's important to fire the actions in the next run loop. Failing to do so
      // would make the outside click not to fire. The reason for this is more
      // often than not the component is rendered as a result of some user
      // interaction, mainly a click. If the component attached the outside click
      // event handler in the same loop, the handler would catch the event and send
      // the action immediately.
      await next(async () => {
        await click('.outside');
        await click('.except-outside');
      });

      assert.expectDeprecation();
    });

    test('event type', async function (assert) {
      assert.expect(1);

      this.set('didClickOutside', () => {
        assert.ok('`didClickOutside` fired only once');
      });

      this.set('toggleFlag', () => {
        this.set('topSide', true);
      });

      await render(hbs`
      {{#if topSide}}
        Blue
      {{else}}

Yellow

      {{/if}}

      {{#click-outside eventType='mousedown' onClickOutside=(action didClickOutside)}}
      {{/click-outside}}
    `);

      await next(async () => {
        await triggerEvent('.outside', 'mousedown');
      });
    });

    test('handle removed DOM element outside', async function (assert) {
      assert.expect(1);

      this.set('didClickOutside', () => {
        assert.ok('`didClickOutside` fired only once');
      });

      this.set('toggleFlag', () => {
        this.set('topSide', true);
      });

      await render(hbs`
      {{#if topSide}}
        Blue
      {{else}}

Yellow

      {{/if}}

      {{#click-outside onClickOutside=(action didClickOutside)}}
      {{/click-outside}}
    `);

      await next(async () => {
        await click('.outside');
      });
    });

    test('`action` handler is still valid', async function (assert) {
      assert.expect(2);

      this.setProperties({
        onClickOutside: () => assert.ok('`onClickOutside` fired once'),
      });

      await render(hbs`

Somewhere, over the rainbow...

      {{#click-outside action=(action onClickOutside)}}

We're in

      {{/click-outside}}
    `);

      await next(async () => {
        await click('.inside');
        await click('.outside');
      });

      assert.expectDeprecation();
    });
  }
);
<commit_msg>Add await settled after waiting for next<commit_after>import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import { next } from '@ember/runloop';
import { render, click, settled, triggerEvent } from '@ember/test-helpers';

module(
  'component',
  'Integration | Component | click outside',
  function (hooks) {
    setupRenderingTest(hooks);

    test('smoke test', async function (assert) {
      assert.expect(2);

      this.set('didClickOutside', (e) => {
        assert.ok('`didClickOutside` fired only once');
        assert.equal(
          e.target.className,
          'outside',
          'the event object was passed and is correct'
        );
      });

      await render(hbs`

Somewhere, over the rainbow...

      {{#click-outside onClickOutside=(action didClickOutside)}}

We're in

      {{/click-outside}}
    `);

      // It's important to fire the actions in the next run loop. Failing to do so
      // would make the outside click not to fire. The reason for this is more
      // often than not the component is rendered as a result of some user
      // interaction, mainly a click. If the component attached the outside click
      // event handler in the same loop, the handler would catch the event and send
      // the action immediately.
      await next(async () => {
        await click('.inside');
        await click('.outside');
      });
      await settled();
    });

    test('real-world scenario', async function (assert) {
      assert.expect(1);
      this.isOpened = false;

      this.open = () => {
        this.set('isOpened', true);
      };

      this.close = () => {
        assert.ok(true, 'The close handler was called');
        this.set('isOpened', false);
      };

      await render(hbs`
      <button data-test-open onclick={{action this.open}}>
        Toggle popover
      </button>

Outside

      {{#if this.isOpened}}
        {{#click-outside onClickOutside=(action this.close)}}
          Popover is opened.
        {{/click-outside}}
      {{/if}}
    `);

      await click('[data-test-open]');
      await click('[data-test-outside]');
    });

    test(`it doesn't throw without an onClickOutside handler`, async function (assert) {
      assert.expect(0);

      await render(hbs`

Somewhere, over the rainbow...

      {{#click-outside}}

We're in

      {{/click-outside}}
    `);

      await click('.outside');
    });

    test('except selector', async function (assert) {
      assert.expect(1);

      this.set('didClickOutside', () => {
        assert.ok('`didClickOutside` fired only once');
      });

      await render(hbs`

Somewhere, over the rainbow...

        Somewhere, under the rainbow...

      {{#click-outside exceptSelector=".except-outside" onClickOutside=(action didClickOutside)}}
      {{/click-outside}}
    `);

      // It's important to fire the actions in the next run loop. Failing to do so
      // would make the outside click not to fire. The reason for this is more
      // often than not the component is rendered as a result of some user
      // interaction, mainly a click. If the component attached the outside click
      // event handler in the same loop, the handler would catch the event and send
      // the action immediately.
      await next(async () => {
        await click('.outside');
        await click('.except-outside');
      });
      await settled();
    });

    test('deprecated `except-selector` still works', async function (assert) {
      assert.expect(2);

      this.set('didClickOutside', () => {
        assert.ok('`didClickOutside` fired only once');
      });

      await render(hbs`

Somewhere, over the rainbow...

        Somewhere, under the rainbow...

      {{#click-outside except-selector=".except-outside" onClickOutside=(action didClickOutside)}}
      {{/click-outside}}
    `);

      // It's important to fire the actions in the next run loop. Failing to do so
      // would make the outside click not to fire. The reason for this is more
      // often than not the component is rendered as a result of some user
      // interaction, mainly a click. If the component attached the outside click
      // event handler in the same loop, the handler would catch the event and send
      // the action immediately.
      await next(async () => {
        await click('.outside');
        await click('.except-outside');
      });
      await settled();

      assert.expectDeprecation();
    });

    test('event type', async function (assert) {
      assert.expect(1);

      this.set('didClickOutside', () => {
        assert.ok('`didClickOutside` fired only once');
      });

      this.set('toggleFlag', () => {
        this.set('topSide', true);
      });

      await render(hbs`
      {{#if topSide}}
        Blue
      {{else}}

Yellow

      {{/if}}

      {{#click-outside eventType='mousedown' onClickOutside=(action didClickOutside)}}
      {{/click-outside}}
    `);

      await next(async () => {
        await triggerEvent('.outside', 'mousedown');
      });
      await settled();
    });

    test('handle removed DOM element outside', async function (assert) {
      assert.expect(1);

      this.set('didClickOutside', () => {
        assert.ok('`didClickOutside` fired only once');
      });

      this.set('toggleFlag', () => {
        this.set('topSide', true);
      });

      await render(hbs`
      {{#if topSide}}
        Blue
      {{else}}

Yellow

      {{/if}}

      {{#click-outside onClickOutside=(action didClickOutside)}}
      {{/click-outside}}
    `);

      await next(async () => {
        await click('.outside');
      });
      await settled();
    });

    test('`action` handler is still valid', async function (assert) {
      assert.expect(2);

      this.setProperties({
        onClickOutside: () => assert.ok('`onClickOutside` fired once'),
      });

      await render(hbs`

Somewhere, over the rainbow...

      {{#click-outside action=(action onClickOutside)}}

We're in

      {{/click-outside}}
    `);

      await next(async () => {
        await click('.inside');
        await click('.outside');
      });
      await settled();

      assert.expectDeprecation();
    });
  }
);
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * 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.
 */

foam.CLASS({
  package: 'com.google.foam.demos.heroes',
  name: 'Controller',
  extends: 'foam.u2.Element',

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

  requires: [
    'com.google.foam.demos.heroes.CitationView',
    'com.google.foam.demos.heroes.DashboardCitationView',
    'com.google.foam.demos.heroes.Hero',
    'foam.dao.ArrayDAO',
    'foam.u2.DAOList',
    'foam.u2.DetailView',
    'foam.u2.CheckBox'
  ],

  exports: [
    'as data',
    'heroDAO',
    'editHero'
  ],

  axioms: [
    foam.u2.CSS.create({
      code: function() {/*
        h2 { color: #aaa; }
        h3 {
          color: #444;
          font-size: 1.75em;
          font-weight: 100;
          margin-top: 0;
        }
        body { margin: 2em; }
        body, input[text], button { color: #888; font-family: Cambria, Georgia; }
        ^starred .foam-u2-DAOList { display: flex; }
        * { font-family: Arial; }
        input {
          font-size: 1em;
          height: 2em;
          padding-left: .4em;
        }
        ^nav .foam-u2-ActionView {
          background: #eee;
          border-radius: 4px;
          border: none;
          color: #607D8B;
          cursor: pointer; cursor: hand;
          font-height: 18px;
          font-size: 1em;
          font-weight: 500;
          margin: 0 3px;
          padding: 10px 12px;
        }
        ^nav .foam-u2-ActionView:hover {
          background-color: #cfd8dc;
        }
        ^nav .foam-u2-ActionView:disabled {
          -webkit-filter: none;
          background-color: #eee;
          color: #039be5;
          cursor: auto;
        }
      */}
    })
  ],

  properties: [
    {
      class: 'String',
      name: 'heroName',
      view: {
        class: 'foam.u2.TextField',
        onKey: true
      }
    },
    {
      name: 'heroDAO',
      view: {
        class: 'foam.u2.DAOList',
        rowView: 'com.google.foam.demos.heroes.CitationView'
      }
    },
    {
      name: 'starredHeroDAO',
      view: {
        class: 'foam.u2.DAOList',
        rowView: 'com.google.foam.demos.heroes.DashboardCitationView'
      },
      factory: function() { return this.heroDAO.where(this.EQ(this.Hero.STARRED, true)); }
    },
    {
      name: 'mode',
      value: 'dashboard'
    },
    {
      name: 'selection',
      view: { class: 'foam.u2.DetailView', title: '' }
    }
  ],

  methods: [
    function initE() {
      this.
        start('h2').add('Tour of Heroes').end().
          // TODO: start(this.HEROES) and set class
        start().cssClass(this.myCls('nav')).
          add(this.DASHBOARD, this.HEROES).
        end().
        br().
        add(this.slot(function(selection, mode) {
          return selection ? this.detailE() :
            mode === 'dashboard' ? this.dashboardE() :
            this.heroesE();
        }));
    },

    function detailE() {
      return this.E('h3').add(this.selection.name$, ' details!').br().add(this.SELECTION, this.BACK);
    },

    function dashboardE() {
      return this.E().cssClass(this.myCls('starred')).start('h3').add('Top Heroes').end().add(this.STARRED_HERO_DAO);
    },

    function heroesE() {
      return this.E().
          start('h3').
            add('My Heroes').
          end().
          start().
            add('Hero name: ', this.HERO_NAME, ' ', this.ADD_HERO).
          end().
          add(this.HERO_DAO);
    },

    function editHero(hero) {
      console.log('*****', this.selection, hero);
      this.selection = hero;
    }
  ],

  actions: [
    {
      name: 'addHero',
      label: 'Add',
      isEnabled: function(heroName) { return !!heroName; },
      code: function() {
        this.heroDAO.put(this.Hero.create({name: this.heroName}));
        this.heroName = '';
      }
    },
    {
      name: 'dashboard',
      isEnabled: function(mode) { return mode != 'dashboard'; },
      code: function() { this.back(); this.mode = 'dashboard'; }
    },
    {
      name: 'heroes',
      isEnabled: function(mode) { return mode != 'heroes'; },
      code: function() { this.back(); this.mode = 'heroes'; }
    },
    {
      name: 'back',
      code: function() { this.selection = null; }
    }
  ]
});
<commit_msg>Remove stale console.log().<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * 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.
 */

foam.CLASS({
  package: 'com.google.foam.demos.heroes',
  name: 'Controller',
  extends: 'foam.u2.Element',

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

  requires: [
    'com.google.foam.demos.heroes.CitationView',
    'com.google.foam.demos.heroes.DashboardCitationView',
    'com.google.foam.demos.heroes.Hero',
    'foam.dao.ArrayDAO',
    'foam.u2.DAOList',
    'foam.u2.DetailView',
    'foam.u2.CheckBox'
  ],

  exports: [
    'as data',
    'heroDAO',
    'editHero'
  ],

  axioms: [
    foam.u2.CSS.create({
      code: function() {/*
        h2 { color: #aaa; }
        h3 {
          color: #444;
          font-size: 1.75em;
          font-weight: 100;
          margin-top: 0;
        }
        body { margin: 2em; }
        body, input[text], button { color: #888; font-family: Cambria, Georgia; }
        ^starred .foam-u2-DAOList { display: flex; }
        * { font-family: Arial; }
        input {
          font-size: 1em;
          height: 2em;
          padding-left: .4em;
        }
        ^nav .foam-u2-ActionView {
          background: #eee;
          border-radius: 4px;
          border: none;
          color: #607D8B;
          cursor: pointer; cursor: hand;
          font-height: 18px;
          font-size: 1em;
          font-weight: 500;
          margin: 0 3px;
          padding: 10px 12px;
        }
        ^nav .foam-u2-ActionView:hover {
          background-color: #cfd8dc;
        }
        ^nav .foam-u2-ActionView:disabled {
          -webkit-filter: none;
          background-color: #eee;
          color: #039be5;
          cursor: auto;
        }
      */}
    })
  ],

  properties: [
    {
      class: 'String',
      name: 'heroName',
      view: {
        class: 'foam.u2.TextField',
        onKey: true
      }
    },
    {
      name: 'heroDAO',
      view: {
        class: 'foam.u2.DAOList',
        rowView: 'com.google.foam.demos.heroes.CitationView'
      }
    },
    {
      name: 'starredHeroDAO',
      view: {
        class: 'foam.u2.DAOList',
        rowView: 'com.google.foam.demos.heroes.DashboardCitationView'
      },
      factory: function() { return this.heroDAO.where(this.EQ(this.Hero.STARRED, true)); }
    },
    {
      name: 'mode',
      value: 'dashboard'
    },
    {
      name: 'selection',
      view: { class: 'foam.u2.DetailView', title: '' }
    }
  ],

  methods: [
    function initE() {
      this.
        start('h2').add('Tour of Heroes').end().
          // TODO: start(this.HEROES) and set class
        start().cssClass(this.myCls('nav')).
          add(this.DASHBOARD, this.HEROES).
        end().
        br().
        add(this.slot(function(selection, mode) {
          return selection ? this.detailE() :
            mode === 'dashboard' ? this.dashboardE() :
            this.heroesE();
        }));
    },

    function detailE() {
      return this.E('h3').add(this.selection.name$, ' details!').br().add(this.SELECTION, this.BACK);
    },

    function dashboardE() {
      return this.E().cssClass(this.myCls('starred')).start('h3').add('Top Heroes').end().add(this.STARRED_HERO_DAO);
    },

    function heroesE() {
      return this.E().
          start('h3').
            add('My Heroes').
          end().
          start().
            add('Hero name: ', this.HERO_NAME, ' ', this.ADD_HERO).
          end().
          add(this.HERO_DAO);
    },

    function editHero(hero) {
      this.selection = hero;
    }
  ],

  actions: [
    {
      name: 'addHero',
      label: 'Add',
      isEnabled: function(heroName) { return !!heroName; },
      code: function() {
        this.heroDAO.put(this.Hero.create({name: this.heroName}));
        this.heroName = '';
      }
    },
    {
      name: 'dashboard',
      isEnabled: function(mode) { return mode != 'dashboard'; },
      code: function() { this.back(); this.mode = 'dashboard'; }
    },
    {
      name: 'heroes',
      isEnabled: function(mode) { return mode != 'heroes'; },
      code: function() { this.back(); this.mode = 'heroes'; }
    },
    {
      name: 'back',
      code: function() { this.selection = null; }
    }
  ]
});
<|endoftext|>


<commit_before>'use strict';

var rfr = require('rfr');
var path = require('path');
var expect = require('chai').expect;
var parser = rfr('src/parser');
var fs = require('fs');
var _ = require('lodash');

describe('A Markdown file without front matter', () => {
	var filepath = 'test/input-without-front-matter.md';
	var content = fs.readFileSync(filepath, 'utf8');
	var language = path.extname(filepath).substr(1);
	var components = parser.parse(content, language);

	it('should not be processed', () => expect(components.length).to.equal(0));
});

describe('A Markdown file', () => {
	var filepath = 'test/input.md';
	var content = fs.readFileSync(filepath, 'utf8');
	var language = path.extname(filepath).substr(1);
	var components = parser.parse(content, language);
	var component = components[0];

	it('should have a single component', () => expect(components.length).to.equal(1));
	it('should have the correct component name', () => expect(component.getName()).to.equal('Button'));
	it('should have the correct component category', () => expect(component.getCategory()).to.equal('Components'));
	it('should have the correct component meta', () => expect(component.getMeta()).to.deep.equal({
		tags: ['element', 'interactive', 'form'],
		version: '1.0.3',
		todos: ['fix padding', 'improve colors'],
	}));
	it('should have the correct component description', () => expect(component.getDescription()).to.equal(
		fs.readFileSync('test/expected.md', 'utf8')
	));
	it('should have the correct number of examples', () => expect(_.size(component.getExamples())).to.equal(6));

	it('should have the correct 1st example', () => expect(component.getExamples().types).to.deep.equal({
		blocks: [
			{
				language: 'html',
				hidden: false,
				content: `<button class="btn btn-default">Default</button>
<button class="btn btn-primary">Primary</button>
<button class="btn btn-success">Success</button>`
			},
			{
				language: 'css',
				hidden: true,
				content: `button { margin: 5px; }`
			},
			{
				language: 'js',
				hidden: true,
				content: `$('button').click(function() { alert('Button clicked!'); });`
			},
		],
		options: {},
	}));

	it('should have the correct 2nd example', () => expect(component.getExamples().sizes).to.deep.equal({
		blocks: [
			{
				language: 'html',
				hidden: true,
				content: `<button class="btn btn-default btn-sm">Small</button>
<button class="btn btn-default">Default</button>
<button class="btn btn-default btn-lg">Large</button>`
			},
		],
		options: {
			height: '100',
		},
	}));

	it('should have the correct 3rd example', () => expect(component.getExamples()['handlebars-button']).to.deep.equal({
		blocks: [
			{
				language: 'handlebars',
				hidden: false,
				content: `{{#bs-button}}Button{{/bs-button}}`
			},
		],
		options: {},
	}));

	it('should have the correct 4rd example', () => expect(component.getExamples()['react-button']).to.deep.equal({
		blocks: [
			{
				language: 'jsx',
				hidden: false,
				content: `<Button>Button</Button>`
			},
		],
		options: {},
	}));

	it('should have the correct 5rd example', () => expect(component.getExamples()['angular-button']).to.deep.equal({
		blocks: [
			{
				language: 'html',
				hidden: false,
				content: `<button class="btn btn-default">{{ text }}</button>`
			},
			{
				language: 'angularjs',
				hidden: false,
				content: `text = 'Button'`
			},
		],
		options: {},
	}));

	it('should have the correct 6rd example', () => expect(component.getExamples()['disabled']).to.deep.equal({
		blocks: [
			{
				language: 'html',
				hidden: false,
				content: `<button class="btn btn-primary" disabled>Disabled button</button>`
			},
		],
		options: {},
	}));
});

describe('A processed source code file', () => {
	var filepath = 'test/input.css';
	var content = fs.readFileSync(filepath, 'utf8');
	var language = path.extname(filepath).substr(1);
	var components = parser.parse(content, language);

	it('should have the correct number of components', () => expect(components.length).to.equal(2));

	describe('first component', () => {
		it('should have the correct component name', () => expect(components[0].getName()).to.equal('Button'));
		it('should have the correct component category', () => expect(components[0].getCategory()).to.equal('Components'));
		it('should have the correct component meta', () => expect(components[0].getMeta()).to.deep.equal({
			tags: ['element', 'interactive', 'form'],
			version: '1.0.3',
			todos: ['fix padding', 'improve colors'],
			author: 'mpetrovich',
		}));
		it('should have the correct component description', () => expect(components[0].getDescription()).to.equal(
			fs.readFileSync('test/expected.md', 'utf8')
		));
		it('should have the correct number of examples', () => expect(_.size(components[0].getExamples())).to.equal(6));

		it('should have the correct 1st example', () => expect(components[0].getExamples().types).to.deep.equal({
			blocks: [
				{
					language: 'html',
					hidden: false,
					content: `<button class="btn btn-default">Default</button>
<button class="btn btn-primary">Primary</button>
<button class="btn btn-success">Success</button>`
				},
				{
					language: 'css',
					hidden: true,
					content: `button { margin: 5px; }`
				},
				{
					language: 'js',
					hidden: true,
					content: `$('button').click(function() { alert('Button clicked!'); });`
				},
			],
			options: {},
		}));
		it('should have the correct 2nd example', () => expect(components[0].getExamples().sizes).to.deep.equal({
			blocks: [
				{
					language: 'html',
					hidden: true,
					content: `<button class="btn btn-default btn-sm">Small</button>
<button class="btn btn-default">Default</button>
<button class="btn btn-default btn-lg">Large</button>`
				},
			],
			options: {
				height: '100',
			},
		}));

		it('should have the correct 3rd example', () => expect(components[0].getExamples()['handlebars-button']).to.deep.equal({
			blocks: [
				{
					language: 'handlebars',
					hidden: false,
					content: `{{#bs-button}}Button{{/bs-button}}`
				},
			],
			options: {},
		}));

		it('should have the correct 4rd example', () => expect(components[0].getExamples()['react-button']).to.deep.equal({
			blocks: [
				{
					language: 'jsx',
					hidden: false,
					content: `<Button>Button</Button>`
				},
			],
			options: {},
		}));

		it('should have the correct 5rd example', () => expect(components[0].getExamples()['angular-button']).to.deep.equal({
			blocks: [
				{
					language: 'html',
					hidden: false,
					content: `<button class="btn btn-default">{{ text }}</button>`
				},
				{
					language: 'angularjs',
					hidden: false,
					content: `text = 'Button'`
				},
			],
			options: {},
		}));

		it('should have the correct 6rd example', () => expect(components[0].getExamples()['disabled']).to.deep.equal({
			blocks: [
				{
					language: 'html',
					hidden: false,
					content: `<button class="btn btn-primary" disabled>Disabled button</button>`
				},
			],
			options: {},
		}));
	});

	describe('second component', () => {
		it('should have the correct component name', () => expect(components[1].getName()).to.equal('Link'));
		it('should have the correct component category', () => expect(components[1].getCategory()).to.equal('Text'));
		it('should have the correct component meta', () => expect(components[1].getMeta()).to.deep.equal({}));
		it('should have the correct component description', () => expect(components[1].getDescription()).to.equal(
			fs.readFileSync('test/expected-2.md', 'utf8')
		));
		it('should have the correct number of examples', () => expect(_.size(components[1].getExamples())).to.equal(1));

		it('should have the correct 1st example', () => expect(components[1].getExamples().link).to.deep.equal({
			blocks: [
				{
					language: 'html',
					hidden: false,
					content: `<a href="#" class="text-link">Click me</a>`
				},
			],
			options: {},
		}));
	});
});
<commit_msg>Fix broken tests<commit_after>'use strict';

var rfr = require('rfr');
var path = require('path');
var expect = require('chai').expect;
var parser = rfr('src/parser');
var fs = require('fs');
var _ = require('lodash');

describe('A Markdown file without front matter', () => {
	var filepath = 'test/input-without-front-matter.md';
	var content = fs.readFileSync(filepath, 'utf8');
	var language = path.extname(filepath).substr(1);
	var components = parser.parse(content, language);

	it('should not be processed', () => expect(components.length).to.equal(0));
});

describe('A Markdown file', () => {
	var filepath = 'test/input.md';
	var content = fs.readFileSync(filepath, 'utf8');
	var language = path.extname(filepath).substr(1);
	var components = parser.parse(content, language);
	var component = components[0];

	it('should have a single component', () => expect(components.length).to.equal(1));
	it('should have the correct component name', () => expect(component.getName()).to.equal('Button'));
	it('should have the correct component category', () => expect(component.getCategory()).to.equal('Components'));
	it('should have the correct component meta', () => expect(component.getMeta()).to.deep.equal({
		tags: ['element', 'interactive', 'form'],
		version: '1.0.3',
		todos: ['fix padding', 'improve colors'],
	}));
	it('should have the correct component description', () => expect(component.getDescription()).to.equal(
		fs.readFileSync('test/expected.md', 'utf8')
	));
	it('should have the correct number of examples', () => expect(_.size(component.getExamples())).to.equal(6));

	it('should have the correct 1st example', () => expect(component.getExamples().types).to.deep.equal({
		name: "types",
		blocks: [
			{
				language: 'html',
				hidden: false,
				content: `<button class="btn btn-default">Default</button>
<button class="btn btn-primary">Primary</button>
<button class="btn btn-success">Success</button>`
			},
			{
				language: 'css',
				hidden: true,
				content: `button { margin: 5px; }`
			},
			{
				language: 'js',
				hidden: true,
				content: `$('button').click(function() { alert('Button clicked!'); });`
			},
		],
		options: {},
	}));

	it('should have the correct 2nd example', () => expect(component.getExamples().sizes).to.deep.equal({
		name: "sizes",
		blocks: [
			{
				language: 'html',
				hidden: true,
				content: `<button class="btn btn-default btn-sm">Small</button>
<button class="btn btn-default">Default</button>
<button class="btn btn-default btn-lg">Large</button>`
			},
		],
		options: {
			height: '100',
		},
	}));

	it('should have the correct 3rd example', () => expect(component.getExamples()['handlebars-button']).to.deep.equal({
		name: "handlebars-button",
		blocks: [
			{
				language: 'handlebars',
				hidden: false,
				content: `{{#bs-button}}Button{{/bs-button}}`
			},
		],
		options: {},
	}));

	it('should have the correct 4rd example', () => expect(component.getExamples()['react-button']).to.deep.equal({
		name: "react-button",
		blocks: [
			{
				language: 'jsx',
				hidden: false,
				content: `<Button>Button</Button>`
			},
		],
		options: {},
	}));

	it('should have the correct 5rd example', () => expect(component.getExamples()['angular-button']).to.deep.equal({
		name: "angular-button",
		blocks: [
			{
				language: 'html',
				hidden: false,
				content: `<button class="btn btn-default">{{ text }}</button>`
			},
			{
				language: 'angularjs',
				hidden: false,
				content: `text = 'Button'`
			},
		],
		options: {},
	}));

	it('should have the correct 6rd example', () => expect(component.getExamples()['disabled']).to.deep.equal({
		name: "disabled",
		blocks: [
			{
				language: 'html',
				hidden: false,
				content: `<button class="btn btn-primary" disabled>Disabled button</button>`
			},
		],
		options: {},
	}));
});

describe('A processed source code file', () => {
	var filepath = 'test/input.css';
	var content = fs.readFileSync(filepath, 'utf8');
	var language = path.extname(filepath).substr(1);
	var components = parser.parse(content, language);

	it('should have the correct number of components', () => expect(components.length).to.equal(2));

	describe('first component', () => {
		it('should have the correct component name', () => expect(components[0].getName()).to.equal('Button'));
		it('should have the correct component category', () => expect(components[0].getCategory()).to.equal('Components'));
		it('should have the correct component meta', () => expect(components[0].getMeta()).to.deep.equal({
			tags: ['element', 'interactive', 'form'],
			version: '1.0.3',
			todos: ['fix padding', 'improve colors'],
			author: 'mpetrovich',
		}));
		it('should have the correct component description', () => expect(components[0].getDescription()).to.equal(
			fs.readFileSync('test/expected.md', 'utf8')
		));
		it('should have the correct number of examples', () => expect(_.size(components[0].getExamples())).to.equal(6));

		it('should have the correct 1st example', () => expect(components[0].getExamples().types).to.deep.equal({
			name: "types",
			blocks: [
				{
					language: 'html',
					hidden: false,
					content: `<button class="btn btn-default">Default</button>
<button class="btn btn-primary">Primary</button>
<button class="btn btn-success">Success</button>`
				},
				{
					language: 'css',
					hidden: true,
					content: `button { margin: 5px; }`
				},
				{
					language: 'js',
					hidden: true,
					content: `$('button').click(function() { alert('Button clicked!'); });`
				},
			],
			options: {},
		}));
		it('should have the correct 2nd example', () => expect(components[0].getExamples().sizes).to.deep.equal({
			name: "sizes",
			blocks: [
				{
					language: 'html',
					hidden: true,
					content: `<button class="btn btn-default btn-sm">Small</button>
<button class="btn btn-default">Default</button>
<button class="btn btn-default btn-lg">Large</button>`
				},
			],
			options: {
				height: '100',
			},
		}));

		it('should have the correct 3rd example', () => expect(components[0].getExamples()['handlebars-button']).to.deep.equal({
			name: "handlebars-button",
			blocks: [
				{
					language: 'handlebars',
					hidden: false,
					content: `{{#bs-button}}Button{{/bs-button}}`
				},
			],
			options: {},
		}));

		it('should have the correct 4rd example', () => expect(components[0].getExamples()['react-button']).to.deep.equal({
			name: "react-button",
			blocks: [
				{
					language: 'jsx',
					hidden: false,
					content: `<Button>Button</Button>`
				},
			],
			options: {},
		}));

		it('should have the correct 5rd example', () => expect(components[0].getExamples()['angular-button']).to.deep.equal({
			name: "angular-button",
			blocks: [
				{
					language: 'html',
					hidden: false,
					content: `<button class="btn btn-default">{{ text }}</button>`
				},
				{
					language: 'angularjs',
					hidden: false,
					content: `text = 'Button'`
				},
			],
			options: {},
		}));

		it('should have the correct 6rd example', () => expect(components[0].getExamples()['disabled']).to.deep.equal({
			name: "disabled",
			blocks: [
				{
					language: 'html',
					hidden: false,
					content: `<button class="btn btn-primary" disabled>Disabled button</button>`
				},
			],
			options: {},
		}));
	});

	describe('second component', () => {
		it('should have the correct component name', () => expect(components[1].getName()).to.equal('Link'));
		it('should have the correct component category', () => expect(components[1].getCategory()).to.equal('Text'));
		it('should have the correct component meta', () => expect(components[1].getMeta()).to.deep.equal({}));
		it('should have the correct component description', () => expect(components[1].getDescription()).to.equal(
			fs.readFileSync('test/expected-2.md', 'utf8')
		));
		it('should have the correct number of examples', () => expect(_.size(components[1].getExamples())).to.equal(1));

		it('should have the correct 1st example', () => expect(components[1].getExamples().link).to.deep.equal({
			name: "link",
			blocks: [
				{
					language: 'html',
					hidden: false,
					content: `<a href="#" class="text-link">Click me</a>`
				},
			],
			options: {},
		}));
	});
});
<|endoftext|>