<commit_before>/* eslint-disable no-restricted-syntax */

import { File, FILETYPE, ROOTID, LINKTO } from '../models';
import { getUserById } from './userUtils';

export const ERR_PARENT_NOT_FOUND = new Error('parent file not found!');
export const ERR_FILE_ALREADY_EXIST = new Error('file already exist!');
export const ERR_FILE_NOT_EXIST = new Error('file not exist!');
export const ERR_PARENT_SHOULD_BE_A_FOLDER = new Error('parent should be a folder!');
export const ERR_CAN_NOT_GET_HOME_FOLDER = new Error('can not find home folder!');
export const ERR_NO_PARAMETER = new Error('no parameter!');

export function fliterObject(object) {
  const newObject = {};
  for (const p in object) {
    if (object[p] !== undefined) {
      newObject[p] = object[p];
    }
  }
  return newObject;
}

export function findFileById(id) {
  return new Promise(async (resolve, reject) => {
    try {
      const file = await File.findOne({
        where: { id },
      });
      if (!file) throw ERR_FILE_NOT_EXIST;
      resolve(file);
    } catch (err) {
      reject(err);
    }
  });
}

export function fileExists({ parent, name, type }) {
  return new Promise(async (resolve, reject) => {
    try {
      resolve(await parent.getSubFile({
        where: { name, type },
      }));
    } catch (err) {
      reject(err);
    }
  });
}

export function createFile({ id, name, type, parentId, parentFolder, mode,
  linkTo, createIfNotExist, force, userId, user }) {
  return new Promise(async (resolve, reject) => {
    let newFile;
    try {
      if (!userId) {
        if (user) {
          userId = user.get('id'); // eslint-disable-line no-param-reassign
        } else {
          throw ERR_NO_PARAMETER;
        }
      }

      let parent;

      if (parentFolder) {
        parent = parentFolder;
      } else if (parentId) {
        parent = await findFileById(parentId);
      } else {
        throw ERR_NO_PARAMETER;
      }
      if (!parent) throw ERR_PARENT_NOT_FOUND;
      if (parent.get('type') === FILETYPE.f) throw ERR_PARENT_SHOULD_BE_A_FOLDER;

      const subFiles = await parent.getSubFile({
        where: { name, type },
      });

      // file exist
      if (subFiles.length) {
        if (createIfNotExist) {
          resolve(subFiles[0]);
          return;
        }
        if (!force) throw ERR_FILE_ALREADY_EXIST;
        subFiles[0].destroy();
      }
      const parentPath = parent.get('path');
      newFile = await parent.createSubFile(fliterObject({
        id,
        userId,
        name,
        type,
        mode,
        linkTo,
        path: `${parentPath === '/' ? '' : parentPath}/${name}`,
      }));
      resolve(newFile);
    } catch (err) {
      if (newFile) await newFile.destroy();
      reject(err);
    }
  });
}

export function findFileByPath(path) {
  return new Promise(async (resolve, reject) => {
    try {
      resolve(await File.findOne({
        where: { path },
      }));
    } catch (err) {
      reject(err);
    }
  });
}

export function createFolder({ id, name, parentId, mode = '664',
  linkTo, createIfNotExist, force, userId }) {
  return new Promise(async (resolve, reject) => {
    let newFolder;
    try {
      newFolder = await createFile(fliterObject({
        id,
        name,
        type: FILETYPE.d,
        parentId,
        mode,
        linkTo,
        createIfNotExist,
        force,
        userId }));
      resolve(newFolder);
    } catch (err) {
      if (newFolder) newFolder.destroy();
      reject(err);
    }
  });
}

export function findOneFile({ id, name, type, parentId = ROOTID,
  createdAt, updatedAt, mode, linkTo }) {
  return new Promise(async (resolve, reject) => {
    try {
      const parentFile = await findFileById(parentId);
      const subfiles = await parentFile.getSubFile({
        where: fliterObject({
          id,
          mode,
          name,
          type,
          linkTo,
          createdAt,
          updatedAt }),
      });
      if (subfiles.length === 0) throw ERR_FILE_NOT_EXIST;
      resolve(subfiles[0]);
      return;
    } catch (err) {
      reject(err);
    }
  });
}

export function findOneFileByName(name, parentId = ROOTID) {
  return findOneFile({
    name, parentId,
  });
}

export function findOneFolderByName(name, parentId = ROOTID) {
  return findOneFile({
    name, parentId, type: FILETYPE.d,
  });
}

export function findOneRegularFileByName(name, parentId = ROOTID) {
  return findOneFile({
    name, parentId, type: FILETYPE.f,
  });
}

export function getFileChildren(id) {
  return new Promise(async (resolve, reject) => {
    try {
      const file = await File.findOne({
        where: { id },
      });
      if (!file) throw ERR_FILE_NOT_EXIST;
      resolve(await file.getSubFile());
    } catch (err) {
      reject(err);
    }
  });
}

export function isFolder(file) {
  return file.get('type') === FILETYPE.d;
}

export function searchFilesByName(name, parentId = ROOTID) {
  return new Promise(async (resolve, reject) => {
    try {
      const parentFile = await findFileById(parentId);
      if (!isFolder(parentFile)) throw ERR_PARENT_SHOULD_BE_A_FOLDER;
      const files = await File.findOne({
        where: {
          path: {
            $like: `${parentFile.get('path')}/%/${name}`,
          },
        },
      });
      resolve(files);
    } catch (err) {
      reject(err);
    }
  });
}

export function getUserHomeFolder(user) {
  return new Promise(async (resolve, reject) => {
    try {
      const homeFolder = await File.findOne({
        where: { path: user.get('homePath') },
      });
      if (!homeFolder) throw ERR_CAN_NOT_GET_HOME_FOLDER;
      resolve(homeFolder);
    } catch (err) {
      reject(err);
    }
  });
}

export function getFileByPath(path, type = FILETYPE.f) {
  return new Promise(async (resolve, reject) => {
    try {
      resolve(await File.findOne({
        where: { path, type },
      }));
    } catch (err) {
      reject(err);
    }
  });
}

export function getFolderByPath(path) {
  return getFileByPath(path, FILETYPE.d);
}

export function mkdir({ name, path, userId, mode = '664' }) {
  return new Promise(async (resolve, reject) => {
    try {
      let parent;
      if (path === '~') {
        const user = await getUserById(userId);
        parent = await getUserHomeFolder(user);
      } else {
        parent = await getFolderByPath(path);
      }
      if (!parent) throw ERR_PARENT_NOT_FOUND;

      resolve(await createFolder({
        name,
        userId,
        mode,
        linkTo: LINKTO.none,
        parentId: parent.id,
      }));
    } catch (err) {
      reject(err);
    }
  });
}

async function rmFile({ file, recurrence = false }) {
  if (file.type === FILETYPE.d && recurrence) {
    await File.destroy({
      where: {
        $or: {
          parentId: file.get('id'),
          id: file.get('id'),
        },
      },
    });
  } else if (file.type === FILETYPE.f) {
    await file.destroy();
  } else if (file.type === FILETYPE.d && !recurrence) {
    throw new Error(`can not remove directory ${file.name} without -r`);
  } else {
    throw new Error(`remove ${file.name} failed!`);
  }
}

export function rm({ path, recurrence = false }) {
  return new Promise(async (resolve, reject) => {
    try {
      const file = await findFileByPath(path);

      if (!file) {
        throw ERR_FILE_NOT_EXIST;
      } else {
        rmFile({ file, recurrence });
        resolve(file);
      }
    } catch (err) {
      reject(err);
    }
  });
}

function convertWildcard(path) {
  let tmp = '';
  let i = 0;

  while (path.charAt(i)) {
    if (path.charAt(i) === '\'') {
      i += 1;

      while (path.charAt(i)) {
        if (path.charAt(i) === '\'') {
          break;
        } else {
          tmp += path.charAt(i);
        }
        i += 1;
      }
    } else if (path.charAt(i) === '[') {
      tmp += path.charAt(i);
      i += 1;

      while (path.charAt(i)) {
        if (path.charAt(i) === ']') {
          tmp += path.charAt(i);
          break;
        } else if (path.charAt(i) === ',') {
          tmp += '';
        } else if (path.charAt(i) === '*') {
          tmp += '%';
        } else {
          tmp += path.charAt(i);
        }
        i += 1;
      }
    // } else if (path.charAt(i) === '{') {
    } else if (path.charAt(i) === '*') {
      tmp += '%';
    } else {
      tmp += path.charAt(i);
    }
    i += 1;
  }

  return tmp;
}

export function rmWithWildcard({ path, recurrence = false }) {
  return new Promise(async (resolve, reject) => {
    try {
      const files = await File.findAll({
        where: {
          path: {
            $like: convertWildcard(path),
          },
        },
      });

      for (const file of files) {
        rmFile({ file, recurrence });
      }

      resolve();
    } catch (err) {
      reject(err);
    }
  });
}

export function mv({ files = [], target } = {}) {
  return new Promise(async (resolve, reject) => {
    try {
      const parent = await File.findOne({
        where: { path: target },
      });

      if (!parent) {
        throw new Error(`not found ${target}`);
      }

      const children = await File.findAll({
        where: {
          $or: files.map(f => ({
            path: {
              $like: convertWildcard(f),
            },
          })),
        },
      });

      const t = [];

      for (const c of children) {
        t.push(c.update({ parentId: parent.id }));
      }

      await Promise.all(t);

      resolve();
    } catch (err) {
      reject(err);
    }
  });
}
<commit_msg>Fix file mv path<commit_after>/* eslint-disable no-restricted-syntax */

import { File, FILETYPE, ROOTID, LINKTO } from '../models';
import { getUserById } from './userUtils';

export const ERR_PARENT_NOT_FOUND = new Error('parent file not found!');
export const ERR_FILE_ALREADY_EXIST = new Error('file already exist!');
export const ERR_FILE_NOT_EXIST = new Error('file not exist!');
export const ERR_PARENT_SHOULD_BE_A_FOLDER = new Error('parent should be a folder!');
export const ERR_CAN_NOT_GET_HOME_FOLDER = new Error('can not find home folder!');
export const ERR_NO_PARAMETER = new Error('no parameter!');

export function fliterObject(object) {
  const newObject = {};
  for (const p in object) {
    if (object[p] !== undefined) {
      newObject[p] = object[p];
    }
  }
  return newObject;
}

export function findFileById(id) {
  return new Promise(async (resolve, reject) => {
    try {
      const file = await File.findOne({
        where: { id },
      });
      if (!file) throw ERR_FILE_NOT_EXIST;
      resolve(file);
    } catch (err) {
      reject(err);
    }
  });
}

export function fileExists({ parent, name, type }) {
  return new Promise(async (resolve, reject) => {
    try {
      resolve(await parent.getSubFile({
        where: { name, type },
      }));
    } catch (err) {
      reject(err);
    }
  });
}

export function createFile({ id, name, type, parentId, parentFolder, mode,
  linkTo, createIfNotExist, force, userId, user }) {
  return new Promise(async (resolve, reject) => {
    let newFile;
    try {
      if (!userId) {
        if (user) {
          userId = user.get('id'); // eslint-disable-line no-param-reassign
        } else {
          throw ERR_NO_PARAMETER;
        }
      }

      let parent;

      if (parentFolder) {
        parent = parentFolder;
      } else if (parentId) {
        parent = await findFileById(parentId);
      } else {
        throw ERR_NO_PARAMETER;
      }
      if (!parent) throw ERR_PARENT_NOT_FOUND;
      if (parent.get('type') === FILETYPE.f) throw ERR_PARENT_SHOULD_BE_A_FOLDER;

      const subFiles = await parent.getSubFile({
        where: { name, type },
      });

      // file exist
      if (subFiles.length) {
        if (createIfNotExist) {
          resolve(subFiles[0]);
          return;
        }
        if (!force) throw ERR_FILE_ALREADY_EXIST;
        subFiles[0].destroy();
      }
      const parentPath = parent.get('path');
      newFile = await parent.createSubFile(fliterObject({
        id,
        userId,
        name,
        type,
        mode,
        linkTo,
        path: `${parentPath === '/' ? '' : parentPath}/${name}`,
      }));
      resolve(newFile);
    } catch (err) {
      if (newFile) await newFile.destroy();
      reject(err);
    }
  });
}

export function findFileByPath(path) {
  return new Promise(async (resolve, reject) => {
    try {
      resolve(await File.findOne({
        where: { path },
      }));
    } catch (err) {
      reject(err);
    }
  });
}

export function createFolder({ id, name, parentId, mode = '664',
  linkTo, createIfNotExist, force, userId }) {
  return new Promise(async (resolve, reject) => {
    let newFolder;
    try {
      newFolder = await createFile(fliterObject({
        id,
        name,
        type: FILETYPE.d,
        parentId,
        mode,
        linkTo,
        createIfNotExist,
        force,
        userId }));
      resolve(newFolder);
    } catch (err) {
      if (newFolder) newFolder.destroy();
      reject(err);
    }
  });
}

export function findOneFile({ id, name, type, parentId = ROOTID,
  createdAt, updatedAt, mode, linkTo }) {
  return new Promise(async (resolve, reject) => {
    try {
      const parentFile = await findFileById(parentId);
      const subfiles = await parentFile.getSubFile({
        where: fliterObject({
          id,
          mode,
          name,
          type,
          linkTo,
          createdAt,
          updatedAt }),
      });
      if (subfiles.length === 0) throw ERR_FILE_NOT_EXIST;
      resolve(subfiles[0]);
      return;
    } catch (err) {
      reject(err);
    }
  });
}

export function findOneFileByName(name, parentId = ROOTID) {
  return findOneFile({
    name, parentId,
  });
}

export function findOneFolderByName(name, parentId = ROOTID) {
  return findOneFile({
    name, parentId, type: FILETYPE.d,
  });
}

export function findOneRegularFileByName(name, parentId = ROOTID) {
  return findOneFile({
    name, parentId, type: FILETYPE.f,
  });
}

export function getFileChildren(id) {
  return new Promise(async (resolve, reject) => {
    try {
      const file = await File.findOne({
        where: { id },
      });
      if (!file) throw ERR_FILE_NOT_EXIST;
      resolve(await file.getSubFile());
    } catch (err) {
      reject(err);
    }
  });
}

export function isFolder(file) {
  return file.get('type') === FILETYPE.d;
}

export function searchFilesByName(name, parentId = ROOTID) {
  return new Promise(async (resolve, reject) => {
    try {
      const parentFile = await findFileById(parentId);
      if (!isFolder(parentFile)) throw ERR_PARENT_SHOULD_BE_A_FOLDER;
      const files = await File.findOne({
        where: {
          path: {
            $like: `${parentFile.get('path')}/%/${name}`,
          },
        },
      });
      resolve(files);
    } catch (err) {
      reject(err);
    }
  });
}

export function getUserHomeFolder(user) {
  return new Promise(async (resolve, reject) => {
    try {
      const homeFolder = await File.findOne({
        where: { path: user.get('homePath') },
      });
      if (!homeFolder) throw ERR_CAN_NOT_GET_HOME_FOLDER;
      resolve(homeFolder);
    } catch (err) {
      reject(err);
    }
  });
}

export function getFileByPath(path, type = FILETYPE.f) {
  return new Promise(async (resolve, reject) => {
    try {
      resolve(await File.findOne({
        where: { path, type },
      }));
    } catch (err) {
      reject(err);
    }
  });
}

export function getFolderByPath(path) {
  return getFileByPath(path, FILETYPE.d);
}

export function mkdir({ name, path, userId, mode = '664' }) {
  return new Promise(async (resolve, reject) => {
    try {
      let parent;
      if (path === '~') {
        const user = await getUserById(userId);
        parent = await getUserHomeFolder(user);
      } else {
        parent = await getFolderByPath(path);
      }
      if (!parent) throw ERR_PARENT_NOT_FOUND;

      resolve(await createFolder({
        name,
        userId,
        mode,
        linkTo: LINKTO.none,
        parentId: parent.id,
      }));
    } catch (err) {
      reject(err);
    }
  });
}

async function rmFile({ file, recurrence = false }) {
  if (file.type === FILETYPE.d && recurrence) {
    await File.destroy({
      where: {
        $or: {
          parentId: file.get('id'),
          id: file.get('id'),
        },
      },
    });
  } else if (file.type === FILETYPE.f) {
    await file.destroy();
  } else if (file.type === FILETYPE.d && !recurrence) {
    throw new Error(`can not remove directory ${file.name} without -r`);
  } else {
    throw new Error(`remove ${file.name} failed!`);
  }
}

export function rm({ path, recurrence = false }) {
  return new Promise(async (resolve, reject) => {
    try {
      const file = await findFileByPath(path);

      if (!file) {
        throw ERR_FILE_NOT_EXIST;
      } else {
        rmFile({ file, recurrence });
        resolve(file);
      }
    } catch (err) {
      reject(err);
    }
  });
}

function convertWildcard(path) {
  let tmp = '';
  let i = 0;

  while (path.charAt(i)) {
    if (path.charAt(i) === '\'') {
      i += 1;

      while (path.charAt(i)) {
        if (path.charAt(i) === '\'') {
          break;
        } else {
          tmp += path.charAt(i);
        }
        i += 1;
      }
    } else if (path.charAt(i) === '[') {
      tmp += path.charAt(i);
      i += 1;

      while (path.charAt(i)) {
        if (path.charAt(i) === ']') {
          tmp += path.charAt(i);
          break;
        } else if (path.charAt(i) === ',') {
          tmp += '';
        } else if (path.charAt(i) === '*') {
          tmp += '%';
        } else {
          tmp += path.charAt(i);
        }
        i += 1;
      }
    // } else if (path.charAt(i) === '{') {
    } else if (path.charAt(i) === '*') {
      tmp += '%';
    } else {
      tmp += path.charAt(i);
    }
    i += 1;
  }

  return tmp;
}

export function rmWithWildcard({ path, recurrence = false }) {
  return new Promise(async (resolve, reject) => {
    try {
      const files = await File.findAll({
        where: {
          path: {
            $like: convertWildcard(path),
          },
        },
      });

      for (const file of files) {
        rmFile({ file, recurrence });
      }

      resolve();
    } catch (err) {
      reject(err);
    }
  });
}

export function mv({ files = [], target } = {}) {
  return new Promise(async (resolve, reject) => {
    try {
      const parent = await File.findOne({
        where: { path: target },
      });

      if (!parent) {
        throw new Error(`not found ${target}`);
      }

      const children = await File.findAll({
        where: {
          $or: files.map(f => ({
            path: {
              $like: convertWildcard(f),
            },
          })),
        },
      });

      const t = [];

      for (const c of children) {
        t.push(c.update({
          parentId: parent.id,
          path: `${parent.path}/${c.name}`,
        }));
      }

      await Promise.all(t);

      resolve();
    } catch (err) {
      reject(err);
    }
  });
}
<|endoftext|>


<commit_before>import {isArray, isObject, values as objectValues, each} from "../util.js";
import {load} from "../load.js";
import {createHashMap} from "../hashmap.js";
import {getTheme} from "../theme.js";
import {createTooltip} from "../render/tooltip.js";

import {createDataNode, updateDataNode} from "../data.js";
import {createScaleNode, updateScaleNode} from "../scales/index.js";
import {createGeomNode, updateGeomNode} from "../render/geoms/index.js";
import {createAxisNode, updateAxisNode} from "../render/axes.js";
import {createPanelsNode, updatePanelsNode} from "../render/panels.js";
import {createLegendNode, updateLegendNode} from "../render/legends.js";
import {createSceneNode, updateSceneNode} from "../render/scene.js";

import {parseSizeValue} from "./parsers.js";
import {parseBackgroundValue} from "./parsers.js";
import {parseMarginValue} from "./parsers.js";

//Update updaters
let nodeUpdate = {
    "data": updateDataNode,
    "scale": updateScaleNode,
    "axis": updateAxisNode,
    "panels": updatePanelsNode,
    "legend": updateLegendNode,
    "scene": updateSceneNode
};

//Update the context
export function updateContext (context, forceUpdate) {
    return new Promise(function (resolve, reject) {
        //Check for no pending updates
        if (context.actions.length() === 0 && forceUpdate === false) {
            //TODO: display in context logs
            return resolve();
        }
        let updateList = createHashMap(); //We will store all changed nodes on this list
        //let recomputeDraw = forceUpdate === true; //Recompute the drawing values
        //Apply each change to the state object
        context.actions.forEach(function (action) {
            let node = action.target;
            //Update the margins
            if (node.type === "margin" || node.type === "outerMargin") {
                let values = parseMarginValue(action.value); //Parse margin value
                //recomputeDraw = true; //We should recompute the draw width and height
                //Check for outermargin node --> translate the main group
                if (node.type === "outerMargin") {
                    context.target.attr("transform", `translate(${values.left},${values.top})`);
                }
                node.value = values; //Save margin values
            }
            //Check for scene node type --> update the style
            else if (node.type === "scene") {
                //TODO
            }
            //Other node type
            else {
                node.value = action.value; //Update the node value
            }
            //updateList.add(node); //Changed node --> add to changes list
            //Add all targets nodes of this changed item
            if (isObject(node.targets)) {
                node.targets.forEach(function (targetNode) {
                    updateList.add(targetNode.id, targetNode);
                });
            }
        });
        //Check for no nodes to update
        if (updateList.length() === 0 && forceUpdate === false) {
            //TODO: display in logs
            return resolve();
        }
        //Loop for all nodes
        context.nodes.forEach(function (node) {
            //Check if this node is not in the update list
            if (updateList.has(node.id) === false && forceUpdate === false) {
                return null;
            }
            //Nodes that we will update: data|panels|scale|axis|legend
            if (typeof nodeUpdate[node.type] === "function") {
                nodeUpdate[node.type](context, node);
            }
            //Update geom node
            else if (node.type === "geom") {
                //If the geom source data has changed we will force a re-rendering
                //If not, we will call only the update props of this shape
                updateGeomNode(context, node, (node.source !== null && updateList.has(node.source.id)) || forceUpdate);
            }
            //Add the targets nodes of the current node ot the list of nodes to update
            //We will ensure that all targets of the updated nodes will be visited.
            if (node.targets !== null && forceUpdate === false) {
                return node.targets.forEach(function (targetNode) {
                    updateList.add(targetNode.id, targetNode);
                });
            }
        });
        context.actions.clear(); //Reset the actions list
        return resolve();
    });
}

//Build the context
export function buildContext (context) {
    //Sources to import
    let sources = Object.keys(context.input).filter(function (key) {
        let node = context.input[key];
        return node.props !== null && typeof node.props.url === "string";
    });
    return new Promise(function (resolve, reject) {
        //console.log(sources);
        //Method for importing data async
        let loadDataAsync = function (index) {
            if (index >= sources.length) {
                //return updateContext(context, true, callback);
                return resolve(); //Continue
            }
            //Get input name
            let name = sources[index];
            //Import data
            return load(context.input[name].props.url).then(function (data) {
                //Save this data to the sources cache
                context.input[name].value = JSON.parse(data); //TODO: convert the data type
                //Continue
                return loadDataAsync(index + 1);
            }).catch(function (error) {
                //console.error(error);
                return reject(error);
            });
        };
        //Start laoding data
        loadDataAsync(0);
    });
};

//Initialize the context
export function initContext (context, schema) {
    context.theme = getTheme(schema["theme"]); //Get context theme
    //Save padding/width and height values from props
    context.draw = {
        "width": context.addNode({
            "id": "draw:width",
            "value": parseSizeValue(schema["width"]),
            "targets": createHashMap(),
            "type": "width"
        }),
        "height": context.addNode({
            "id": "draw:height",
            "value": parseSizeValue(schema["height"]),
            "targets": createHashMap(),
            "type": "height"
        }),
        //Internal margins
        "margin": context.addNode({
            "id": "draw:margin",
            "value": parseMarginValue(schema["margin"]),
            "targets": createHashMap(),
            "type": "margin"
        }),
        //Outer margins
        "outerMargin": context.addNode({
            "id": "draw:outer-margin",
            "value": parseMarginValue(schema["outerMargin"]),
            "targets": createHashMap(),
            "type": "outerMargin"
        })
    };
    //Create the scene node
    createSceneNode(context, null, schema["style"]);
    context.target = context.scene.element.append("g"); //Add context target group
    createTooltip(context.scene.element.append("g"), {}); //Create the tooltip parent
    //Initialize input data
    each(schema["data"], function (index, props) {
        let name = (typeof props["name"] === "string") ? props["name"] : index;
        //Check for data to be imported from url --> save the url
        if (typeof props["url"] === "string") {
            context.input[name] = context.addNode({
                "id": `input:${name}`,
                "props": props,
                //"type": "input",
                "value": [], //Empty values list
                "targets": createHashMap()
            });
        }
        //Check for value data --> save the values in a new node
        else if (isArray(props["value"]) && typeof props["source"] !== "string") {
            context.input[name] = context.addNode({
                "id": `input:${name}`,
                "value": props["value"], //Save the data values
                //"type": "input",
                "targets": createHashMap()
            });
        }
    });
    //Initialize state nodes
    each(schema["state"], function (index, props) {
        let name = (typeof props["name"] === "string") ? props["name"] : index;
        context.state[name] = context.addNode({
            "id": `state:${name}`,
            "value": props["value"],
            "targets": createHashMap(),
            "type": "state"
        });
        //createStateNode(context, name, stateProps);
    });
    //Build data props
    each(schema["data"], function (index, props) {
        let name = (typeof props["name"] === "string") ? props["name"] : index;
        createDataNode(context, name, props);
    });
    //Create panels node
    createPanelsNode(context, schema["panels"]);
    //Build scale props
    each(schema["scales"], function (index, props) {
        let name = (typeof props["name"] === "string") ? props["name"] : index;
        createScaleNode(context, name, props);
    });
    //Draw all axes
    each(schema["axes"], function (index, props) {
        createAxisNode(context, index, props);
    });
    //Render all geoms
    each(schema["geoms"], function (index, props) {
        createGeomNode(context, index, props);
    });
    //Render all legends
    each(schema["legends"], function (index, props) {
        createLegendNode(context, index, props);
    });
    //console.log(context);
}

<commit_msg>lifecycle: fixed minor bug getting node targets<commit_after>import {isArray, isObject, values as objectValues, each} from "../util.js";
import {load} from "../load.js";
import {createHashMap} from "../hashmap.js";
import {getTheme} from "../theme.js";
import {createTooltip} from "../render/tooltip.js";

import {createDataNode, updateDataNode} from "../data.js";
import {createScaleNode, updateScaleNode} from "../scales/index.js";
import {createGeomNode, updateGeomNode} from "../render/geoms/index.js";
import {createAxisNode, updateAxisNode} from "../render/axes.js";
import {createPanelsNode, updatePanelsNode} from "../render/panels.js";
import {createLegendNode, updateLegendNode} from "../render/legends.js";
import {createSceneNode, updateSceneNode} from "../render/scene.js";

import {parseSizeValue} from "./parsers.js";
import {parseBackgroundValue} from "./parsers.js";
import {parseMarginValue} from "./parsers.js";

//Update updaters
let nodeUpdate = {
    "data": updateDataNode,
    "scale": updateScaleNode,
    "axis": updateAxisNode,
    "panels": updatePanelsNode,
    "legend": updateLegendNode,
    "scene": updateSceneNode
};

//Update the context
export function updateContext (context, forceUpdate) {
    return new Promise(function (resolve, reject) {
        //Check for no pending updates
        if (context.actions.length() === 0 && forceUpdate === false) {
            //TODO: display in context logs
            return resolve();
        }
        let updateList = createHashMap(); //We will store all changed nodes on this list
        //let recomputeDraw = forceUpdate === true; //Recompute the drawing values
        //Apply each change to the state object
        context.actions.forEach(function (action) {
            let node = action.target;
            //Update the margins
            if (node.type === "margin" || node.type === "outerMargin") {
                let values = parseMarginValue(action.value); //Parse margin value
                //recomputeDraw = true; //We should recompute the draw width and height
                //Check for outermargin node --> translate the main group
                if (node.type === "outerMargin") {
                    context.target.attr("transform", `translate(${values.left},${values.top})`);
                }
                node.value = values; //Save margin values
            }
            //Check for scene node type --> update the style
            else if (node.type === "scene") {
                //TODO
            }
            //Other node type
            else {
                node.value = action.value; //Update the node value
            }
            //updateList.add(node); //Changed node --> add to changes list
            //Add all targets nodes of this changed item
            if (isObject(node.targets)) {
                node.targets.forEach(function (targetNode) {
                    updateList.add(targetNode.id, targetNode);
                });
            }
        });
        //Check for no nodes to update
        if (updateList.length() === 0 && forceUpdate === false) {
            //TODO: display in logs
            return resolve();
        }
        //Loop for all nodes
        context.nodes.forEach(function (node) {
            //Check if this node is not in the update list
            if (updateList.has(node.id) === false && forceUpdate === false) {
                return null;
            }
            //Nodes that we will update: data|panels|scale|axis|legend
            if (typeof nodeUpdate[node.type] === "function") {
                nodeUpdate[node.type](context, node);
            }
            //Update geom node
            else if (node.type === "geom") {
                //If the geom source data has changed we will force a re-rendering
                //If not, we will call only the update props of this shape
                updateGeomNode(context, node, (node.source !== null && updateList.has(node.source.id)) || forceUpdate);
            }
            //Add the targets nodes of the current node ot the list of nodes to update
            //We will ensure that all targets of the updated nodes will be visited.
            if (forceUpdate === false && isObject(node.targets)) {
                return node.targets.forEach(function (targetNode) {
                    updateList.add(targetNode.id, targetNode);
                });
            }
        });
        context.actions.clear(); //Reset the actions list
        return resolve();
    });
}

//Build the context
export function buildContext (context) {
    //Sources to import
    let sources = Object.keys(context.input).filter(function (key) {
        let node = context.input[key];
        return node.props !== null && typeof node.props.url === "string";
    });
    return new Promise(function (resolve, reject) {
        //console.log(sources);
        //Method for importing data async
        let loadDataAsync = function (index) {
            if (index >= sources.length) {
                //return updateContext(context, true, callback);
                return resolve(); //Continue
            }
            //Get input name
            let name = sources[index];
            //Import data
            return load(context.input[name].props.url).then(function (data) {
                //Save this data to the sources cache
                context.input[name].value = JSON.parse(data); //TODO: convert the data type
                //Continue
                return loadDataAsync(index + 1);
            }).catch(function (error) {
                //console.error(error);
                return reject(error);
            });
        };
        //Start laoding data
        loadDataAsync(0);
    });
};

//Initialize the context
export function initContext (context, schema) {
    context.theme = getTheme(schema["theme"]); //Get context theme
    //Save padding/width and height values from props
    context.draw = {
        "width": context.addNode({
            "id": "draw:width",
            "value": parseSizeValue(schema["width"]),
            "targets": createHashMap(),
            "type": "width"
        }),
        "height": context.addNode({
            "id": "draw:height",
            "value": parseSizeValue(schema["height"]),
            "targets": createHashMap(),
            "type": "height"
        }),
        //Internal margins
        "margin": context.addNode({
            "id": "draw:margin",
            "value": parseMarginValue(schema["margin"]),
            "targets": createHashMap(),
            "type": "margin"
        }),
        //Outer margins
        "outerMargin": context.addNode({
            "id": "draw:outer-margin",
            "value": parseMarginValue(schema["outerMargin"]),
            "targets": createHashMap(),
            "type": "outerMargin"
        })
    };
    //Create the scene node
    createSceneNode(context, null, schema["style"]);
    context.target = context.scene.element.append("g"); //Add context target group
    createTooltip(context.scene.element.append("g"), {}); //Create the tooltip parent
    //Initialize input data
    each(schema["data"], function (index, props) {
        let name = (typeof props["name"] === "string") ? props["name"] : index;
        //Check for data to be imported from url --> save the url
        if (typeof props["url"] === "string") {
            context.input[name] = context.addNode({
                "id": `input:${name}`,
                "props": props,
                //"type": "input",
                "value": [], //Empty values list
                "targets": createHashMap()
            });
        }
        //Check for value data --> save the values in a new node
        else if (isArray(props["value"]) && typeof props["source"] !== "string") {
            context.input[name] = context.addNode({
                "id": `input:${name}`,
                "value": props["value"], //Save the data values
                //"type": "input",
                "targets": createHashMap()
            });
        }
    });
    //Initialize state nodes
    each(schema["state"], function (index, props) {
        let name = (typeof props["name"] === "string") ? props["name"] : index;
        context.state[name] = context.addNode({
            "id": `state:${name}`,
            "value": props["value"],
            "targets": createHashMap(),
            "type": "state"
        });
        //createStateNode(context, name, stateProps);
    });
    //Build data props
    each(schema["data"], function (index, props) {
        let name = (typeof props["name"] === "string") ? props["name"] : index;
        createDataNode(context, name, props);
    });
    //Create panels node
    createPanelsNode(context, schema["panels"]);
    //Build scale props
    each(schema["scales"], function (index, props) {
        let name = (typeof props["name"] === "string") ? props["name"] : index;
        createScaleNode(context, name, props);
    });
    //Draw all axes
    each(schema["axes"], function (index, props) {
        createAxisNode(context, index, props);
    });
    //Render all geoms
    each(schema["geoms"], function (index, props) {
        createGeomNode(context, index, props);
    });
    //Render all legends
    each(schema["legends"], function (index, props) {
        createLegendNode(context, index, props);
    });
    //console.log(context);
}

<|endoftext|>


<commit_before>// Google Analytics
// ----------------
// [Documentation](https://developers.google.com/analytics/devguides/collection/gajs/).

analytics.addProvider('Google Analytics', {

    settings : {
        anonymizeIp             : false,
        enhancedLinkAttribution : false,
        siteSpeedSampleRate     : null,
        domain                  : null,
        trackingId              : null
    },

    // Initialize
    // ----------

    // Changes to the Google Analytics snippet:
    //
    // * Added `trackingId`.
    // * Added optional support for `enhancedLinkAttribution`
    // * Added optional support for `siteSpeedSampleRate`
    // * Added optional support for `anonymizeIp`
    initialize : function (settings) {
        settings = analytics.utils.resolveSettings(settings, 'trackingId');
        analytics.utils.extend(this.settings, settings);

        var _gaq = window._gaq = window._gaq || [];
        _gaq.push(['_setAccount', this.settings.trackingId]);
        if (this.settings.enhancedLinkAttribution) {
            var pluginUrl = (('https:' == document.location.protocol) ? 'https://www.' : 'http://www.') + 'google-analytics.com/plugins/ga/inpage_linkid.js';
            _gaq.push(['_require', 'inpage_linkid', pluginUrl]);
        }
        if (analytics.utils.isNumber(this.settings.siteSpeedSampleRate)) {
            _gaq.push(['_setSiteSpeedSampleRate', this.settings.siteSpeedSampleRate]);
        }
        if(this.settings.domain) {
            _gaq.push(['_setDomainName', this.settings.domain]);
        }
        if(this.settings.anonymizeIp) {
            _gaq.push(['_gat._anonymizeIp']);
        }
        _gaq.push(['_trackPageview']);

        (function() {
            var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
            ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
            var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
        })();
    },

    // Track
    // -----

    track : function (event, properties) {
        properties || (properties = {});

        // Try to check for a `category` and `label`. A `category` is required,
        // so if it's not there we use `'All'` as a default. We can safely push
        // undefined if the special properties don't exist.
        window._gaq.push([
            '_trackEvent',
            properties.category || 'All',
            event,
            properties.label
        ]);
    },

    // Pageview
    // --------

    pageview : function (url) {
        // If there isn't a url, that's fine.
        window._gaq.push(['_trackPageview', url]);
    }

});

<commit_msg>Add arguments to ga.js: value and noninteraction<commit_after>// Google Analytics
// ----------------
// [Documentation](https://developers.google.com/analytics/devguides/collection/gajs/).

analytics.addProvider('Google Analytics', {

    settings : {
        anonymizeIp             : false,
        enhancedLinkAttribution : false,
        siteSpeedSampleRate     : null,
        domain                  : null,
        trackingId              : null
    },

    // Initialize
    // ----------

    // Changes to the Google Analytics snippet:
    //
    // * Added `trackingId`.
    // * Added optional support for `enhancedLinkAttribution`
    // * Added optional support for `siteSpeedSampleRate`
    // * Added optional support for `anonymizeIp`
    initialize : function (settings) {
        settings = analytics.utils.resolveSettings(settings, 'trackingId');
        analytics.utils.extend(this.settings, settings);

        var _gaq = window._gaq = window._gaq || [];
        _gaq.push(['_setAccount', this.settings.trackingId]);
        if (this.settings.enhancedLinkAttribution) {
            var pluginUrl = (('https:' == document.location.protocol) ? 'https://www.' : 'http://www.') + 'google-analytics.com/plugins/ga/inpage_linkid.js';
            _gaq.push(['_require', 'inpage_linkid', pluginUrl]);
        }
        if (analytics.utils.isNumber(this.settings.siteSpeedSampleRate)) {
            _gaq.push(['_setSiteSpeedSampleRate', this.settings.siteSpeedSampleRate]);
        }
        if(this.settings.domain) {
            _gaq.push(['_setDomainName', this.settings.domain]);
        }
        if(this.settings.anonymizeIp) {
            _gaq.push(['_gat._anonymizeIp']);
        }
        _gaq.push(['_trackPageview']);

        (function() {
            var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
            ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
            var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
        })();
    },

    // Track
    // -----

    track : function (event, properties) {
        properties || (properties = {});

        // Try to check for a `category` and `label`. A `category` is required,
        // so if it's not there we use `'All'` as a default. We can safely push
        // undefined if the special properties don't exist.
        window._gaq.push([
            '_trackEvent',
            properties.category || 'All',
            event,
            properties.label,
            properties.value,
            properties.noninteraction
        ]);
    },

    // Pageview
    // --------

    pageview : function (url) {
        // If there isn't a url, that's fine.
        window._gaq.push(['_trackPageview', url]);
    }

});

<|endoftext|>


<commit_before>'use strict';

var childProcess = require('child_process');
var moment = require('moment');
var fs = require('fs');

function DatabaseToFileDumper(dumperSettings) {

    this.hosts = dumperSettings.hosts;
    this.authentication = dumperSettings.authentication;
    this.db = dumperSettings.db;
    this.output = dumperSettings.output;
};

DatabaseToFileDumper.prototype.transport = function(){

    var dumperAssets = this.loadAssets();

    childProcess.exec(dumperAssets.command, function (error, stdout, stderr) {

        var output = 'mongodump STDERR: \n' + stderr +'\n\n';

        output += 'mongodump STDOUT: \n' + stdout +'\n\n';

        if (error !== null) {
            output += 'mongodump exec error: \n' + error + '\n\n';

            console.log(output);

            fs.writeFile(dumperAssets.fullPath + '.log', output, function(err) {

                if(err)
                    throw(err);

                console.log("LOG was saved!");
            });
        } else {
            if(dumperAssets.compressionCommand){

                childProcess.exec(dumperAssets.compressionCommand, function (error, stdout, stderr) {

                    output += 'Compression STDERR: \n' + stderr + '\n\n';

                    output += 'Compression STDOUT: \n' + stdout + '\n\n';

                    if (error !== null) {
                        output += 'Compression exec error: \n' + error + '\n\n';
                    }

                    console.log(output);

                    fs.writeFile(dumperAssets.fullPath + '.log', output, function(err) {

                        if(err)
                            throw(err);

                        console.log("LOG was saved!");
                    });
                });
            } else {

                console.log(output);

                fs.writeFile(dumperAssets.fullPath + '.log', output, function(err) {

                    if(err)
                        throw(err);

                    console.log("LOG was saved!");
                });
            }
        }
    });
};

DatabaseToFileDumper.prototype.loadAssets = function(){

    var dumpAssets = { command : 'mongodump' };

    if(process.env.DUMPER_HOSTS){

        dumpAssets.command += ' --host ' + process.env.DUMPER_HOSTS;

    } else if(this.hosts) {

        dumpAssets.command += ' --host ' + this.hosts;
    }

    if(this.authentication || (process.env.DUMPER_AUTH && process.env.DUMPER_AUTH === 'true') ){

        if(process.env.DUMPER_AUTH_DB){

            dumpAssets.command += ' --authenticationDatabase ' + process.env.DUMPER_AUTH_DB;

        } else if(this.authentication && this.authentication.database){

            dumpAssets.command += ' --authenticationDatabase ' + this.authentication.database;

        } else {

            dumpAssets.command += ' --authenticationDatabase admin';
        }

        dumpAssets.command += ' --username ' + process.env.DUMPER_AUTH_USER || this.authentication.user;

        if(process.env.DUMPER_AUTH_PASSWORD){

            dumpAssets.command += ' --password ' + process.env.DUMPER_AUTH_PASSWORD;

        } else if(this.authentication && this.authentication.password){

            dumpAssets.command += ' --password ' + this.authentication.password;
        }
    }

    if(this.db){

        dumpAssets.command += ' --db ' + this.db.name;

        if(this.db.collection){

            dumpAssets.command += ' --collection ' + this.db.collection.name;

            if(this.db.collection.query){

                dumpAssets.command += ' --query ' + this.db.collection.query;
            }
        }
    }

    dumpAssets.command += ' --oplog ';

    if(this.output){
        dumpAssets.fullPath = '';

        if(this.output.filepath) {

            dumpAssets.fullPath = this.output.filepath + '/';
        }

        if(this.output.prefix) {

            dumpAssets.fullPath += this.output.prefix;

        } else {

            dumpAssets.fullPath += 'dump';
        }

        if(this.output.timestampLabel){

            dumpAssets.fullPath += '_' + moment().format(this.output.timestampLabel);
        }

        if(this.output.compression === 'tar.gz'){

            dumpAssets.compressionCommand =
                'tar -zcvf '+dumpAssets.fullPath+'.tar.gz '+
                dumpAssets.fullPath;
        }

        dumpAssets.command += ' --out ' + dumpAssets.fullPath;
    }

    return dumpAssets;
};

module.exports = DatabaseToFileDumper;<commit_msg>Fixing error code when bkp goes wrong<commit_after>'use strict';

var childProcess = require('child_process');
var moment = require('moment');
var fs = require('fs');

function DatabaseToFileDumper(dumperSettings) {

    this.hosts = dumperSettings.hosts;
    this.authentication = dumperSettings.authentication;
    this.db = dumperSettings.db;
    this.output = dumperSettings.output;
};

DatabaseToFileDumper.prototype.transport = function(){

    var dumperAssets = this.loadAssets();

    childProcess.exec(dumperAssets.command, function (error, stdout, stderr) {

        var output = 'mongodump STDERR: \n' + stderr +'\n\n';

        output += 'mongodump STDOUT: \n' + stdout +'\n\n';

        if (error !== null) {
            output += 'mongodump exec error: \n' + error + '\n\n';

            console.log(output);

            fs.writeFile(dumperAssets.fullPath + '.log', output, function(err) {

                if(err)
                    throw(err);

                console.log("LOG was saved!");

                process.exit(1);
            });
        } else {
            if(dumperAssets.compressionCommand){

                childProcess.exec(dumperAssets.compressionCommand, function (error, stdout, stderr) {

                    output += 'Compression STDERR: \n' + stderr + '\n\n';

                    output += 'Compression STDOUT: \n' + stdout + '\n\n';

                    if (error !== null) {
                        output += 'Compression exec error: \n' + error + '\n\n';
                    }

                    console.log(output);

                    fs.writeFile(dumperAssets.fullPath + '.log', output, function(err) {

                        if(err)
                            throw(err);

                        console.log("LOG was saved!");

                        process.exit(1);
                    });
                });
            } else {

                console.log(output);

                fs.writeFile(dumperAssets.fullPath + '.log', output, function(err) {

                    if(err)
                        throw(err);

                    console.log("LOG was saved!");
                });
            }
        }
    });
};

DatabaseToFileDumper.prototype.loadAssets = function(){

    var dumpAssets = { command : 'mongodump' };

    if(process.env.DUMPER_HOSTS){

        dumpAssets.command += ' --host ' + process.env.DUMPER_HOSTS;

    } else if(this.hosts) {

        dumpAssets.command += ' --host ' + this.hosts;
    }

    if(this.authentication || (process.env.DUMPER_AUTH && process.env.DUMPER_AUTH === 'true') ){

        if(process.env.DUMPER_AUTH_DB){

            dumpAssets.command += ' --authenticationDatabase ' + process.env.DUMPER_AUTH_DB;

        } else if(this.authentication && this.authentication.database){

            dumpAssets.command += ' --authenticationDatabase ' + this.authentication.database;

        } else {

            dumpAssets.command += ' --authenticationDatabase admin';
        }

        dumpAssets.command += ' --username ' + process.env.DUMPER_AUTH_USER || this.authentication.user;

        if(process.env.DUMPER_AUTH_PASSWORD){

            dumpAssets.command += ' --password ' + process.env.DUMPER_AUTH_PASSWORD;

        } else if(this.authentication && this.authentication.password){

            dumpAssets.command += ' --password ' + this.authentication.password;
        }
    }

    if(this.db){

        dumpAssets.command += ' --db ' + this.db.name;

        if(this.db.collection){

            dumpAssets.command += ' --collection ' + this.db.collection.name;

            if(this.db.collection.query){

                dumpAssets.command += ' --query ' + this.db.collection.query;
            }
        }
    }

    dumpAssets.command += ' --oplog ';

    if(this.output){
        dumpAssets.fullPath = '';

        if(this.output.filepath) {

            dumpAssets.fullPath = this.output.filepath + '/';
        }

        if(this.output.prefix) {

            dumpAssets.fullPath += this.output.prefix;

        } else {

            dumpAssets.fullPath += 'dump';
        }

        if(this.output.timestampLabel){

            dumpAssets.fullPath += '_' + moment().format(this.output.timestampLabel);
        }

        if(this.output.compression === 'tar.gz'){

            dumpAssets.compressionCommand =
                'tar -zcvf '+dumpAssets.fullPath+'.tar.gz '+
                dumpAssets.fullPath;
        }

        dumpAssets.command += ' --out ' + dumpAssets.fullPath;
    }

    return dumpAssets;
};

module.exports = DatabaseToFileDumper;<|endoftext|>


<commit_before>var crypto = require('crypto');
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var request = require('request-promise');

const SUBDB_API_URL = 'http://api.thesubdb.com';

module.exports = {
  downloadSubtitles: downloadSubtitles
};

/**
 * Download subtitles for the given file.
 *
 * @param {string} file - path to a file
 * @returns {Promise<string>} the subtitles, formatted as .srt
 */
function downloadSubtitles(file) {
  return computeHash(file)
    .then(downloadSubtitlesByHash);
}

/**
 * Create the hash used to identify the file on SubDB.
 *
 * @param {string} file - path to a file
 * @returns {Promise<string>} a hex string representing the MD5 digest of the
 *                            first 64kB and the last 64kB of the given file
 */
function computeHash(file) {
  return fs
    .openAsync(file, 'r')
    .then(function(fileDescriptor) {
      return fs
        .fstatAsync(fileDescriptor)
        .then(function(stats) {
          var fileSize = stats.size;
          var chunkSize = 64 * 1024;
          var buffer = new Buffer(2 * chunkSize);

          var first64kBPromise = fs.readAsync(
            fileDescriptor,
            buffer,              // buffer to write to
            0,                   // offset in the buffer to start writing at
            chunkSize,           // number of bytes to read
            0                    // where to begin reading from in the file
          );
          var last64kBPromise = fs.readAsync(
            fileDescriptor,
            buffer,              // buffer to write to
            chunkSize,           // offset in the buffer to start writing at
            chunkSize,           // number of bytes to read
            fileSize - chunkSize // where to begin reading from in the file
          );

          return Promise
            .all([first64kBPromise, last64kBPromise])
            .then(() => buffer);
        });
    })
    .then(md5hex);
}

/**
 * Compute the MD5 digest of the given Buffer.
 *
 * @param {Buffer} buffer
 * @returns {string} the hexadecimal MD5 hash of the given buffer
 */
function md5hex(buffer) {
  return crypto
    .createHash('md5')
    .update(buffer)
    .digest('hex');
}

/**
 * Download subtitles for the given hash.
 *
 * @param {string} hash - a hex string that identifies a file
 * @returns {Promise<string>} the subtitles, formatted as .srt
 */
function downloadSubtitlesByHash(hash) {
  return request({
    method: 'GET',
    uri: SUBDB_API_URL,
    qs: {
      action: 'download',
      hash: hash,
      language: 'en'
    },
    headers: {
      'User-Agent': 'SubDB/1.0 (subz-hero/0.1; https://github.com/BeLi4L/subz-hero)'
    }
  });
}
<commit_msg>refact(subdb): Extract function<commit_after>var crypto = require('crypto');
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var request = require('request-promise');

const SUBDB_API_URL = 'http://api.thesubdb.com';

module.exports = {
  downloadSubtitles: downloadSubtitles
};

/**
 * Download subtitles for the given file.
 *
 * @param {string} file - path to a file
 * @returns {Promise<string>} the subtitles, formatted as .srt
 */
function downloadSubtitles(file) {
  return computeHash(file)
    .then(downloadSubtitlesByHash);
}

/**
 * Create the hash used to identify the file on SubDB.
 *
 * @param {string} file - path to a file
 * @returns {Promise<string>} a hex string representing the MD5 digest of the
 *                            first 64kB and the last 64kB of the given file
 */
function computeHash(file) {
  return getFileSize(file)
    .then(function(filesize) {
      var chunkSize = 64 * 1024;
      var firstBytesPromise = readBytes({
        file: file,
        start: 0,
        chunkSize: chunkSize
      });
      var lastBytesPromise = readBytes({
        file: file,
        start: filesize - chunkSize,
        chunkSize: chunkSize
      });
      return Promise.all([firstBytesPromise, lastBytesPromise]);
    })
    .then(Buffer.concat)
    .then(md5hex);
}

/**
 * Retrieve the size of a file.
 *
 * @param {string} file - path to a file
 * @returns {Promise<number>} the size of the given file
 */
function getFileSize(file) {
  return fs
    .statAsync(file)
    .then(stats => stats.size);
}

/**
 * Read {chunkSize} bytes from the given {file}, starting from byte number {start}.
 *
 * @param {string} file      - path to a file
 * @param {number} start     - where to start reading from
 * @param {number} chunkSize - number of bytes to read
 * @returns {Promise<Buffer>} a buffer
 */
function readBytes({file, start, chunkSize}) {
  var buffer = new Buffer(chunkSize);
  return fs
    .openAsync(file, 'r')
    .then(function(fileDescriptor) {
      return fs.readAsync(
        fileDescriptor,
        buffer,          // buffer to write to
        0,               // offset in the buffer to start writing at
        chunkSize,       // number of bytes to read
        start            // where to begin reading from in the file
      );
    })
    .then(() => buffer);
}

/**
 * Compute the MD5 digest of the given Buffer.
 *
 * @param {Buffer} buffer
 * @returns {string} the hexadecimal MD5 hash of the given buffer
 */
function md5hex(buffer) {
  return crypto
    .createHash('md5')
    .update(buffer)
    .digest('hex');
}

/**
 * Download subtitles for the given hash.
 *
 * @param {string} hash - a hex string that identifies a file
 * @returns {Promise<string>} the subtitles, formatted as .srt
 */
function downloadSubtitlesByHash(hash) {
  return request({
    method: 'GET',
    uri: SUBDB_API_URL,
    qs: {
      action: 'download',
      hash: hash,
      language: 'en'
    },
    headers: {
      'User-Agent': 'SubDB/1.0 (subz-hero/0.1; https://github.com/BeLi4L/subz-hero)'
    }
  });
}
<|endoftext|>


<commit_before>/**
 * Implements a PostgREST backend
 *
 * See https://github.com/begriffs/postgrest/wiki/Routing
 */
Ext.define('Jarvus.proxy.Postgrest', {
    extend: 'Jarvus.proxy.API',
    alias : 'proxy.postgrest',
    requires: [
        'Jarvus.connection.Postgrest',
        'Jarvus.reader.Postgrest',
        'Jarvus.writer.Postgrest'
    ],

    config: {
        connection: 'Jarvus.connection.Postgrest',

        headers: {
            Prefer: 'return=representation'
        },

        reader: 'postgrest',
        writer: 'postgrest',
        sortParam: 'order'
    },

    /**
     * postgrest supports the following operators:
     * ===========================================
     * eq    | equals
     * gt    | greater than
     * lt    | less than
     * gte   | greater than or equal
     * lte   | less than or equal
     * neq   | not equal
     * like  | LIKE operator (use * in place of %)
     * ilike | ILIKE operator (use * in place of %)
     * is	 | checking for exact equality (null,true,false)
     * isnot |checking for exact inequality
     * in	 | one of a list of values e.g. `?a=in.1,2,3`
     */
    filterOperators: ['eq', 'gt', 'lt', 'gte', 'lte', 'neq', 'like', 'ilike', 'is', 'isnot', 'in'],

    getHeaders: function(request) {
        var headers = this.callParent(),
            action = request.getAction(),
            operation = request.getOperation(),
            page, start, limit;

        if (action == 'read') {
            page = operation.getPage(),
            start = operation.getStart(),
            limit = operation.getLimit();

            if (limit && page) {
                headers['Range-Unit'] = 'items';
                headers['Range'] = ((page - 1) * limit) + '-' + (start + limit - 1);
            } else if(start || start === 0 || limit) {
                headers['Range-Unit'] = 'items';
                headers['Range'] = (start || 0) + '-' + (limit - 1 || '');
            }
        }

        return headers;
    },

    getParams: function(operation) {
        var me = this,
            filterOperators = me.filterOperators,

            action = operation.getAction(),
            isRead = action == 'read',
            records = operation.getRecords(),
            record = records ? records[0] : null,

            sorters = isRead && operation.getSorters(),
            sortParam = me.getSortParam(),

            filters = isRead && operation.getFilters(),
            filtersLength = filters && filters.length, filterIndex = 0,
            filter, filterOperator, filterValue,

            params = {};

        // do not call parent, we don't want any metadata added to the parameters

        // handle remote sorting
        if (sortParam && sorters && sorters.length > 0) {
            params[sortParam] = me.encodeSorters(sorters);
        }

        // write filters
        if (filtersLength) {
            for (; filterIndex < filtersLength; filterIndex++) {
                filter = filters[filterIndex];
                filterOperator = filter.getOperator() || 'eq';
                filterValue = filter.getValue();

                if (filterOperators.indexOf(filterOperator) == -1) {
                    Ext.Error.raise(filterOperators + ' is not in the list of supported filter operators: ' +  me.filterOperators.join(','));
                }

                if (filterOperator == 'in') {
                    if (!Ext.isArray(filterValue)) {
                        Ext.Error.raise('the in filter operator requires values to be an array, not: ' + filterValue);
                    }

                    filterValue = filterValue.join(',');
                }

                params[filter.getProperty()] = filterOperator + '.' + filterValue;
            }
        }

        // target ID for update and destroy ops must be specified as a filter
        if (action == 'update' || action == 'destroy') {
            params[record.getIdProperty()] = 'eq.' + record.getId();
        }

        return params;
    },

    encodeSorters: function(sorters) {
        return sorters.map(function(sorter) {
            var direction = sorter.getDirection(),
                sortStr = sorter.getProperty();

            if (direction) {
                sortStr += '.' + direction.toLowerCase();
            }

            if (sorter.nullsFirst) {
                sortStr += '.nullsfirst';
            }

            if (sorter.nullsLast) {
                sortStr += '.nullslast';
            }

            return sortStr;
        }).join(',');
    },

    buildRequest: function(operation) {
        var me = this,
            request = me.callParent(arguments),
            params = request.getParams(),
            idParam = me.getIdParam(),
            operationId = operation.getId();

        // proxy.Server sets this regardless of idParam's value, see https://www.sencha.com/forum/showthread.php?303751
        if (idParam && operationId && params[idParam] == operationId) {
            params[idParam] = 'eq.' + operationId
        } else if (!idParam && idParam in params) {
            delete params[idParam];
        }

        return request;
    }
});<commit_msg>Move falsey idParam fix upstream to jarvus-apikit package<commit_after>/**
 * Implements a PostgREST backend
 *
 * See https://github.com/begriffs/postgrest/wiki/Routing
 */
Ext.define('Jarvus.proxy.Postgrest', {
    extend: 'Jarvus.proxy.API',
    alias : 'proxy.postgrest',
    requires: [
        'Jarvus.connection.Postgrest',
        'Jarvus.reader.Postgrest',
        'Jarvus.writer.Postgrest'
    ],

    config: {
        connection: 'Jarvus.connection.Postgrest',

        headers: {
            Prefer: 'return=representation'
        },

        reader: 'postgrest',
        writer: 'postgrest',
        sortParam: 'order'
    },

    /**
     * postgrest supports the following operators:
     * ===========================================
     * eq    | equals
     * gt    | greater than
     * lt    | less than
     * gte   | greater than or equal
     * lte   | less than or equal
     * neq   | not equal
     * like  | LIKE operator (use * in place of %)
     * ilike | ILIKE operator (use * in place of %)
     * is	 | checking for exact equality (null,true,false)
     * isnot |checking for exact inequality
     * in	 | one of a list of values e.g. `?a=in.1,2,3`
     */
    filterOperators: ['eq', 'gt', 'lt', 'gte', 'lte', 'neq', 'like', 'ilike', 'is', 'isnot', 'in'],

    getHeaders: function(request) {
        var headers = this.callParent(),
            action = request.getAction(),
            operation = request.getOperation(),
            page, start, limit;

        if (action == 'read') {
            page = operation.getPage(),
            start = operation.getStart(),
            limit = operation.getLimit();

            if (limit && page) {
                headers['Range-Unit'] = 'items';
                headers['Range'] = ((page - 1) * limit) + '-' + (start + limit - 1);
            } else if(start || start === 0 || limit) {
                headers['Range-Unit'] = 'items';
                headers['Range'] = (start || 0) + '-' + (limit - 1 || '');
            }
        }

        return headers;
    },

    getParams: function(operation) {
        var me = this,
            filterOperators = me.filterOperators,

            action = operation.getAction(),
            isRead = action == 'read',
            records = operation.getRecords(),
            record = records ? records[0] : null,

            sorters = isRead && operation.getSorters(),
            sortParam = me.getSortParam(),

            filters = isRead && operation.getFilters(),
            filtersLength = filters && filters.length, filterIndex = 0,
            filter, filterOperator, filterValue,

            params = {};

        // do not call parent, we don't want any metadata added to the parameters

        // handle remote sorting
        if (sortParam && sorters && sorters.length > 0) {
            params[sortParam] = me.encodeSorters(sorters);
        }

        // write filters
        if (filtersLength) {
            for (; filterIndex < filtersLength; filterIndex++) {
                filter = filters[filterIndex];
                filterOperator = filter.getOperator() || 'eq';
                filterValue = filter.getValue();

                if (filterOperators.indexOf(filterOperator) == -1) {
                    Ext.Error.raise(filterOperators + ' is not in the list of supported filter operators: ' +  me.filterOperators.join(','));
                }

                if (filterOperator == 'in') {
                    if (!Ext.isArray(filterValue)) {
                        Ext.Error.raise('the in filter operator requires values to be an array, not: ' + filterValue);
                    }

                    filterValue = filterValue.join(',');
                }

                params[filter.getProperty()] = filterOperator + '.' + filterValue;
            }
        }

        // target ID for update and destroy ops must be specified as a filter
        if (action == 'update' || action == 'destroy') {
            params[record.getIdProperty()] = 'eq.' + record.getId();
        }

        return params;
    },

    encodeSorters: function(sorters) {
        return sorters.map(function(sorter) {
            var direction = sorter.getDirection(),
                sortStr = sorter.getProperty();

            if (direction) {
                sortStr += '.' + direction.toLowerCase();
            }

            if (sorter.nullsFirst) {
                sortStr += '.nullsfirst';
            }

            if (sorter.nullsLast) {
                sortStr += '.nullslast';
            }

            return sortStr;
        }).join(',');
    },

    buildRequest: function(operation) {
        var me = this,
            request = me.callParent(arguments),
            params = request.getParams(),
            idParam = me.getIdParam(),
            operationId = operation.getId();

        // proxy.Server sets this regardless of idParam's value, see https://www.sencha.com/forum/showthread.php?303751
        if (idParam && operationId && params[idParam] == operationId) {
            params[idParam] = 'eq.' + operationId
        }

        return request;
    }
});<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 DataTorrent, Inc. ALL Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

angular.module('ui.dashboard', ['ui.bootstrap', 'ui.sortable']);

angular.module('ui.dashboard')
  .directive('dashboard', ['WidgetModel', 'WidgetDefCollection', '$modal', 'DashboardState', function (WidgetModel, WidgetDefCollection, $modal, DashboardState) {
    return {
      restrict: 'A',
      templateUrl: function(element, attr) {
        return attr.templateUrl ? attr.templateUrl : 'template/dashboard.html';
      },
      scope: true,

      controller: ['$scope', '$attrs', function (scope, attrs) {
        var defaults = {
          stop: function () {
            scope.saveDashboard();
          },
          handle: '.widget-header'
        };
        var options = scope.$eval(attrs.dashboard);
        scope.sortableOptions = angular.extend({}, defaults, options.sortableOptions || {});

      }],
      link: function (scope, element, attrs) {

        // default options
        var defaults = {
          stringifyStorage: true
        };

        scope.options = scope.$eval(attrs.dashboard);

        // from dashboard="options"
        angular.extend(defaults, scope.options);
        angular.extend(scope.options, defaults);

        // Save default widget config for reset
        scope.defaultWidgets = scope.options.defaultWidgets;

        //scope.widgetDefs = scope.options.widgetDefinitions;
        scope.widgetDefs = new WidgetDefCollection(scope.options.widgetDefinitions);
        var count = 1;

        // Instantiate new instance of dashboard state
        scope.dashboardState = new DashboardState(
          scope.options.storage,
          scope.options.storageId,
          scope.options.storageHash,
          scope.widgetDefs,
          scope.options.stringifyStorage
        );

        /**
         * Instantiates a new widget on the dashboard
         * @param {Object} widgetToInstantiate The definition object of the widget to be instantiated
         */
        scope.addWidget = function (widgetToInstantiate, doNotSave) {
          var defaultWidgetDefinition = scope.widgetDefs.getByName(widgetToInstantiate.name);
          if (!defaultWidgetDefinition) {
            throw 'Widget ' + widgetToInstantiate.name + ' is not found.';
          }

          // Determine the title for the new widget
          var title;
          if (widgetToInstantiate.title) {
            title = widgetToInstantiate.title;
          } else if (defaultWidgetDefinition.title) {
            title = defaultWidgetDefinition.title;
          } else {
            title = 'Widget ' + count++;
          }

          // Deep extend a new object for instantiation
          widgetToInstantiate = jQuery.extend(true, {}, defaultWidgetDefinition, widgetToInstantiate);

          // Instantiation
          var widget = new WidgetModel(widgetToInstantiate, {
            title: title
          });

          scope.widgets.push(widget);
          if (!doNotSave) {
            scope.saveDashboard();
          }
        };

        /**
         * Removes a widget instance from the dashboard
         * @param  {Object} widget The widget instance object (not a definition object)
         */
        scope.removeWidget = function (widget) {
          scope.widgets.splice(_.indexOf(scope.widgets, widget), 1);
          scope.saveDashboard();
        };

        /**
         * Opens a dialog for setting and changing widget properties
         * @param  {Object} widget The widget instance object
         */
        scope.openWidgetDialog = function (widget) {
          var options = widget.editModalOptions;

          // use default options when none are supplied by widget
          if (!options) {
            options = {
              templateUrl: 'template/widget-template.html',
              resolve: {
                widget: function () {
                  return widget;
                },
                optionsTemplateUrl: function () {
                  return scope.options.optionsTemplateUrl;
                }
              },
              controller: 'WidgetDialogCtrl'
            };
          }
          var modalInstance = $modal.open(options);

          // Set resolve and reject callbacks for the result promise
          modalInstance.result.then(
            function (result) {
              console.log('widget dialog closed');
              console.log('result: ', result);
              widget.title = result.title;
              //AW Persist title change from options editor
              scope.$emit('widgetChanged', widget);
            },
            function (reason) {
              console.log('widget dialog dismissed: ', reason);

            }
          );

        };

        /**
         * Remove all widget instances from dashboard
         */
        scope.clear = function (doNotSave) {
          scope.widgets = [];
          if (doNotSave === true) {
            return;
          }
          scope.saveDashboard();
        };

        /**
         * Used for preventing default on click event
         * @param {Object} event     A click event
         * @param {Object} widgetDef A widget definition object
         */
        scope.addWidgetInternal = function (event, widgetDef) {
          event.preventDefault();
          scope.addWidget(widgetDef);
        };

        /**
         * Uses dashboardState service to save state
         */
        scope.saveDashboard = function (force) {
          if (!scope.options.explicitSave) {
            scope.dashboardState.save(scope.widgets);
          } else {
            if (typeof scope.options.unsavedChangeCount !== 'number') {
              scope.options.unsavedChangeCount = 0;
            }
            if (force) {
              scope.options.unsavedChangeCount = 0;
              scope.dashboardState.save(scope.widgets);

            } else {
              ++scope.options.unsavedChangeCount;
            }
          }
        };

        /**
         * Wraps saveDashboard for external use.
         */
        scope.externalSaveDashboard = function() {
          scope.saveDashboard(true);
        };

        /**
         * Clears current dash and instantiates widget definitions
         * @param  {Array} widgets Array of definition objects
         */
        scope.loadWidgets = function (widgets) {
          // AW dashboards are continuously saved today (no "save" button).
          //scope.defaultWidgets = widgets;
          scope.savedWidgetDefs = widgets;
          scope.clear(true);
          _.each(widgets, function (widgetDef) {
            scope.addWidget(widgetDef, true);
          });
        };

        /**
         * Resets widget instances to default config
         * @return {[type]} [description]
         */
        scope.resetWidgetsToDefault = function () {
          scope.loadWidgets(scope.defaultWidgets);
          scope.saveDashboard();
        };

        // Set default widgets array
        var savedWidgetDefs = scope.dashboardState.load();

        // Success handler
        function handleStateLoad(saved) {
          scope.options.unsavedChangeCount = 0;
          if (saved && saved.length) {
            scope.loadWidgets(saved);
          } else if (scope.defaultWidgets) {
            scope.loadWidgets(scope.defaultWidgets);
          } else {
            scope.clear(true);
          }
        }

        if (savedWidgetDefs instanceof Array) {
          handleStateLoad(savedWidgetDefs);
        }
        else if (savedWidgetDefs && typeof savedWidgetDefs === 'object' && typeof savedWidgetDefs.then === 'function') {
          savedWidgetDefs.then(handleStateLoad, handleStateLoad);
        }
        else {
          handleStateLoad();
        }

        // expose functionality externally
        // functions are appended to the provided dashboard options
        scope.options.addWidget = scope.addWidget;
        scope.options.loadWidgets = scope.loadWidgets;
        scope.options.saveDashboard = scope.externalSaveDashboard;

        // save state
        scope.$on('widgetChanged', function (event) {
          event.stopPropagation();
          scope.saveDashboard();
        });
      }
    };
  }]);
<commit_msg>Moving dashboardOptions to controller so that there is no duplicate code<commit_after>/*
 * Copyright (c) 2014 DataTorrent, Inc. ALL Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

angular.module('ui.dashboard', ['ui.bootstrap', 'ui.sortable']);

angular.module('ui.dashboard')
  .directive('dashboard', ['WidgetModel', 'WidgetDefCollection', '$modal', 'DashboardState', function (WidgetModel, WidgetDefCollection, $modal, DashboardState) {
    return {
      restrict: 'A',
      templateUrl: function(element, attr) {
        return attr.templateUrl ? attr.templateUrl : 'template/dashboard.html';
      },
      scope: true,

      controller: ['$scope', '$attrs', function (scope, attrs) {
        // default options
        var defaults = {
          stringifyStorage: true
        };

        scope.options = scope.$eval(attrs.dashboard);

        // from dashboard="options"
        angular.extend(defaults, scope.options);
        angular.extend(scope.options, defaults);

        var sortableDefaults = {
          stop: function () {
            scope.saveDashboard();
          },
          handle: '.widget-header'
        };
        scope.sortableOptions = angular.extend({}, sortableDefaults, scope.options.sortableOptions || {});

      }],
      link: function (scope) {

        // Save default widget config for reset
        scope.defaultWidgets = scope.options.defaultWidgets;

        //scope.widgetDefs = scope.options.widgetDefinitions;
        scope.widgetDefs = new WidgetDefCollection(scope.options.widgetDefinitions);
        var count = 1;

        // Instantiate new instance of dashboard state
        scope.dashboardState = new DashboardState(
          scope.options.storage,
          scope.options.storageId,
          scope.options.storageHash,
          scope.widgetDefs,
          scope.options.stringifyStorage
        );

        /**
         * Instantiates a new widget on the dashboard
         * @param {Object} widgetToInstantiate The definition object of the widget to be instantiated
         */
        scope.addWidget = function (widgetToInstantiate, doNotSave) {
          var defaultWidgetDefinition = scope.widgetDefs.getByName(widgetToInstantiate.name);
          if (!defaultWidgetDefinition) {
            throw 'Widget ' + widgetToInstantiate.name + ' is not found.';
          }

          // Determine the title for the new widget
          var title;
          if (widgetToInstantiate.title) {
            title = widgetToInstantiate.title;
          } else if (defaultWidgetDefinition.title) {
            title = defaultWidgetDefinition.title;
          } else {
            title = 'Widget ' + count++;
          }

          // Deep extend a new object for instantiation
          widgetToInstantiate = jQuery.extend(true, {}, defaultWidgetDefinition, widgetToInstantiate);

          // Instantiation
          var widget = new WidgetModel(widgetToInstantiate, {
            title: title
          });

          scope.widgets.push(widget);
          if (!doNotSave) {
            scope.saveDashboard();
          }
        };

        /**
         * Removes a widget instance from the dashboard
         * @param  {Object} widget The widget instance object (not a definition object)
         */
        scope.removeWidget = function (widget) {
          scope.widgets.splice(_.indexOf(scope.widgets, widget), 1);
          scope.saveDashboard();
        };

        /**
         * Opens a dialog for setting and changing widget properties
         * @param  {Object} widget The widget instance object
         */
        scope.openWidgetDialog = function (widget) {
          var options = widget.editModalOptions;

          // use default options when none are supplied by widget
          if (!options) {
            options = {
              templateUrl: 'template/widget-template.html',
              resolve: {
                widget: function () {
                  return widget;
                },
                optionsTemplateUrl: function () {
                  return scope.options.optionsTemplateUrl;
                }
              },
              controller: 'WidgetDialogCtrl'
            };
          }
          var modalInstance = $modal.open(options);

          // Set resolve and reject callbacks for the result promise
          modalInstance.result.then(
            function (result) {
              console.log('widget dialog closed');
              console.log('result: ', result);
              widget.title = result.title;
              //AW Persist title change from options editor
              scope.$emit('widgetChanged', widget);
            },
            function (reason) {
              console.log('widget dialog dismissed: ', reason);

            }
          );

        };

        /**
         * Remove all widget instances from dashboard
         */
        scope.clear = function (doNotSave) {
          scope.widgets = [];
          if (doNotSave === true) {
            return;
          }
          scope.saveDashboard();
        };

        /**
         * Used for preventing default on click event
         * @param {Object} event     A click event
         * @param {Object} widgetDef A widget definition object
         */
        scope.addWidgetInternal = function (event, widgetDef) {
          event.preventDefault();
          scope.addWidget(widgetDef);
        };

        /**
         * Uses dashboardState service to save state
         */
        scope.saveDashboard = function (force) {
          if (!scope.options.explicitSave) {
            scope.dashboardState.save(scope.widgets);
          } else {
            if (typeof scope.options.unsavedChangeCount !== 'number') {
              scope.options.unsavedChangeCount = 0;
            }
            if (force) {
              scope.options.unsavedChangeCount = 0;
              scope.dashboardState.save(scope.widgets);

            } else {
              ++scope.options.unsavedChangeCount;
            }
          }
        };

        /**
         * Wraps saveDashboard for external use.
         */
        scope.externalSaveDashboard = function() {
          scope.saveDashboard(true);
        };

        /**
         * Clears current dash and instantiates widget definitions
         * @param  {Array} widgets Array of definition objects
         */
        scope.loadWidgets = function (widgets) {
          // AW dashboards are continuously saved today (no "save" button).
          //scope.defaultWidgets = widgets;
          scope.savedWidgetDefs = widgets;
          scope.clear(true);
          _.each(widgets, function (widgetDef) {
            scope.addWidget(widgetDef, true);
          });
        };

        /**
         * Resets widget instances to default config
         * @return {[type]} [description]
         */
        scope.resetWidgetsToDefault = function () {
          scope.loadWidgets(scope.defaultWidgets);
          scope.saveDashboard();
        };

        // Set default widgets array
        var savedWidgetDefs = scope.dashboardState.load();

        // Success handler
        function handleStateLoad(saved) {
          scope.options.unsavedChangeCount = 0;
          if (saved && saved.length) {
            scope.loadWidgets(saved);
          } else if (scope.defaultWidgets) {
            scope.loadWidgets(scope.defaultWidgets);
          } else {
            scope.clear(true);
          }
        }

        if (savedWidgetDefs instanceof Array) {
          handleStateLoad(savedWidgetDefs);
        }
        else if (savedWidgetDefs && typeof savedWidgetDefs === 'object' && typeof savedWidgetDefs.then === 'function') {
          savedWidgetDefs.then(handleStateLoad, handleStateLoad);
        }
        else {
          handleStateLoad();
        }

        // expose functionality externally
        // functions are appended to the provided dashboard options
        scope.options.addWidget = scope.addWidget;
        scope.options.loadWidgets = scope.loadWidgets;
        scope.options.saveDashboard = scope.externalSaveDashboard;

        // save state
        scope.$on('widgetChanged', function (event) {
          event.stopPropagation();
          scope.saveDashboard();
        });
      }
    };
  }]);
<|endoftext|>


<commit_before>import { on, off, warn } from '../../util/index'
import { ON } from '../priorities'

// keyCode aliases
const keyCodes = {
  esc: 27,
  tab: 9,
  enter: 13,
  space: 32,
  'delete': [8, 46],
  up: 38,
  left: 37,
  right: 39,
  down: 40
}

function keyFilter (handler, keys) {
  var codes = keys.map(function (key) {
    var charCode = key.charCodeAt(0)
    if (charCode > 47 && charCode < 58) {
      return parseInt(key, 10)
    }
    if (key.length === 1) {
      charCode = key.toUpperCase().charCodeAt(0)
      if (charCode > 64 && charCode < 91) {
        return charCode
      }
    }
    return keyCodes[key]
  })
  codes = [].concat.apply([], codes)
  return function keyHandler (e) {
    if (codes.indexOf(e.keyCode) > -1) {
      return handler.call(this, e)
    }
  }
}

function stopFilter (handler) {
  return function stopHandler (e) {
    e.stopPropagation()
    return handler.call(this, e)
  }
}

function preventFilter (handler) {
  return function preventHandler (e) {
    e.preventDefault()
    return handler.call(this, e)
  }
}

function selfFilter (handler) {
  return function selfHandler (e) {
    if (e.target === e.currentTarget) {
      return handler.call(this, e)
    }
  }
}

export default {

  acceptStatement: true,
  priority: ON,

  bind () {
    // deal with iframes
    if (
      this.el.tagName === 'IFRAME' &&
      this.arg !== 'load'
    ) {
      var self = this
      this.iframeBind = function () {
        on(
          self.el.contentWindow,
          self.arg,
          self.handler,
          self.modifiers.capture
        )
      }
      this.on('load', this.iframeBind)
    }
  },

  update (handler) {
    // stub a noop for v-on with no value,
    // e.g. @mousedown.prevent
    if (!this.descriptor.raw) {
      handler = function () {}
    }

    if (typeof handler !== 'function') {
      process.env.NODE_ENV !== 'production' && warn(
        'v-on:' + this.arg + '="' +
        this.expression + '" expects a function value, ' +
        'got ' + handler
      )
      return
    }

    // apply modifiers
    if (this.modifiers.stop) {
      handler = stopFilter(handler)
    }
    if (this.modifiers.prevent) {
      handler = preventFilter(handler)
    }
    if (this.modifiers.self) {
      handler = selfFilter(handler)
    }
    // key filter
    var keys = Object.keys(this.modifiers)
      .filter(function (key) {
        return key !== 'stop' && key !== 'prevent'
      })
    if (keys.length) {
      handler = keyFilter(handler, keys)
    }

    this.reset()
    this.handler = handler

    if (this.iframeBind) {
      this.iframeBind()
    } else {
      on(
        this.el,
        this.arg,
        this.handler,
        this.modifiers.capture
      )
    }
  },

  reset () {
    var el = this.iframeBind
      ? this.el.contentWindow
      : this.el
    if (this.handler) {
      off(el, this.arg, this.handler)
    }
  },

  unbind () {
    this.reset()
  }
}
<commit_msg>expose keycodes on v-on<commit_after>import { on, off, warn } from '../../util/index'
import { ON } from '../priorities'

// keyCode aliases
const keyCodes = {
  esc: 27,
  tab: 9,
  enter: 13,
  space: 32,
  'delete': [8, 46],
  up: 38,
  left: 37,
  right: 39,
  down: 40
}

function keyFilter (handler, keys) {
  var codes = keys.map(function (key) {
    var charCode = key.charCodeAt(0)
    if (charCode > 47 && charCode < 58) {
      return parseInt(key, 10)
    }
    if (key.length === 1) {
      charCode = key.toUpperCase().charCodeAt(0)
      if (charCode > 64 && charCode < 91) {
        return charCode
      }
    }
    return keyCodes[key]
  })
  codes = [].concat.apply([], codes)
  return function keyHandler (e) {
    if (codes.indexOf(e.keyCode) > -1) {
      return handler.call(this, e)
    }
  }
}

function stopFilter (handler) {
  return function stopHandler (e) {
    e.stopPropagation()
    return handler.call(this, e)
  }
}

function preventFilter (handler) {
  return function preventHandler (e) {
    e.preventDefault()
    return handler.call(this, e)
  }
}

function selfFilter (handler) {
  return function selfHandler (e) {
    if (e.target === e.currentTarget) {
      return handler.call(this, e)
    }
  }
}

export default {

  priority: ON,
  acceptStatement: true,
  keyCodes,

  bind () {
    // deal with iframes
    if (
      this.el.tagName === 'IFRAME' &&
      this.arg !== 'load'
    ) {
      var self = this
      this.iframeBind = function () {
        on(
          self.el.contentWindow,
          self.arg,
          self.handler,
          self.modifiers.capture
        )
      }
      this.on('load', this.iframeBind)
    }
  },

  update (handler) {
    // stub a noop for v-on with no value,
    // e.g. @mousedown.prevent
    if (!this.descriptor.raw) {
      handler = function () {}
    }

    if (typeof handler !== 'function') {
      process.env.NODE_ENV !== 'production' && warn(
        'v-on:' + this.arg + '="' +
        this.expression + '" expects a function value, ' +
        'got ' + handler
      )
      return
    }

    // apply modifiers
    if (this.modifiers.stop) {
      handler = stopFilter(handler)
    }
    if (this.modifiers.prevent) {
      handler = preventFilter(handler)
    }
    if (this.modifiers.self) {
      handler = selfFilter(handler)
    }
    // key filter
    var keys = Object.keys(this.modifiers)
      .filter(function (key) {
        return key !== 'stop' && key !== 'prevent'
      })
    if (keys.length) {
      handler = keyFilter(handler, keys)
    }

    this.reset()
    this.handler = handler

    if (this.iframeBind) {
      this.iframeBind()
    } else {
      on(
        this.el,
        this.arg,
        this.handler,
        this.modifiers.capture
      )
    }
  },

  reset () {
    var el = this.iframeBind
      ? this.el.contentWindow
      : this.el
    if (this.handler) {
      off(el, this.arg, this.handler)
    }
  },

  unbind () {
    this.reset()
  }
}
<|endoftext|>