<commit_before>define([
    './../core',
    '../etc/debounce'
], function (franky) {

    franky.amd.define(
        'statlog',
        function () {
            var root = null,
                params = {
                    type: 'show',
                    root: 'testRoot',
                    statDataAttr: 'statlog',
                    dataNoRedirKey: 'statlog-noredir',
                    doc: null,
                    sendAtWill: true,
                    dataReadyKey: 'statlog-ready',
                    clickEvent: 'mousedown',
                    skipVisibleCheck: false,
                    statEnabled: false,
                    clickCounter: function (path, element) {
                        element = element || null;
                        window.cp(getRoot() + '.' + path, element);
                    }
                },
                getRoot = function () {
                    return typeof Lego !== 'undefined' && Lego.params && Lego.params.statRoot ? Lego.params.statRoot : params.root;
                },
                dumpDATA = function (params) {
                    var res = [];
                    if (root) {
                        res = JSON.stringify([{
                            event: params.type,
                            "parent-path": getRoot(),
                            blocks: root.children
                        }]);
                    }
                    return res;
                },
                isEnabled = function () {
                    return typeof Lego !== 'undefined' && Lego.params ? Lego.params.statEnabled  : params.statEnabled;
                },
                handleClicks = function () {
                    // используем делегирование для обработки клика
                    $(params.doc || document).on(params.clickEvent, "*[data-" + params.statDataAttr + "]", function (e) {
                        // считаем клики только когда пользователь попадает в выборку
                        if (isEnabled()) {
                            var clickPath = $(this).data(params.statDataAttr),
                                notRedirect = $(this).data(params.dataNoRedirKey);

                            if (clickPath) {
                                params.clickCounter(clickPath, notRedirect ? null : this);

                                if (notRedirect) {
                                    e.preventDefault();
                                    e.stopPropagation();
                                }
                            }
                        }

                    });
                };

            handleClicks();

            return {
                // Для того, чтобы сторонние компоненты могли смотреть на настройки - рассказываем о них в инстансе
                params: params,

                getSingleRequest : function (data) {

                    return x.stringf(
                        'http://clck.yandex.ru/counter/dtype=%s/session_id=%s/events=%s/*',
                        data.project,
                        data.showId,
                        JSON.stringify([{
                            "event": data.event,
                            "parent-path": data.path.substr(0, data.path.lastIndexOf('.')),
                            "blocks": [{
                                ctag: data.path.substr(data.path.lastIndexOf('.') + 1)
                            }]
                        }])
                    );
                },
                singleLog: function (statPath) {
                    var url = this.getSingleRequest({
                        project: 'clck',
                        showId: this.getShowID(),
                        event: 'show',
                        path: getRoot() + '.' + statPath
                    });
                    var img = new Image();
                    img.src = url;
                },
                // Балковое логирование с использованием CORS
                bulkLog: function (/**String*/statPath) /**Object*/ {
                    var current = root ? root : root = {},
                        self = this;

                    franky.forEach(statPath.split('.'), function (key) {
                        var existFunction = function (child) {
                            return child.ctag === key;
                        };
                        if (!current.children) {
                            current.children = [];
                        }

                        var tryToGet = franky.filter(current.children, existFunction);
                        if (!tryToGet.length) {
                            current.children.push({
                                ctag: key
                            });
                        }
                        current = franky.filter(current.children, existFunction)[0];
                    });

                    if (params.sendAtWill) {
                        self.send();
                    }
                    return this;
                },
                // Логирование счетчика с заданным путем в виде аргумента
                // e.g. statlog.add('v12.head.login').
                log: function (statPath) {
                    // Для браузеров не поддерживающих балковое логирование в виде CORS запроса используем одиночные запросы
                    var logFunc = $.support.cors ? this.bulkLog : this.singleLog;

                    // проверяем наличие пути к статистике и флаг логирования
                    if (typeof statPath !== 'undefined' && isEnabled()) {
                        return logFunc.call(this, statPath);
                    }
                },
                // добавление счетчиков по результат парсинга куска DOM
                logByDocument: function (/**Object*/p) {

                    var _params = franky.beget(params, p),
                        self = this,
                        attrName = 'data-' + _params.statDataAttr,
                        dataKey = attrName.substr(5),
                        dataNoRedirKey = dataKey + '-noredir',
                        exp =  "*["+attrName+"]",
                        l = _params.doc ? $(_params.doc).find(exp) : $(exp);

                    // обходим все элементы с data-statlog - считаем их 'элементами' для обсчета статистикой
                    $(l).each(function () {
                        // проверяем состояние видимости элемента
                        if ($(this).is(':visible') || _params.skipVisibleCheck) {
                            if (!attrName.match(/^data-/)) {
                                throw new Error("data attribute expected as statAttr");
                            }

                            // забираем путь к счетчику, считая что это дата-атрибут
                            var dataPath = $(this).data(dataKey);

                            // Чтобы не считать элемент несколько раз - используем специальный флаг
                            if (!$(this).data(_params.dataReadyKey)) {

                                // складываем в хранилище логера

                                self.log(dataPath);
                                $(this).data(_params.dataReadyKey, dataPath);
                            }

                        }
                    });
                },

                // дамп залогированных счетчиков
                dumpDATA: dumpDATA,

                // Returns showID as string
                getShowID: function () /**String*/ {
                    var res = "";
                    if (typeof Lego !== 'undefined' && Lego.params && Lego.params.msid) {
                        res = Lego.params.msid;
                    }
                    return res;
                },

                // Отправка запроса в статистику
                send: franky.debounce(function () {
                    if (root) {
                        $.ajax({
                            type: "POST",
                            url: "http://clck.yandex.ru/counter/*",
                            data: {
                                dtype: "clck",
                                session_id: this.getShowID(),
                                events: dumpDATA(params)
                            },
                            success: function () {
                                root = null;
                            }
                        });
                    }
                }, 1000)
            };
        }
    );

});
<commit_msg>change format<commit_after>define([
    './../core',
    '../etc/debounce'
], function (franky) {

    franky.amd.define(
        'statlog',
        function () {
            var root = null,
                clckDemonHost = 'http://clck.yandex.ru/counter',
                clckDemonPath = '/dtype=%s/session_id=%s/events=%s/*',
                params = {
                    type: 'show',
                    root: 'testRoot',
                    statDataAttr: 'statlog',
                    dataNoRedirKey: 'statlog-noredir',
                    doc: null,
                    sendAtWill: true,
                    dataReadyKey: 'statlog-ready',
                    clickEvent: 'mousedown',
                    skipVisibleCheck: false,
                    statEnabled: false,
                    clickCounter: function (path, element) {
                        element = element || null;
                        window.cp(getRoot() + '.' + path, element);
                    }
                },
                getRoot = function () {
                    return typeof Lego !== 'undefined' && Lego.params && Lego.params.statRoot ? Lego.params.statRoot : params.root;
                },
                dumpDATA = function (params) {
                    var res = [];
                    if (root) {
                        res = JSON.stringify([{
                            event: params.type,
                            "parent-path": getRoot(),
                            blocks: root.children
                        }]);
                    }
                    return res;
                },
                isEnabled = function () {
                    return typeof Lego !== 'undefined' && Lego.params ? Lego.params.statEnabled  : params.statEnabled;
                },
                handleClicks = function () {
                    // используем делегирование для обработки клика
                    $(params.doc || document).on(params.clickEvent, "*[data-" + params.statDataAttr + "]", function (e) {
                        // считаем клики только когда пользователь попадает в выборку
                        if (isEnabled()) {
                            var clickPath = $(this).data(params.statDataAttr),
                                notRedirect = $(this).data(params.dataNoRedirKey);

                            if (clickPath) {
                                params.clickCounter(clickPath, notRedirect ? null : this);

                                if (notRedirect) {
                                    e.preventDefault();
                                    e.stopPropagation();
                                }
                            }
                        }

                    });
                };

            handleClicks();

            return {
                // Для того, чтобы сторонние компоненты могли смотреть на настройки - рассказываем о них в инстансе
                params: params,

                getSingleRequest : function (data) {

                    return franky.stringf(
                        clckDemonHost + clckDemonPath,
                        data.project,
                        data.showId,
                        JSON.stringify([{
                            "event": data.event,
                            "parent-path": data.path.substr(0, data.path.lastIndexOf('.')),
                            "blocks": [{
                                ctag: data.path.substr(data.path.lastIndexOf('.') + 1)
                            }]
                        }])
                    );
                },
                singleLog: function (statPath) {
                    var url = this.getSingleRequest({
                        project: 'clck',
                        showId: this.getShowID(),
                        event: 'show',
                        path: getRoot() + '.' + statPath
                    });
                    var img = new Image();
                    img.src = url;
                },
                // Балковое логирование с использованием CORS
                bulkLog: function (/**String*/statPath) /**Object*/ {
                    var current = root ? root : root = {},
                        self = this;

                    franky.forEach(statPath.split('.'), function (key) {
                        var existFunction = function (child) {
                            return child.ctag === key;
                        };
                        if (!current.children) {
                            current.children = [];
                        }

                        var tryToGet = franky.filter(current.children, existFunction);
                        if (!tryToGet.length) {
                            current.children.push({
                                ctag: key
                            });
                        }
                        current = franky.filter(current.children, existFunction)[0];
                    });

                    if (params.sendAtWill) {
                        self.send();
                    }
                    return this;
                },
                // Логирование счетчика с заданным путем в виде аргумента
                // e.g. statlog.add('v12.head.login').
                log: function (statPath) {
                    // Для браузеров не поддерживающих балковое логирование в виде CORS запроса используем одиночные запросы
                    var logFunc = $.support.cors ? this.bulkLog : this.singleLog;

                    // проверяем наличие пути к статистике и флаг логирования
                    if (typeof statPath !== 'undefined' && isEnabled()) {
                        return logFunc.call(this, statPath);
                    }
                },
                // добавление счетчиков по результат парсинга куска DOM
                logByDocument: function (/**Object*/p) {

                    var _params = franky.beget(params, p),
                        self = this,
                        attrName = 'data-' + _params.statDataAttr,
                        dataKey = attrName.substr(5),
                        dataNoRedirKey = dataKey + '-noredir',
                        exp =  "*["+attrName+"]",
                        l = _params.doc ? $(_params.doc).find(exp) : $(exp);

                    // обходим все элементы с data-statlog - считаем их 'элементами' для обсчета статистикой
                    $(l).each(function () {
                        // проверяем состояние видимости элемента
                        if ($(this).is(':visible') || _params.skipVisibleCheck) {
                            if (!attrName.match(/^data-/)) {
                                throw new Error("data attribute expected as statAttr");
                            }

                            // забираем путь к счетчику, считая что это дата-атрибут
                            var dataPath = $(this).data(dataKey);

                            // Чтобы не считать элемент несколько раз - используем специальный флаг
                            if (!$(this).data(_params.dataReadyKey)) {

                                // складываем в хранилище логера

                                self.log(dataPath);
                                $(this).data(_params.dataReadyKey, dataPath);
                            }

                        }
                    });
                },

                // дамп залогированных счетчиков
                dumpDATA: dumpDATA,

                // Returns showID as string
                getShowID: function () /**String*/ {
                    var res = "";
                    if (typeof Lego !== 'undefined' && Lego.params && Lego.params.msid) {
                        res = Lego.params.msid;
                    }
                    return res;
                },

                // Отправка запроса в статистику
                send: franky.debounce(function () {
                    if (root) {
                        $.ajax({
                            type: "POST",
                            url: clckDemonHost,
                            data: franky.stringf(
                                clckDemonPath,
                                'clck',
                                this.getShowID(),
                                dumpDATA(params)
                            ),
                            success: function () {
                                root = null;
                            }
                        });
                    }
                }, 1000)
            };
        }
    );
});<|endoftext|>


<commit_before>'use strict';

var I = require('immutable');
var DS = require('./delaney');
var properties = require('./properties');
var permutations = require('../common/permutations');

var dual = function dual(ds) {
  var d = DS.dim(ds);

  return DS.build(
    d, DS.size(ds),
    function(_, i) {
      return ds.elements().map(function(D) {
        return [D, ds.s(d - i, D)];
      });
    },
    function(tmp, i) {
      return ds.elements().map(function(D) {
        return [D, ds.v(d-i-1, d-i, D)];
      });
    }
  );
};

var cover = function cover(ds, nrSheets, transferFn) {
  var n = DS.size(ds);

  return DS.build(
    DS.dim(ds), n * nrSheets,
    function(_, i) {
      return ds.elements().flatMap(function(D) {
        return I.Range(0, nrSheets).map(function(k) {
          return [k * n + D, transferFn(k, i, D) * n + ds.s(i, D)];
        });
      });
    },
    function(tmp, i) {
      var j = i+1;
      return DS.orbitReps2(tmp, i, j).map(function(D) {
        var D0 = (D - 1) % n + 1;
        var v = (DS.m(ds, i, j, D0) || 0) / DS.r(tmp, i, j, D);
        return [D, v];
      });
    }
  );
};

var minimal = function minimal(ds) {
  if (properties.isMinimal(ds))
    return ds;
  else {
    var p = properties.typePartition(ds);
    var reps = ds.elements().filter(function(D) { return p.get(D) == D; });
    var emap = I.Map(reps.zip(I.Range(1)));
    var imap = I.Map(I.Range().zip(ds.indices()));

    return DS.build(
      DS.dim(ds), reps.count(),
      function(_, i) {
        return reps.map(function(D) {
          return [emap.get(D), emap.get(p.get(ds.s(imap.get(i), D)))];
        });
      },
      function(tmp, i) {
        return reps.map(function(D) {
          var v =
            (DS.m(ds, imap.get(i), imap.get(i+1), D) || 0)
            /
            DS.r(tmp, i, i+1, emap.get(D));
          return [emap.get(D), v];
        });
      }
    );
  }
};

var _apply = function _apply(p, i) {
  if (i < 0 || i >= p.size)
    return i;
  else
    return p.get(i) - 1;
};

var barycentricSubdivision = function barycentricSubdivision(ds, splitDim) {
  if (splitDim == 0)
    return ds;
  else {
    var dim = DS.dim(ds);
    var perms = I.List(permutations(splitDim + 1)).map(I.List);
    var pidx = I.Map(I.List(perms).zip(I.Range()));
    var n = DS.size(ds);
    var m = perms.size;

    return DS.build(
      DS.dim(ds), n * m,
      function(_, i) {
        return ds.elements().flatMap(function(D) {
          return I.Range(0, m).map(function(j) {
            var p = perms.get(j);
            if (i < splitDim) {
              var pi = p.set(i, p.get(i+1)).set(i+1, p.get(i));
              var k = pidx.get(pi);
              return [n * j + D, n * k + D];
            } else {
              var E = ds.s(_apply(p, i), D);
              return [n * j + D, n * j + E];
            }
          });
        });
      },
      function(tmp, i) {
        return ds.elements().flatMap(function(D) {
          return I.Range(0, m).map(function(j) {
            var p = perms.get(j);
            var v;
            if (i < splitDim - 1)
              v = 1;
            else
              v = ds.v(_apply(p, i), _apply(p, i+1), D);
            return [n * j + D, v];
          });
        });
      }
    );
  }
};

if (require.main == module) {
  var ds = DS.parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>');

  console.log('' + ds);
  console.log('' + dual(ds));

  console.log('' + cover(ds, 2, function(k, i, D) {
    return ds.s(i, D) == D ? 1 - k : k;
  }));

  console.log('' + minimal(DS.parse(
    '<1.1:24:' +
      '2 4 6 8 10 12 14 16 18 20 22 24,' +
      '16 3 5 7 9 11 13 15 24 19 21 23,' +
      '10 9 20 19 14 13 22 21 24 23 18 17:' +
      '8 4,3 3 3 3>')));

  console.log('' + barycentricSubdivision(ds, 0));
  console.log('' + barycentricSubdivision(ds, 1));
  console.log('' + barycentricSubdivision(ds, 2));
}
<commit_msg>add a 3d test for barycentric subdivision and T-tiling<commit_after>'use strict';

var I = require('immutable');
var DS = require('./delaney');
var properties = require('./properties');
var permutations = require('../common/permutations');

var dual = function dual(ds) {
  var d = DS.dim(ds);

  return DS.build(
    d, DS.size(ds),
    function(_, i) {
      return ds.elements().map(function(D) {
        return [D, ds.s(d - i, D)];
      });
    },
    function(tmp, i) {
      return ds.elements().map(function(D) {
        return [D, ds.v(d-i-1, d-i, D)];
      });
    }
  );
};

var cover = function cover(ds, nrSheets, transferFn) {
  var n = DS.size(ds);

  return DS.build(
    DS.dim(ds), n * nrSheets,
    function(_, i) {
      return ds.elements().flatMap(function(D) {
        return I.Range(0, nrSheets).map(function(k) {
          return [k * n + D, transferFn(k, i, D) * n + ds.s(i, D)];
        });
      });
    },
    function(tmp, i) {
      var j = i+1;
      return DS.orbitReps2(tmp, i, j).map(function(D) {
        var D0 = (D - 1) % n + 1;
        var v = (DS.m(ds, i, j, D0) || 0) / DS.r(tmp, i, j, D);
        return [D, v];
      });
    }
  );
};

var minimal = function minimal(ds) {
  if (properties.isMinimal(ds))
    return ds;
  else {
    var p = properties.typePartition(ds);
    var reps = ds.elements().filter(function(D) { return p.get(D) == D; });
    var emap = I.Map(reps.zip(I.Range(1)));
    var imap = I.Map(I.Range().zip(ds.indices()));

    return DS.build(
      DS.dim(ds), reps.count(),
      function(_, i) {
        return reps.map(function(D) {
          return [emap.get(D), emap.get(p.get(ds.s(imap.get(i), D)))];
        });
      },
      function(tmp, i) {
        return reps.map(function(D) {
          var v =
            (DS.m(ds, imap.get(i), imap.get(i+1), D) || 0)
            /
            DS.r(tmp, i, i+1, emap.get(D));
          return [emap.get(D), v];
        });
      }
    );
  }
};

var _apply = function _apply(p, i) {
  if (i < 0 || i >= p.size)
    return i;
  else
    return p.get(i) - 1;
};

var barycentricSubdivision = function barycentricSubdivision(ds, splitDim) {
  if (splitDim == 0)
    return ds;
  else {
    var dim = DS.dim(ds);
    var perms = I.List(permutations(splitDim + 1)).map(I.List);
    var pidx = I.Map(I.List(perms).zip(I.Range()));
    var n = DS.size(ds);
    var m = perms.size;

    return DS.build(
      DS.dim(ds), n * m,
      function(_, i) {
        return ds.elements().flatMap(function(D) {
          return I.Range(0, m).map(function(j) {
            var p = perms.get(j);
            if (i < splitDim) {
              var pi = p.set(i, p.get(i+1)).set(i+1, p.get(i));
              var k = pidx.get(pi);
              return [n * j + D, n * k + D];
            } else {
              var E = ds.s(_apply(p, i), D);
              return [n * j + D, n * j + E];
            }
          });
        });
      },
      function(tmp, i) {
        return ds.elements().flatMap(function(D) {
          return I.Range(0, m).map(function(j) {
            var p = perms.get(j);
            var v;
            if (i < splitDim - 1)
              v = 1;
            else
              v = ds.v(_apply(p, i), _apply(p, i+1), D);
            return [n * j + D, v];
          });
        });
      }
    );
  }
};

if (require.main == module) {
  var ds = DS.parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>');

  console.log('' + ds);
  console.log('' + dual(ds));

  console.log('' + cover(ds, 2, function(k, i, D) {
    return ds.s(i, D) == D ? 1 - k : k;
  }));

  console.log('' + minimal(DS.parse(
    '<1.1:24:' +
      '2 4 6 8 10 12 14 16 18 20 22 24,' +
      '16 3 5 7 9 11 13 15 24 19 21 23,' +
      '10 9 20 19 14 13 22 21 24 23 18 17:' +
      '8 4,3 3 3 3>')));

  console.log('' + barycentricSubdivision(ds, 0));
  console.log('' + barycentricSubdivision(ds, 1));
  console.log('' + barycentricSubdivision(ds, 2));

  var dia = DS.parse('<1.1:2 3:2,1 2,1 2,2:6,3 2,6>');
  console.log('' + minimal(dual(barycentricSubdivision(dia, 3))));
}
<|endoftext|>


<commit_before>angular.module('avAdmin')
    .factory(
      'ElectionsApi',
      function(
        $q,
        Plugins,
        Authmethod,
        ConfigService,
        $i18next,
        $http,
        $cookies,
        localStorageService,
        $rootScope)
      {

        /**
         * Generates a saved election key for the local storage key value store
         * in a secure way: the key is a cryptographically secure id, stored
         * as a cookie.
         */
        function getSavedElectionKey() {
          if (!$cookies.savedElectionKey) {
            /* jshint ignore:start */
            $cookies.savedElectionKey = "savedElectionKey_" + sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0));
            /* jshint ignore:end */
          }

          return $cookies.savedElectionKey;
        }

        /**
         * Return the saved election in local storage if it exists or null.
         */
        function getSavedElection() {
          return JSON.parse(
            localStorageService.get(
              getSavedElectionKey()));
        }

        /**
         * Saves locally the election into local storage.
         */
        function localSaveElection(election) {
          localStorageService.set(
            getSavedElectionKey(),
            JSON.stringify(election));
        }

        /**
         * Return boolean that specifies if there's any saved election in local
         * storage.
         */
        function hasSavedElection() {
          return !!localStorageService.get(
              getSavedElectionKey());
        }

        var backendUrl = ConfigService.electionsAPI;
        var electionsapi = {cache: {}, permcache: {}};
        electionsapi.waitingCurrent = [];
        electionsapi.currentElection = {};

        electionsapi.currentElections = [];
        electionsapi.newElection = false;

        electionsapi.waitForCurrent = function(f) {
            if (electionsapi.currentElection.id || electionsapi.newElection) {
                f();
            } else {
                electionsapi.waitingCurrent.push(f);
            }
        };

        electionsapi.setCurrent = function(el) {
            electionsapi.currentElection = el;
            electionsapi.newElection = !el.id;

            $rootScope.currentElection = el;
            if (!$rootScope.watchingElection) {
                $rootScope.$watch('currentElection', function(newv, oldv) {
                  Plugins.hook('election-modified', {'old': oldv, 'el': newv});
                  if (!$rootScope.currentElection.id) {
                    localSaveElection($rootScope.currentElection);
                  }
                }, true);
                $rootScope.watchingElection = true;
            }

            electionsapi.waitingCurrent.forEach(function(f) {
                f();
            });
            electionsapi.waitingCurrent = [];
        };

        if (hasSavedElection()) {
            try {
                var el = getSavedElection();
                console.log(getSavedElection());
                electionsapi.setCurrent(el);
            } catch (e) {
                localSaveElection(electionsapi.currentElection);
            }
        }

        function asyncElection(id) {
            var deferred = $q.defer();

            electionsapi.election(id)
                .success(function(data) {
                    var el = electionsapi.parseElection(data);
                    deferred.resolve(el);
                }).error(deferred.reject);

            return deferred.promise;
        }

        function asyncElectionAuth(el) {
            var deferred = $q.defer();

            Authmethod.viewEvent(el.id)
                .success(function(data) {
                    el.auth = {};
                    el.auth.authentication = data.events.auth_method;
                    el.auth.census = data.events.users;
                    el.raw = data.events;
                    if (el.auth.census) {
                        el.votes = el.stats.votes;
                        el.votes_percentage = ( el.stats.votes * 100 )/ el.auth.census;
                    } else {
                        el.votes_percentage = 0;
                        el.votes = el.stats.votes || 0;
                    }

                    // updating census
                    el.census.auth_method = data.events.auth_method;
                    el.census.extra_fields = data.events.extra_fields;
                    el.census.census = data.events.census;

                    var newConf = data.events.auth_method_config.config;
                    // not updating msgs if are modified
                    if (el.census.config && el.census.config.msg) {
                        newConf.msg = el.census.config.msg;
                        newConf.subject = el.census.config.subject;
                    }
                    el.census.config = newConf;

                    deferred.resolve(el);
                })
                .error(deferred.reject);

            return deferred.promise;
        }

        electionsapi.cache_election = function(id, election) {
            electionsapi.chache[id] = election;
        };

        electionsapi.getElection = function(id, ignorecache) {
            var deferred = $q.defer();

            var cached = electionsapi.cache[id];
            if (ignorecache || !cached) {
                asyncElection(id)
                  .then(electionsapi.stats)
                  .then(asyncElectionAuth)
                  .then(deferred.resolve)
                  .catch(deferred.reject);
            } else {
                deferred.resolve(cached);
            }

            return deferred.promise;
        };

        electionsapi.election = function(id) {
            return $http.get(backendUrl + 'election/'+id);
        };

        electionsapi.parseElection = function(d) {
            var election = d.payload;
            var conf = electionsapi.templateEl();
            conf = _.extend(conf, election.configuration);
            conf.status = election.state;
            conf.stats = {};
            conf.results = {};

            conf.votes = 0;
            conf.votes_percentage = 0;

            // number of answers
            conf.answers = 0;
            conf.questions.forEach(function(q) {
                conf.answers += q.answers.length;
            });

            // adding director to the list of authorities
            conf.auths = [conf.director, ];
            conf.authorities.forEach(function(a) { conf.auths.push(a); });

            // results
            if (election.results) {
                conf.results = angular.fromJson(election.results);
            }

            // extra_data
            if (!conf.extra_data) {
                conf.extra_data = {};
            } else if (typeof conf.extra_data === 'string') {
                conf.extra_data = JSON.parse(conf.extra_data);
            }

            // caching election
            electionsapi.cache[conf.id] = conf;
            return conf;
        };

        electionsapi.getEditPerm = function(id) {
            var deferred = $q.defer();

            var cached = electionsapi.permcache[id];
            if (!cached) {
                Authmethod.getPerm("edit", "AuthEvent", id)
                    .success(function(data) {
                        var perm = data['permission-token'];
                        electionsapi.permcache[id] = perm;
                        deferred.resolve(perm);
                    });
            } else {
                deferred.resolve(cached);
            }

            return deferred.promise;
        };

        electionsapi.stats = function(el) {
            var deferred = $q.defer();

            electionsapi.command(el, 'stats', 'GET')
                .then(function(d) {
                        el.stats = d.payload;
                        deferred.resolve(el);
                      })
                 .catch(deferred.reject);

            return deferred.promise;
        };

        electionsapi.autoreloadStatsTimer = null;
        electionsapi.autoreloadStats = function(el) {
            clearTimeout(electionsapi.autoreloadStatsTimer);
            if (!el) {
                return;
            }

            electionsapi.stats(el)
                .then(asyncElectionAuth)
                .finally(function() {
                    electionsapi.autoreloadStatsTimer = setTimeout(function() { electionsapi.autoreloadStats(el); }, 5000);
                });
        };

        electionsapi.results = function(el) {
            var deferred = $q.defer();

            electionsapi.command(el, 'results', 'GET')
                .then(function(d) {
                        el.results = angular.fromJson(d.payload);
                        deferred.resolve(el);
                      })
                 .catch(deferred.reject);

            return deferred.promise;
        };

        electionsapi.command = function(el, command, method, data) {
            var deferred = $q.defer();
            var m = {};
            var d = data || {};
            var url = backendUrl + 'election/'+el.id;

            if (command) {
                url += '/'+command;
            }

            electionsapi.getEditPerm(el.id)
                .then(function(perm) {
                    if (method === "POST") {
                        m = $http.post(url, data, {headers: {'Authorization': perm}});
                    } else {
                        m = $http.get(url, {headers: {'Authorization': perm}});
                    }

                    m.success(deferred.resolve).error(deferred.reject);
                });

            return deferred.promise;
        };

        electionsapi.templateEl = function() {
            var el = {
                title: $i18next('avAdmin.sidebar.newel'),
                description: "",
                start_date: "2015-01-27T16:00:00.001",
                end_date: "2015-01-27T16:00:00.001",
                authorities: ConfigService.authorities,
                director: ConfigService.director,
                presentation: {
                    theme: 'default',
                    share_text: '',
                    urls: [],
                    theme_css: ''
                },
                layout: 'simple',
                real: false,
                census: {
                    voters: [],
                    auth_method: 'email',
                    census:'close',
                    extra_fields: [ ],
                    config: {
                        "msg": $i18next('avAdmin.auth.emaildef'),
                        "subject": $i18next('avAdmin.auth.emailsubdef',
                          {name: ConfigService.organization.orgName}),
                        "authentication-action": {
                          "mode": "vote",
                          "mode-config": {
                            "url": ""
                          }
                        },
                        "registration-action": {
                          "mode": "vote",
                          "mode-config": null
                        }
                    }
                },
                questions: [],
                extra_data: {}
            };
            return el;
        };

        electionsapi.templateQ = function(title) {
            var q = {
                "answer_total_votes_percentage": "over-total-valid-votes",
                "answers": [],
                "description": "",
                "layout": "accordion",
                "max": 1,
                "min": 1,
                "num_winners": 1,
                "randomize_answer_order": true,
                "tally_type": "plurality-at-large",
                "title": title
            };
            return q;
        };

        electionsapi.getCensus = function(el, page, size, filterStr, filterOptions) {
            var deferred = $q.defer();

            if (size === 'max') {
              size = 500;
            } else if (angular.isNumber(size) && size > 0 && size < 500) {
              size = parseInt(size);
            } else {
              size = 10;
            }

            function getAuthCensus(d) {
              var voters = d.payload;
              var deferred = $q.defer();
              var params = {};

              if (!angular.isNumber(page)) {
                page = 1;
              }
              params.page = page;
              params.size = size;
              _.extend(params, filterOptions);
              if (filterStr && filterStr.length > 0) {
                params.filter = filterStr;
              }

              Authmethod.getCensus(el.id, params)
                .success(function(data) {
                  _.each(data.object_list, function(user) {
                    user.vote = false;
                    if (voters.indexOf(user.username) >= 0) {
                      user.vote = true;
                    }
                  });
                  if (!angular.isArray(el.census.voters)) {
                    el.census.voters = [];
                  }
                  _.each(data.object_list, function (obj) {
                    obj.selected = false;
                    el.census.voters.push(obj);
                  });
                  el.data = data;
                  deferred.resolve(el);
                })
                .error(deferred.reject);
                return deferred.promise;
            }

            electionsapi.command(el, 'voters', 'GET')
                .then(getAuthCensus)
                .then(deferred.resolve)
                .catch(deferred.reject);

            return deferred.promise;
        };

        return electionsapi;
    });
<commit_msg>adding view permission<commit_after>angular.module('avAdmin')
    .factory(
      'ElectionsApi',
      function(
        $q,
        Plugins,
        Authmethod,
        ConfigService,
        $i18next,
        $http,
        $cookies,
        localStorageService,
        $rootScope)
      {

        /**
         * Generates a saved election key for the local storage key value store
         * in a secure way: the key is a cryptographically secure id, stored
         * as a cookie.
         */
        function getSavedElectionKey() {
          if (!$cookies.savedElectionKey) {
            /* jshint ignore:start */
            $cookies.savedElectionKey = "savedElectionKey_" + sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0));
            /* jshint ignore:end */
          }

          return $cookies.savedElectionKey;
        }

        /**
         * Return the saved election in local storage if it exists or null.
         */
        function getSavedElection() {
          return JSON.parse(
            localStorageService.get(
              getSavedElectionKey()));
        }

        /**
         * Saves locally the election into local storage.
         */
        function localSaveElection(election) {
          localStorageService.set(
            getSavedElectionKey(),
            JSON.stringify(election));
        }

        /**
         * Return boolean that specifies if there's any saved election in local
         * storage.
         */
        function hasSavedElection() {
          return !!localStorageService.get(
              getSavedElectionKey());
        }

        var backendUrl = ConfigService.electionsAPI;
        var electionsapi = {cache: {}, permcache: {}};
        electionsapi.waitingCurrent = [];
        electionsapi.currentElection = {};

        electionsapi.currentElections = [];
        electionsapi.newElection = false;

        electionsapi.waitForCurrent = function(f) {
            if (electionsapi.currentElection.id || electionsapi.newElection) {
                f();
            } else {
                electionsapi.waitingCurrent.push(f);
            }
        };

        electionsapi.setCurrent = function(el) {
            electionsapi.currentElection = el;
            electionsapi.newElection = !el.id;

            $rootScope.currentElection = el;
            if (!$rootScope.watchingElection) {
                $rootScope.$watch('currentElection', function(newv, oldv) {
                  Plugins.hook('election-modified', {'old': oldv, 'el': newv});
                  if (!$rootScope.currentElection.id) {
                    localSaveElection($rootScope.currentElection);
                  }
                }, true);
                $rootScope.watchingElection = true;
            }

            electionsapi.waitingCurrent.forEach(function(f) {
                f();
            });
            electionsapi.waitingCurrent = [];
        };

        if (hasSavedElection()) {
            try {
                var el = getSavedElection();
                console.log(getSavedElection());
                electionsapi.setCurrent(el);
            } catch (e) {
                localSaveElection(electionsapi.currentElection);
            }
        }

        function asyncElection(id) {
            var deferred = $q.defer();

            electionsapi.election(id)
                .success(function(data) {
                    var el = electionsapi.parseElection(data);
                    deferred.resolve(el);
                }).error(deferred.reject);

            return deferred.promise;
        }

        function asyncElectionAuth(el) {
            var deferred = $q.defer();

            Authmethod.viewEvent(el.id)
                .success(function(data) {
                    el.auth = {};
                    el.auth.authentication = data.events.auth_method;
                    el.auth.census = data.events.users;
                    el.raw = data.events;
                    if (el.auth.census) {
                        el.votes = el.stats.votes;
                        el.votes_percentage = ( el.stats.votes * 100 )/ el.auth.census;
                    } else {
                        el.votes_percentage = 0;
                        el.votes = el.stats.votes || 0;
                    }

                    // updating census
                    el.census.auth_method = data.events.auth_method;
                    el.census.extra_fields = data.events.extra_fields;
                    el.census.census = data.events.census;

                    var newConf = data.events.auth_method_config.config;
                    // not updating msgs if are modified
                    if (el.census.config && el.census.config.msg) {
                        newConf.msg = el.census.config.msg;
                        newConf.subject = el.census.config.subject;
                    }
                    el.census.config = newConf;

                    deferred.resolve(el);
                })
                .error(deferred.reject);

            return deferred.promise;
        }

        electionsapi.cache_election = function(id, election) {
            electionsapi.chache[id] = election;
        };

        electionsapi.getElection = function(id, ignorecache) {
            var deferred = $q.defer();

            var cached = electionsapi.cache[id];
            if (ignorecache || !cached) {
                asyncElection(id)
                  .then(electionsapi.stats)
                  .then(asyncElectionAuth)
                  .then(deferred.resolve)
                  .catch(deferred.reject);
            } else {
                deferred.resolve(cached);
            }

            return deferred.promise;
        };

        electionsapi.election = function(id) {
            return $http.get(backendUrl + 'election/'+id);
        };

        electionsapi.parseElection = function(d) {
            var election = d.payload;
            var conf = electionsapi.templateEl();
            conf = _.extend(conf, election.configuration);
            conf.status = election.state;
            conf.stats = {};
            conf.results = {};

            conf.votes = 0;
            conf.votes_percentage = 0;

            // number of answers
            conf.answers = 0;
            conf.questions.forEach(function(q) {
                conf.answers += q.answers.length;
            });

            // adding director to the list of authorities
            conf.auths = [conf.director, ];
            conf.authorities.forEach(function(a) { conf.auths.push(a); });

            // results
            if (election.results) {
                conf.results = angular.fromJson(election.results);
            }

            // extra_data
            if (!conf.extra_data) {
                conf.extra_data = {};
            } else if (typeof conf.extra_data === 'string') {
                conf.extra_data = JSON.parse(conf.extra_data);
            }

            // caching election
            electionsapi.cache[conf.id] = conf;
            return conf;
        };

        electionsapi.getEditPerm = function(id) {
            var deferred = $q.defer();

            var cached = electionsapi.permcache[id];
            if (!cached) {
                Authmethod.getPerm("edit", "AuthEvent", id)
                    .success(function(data) {
                        var perm = data['permission-token'];
                        electionsapi.permcache[id] = perm;
                        deferred.resolve(perm);
                    }).error(function (data) {
                      // try with view permission
                      Authmethod.getPerm("view", "AuthEvent", id)
                          .success(function(data) {
                              var perm = data['permission-token'];
                              electionsapi.permcache[id] = perm;
                              deferred.resolve(perm);
                          });
                    });
            } else {
                deferred.resolve(cached);
            }

            return deferred.promise;
        };

        electionsapi.stats = function(el) {
            var deferred = $q.defer();

            electionsapi.command(el, 'stats', 'GET')
                .then(function(d) {
                        el.stats = d.payload;
                        deferred.resolve(el);
                      })
                 .catch(deferred.reject);

            return deferred.promise;
        };

        electionsapi.autoreloadStatsTimer = null;
        electionsapi.autoreloadStats = function(el) {
            clearTimeout(electionsapi.autoreloadStatsTimer);
            if (!el) {
                return;
            }

            electionsapi.stats(el)
                .then(asyncElectionAuth)
                .finally(function() {
                    electionsapi.autoreloadStatsTimer = setTimeout(function() { electionsapi.autoreloadStats(el); }, 5000);
                });
        };

        electionsapi.results = function(el) {
            var deferred = $q.defer();

            electionsapi.command(el, 'results', 'GET')
                .then(function(d) {
                        el.results = angular.fromJson(d.payload);
                        deferred.resolve(el);
                      })
                 .catch(deferred.reject);

            return deferred.promise;
        };

        electionsapi.command = function(el, command, method, data) {
            var deferred = $q.defer();
            var m = {};
            var d = data || {};
            var url = backendUrl + 'election/'+el.id;

            if (command) {
                url += '/'+command;
            }

            electionsapi.getEditPerm(el.id)
                .then(function(perm) {
                    if (method === "POST") {
                        m = $http.post(url, data, {headers: {'Authorization': perm}});
                    } else {
                        m = $http.get(url, {headers: {'Authorization': perm}});
                    }

                    m.success(deferred.resolve).error(deferred.reject);
                });

            return deferred.promise;
        };

        electionsapi.templateEl = function() {
            var el = {
                title: $i18next('avAdmin.sidebar.newel'),
                description: "",
                start_date: "2015-01-27T16:00:00.001",
                end_date: "2015-01-27T16:00:00.001",
                authorities: ConfigService.authorities,
                director: ConfigService.director,
                presentation: {
                    theme: 'default',
                    share_text: '',
                    urls: [],
                    theme_css: ''
                },
                layout: 'simple',
                real: false,
                census: {
                    voters: [],
                    auth_method: 'email',
                    census:'close',
                    extra_fields: [ ],
                    config: {
                        "msg": $i18next('avAdmin.auth.emaildef'),
                        "subject": $i18next('avAdmin.auth.emailsubdef',
                          {name: ConfigService.organization.orgName}),
                        "authentication-action": {
                          "mode": "vote",
                          "mode-config": {
                            "url": ""
                          }
                        },
                        "registration-action": {
                          "mode": "vote",
                          "mode-config": null
                        }
                    }
                },
                questions: [],
                extra_data: {}
            };
            return el;
        };

        electionsapi.templateQ = function(title) {
            var q = {
                "answer_total_votes_percentage": "over-total-valid-votes",
                "answers": [],
                "description": "",
                "layout": "accordion",
                "max": 1,
                "min": 1,
                "num_winners": 1,
                "randomize_answer_order": true,
                "tally_type": "plurality-at-large",
                "title": title
            };
            return q;
        };

        electionsapi.getCensus = function(el, page, size, filterStr, filterOptions) {
            var deferred = $q.defer();

            if (size === 'max') {
              size = 500;
            } else if (angular.isNumber(size) && size > 0 && size < 500) {
              size = parseInt(size);
            } else {
              size = 10;
            }

            function getAuthCensus(d) {
              var voters = d.payload;
              var deferred = $q.defer();
              var params = {};

              if (!angular.isNumber(page)) {
                page = 1;
              }
              params.page = page;
              params.size = size;
              _.extend(params, filterOptions);
              if (filterStr && filterStr.length > 0) {
                params.filter = filterStr;
              }

              Authmethod.getCensus(el.id, params)
                .success(function(data) {
                  _.each(data.object_list, function(user) {
                    user.vote = false;
                    if (voters.indexOf(user.username) >= 0) {
                      user.vote = true;
                    }
                  });
                  if (!angular.isArray(el.census.voters)) {
                    el.census.voters = [];
                  }
                  _.each(data.object_list, function (obj) {
                    obj.selected = false;
                    el.census.voters.push(obj);
                  });
                  el.data = data;
                  deferred.resolve(el);
                })
                .error(deferred.reject);
                return deferred.promise;
            }

            electionsapi.command(el, 'voters', 'GET')
                .then(getAuthCensus)
                .then(deferred.resolve)
                .catch(deferred.reject);

            return deferred.promise;
        };

        return electionsapi;
    });
<|endoftext|>


<commit_before>/* jshint -W084 */
var React = require('react');
var warning = require('react/lib/warning');
var invariant = require('react/lib/invariant');
var DefaultRoute = require('./components/DefaultRoute');
var NotFoundRoute = require('./components/NotFoundRoute');
var Redirect = require('./components/Redirect');
var Path = require('./utils/Path');

function createRedirectHandler(to, _params, _query) {
  return React.createClass({
    statics: {
      willTransitionTo: function (transition, params, query) {
        transition.redirect(to, _params || params, _query || query);
      }
    },

    render: function () {
      return null;
    }
  });
}

function checkPropTypes(componentName, propTypes, props) {
  for (var propName in propTypes) {
    if (propTypes.hasOwnProperty(propName)) {
      var error = propTypes[propName](props, propName, componentName);

      if (error instanceof Error)
        warning(false, error.message);
    }
  }
}

function createRoute(element, parentRoute, namedRoutes) {
  var type = element.type;
  var props = element.props;

  if (type.propTypes)
    checkPropTypes(type.displayName, type.propTypes, props);

  var route = { name: props.name };

  if (props.ignoreScrollBehavior)
    route.ignoreScrollBehavior = true;

  if (type === Redirect.type) {
    route.handler = createRedirectHandler(props.to, props.params, props.query);
    props.path = props.path || props.from || '*';
  } else {
    route.handler = props.handler;
  }

  var parentPath = (parentRoute && parentRoute.path) || '/';

  if ((props.path || props.name) && type !== DefaultRoute.type && type !== NotFoundRoute.type) {
    var path = props.path || props.name;

    // Relative paths extend their parent.
    if (!Path.isAbsolute(path))
      path = Path.join(parentPath, path);

    route.path = Path.normalize(path);
  } else {
    route.path = parentPath;

    if (type === NotFoundRoute.type)
      route.path += '*';
  }

  route.paramNames = Path.extractParamNames(route.path);

  // Make sure the route's path has all params its parent needs.
  if (parentRoute && Array.isArray(parentRoute.paramNames)) {
    parentRoute.paramNames.forEach(function (paramName) {
      invariant(
        route.paramNames.indexOf(paramName) !== -1,
        'The nested route path "%s" is missing the "%s" parameter of its parent path "%s"',
        route.path, paramName, parentRoute.path
      );
    });
  }

  // Make sure the route can be looked up by <Link>s.
  if (props.name) {
    invariant(
      namedRoutes[props.name] == null,
      'You cannot use the name "%s" for more than one route',
      props.name
    );

    namedRoutes[props.name] = route;
  }

  // Handle <NotFoundRoute>.
  if (type === NotFoundRoute.type) {
    invariant(
      parentRoute,
      '<NotFoundRoute> must have a parent <Route>'
    );

    invariant(
      parentRoute.notFoundRoute == null,
      'You may not have more than one <NotFoundRoute> per <Route>'
    );

    parentRoute.notFoundRoute = route;

    return null;
  }

  // Handle <DefaultRoute>.
  if (type === DefaultRoute.type) {
    invariant(
      parentRoute,
      '<DefaultRoute> must have a parent <Route>'
    );

    invariant(
      parentRoute.defaultRoute == null,
      'You may not have more than one <DefaultRoute> per <Route>'
    );

    parentRoute.defaultRoute = route;

    return null;
  }

  route.routes = createRoutesFromReactChildren(props.children, route, namedRoutes);

  return route;
}

/**
 * Creates and returns an array of route objects from the given ReactChildren.
 */
function createRoutesFromReactChildren(children, parentRoute, namedRoutes) {
  var routes = [];

  React.Children.forEach(children, function (child) {
    // Exclude null values, <DefaultRoute>s and <NotFoundRoute>s.
    if (child && (child = createRoute(child, parentRoute, namedRoutes)))
      routes.push(child);
  });

  return routes;
}

module.exports = createRoutesFromReactChildren;
<commit_msg>Throw when <DefaultRoute> or <NotFoundRoute> have children<commit_after>/* jshint -W084 */
var React = require('react');
var warning = require('react/lib/warning');
var invariant = require('react/lib/invariant');
var DefaultRoute = require('./components/DefaultRoute');
var NotFoundRoute = require('./components/NotFoundRoute');
var Redirect = require('./components/Redirect');
var Path = require('./utils/Path');

function createRedirectHandler(to, _params, _query) {
  return React.createClass({
    statics: {
      willTransitionTo: function (transition, params, query) {
        transition.redirect(to, _params || params, _query || query);
      }
    },

    render: function () {
      return null;
    }
  });
}

function checkPropTypes(componentName, propTypes, props) {
  for (var propName in propTypes) {
    if (propTypes.hasOwnProperty(propName)) {
      var error = propTypes[propName](props, propName, componentName);

      if (error instanceof Error)
        warning(false, error.message);
    }
  }
}

function createRoute(element, parentRoute, namedRoutes) {
  var type = element.type;
  var props = element.props;

  if (type.propTypes)
    checkPropTypes(type.displayName, type.propTypes, props);

  var route = { name: props.name };

  if (props.ignoreScrollBehavior)
    route.ignoreScrollBehavior = true;

  if (type === Redirect.type) {
    route.handler = createRedirectHandler(props.to, props.params, props.query);
    props.path = props.path || props.from || '*';
  } else {
    route.handler = props.handler;
  }

  var parentPath = (parentRoute && parentRoute.path) || '/';

  if ((props.path || props.name) && type !== DefaultRoute.type && type !== NotFoundRoute.type) {
    var path = props.path || props.name;

    // Relative paths extend their parent.
    if (!Path.isAbsolute(path))
      path = Path.join(parentPath, path);

    route.path = Path.normalize(path);
  } else {
    route.path = parentPath;

    if (type === NotFoundRoute.type)
      route.path += '*';
  }

  route.paramNames = Path.extractParamNames(route.path);

  // Make sure the route's path has all params its parent needs.
  if (parentRoute && Array.isArray(parentRoute.paramNames)) {
    parentRoute.paramNames.forEach(function (paramName) {
      invariant(
        route.paramNames.indexOf(paramName) !== -1,
        'The nested route path "%s" is missing the "%s" parameter of its parent path "%s"',
        route.path, paramName, parentRoute.path
      );
    });
  }

  // Make sure the route can be looked up by <Link>s.
  if (props.name) {
    invariant(
      namedRoutes[props.name] == null,
      'You cannot use the name "%s" for more than one route',
      props.name
    );

    namedRoutes[props.name] = route;
  }

  // Handle <NotFoundRoute>.
  if (type === NotFoundRoute.type) {
    invariant(
      parentRoute,
      '<NotFoundRoute> must have a parent <Route>'
    );

    invariant(
      parentRoute.notFoundRoute == null,
      'You may not have more than one <NotFoundRoute> per <Route>'
    );

    invariant(
      props.children == null,
      '<NotFoundRoute> must not have children'
    );

    parentRoute.notFoundRoute = route;

    return null;
  }

  // Handle <DefaultRoute>.
  if (type === DefaultRoute.type) {
    invariant(
      parentRoute,
      '<DefaultRoute> must have a parent <Route>'
    );

    invariant(
      parentRoute.defaultRoute == null,
      'You may not have more than one <DefaultRoute> per <Route>'
    );

    invariant(
      props.children == null,
      '<DefaultRoute> must not have children'
    );

    parentRoute.defaultRoute = route;

    return null;
  }

  route.routes = createRoutesFromReactChildren(props.children, route, namedRoutes);

  return route;
}

/**
 * Creates and returns an array of route objects from the given ReactChildren.
 */
function createRoutesFromReactChildren(children, parentRoute, namedRoutes) {
  var routes = [];

  React.Children.forEach(children, function (child) {
    // Exclude null values, <DefaultRoute>s and <NotFoundRoute>s.
    if (child && (child = createRoute(child, parentRoute, namedRoutes)))
      routes.push(child);
  });

  return routes;
}

module.exports = createRoutesFromReactChildren;
<|endoftext|>


<commit_before>/**
 * @fileOverview CRUD controller.
 */
var fs = require('fs');
var util = require('util');

var __ = require('lodash');
var jade = require('jade');

var Controller = require('./controller');
var tplHelpers = require('./tpl-helpers.js');

var PaginationMidd = require('./pagination.midd');

/**
 * The CRUD Controller
 *
 * @param {mongoose.Model} Model The mongoose model.
 * @param {string} baseUrl The base url.
 * @param {Object=} optOpts Optionally define options.
 * @contructor
 * @extends {crude.Controller}
 */
var CrudCtrl = module.exports = function(Model, baseUrl, optOpts){
  Controller.apply(this, arguments);

  this.Model = Model;
  this.baseUrl = baseUrl;
  this._schemaViews = null;
  var defaultOpts = {
    baseUrl: baseUrl,
    urlField: '_localUrl',
    nameField: 'name',
    idField: 'id',
    // A jade view
    layoutView: null,
    // The edit / create view.
    editView: null,
    // show the doc id
    showId: false,
  };
  this.opts = __.extend(defaultOpts, optOpts || {});

  // prep pagination
  var paginationMidd = new PaginationMidd();

  // define CRUD handlers
  this.create = [this._create.bind(this)];
  this.createView = [this._createView.bind(this)];
  this.readList = [
    this._prepResponse.bind(this),
    paginationMidd.paginate(Model),
    this._readList.bind(this),
  ];
  this.readOne = [
    this._prepResponse.bind(this),
    this._readOne.bind(this),
  ];
  this.update = [
    this._prepResponse.bind(this),
    this._update.bind(this),
  ];
  this.updateView = [
    this._prepResponse.bind(this),
    this._updateView.bind(this),
  ];
  this.delete = [this._delete.bind(this)];

  // set default view template locations
  this.views = {
    add: __dirname + '/views/add.jade',
    view: __dirname + '/views/view.jade',
    list: __dirname + '/views/list.jade',
    edit: __dirname + '/views/edit.jade',
  };

  this.compiled = {};

  __.forOwn(this.views, function(tpl, key) {
    fs.readFile(tpl, function(err, data){
      if (err) {
        console.error('Error Reading file "' + tpl + '", error:', err);
        return;
      }
      this.compiled[key] = jade.compile(data, {
        filename: tpl
      });

    }.bind(this));
  }, this);
};
util.inherits(CrudCtrl, Controller);

/** @define {string} The view key in which the output will be available.  */
CrudCtrl.VIEW_OUTPUT_KEY = 'crudView';

/**
 * Mark the proto to check for proper inheritance of methods,
 * we do this so we can lax comparison by not relying 100% on "instanceof".
 *
 * @type {boolean}
 * @protected
 */
CrudCtrl.prototype._isCrude = true;

/**
 * Prepare the response object for each request, an internal middleware.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @param {Function} next callback.
 * @protected
 */
CrudCtrl.prototype._prepResponse = function(req, res, next) {
  res.locals.opts = this.opts;
  res.locals.schema = this._getSchema();

  // all template functions
  res.locals.fn = {};
  __.extend(res.locals.fn, tplHelpers);

  next();
};

/**
 * Add fields required for the views to render properly.
 *
 * Render at first runtime and serve cached afterwards.
 *
 * @return {Object} An extended mongoose schema.
 */
CrudCtrl.prototype._getSchema = function() {
  if (this._schemaViews) {
    return this._schemaViews;
  }

  var schemaViews = this._schemaViews = Object.create(this.Model.schema.paths);

  __.forIn(schemaViews, function(schemaItem, path) {
    schemaViews[path]._viewData = {
      canShow: tplHelpers.canShow(schemaItem, this.opts),
      name: tplHelpers.getName(path),
    };
  }, this);

  return schemaViews;
};

/**
 * Handle new item creation
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._create = function(req, res) {
  var item = new this.Model(req.body);
  item.save(this._createCallback.bind(this, req, res));
};

/**
 * Handle a new item save, this is the CrudCtrl Model Save callback.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @param {Error=} err Operation failed.
 * @param {mongoose.Document} optDoc The saved document.
 * @protected
 */
CrudCtrl.prototype._createCallback = function(req, res, err, optDoc){
  if (err) {
    this.addFlashError(req, err);
    return res.redirect(this.baseUrl + '/add');
  }

  this.addFlashSuccess(req, optDoc);
  res.redirect(this.baseUrl + '/' + optDoc[this.opts.urlField]);
};

/**
 * Create an item view.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._createView = function(req, res){
  this.checkFlashError(req, res);
  this.checkFlashSuccess(req, res);
  res.render(this.opts.editView);
};

/**
 * Handle item listing.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._readList = function(req, res){
  // render the template and store in response locals.
  res.locals[CrudCtrl.VIEW_OUTPUT_KEY] = this.compiled.list(res.locals);

  if (!this.opts.layoutView) {
    res.send(res.locals[CrudCtrl.VIEW_OUTPUT_KEY]);
  } else {
    res.render(this.opts.layoutView);
  }
};

/**
 * Handle a single item view.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._readOne = function(req, res){
  // attempt to fetch the record...
  var query = new Object(null);
  query[this.opts.urlField] = req.params.id;

  this.Model.findOne(query, function(err, doc){
    if (err) {
      this.addError(res, err);
      return res.render(this.views.view);
    }

    if (!doc) {
      var error = new Error('No results');
      this.addError(res, error);
      return res.render(this.views.view);
    }

    // assign the item to the tpl vars.
    res.locals.item = doc;

    this.checkFlashSuccess(req, res);

    // render the template and store in response locals.
    res.locals[CrudCtrl.VIEW_OUTPUT_KEY] = this.compiled.view(res.locals);

    if (!this.opts.layoutView) {
      res.send(res.locals[CrudCtrl.VIEW_OUTPUT_KEY]);
    } else {
      res.render(this.opts.layoutView);
    }
  }.bind(this));
};

/**
 * Handle item update.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._update = function(req, res) {
  if (!req.body.id) {
    return res.send('Not implemented. No "id" field passed');
  }
  if (!this.opts.editView) {
    return res.send('Not implemented. Define "editView" parameter.');
  }

  // attempt to fetch the record...
  this.Model.findById(req.body.id, function(err, doc){
    if (err) {
      this.addFlashError(req, err);
      // log.fine('_update() :: Fetch item fail:', err.type, err.message);
      return res.redirect(req.header('Referer'));
    }

    var query = new Object(null);
    query[this.opts.idField] = req.body.id;
    this.Model.update(query, { $set: req.body },
      this._updateCallback.bind(this, req, res, doc));
  }.bind(this));
};

/**
 * Handle an update callback, this is the Model Update callback.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @param {mongoose.Document} doc The mongoose document.
 * @param {Error=} err Operation failed.
 * @param {number=} optUpdateCount How many records were updated.
 * @protected
 */
CrudCtrl.prototype._updateCallback = function(req, res, doc, err, optUpdateCount){

  if (err) {
    this.addFlashError(req, err);
    // log.fine('_updateCallback() :: Edit item fail:', err.message);
    return res.redirect(req.header('Referer'));
  }

  if (1 !== optUpdateCount) {
    var error = new error.Database('No record was updated');
    error.count = optUpdateCount;
    this.addFlashError(req, error);
    // log.fine('_updateCallback() :: Fail, no record was updated. Update Count:', optUpdateCount);
    return res.redirect(req.header('Referer'));
  }

  var finalPath = req.url.split('/').pop();
  if (doc[this.opts.urlField] !== finalPath) {
    // log.fine('_updateCallback() :: Changed url. Old:', req.url, 'New:', doc);
    this.addFlashSuccess(req, doc);
    return res.redirect(this.baseUrl + '/' + doc._localUrl);
  }

  this.addSuccess(res, doc);
  res.locals.item = doc;

  // render the template and store in response locals.
  res.locals[CrudCtrl.VIEW_OUTPUT_KEY] = this.compiled.view(res.locals);
  if (!this.opts.layoutView) {
    res.send(res.locals[CrudCtrl.VIEW_OUTPUT_KEY]);
  } else {
    res.render(this.opts.layoutView);
  }
};

/**
 * Show single item update view
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._updateView = function(req, res) {
  if (!this.opts.editView) {
    return res.send('Not implemented. Define "editView" parameter.');
  }

  // attempt to fetch the record...
  var query = new Object(null);
  query[this.opts.urlField] = req.params.id;
  this.Model.findOne(query, function(err, doc){
    if (err) {
      this.addError(res, err);
      return res.render(this.opts.editView);
    }

    if (!doc) {
      var error = new Error('No results');
      this.addError(res, error);
      return res.render(this.opts.editView);
    }

    // assign the item to the tpl vars.
    res.locals.item = doc;

    this.checkFlashError(req, res);

    res.render(this.opts.editView);
  }.bind(this));
};

/**
 * Handle item deletion.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._delete = function(req, res){
  res.send('NOT IMPLEMENTED');
};

<commit_msg>Add process helper func<commit_after>/**
 * @fileOverview CRUD controller.
 */
var fs = require('fs');
var util = require('util');

var __ = require('lodash');
var jade = require('jade');

var Controller = require('./controller');
var tplHelpers = require('./tpl-helpers.js');

var PaginationMidd = require('./pagination.midd');

/**
 * The CRUD Controller
 *
 * @param {mongoose.Model} Model The mongoose model.
 * @param {string} baseUrl The base url.
 * @param {Object=} optOpts Optionally define options.
 * @contructor
 * @extends {crude.Controller}
 */
var CrudCtrl = module.exports = function(Model, baseUrl, optOpts){
  Controller.apply(this, arguments);

  this.Model = Model;
  this.baseUrl = baseUrl;
  this._schemaViews = null;
  var defaultOpts = {
    baseUrl: baseUrl,
    urlField: '_localUrl',
    nameField: 'name',
    idField: 'id',
    // A jade view
    layoutView: null,
    // The edit / create view.
    editView: null,
    // show the doc id
    showId: false,
  };
  this.opts = __.extend(defaultOpts, optOpts || {});

  // prep pagination
  var paginationMidd = new PaginationMidd();

  // define CRUD handlers
  this.create = [this._create.bind(this)];
  this.createView = [this._createView.bind(this)];
  this.readList = [
    this._prepResponse.bind(this),
    paginationMidd.paginate(Model),
    this._readList.bind(this),
  ];
  this.readOne = [
    this._prepResponse.bind(this),
    this._readOne.bind(this),
  ];
  this.update = [
    this._prepResponse.bind(this),
    this._update.bind(this),
  ];
  this.updateView = [
    this._prepResponse.bind(this),
    this._updateView.bind(this),
  ];
  this.delete = [this._delete.bind(this)];

  // set default view template locations
  this.views = {
    add: __dirname + '/views/add.jade',
    view: __dirname + '/views/view.jade',
    list: __dirname + '/views/list.jade',
    edit: __dirname + '/views/edit.jade',
  };

  this.compiled = {};

  __.forOwn(this.views, function(tpl, key) {
    fs.readFile(tpl, function(err, data){
      if (err) {
        console.error('Error Reading file "' + tpl + '", error:', err);
        return;
      }
      this.compiled[key] = jade.compile(data, {
        filename: tpl
      });

    }.bind(this));
  }, this);
};
util.inherits(CrudCtrl, Controller);

/** @define {string} The view key in which the output will be available.  */
CrudCtrl.VIEW_OUTPUT_KEY = 'crudView';

/**
 * Mark the proto to check for proper inheritance of methods,
 * we do this so we can lax comparison by not relying 100% on "instanceof".
 *
 * @type {boolean}
 * @protected
 */
CrudCtrl.prototype._isCrude = true;

/**
 * Prepare the response object for each request, an internal middleware.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @param {Function} next callback.
 * @protected
 */
CrudCtrl.prototype._prepResponse = function(req, res, next) {
  res.locals.opts = this.opts;
  res.locals.schema = this._getSchema();

  // all template functions
  res.locals.fn = {};
  __.extend(res.locals.fn, tplHelpers);

  next();
};

/**
 * Add fields required for the views to render properly.
 *
 * Render at first runtime and serve cached afterwards.
 *
 * @return {Object} An extended mongoose schema.
 */
CrudCtrl.prototype._getSchema = function() {
  if (this._schemaViews) {
    return this._schemaViews;
  }

  var schemaViews = this._schemaViews = Object.create(this.Model.schema.paths);

  __.forIn(schemaViews, function(schemaItem, path) {
    schemaViews[path]._viewData = {
      canShow: tplHelpers.canShow(schemaItem, this.opts),
      name: tplHelpers.getName(path),
    };
  }, this);

  return schemaViews;
};

/**
 * Handle new item creation
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._create = function(req, res) {
  var item = new this.Model(req.body);
  item.save(this._createCallback.bind(this, req, res));
};

/**
 * Handle a new item save, this is the CrudCtrl Model Save callback.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @param {Error=} err Operation failed.
 * @param {mongoose.Document} optDoc The saved document.
 * @protected
 */
CrudCtrl.prototype._createCallback = function(req, res, err, optDoc){
  if (err) {
    this.addFlashError(req, err);
    return res.redirect(this.baseUrl + '/add');
  }

  this.addFlashSuccess(req, optDoc);
  res.redirect(this.baseUrl + '/' + optDoc[this.opts.urlField]);
};

/**
 * Create an item view.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._createView = function(req, res){
  this.checkFlashError(req, res);
  this.checkFlashSuccess(req, res);
  res.render(this.opts.editView);
};

/**
 * Handle item listing.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._readList = function(req, res){
  // render the template and store in response locals.
  res.locals[CrudCtrl.VIEW_OUTPUT_KEY] = this.compiled.list(res.locals);

  if (!this.opts.layoutView) {
    res.send(res.locals[CrudCtrl.VIEW_OUTPUT_KEY]);
  } else {
    res.render(this.opts.layoutView);
  }
};

/**
 * Handle a single item view.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._readOne = function(req, res){
  // attempt to fetch the record...
  var query = new Object(null);
  query[this.opts.urlField] = req.params.id;

  this.Model.findOne(query, function(err, doc){
    if (err) {
      this.addError(res, err);
      return res.render(this.views.view);
    }

    if (!doc) {
      var error = new Error('No results');
      this.addError(res, error);
      return res.render(this.views.view);
    }

    // assign the item to the tpl vars.
    res.locals.item = doc;

    this.checkFlashSuccess(req, res);

    // render the template and store in response locals.
    res.locals[CrudCtrl.VIEW_OUTPUT_KEY] = this.compiled.view(res.locals);

    if (!this.opts.layoutView) {
      res.send(res.locals[CrudCtrl.VIEW_OUTPUT_KEY]);
    } else {
      res.render(this.opts.layoutView);
    }
  }.bind(this));
};

/**
 * Handle item update.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._update = function(req, res) {
  if (!req.body.id) {
    return res.send('Not implemented. No "id" field passed');
  }
  if (!this.opts.editView) {
    return res.send('Not implemented. Define "editView" parameter.');
  }

  // attempt to fetch the record...
  this.Model.findById(req.body.id, function(err, doc){
    if (err) {
      this.addFlashError(req, err);
      // log.fine('_update() :: Fetch item fail:', err.type, err.message);
      return res.redirect(req.header('Referer'));
    }

    var processedVars = this.process(req.body);

    var query = new Object(null);
    query[this.opts.idField] = req.body.id;
    this.Model.update(query, { $set: req.body },
      this._updateCallback.bind(this, req, res, doc));
  }.bind(this));
};

/**
 * Handle an update callback, this is the Model Update callback.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @param {mongoose.Document} doc The mongoose document.
 * @param {Error=} err Operation failed.
 * @param {number=} optUpdateCount How many records were updated.
 * @protected
 */
CrudCtrl.prototype._updateCallback = function(req, res, doc, err, optUpdateCount){

  if (err) {
    this.addFlashError(req, err);
    // log.fine('_updateCallback() :: Edit item fail:', err.message);
    return res.redirect(req.header('Referer'));
  }

  if (1 !== optUpdateCount) {
    var error = new error.Database('No record was updated');
    error.count = optUpdateCount;
    this.addFlashError(req, error);
    // log.fine('_updateCallback() :: Fail, no record was updated. Update Count:', optUpdateCount);
    return res.redirect(req.header('Referer'));
  }

  var finalPath = req.url.split('/').pop();
  if (doc[this.opts.urlField] !== finalPath) {
    // log.fine('_updateCallback() :: Changed url. Old:', req.url, 'New:', doc);
    this.addFlashSuccess(req, doc);
    return res.redirect(this.baseUrl + '/' + doc._localUrl);
  }

  this.addSuccess(res, doc);
  res.locals.item = doc;

  // render the template and store in response locals.
  res.locals[CrudCtrl.VIEW_OUTPUT_KEY] = this.compiled.view(res.locals);
  if (!this.opts.layoutView) {
    res.send(res.locals[CrudCtrl.VIEW_OUTPUT_KEY]);
  } else {
    res.render(this.opts.layoutView);
  }
};

/**
 * Show single item update view
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._updateView = function(req, res) {
  if (!this.opts.editView) {
    return res.send('Not implemented. Define "editView" parameter.');
  }

  // attempt to fetch the record...
  var query = new Object(null);
  query[this.opts.urlField] = req.params.id;
  this.Model.findOne(query, function(err, doc){
    if (err) {
      this.addError(res, err);
      return res.render(this.opts.editView);
    }

    if (!doc) {
      var error = new Error('No results');
      this.addError(res, error);
      return res.render(this.opts.editView);
    }

    // assign the item to the tpl vars.
    res.locals.item = doc;

    this.checkFlashError(req, res);

    res.render(this.opts.editView);
  }.bind(this));
};

/**
 * Handle item deletion.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 * @protected
 */
CrudCtrl.prototype._delete = function(req, res){
  res.send('NOT IMPLEMENTED');
};

/**
 * Process incoming POST vars, we will:
 *  * Remove all vars starting with underscore ( _ ).
 *
 *
 * This is not a validation step, just forbit "private" keys from passing.
 *
 * @param  {Object} params Hash with key / value pairs
 * @return {Object} a processed object.
 */
CrudCtrl.prototype.process = function(params) {
  var outObj = {};
  __.forOwn(params, function(key, value){
    if ('_' !== key.indexOf(0)) {
      outObj[key] = value;
    }
  });

  return outObj;
};

<|endoftext|>