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

import nl from 'js-webdav-client';
import { extractParentDirectoryPath } from '../utils/paths';

export default class WebDAVIO {
  davClient: any;

  constructor() {
    console.log('Loading web.js..' + nl);
    // exact copy of getAjax with timeout added
    nl.sara.webdav.Client.prototype.getAjax = (
      method,
      url,
      callback,
      headers
    ) => {
      let /** @type XMLHttpRequest */ ajax =
        typeof Components !== 'undefined' &&
        typeof Components.classes !== 'undefined'
          ? Components.classes[
              '@mozilla.org/xmlextras/xmlhttprequest;1'
            ].createInstance(Components.interfaces.nsIXMLHttpRequest)
          : new XMLHttpRequest();
      if (this._username !== null) {
        ajax.open(method, url, true, this._username, this._password);
      } else {
        ajax.open(method, url, true);
      }
      ajax.onreadystatechange = () => {
        nl.sara.webdav.Client.ajaxHandler(ajax, callback);
      };

      ajax.ontimeout = () => {
        ajax.readyState = 4;
        ajax.ajax.status = -1;
        nl.sara.webdav.Client.ajaxHandler(ajax, callback);
      };

      if (headers === undefined) {
        headers = {};
      }
      for (let header in this._headers) {
        if (headers[header] === undefined) {
          ajax.setRequestHeader(header, this._headers[header]);
        }
      }
      for (let header in headers) {
        ajax.setRequestHeader(header, headers[header]);
      }
      return ajax;
    };

    console.log('Connecting webdav...');
    let useHTTPS = false;
    if (location.href.indexOf('https') === 0) {
      useHTTPS = true;
    }
    this.davClient = new nl.sara.webdav.Client(
      location.hostname,
      useHTTPS,
      location.port
    );
  }

  isWorkerAvailable = (): boolean => false;

  getDevicePaths = (): Object => {
    const paths = {
      Home: '/files/',
    };
    return paths;
  };

  getAppDataPath = (): string => {
    // TODO
    return 'SOMEPATH_FIX_ME';
  };

  getUserHomePath = (): string => {
    return '/';
  };

  getNameForPath = (entrypath: string, separator = '/') => {
    let path = entrypath;
    if (path.lastIndexOf(separator) === path.length - 1) {
      path = path.substring(0, path.lastIndexOf(separator));
    }
    const encodedName = path.substring(path.lastIndexOf(separator) + 1, path.length);
    return decodeURI(encodedName);
  };

  isDirectory = (path: string): boolean => {
    // TODO find a better solution
    return path.lastIndexOf('/') === path.length - 1;
  };

  checkStatusCode = (code: number): boolean => {
    const status = parseInt(code / 100);
    if (status === 2) {
      return true;
    }
    return false;
  };

  // TODO
  handleStartParameters = (): void => {
    /* let filePath = getURLParameter('open');
    if (filePath && filePath.length > 0) {
      filePath = decodeURIComponent(filePath);
      console.log('Opening file in browser: ' + filePath);
      FileOpener.openFileOnStartup(filePath);
    } */
  };

  /**
   * Bring the TagSpaces window on top of the windows
   */
  focusWindow = (): void => {
    window.focus();
  };

  /**
   * Checks if new version is available
   */
  checkNewVersion = () => {
    // TODO
    console.log('Checking for new version not implemented');
  };

  /**
   * Creates recursively a tree structure for a given directory path
   * @param {string} dirPath - the full path of the directory for which the tree will be generated
   */
  createDirectoryTree = (dirPath: string) => {
    console.log('Creating directory index for: ' + dirPath);
    const directoyTree = [];
    return directoyTree;
  };

  /**
   * Creates a list with containing the files and the sub directories of a given directory
   */
  listDirectoryPromise = (directoryPath: string) => {
    let dirPath = directoryPath.split('//').join('/');
    console.log('Listing directory: ' + dirPath);
    return new Promise((resolve, reject) => {
      let anotatedDirList;

      const davSuccess = (status, data) => {
        console.log('Dirlist Status:  ' + status);
        if (!this.checkStatusCode(status)) {
          console.warn('Listing directory ' + dirPath + ' failed ' + status);
          reject('Listing directory ' + dirPath + ' failed ' + status);
        }
        let dirList = data._responses;
        let fileName, isDir, filesize, lmdt, path;

        anotatedDirList = [];
        for (let entry in dirList) {
          path = dirList[entry].href;
          if (dirPath.toLowerCase() === path.toLowerCase()) {
            console.log('Skipping current folder');
          } else {
            isDir = false;
            filesize = undefined;
            lmdt = undefined;
            // console.log(dirList[entry]._namespaces['DAV:']);
            if (
              typeof dirList[entry]._namespaces['DAV:'].getcontentlength ===
                'undefined' ||
              dirList[entry]._namespaces['DAV:'].getcontentlength._xmlvalue
                .length === 0 ||
              (dirList[entry]._namespaces['DAV:'].resourcetype._xmlvalue
                .length === 1 &&
                dirList[entry]._namespaces['DAV:'].resourcetype._xmlvalue[0]
                  .localName === 'collection')
            ) {
              isDir = true;
            } else {
              filesize =
                dirList[entry]._namespaces['DAV:'].getcontentlength._xmlvalue[0]
                  .data;
              lmdt =
                data._responses[entry]._namespaces['DAV:'].getlastmodified
                  ._xmlvalue[0].data;
            }
            fileName = this.getNameForPath(path);
            anotatedDirList.push({
              name: fileName,
              isFile: !isDir,
              size: filesize,
              lmdt: lmdt,
              path: decodeURI(path)
            });
          }
        }
        resolve(anotatedDirList);
      };

      if (dirPath.substring(dirPath.length - 1) !== '/') {
        dirPath = dirPath + '/';
      }
      dirPath = encodeURI(dirPath);

      this.davClient.propfind(
        dirPath,
        davSuccess,
        1 // 1 , davClient.INFINITY
      );
    });
  };

  /**
   * Finds out the properties of a file or directory such last modification date or file size
   */
  getPropertiesPromise = (filePath: string): Promise<Object> => {
    return new Promise((resolve, reject) => {
      this.davClient.propfind(
        encodeURI(filePath),
        (status, data) => {
          console.log(
            'Properties Status / Content: ' +
              status +
              ' / ' +
              JSON.stringify(data._responses)
          );
          const fileProperties = {};
          if (this.checkStatusCode(status)) {
            for (let entry in data._responses) {
              fileProperties.path = filePath;
              fileProperties.size =
                data._responses[entry]._namespaces['DAV:'].getcontentlength;
              fileProperties.lmdt =
                data._responses[entry]._namespaces[
                  'DAV:'
                ].getlastmodified._xmlvalue[0].data;
            }
            resolve(fileProperties);
          } else {
            reject('getFileProperties ' + filePath + ' failed ' + status);
          }
        },
        1
      );
    });
  };

  /**
   * Load the content of a text file
   */
  loadTextFilePromise = (filePath: string) =>
    this.getFileContentPromise(filePath, 'text');

  /**
   * Gets the content of file, useful for binary files
   */
  getFileContentPromise = (filePath: string, type): Promise<Object> => {
    console.log('getFileContent file: ' + filePath);
    return new Promise((resolve, reject) => {
      const ajax = this.davClient.getAjax('GET', encodeURI(filePath));
      ajax.onreadystatechange = null;
      ajax.responseType = type || 'arraybuffer';
      ajax.onerror = reject;

      ajax.onload = () => {
        const response = ajax.response || ajax.responseText;
        if (this.checkStatusCode(ajax.status)) {
          resolve(response);
        } else {
          reject('getFileContentPromise ajax error');
        }
      };
      ajax.send();
    });
  };

  /**
   * Persists a given content(binary supported) to a specified filepath
   */
  saveFilePromise = (
    filePath: string,
    content: string,
    overWrite: boolean,
    mode: string
  ): Promise<Object> => {
    return new Promise((resolve, reject) => {
      let isNewFile = false;
      this.davClient.propfind(
        encodeURI(filePath),
        (status, data) => {
          console.log(
            'Check file exists: Status / Content: ' + status + ' / ' + data
          );
          if (parseInt(status) === 404) {
            isNewFile = true;
          }
          if (isNewFile || overWrite === true || mode === 'text') {
            this.davClient.put(
              encodeURI(filePath),
              (status, data, headers) => {
                console.log(
                  'Creating File Status/Content/Headers:  ' +
                    status +
                    ' / ' +
                    data +
                    ' / ' +
                    headers
                );
                if (this.checkStatusCode(status)) {
                  resolve(isNewFile);
                } else {
                  reject('saveFilePromise: ' + filePath + ' failed ' + status);
                }
              },
              content,
              'application/octet-stream'
            );
          } else {
            reject('File Already Exists.');
          }
        },
        1
      );
    });
  };

  /**
   * Persists a given text content to a specified filepath
   */
  saveTextFilePromise(filePath: string, content: string, overWrite: boolean): Promise<Object> {
    console.log('Saving text file: ' + filePath);
    return this.saveFilePromise(filePath, content, overWrite, 'text');
  }

  /**
   * Persists a given binary content to a specified filepath
   */
  saveBinaryFilePromise(filePath: string, content: string, overWrite: boolean): Promise<Object> {
    console.log('Saving binary file: ' + filePath);
    return this.saveFilePromise(filePath, content, overWrite);
  }

  /**
   * Creates a directory
   */
  createDirectoryPromise = (dirPath: string): Promise<Object> => {
    console.log('Creating directory: ' + dirPath);
    return new Promise((resolve, reject) => {
      this.davClient.mkcol(encodeURI(dirPath), (status, data, headers) => {
        console.log(
          'Directory Creation Status/Content/Headers:  ' +
            status +
            ' / ' +
            data +
            ' / ' +
            headers
        );
        if (this.checkStatusCode(status)) {
          resolve(dirPath);
        } else {
          reject('createDirectory ' + dirPath + ' failed ' + status);
        }
      });
    });
  }

  /**
   * Copies a given file to a specified location
   */
  copyFilePromise = (filePath: string, newFilePath: string): Promise<Object> => {
    console.log('Copying file: ' + filePath + ' to ' + newFilePath);
    return new Promise((resolve, reject) => {
      if (filePath.toLowerCase() === newFilePath.toLowerCase()) {
        reject('Copying file failed, files have the same path');
      } else {
        this.davClient.copy(
          encodeURI(filePath),
          (status, data, headers) => {
            console.log(
              'Copy File Status/Content/Headers:  ' +
                status +
                ' / ' +
                data +
                ' / ' +
                headers
            );
            if (this.checkStatusCode(status)) {
              resolve(filePath, newFilePath);
            } else {
              reject('copyFile ' + filePath + ' failed ' + status);
            }
          },
          encodeURI(newFilePath),
          this.davClient.FAIL_ON_OVERWRITE
        );
      }
    });
  }

  /**
   * Renames a given file
   */
  renameFilePromise = (filePath: string, newFilePath: string): Promise<Object> => {
    console.log('Renaming file: ' + filePath + ' to ' + newFilePath);
    return new Promise((resolve, reject) => {
      if (filePath === newFilePath) {
        reject('Renaming file failed, files have the same path');
      } else {
        this.davClient.move(
          encodeURI(filePath),
          (status, data, headers) => {
            console.log(
              'Rename File Status/Content/Headers:  ' +
                status +
                ' / ' +
                data +
                ' / ' +
                headers
            );
            if (this.checkStatusCode(status)) {
              resolve([filePath, newFilePath]);
            } else {
              reject('rename: ' + filePath + ' failed ' + status);
            }
          },
          encodeURI(newFilePath),
          this.davClient.FAIL_ON_OVERWRITE
        );
      }
    });
  }

  /**
   * Rename a directory
   */
  renameDirectoryPromise = (dirPath: string, newDirectoryPath: string): Promise<Object> => {
    const newDirPath =
      extractParentDirectoryPath(dirPath) + '/' + newDirectoryPath;
    console.log('Renaming directory: ' + dirPath + ' to ' + newDirPath);
    return new Promise((resolve, reject) => {
      if (dirPath === newDirPath) {
        reject('Renaming directory failed, directories have the same path');
      } else {
        this.davClient.move(
          encodeURI(dirPath),
          (status, data, headers) => {
            console.log(
              'Rename Directory Status/Content/Headers:  ' +
                status +
                ' / ' +
                data +
                ' / ' +
                headers
            );
            if (this.checkStatusCode(status)) {
              resolve(newDirPath);
            } else {
              reject('rename: ' + dirPath + ' failed ' + status);
            }
          },
          encodeURI(newDirPath),
          this.davClient.FAIL_ON_OVERWRITE
        );
      }
    });
  };

  /**
   * Delete a specified file
   */
  deleteFilePromise = (path: string): Promise<Object> => this.deleteDirectoryPromise(path);

  /**
   * Delete a specified directory, the directory should be empty, if the trash can functionality is not enabled
   */
  deleteDirectoryPromise = (path: string): Promise<Object> =>
    new Promise((resolve, reject) => {
      this.davClient.remove(encodeURI(path), (status, data, headers) => {
        console.log(
          'Directory/File Deletion Status/Content/Headers:  ' +
            status +
            ' / ' +
            data +
            ' / ' +
            headers
        );
        if (this.checkStatusCode(status)) {
          resolve(path);
        } else {
          reject('delete ' + path + ' failed ' + status);
        }
      });
    });

  /**
   * Choosing directory
   */
  selectDirectoryDialog = () => {
    console.log('Select directory is not implemented in the webdav edition');
  };

  /**
   * Choosing file
   */
  selectFileDialog = () => {
    console.log('selectFile not relevant for the web version');
  };

  /**
   * Not possible in the WebDAV version
   */
  openDirectory = (dirPath: string) => {
    console.log(
      'openDirectory ' + dirPath + ' not possible for the web version.'
    );
  };

  /**
   * Open the file url in a new tab
   */
  openFile = (filePath: string): void => {
    window.open(filePath, '_blank');
  };
}
<commit_msg>improving logging and comments<commit_after>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import nl from 'js-webdav-client';
import { extractParentDirectoryPath } from '../utils/paths';

export default class WebDAVIO {
  davClient: any;

  constructor() {
    console.log('Loading web.js..' + nl);
    // exact copy of getAjax with timeout added
    nl.sara.webdav.Client.prototype.getAjax = (
      method,
      url,
      callback,
      headers
    ) => {
      let /** @type XMLHttpRequest */ ajax =
        typeof Components !== 'undefined' &&
        typeof Components.classes !== 'undefined'
          ? Components.classes[
              '@mozilla.org/xmlextras/xmlhttprequest;1'
            ].createInstance(Components.interfaces.nsIXMLHttpRequest)
          : new XMLHttpRequest();
      if (this._username !== null) {
        ajax.open(method, url, true, this._username, this._password);
      } else {
        ajax.open(method, url, true);
      }
      ajax.onreadystatechange = () => {
        nl.sara.webdav.Client.ajaxHandler(ajax, callback);
      };

      ajax.ontimeout = () => {
        ajax.readyState = 4;
        ajax.ajax.status = -1;
        nl.sara.webdav.Client.ajaxHandler(ajax, callback);
      };

      if (headers === undefined) {
        headers = {};
      }
      for (let header in this._headers) {
        if (headers[header] === undefined) {
          ajax.setRequestHeader(header, this._headers[header]);
        }
      }
      for (let header in headers) {
        ajax.setRequestHeader(header, headers[header]);
      }
      return ajax;
    };

    console.log('Connecting webdav...');
    let useHTTPS = false;
    if (location.href.indexOf('https') === 0) {
      useHTTPS = true;
    }
    this.davClient = new nl.sara.webdav.Client(
      location.hostname,
      useHTTPS,
      location.port
    );
  }

  isWorkerAvailable = (): boolean => false;

  getDevicePaths = (): Object => {
    const paths = {
      Home: '/files/',
    };
    return paths;
  };

  getAppDataPath = (): string => {
    // TODO
    return 'SOMEPATH_FIX_ME';
  };

  getUserHomePath = (): string => {
    return '/';
  };

  getNameForPath = (entrypath: string, separator = '/') => {
    let path = entrypath;
    if (path.lastIndexOf(separator) === path.length - 1) {
      path = path.substring(0, path.lastIndexOf(separator));
    }
    const encodedName = path.substring(path.lastIndexOf(separator) + 1, path.length);
    return decodeURI(encodedName);
  };

  isDirectory = (path: string): boolean => {
    // TODO find a better solution
    return path.lastIndexOf('/') === path.length - 1;
  };

  checkStatusCode = (code: number): boolean => {
    const status = parseInt(code / 100);
    if (status === 2) {
      return true;
    }
    return false;
  };

  // TODO
  handleStartParameters = (): void => {
    /* let filePath = getURLParameter('open');
    if (filePath && filePath.length > 0) {
      filePath = decodeURIComponent(filePath);
      console.log('Opening file in browser: ' + filePath);
      FileOpener.openFileOnStartup(filePath);
    } */
  };

  /**
   * Bring the TagSpaces window on top of the windows
   */
  focusWindow = (): void => {
    window.focus();
  };

  /**
   * Checks if new version is available
   */
  checkNewVersion = () => {
    // TODO
    console.log('Checking for new version not implemented');
  };

  /**
   * Creates recursively a tree structure for a given directory path
   * @param {string} dirPath - the full path of the directory for which the tree will be generated
   */
  createDirectoryTree = (dirPath: string) => {
    console.log('Creating directory index for: ' + dirPath);
    const directoyTree = [];
    return directoyTree;
  };

  /**
   * Creates a list with containing the files and the sub directories of a given directory
   */
  listDirectoryPromise = (directoryPath: string) => {
    let dirPath = directoryPath.split('//').join('/');
    console.log('Listing directory: ' + dirPath);
    return new Promise((resolve, reject) => {
      let anotatedDirList;

      const davSuccess = (status, data) => {
        console.log('Dirlist Status:  ' + status);
        if (!this.checkStatusCode(status)) {
          console.warn('Listing directory ' + dirPath + ' failed ' + status);
          reject('Listing directory ' + dirPath + ' failed ' + status);
        }
        let dirList = data._responses;
        let fileName, isDir, filesize, lmdt, path;

        anotatedDirList = [];
        for (let entry in dirList) {
          path = dirList[entry].href;
          if (dirPath.toLowerCase() === path.toLowerCase()) {
            console.log('Skipping current folder');
          } else {
            isDir = false;
            filesize = undefined;
            lmdt = undefined;
            // console.log(dirList[entry]._namespaces['DAV:']);
            if (
              typeof dirList[entry]._namespaces['DAV:'].getcontentlength ===
                'undefined' ||
              dirList[entry]._namespaces['DAV:'].getcontentlength._xmlvalue
                .length === 0 ||
              (dirList[entry]._namespaces['DAV:'].resourcetype._xmlvalue
                .length === 1 &&
                dirList[entry]._namespaces['DAV:'].resourcetype._xmlvalue[0]
                  .localName === 'collection')
            ) {
              isDir = true;
            } else {
              filesize =
                dirList[entry]._namespaces['DAV:'].getcontentlength._xmlvalue[0]
                  .data;
              lmdt =
                data._responses[entry]._namespaces['DAV:'].getlastmodified
                  ._xmlvalue[0].data;
            }
            fileName = this.getNameForPath(path);
            anotatedDirList.push({
              name: fileName,
              isFile: !isDir,
              size: filesize,
              lmdt: lmdt,
              path: decodeURI(path)
            });
          }
        }
        resolve(anotatedDirList);
      };

      if (dirPath.substring(dirPath.length - 1) !== '/') {
        dirPath = dirPath + '/';
      }
      dirPath = encodeURI(dirPath);

      this.davClient.propfind(
        dirPath,
        davSuccess,
        1 // 1 , davClient.INFINITY
      );
    });
  };

  /**
   * Finds out the properties of a file or directory such last modification date or file size
   */
  getPropertiesPromise = (filePath: string): Promise<Object> => {
    return new Promise((resolve, reject) => {
      this.davClient.propfind(
        encodeURI(filePath),
        (status, data) => {
          console.log(
            'Properties Status / Content: ' +
              status +
              ' / ' +
              JSON.stringify(data._responses)
          );
          const fileProperties = {};
          if (this.checkStatusCode(status)) {
            for (let entry in data._responses) {
              fileProperties.path = filePath;
              fileProperties.size =
                data._responses[entry]._namespaces['DAV:'].getcontentlength;
              fileProperties.lmdt =
                data._responses[entry]._namespaces[
                  'DAV:'
                ].getlastmodified._xmlvalue[0].data;
            }
            resolve(fileProperties);
          } else {
            reject('getFileProperties ' + filePath + ' failed ' + status);
          }
        },
        1
      );
    });
  };

  /**
   * Load the content of a text file
   */
  loadTextFilePromise = (filePath: string) =>
    this.getFileContentPromise(filePath, 'text');

  /**
   * Gets the content of file, useful for binary files
   */
  getFileContentPromise = (filePath: string, type): Promise<Object> => {
    console.log('getFileContent file: ' + filePath);
    return new Promise((resolve, reject) => {
      const ajax = this.davClient.getAjax('GET', encodeURI(filePath));
      ajax.onreadystatechange = null;
      ajax.responseType = type || 'arraybuffer';
      ajax.onerror = reject;

      ajax.onload = () => {
        const response = ajax.response || ajax.responseText;
        if (this.checkStatusCode(ajax.status)) {
          resolve(response);
        } else {
          reject('getFileContentPromise ajax error');
        }
      };
      ajax.send();
    });
  };

  /**
   * Persists a given content(binary supported) to a specified filepath
   */
  saveFilePromise = (
    filePath: string,
    content: string,
    overWrite: boolean,
    mode: string
  ): Promise<Object> => {
    return new Promise((resolve, reject) => {
      let isNewFile = false;
      this.davClient.propfind(
        encodeURI(filePath),
        (status, data) => {
          console.log(
            'Check file exists: Status / Content: ' + status + ' / ' + data
          );
          if (parseInt(status) === 404) {
            isNewFile = true;
          }
          if (isNewFile || overWrite === true || mode === 'text') {
            this.davClient.put(
              encodeURI(filePath),
              (status, data, headers) => {
                console.log(
                  'Creating File Status/Content/Headers:  ' +
                    status +
                    ' / ' +
                    data +
                    ' / ' +
                    headers
                );
                if (this.checkStatusCode(status)) {
                  resolve(isNewFile);
                } else {
                  reject('saveFilePromise: ' + filePath + ' failed ' + status);
                }
              },
              content,
              'application/octet-stream'
            );
          } else {
            reject('File Already Exists.');
          }
        },
        1
      );
    });
  };

  /**
   * Persists a given text content to a specified filepath
   */
  saveTextFilePromise(filePath: string, content: string, overWrite: boolean): Promise<Object> {
    console.log('Saving text file: ' + filePath);
    return this.saveFilePromise(filePath, content, overWrite, 'text');
  }

  /**
   * Persists a given binary content to a specified filepath
   */
  saveBinaryFilePromise(filePath: string, content: string, overWrite: boolean): Promise<Object> {
    console.log('Saving binary file: ' + filePath);
    return this.saveFilePromise(filePath, content, overWrite);
  }

  /**
   * Creates a directory
   */
  createDirectoryPromise = (dirPath: string): Promise<Object> => {
    console.log('Creating directory: ' + dirPath);
    return new Promise((resolve, reject) => {
      this.davClient.mkcol(encodeURI(dirPath), (status, data, headers) => {
        console.log(
          'Directory Creation Status/Content/Headers:  ' +
            status +
            ' / ' +
            data +
            ' / ' +
            headers
        );
        if (this.checkStatusCode(status)) {
          resolve(dirPath);
        } else {
          reject('createDirectory ' + dirPath + ' failed ' + status);
        }
      });
    });
  }

  /**
   * Copies a given file to a specified location
   */
  copyFilePromise = (filePath: string, newFilePath: string): Promise<Object> => {
    console.log('Copying file: ' + filePath + ' to ' + newFilePath);
    return new Promise((resolve, reject) => {
      if (filePath.toLowerCase() === newFilePath.toLowerCase()) {
        reject('Copying file failed, files have the same path');
      } else {
        this.davClient.copy(
          encodeURI(filePath),
          (status, data, headers) => {
            console.log(
              'Copy File Status/Content/Headers:  ' +
                status +
                ' / ' +
                data +
                ' / ' +
                headers
            );
            if (this.checkStatusCode(status)) {
              resolve(filePath, newFilePath);
            } else {
              reject('copyFile ' + filePath + ' failed ' + status);
            }
          },
          encodeURI(newFilePath),
          this.davClient.FAIL_ON_OVERWRITE
        );
      }
    });
  }

  /**
   * Renames a given file
   */
  renameFilePromise = (filePath: string, newFilePath: string): Promise<Object> => {
    console.log('Renaming file: ' + filePath + ' to ' + newFilePath);
    return new Promise((resolve, reject) => {
      if (filePath === newFilePath) {
        reject('Renaming file failed, files have the same path');
      } else {
        this.davClient.move(
          encodeURI(filePath),
          (status, data, headers) => {
            console.log(
              'Rename File Status/Content/Headers:  ' +
                status +
                ' / ' +
                data +
                ' / ' +
                headers
            );
            if (this.checkStatusCode(status)) {
              resolve([filePath, newFilePath]);
            } else {
              reject('rename: ' + filePath + ' failed ' + status);
            }
          },
          encodeURI(newFilePath),
          this.davClient.FAIL_ON_OVERWRITE
        );
      }
    });
  }

  /**
   * Rename a directory
   */
  renameDirectoryPromise = (dirPath: string, newDirectoryPath: string): Promise<Object> => {
    const newDirPath =
      extractParentDirectoryPath(dirPath) + '/' + newDirectoryPath;
    console.log('Renaming directory: ' + dirPath + ' to ' + newDirPath);
    return new Promise((resolve, reject) => {
      if (dirPath === newDirPath) {
        reject('Renaming directory failed, directories have the same path');
      } else {
        this.davClient.move(
          encodeURI(dirPath),
          (status, data, headers) => {
            console.log(
              'Rename Directory Status/Content/Headers:  ' +
                status +
                ' / ' +
                data +
                ' / ' +
                headers
            );
            if (this.checkStatusCode(status)) {
              resolve(newDirPath);
            } else {
              reject('rename: ' + dirPath + ' failed ' + status);
            }
          },
          encodeURI(newDirPath),
          this.davClient.FAIL_ON_OVERWRITE
        );
      }
    });
  };

  /**
   * Delete a specified file
   */
  deleteFilePromise = (path: string): Promise<Object> => this.deleteDirectoryPromise(path);

  /**
   * Delete a specified directory, the directory should be empty, if the trash can functionality is not enabled
   */
  deleteDirectoryPromise = (path: string): Promise<Object> =>
    new Promise((resolve, reject) => {
      this.davClient.remove(encodeURI(path), (status, data, headers) => {
        console.log(
          'Directory/File Deletion Status/Content/Headers:  ' +
            status +
            ' / ' +
            data +
            ' / ' +
            headers
        );
        if (this.checkStatusCode(status)) {
          resolve(path);
        } else {
          reject('delete ' + path + ' failed ' + status);
        }
      });
    });

  /**
   * Choosing directory
   */
  selectDirectoryDialog = () => {
    console.log('Selecting directory is not implemented in the web version');
  };

  /**
   * Choosing file
   */
  selectFileDialog = () => {
    console.log('Selecting file not relevant for the web version');
  };

  /**
   * Opens directory in new tab / window
   */
  openDirectory = (dirPath: string) => {
    console.log(
      'Opening directory ' + dirPath + ' not possible for the web version.'
    );
  };

  /**
   * Open the file url in a new tab / window
   */
  openFile = (filePath: string): void => {
    window.open(filePath, '_blank');
  };
}
<|endoftext|>


<commit_before>(function () {

	var TwtrGlobe = this.TwtrGlobe = { },

	// Constants
	POS_X = 0,
	POS_Y = 800,
	POS_Z = 2000,
	FOV = 45,
	NEAR = 1,
	FAR = 150000,
	PI_HALF = Math.PI / 2;

	var renderer, camera, scene, pubnub, innerWidth, innerHeight;

	/**
	 *	Initiates WebGL view with Three.js
	 */
	TwtrGlobe.init = function () {

		if (!this.supportsWebGL()) {
			window.location = '/upgrade';
			return;
		}

		var innerWidth = window.innerWidth;
		var innerHeight = window.innerHeight;

		renderer = new THREE.WebGLRenderer({ antialiasing: true });
		renderer.setSize(innerWidth, innerHeight);
		renderer.setClearColor(0x00000000, 0.0);

		document.getElementById('globe-holder').appendChild(renderer.domElement);

		camera = new THREE.PerspectiveCamera(FOV, innerWidth / innerHeight, NEAR, FAR);
		camera.position.set(POS_X, POS_Y, POS_Z);
		camera.lookAt( new THREE.Vector3(0,0,0) );

		scene = new THREE.Scene();
		scene.add(camera);

		addEarth();
		addStats();
		animate();

		window.addEventListener ('resize', onWindowResize);
	}

	var earthMesh, beaconHolder;

	/**
	 *	Creates the Earth sphere
	 */
	function addEarth () {

	  var sphereGeometry = new THREE.SphereGeometry(600, 50, 50);

	  var shader = Shaders.earth;
	  var uniforms = THREE.UniformsUtils.clone(shader.uniforms);

	  uniforms['texture'].value = THREE.ImageUtils.loadTexture('/images/world-dark.jpg');

	  var material = new THREE.ShaderMaterial({
	    uniforms: uniforms,
	    vertexShader: shader.vertexShader,
	    fragmentShader: shader.fragmentShader
	  });

	  earthMesh = new THREE.Mesh(sphereGeometry, material);
	  scene.add(earthMesh);

	  // add an empty container for the beacons to be added to
	  beaconHolder = new THREE.Object3D();
	  earthMesh.add(beaconHolder);
	}

	var stats;

	/**
	 * Adds FPS stats view for debugging
	 */
	function addStats () {
		stats = new Stats();
		stats.setMode(0); // 0: fps, 1: ms

		stats.domElement.style.position = 'absolute';
		stats.domElement.style.right = '20px';
		stats.domElement.style.bottom = '100px';

		document.body.appendChild( stats.domElement );
	}

	/**
	 * Converts a latlong to Vector3 for use in Three.js
	 */
	function latLonToVector3 (lat, lon, height) {

		height = height ? height : 0;

	  var vector3 = new THREE.Vector3(0, 0, 0);

	  lon = lon + 10;
	  lat = lat - 2;

	  var phi = PI_HALF - lat * Math.PI / 180 - Math.PI * 0.01;
	  var theta = 2 * Math.PI - lon * Math.PI / 180 + Math.PI * 0.06;
	  var rad = 600 + height;

	  vector3.x = Math.sin(phi) * Math.cos(theta) * rad;
	  vector3.y = Math.cos(phi) * rad;
	  vector3.z = Math.sin(phi) * Math.sin(theta) * rad;

	  return vector3;
	};

	/**
	 *	Adds a Tweet to the Earth, called from TweetHud.js
	 */
	TwtrGlobe.onTweet = function (tweet) {

		// extract a latlong from the Tweet object
		var latlong = {
			lat: tweet.coordinates.coordinates[1],
			lon: tweet.coordinates.coordinates[0]
		};

		var position = latLonToVector3(latlong.lat, latlong.lon);

		addBeacon(position, tweet);
	}

	/**
	 *	Adds a beacon (line) to the surface of the Earth
	 */
	function addBeacon (position, tweet) {

		var beacon = new TweetBeacon(tweet);

	  beacon.position.x = position.x;
	  beacon.position.y = position.y;
	  beacon.position.z = position.z;
	  beacon.lookAt(earthMesh.position);
		beaconHolder.add(beacon);

		// remove beacon from scene when it expires itself
		beacon.onHide(function () {
			beaconHolder.remove(beacon);
		});
	}

	/**
	 * Render loop
	 */
	function animate () {
	  requestAnimationFrame(animate);
    if (stats) stats.begin();
    render();
    if (stats) stats.end();
	}

	/**
	 * Runs on each animation frame
	 */
	function render () {

		earthMesh.rotation.y = earthMesh.rotation.y + 0.005;

	  renderer.autoClear = false;
	  renderer.clear();
	  renderer.render( scene, camera );
	}

	/**
	 * Updates camera and rendered when browser resized
	 */
	function onWindowResize (event) {
		innerWidth = window.innerWidth;
		innerHeight = window.innerHeight;

	  camera.aspect = innerWidth / innerHeight;
	  camera.updateProjectionMatrix();
	  renderer.setSize(innerWidth, innerHeight);
	}

	/**
	 * Detects WebGL support
	 */
	TwtrGlobe.supportsWebGL = function () {
		return ( function () { try { var canvas = document.createElement( 'canvas' ); return !! window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ); } catch( e ) { return false; } } )();
	}

	return TwtrGlobe;

})().init();
<commit_msg>Handle null tweet.coordinates<commit_after>(function () {

	var TwtrGlobe = this.TwtrGlobe = { },

	// Constants
	POS_X = 0,
	POS_Y = 800,
	POS_Z = 2000,
	FOV = 45,
	NEAR = 1,
	FAR = 150000,
	PI_HALF = Math.PI / 2;

	var renderer, camera, scene, pubnub, innerWidth, innerHeight;

	/**
	 *	Initiates WebGL view with Three.js
	 */
	TwtrGlobe.init = function () {

		if (!this.supportsWebGL()) {
			window.location = '/upgrade';
			return;
		}

		var innerWidth = window.innerWidth;
		var innerHeight = window.innerHeight;

		renderer = new THREE.WebGLRenderer({ antialiasing: true });
		renderer.setSize(innerWidth, innerHeight);
		renderer.setClearColor(0x00000000, 0.0);

		document.getElementById('globe-holder').appendChild(renderer.domElement);

		camera = new THREE.PerspectiveCamera(FOV, innerWidth / innerHeight, NEAR, FAR);
		camera.position.set(POS_X, POS_Y, POS_Z);
		camera.lookAt( new THREE.Vector3(0,0,0) );

		scene = new THREE.Scene();
		scene.add(camera);

		addEarth();
		addStats();
		animate();

		window.addEventListener ('resize', onWindowResize);
	}

	var earthMesh, beaconHolder;

	/**
	 *	Creates the Earth sphere
	 */
	function addEarth () {

	  var sphereGeometry = new THREE.SphereGeometry(600, 50, 50);

	  var shader = Shaders.earth;
	  var uniforms = THREE.UniformsUtils.clone(shader.uniforms);

	  uniforms['texture'].value = THREE.ImageUtils.loadTexture('/images/world-dark.jpg');

	  var material = new THREE.ShaderMaterial({
	    uniforms: uniforms,
	    vertexShader: shader.vertexShader,
	    fragmentShader: shader.fragmentShader
	  });

	  earthMesh = new THREE.Mesh(sphereGeometry, material);
	  scene.add(earthMesh);

	  // add an empty container for the beacons to be added to
	  beaconHolder = new THREE.Object3D();
	  earthMesh.add(beaconHolder);
	}

	var stats;

	/**
	 * Adds FPS stats view for debugging
	 */
	function addStats () {
		stats = new Stats();
		stats.setMode(0); // 0: fps, 1: ms

		stats.domElement.style.position = 'absolute';
		stats.domElement.style.right = '20px';
		stats.domElement.style.bottom = '100px';

		document.body.appendChild( stats.domElement );
	}

	/**
	 * Converts a latlong to Vector3 for use in Three.js
	 */
	function latLonToVector3 (lat, lon, height) {

		height = height ? height : 0;

	  var vector3 = new THREE.Vector3(0, 0, 0);

	  lon = lon + 10;
	  lat = lat - 2;

	  var phi = PI_HALF - lat * Math.PI / 180 - Math.PI * 0.01;
	  var theta = 2 * Math.PI - lon * Math.PI / 180 + Math.PI * 0.06;
	  var rad = 600 + height;

	  vector3.x = Math.sin(phi) * Math.cos(theta) * rad;
	  vector3.y = Math.cos(phi) * rad;
	  vector3.z = Math.sin(phi) * Math.sin(theta) * rad;

	  return vector3;
	};

	/**
	 *	Adds a Tweet to the Earth, called from TweetHud.js
	 */
	TwtrGlobe.onTweet = function (tweet) {
		if (tweet.coordinates==null){
			return;
		}
		// extract a latlong from the Tweet object
		var latlong = {
			lat: tweet.coordinates.coordinates[1],
			lon: tweet.coordinates.coordinates[0]
		};

		var position = latLonToVector3(latlong.lat, latlong.lon);

		addBeacon(position, tweet);
	}

	/**
	 *	Adds a beacon (line) to the surface of the Earth
	 */
	function addBeacon (position, tweet) {

		var beacon = new TweetBeacon(tweet);

	  beacon.position.x = position.x;
	  beacon.position.y = position.y;
	  beacon.position.z = position.z;
	  beacon.lookAt(earthMesh.position);
		beaconHolder.add(beacon);

		// remove beacon from scene when it expires itself
		beacon.onHide(function () {
			beaconHolder.remove(beacon);
		});
	}

	/**
	 * Render loop
	 */
	function animate () {
	  requestAnimationFrame(animate);
    if (stats) stats.begin();
    render();
    if (stats) stats.end();
	}

	/**
	 * Runs on each animation frame
	 */
	function render () {

		earthMesh.rotation.y = earthMesh.rotation.y + 0.005;

	  renderer.autoClear = false;
	  renderer.clear();
	  renderer.render( scene, camera );
	}

	/**
	 * Updates camera and rendered when browser resized
	 */
	function onWindowResize (event) {
		innerWidth = window.innerWidth;
		innerHeight = window.innerHeight;

	  camera.aspect = innerWidth / innerHeight;
	  camera.updateProjectionMatrix();
	  renderer.setSize(innerWidth, innerHeight);
	}

	/**
	 * Detects WebGL support
	 */
	TwtrGlobe.supportsWebGL = function () {
		return ( function () { try { var canvas = document.createElement( 'canvas' ); return !! window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ); } catch( e ) { return false; } } )();
	}

	return TwtrGlobe;

})().init();
<|endoftext|>


<commit_before>$(document).ready(function () {
	// initialize web socket
	var socket = io.connect(null);

  // prepopulate fields with last used values
  if (typeof(Storage) !== "undefined" && localStorage && localStorage.ircOptions) {
    var opts = JSON.parse(localStorage.ircOptions);

    // populate each options field, assuming the option name matches the input ID
    // now with logic to accomodate checkboxes!
    for (option in opts) {
      var ele = $('#' + option);
      if (ele.is(':checkbox')) {
        opts[option] ? ele.attr('checked', 'checked') : ele.removeAttr('checked');
      } else {
        ele.val(opts[option])
      }
    }
  }
  // select first empty text field. if none are empty, select the first text field
  $("#connect-form input:first, #connect-form input[value=]:first").last().select();

  // initialize nirc
  $('#connect').click(function () {
    if (!navigator.onLine) {
      alert('You must be connected to the internet.');
      return false;
    }

    // if supports webkit notifications, and if they haven't allowed already
    if (window.webkitNotifications && navigator.userAgent.indexOf("Chrome") > -1) {
      if (window.webkitNotifications.checkPermission() == 1) {
        window.webkitNotifications.requestPermission();
      }
    } else if (window.Notification && navigator.userAgent.toLowerCase().indexOf("firefox") > -1) {
      if (Notification.permission == 'default') {
        Notification.requestPermission(function(perm) {
          alert(perm);
        });
      }
    }
    $.nirc(socket);
  });

  window.hasFocus = true;
  //misc listeners for when the tab loses/gains focus
  window.addEventListener('focus', function() {
    window.hasFocus = true;
  });

  window.addEventListener('blur', function() {
    window.hasFocus = false;
  });
});
<commit_msg>Removed Debug Code<commit_after>$(document).ready(function () {
	// initialize web socket
	var socket = io.connect(null);

  // prepopulate fields with last used values
  if (typeof(Storage) !== "undefined" && localStorage && localStorage.ircOptions) {
    var opts = JSON.parse(localStorage.ircOptions);

    // populate each options field, assuming the option name matches the input ID
    // now with logic to accomodate checkboxes!
    for (option in opts) {
      var ele = $('#' + option);
      if (ele.is(':checkbox')) {
        opts[option] ? ele.attr('checked', 'checked') : ele.removeAttr('checked');
      } else {
        ele.val(opts[option])
      }
    }
  }
  // select first empty text field. if none are empty, select the first text field
  $("#connect-form input:first, #connect-form input[value=]:first").last().select();

  // initialize nirc
  $('#connect').click(function () {
    if (!navigator.onLine) {
      alert('You must be connected to the internet.');
      return false;
    }

    // if supports webkit notifications, and if they haven't allowed already
    if (window.webkitNotifications && navigator.userAgent.indexOf("Chrome") > -1) {
      if (window.webkitNotifications.checkPermission() == 1) {
        window.webkitNotifications.requestPermission();
      }
    } else if (window.Notification && navigator.userAgent.toLowerCase().indexOf("firefox") > -1) {
      if (Notification.permission == 'default') {
        Notification.requestPermission();
      }
    }
    $.nirc(socket);
  });

  window.hasFocus = true;
  //misc listeners for when the tab loses/gains focus
  window.addEventListener('focus', function() {
    window.hasFocus = true;
  });

  window.addEventListener('blur', function() {
    window.hasFocus = false;
  });
});
<|endoftext|>


<commit_before>var getWhere = function(name, params) {
  var options = params.options;
  var flags = params.flags;
  var data = params.data;
  var toReturn = params.toReturn;

  if(typeof name != 'string') {
    throw new Error("Invalid preference name");
  }

  data = data[name];

  // if preference value is string return it
  if(typeof data != 'object')
    return data;

  // if preference value is object and there is no options, return property
  if(!options) {
    if(flags && flags.explicit) {
      var dataKeys = Object.keys(data);
      if(dataKeys.length == 1 && dataKeys.indexOf(toReturn))
        return data[toReturn];
      else
        return false;
    }
    return data[toReturn];
  }

  // function for checking options and retriving appropriate keys
  var getValue = function(data) {
    // get data and options keys
    var dataKeys = Object.keys(data);
    var optKeys = Object.keys(options);

    if(flags && flags.explicit) {
      //if explicit is set, return only if options keys match data keys
      var hasOptionskeys = dataKeys.filter(function(n) {
        // special key not in options object
        if(n == toReturn)
          return true;
        // ignore if object
        if(typeof data[n] == 'object')
          return true;
        // if data key exists in options
        return optKeys.indexOf(n) != -1
      });

      if(hasOptionskeys.length != dataKeys.length)
        return false;
    }

    // intersection between optKeys and dataKeys
    var hasDataKeys = optKeys.filter(function(n) {
        return dataKeys.indexOf(n) != -1
    });

    var returnKeys = true;

    if(dataKeys.indexOf(toReturn) == -1) {
      returnKeys = false;
    }

    // loop trough key filters
    for(var i in hasDataKeys) {
      var key = hasDataKeys[i];

      if(typeof data[key] == 'string') {
        // if key filter is string only check if it is the same as in options
        if(data[key] != options[key]) {
          returnKeys = false;
          break;
        }
      } else if(Array.isArray(data[key])) {
        // if key filter is array check if it contains the same as in options
        if(data[key].indexOf(options[key]) == -1) {
          returnKeys = false;
          break;
        }
      } else if (typeof data[key] == 'object' && data[key][options[key]]) {
        // if key filter is object, recursive call to getValue with new data
        return getValue(data[key][options[key]]);
      }
    }

    if(returnKeys) {
      return data[toReturn];
    }

    return false;
  }

  return getValue(data);
};

if (typeof exports == "object" && typeof module == "object")
  module.exports = getWhere;
else
  window.getWhere = getWhere;<commit_msg>index of bug fixed<commit_after>var getWhere = function(name, params) {
  var options = params.options;
  var flags = params.flags;
  var data = params.data;
  var toReturn = params.toReturn;

  if(typeof name != 'string') {
    throw new Error("Invalid preference name");
  }

  data = data[name];

  // if preference value is string return it
  if(typeof data != 'object')
    return data;

  // if preference value is object and there is no options, return property
  if(!options) {
    if(flags && flags.explicit) {
      var dataKeys = Object.keys(data);
      if(dataKeys.length == 1 && dataKeys.indexOf(toReturn) != -1)
        return data[toReturn];
      else
        return false;
    }
    return data[toReturn];
  }

  // function for checking options and retriving appropriate keys
  var getValue = function(data) {
    // get data and options keys
    var dataKeys = Object.keys(data);
    var optKeys = Object.keys(options);

    if(flags && flags.explicit) {
      //if explicit is set, return only if options keys match data keys
      var hasOptionskeys = dataKeys.filter(function(n) {
        // special key not in options object
        if(n == toReturn)
          return true;
        // ignore if object
        if(typeof data[n] == 'object')
          return true;
        // if data key exists in options
        return optKeys.indexOf(n) != -1
      });

      if(hasOptionskeys.length != dataKeys.length)
        return false;
    }

    // intersection between optKeys and dataKeys
    var hasDataKeys = optKeys.filter(function(n) {
        return dataKeys.indexOf(n) != -1
    });

    var returnKeys = true;

    if(dataKeys.indexOf(toReturn) == -1) {
      returnKeys = false;
    }

    // loop trough key filters
    for(var i in hasDataKeys) {
      var key = hasDataKeys[i];

      if(typeof data[key] == 'string') {
        // if key filter is string only check if it is the same as in options
        if(data[key] != options[key]) {
          returnKeys = false;
          break;
        }
      } else if(Array.isArray(data[key])) {
        // if key filter is array check if it contains the same as in options
        if(data[key].indexOf(options[key]) == -1) {
          returnKeys = false;
          break;
        }
      } else if (typeof data[key] == 'object' && data[key][options[key]]) {
        // if key filter is object, recursive call to getValue with new data
        return getValue(data[key][options[key]]);
      }
    }

    if(returnKeys) {
      return data[toReturn];
    }

    return false;
  }

  return getValue(data);
};

if (typeof exports == "object" && typeof module == "object")
  module.exports = getWhere;
else
  window.getWhere = getWhere;<|endoftext|>


<commit_before>$(function() {
    $("#job-name").text($("#index-job-name").text());
    authorityControl();
    renderShardingTable();
    renderBreadCrumbMenu();
    bindButtons();
});

function renderShardingTable() {
    var jobName = $("#job-name").text();
    $("#sharding").bootstrapTable({
        url: "/api/jobs/" + jobName + "/sharding",
        cache: false,
        columns: [
            {
                field: "item",
                title: "分片项",
                sortable: "true"
            }, {
                field: "serverIp",
                title: "服务器IP",
                sortable: "true"
            }, {
                field: "instanceId",
                title: "进程ID",
                sortable: "true"
            }, {
                field: "status",
                title: "状态",
                formatter: "shardingStatusFormatter"
            }, {
                field: "failover",
                title: "失效转移",
                formatter: "failoverFormatter"
            }, {
                field: "operation",
                title: "操作",
                formatter: "generateOperationButtons"
            }]
    });
}
concle
function shardingStatusFormatter(value, row) {
    switch(value) {
        case "DISABLED":
            return "禁用中";
            break;
        case "RUNNING":
            return "运行中";
            break;
        case "SHARDING_FLAG":
            return "分片待调整";
            break;
        default:
            return "等待运行";
            break;
    }
}

function failoverFormatter(value, row) {
    return value ? "是" : "-";
}

function generateOperationButtons(val, row) {
    var disableButton = "<button operation='disable-sharding' class='btn-xs btn-warning' job-name='" + row.jobName + "' item='" + row.item + "' >禁用</button>";
    var enableButton = "<button operation='enable-sharding' class='btn-xs btn-success' job-name='" + row.jobName + "' item='" + row.item + "' >启用</button>";
    if ("DISABLED" === row.status) {
        return enableButton;
    } else {
        return disableButton;
    }
}

function bindButtons() {
    bindDisableButton();
    bindEnableButton();
}

function bindDisableButton() {
    $(document).off("click", "button[operation='disable-sharding']");
    $(document).on("click", "button[operation='disable-sharding']", function(event) {
        var jobName = $("#index-job-name").text();
        var item = $(event.currentTarget).attr("item");
        $.ajax({
            url: "/api/jobs/" + jobName + "/sharding/" + item + "/disable",
            type: "POST",
            success: function() {
                showSuccessDialog();
                $("#sharding").bootstrapTable("refresh");
            }
        });
    });
}

function bindEnableButton() {
    $(document).off("click", "button[operation='enable-sharding']");
    $(document).on("click", "button[operation='enable-sharding']", function(event) {
        var jobName = $("#index-job-name").text();
        var item = $(event.currentTarget).attr("item");
        $.ajax({
            url: "/api/jobs/" + jobName + "/sharding/" + item + "/disable",
            type: "DELETE",
            success: function () {
                showSuccessDialog();
                $("#sharding").bootstrapTable("refresh");
            }
        });
    });
}

function renderBreadCrumbMenu() {
    $("#breadcrumb-job").click(function() {
        $("#content").load("html/status/job/jobs_status_overview.html");
    });
}
<commit_msg>adjust display text for sharding flag status on lite console<commit_after>$(function() {
    $("#job-name").text($("#index-job-name").text());
    authorityControl();
    renderShardingTable();
    renderBreadCrumbMenu();
    bindButtons();
});

function renderShardingTable() {
    var jobName = $("#job-name").text();
    $("#sharding").bootstrapTable({
        url: "/api/jobs/" + jobName + "/sharding",
        cache: false,
        columns: [
            {
                field: "item",
                title: "分片项",
                sortable: "true"
            }, {
                field: "serverIp",
                title: "服务器IP",
                sortable: "true"
            }, {
                field: "instanceId",
                title: "进程ID",
                sortable: "true"
            }, {
                field: "status",
                title: "状态",
                formatter: "shardingStatusFormatter"
            }, {
                field: "failover",
                title: "失效转移",
                formatter: "failoverFormatter"
            }, {
                field: "operation",
                title: "操作",
                formatter: "generateOperationButtons"
            }]
    });
}

function shardingStatusFormatter(value, row) {
    switch(value) {
        case "DISABLED":
            return "禁用中";
            break;
        case "RUNNING":
            return "运行中";
            break;
        case "SHARDING_FLAG":
            return "分片待调整";
            break;
        default:
            return "等待运行";
            break;
    }
}

function failoverFormatter(value, row) {
    return value ? "是" : "-";
}

function generateOperationButtons(val, row) {
    var disableButton = "<button operation='disable-sharding' class='btn-xs btn-warning' job-name='" + row.jobName + "' item='" + row.item + "' >禁用</button>";
    var enableButton = "<button operation='enable-sharding' class='btn-xs btn-success' job-name='" + row.jobName + "' item='" + row.item + "' >启用</button>";
    if ("DISABLED" === row.status) {
        return enableButton;
    } else {
        return disableButton;
    }
}

function bindButtons() {
    bindDisableButton();
    bindEnableButton();
}

function bindDisableButton() {
    $(document).off("click", "button[operation='disable-sharding']");
    $(document).on("click", "button[operation='disable-sharding']", function(event) {
        var jobName = $("#index-job-name").text();
        var item = $(event.currentTarget).attr("item");
        $.ajax({
            url: "/api/jobs/" + jobName + "/sharding/" + item + "/disable",
            type: "POST",
            success: function() {
                showSuccessDialog();
                $("#sharding").bootstrapTable("refresh");
            }
        });
    });
}

function bindEnableButton() {
    $(document).off("click", "button[operation='enable-sharding']");
    $(document).on("click", "button[operation='enable-sharding']", function(event) {
        var jobName = $("#index-job-name").text();
        var item = $(event.currentTarget).attr("item");
        $.ajax({
            url: "/api/jobs/" + jobName + "/sharding/" + item + "/disable",
            type: "DELETE",
            success: function () {
                showSuccessDialog();
                $("#sharding").bootstrapTable("refresh");
            }
        });
    });
}

function renderBreadCrumbMenu() {
    $("#breadcrumb-job").click(function() {
        $("#content").load("html/status/job/jobs_status_overview.html");
    });
}
<|endoftext|>


<commit_before>/*
 * grunt-ember-templates
 *
 * Copyright (c) 2012 Dan Gebhardt, Tim Branyen, contributors
 * Licensed under the MIT license.
 * https://github.com/dgeb/grunt-ember-templates/blob/master/LICENSE
 */

function manualCompile(handlebarsPath, templateCompilerPath, template){
  'use strict';

  var fs                 = require('fs'),
      vm                 = require('vm'),
      handlebarsJs       = fs.readFileSync(handlebarsPath, 'utf8'),
      templateCompilerJs = fs.readFileSync(templateCompilerPath, 'utf8');

  // Create a context into which we will load both the ember template compiler
  // as well as the template to be compiled. The ember template compiler expects
  // `exports` to be defined, and uses it to export `precompile()`.
  var context = vm.createContext({
    exports: {},
    template: template
  });

  // Load handlebars
  vm.runInContext(handlebarsJs, context, 'handlebars.js');

  // Load the ember template compiler
  vm.runInContext(templateCompilerJs, context, 'ember-template-compiler.js');

  // Compile the template
  vm.runInContext('compiledJS = exports.precompile(template);', context);

  return context.compiledJS;
};

module.exports = function(grunt) {
  'use strict';

  var compiler = require('ember-template-compiler');

  var emberTemplatesTask = function() {
    var options = this.options({
      templateFileExtensions: /\.(hbs|hjs|handlebars)/,
      amd: false,
      precompile: true,
      templateName: function(name) { return name; },
      templateNameFromFile: function(file) {
        // `templateBaseDir` is an alias for `templateBasePath`
        var templateBasePath = options.templateBasePath || options.templateBaseDir;
        [templateBasePath, options.templateFileExtensions].forEach(function(match) {
          if (match) {
            file = file.replace(match, '');
          }
        });

        if (file.charAt(0) === '/'){
          file = file.slice(1);
        }

        return options.templateName(file);
      },
      templateRegistration: function(name, contents) {
        return 'Ember.TEMPLATES[' + JSON.stringify(name) + '] = ' + contents + ';'
      },
      handlebarsPath: null,
      templateCompilerPath: null
    });

    grunt.verbose.writeflags(options, 'Options');

    // Iterate files
    this.files.forEach(function(f) {
      var processedTemplates = [],
          output = [],
          name,
          contents;

      if (options.amd) {
        output = output.concat('define(["ember"], function(Ember){');
      }

      f.src.forEach(function(file) {
        try {

          name = options.templateNameFromFile(file);

          if (options.precompile) {

            var template = grunt.file.read(file);
            var compiledTemplate;

            // Run the `preprocess` function if specified in the options.
            if (typeof options.preprocess === 'function') {
              template = options.preprocess(template);
            }

            if (options.handlebarsPath && options.templateCompilerPath) {
              compiledTemplate = manualCompile(options.handlebarsPath, options.templateCompilerPath, template);
            } else {
              compiledTemplate = compiler.precompile(template).toString();
            }

            // Wrap compiled template
            contents = 'Ember.Handlebars.template(' + compiledTemplate + ')';
          } else {
            // Wrap raw (uncompiled) template
            contents = 'Ember.Handlebars.compile(' + JSON.stringify(grunt.file.read(file)) + ')';
          }

          processedTemplates.push(options.templateRegistration(name, contents));

        } catch(e) {
          grunt.log.error(e);
          grunt.fail.warn('Ember Handlebars failed to compile ' + file + '.');
        }
      });

      output = output.concat(processedTemplates);

      if (options.amd) {
        output = output.concat('});');
      }

      if (output.length > 0) {
        grunt.file.write(f.dest, output.join('\n\n'));
        grunt.log.writeln('File "' + f.dest + '" created.');
      }

    });
  };

  grunt.registerMultiTask('emberTemplates', 'Compile Handlebars templates for Ember.', emberTemplatesTask);

  // TODO: remove deprecated `ember_templates` task from v0.5
  grunt.registerMultiTask('ember_templates', 'Compile Handlebars templates for Ember. [DEPRECATED: please use `emberTemplates` instead]', function() {
    grunt.log.warn('`ember_templates` is deprecated and will be removed in v0.5. Please use `emberTemplates` instead.');
    emberTemplatesTask.apply(this, arguments);
  });
};
<commit_msg>Minor comment + cleanup<commit_after>/*
 * grunt-ember-templates
 *
 * Copyright (c) 2012 Dan Gebhardt, Tim Branyen, contributors
 * Licensed under the MIT license.
 * https://github.com/dgeb/grunt-ember-templates/blob/master/LICENSE
 */

function manualCompile(handlebarsPath, templateCompilerPath, template){
  'use strict';

  var fs                 = require('fs'),
      vm                 = require('vm'),
      handlebarsJs       = fs.readFileSync(handlebarsPath, 'utf8'),
      templateCompilerJs = fs.readFileSync(templateCompilerPath, 'utf8');

  // Create a context into which we will load both the ember template compiler
  // as well as the template to be compiled. The ember template compiler expects
  // `exports` to be defined, and uses it to export `precompile()`.
  var context = vm.createContext({
    exports: {},
    template: template
  });

  // Load handlebars
  vm.runInContext(handlebarsJs, context, 'handlebars.js');

  // Load the ember template compiler
  vm.runInContext(templateCompilerJs, context, 'ember-template-compiler.js');

  // Compile the template
  vm.runInContext('compiledJS = exports.precompile(template);', context);

  return context.compiledJS;
};

module.exports = function(grunt) {
  'use strict';

  var compiler = require('ember-template-compiler');

  var emberTemplatesTask = function() {
    var options = this.options({
      templateFileExtensions: /\.(hbs|hjs|handlebars)/,
      amd: false,
      precompile: true,
      templateName: function(name) { return name; },
      templateNameFromFile: function(file) {
        // `templateBaseDir` is an alias for `templateBasePath`
        var templateBasePath = options.templateBasePath || options.templateBaseDir;
        [templateBasePath, options.templateFileExtensions].forEach(function(match) {
          if (match) {
            file = file.replace(match, '');
          }
        });

        // Remove leading slash if necessary
        if (file.charAt(0) === '/') {
          file = file.slice(1);
        }

        return options.templateName(file);
      },
      templateRegistration: function(name, contents) {
        return 'Ember.TEMPLATES[' + JSON.stringify(name) + '] = ' + contents + ';'
      },
      handlebarsPath: null,
      templateCompilerPath: null
    });

    grunt.verbose.writeflags(options, 'Options');

    // Iterate files
    this.files.forEach(function(f) {
      var processedTemplates = [],
          output = [],
          name,
          contents;

      if (options.amd) {
        output = output.concat('define(["ember"], function(Ember){');
      }

      f.src.forEach(function(file) {
        try {

          name = options.templateNameFromFile(file);

          if (options.precompile) {

            var template = grunt.file.read(file);
            var compiledTemplate;

            // Run the `preprocess` function if specified in the options.
            if (typeof options.preprocess === 'function') {
              template = options.preprocess(template);
            }

            if (options.handlebarsPath && options.templateCompilerPath) {
              compiledTemplate = manualCompile(options.handlebarsPath, options.templateCompilerPath, template);
            } else {
              compiledTemplate = compiler.precompile(template).toString();
            }

            // Wrap compiled template
            contents = 'Ember.Handlebars.template(' + compiledTemplate + ')';
          } else {
            // Wrap raw (uncompiled) template
            contents = 'Ember.Handlebars.compile(' + JSON.stringify(grunt.file.read(file)) + ')';
          }

          processedTemplates.push(options.templateRegistration(name, contents));

        } catch(e) {
          grunt.log.error(e);
          grunt.fail.warn('Ember Handlebars failed to compile ' + file + '.');
        }
      });

      output = output.concat(processedTemplates);

      if (options.amd) {
        output = output.concat('});');
      }

      if (output.length > 0) {
        grunt.file.write(f.dest, output.join('\n\n'));
        grunt.log.writeln('File "' + f.dest + '" created.');
      }

    });
  };

  grunt.registerMultiTask('emberTemplates', 'Compile Handlebars templates for Ember.', emberTemplatesTask);

  // TODO: remove deprecated `ember_templates` task from v0.5
  grunt.registerMultiTask('ember_templates', 'Compile Handlebars templates for Ember. [DEPRECATED: please use `emberTemplates` instead]', function() {
    grunt.log.warn('`ember_templates` is deprecated and will be removed in v0.5. Please use `emberTemplates` instead.');
    emberTemplatesTask.apply(this, arguments);
  });
};
<|endoftext|>


<commit_before>import { createSelector } from 'reselect';
import i from "icepick";

//TODO: definitely lots of memoization opportunities here

// there might be a better way to organize sections
// maybe merge all of them into a single map for simpler lookup?
// there might also be similar functionality in react/redux-router that could replace all of this
const referenceSections = {
    [`/reference/guide`]: {
        id: `/reference/guide`,
        name: "Understanding our data",
        breadcrumb: "Guide",
        icon: "all"
    },
    [`/reference/metrics`]: {
        id: `/reference/metrics`,
        name: "Metrics",
        breadcrumb: "Metrics",
        // mapping of propname to args of dispatch function
        fetch: {fetchMetrics: []},
        get: 'getMetrics',
        icon: "star"
    },
    [`/reference/lists`]: {
        id: `/reference/lists`,
        name: "Lists",
        breadcrumb: "Lists",
        fetch: {fetchLists: []},
        get: 'getLists',
        icon: "recents"
    },
    [`/reference/databases`]: {
        id: `/reference/databases`,
        name: "Databases and tables",
        breadcrumb: "Databases",
        fetch: {fetchDatabases: []},
        get: 'getDatabases',
        icon: "mine"
    }
};

const getReferenceSections = (state) => referenceSections;

const getMetricSections = (metric) => metric ? {
    [`/reference/metrics/${metric.id}`]: {
        id: `/reference/metrics/${metric.id}`,
        name: "Details",
        breadcrumb: `${metric.name}`,
        fetch: {fetchMetrics: []},
        get: 'getMetric',
        icon: "all",
        parent: referenceSections[`/reference/metrics`]
    },
    [`/reference/metrics/${metric.id}/questions`]: {
        id: `/reference/metrics/${metric.id}/questions`,
        name: "Questions about this metric",
        breadcrumb: `${metric.name}`,
        fetch: {fetchQuestions: []},
        get: 'getMetricQuestions',
        icon: "all",
        parent: referenceSections[`/reference/metrics`]
    },
    [`/reference/metrics/${metric.id}/history`]: {
        id: `/reference/metrics/${metric.id}/history`,
        name: "Revision history",
        breadcrumb: `${metric.name}`,
        icon: "all",
        parent: referenceSections[`/reference/metrics`]
    }
} : {};

const getListSections = (list) => list ? {
    [`/reference/lists/${list.id}`]: {
        id: `/reference/lists/${list.id}`,
        name: "Details",
        breadcrumb: `${list.name}`,
        fetch: {fetchLists: []},
        get: 'getList',
        icon: "all",
        parent: referenceSections[`/reference/lists`]
    },
    [`/reference/lists/${list.id}/fields`]: {
        id: `/reference/lists/${list.id}/fields`,
        name: "Fields in this list",
        breadcrumb: `${list.name}`,
        icon: "all",
        parent: referenceSections[`/reference/lists`]
    },
    [`/reference/lists/${list.id}/questions`]: {
        id: `/reference/lists/${list.id}/questions`,
        name: `Questions about ${list.name}`,
        breadcrumb: `${list.name}`,
        fetch: {fetchQuestions: []},
        get: 'getListQuestions',
        icon: "all",
        parent: referenceSections[`/reference/lists`]
    },
    [`/reference/lists/${list.id}/history`]: {
        id: `/reference/lists/${list.id}/history`,
        name: "Revision history",
        breadcrumb: `${list.name}`,
        icon: "all",
        parent: referenceSections[`/reference/lists`]
    }
} : {};

const getDatabaseSections = (database) => database ? {
    [`/reference/databases/${database.id}`]: {
        id: `/reference/databases/${database.id}`,
        name: "Details",
        breadcrumb: `${database.name}`,
        fetch: {fetchDatabaseMetadata: [database.id]},
        get: 'getDatabase',
        icon: "all",
        parent: referenceSections[`/reference/databases`]
    },
    [`/reference/databases/${database.id}/tables`]: {
        id: `/reference/databases/${database.id}/tables`,
        name: `Tables in this database`,
        breadcrumb: `${database.name}`,
        fetch: {fetchDatabaseMetadata: [database.id]},
        get: 'getTables',
        icon: "star",
        parent: referenceSections[`/reference/databases`]
    }
} : {};

const getTableSections = (database, table) => database && table ? {
    [`/reference/databases/${database.id}/tables/${table.id}`]: {
        id: `/reference/databases/${database.id}/tables/${table.id}`,
        name: "Details",
        breadcrumb: `${table.name}`,
        fetch: {fetchDatabaseMetadata: [database.id]},
        get: 'getTable',
        icon: "all",
        parent: getDatabaseSections(database)[`/reference/databases/${database.id}/tables`]
    },
    [`/reference/databases/${database.id}/tables/${table.id}/fields`]: {
        id: `/reference/databases/${database.id}/tables/${table.id}/fields`,
        name: `Fields in this table`,
        breadcrumb: `${table.name}`,
        fetch: {fetchDatabaseMetadata: [database.id]},
        icon: "star",
        parent: getDatabaseSections(database)[`/reference/databases/${database.id}/tables`]
    },
    [`/reference/databases/${database.id}/tables/${table.id}/questions`]: {
        id: `/reference/databases/${database.id}/tables/${table.id}/questions`,
        name: `Questions about this table`,
        breadcrumb: `${table.name}`,
        //TODO: make this more flexible, needs to fetch both questions and database metadata
        fetch: {fetchDatabaseMetadata: [database.id], fetchQuestions: []},
        icon: "star",
        parent: getDatabaseSections(database)[`/reference/databases/${database.id}/tables`]
    },
    [`/reference/databases/${database.id}/tables/${table.id}/history`]: {
        id: `/reference/databases/${database.id}/tables/${table.id}/history`,
        name: `Revision history`,
        breadcrumb: `${table.name}`,
        fetch: {fetchDatabaseMetadata: [database.id]},
        icon: "star",
        parent: getDatabaseSections(database)[`/reference/databases/${database.id}/tables`]
    }
} : {};

export const getSectionId = (state) => state.router.location.pathname;

export const getMetricId = (state) => state.router.params.metricId;
const getMetrics = (state) => state.metadata.metrics;
const getMetric = createSelector(
    [getMetricId, getMetrics],
    (metricId, metrics) => metrics[metricId] || { id: metricId }
);

export const getListId = (state) => state.router.params.listId;
const getLists = (state) => state.metadata.lists;
const getList = createSelector(
    [getListId, getLists],
    (listId, lists) => lists[listId] || { id: listId }
);

export const getDatabaseId = (state) => state.router.params.databaseId;
const getDatabases = (state) => state.metadata.databases;
const getDatabase = createSelector(
    [getDatabaseId, getDatabases],
    (databaseId, databases) => databases[databaseId] || { id: databaseId }
);

export const getTableId = (state) => state.router.params.tableId;
const getTables = createSelector(
    [getDatabase],
    (database) => database && database.tables ?
        database.tables.reduce((tableMap, table) => i.assoc(tableMap, table.id, table), {}) : {}
);

const getTable = createSelector(
    [getTableId, getTables],
    (tableId, tables) => tables[tableId] || { id: tableId }
);

const getQuestions = (state) => i.getIn(state, ['questions', 'entities', 'cards']) || {};
// TODO: get someone to look over this
// probably not be the best way to determine which questions are associated
// with a particular metric/list, but seems to work
const filterMetricQuestions = (metricId, question) => {
    const aggregation = i.getIn(question, ['dataset_query', 'query', 'aggregation']);
    if (!aggregation) {
        return false;
    }

    return aggregation[0] === "METRIC" && aggregation[1].toString() === metricId;
};

const getMetricQuestions = createSelector(
    [getMetricId, getQuestions],
    (metricId, questions) => Object.values(questions)
        .filter(question => filterMetricQuestions(metricId, question))
        .reduce((map, question) => i.assoc(map, question.id, question), {})
);

const filterListQuestions = (listId, question) => {
    const filter = i.getIn(question, ['dataset_query', 'query', 'filter']);
    if (!filter) {
        return false;
    }
    if (filter[0] === "AND") {
        const filters = filter.slice(1);
        const matchingFilters = filters
            .filter(filter => filter[0] === "SEGMENT" && filter[1].toString() === listId);

        return matchingFilters.length === 1;
    }

    return filter[0] === "SEGMENT" && filter[1].toString() === listId;
};

const getListQuestions = createSelector(
    [getListId, getQuestions],
    (listId, questions) => Object.values(questions)
        .filter(question => filterListQuestions(listId, question))
        .reduce((map, question) => i.assoc(map, question.id, question), {})
);

export const getSections = createSelector(
    [getSectionId, getMetric, getList, getDatabase, getTable, getReferenceSections],
    (sectionId, metric, list, database, table, referenceSections) => {
        // can be simplified if we had a single map of all sections
        if (referenceSections[sectionId]) {
            return referenceSections;
        }

        const metricSections = getMetricSections(metric);
        if (metricSections[sectionId]) {
            return metricSections;
        }

        const listSections = getListSections(list);
        if (listSections[sectionId]) {
            return listSections;
        }

        const databaseSections = getDatabaseSections(database);
        if (databaseSections[sectionId]) {
            return databaseSections;
        }

        const tableSections = getTableSections(database, table);
        if (tableSections[sectionId]) {
            return tableSections;
        }

        return {};
    }
);

export const getSection = createSelector(
    [getSectionId, getSections],
    (sectionId, sections) => sections[sectionId] || {}
);

const dataSelectors = {
    getMetric,
    getMetricQuestions,
    getMetrics,
    getList,
    getListQuestions,
    getLists,
    getDatabase,
    getDatabases,
    getTable,
    getTables,
};

export const getData = (state) => {
    const section = getSection(state);
    if (!section) {
        return {};
    }
    const selector = dataSelectors[section.get];
    if (!selector) {
        return {};
    }

    return selector(state);
};

const mapFetchToRequestStatePaths = (fetch) => {
    return Object.keys(fetch).map(key => {
        switch(key) {
            case 'fetchQuestions':
                return ['questions/all'];
            case 'fetchMetrics':
                return ['metadata/metrics'];
            case 'fetchLists':
                return ['metadata/lists'];
            case 'fetchDatabases':
                return ['metadata/databases'];
            case 'fetchDatabaseMetadata':
                return ['metadata/database'].concat(fetch[key]);
            default:
                return [];
        }
    });
};

const getRequests = (state) => i.getIn(state, ['requests']);

const getRequestPaths = createSelector(
    [getSection],
    (section) => mapFetchToRequestStatePaths(section.fetch)
);

export const getLoading = createSelector(
    [getRequestPaths, getRequests],
    (requestPaths, requests) => requestPaths
        .reduce((isLoading, requestPath) => isLoading || i.getIn(requests, requestPath) === 'LOADING', false)
)

export const getError = createSelector(
    [getRequestPaths, getRequests],
    (requestPaths, requests) => requestPaths
        .reduce((error, requestPath) => error || i.getIn(requests, requestPath.concat('error')), false)
)

const getBreadcrumb = (section, index, sections) => index !== sections.length - 1 ?
    [section.breadcrumb, section.id] : [section.breadcrumb];

const getParentSections = (section) => {
    if (!section.parent) {
        return [section];
    }

    const parentSections = []
        .concat(getParentSections(section.parent), section);

    return parentSections;
};

const buildBreadcrumbs = (section) => getParentSections(section).map(getBreadcrumb);

export const getBreadcrumbs = createSelector(
    [getSection],
    buildBreadcrumbs
)
<commit_msg>Added questions view for tables, added missing data fetches<commit_after>import { createSelector } from 'reselect';
import i from "icepick";

//TODO: definitely lots of memoization opportunities here

// there might be a better way to organize sections
// maybe merge all of them into a single map for simpler lookup?
// there might also be similar functionality in react/redux-router that could replace all of this
const referenceSections = {
    [`/reference/guide`]: {
        id: `/reference/guide`,
        name: "Understanding our data",
        breadcrumb: "Guide",
        icon: "all"
    },
    [`/reference/metrics`]: {
        id: `/reference/metrics`,
        name: "Metrics",
        breadcrumb: "Metrics",
        // mapping of propname to args of dispatch function
        fetch: {fetchMetrics: []},
        get: 'getMetrics',
        icon: "star"
    },
    [`/reference/lists`]: {
        id: `/reference/lists`,
        name: "Lists",
        breadcrumb: "Lists",
        fetch: {fetchLists: []},
        get: 'getLists',
        icon: "recents"
    },
    [`/reference/databases`]: {
        id: `/reference/databases`,
        name: "Databases and tables",
        breadcrumb: "Databases",
        fetch: {fetchDatabases: []},
        get: 'getDatabases',
        icon: "mine"
    }
};

const getReferenceSections = (state) => referenceSections;

const getMetricSections = (metric) => metric ? {
    [`/reference/metrics/${metric.id}`]: {
        id: `/reference/metrics/${metric.id}`,
        name: "Details",
        breadcrumb: `${metric.name}`,
        fetch: {fetchMetrics: []},
        get: 'getMetric',
        icon: "all",
        parent: referenceSections[`/reference/metrics`]
    },
    [`/reference/metrics/${metric.id}/questions`]: {
        id: `/reference/metrics/${metric.id}/questions`,
        name: "Questions about this metric",
        breadcrumb: `${metric.name}`,
        fetch: {fetchMetrics: [], fetchQuestions: []},
        get: 'getMetricQuestions',
        icon: "all",
        parent: referenceSections[`/reference/metrics`]
    },
    [`/reference/metrics/${metric.id}/history`]: {
        id: `/reference/metrics/${metric.id}/history`,
        name: "Revision history",
        breadcrumb: `${metric.name}`,
        icon: "all",
        parent: referenceSections[`/reference/metrics`]
    }
} : {};

const getListSections = (list) => list ? {
    [`/reference/lists/${list.id}`]: {
        id: `/reference/lists/${list.id}`,
        name: "Details",
        breadcrumb: `${list.name}`,
        fetch: {fetchLists: []},
        get: 'getList',
        icon: "all",
        parent: referenceSections[`/reference/lists`]
    },
    [`/reference/lists/${list.id}/fields`]: {
        id: `/reference/lists/${list.id}/fields`,
        name: "Fields in this list",
        breadcrumb: `${list.name}`,
        icon: "all",
        parent: referenceSections[`/reference/lists`]
    },
    [`/reference/lists/${list.id}/questions`]: {
        id: `/reference/lists/${list.id}/questions`,
        name: `Questions about ${list.name}`,
        breadcrumb: `${list.name}`,
        fetch: {fetchLists: [], fetchQuestions: []},
        get: 'getListQuestions',
        icon: "all",
        parent: referenceSections[`/reference/lists`]
    },
    [`/reference/lists/${list.id}/history`]: {
        id: `/reference/lists/${list.id}/history`,
        name: "Revision history",
        breadcrumb: `${list.name}`,
        icon: "all",
        parent: referenceSections[`/reference/lists`]
    }
} : {};

const getDatabaseSections = (database) => database ? {
    [`/reference/databases/${database.id}`]: {
        id: `/reference/databases/${database.id}`,
        name: "Details",
        breadcrumb: `${database.name}`,
        fetch: {fetchDatabaseMetadata: [database.id]},
        get: 'getDatabase',
        icon: "all",
        parent: referenceSections[`/reference/databases`]
    },
    [`/reference/databases/${database.id}/tables`]: {
        id: `/reference/databases/${database.id}/tables`,
        name: `Tables in this database`,
        breadcrumb: `${database.name}`,
        fetch: {fetchDatabaseMetadata: [database.id]},
        get: 'getTables',
        icon: "star",
        parent: referenceSections[`/reference/databases`]
    }
} : {};

const getTableSections = (database, table) => database && table ? {
    [`/reference/databases/${database.id}/tables/${table.id}`]: {
        id: `/reference/databases/${database.id}/tables/${table.id}`,
        name: "Details",
        breadcrumb: `${table.name}`,
        fetch: {fetchDatabaseMetadata: [database.id]},
        get: 'getTable',
        icon: "all",
        parent: getDatabaseSections(database)[`/reference/databases/${database.id}/tables`]
    },
    [`/reference/databases/${database.id}/tables/${table.id}/fields`]: {
        id: `/reference/databases/${database.id}/tables/${table.id}/fields`,
        name: `Fields in this table`,
        breadcrumb: `${table.name}`,
        fetch: {fetchDatabaseMetadata: [database.id]},
        icon: "star",
        parent: getDatabaseSections(database)[`/reference/databases/${database.id}/tables`]
    },
    [`/reference/databases/${database.id}/tables/${table.id}/questions`]: {
        id: `/reference/databases/${database.id}/tables/${table.id}/questions`,
        name: `Questions about this table`,
        breadcrumb: `${table.name}`,
        fetch: {fetchDatabaseMetadata: [database.id], fetchQuestions: []},
        get: 'getTableQuestions',
        icon: "star",
        parent: getDatabaseSections(database)[`/reference/databases/${database.id}/tables`]
    },
    [`/reference/databases/${database.id}/tables/${table.id}/history`]: {
        id: `/reference/databases/${database.id}/tables/${table.id}/history`,
        name: `Revision history`,
        breadcrumb: `${table.name}`,
        fetch: {fetchDatabaseMetadata: [database.id]},
        icon: "star",
        parent: getDatabaseSections(database)[`/reference/databases/${database.id}/tables`]
    }
} : {};

export const getSectionId = (state) => state.router.location.pathname;

export const getMetricId = (state) => state.router.params.metricId;
const getMetrics = (state) => state.metadata.metrics;
const getMetric = createSelector(
    [getMetricId, getMetrics],
    (metricId, metrics) => metrics[metricId] || { id: metricId }
);

export const getListId = (state) => state.router.params.listId;
const getLists = (state) => state.metadata.lists;
const getList = createSelector(
    [getListId, getLists],
    (listId, lists) => lists[listId] || { id: listId }
);

export const getDatabaseId = (state) => state.router.params.databaseId;
const getDatabases = (state) => state.metadata.databases;
const getDatabase = createSelector(
    [getDatabaseId, getDatabases],
    (databaseId, databases) => databases[databaseId] || { id: databaseId }
);

export const getTableId = (state) => state.router.params.tableId;
const getTables = createSelector(
    [getDatabase],
    (database) => database && database.tables ?
        database.tables.reduce((tableMap, table) => i.assoc(tableMap, table.id, table), {}) : {}
);

const getTable = createSelector(
    [getTableId, getTables],
    (tableId, tables) => tables[tableId] || { id: tableId }
);

const getQuestions = (state) => i.getIn(state, ['questions', 'entities', 'cards']) || {};
// TODO: get someone to look over this
// probably not be the best way to determine which questions are associated
// with a particular metric/list, but seems to work
const filterMetricQuestions = (metricId, question) => {
    const aggregation = i.getIn(question, ['dataset_query', 'query', 'aggregation']);
    if (!aggregation) {
        return false;
    }

    return aggregation[0] === "METRIC" && aggregation[1].toString() === metricId;
};

const getMetricQuestions = createSelector(
    [getMetricId, getQuestions],
    (metricId, questions) => Object.values(questions)
        .filter(question => filterMetricQuestions(metricId, question))
        .reduce((map, question) => i.assoc(map, question.id, question), {})
);

const filterListQuestions = (listId, question) => {
    const filter = i.getIn(question, ['dataset_query', 'query', 'filter']);
    if (!filter) {
        return false;
    }
    if (filter[0] === "AND") {
        const filters = filter.slice(1);
        const matchingFilters = filters
            .filter(filter => filter[0] === "SEGMENT" && filter[1].toString() === listId);

        return matchingFilters.length === 1;
    }

    return filter[0] === "SEGMENT" && filter[1].toString() === listId;
};

const getListQuestions = createSelector(
    [getListId, getQuestions],
    (listId, questions) => Object.values(questions)
        .filter(question => filterListQuestions(listId, question))
        .reduce((map, question) => i.assoc(map, question.id, question), {})
);

const getTableQuestions = createSelector(
    [getTable, getQuestions],
    (table, questions) => Object.values(questions)
        .filter(question => question.table_id === table.id)
);

export const getSections = createSelector(
    [getSectionId, getMetric, getList, getDatabase, getTable, getReferenceSections],
    (sectionId, metric, list, database, table, referenceSections) => {
        // can be simplified if we had a single map of all sections
        if (referenceSections[sectionId]) {
            return referenceSections;
        }

        const metricSections = getMetricSections(metric);
        if (metricSections[sectionId]) {
            return metricSections;
        }

        const listSections = getListSections(list);
        if (listSections[sectionId]) {
            return listSections;
        }

        const databaseSections = getDatabaseSections(database);
        if (databaseSections[sectionId]) {
            return databaseSections;
        }

        const tableSections = getTableSections(database, table);
        if (tableSections[sectionId]) {
            return tableSections;
        }

        return {};
    }
);

export const getSection = createSelector(
    [getSectionId, getSections],
    (sectionId, sections) => sections[sectionId] || {}
);

const dataSelectors = {
    getMetric,
    getMetricQuestions,
    getMetrics,
    getList,
    getListQuestions,
    getLists,
    getDatabase,
    getDatabases,
    getTable,
    getTableQuestions,
    getTables,
};

export const getData = (state) => {
    const section = getSection(state);
    if (!section) {
        return {};
    }
    const selector = dataSelectors[section.get];
    if (!selector) {
        return {};
    }

    return selector(state);
};

const mapFetchToRequestStatePaths = (fetch) => fetch ?
    Object.keys(fetch).map(key => {
        switch(key) {
            case 'fetchQuestions':
                return ['questions/all'];
            case 'fetchMetrics':
                return ['metadata/metrics'];
            case 'fetchLists':
                return ['metadata/lists'];
            case 'fetchDatabases':
                return ['metadata/databases'];
            case 'fetchDatabaseMetadata':
                return ['metadata/database'].concat(fetch[key]);
            default:
                return [];
        }
    }) : [];

const getRequests = (state) => i.getIn(state, ['requests']);

const getRequestPaths = createSelector(
    [getSection],
    (section) => mapFetchToRequestStatePaths(section.fetch)
);

export const getLoading = createSelector(
    [getRequestPaths, getRequests],
    (requestPaths, requests) => requestPaths
        .reduce((isLoading, requestPath) => isLoading || i.getIn(requests, requestPath) === 'LOADING', false)
)

export const getError = createSelector(
    [getRequestPaths, getRequests],
    (requestPaths, requests) => requestPaths
        .reduce((error, requestPath) => error || i.getIn(requests, requestPath.concat('error')), false)
)

const getBreadcrumb = (section, index, sections) => index !== sections.length - 1 ?
    [section.breadcrumb, section.id] : [section.breadcrumb];

const getParentSections = (section) => {
    if (!section.parent) {
        return [section];
    }

    const parentSections = []
        .concat(getParentSections(section.parent), section);

    return parentSections;
};

const buildBreadcrumbs = (section) => getParentSections(section).map(getBreadcrumb);

export const getBreadcrumbs = createSelector(
    [getSection],
    buildBreadcrumbs
)
<|endoftext|>


<commit_before>'use strict';

var nib = require('../').nib;
var log = require('../util/log');
var mapStream = require('map-stream');
var tap = require('tap-stream');

/**
 * Maps the npm username to a stream of package infos for packages the user owns.
 *
 * @name npmUserToPackageInfoStream
 * @function
 * @param npmPackages {Sublevel} npm.packages
 * @param byOwner {Sublevel} npm.byOwner
 * @param username {String} npm username
 * @return {Stream} packageInfos from npmPackages sublevel, each info has the following properties:
 *  - user: npm user name
 *  - pack: npm package info
 */
exports.npmUserToPackageInfoStream = function (npmPackages, byOwner, username) {

  function packInfo (name, cb) {
    npmPackages.get(name, function (err, pack) {
      if (err) log.warn('github-logins', 'name', name, err);
      if (!err && pack) cb(null, { user: name, pack: pack });
      else cb();
    });
  }

  return byOwner
    .createReadStream({ start: username, end: username + nib, keys: false, values: true })
    .pipe(mapStream(packInfo))
}

// Test
if (!module.parent) {
  var sublevels  =  require('./sublevels');
  var leveldb    =  require('./leveldb');
  var nestStream =  require('nest-stream');
  var from       =  require('from');

  leveldb.open(function (err, db) {
    if (err) return console.error(err);
    var subnpm = sublevels(db).npm;
    subnpm.users.get('thlorenz', function (err, res) {
      if (err) return console.error(err);
      console.log(res);
    });

    from([ 'raynos', 'thlorenz' ])
      .pipe(nestStream(exports.npmUserToPackageInfoStream.bind(null, subnpm.packages, subnpm.byOwner), ''))
      .pipe(tap());
  });
}
<commit_msg>fixing bug in map<commit_after>'use strict';

var nib = require('../').nib;
var log = require('../util/log');
var mapStream = require('map-stream');
var tap = require('tap-stream');

/**
 * Maps the npm username to a stream of package infos for packages the user owns.
 *
 * @name npmUserToPackageInfoStream
 * @function
 * @param npmPackages {Sublevel} npm.packages
 * @param byOwner {Sublevel} npm.byOwner
 * @param username {String} npm username
 * @return {Stream} packageInfos from npmPackages sublevel, each info has the following properties:
 *  - user: npm user name
 *  - pack: npm package info
 */
exports.npmUserToPackageInfoStream = function (npmPackages, byOwner, username) {

  function packInfo (name, cb) {
    npmPackages.get(name, function (err, pack) {
      if (err) log.warn('github-logins', 'name', name, err);
      if (!err && pack) cb(null, { user: username, pack: pack });
      else cb();
    });
  }

  return byOwner
    .createReadStream({ start: username, end: username + nib, keys: false, values: true })
    .pipe(mapStream(packInfo))
}

// Test
if (!module.parent) {
  var sublevels  =  require('./sublevels');
  var leveldb    =  require('./leveldb');
  var nestStream =  require('nest-stream');
  var from       =  require('from');

  leveldb.open(function (err, db) {
    if (err) return console.error(err);
    var subnpm = sublevels(db).npm;
    subnpm.users.get('thlorenz', function (err, res) {
      if (err) return console.error(err);
      console.log(res);
    });

    from([ 'raynos', 'thlorenz' ])
      .pipe(nestStream(exports.npmUserToPackageInfoStream.bind(null, subnpm.packages, subnpm.byOwner), ''))
      .pipe(tap());
  });
}
<|endoftext|>


<commit_before>// @flow

import '../../components/code';
import '../../components/layout';
import '../../components/primitives';

import { define } from 'skatejs';

import { Component } from '../../utils';

import './__samples__/with-lit-html';
import codeWithLitHtml from '!raw-loader!./__samples__/with-lit-html';
import codeWithLitHtmlHtml from '!raw-loader!./__samples__/with-lit-html.html';

@define
export default class extends Component {
  static is = 'x-pages-renderers-lit-html';
  render() {
    return this.$`
      ${this.$style}
      <x-layout title="LitHTML renderer">

          The

            LitHTML renderer

          allows you to render using
          LitHTML.

        <x-runnable
          code="${codeWithLitHtml}"
          html="${codeWithLitHtmlHtml}"></x-runnable>
      </x-layout>
    `;
  }
}
<commit_msg>docs(site): reference usage of lit html extended renderer<commit_after>// @flow

import '../../components/code';
import '../../components/layout';
import '../../components/primitives';

import { define } from 'skatejs';

import { Component } from '../../utils';

import './__samples__/with-lit-html';
import codeWithLitHtml from '!raw-loader!./__samples__/with-lit-html';
import codeWithLitHtmlHtml from '!raw-loader!./__samples__/with-lit-html.html';

@define
export default class extends Component {
  static is = 'x-pages-renderers-lit-html';
  render() {
    return this.$`
      ${this.$style}
      <x-layout title="LitHTML renderer">

          The

            LitHTML renderer

          allows you to render using
          LitHTML.

          Please note this renderer uses Lit HTML's extended renderer by default. This provides slightly different
          than default, and enhanced, functionality as described

            here
          .

        <x-runnable
          code="${codeWithLitHtml}"
          html="${codeWithLitHtmlHtml}"></x-runnable>
      </x-layout>
    `;
  }
}
<|endoftext|>