<commit_before>/**
 * Prepros
 * (c) Subash Pathak
 * sbshpthk@gmail.com
 * License: MIT
 */

/*jshint browser: true, node: true*/
/*global prepros,  _*/

//Storage
prepros.factory('projectsManager', function (config, storage, fileTypes, notification, utils, $rootScope, $location) {

    'use strict';

    var fs = require('fs'),
        path = require('path'),
        _id = utils.id;

    //Projects List
    var projects = storage.getProjects();

    //Files List
    var files = storage.getFiles();

    //Imports List
    var imports = storage.getImports();

    var _broadCast = function(){
        $rootScope.$broadcast('dataChange', {projects: projects, files: files, imports: imports});
    };

    //Remove any project that no longer exists
    _.each(projects, function(project){

        if(!fs.existsSync(project.path)){

            removeProject(project.id);
        }

    });

    //Remove any file that no longer exists
    _.each(files, function(file){

        if(!fs.existsSync(file.input)){

            removeFile(file.id);
        }

    });

    //Remove any import that no longer exists
    _.each(imports, function(imported){

        if(!fs.existsSync(imported.path)){

            removeImport(imported.path);
        }

    });

    //Function to add new project
    function addProject(folder) {

        //Check if folder already exists in project list
        var already = _.isEmpty(_.findWhere(projects, {path: folder})) ? false : true;

        //If project doesn't exist
        if (!already) {

            //Project to push
            var project = {
                id: _id(folder),
                name: path.basename(folder),
                path: folder,
                config: {
                    liveRefresh: true,
                    serverUrl: _id(folder),
                    filterPatterns: '',
                    useCustomServer: false,
                    customServerUrl: '',
                    cssPath : config.getUserOptions().cssPath,
                    jsPath : config.getUserOptions().jsPath,
                    htmlPath : config.getUserOptions().htmlPath,
                    jsMinPath: config.getUserOptions().jsMinPath
                }
            };

            var serverUrl = project.name.replace(/\s/gi, '-').replace(/[^a-zA-Z0-9\-_]/g, '');

            var isNotUsed = _.isEmpty(_.where(projects, function(p){
                p.config.serverUrl = serverUrl;
            }));

            if(isNotUsed && serverUrl !== '') {

                project.config.serverUrl = serverUrl;
            }

            //Push project to projects list
            projects.push(project);

            refreshProjectFiles(project.id);

            //Redirect to newly added project
            $location.path('/files/' + _id(folder));

            _broadCast();
        }
    }

    //Function to remove project
    function removeProject(pid) {

        if (!_.isEmpty(_.findWhere(projects, {id: pid}))) {

            //Reject projects from list
            projects = _.reject(projects, function (project) {
                return project.id === pid;
            });

            removeProjectFiles(pid);
        }

        _broadCast();
    }

    //function to get all project files
    function getProjectFiles(pid){
        return _.where(files, {pid : pid});
    }

    //Function to get current Project config
    function getProjectConfig(pid) {
        return getProjectById(pid).config;
    }

    //Function to remove project files
    function removeProjectFiles(pid) {

        if (!_.isEmpty(_.where(files, {pid: pid}))) {

            //Reject the file from list
            files = _.reject(files, function (file) {
                return file.pid === pid;
            });

            //Reject the imports from list
            imports = _.reject(imports, function (imp) {
                return imp.pid === pid;
            });
        }

        _broadCast();
    }

    //Function to get project by it's id
    function getProjectById(id) {
        return _.findWhere(projects, {id: id});
    }

    //Function to get file by its id
    function getFileById(id) {
        return _.findWhere(files, {id: id});
    }

    //Function to get file by its id
    function getImportById(id) {
        return _.findWhere(imports, {id: id});
    }

    //Function to remove a file
    function removeFile(id) {

        if (!_.isEmpty(_.findWhere(files, {id: id}))) {

            //Reject the file from list
            files = _.reject(files, function (file) {
                return file.id === id;
            });

            //Remove file from imports parent list
            removeParentFromAllImports(id);

            _broadCast();
        }

    }

    //Function to remove a import file
    function removeImport(id) {

        if (!_.isEmpty(_.findWhere(imports, {id: id}))) {

            //Reject import from imports list
            imports = _.reject(imports, function (imp) {
                return imp.id === id;
            });
        }

        _broadCast();
    }

    //Function to get file imports in imports list
    function getFileImports(fid){

        return _.filter(imports, function(im){
            return _.contains(im.parents, fid);
        });
    }

    //Function to remove file from import parent
    function removeParentFromAllImports(fid){

        _.each(imports, function (imp) {

            removeImportParent(imp.id, fid);

        });

    }

    //Remove parent from certain import
    function removeImportParent(impid, fid){

        var imported = _.findWhere(imports, {id: impid});

        var newImports = _.reject(imports, function (imp) {

            return imported.path === imp.path;

        });

        imported.parents = _.without(imported.parents, fid);

        //If after removing one file as parent the parents list becomes empty remove whole import item
        if (!_.isEmpty(imported.parents)) {

            newImports.push(imported);

        }

        imports = newImports;

        _broadCast();
    }

    //Function to get all files inside project folder
    function getProjectFolderFiles(pid) {

        var folder = getProjectById(pid).path;

        var f = [];

        function get(dir) {

            var files = fs.readdirSync(dir);

            files.forEach(function(file){

                var fp = dir + path.sep + file;

                if(fs.statSync(fp).isDirectory()) {

                    get(fp);

                } else {

                    if(fileTypes.isFileSupported(fp)){
                        f.push(fp);
                    }
                }
            });
        }

        try {

            get(folder);

        } catch(e) {

            notification.error('Error ! ', 'An error occurred while scanning files', e.message);

        }

        return f;
    }

    //Function to match files against global and project specific filters
    function matchFileFilters(pid, file) {

        var projectFilterPatterns = '';

        if(getProjectById(pid).config.filterPatterns) {

            projectFilterPatterns = getProjectById(pid).config.filterPatterns;
        }

        var globalFilterPatterns = config.getUserOptions().filterPatterns.split(',');

        projectFilterPatterns = projectFilterPatterns.split(',');

        var filterPatterns = _.unique(_.union(globalFilterPatterns, projectFilterPatterns));

        var matchFilter = false;

        _.each(filterPatterns, function(pattern){

            pattern = pattern.trim();

            if(pattern !=="" && file.indexOf(pattern) !== -1) {

                matchFilter = true;

            }

        });

        return matchFilter;

    }

    //Function to add file
    function addFile(filePath, projectPath) {

        //Check if file already exists in files list
        var already = _.isEmpty(_.findWhere(files, {input: filePath})) ? false : true;

        var inImports = _.isEmpty(_.findWhere(imports, {path: filePath})) ? false : true;

        var isFileSupported = fileTypes.isFileSupported(filePath);

        if (isFileSupported && !already && !inImports) {

            files.push(fileTypes.format(filePath, projectPath));
        }

    }

    //Function that refreshes files in a project folder
    function refreshProjectFiles(pid) {

        utils.showLoading();

        var folder = getProjectById(pid).path;

        //Remove file that doesn't exist or matches the filter pattern
        _.each(getProjectFiles(pid), function(file){

            //Remove if matches filter patterns or doesn't exist
            if (matchFileFilters(pid, file.input) || !fs.existsSync(file.input)){

                removeFile(file.id);

            }

        });

        if(fs.existsSync(folder)) {

            var projectFiles = getProjectFolderFiles(pid);

            var filesToAdd = [];

            _.each(projectFiles, function(file) {

                if (!matchFileFilters(pid, file)) {

                    filesToAdd.push({
                        path: file,
                        imports: fileTypes.getImports(file)
                    });
                }
            });

            //Check if file is in the imports list of another file
            //If it is ignore the file
            var importsOfAllFiles = _.uniq(_.flatten(_.pluck(filesToAdd, 'imports')));

            _.each(filesToAdd, function(file) {

                //Check
                if(!_.contains(importsOfAllFiles, file.path)){

                    //Add file
                    addFile(file.path, folder);

                    //Add imports
                    _.each(file.imports, function(imp) {
                        addFileImport(folder, file.path, imp);
                    });
                }

                //Remove any previously imported file that is not imported anymore
                var oldImports = getFileImports(_id(file.path));

                _.each(oldImports, function(imp){

                    if(!_.contains(file.imports, imp.path)){

                        removeImportParent(imp.id, _id(file.path));
                    }
                });
            });

            _broadCast();

            utils.hideLoading();

        } else {

            removeProject(pid);

            utils.hideLoading();
        }
    }

    //function to add imported file to import list
    function addFileImport(projectPath, parentPath, importedPath){

        //If @imported file is not in imports list create new entry otherwise add the file as parent
        if (_.isEmpty(_.findWhere(imports, {path: importedPath}))) {

            imports.push({
                id: _id(importedPath),
                pid: _id(projectPath),
                path: importedPath,
                parents: [_id(parentPath)]
            });

        } else {

            var im = _.findWhere(imports, {path: importedPath});

            if (!_.contains(im.parents, _id(parentPath))) {
                im.parents.push(_id(parentPath));
            }

            //Remove old import file without new parent
            var newImports = _.reject(imports, function (imp) {
                return imp.path === importedPath;
            });

            //Push new import file with new parent
            newImports.push(im);

            //finally add to global imports list
            imports = newImports;

        }

        //Remove any file that is in files list and is imported by this file
        removeFile(_id(importedPath));

    }

    //Function to change file output path
    function changeFileOutput(id, newPath){

        var file = getFileById(id),
            project = getProjectById(file.pid);

        if(path.extname(path.basename(newPath)) === ''){

            newPath = newPath + fileTypes.getCompiledExtension(file.input);
        }

        file.output = newPath;

        file.shortOutput = newPath.replace(/\\/g, '/');

        //Show Relative path if output file is within project folder
        if (path.relative(project.path, newPath).indexOf('.' + path.sep) === -1) {

            file.shortOutput = path.relative(project.path, newPath).replace(/\\/g, '/');
        }
    }

    //Return
    return {
        projects: projects,
        files: files,
        imports: imports,

        getProjectById: getProjectById,
        getFileById: getFileById,
        getImportById: getImportById,

        addProject: addProject,

        removeFile: removeFile,
        removeProject: removeProject,
        removeImport: removeImport,

        refreshProjectFiles: refreshProjectFiles,
        getProjectFiles: getProjectFiles,
        getProjectConfig: getProjectConfig,
        changeFileOutput: changeFileOutput
    };
});
<commit_msg>cleanup<commit_after>/**
 * Prepros
 * (c) Subash Pathak
 * sbshpthk@gmail.com
 * License: MIT
 */

/*jshint browser: true, node: true*/
/*global prepros,  _*/

//Storage
prepros.factory('projectsManager', function (config, storage, fileTypes, notification, utils, $rootScope, $location) {

    'use strict';

    var fs = require('fs'),
        path = require('path'),
        _id = utils.id;

    //Projects List
    var projects = storage.getProjects();

    //Files List
    var files = storage.getFiles();

    //Imports List
    var imports = storage.getImports();

    var _broadCast = function(){
        $rootScope.$broadcast('dataChange', {projects: projects, files: files, imports: imports});
    };

    //Remove any project that no longer exists
    _.each(projects, function(project){

        if(!fs.existsSync(project.path)){

            removeProject(project.id);
        }

    });

    //Remove any file that no longer exists
    _.each(files, function(file){

        if(!fs.existsSync(file.input)){

            removeFile(file.id);
        }

    });

    //Remove any import that no longer exists
    _.each(imports, function(imported){

        if(!fs.existsSync(imported.path)){

            removeImport(imported.path);
        }

    });

    //Function to add new project
    function addProject(folder) {

        //Check if folder already exists in project list
        var already = _.isEmpty(_.findWhere(projects, {path: folder})) ? false : true;

        //If project doesn't exist
        if (!already) {

            //Project to push
            var project = {
                id: _id(folder),
                name: path.basename(folder),
                path: folder,
                config: {
                    liveRefresh: true,
                    serverUrl: _id(folder),
                    filterPatterns: '',
                    useCustomServer: false,
                    customServerUrl: '',
                    cssPath : config.getUserOptions().cssPath,
                    jsPath : config.getUserOptions().jsPath,
                    htmlPath : config.getUserOptions().htmlPath,
                    jsMinPath: config.getUserOptions().jsMinPath
                }
            };

            var serverUrl = project.name.replace(/\s/gi, '-').replace(/[^a-zA-Z0-9\-_]/g, '');

            var isNotUsed = _.isEmpty(_.where(projects, function(p){
                p.config.serverUrl = serverUrl;
            }));

            if(isNotUsed && serverUrl !== '') {

                project.config.serverUrl = serverUrl;
            }

            //Push project to projects list
            projects.push(project);

            refreshProjectFiles(project.id);

            //Redirect to newly added project
            $location.path('/files/' + _id(folder));

            _broadCast();
        }
    }

    //Function to remove project
    function removeProject(pid) {

        if (!_.isEmpty(_.findWhere(projects, {id: pid}))) {

            //Reject projects from list
            projects = _.reject(projects, function (project) {
                return project.id === pid;
            });

            removeProjectFiles(pid);
        }

        _broadCast();
    }

    //function to get all project files
    function getProjectFiles(pid){
        return _.where(files, {pid : pid});
    }

    //Function to get current Project config
    function getProjectConfig(pid) {
        return getProjectById(pid).config;
    }

    //Function to remove project files
    function removeProjectFiles(pid) {

        if (!_.isEmpty(_.where(files, {pid: pid}))) {

            //Reject the file from list
            files = _.reject(files, function (file) {
                return file.pid === pid;
            });

            //Reject the imports from list
            imports = _.reject(imports, function (imp) {
                return imp.pid === pid;
            });
        }

        _broadCast();
    }

    //Function to get project by it's id
    function getProjectById(id) {
        return _.findWhere(projects, {id: id});
    }

    //Function to get file by its id
    function getFileById(id) {
        return _.findWhere(files, {id: id});
    }

    //Function to get file by its id
    function getImportById(id) {
        return _.findWhere(imports, {id: id});
    }

    //Function to remove a file
    function removeFile(id) {

        if (!_.isEmpty(_.findWhere(files, {id: id}))) {

            //Reject the file from list
            files = _.reject(files, function (file) {
                return file.id === id;
            });

            //Remove file from imports parent list
            removeParentFromAllImports(id);

            _broadCast();
        }

    }

    //Function to remove a import file
    function removeImport(id) {

        if (!_.isEmpty(_.findWhere(imports, {id: id}))) {

            //Reject import from imports list
            imports = _.reject(imports, function (imp) {
                return imp.id === id;
            });
        }

        _broadCast();
    }

    //Function to get file imports in imports list
    function getFileImports(fid){

        return _.filter(imports, function(im){
            return _.contains(im.parents, fid);
        });
    }

    //Function to remove file from import parent
    function removeParentFromAllImports(fid){

        _.each(imports, function (imp) {

            removeImportParent(imp.id, fid);

        });

    }

    //Remove parent from certain import
    function removeImportParent(impid, fid){

        var imported = _.findWhere(imports, {id: impid});

        var newImports = _.reject(imports, function (imp) {

            return imported.path === imp.path;

        });

        imported.parents = _.without(imported.parents, fid);

        //If after removing one file as parent the parents list becomes empty remove whole import item
        if (!_.isEmpty(imported.parents)) {

            newImports.push(imported);

        }

        imports = newImports;

        _broadCast();
    }

    //Function to get all files inside project folder
    function getProjectFolderFiles(pid) {

        var folder = getProjectById(pid).path;

        var f = [];

        function get(dir) {

            var files = fs.readdirSync(dir);

            files.forEach(function(file){

                var fp = dir + path.sep + file;

                if(fs.statSync(fp).isDirectory()) {

                    get(fp);

                } else {

                    if(fileTypes.isFileSupported(fp)){
                        f.push(fp);
                    }
                }
            });
        }

        try {

            get(folder);

        } catch(e) {

            notification.error('Error ! ', 'An error occurred while scanning files', e.message);

        }

        return f;
    }

    //Function to match files against global and project specific filters
    function matchFileFilters(pid, file) {

        var projectFilterPatterns = '';

        if(getProjectById(pid).config.filterPatterns) {

            projectFilterPatterns = getProjectById(pid).config.filterPatterns;
        }

        var globalFilterPatterns = config.getUserOptions().filterPatterns.split(',');

        projectFilterPatterns = projectFilterPatterns.split(',');

        var filterPatterns = _.unique(_.union(globalFilterPatterns, projectFilterPatterns));

        var matchFilter = false;

        _.each(filterPatterns, function(pattern){

            pattern = pattern.trim();

            if(pattern !=="" && file.indexOf(pattern) !== -1) {

                matchFilter = true;

            }

        });

        return matchFilter;

    }

    //Function to add file
    function addFile(filePath, projectPath) {

        //Check if file already exists in files list
        var already = _.isEmpty(_.findWhere(files, {input: filePath})) ? false : true;

        var inImports = _.isEmpty(_.findWhere(imports, {path: filePath})) ? false : true;

        var isFileSupported = fileTypes.isFileSupported(filePath);

        if (isFileSupported && !already && !inImports) {

            files.push(fileTypes.format(filePath, projectPath));
        }

    }

    //Function that refreshes files in a project folder
    function refreshProjectFiles(pid) {

        utils.showLoading();

        var folder = getProjectById(pid).path;

        //Remove file that doesn't exist or matches the filter pattern
        _.each(getProjectFiles(pid), function(file){

            //Remove if matches filter patterns or doesn't exist
            if (matchFileFilters(pid, file.input) || !fs.existsSync(file.input)){

                removeFile(file.id);

            }

        });

        if(fs.existsSync(folder)) {

            var projectFiles = getProjectFolderFiles(pid);

            var filesToAdd = [];

            _.each(projectFiles, function(file) {

                if (!matchFileFilters(pid, file)) {

                    filesToAdd.push({
                        path: file,
                        imports: fileTypes.getImports(file)
                    });
                }
            });

            //Check if file is in the imports list of another file
            //If it is ignore the file
            var importsOfAllFiles = _.uniq(_.flatten(_.pluck(filesToAdd, 'imports')));

            _.each(filesToAdd, function(file) {

                //Check
                if(!_.contains(importsOfAllFiles, file.path)){

                    //Add file
                    addFile(file.path, folder);

                    //Add imports
                    _.each(file.imports, function(imp) {
                        addFileImport(folder, file.path, imp);
                    });
                }

                //Remove any previously imported file that is not imported anymore
                var oldImports = getFileImports(_id(file.path));

                _.each(oldImports, function(imp){

                    if(!_.contains(file.imports, imp.path)){

                        removeImportParent(imp.id, _id(file.path));
                    }
                });
            });

            _broadCast();

            utils.hideLoading();

        } else {

            removeProject(pid);

            utils.hideLoading();
        }
    }

    //function to add imported file to import list
    function addFileImport(projectPath, parentPath, importedPath){

        //If @imported file is not in imports list create new entry otherwise add the file as parent
        if (_.isEmpty(_.findWhere(imports, {path: importedPath}))) {

            imports.push({
                id: _id(importedPath),
                pid: _id(projectPath),
                path: importedPath,
                parents: [_id(parentPath)]
            });

        } else {

            var im = _.findWhere(imports, {path: importedPath});

            if (!_.contains(im.parents, _id(parentPath))) {
                im.parents.push(_id(parentPath));
            }

            //Remove old import file without new parent
            var newImports = _.reject(imports, function (imp) {
                return imp.path === importedPath;
            });

            //Push new import file with new parent
            newImports.push(im);

            //finally add to global imports list
            imports = newImports;

        }

        //Remove any file that is in files list and is imported by this file
        removeFile(_id(importedPath));

    }

    //Function to change file output path
    function changeFileOutput(id, newPath){

        var file = getFileById(id),
            project = getProjectById(file.pid);

        if(path.extname(path.basename(newPath)) === ''){

            newPath = newPath + fileTypes.getCompiledExtension(file.input);
        }

        file.output = file.shortOutput = newPath;

        //Show Relative path if output file is within project folder
        if (path.relative(project.path, newPath).indexOf('.' + path.sep) === -1) {

            file.shortOutput = path.relative(project.path, newPath);
        }
    }

    //Return
    return {
        projects: projects,
        files: files,
        imports: imports,

        getProjectById: getProjectById,
        getFileById: getFileById,
        getImportById: getImportById,

        addProject: addProject,

        removeFile: removeFile,
        removeProject: removeProject,
        removeImport: removeImport,

        refreshProjectFiles: refreshProjectFiles,
        getProjectFiles: getProjectFiles,
        getProjectConfig: getProjectConfig,
        changeFileOutput: changeFileOutput
    };
});
<|endoftext|>


<commit_before>import { createCellDragBehaviour } from './cell-drag-behaviour'
import { dispatch  as createDispatch }  from 'd3-dispatch'
import { modulo } from '@zambezi/fun'
import { rebind, keyCodeHandler } from '@zambezi/d3-utils'
import { reduce, indexBy, findIndex, range, debounce, map, uniqueId } from 'underscore'
import { select, event } from 'd3-selection'
import { someResult as some } from '@zambezi/fun'
import { unwrap } from '@zambezi/grid'

import './cell-selection.css'

export function createCellSelection() {

  const dispatch = createDispatch(
          'cell-selected-change'
        , 'cell-active-action'
        , 'cell-active-change'
        , 'cell-active-copy'
        , 'cell-active-paste'
        )
      , pasteId = uniqueId('paste.')
      , copyId = uniqueId('copy.')
      , cellDragBehaviour = createCellDragBehaviour()

  let gesture = 'click'
    , selected = []
    , selectedCandidates
    , selectedRowsByColumnId = {}
    , acceptPasteFrom = []
    , active
    , selectable = true
    , trackPaste = true
    , typeToActivate = true
    , lastOverCell

  function cellSelection(s) {
    s.each(cellSelectionEach)
  }

  cellSelection.selected = function(value) {
    if (!arguments.length) return selected
    selected = value
    selectedCandidates = value
    return cellSelection
  }

  cellSelection.trackPaste = function(value) {
    if (!arguments.length) return trackPaste
    trackPaste = value
    return cellSelection
  }

  cellSelection.acceptPasteFrom = function(value) {
    if (!arguments.length) return acceptPasteFrom
    acceptPasteFrom = value
    return cellSelection
  }

  cellSelection.typeToActivate = function(value) {
    if (!arguments.length) return typeToActivate
    typeToActivate = value
    return cellSelection
  }

  cellSelection.selectable = function(value) {
    if (!arguments.length) return selectable
    selectable = value
    return cellSelection
  }

  cellSelection.active = function(value) {
    if (!arguments.length) return active
    active = value
    return cellSelection
  }

  cellSelection.rowChangedKey = function(targetRow) {
    const unwrappedRow = unwrap(targetRow)
        , selectedColumnIds = []
    let key = ''

    selected.forEach(addSelected)

    function addSelected({row, column}) {
      if (unwrappedRow !== row) return
      selectedColumnIds.push(column.id)
    }

    key = selectedColumnIds.join('↑')

    if (active && unwrappedRow === active.row) {
      key += ('☆' + active.column.id)
    }
    return key
  }

  return rebind().from(dispatch, 'on')(cellSelection)

  function cellSelectionEach(bundle, i) {
    const target = select(this)
        , columns = bundle.columns
        , rows = bundle.rows
        , columnById = indexBy(columns, 'id')

    setupDragEvents()
    setupPasteEvents()
    setupCopyEvents()
    setupKeyboardNavEvents()

    if (selectedCandidates) updateFromCandidates()

    bundle.dispatcher
        .on('cell-update.cell-selection', onCellUpdate)

    function moveHorizontal(step) {
      if (!active) return

      const { column, row } = active
          , currentColumnIndex = columns.indexOf(column)
          , newColumn = columns[modulo(currentColumnIndex + step, columns.length)]

      setActive({ row, column: newColumn })
      target.dispatch('redraw', { bubbles: true })
    }

    function moveVertical(step, rows) {
      if (!active) return

      const { column, row } = active
          , currentRowIndex = findIndex(rows, r => unwrap(r) === row)
          , newRow = rows[modulo(currentRowIndex + step, rows.length)]

      setActive({ row: newRow, column })
      target.dispatch('redraw', { bubbles: true })
    }

    function setActiveIfNone(d) {
      if (active) return
      if (!bundle.length) return
      if (!columns.length) return
      setActive({ row: bundle[0], column: columns[0] })
      select(this).dispatch('redraw', { bubbles: true })
    }

    function updateFromCandidates() {
      selectedRowsByColumnId = selectedCandidates.reduce(toRealSelection, {})
      selected = compileSelected()
      selectedCandidates = null
    }

    function toRealSelection(acc, { row, column }) {
      const columnFound = typeof column == 'string' ? columnById[column] : column
          , rowFound = typeof row == 'number' ? bundle[row] : row

      if (!columnFound || !rowFound) {
        console.warn("Couldn't find cell for", { row, column })
        return acc
      }

      const columnId = columnFound.id
          , set = acc[columnId] || new Set()

      set.add(rowFound)
      acc[columnId] = set
      return acc
    }

    function compileSelected() {
      return reduce(selectedRowsByColumnId, toCells, [])
    }

    function setupKeyboardNavEvents() {
      target.attr('tabindex', '0')
          .on('focus', debounce(setActiveIfNone, 200))
          .on(
            'keydown.keyboard-cell-selection'
          , some(
              keyCodeHandler(() => dispatch.call('cell-active-action', this, active), 13) // enter
            , keyCodeHandler(d => moveVertical(-1, d.rows), 38) // up
            , keyCodeHandler(d => moveVertical(1, d.rows), 40)  // down
            , keyCodeHandler(() => moveHorizontal(-1), 37)      // left
            , keyCodeHandler(() => moveHorizontal(1), 39)       // right
            , keyCodeHandler(onTab, 9)
            , activateFromInput
            )
          )
    }

    function setupPasteEvents() {
      select(document)
          .on(pasteId, trackPaste ? onPaste : null)
          .on(`keypress.${pasteId}`, trackPaste ? onPaste : null)
    }

    function setupDragEvents() {
      target.call(
        cellDragBehaviour
            .on('dragstart.select', d => mouseSelection(d))
            .on('dragstart.active', setActive)
            .on('dragstart.redraw', () => target.dispatch('redraw', { bubbles: true }))

            .on('dragend.log', d => console.log('end the drag', d))
            .on('dragend.set-active', activateLastCell)
            .on('dragend.redraw', () => target.dispatch('redraw', { bubbles: true }))

            .on('dragover.select', d => mouseSelection(d, true))
            .on('dragover.cache', d => lastOverCell = d)
            .on('dragover.redraw', () => target.dispatch('redraw', { bubbles: true }))
      )
    }

    function activateLastCell() {
      if (!lastOverCell) return
      setActive(lastOverCell)
      lastOverCell = null
    }

    function mouseBlockSelect(cell) {
      setSelectionToRange(active, cell)
      target.dispatch('redraw', { bubbles: true })
    }

    function setupCopyEvents() {
      select(document)
          .on(`keydown.${copyId}`, trackPaste ? onCopy : null)
    }

    function onCopy() {
      const targetNode = target.node()
          , { ctrlKey, key } = event

      if (key !== 'c') return
      if (!ctrlKey) return
      if (!targetNode.contains(document.activeElement)) return

      event.preventDefault()
      event.stopPropagation()

      dispatch.call('cell-active-copy', targetNode, active, selected)
    }

    function onPaste() {
      const targetNode = target.node()
          , allAcceptedNodes = (acceptPasteFrom || []).concat(targetNode)

      if (!allAcceptedNodes.some(n => n.contains(document.activeElement))) return

      let clipboardData = event.clipboardData
      let text = ''

      if (!clipboardData) {
        // oh oh, smells like IE
        if (!event.ctrlKey) return
        if (event.key !== 'v') return
        clipboardData = window.clipboardData
      }

      try {
        text = clipboardData.getData('Text')
      } catch(e) {
        console.warn('Unable to extract clipboard data', e)
      }

      dispatch.call('cell-active-paste', targetNode, active, text)
    }

    function activateFromInput(d) {
      const { key, ctrlKey, altKey, metaKey } = event

      if (!typeToActivate) return
      if (ctrlKey || altKey || metaKey) return
      if (!active) return
      if (isASpecialKey(key)) return

      dispatch.call('cell-active-action', target.node(), active, key)
    }

    function onTab(d) {
      moveHorizontal(event.shiftKey ? -1 : 1)
      event.preventDefault()
    }

    function mouseSelection(d, forceAppend) {
      const column = d.column
          , columnId = column.id
          , set = selectedRowsByColumnId[columnId]
          , row = unwrap(d.row)
          , { shiftKey, ctrlKey } = event.sourceEvent ? event.sourceEvent : event
          , cell = { row, column }
          , isAlreadySelected = set && set.has(row)
          , hasActive = !!active

      if (selectable) {
        switch(true) {
          case ctrlKey && shiftKey && hasActive:
            addRangeToSelection(active, cell)
            break

          case forceAppend || shiftKey && hasActive:
            setSelectionToRange(active, cell)
            break

          case ctrlKey && isAlreadySelected:
            removeFromSelected(cell)
            break

          case ctrlKey:
            addToSelected(cell)
            break

          default:
            selectOnly(cell)
        }

        selected = compileSelected()
        dispatch.call('cell-selected-change', this, selected, active)
      }
    }

    function setActive(cell) {

      if (!cell) {
        active = null
        return
      }

      const { row, column } = cell
      active = { row: unwrap(row), column }
      dispatch.call('cell-active-change', this, active)
    }

    function setSelectionToRange(a, b) {
      selectedRowsByColumnId = {}
      rangeFrom(a, b).forEach(addToSelected)
    }

    function addRangeToSelection(a, b) {
      rangeFrom(a, b).forEach(addToSelected)
    }

    function rangeFrom(a, b) {
      const columnRange = rangeFromUnorderedIndices(
              columns
            , columns.indexOf(a.column)
            , columns.indexOf(b.column)
            )
          , rowRange = rangeFromUnorderedIndices(
              bundle.rows
            , findIndex(bundle.rows, r => unwrap(r) == a.row)
            , findIndex(bundle.rows, r => unwrap(r) == b.row)
            ).map(unwrap)

      return rowRange.reduce(allCellsInColumns, [])

      function allCellsInColumns(acc, row) {
        return acc.concat(columnRange.map(column => ({ column, row })))
      }
    }

    function selectOnly(target) {
      selectedRowsByColumnId = {}
      addToSelected(target)
    }

    function rangeFromUnorderedIndices(array, i1, i2) {
      return array.slice(
        Math.min(i1, i2)
      , Math.max(i1, i2) + 1
      )
    }

    function removeFromSelected({ row, column }) {
      const columnId = column.id
          , set = selectedRowsByColumnId[columnId]

      if (set) set.delete(row)
    }

    function addToSelected({ row, column }) {
      const columnId = column.id
          , set = selectedRowsByColumnId[columnId] || new Set()

      set.add(row)
      selectedRowsByColumnId[columnId] = set
    }

    function toCells(acc, set, columnId) {
      const column = columnById[columnId]
      return acc.concat(Array.from(set.values()).map(row => ({ row, column })))
    }
  }

  function onCellUpdate(d, i){
    select(this)
        .classed('is-selected', isCellSelected)
        .classed('is-active', isCellActive)
  }

  function isCellSelected(d, i) {
    const rowSetForColumn = selectedRowsByColumnId[d.column.id]
    return rowSetForColumn && rowSetForColumn.has(unwrap(d.row))
  }

  function isCellActive(d) {
    if (!active) return false
    return areSameCell(d, active)
  }
}

function isASpecialKey(key) {
  return key.length > 1
}

function areSameCell(a, b) {
  return a && b && unwrap(a.row) == unwrap(b.row) && a.column == b.column
}
<commit_msg>Cleanup.<commit_after>import { createCellDragBehaviour } from './cell-drag-behaviour'
import { dispatch  as createDispatch }  from 'd3-dispatch'
import { modulo } from '@zambezi/fun'
import { rebind, keyCodeHandler } from '@zambezi/d3-utils'
import { reduce, indexBy, findIndex, range, debounce, map, uniqueId } from 'underscore'
import { select, event } from 'd3-selection'
import { someResult as some } from '@zambezi/fun'
import { unwrap } from '@zambezi/grid'

import './cell-selection.css'

export function createCellSelection() {

  const dispatch = createDispatch(
          'cell-selected-change'
        , 'cell-active-action'
        , 'cell-active-change'
        , 'cell-active-copy'
        , 'cell-active-paste'
        )
      , pasteId = uniqueId('paste.')
      , copyId = uniqueId('copy.')
      , cellDragBehaviour = createCellDragBehaviour()

  let gesture = 'click'
    , selected = []
    , selectedCandidates
    , selectedRowsByColumnId = {}
    , acceptPasteFrom = []
    , active
    , selectable = true
    , trackPaste = true
    , typeToActivate = true
    , lastOverCell

  function cellSelection(s) {
    s.each(cellSelectionEach)
  }

  cellSelection.selected = function(value) {
    if (!arguments.length) return selected
    selected = value
    selectedCandidates = value
    return cellSelection
  }

  cellSelection.trackPaste = function(value) {
    if (!arguments.length) return trackPaste
    trackPaste = value
    return cellSelection
  }

  cellSelection.acceptPasteFrom = function(value) {
    if (!arguments.length) return acceptPasteFrom
    acceptPasteFrom = value
    return cellSelection
  }

  cellSelection.typeToActivate = function(value) {
    if (!arguments.length) return typeToActivate
    typeToActivate = value
    return cellSelection
  }

  cellSelection.selectable = function(value) {
    if (!arguments.length) return selectable
    selectable = value
    return cellSelection
  }

  cellSelection.active = function(value) {
    if (!arguments.length) return active
    active = value
    return cellSelection
  }

  cellSelection.rowChangedKey = function(targetRow) {
    const unwrappedRow = unwrap(targetRow)
        , selectedColumnIds = []
    let key = ''

    selected.forEach(addSelected)

    function addSelected({row, column}) {
      if (unwrappedRow !== row) return
      selectedColumnIds.push(column.id)
    }

    key = selectedColumnIds.join('↑')

    if (active && unwrappedRow === active.row) {
      key += ('☆' + active.column.id)
    }
    return key
  }

  return rebind().from(dispatch, 'on')(cellSelection)

  function cellSelectionEach(bundle, i) {
    const target = select(this)
        , columns = bundle.columns
        , rows = bundle.rows
        , columnById = indexBy(columns, 'id')

    setupDragEvents()
    setupPasteEvents()
    setupCopyEvents()
    setupKeyboardNavEvents()

    if (selectedCandidates) updateFromCandidates()

    bundle.dispatcher
        .on('cell-update.cell-selection', onCellUpdate)

    function moveHorizontal(step) {
      if (!active) return

      const { column, row } = active
          , currentColumnIndex = columns.indexOf(column)
          , newColumn = columns[modulo(currentColumnIndex + step, columns.length)]

      setActive({ row, column: newColumn })
      target.dispatch('redraw', { bubbles: true })
    }

    function moveVertical(step, rows) {
      if (!active) return

      const { column, row } = active
          , currentRowIndex = findIndex(rows, r => unwrap(r) === row)
          , newRow = rows[modulo(currentRowIndex + step, rows.length)]

      setActive({ row: newRow, column })
      target.dispatch('redraw', { bubbles: true })
    }

    function setActiveIfNone(d) {
      if (active) return
      if (!bundle.length) return
      if (!columns.length) return
      setActive({ row: bundle[0], column: columns[0] })
      select(this).dispatch('redraw', { bubbles: true })
    }

    function updateFromCandidates() {
      selectedRowsByColumnId = selectedCandidates.reduce(toRealSelection, {})
      selected = compileSelected()
      selectedCandidates = null
    }

    function toRealSelection(acc, { row, column }) {
      const columnFound = typeof column == 'string' ? columnById[column] : column
          , rowFound = typeof row == 'number' ? bundle[row] : row

      if (!columnFound || !rowFound) {
        console.warn("Couldn't find cell for", { row, column })
        return acc
      }

      const columnId = columnFound.id
          , set = acc[columnId] || new Set()

      set.add(rowFound)
      acc[columnId] = set
      return acc
    }

    function compileSelected() {
      return reduce(selectedRowsByColumnId, toCells, [])
    }

    function setupKeyboardNavEvents() {
      target.attr('tabindex', '0')
          .on('focus', debounce(setActiveIfNone, 200))
          .on(
            'keydown.keyboard-cell-selection'
          , some(
              keyCodeHandler(() => dispatch.call('cell-active-action', this, active), 13) // enter
            , keyCodeHandler(d => moveVertical(-1, d.rows), 38) // up
            , keyCodeHandler(d => moveVertical(1, d.rows), 40)  // down
            , keyCodeHandler(() => moveHorizontal(-1), 37)      // left
            , keyCodeHandler(() => moveHorizontal(1), 39)       // right
            , keyCodeHandler(onTab, 9)
            , activateFromInput
            )
          )
    }

    function setupPasteEvents() {
      select(document)
          .on(pasteId, trackPaste ? onPaste : null)
          .on(`keypress.${pasteId}`, trackPaste ? onPaste : null)
    }

    function setupDragEvents() {
      target.call(
        cellDragBehaviour
            .on('dragstart.select', d => mouseSelection(d))
            .on('dragstart.active', setActive)
            .on('dragstart.redraw', () => target.dispatch('redraw', { bubbles: true }))

            .on('dragend.set-active', activateLastCell)
            .on('dragend.redraw', () => target.dispatch('redraw', { bubbles: true }))

            .on('dragover.select', d => mouseSelection(d, true))
            .on('dragover.cache', d => lastOverCell = d)
            .on('dragover.redraw', () => target.dispatch('redraw', { bubbles: true }))
      )
    }

    function activateLastCell() {
      if (!lastOverCell) return
      setActive(lastOverCell)
      lastOverCell = null
    }

    function mouseBlockSelect(cell) {
      setSelectionToRange(active, cell)
      target.dispatch('redraw', { bubbles: true })
    }

    function setupCopyEvents() {
      select(document)
          .on(`keydown.${copyId}`, trackPaste ? onCopy : null)
    }

    function onCopy() {
      const targetNode = target.node()
          , { ctrlKey, key } = event

      if (key !== 'c') return
      if (!ctrlKey) return
      if (!targetNode.contains(document.activeElement)) return

      event.preventDefault()
      event.stopPropagation()

      dispatch.call('cell-active-copy', targetNode, active, selected)
    }

    function onPaste() {
      const targetNode = target.node()
          , allAcceptedNodes = (acceptPasteFrom || []).concat(targetNode)

      if (!allAcceptedNodes.some(n => n.contains(document.activeElement))) return

      let clipboardData = event.clipboardData
      let text = ''

      if (!clipboardData) {
        // oh oh, smells like IE
        if (!event.ctrlKey) return
        if (event.key !== 'v') return
        clipboardData = window.clipboardData
      }

      try {
        text = clipboardData.getData('Text')
      } catch(e) {
        console.warn('Unable to extract clipboard data', e)
      }

      dispatch.call('cell-active-paste', targetNode, active, text)
    }

    function activateFromInput(d) {
      const { key, ctrlKey, altKey, metaKey } = event

      if (!typeToActivate) return
      if (ctrlKey || altKey || metaKey) return
      if (!active) return
      if (isASpecialKey(key)) return

      dispatch.call('cell-active-action', target.node(), active, key)
    }

    function onTab(d) {
      moveHorizontal(event.shiftKey ? -1 : 1)
      event.preventDefault()
    }

    function mouseSelection(d, forceAppend) {
      const column = d.column
          , columnId = column.id
          , set = selectedRowsByColumnId[columnId]
          , row = unwrap(d.row)
          , { shiftKey, ctrlKey } = event.sourceEvent ? event.sourceEvent : event
          , cell = { row, column }
          , isAlreadySelected = set && set.has(row)
          , hasActive = !!active

      if (selectable) {
        switch(true) {
          case ctrlKey && shiftKey && hasActive:
            addRangeToSelection(active, cell)
            break

          case forceAppend || shiftKey && hasActive:
            setSelectionToRange(active, cell)
            break

          case ctrlKey && isAlreadySelected:
            removeFromSelected(cell)
            break

          case ctrlKey:
            addToSelected(cell)
            break

          default:
            selectOnly(cell)
        }

        selected = compileSelected()
        dispatch.call('cell-selected-change', this, selected, active)
      }
    }

    function setActive(cell) {

      if (!cell) {
        active = null
        return
      }

      const { row, column } = cell
      active = { row: unwrap(row), column }
      dispatch.call('cell-active-change', this, active)
    }

    function setSelectionToRange(a, b) {
      selectedRowsByColumnId = {}
      rangeFrom(a, b).forEach(addToSelected)
    }

    function addRangeToSelection(a, b) {
      rangeFrom(a, b).forEach(addToSelected)
    }

    function rangeFrom(a, b) {
      const columnRange = rangeFromUnorderedIndices(
              columns
            , columns.indexOf(a.column)
            , columns.indexOf(b.column)
            )
          , rowRange = rangeFromUnorderedIndices(
              bundle.rows
            , findIndex(bundle.rows, r => unwrap(r) == a.row)
            , findIndex(bundle.rows, r => unwrap(r) == b.row)
            ).map(unwrap)

      return rowRange.reduce(allCellsInColumns, [])

      function allCellsInColumns(acc, row) {
        return acc.concat(columnRange.map(column => ({ column, row })))
      }
    }

    function selectOnly(target) {
      selectedRowsByColumnId = {}
      addToSelected(target)
    }

    function rangeFromUnorderedIndices(array, i1, i2) {
      return array.slice(
        Math.min(i1, i2)
      , Math.max(i1, i2) + 1
      )
    }

    function removeFromSelected({ row, column }) {
      const columnId = column.id
          , set = selectedRowsByColumnId[columnId]

      if (set) set.delete(row)
    }

    function addToSelected({ row, column }) {
      const columnId = column.id
          , set = selectedRowsByColumnId[columnId] || new Set()

      set.add(row)
      selectedRowsByColumnId[columnId] = set
    }

    function toCells(acc, set, columnId) {
      const column = columnById[columnId]
      return acc.concat(Array.from(set.values()).map(row => ({ row, column })))
    }
  }

  function onCellUpdate(d, i){
    select(this)
        .classed('is-selected', isCellSelected)
        .classed('is-active', isCellActive)
  }

  function isCellSelected(d, i) {
    const rowSetForColumn = selectedRowsByColumnId[d.column.id]
    return rowSetForColumn && rowSetForColumn.has(unwrap(d.row))
  }

  function isCellActive(d) {
    if (!active) return false
    return areSameCell(d, active)
  }
}

function isASpecialKey(key) {
  return key.length > 1
}

function areSameCell(a, b) {
  return a && b && unwrap(a.row) == unwrap(b.row) && a.column == b.column
}
<|endoftext|>


<commit_before>/*global Backbone, _, window, Promise, $ */
"use strict";
(function(NS) {
    //Filename: views/core-view.js
    NS = NS || {};
    var isInBrowser = typeof module === 'undefined' && typeof window !== 'undefined';
    var postRenderingBuilder = isInBrowser ? NS.Helpers.postRenderingBuilder : require('../helpers/post_rendering_builder');
    var ErrorHelper = isInBrowser ? NS.Helpers.errorHelper : require('../helpers/error_helper');
    var RefHelper = isInBrowser ? NS.Helpers.referenceHelper : require('../helpers/reference_helper');
    var ArgumentNullException = isInBrowser ? NS.Helpers.Exceptions.ArgumentNullException : require("../helpers/custom_exception").ArgumentNullException;
    var Model = isInBrowser ? NS.Models.Model : require("../models/model");
    var PaginatedCollection = isInBrowser ? NS.Models.PaginatedCollection : require("../models/paginatedCollection");
    var sessionHelper = isInBrowser ? NS.Helpers.sessionHelper : require('../helpers/session_helper');
    var templateSpinner = isInBrowser ? NS.templates.spinner : function() {
        return "Template spinner to define...";
    };
    //View which is the default view for each view.
    //This view is able to deal with errors and to render the default json moodel.
    var CoreView = Backbone.View.extend({
        toogleIsHidden: function(options) {
            this.isHidden = !this.isHidden;
            this.render(options);
        },
        //Reference lists names.
        //These _names_, must have been registered inside the the application to be used.
        referenceNames: undefined,

        //Options define by default for the view.
        defaultOptions: {
            isElementRedefinition: false, //This options is use in order to not have a tag container generated by Backbone arround the view.
            isReadyReferences: true
        },
        //Options overriden By the instanciate view.
        customOptions: {},

        //This property is use in order to create a new Model if no model are define in the view.
        modelName: undefined,

        //Initialization of the coreview.
        initialize: function initializeCoreView(options) {
            options = options || {};
            //Define default options foreach _core_ view, and override these options for each _project view_.
            //Then each view will have access to options in any methods.
            this.opts = _.extend({}, this.defaultOptions, this.customOptions, options);

            this.on('toogleIsHidden', this.toogleIsHidden);
            this.initializeModel();

            /*Register after renger.*/
            _.bindAll(this, 'render', 'afterRender');
            var _this = this;
            this.render = _.wrap(this.render, function(render, options) {
                //If the view is ready perform the standard render.
                if (_this.isReady()) {
                    render(options);
                    _this.afterRender();
                } else {
                    //Else render the spinner.
                    this.renderSpinner();
                }
                return _this;
            });

            //Listen to the reference list loading.
            this.listenTo(this.model, "references:loaded", this.render, this);

            //Load all the references lists which are defined in referenceNames.
            var currentView = this;
            if (_.isArray(this.referenceNames) && this.referenceNames.length > 0) {
                this.opts.isReadyReferences = false;
                Promise.all(RefHelper.loadMany(this.referenceNames)).then(function(results) {
                    currentView.opts.isReadyReferences = true;
                    //console.log('resultsreferenceNames', results);
                    var res = {}; //Container for all the results.
                    for (var i = 0, l = results.length; i < l; i++) {
                        res[currentView.referenceNames[i]] = results[i];
                        //The results are save into an object with a name for each reference list.
                    }
                    //Add the reference lists as model properties.
                    _.extend(currentView.model, res);
                    currentView.model.trigger('references:loaded');
                    //Inform the view that we are ready to render well.
                }).then(null, function(error) {
                    currentView.opts.isReadyReferences = true;
                    currentView.render();
                    ErrorHelper.manageResponseErrors(error, {
                        isDisplay: true
                    });
                });
            }

        },

        //Initialize the model of the view.
        //In order to be able to be initialize, a view must have a _model_ or a _modelName_.
        initializeModel: function initializeModelCoreView() {
            if (this.model) {
                return;
            } else if (this.opts.modelName) {
                //Création d'une collection et d'un model
                var ModelCreated = Model.extend({
                    modelName: this.opts.modelName
                });
                //Case of a collection.
                if (this.opts.modelType !== undefined && this.opts.modelType === "collection") {
                    var CollectionCreated = PaginatedCollection.extend({
                        modelName: this.opts.modelName,
                        model: ModelCreated
                    });
                    this.model = new CollectionCreated();
                } else {
                    //Case of a model.
                    this.model = new ModelCreated();

                }
            } else {
                throw new ArgumentNullException("The view must have a model or a model name.", this);
            }
        },
        //The handlebars template has to be defined here.
        template: function emptyTemplate(json) {
            console.log("templateData", json);
            return "
Your template has to be implemented.
";
        }, // Example: require('./templates/coreView'),
        templateSpinner: templateSpinner,
        //Defaults events.
        events: {
            "focus input": "inputFocus", //Deal with the focus in the field.
            "blur input": "inputBlur", //Deal with the focus out of the field.
            "click .panel-collapse.in": "hideCollapse",
            "click .panel-collapse:not('.in')": "showCollapse",
            "click button[data-loading]": "loadingButton"
        },
        //Input focus event.
        inputFocus: function coreViewInputFocus(event) {
            if (!this.model.has('errors')) {
                return;
            }
            //Remove the input hidden attribute.
            return event.target.parentElement.parentElement.childNodes[5].removeAttribute('hidden');
        },
        //Input blur event gestion
        inputBlur: function coreViewInputBlur(event) {
            if (!this.model.has('errors')) {
                return;
            }
            //If there is an error add the hidden attribute into it in odere to hide the errors.
            return event.target.parentElement.parentElement.childNodes[5].setAttribute("hidden", "hidden");
        },
        //This method is use in order to inject json data to the template. By default, the this.model.toJSON() is called.
        getRenderData: function getCoreViewRenderData() {
            return this.model.toJSON();
        },
        showCollapse: function showCollapseCoreView() {
            $('.collapse', this.$el).collapse('show');
        },
        hideCollapse: function hideCollapseCoreView() {
            $('.collapse', this.$el).collapse('hide');
        },
        toogleCollapse: function toogleCollapseCoreView(event) {
            $(".panel-collapse.in", event.target.parentNode.parentNode).collapse('hide'); //todo: change the selector
            $(".panel-collapse:not('.in')", event.target.parentNode.parentNode).collapse('show');
        },
        /**
         * Debug the core View. Display whatever you need in the console on render.
         * @return {undefined}
         */
        debug: function debugCoreView(){
            console.log("--------------CORE VIEW-----------------");
            console.log("View:     ",  this);
            console.log("Model:    ", this.model);
            console.log("Template: ",this.template(this.getRenderData)());
            console.log("--------------CORE VIEW-----------------");
        },
        //Render function  by default call the getRenderData and inject it into the view dom element.
        render: function renderCoreView() {
            if(this.opts.DEBUG){
                this.debug();
            }
            //If the view is not ready.
            this.$el.html(this.template(this.getRenderData()));

            //_.defer(this.afterRender, this);
            return this;
        },
        afterRender: function afterRenderCoreView() {
            //Eventually pass the currentview as argument for this binding.
            postRenderingBuilder({
                model: this.model,
                viewSelector: this.$el
            });
            $('.collapse', this.$el).collapse({
                toogle: true
            });
        },
        //Get the criteria from the session storage if exists.
        getSessionCriteria: function getCriteria() {
            return sessionHelper.getItem(this.getCriteriaId());
        },
        //Save the criteria in the session storage.
        saveSessionCriteria: function saveSessionCriteria(criteria) {
            return sessionHelper.saveItem(this.getCriteriaId(), criteria);
        },
        // Get the id of the criteria.
        getCriteriaId: function getCriteriaId() {
            var hash = window.location.hash;
            if (this.model.modelName !== undefined) {
                hash += this.model.modelName;
            }
            if (this.opts.criteriaId !== undefined) {
                hash += this.opts.criteriaId;
            }
            return hash;
        },
        //Call the history back.
        back: function back() {
            Backbone.history.history.back();
        },
        //Function which tells if the view is ready to be display.
        isReady: function isReady() {
            return this.opts.isReadyReferences === true;
        },
        //renderSpinner
        renderSpinner: function renderSpinner() {
            this.$el.html(this.templateSpinner(this.getRenderData()));
            return this;
        },
        /**
         * Change the button state (loading, reset) ...
         * @param  {string} selector - CSS selector for the button.
         * @param  {string} state    - The new state you want for the button.
         * @return {undefined}
         */
        changeButtonState: function changeButtonState(selector, state){
            $(selector, this.$el).button(state);
        }
    });

    // Differenciating export for node or browser.
    if (isInBrowser) {
        NS.Views = NS.Views || {};
        NS.Views.CoreView = CoreView;
    } else {
        module.exports = CoreView;
    }
})(typeof module === 'undefined' && typeof window !== 'undefined' ? window.Fmk : module.exports);
// ## Example calll:
// ```javascript
// var CoreView = require('./views/core-view');
// new CoreView({model: new Model({firstName: "first name", lastName: "last name"}).render().el //Get the dom element of the view.
//```<commit_msg>Correct the DEBUG functionnality #15.<commit_after>/*global Backbone, _, window, Promise, $ */
"use strict";
(function(NS) {
    //Filename: views/core-view.js
    NS = NS || {};
    var isInBrowser = typeof module === 'undefined' && typeof window !== 'undefined';
    var postRenderingBuilder = isInBrowser ? NS.Helpers.postRenderingBuilder : require('../helpers/post_rendering_builder');
    var ErrorHelper = isInBrowser ? NS.Helpers.errorHelper : require('../helpers/error_helper');
    var RefHelper = isInBrowser ? NS.Helpers.referenceHelper : require('../helpers/reference_helper');
    var ArgumentNullException = isInBrowser ? NS.Helpers.Exceptions.ArgumentNullException : require("../helpers/custom_exception").ArgumentNullException;
    var Model = isInBrowser ? NS.Models.Model : require("../models/model");
    var PaginatedCollection = isInBrowser ? NS.Models.PaginatedCollection : require("../models/paginatedCollection");
    var sessionHelper = isInBrowser ? NS.Helpers.sessionHelper : require('../helpers/session_helper');
    var templateSpinner = isInBrowser ? NS.templates.spinner : function() {
        return "Template spinner to define...";
    };
    //View which is the default view for each view.
    //This view is able to deal with errors and to render the default json moodel.
    var CoreView = Backbone.View.extend({
        toogleIsHidden: function(options) {
            this.isHidden = !this.isHidden;
            this.render(options);
        },
        //Reference lists names.
        //These _names_, must have been registered inside the the application to be used.
        referenceNames: undefined,

        //Options define by default for the view.
        defaultOptions: {
            isElementRedefinition: false, //This options is use in order to not have a tag container generated by Backbone arround the view.
            isReadyReferences: true
        },
        //Options overriden By the instanciate view.
        customOptions: {},

        //This property is use in order to create a new Model if no model are define in the view.
        modelName: undefined,

        //Initialization of the coreview.
        initialize: function initializeCoreView(options) {
            options = options || {};
            //Define default options foreach _core_ view, and override these options for each _project view_.
            //Then each view will have access to options in any methods.
            this.opts = _.extend({}, this.defaultOptions, this.customOptions, options);

            this.on('toogleIsHidden', this.toogleIsHidden);
            this.initializeModel();

            /*Register after renger.*/
            _.bindAll(this, 'render', 'afterRender');
            var _this = this;
            this.render = _.wrap(this.render, function(render, options) {
                //If the view is ready perform the standard render.
                if (_this.isReady()) {
                    if (_this.opts.DEBUG) {
                        _this.debug();
                    }
                    render(options);
                    _this.afterRender();
                } else {
                    //Else render the spinner.
                    this.renderSpinner();
                }
                return _this;
            });

            //Listen to the reference list loading.
            this.listenTo(this.model, "references:loaded", this.render, this);

            //Load all the references lists which are defined in referenceNames.
            var currentView = this;
            if (_.isArray(this.referenceNames) && this.referenceNames.length > 0) {
                this.opts.isReadyReferences = false;
                Promise.all(RefHelper.loadMany(this.referenceNames)).then(function(results) {
                    currentView.opts.isReadyReferences = true;
                    //console.log('resultsreferenceNames', results);
                    var res = {}; //Container for all the results.
                    for (var i = 0, l = results.length; i < l; i++) {
                        res[currentView.referenceNames[i]] = results[i];
                        //The results are save into an object with a name for each reference list.
                    }
                    //Add the reference lists as model properties.
                    _.extend(currentView.model, res);
                    currentView.model.trigger('references:loaded');
                    //Inform the view that we are ready to render well.
                }).then(null, function(error) {
                    currentView.opts.isReadyReferences = true;
                    currentView.render();
                    ErrorHelper.manageResponseErrors(error, {
                        isDisplay: true
                    });
                });
            }

        },

        //Initialize the model of the view.
        //In order to be able to be initialize, a view must have a _model_ or a _modelName_.
        initializeModel: function initializeModelCoreView() {
            if (this.model) {
                return;
            } else if (this.opts.modelName) {
                //Création d'une collection et d'un model
                var ModelCreated = Model.extend({
                    modelName: this.opts.modelName
                });
                //Case of a collection.
                if (this.opts.modelType !== undefined && this.opts.modelType === "collection") {
                    var CollectionCreated = PaginatedCollection.extend({
                        modelName: this.opts.modelName,
                        model: ModelCreated
                    });
                    this.model = new CollectionCreated();
                } else {
                    //Case of a model.
                    this.model = new ModelCreated();

                }
            } else {
                throw new ArgumentNullException("The view must have a model or a model name.", this);
            }
        },
        //The handlebars template has to be defined here.
        template: function emptyTemplate(json) {
            console.log("templateData", json);
            return "
Your template has to be implemented.
";
        }, // Example: require('./templates/coreView'),
        templateSpinner: templateSpinner,
        //Defaults events.
        events: {
            "focus input": "inputFocus", //Deal with the focus in the field.
            "blur input": "inputBlur", //Deal with the focus out of the field.
            "click .panel-collapse.in": "hideCollapse",
            "click .panel-collapse:not('.in')": "showCollapse",
            "click button[data-loading]": "loadingButton"
        },
        //Input focus event.
        inputFocus: function coreViewInputFocus(event) {
            if (!this.model.has('errors')) {
                return;
            }
            //Remove the input hidden attribute.
            return event.target.parentElement.parentElement.childNodes[5].removeAttribute('hidden');
        },
        //Input blur event gestion
        inputBlur: function coreViewInputBlur(event) {
            if (!this.model.has('errors')) {
                return;
            }
            //If there is an error add the hidden attribute into it in odere to hide the errors.
            return event.target.parentElement.parentElement.childNodes[5].setAttribute("hidden", "hidden");
        },
        //This method is use in order to inject json data to the template. By default, the this.model.toJSON() is called.
        getRenderData: function getCoreViewRenderData() {
            return this.model.toJSON();
        },
        showCollapse: function showCollapseCoreView() {
            $('.collapse', this.$el).collapse('show');
        },
        hideCollapse: function hideCollapseCoreView() {
            $('.collapse', this.$el).collapse('hide');
        },
        toogleCollapse: function toogleCollapseCoreView(event) {
            $(".panel-collapse.in", event.target.parentNode.parentNode).collapse('hide'); //todo: change the selector
            $(".panel-collapse:not('.in')", event.target.parentNode.parentNode).collapse('show');
        },
        /**
         * Debug the core View. Display whatever you need in the console on render.
         * @return {undefined}
         */
        debug: function debugCoreView() {
            console.log("--------------CORE VIEW-----------------");
            console.log("View:     ", this);
            console.log("Model:    ", this.model);
            if (this.template) {
                console.log("Template: ", this.template(this.getRenderData()));
            }
            console.log("----------------------------------------");
        },
        //Render function  by default call the getRenderData and inject it into the view dom element.
        render: function renderCoreView() {
            //If the view is not ready.
            this.$el.html(this.template(this.getRenderData()));

            //_.defer(this.afterRender, this);
            return this;
        },
        afterRender: function afterRenderCoreView() {
            //Eventually pass the currentview as argument for this binding.
            postRenderingBuilder({
                model: this.model,
                viewSelector: this.$el
            });
            $('.collapse', this.$el).collapse({
                toogle: true
            });
        },
        //Get the criteria from the session storage if exists.
        getSessionCriteria: function getCriteria() {
            return sessionHelper.getItem(this.getCriteriaId());
        },
        //Save the criteria in the session storage.
        saveSessionCriteria: function saveSessionCriteria(criteria) {
            return sessionHelper.saveItem(this.getCriteriaId(), criteria);
        },
        // Get the id of the criteria.
        getCriteriaId: function getCriteriaId() {
            var hash = window.location.hash;
            if (this.model.modelName !== undefined) {
                hash += this.model.modelName;
            }
            if (this.opts.criteriaId !== undefined) {
                hash += this.opts.criteriaId;
            }
            return hash;
        },
        //Call the history back.
        back: function back() {
            Backbone.history.history.back();
        },
        //Function which tells if the view is ready to be display.
        isReady: function isReady() {
            return this.opts.isReadyReferences === true;
        },
        //renderSpinner
        renderSpinner: function renderSpinner() {
            this.$el.html(this.templateSpinner(this.getRenderData()));
            return this;
        },
        /**
         * Change the button state (loading, reset) ...
         * @param  {string} selector - CSS selector for the button.
         * @param  {string} state    - The new state you want for the button.
         * @return {undefined}
         */
        changeButtonState: function changeButtonState(selector, state) {
            $(selector, this.$el).button(state);
        }
    });

    // Differenciating export for node or browser.
    if (isInBrowser) {
        NS.Views = NS.Views || {};
        NS.Views.CoreView = CoreView;
    } else {
        module.exports = CoreView;
    }
})(typeof module === 'undefined' && typeof window !== 'undefined' ? window.Fmk : module.exports);
// ## Example calll:
// ```javascript
// var CoreView = require('./views/core-view');
// new CoreView({model: new Model({firstName: "first name", lastName: "last name"}).render().el //Get the dom element of the view.
//```<|endoftext|>


<commit_before>(function() {
  'use strict';

  angular.module('dimApp')
    .directive('dimStats', Stats);

  Stats.$inject = [];

  function Stats() {
    return {
      controller: StatsCtrl,
      controllerAs: 'vm',
      bindToController: true,
      scope: {
        stats: '='
      },
      template: [
        '
',
        '
',
        '    ',
        '
',
        '

',
        '
',
        '
',
        '
',
        '    ',
        '
',
        '

',
        '
',
        '
',
        '
',
        '    ',
        '
',
        '

',
        '
',
        '
',
        '
'
      ].join('')
    };
  }

  StatsCtrl.$inject = ['$filter'];

  function StatsCtrl($filter) {
    var vm = this;

    _.each(vm.stats, function(stat) {
      stat.normalized = stat.value > 300 ? 300 : stat.value;
      stat.tier = Math.floor(stat.normalized / 60);
      stat.tiers = [];
      stat.remaining = stat.value;
      for (var t = 0; t < 5; t++) {
        stat.remaining -= stat.tiers[t] = stat.remaining > 60 ? 60 : stat.remaining;
      }
      stat.percentage = (100 * stat.normalized / 300).toFixed();
    });

    vm.formatTooltip = function(which) {
      var next = ' (' + vm.stats[which].value + '/300)';
      var tier = vm.stats[which].tier;
      var cooldown = vm.stats[which].cooldown || '';
      if (tier !== 5) {
        next = ' (' + $filter('translate')('tier_progress', { progress: (vm.stats[which].value % 60) + "/60", tier: 'T' + (tier + 1) }) + ')';
      }
      if (cooldown) {
        cooldown = '\n' + $filter('translate')(vm.stats[which].effect + '_cooldown') + ": " + cooldown;
        //cooldown = '\n' + vm.stats[which].effect + ' cooldown: ' + cooldown;
      }
      return 'T' + tier + ' ' + vm.stats[which].name + next + cooldown;
    };
  }
})();
<commit_msg>Remove commented-out line<commit_after>(function() {
  'use strict';

  angular.module('dimApp')
    .directive('dimStats', Stats);

  Stats.$inject = [];

  function Stats() {
    return {
      controller: StatsCtrl,
      controllerAs: 'vm',
      bindToController: true,
      scope: {
        stats: '='
      },
      template: [
        '
',
        '
',
        '    ',
        '
',
        '

',
        '
',
        '
',
        '
',
        '    ',
        '
',
        '

',
        '
',
        '
',
        '
',
        '    ',
        '
',
        '

',
        '
',
        '
',
        '
'
      ].join('')
    };
  }

  StatsCtrl.$inject = ['$filter'];

  function StatsCtrl($filter) {
    var vm = this;

    _.each(vm.stats, function(stat) {
      stat.normalized = stat.value > 300 ? 300 : stat.value;
      stat.tier = Math.floor(stat.normalized / 60);
      stat.tiers = [];
      stat.remaining = stat.value;
      for (var t = 0; t < 5; t++) {
        stat.remaining -= stat.tiers[t] = stat.remaining > 60 ? 60 : stat.remaining;
      }
      stat.percentage = (100 * stat.normalized / 300).toFixed();
    });

    vm.formatTooltip = function(which) {
      var next = ' (' + vm.stats[which].value + '/300)';
      var tier = vm.stats[which].tier;
      var cooldown = vm.stats[which].cooldown || '';
      if (tier !== 5) {
        next = ' (' + $filter('translate')('tier_progress', { progress: (vm.stats[which].value % 60) + "/60", tier: 'T' + (tier + 1) }) + ')';
      }
      if (cooldown) {
        cooldown = '\n' + $filter('translate')(vm.stats[which].effect + '_cooldown') + ": " + cooldown;
      }
      return 'T' + tier + ' ' + vm.stats[which].name + next + cooldown;
    };
  }
})();
<|endoftext|>