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

let _WebVRUI_css_injected = false;

/**
 * the css class prefix,
 * every element has a class pattern if "webvr-ui-${name}"
 * @type {string}
 */
export const cssPrefix = "webvr-ui";

/**
 * @private
 * generate the innerHTML for the button
 * @param {Number} fontSize
 * @returns {string}
 */
const generateInnerHTML = (fontSize, theme)=>{
    const svgString = generateVRIcon(fontSize, (theme == 'dark') ? 'white' : 'black');

    return `<button class="${cssPrefix}-button">

${svgString}

        </button>`;
};

/**
 * inject the CSS string to the head of the document
 * @param {string} cssText the css to inject
 */
export const injectCSS = (cssText)=>{
    // Make sure its only injected once
    if(!_WebVRUI_css_injected) {
        _WebVRUI_css_injected = true;

        // Create the css
        const style = document.createElement("style");
        style.innerHTML = cssText;

        var head = document.getElementsByTagName("head")[0];
        head.insertBefore(style,head.firstChild);
    }
};

/**
 * generate DOM element view for button
 * @param {Number} height
 * @param {boolean} [injectCSSStyles=true] inject the view's CSS into the DOM
 * @returns {HTMLElement}
 */
export const createView = (height, injectCSSStyles=true, theme='light')=>{
    const fontSize = height / 2.5;
    if(injectCSSStyles){
        injectCSS(generateCSS(height, fontSize, theme));
    }

    const el = document.createElement("div");
    el.innerHTML = generateInnerHTML(fontSize, theme);
    return el.firstChild;
};

/**
 * generate the VR Icons SVG
 * @param {Number} height
 * @param {string} [fill="#000000"]
 * @returns {string}
 */
export const generateVRIcon = (height, fill="#000000")=>{
    let aspect = 28/18;
    return `
        <svg class="${cssPrefix}-svg" version="1.1" x="0px" y="0px" width="${aspect*height}px" height="${height}px" viewBox="0 0 28 18" xml:space="preserve">
            <path fill="${fill}" d="M26.8,1.1C26.1,0.4,25.1,0,24.2,0H3.4c-1,0-1.7,0.4-2.4,1.1C0.3,1.7,0,2.7,0,3.6v10.7
            c0,1,0.3,1.9,0.9,2.6C1.6,17.6,2.4,18,3.4,18h5c0.7,0,1.3-0.2,1.8-0.5c0.6-0.3,1-0.8,1.3-1.4l1.5-2.6C13.2,13.1,13,13,14,13v0h-0.2
            h0c0.3,0,0.7,0.1,0.8,0.5l1.4,2.6c0.3,0.6,0.8,1.1,1.3,1.4c0.6,0.3,1.2,0.5,1.8,0.5h5c1,0,2-0.4,2.7-1.1c0.7-0.7,1.2-1.6,1.2-2.6
            V3.6C28,2.7,27.5,1.7,26.8,1.1z M7.4,11.8c-1.6,0-2.8-1.3-2.8-2.8c0-1.6,1.3-2.8,2.8-2.8c1.6,0,2.8,1.3,2.8,2.8
            C10.2,10.5,8.9,11.8,7.4,11.8z M20.1,11.8c-1.6,0-2.8-1.3-2.8-2.8c0-1.6,1.3-2.8,2.8-2.8C21.7,6.2,23,7.4,23,9
            C23,10.5,21.7,11.8,20.1,11.8z"/>
        </svg>
        <svg class="${cssPrefix}-svg-error" x="0px" y="0px" width="${aspect*height}px" height="${aspect*height}px" viewBox="0 0 28 28" xml:space="preserve">
            <path fill="${fill}" d="M17.6,13.4c0-0.2-0.1-0.4-0.1-0.6c0-1.6,1.3-2.8,2.8-2.8s2.8,1.3,2.8,2.8s-1.3,2.8-2.8,2.8
            c-0.2,0-0.4,0-0.6-0.1l5.9,5.9c0.5-0.2,0.9-0.4,1.3-0.8c0.7-0.7,1.1-1.6,1.1-2.5V7.4c0-1-0.4-1.9-1.1-2.5c-0.7-0.7-1.6-1-2.5-1H8.1
            L17.6,13.4z"/>
            <path fill="${fill}" d="M10.1,14.2c-0.5,0.9-1.4,1.4-2.4,1.4c-1.6,0-2.8-1.3-2.8-2.8c0-1.1,0.6-2,1.4-2.5L0.9,5.1
            C0.3,5.7,0,6.6,0,7.5v10.7c0,1,0.4,1.8,1.1,2.5c0.7,0.7,1.6,1,2.5,1h5c0.7,0,1.3-0.1,1.8-0.5c0.6-0.3,1-0.8,1.3-1.4l1.3-2.6
            L10.1,14.2z"/>
            <path fill="${fill}" d="M25.5,27.5l-25-25C-0.1,2-0.1,1,0.5,0.4l0,0C1-0.1,2-0.1,2.6,0.4l25,25c0.6,0.6,0.6,1.5,0,2.1l0,0
            C27,28.1,26,28.1,25.5,27.5z"/>
        </svg>

`;
};

/**
 * generate the CSS string to inject
 * @param {Number} [height=50]
 * @param {Number} [fontSize=18]
 * @param {string} [disabledColor="rgba(255,255,255,0.4)"]
 * @returns {string}
 */
export const generateCSS = (height=50, fontSize=18, theme='light')=>{
    let primaryColor = "white";
    let disabledColor = "rgba(255,255,255,0.5)";

    if(theme == 'dark'){
        primaryColor = "black";
        disabledColor = "rgba(0,0,0,0.5)";
    }

    let borderWidth = 2;
    let borderRadius = height / 2;
    // borderRadius = 0;

    return (`
        @font-face {
            font-family: 'Karla';
            font-style: normal;
            font-weight: 400;
            src: local('Karla'), local('Karla-Regular'), url(https://fonts.gstatic.com/s/karla/v5/31P4mP32i98D9CEnGyeX9Q.woff2) format('woff2');
            unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
        }
        @font-face {
            font-family: 'Karla';
            font-style: normal;
            font-weight: 400;
            src: local('Karla'), local('Karla-Regular'), url(https://fonts.gstatic.com/s/karla/v5/Zi_e6rBgGqv33BWF8WTq8g.woff2) format('woff2');
            unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
        }

        .${cssPrefix} {
            font-family: 'Karla', sans-serif;
        }

        button.${cssPrefix}-button {
            border: ${primaryColor} ${borderWidth}px solid;
            border-radius: ${borderRadius}px;
            box-sizing: border-box;
            background: rgba(0,0,0, 0);

            height: ${height}px;
            min-width: ${125}px;
            display: inline-block;
            position: relative;

            cursor: pointer;
            -webkit-transition: width 0.5s;
            transition: width 0.5s;
        }

        /*
        * Logo
        */

        .${cssPrefix}-logo {
            width: ${height}px;
            height: ${height}px;
            border-radius: ${borderRadius}px;
            background-color: ${primaryColor};
            position: absolute;
            top:-${borderWidth}px;
            left:-${borderWidth}px;
        }
        .${cssPrefix}-svg {
            margin-top: ${(height - fontSize) / 2}px;
        }
        .${cssPrefix}-svg-error {
            display:none;
        }

        /*
        * Title
        */

        .${cssPrefix}-title {
            color: ${primaryColor};
            position: relative;
            font-size: ${fontSize}px;
            top: -${borderWidth}px;
            line-height: ${height - borderWidth * 2}px;
            text-align: left;
            padding-left: ${height * 1.05}px;
            padding-right: ${(borderRadius-10 < 5) ? 5 : borderRadius-10}px;
        }

        /*
        * disabled
        */

        button.${cssPrefix}-button[disabled=true] {
            border-color: ${disabledColor};
        }
        button.${cssPrefix}-button[disabled=true] > .${cssPrefix}-logo {
            background-color: ${disabledColor};
            background-color: ${disabledColor};
            top:0;
            left:0;
            width: ${height-4}px;
            height: ${height-4}px;
        }
        button.${cssPrefix}-button[disabled=true] > .${cssPrefix}-logo > .${cssPrefix}-svg {
            display:none;
        }
        button.${cssPrefix}-button[disabled=true] > .${cssPrefix}-logo > .${cssPrefix}-svg-error {
            display:initial;
            margin-top: ${(height - 28/18 * fontSize) / 2 - 2}px;
            margin-left: -2px;
        }

        button.${cssPrefix}-button[disabled=true] > .${cssPrefix}-title {
            color: ${disabledColor};
        }

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

let _WebVRUI_css_injected = false;

/**
 * the css class prefix,
 * every element has a class pattern if "webvr-ui-${name}"
 * @type {string}
 */
export const cssPrefix = "webvr-ui";

/**
 * @private
 * generate the innerHTML for the button
 * @param {Number} fontSize
 * @param {String} theme either 'light' or 'dark'
 * @returns {string}
 */
const generateInnerHTML = (fontSize, theme)=>{
    const svgString = generateVRIcon(fontSize, (theme == 'dark') ? 'white' : 'black');

    return `<button class="${cssPrefix}-button">

${svgString}

        </button>`;
};

/**
 * inject the CSS string to the head of the document
 * @param {string} cssText the css to inject
 */
export const injectCSS = (cssText)=>{
    // Make sure its only injected once
    if(!_WebVRUI_css_injected) {
        _WebVRUI_css_injected = true;

        // Create the css
        const style = document.createElement("style");
        style.innerHTML = cssText;

        var head = document.getElementsByTagName("head")[0];
        head.insertBefore(style,head.firstChild);
    }
};

/**
 * generate DOM element view for button
 * @param {Number} height
 * @param {boolean} [injectCSSStyles=true] inject the view's CSS into the DOM
 * @param {String} theme either 'light' or 'dark'
 * @returns {HTMLElement}
 */
export const createView = (height, injectCSSStyles=true, theme='light')=>{
    const fontSize = height / 2.5;
    if(injectCSSStyles){
        injectCSS(generateCSS(height, fontSize, theme));
    }

    const el = document.createElement("div");
    el.innerHTML = generateInnerHTML(fontSize, theme);
    return el.firstChild;
};

/**
 * generate the VR Icons SVG
 * @param {Number} height
 * @param {string} [fill="#000000"]
 * @returns {string}
 */
export const generateVRIcon = (height, fill="#000000")=>{
    let aspect = 28/18;
    return `
        <svg class="${cssPrefix}-svg" version="1.1" x="0px" y="0px" width="${aspect*height}px" height="${height}px" viewBox="0 0 28 18" xml:space="preserve">
            <path fill="${fill}" d="M26.8,1.1C26.1,0.4,25.1,0,24.2,0H3.4c-1,0-1.7,0.4-2.4,1.1C0.3,1.7,0,2.7,0,3.6v10.7
            c0,1,0.3,1.9,0.9,2.6C1.6,17.6,2.4,18,3.4,18h5c0.7,0,1.3-0.2,1.8-0.5c0.6-0.3,1-0.8,1.3-1.4l1.5-2.6C13.2,13.1,13,13,14,13v0h-0.2
            h0c0.3,0,0.7,0.1,0.8,0.5l1.4,2.6c0.3,0.6,0.8,1.1,1.3,1.4c0.6,0.3,1.2,0.5,1.8,0.5h5c1,0,2-0.4,2.7-1.1c0.7-0.7,1.2-1.6,1.2-2.6
            V3.6C28,2.7,27.5,1.7,26.8,1.1z M7.4,11.8c-1.6,0-2.8-1.3-2.8-2.8c0-1.6,1.3-2.8,2.8-2.8c1.6,0,2.8,1.3,2.8,2.8
            C10.2,10.5,8.9,11.8,7.4,11.8z M20.1,11.8c-1.6,0-2.8-1.3-2.8-2.8c0-1.6,1.3-2.8,2.8-2.8C21.7,6.2,23,7.4,23,9
            C23,10.5,21.7,11.8,20.1,11.8z"/>
        </svg>
        <svg class="${cssPrefix}-svg-error" x="0px" y="0px" width="${aspect*height}px" height="${aspect*height}px" viewBox="0 0 28 28" xml:space="preserve">
            <path fill="${fill}" d="M17.6,13.4c0-0.2-0.1-0.4-0.1-0.6c0-1.6,1.3-2.8,2.8-2.8s2.8,1.3,2.8,2.8s-1.3,2.8-2.8,2.8
            c-0.2,0-0.4,0-0.6-0.1l5.9,5.9c0.5-0.2,0.9-0.4,1.3-0.8c0.7-0.7,1.1-1.6,1.1-2.5V7.4c0-1-0.4-1.9-1.1-2.5c-0.7-0.7-1.6-1-2.5-1H8.1
            L17.6,13.4z"/>
            <path fill="${fill}" d="M10.1,14.2c-0.5,0.9-1.4,1.4-2.4,1.4c-1.6,0-2.8-1.3-2.8-2.8c0-1.1,0.6-2,1.4-2.5L0.9,5.1
            C0.3,5.7,0,6.6,0,7.5v10.7c0,1,0.4,1.8,1.1,2.5c0.7,0.7,1.6,1,2.5,1h5c0.7,0,1.3-0.1,1.8-0.5c0.6-0.3,1-0.8,1.3-1.4l1.3-2.6
            L10.1,14.2z"/>
            <path fill="${fill}" d="M25.5,27.5l-25-25C-0.1,2-0.1,1,0.5,0.4l0,0C1-0.1,2-0.1,2.6,0.4l25,25c0.6,0.6,0.6,1.5,0,2.1l0,0
            C27,28.1,26,28.1,25.5,27.5z"/>
        </svg>

`;
};

/**
 * generate the CSS string to inject
 * @param {Number} [height=50]
 * @param {Number} [fontSize=18]
 * @param {string} theme either 'light' or 'dark'
 * @returns {string}
 */
export const generateCSS = (height=50, fontSize=18, theme='light')=>{
    let primaryColor = "white";
    let disabledColor = "rgba(255,255,255,0.5)";

    if(theme == 'dark'){
        primaryColor = "black";
        disabledColor = "rgba(0,0,0,0.5)";
    }

    let borderWidth = 2;
    let borderRadius = height / 2;
    // borderRadius = 0;

    return (`
        @font-face {
            font-family: 'Karla';
            font-style: normal;
            font-weight: 400;
            src: local('Karla'), local('Karla-Regular'), url(https://fonts.gstatic.com/s/karla/v5/31P4mP32i98D9CEnGyeX9Q.woff2) format('woff2');
            unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
        }
        @font-face {
            font-family: 'Karla';
            font-style: normal;
            font-weight: 400;
            src: local('Karla'), local('Karla-Regular'), url(https://fonts.gstatic.com/s/karla/v5/Zi_e6rBgGqv33BWF8WTq8g.woff2) format('woff2');
            unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
        }

        .${cssPrefix} {
            font-family: 'Karla', sans-serif;
        }

        button.${cssPrefix}-button {
            border: ${primaryColor} ${borderWidth}px solid;
            border-radius: ${borderRadius}px;
            box-sizing: border-box;
            background: rgba(0,0,0, 0);

            height: ${height}px;
            min-width: ${125}px;
            display: inline-block;
            position: relative;

            cursor: pointer;
            -webkit-transition: width 0.5s;
            transition: width 0.5s;
        }

        /*
        * Logo
        */

        .${cssPrefix}-logo {
            width: ${height}px;
            height: ${height}px;
            border-radius: ${borderRadius}px;
            background-color: ${primaryColor};
            position: absolute;
            top:-${borderWidth}px;
            left:-${borderWidth}px;
        }
        .${cssPrefix}-svg {
            margin-top: ${(height - fontSize) / 2}px;
        }
        .${cssPrefix}-svg-error {
            display:none;
        }

        /*
        * Title
        */

        .${cssPrefix}-title {
            color: ${primaryColor};
            position: relative;
            font-size: ${fontSize}px;
            top: -${borderWidth}px;
            line-height: ${height - borderWidth * 2}px;
            text-align: left;
            padding-left: ${height * 1.05}px;
            padding-right: ${(borderRadius-10 < 5) ? 5 : borderRadius-10}px;
        }

        /*
        * disabled
        */

        button.${cssPrefix}-button[disabled=true] {
            border-color: ${disabledColor};
        }
        button.${cssPrefix}-button[disabled=true] > .${cssPrefix}-logo {
            background-color: ${disabledColor};
            background-color: ${disabledColor};
            top:0;
            left:0;
            width: ${height-4}px;
            height: ${height-4}px;
        }
        button.${cssPrefix}-button[disabled=true] > .${cssPrefix}-logo > .${cssPrefix}-svg {
            display:none;
        }
        button.${cssPrefix}-button[disabled=true] > .${cssPrefix}-logo > .${cssPrefix}-svg-error {
            display:initial;
            margin-top: ${(height - 28/18 * fontSize) / 2 - 2}px;
            margin-left: -2px;
        }

        button.${cssPrefix}-button[disabled=true] > .${cssPrefix}-title {
            color: ${disabledColor};
        }

    `);
};
<|endoftext|>


<commit_before>let openapiUtil = require('../util/openapi');
let request = require('request');
let Action = require('./action');
let Response = require('./response');
let rssParser = require('rss-parser');
let zlib = require('zlib');
const ZLIB_OPTIONS = {
  flush: zlib.Z_SYNC_FLUSH,
  finishFlush: zlib.Z_SYNC_FLUSH
}

const BODY_METHODS = ['put', 'patch', 'post'];

const getActionFromOperation = module.exports = function(method, path, openapi, integration) {
  let op = openapi.paths[path][method];
  let params = op.parameters || [];
  let hasRequiredParam = !!params.filter(p => p.required).length;
  let inputSchema = {
    type: hasRequiredParam ? 'object' : ['object', 'null'],
    properties: {},
    additionalProperties: false,
    definitions: openapi.definitions,
  };
  let names = openapiUtil.getUniqueNames(params);
  params.forEach((param, idx) => {
    let name = names[idx];
    inputSchema.properties[name] = getSchemaFromParam(param);
    if (param.required) {
      inputSchema.required = inputSchema.required || [];
      inputSchema.required.push(name);
    }
  });
  let response = getDefaultResponse(op);
  let outputSchema = Object.assign({definitions: openapi.definitions}, response.schema);
  let actionSecurity = {};
  if (!op.security || op.security.length) {
    actionSecurity = integration.security;
  } else {
    actionSecurity[integration.id] = false;
  }
  return new Action({
    title: op.operationId || (method.toUpperCase() + ' ' + path),
    description: op.description || op.summary,
    inputSchema: params.length ? inputSchema : {},
    outputSchema: outputSchema,
    security: actionSecurity,
    ajv: integration.ajv,
    handler: function(input, ctx) {
      input = input || {};
      let reqOpts = {
        method,
        url: openapi.schemes[0] + '://' + openapi.host,
        qs: {},
        qsStringifyOptions: {},
        headers: {},
        form: {},
        body: null,
        encoding: null,
      }
      if (openapi.basePath && openapi.basePath !== '/') reqOpts.url += openapi.basePath;
      reqOpts.url += path;

      let addParam = (loc, name, val) => {
        if (loc === 'query') reqOpts.qs[name] = val;
        else if (loc === 'header') reqOpts.headers[name] = val;
        else if (loc === 'path') reqOpts.url = reqOpts.url.replace('{' + name + '}', val);
        else if (loc === 'formData') reqOpts.form[name] = val;
        else if (loc === 'body') reqOpts.body = JSON.stringify(val);
      }
      let names = openapiUtil.getUniqueNames(params);
      params.forEach((param, idx) => {
        let val = input[names[idx]];
        if (param.collectionFormat && Array.isArray(val)) {
          if (param.collectionFormat === 'multi') {
            reqOpts.qsStringifyOptions.arrayFormat = 'repeat';
          } else {
            reqOpts.qsStringifyOptions.sep = getCollectionFormatSeparator(param.collectionFormat);
          }
        }
        addParam(param.in, param.name, val);
      });

      let accountName = Object.keys(integration.security)[0];
      let account = ctx.accounts[accountName];
      let hasRefreshToken = false;
      let oauthDef = null;
      if (account) {
        for (let key in openapi.securityDefinitions || {}) {
          let def = openapi.securityDefinitions[key];
          if (def.type === 'basic' && account.username && account.password) {
            let details = account.username + ':' + account.password;
            addParam('header', 'Authorization', "Basic " + new Buffer(details, 'utf8').toString('base64'));
          } else if (def.type === 'apiKey' && account[key]) {
            addParam(def.in, def.name, account[key]);
          } else if (def.type === 'oauth2' && account.access_token) {
            hasRefreshToken = !!account.refresh_token;
            if (!oauthDef || oauthDef.flow === 'implicit') {
              oauthDef = def;
            }
            if (def.in) {
              addParam(def.in, def.name, account.access_token);
            } else {
              addParam('header', 'Authorization', "Bearer " + account.access_token);
            }
          }
        }
      }

      if (Object.keys(reqOpts.form).length === 0) delete reqOpts.form;

      let refreshOAuthToken = (callback) => {
        let form = {
          client_id: account.client_id,
          client_secret: account.client_secret,
          refresh_token: account.refresh_token,
          grant_type: 'refresh_token'
        };
        request.post({
          url: account.refresh_url || oauthDef.tokenUrl,
          json: true,
          form,
        }, (err, resp, body) => {
          if (err) return callback(err);
          if (resp.statusCode >= 300) return callback(new Error(resp.statusCode));
          account.access_token = body.access_token;
          addParam('header', 'Authorization', "Bearer " + body.access_token);
          callback();
        })
      }
      if (BODY_METHODS.indexOf(method) !== -1) {
        let consumes = op.consumes || ['application/json'];
        let cType = consumes.indexOf('application/json') === -1 ? consumes[0] : 'application/json';
        addParam('header', 'Content-Type', cType);
      }

      addParam('header', 'User-Agent', 'DataFire');

      let sendRequest = (resolve, reject, isRetry) => {
        request(reqOpts, (err, resp, body) => {
          if (err) {
            return reject(err);
          }
          if (body) {
            let encoding = resp.headers['content-encoding'];
            if (encoding === 'gzip') {
              body = zlib.gunzipSync(body, ZLIB_OPTIONS).toString('utf8');
            } else if (encoding === 'deflate') {
              body = zlib.inflateSync(body, ZLIB_OPTIONS).toString('utf8');
            } else {
              body = body.toString('utf8');
            }
          }
          if (!isRetry && resp.statusCode === 401 && hasRefreshToken) {
            refreshOAuthToken(err => {
              if (err) reject(new Response({statusCode: 401}));
              else sendRequest(resolve, reject, true);
            })
            return;
          } else if (resp.statusCode >= 300) {
            return reject(new Response({statusCode: resp.statusCode, body}));
          }
          let ctype = resp.headers['content-type'] || '';
          if (ctype.indexOf('application/json') !== -1) {
            body = JSON.parse(body);
            resolve(body);
          } else if (openapi.info['x-datafire'] && openapi.info['x-datafire'].type === 'rss') {
            rssParser.parseString(body, (err, feed) => {
              if (err) reject(err);
              else resolve(feed);
            })
          } else {
            resolve(body);
          }
        })
      }

      return new Promise(sendRequest);
    }
  });
}

const getSchemaFromParam = function(param) {
  if (param.in === 'body') return param.schema;
  let schema = {};
  schema.type = param.type === 'file' ? 'string' : param.type; // FIXME: handle file inputs
  openapiUtil.PARAM_SCHEMA_FIELDS.forEach(f => {
    if (param[f] !== undefined) schema[f] = param[f];
  })
  return schema;
}

const getDefaultResponse = function(op) {
  let keys = Object.keys(op.responses).sort();
  return op.responses[keys[0]];
}

<commit_msg>don't set undefined params<commit_after>let openapiUtil = require('../util/openapi');
let request = require('request');
let Action = require('./action');
let Response = require('./response');
let rssParser = require('rss-parser');
let zlib = require('zlib');
const ZLIB_OPTIONS = {
  flush: zlib.Z_SYNC_FLUSH,
  finishFlush: zlib.Z_SYNC_FLUSH
}

const BODY_METHODS = ['put', 'patch', 'post'];

const getActionFromOperation = module.exports = function(method, path, openapi, integration) {
  let op = openapi.paths[path][method];
  let params = op.parameters || [];
  let hasRequiredParam = !!params.filter(p => p.required).length;
  let inputSchema = {
    type: hasRequiredParam ? 'object' : ['object', 'null'],
    properties: {},
    additionalProperties: false,
    definitions: openapi.definitions,
  };
  let names = openapiUtil.getUniqueNames(params);
  params.forEach((param, idx) => {
    let name = names[idx];
    inputSchema.properties[name] = getSchemaFromParam(param);
    if (param.required) {
      inputSchema.required = inputSchema.required || [];
      inputSchema.required.push(name);
    }
  });
  let response = getDefaultResponse(op);
  let outputSchema = Object.assign({definitions: openapi.definitions}, response.schema);
  let actionSecurity = {};
  if (!op.security || op.security.length) {
    actionSecurity = integration.security;
  } else {
    actionSecurity[integration.id] = false;
  }
  return new Action({
    title: op.operationId || (method.toUpperCase() + ' ' + path),
    description: op.description || op.summary,
    inputSchema: params.length ? inputSchema : {},
    outputSchema: outputSchema,
    security: actionSecurity,
    ajv: integration.ajv,
    handler: function(input, ctx) {
      input = input || {};
      let reqOpts = {
        method,
        url: openapi.schemes[0] + '://' + openapi.host,
        qs: {},
        qsStringifyOptions: {},
        headers: {},
        form: {},
        body: null,
        encoding: null,
      }
      if (openapi.basePath && openapi.basePath !== '/') reqOpts.url += openapi.basePath;
      reqOpts.url += path;

      let addParam = (loc, name, val) => {
        if (val === undefined) return;
        if (loc === 'query') reqOpts.qs[name] = val;
        else if (loc === 'header') reqOpts.headers[name] = val;
        else if (loc === 'path') reqOpts.url = reqOpts.url.replace('{' + name + '}', val);
        else if (loc === 'formData') reqOpts.form[name] = val;
        else if (loc === 'body') reqOpts.body = JSON.stringify(val);
      }
      let names = openapiUtil.getUniqueNames(params);
      params.forEach((param, idx) => {
        let val = input[names[idx]];
        if (param.collectionFormat && Array.isArray(val)) {
          if (param.collectionFormat === 'multi') {
            reqOpts.qsStringifyOptions.arrayFormat = 'repeat';
          } else {
            reqOpts.qsStringifyOptions.sep = getCollectionFormatSeparator(param.collectionFormat);
          }
        }
        addParam(param.in, param.name, val);
      });

      let accountName = Object.keys(integration.security)[0];
      let account = ctx.accounts[accountName];
      let hasRefreshToken = false;
      let oauthDef = null;
      if (account) {
        for (let key in openapi.securityDefinitions || {}) {
          let def = openapi.securityDefinitions[key];
          if (def.type === 'basic' && account.username && account.password) {
            let details = account.username + ':' + account.password;
            addParam('header', 'Authorization', "Basic " + new Buffer(details, 'utf8').toString('base64'));
          } else if (def.type === 'apiKey' && account[key]) {
            addParam(def.in, def.name, account[key]);
          } else if (def.type === 'oauth2' && account.access_token) {
            hasRefreshToken = !!account.refresh_token;
            if (!oauthDef || oauthDef.flow === 'implicit') {
              oauthDef = def;
            }
            if (def.in) {
              addParam(def.in, def.name, account.access_token);
            } else {
              addParam('header', 'Authorization', "Bearer " + account.access_token);
            }
          }
        }
      }

      if (Object.keys(reqOpts.form).length === 0) delete reqOpts.form;

      let refreshOAuthToken = (callback) => {
        let form = {
          client_id: account.client_id,
          client_secret: account.client_secret,
          refresh_token: account.refresh_token,
          grant_type: 'refresh_token'
        };
        request.post({
          url: account.refresh_url || oauthDef.tokenUrl,
          json: true,
          form,
        }, (err, resp, body) => {
          if (err) return callback(err);
          if (resp.statusCode >= 300) return callback(new Error(resp.statusCode));
          account.access_token = body.access_token;
          addParam('header', 'Authorization', "Bearer " + body.access_token);
          callback();
        })
      }
      if (BODY_METHODS.indexOf(method) !== -1) {
        let consumes = op.consumes || ['application/json'];
        let cType = consumes.indexOf('application/json') === -1 ? consumes[0] : 'application/json';
        addParam('header', 'Content-Type', cType);
      }

      addParam('header', 'User-Agent', 'DataFire');

      let sendRequest = (resolve, reject, isRetry) => {
        request(reqOpts, (err, resp, body) => {
          if (err) {
            return reject(err);
          }
          if (body) {
            let encoding = resp.headers['content-encoding'];
            if (encoding === 'gzip') {
              body = zlib.gunzipSync(body, ZLIB_OPTIONS).toString('utf8');
            } else if (encoding === 'deflate') {
              body = zlib.inflateSync(body, ZLIB_OPTIONS).toString('utf8');
            } else {
              body = body.toString('utf8');
            }
          }
          if (!isRetry && resp.statusCode === 401 && hasRefreshToken) {
            refreshOAuthToken(err => {
              if (err) reject(new Response({statusCode: 401}));
              else sendRequest(resolve, reject, true);
            })
            return;
          } else if (resp.statusCode >= 300) {
            return reject(new Response({statusCode: resp.statusCode, body}));
          }
          let ctype = resp.headers['content-type'] || '';
          if (ctype.indexOf('application/json') !== -1) {
            body = JSON.parse(body);
            resolve(body);
          } else if (openapi.info['x-datafire'] && openapi.info['x-datafire'].type === 'rss') {
            rssParser.parseString(body, (err, feed) => {
              if (err) reject(err);
              else resolve(feed);
            })
          } else {
            resolve(body);
          }
        })
      }

      return new Promise(sendRequest);
    }
  });
}

const getSchemaFromParam = function(param) {
  if (param.in === 'body') return param.schema;
  let schema = {};
  schema.type = param.type === 'file' ? 'string' : param.type; // FIXME: handle file inputs
  openapiUtil.PARAM_SCHEMA_FIELDS.forEach(f => {
    if (param[f] !== undefined) schema[f] = param[f];
  })
  return schema;
}

const getDefaultResponse = function(op) {
  let keys = Object.keys(op.responses).sort();
  return op.responses[keys[0]];
}

<|endoftext|>


<commit_before>import jsan from 'jsan';
import logMonitorReducer from 'redux-devtools-log-monitor/lib/reducers';
import configureStore from '../../../app/store/configureStore';
import { isAllowed } from '../options/syncOptions';
import notifyErrors from '../utils/notifyErrors';

const monitorActions = [
  'TOGGLE_ACTION', 'SWEEP', 'SET_ACTIONS_ACTIVE',
  '@@redux-devtools-log-monitor/START_CONSECUTIVE_TOGGLE'
];

window.devToolsExtension = function(config = {}) {
  let liftedStore;
  if (!window.devToolsOptions) window.devToolsOptions = {};

  let localFilter;
  if (config.actionsBlacklist || config.actionsWhitelist) {
    localFilter = {
      whitelist: config.actionsWhitelist && config.actionsWhitelist.join('|'),
      blacklist: config.actionsBlacklist && config.actionsBlacklist.join('|')
    };
  }

  let shouldSerialize = false;
  let lastAction;
  let errorOccurred = false;
  let isMonitored = false;

  function stringify(obj) {
    return jsan.stringify(obj);
    /*
    return jsan.stringify(obj, function(key, value) {
      if (value && value.toJS) { return value.toJS(); }
      return value;
    }, null, true);
    */
  }

  function relaySerialized(message) {
    if (message.payload) message.payload = stringify(message.payload);
    if (message.action !== '') message.action = stringify(message.action);
    window.postMessage(message, '*');
  }

  function relay(type, state, action, nextActionId, isExcess) {
    setTimeout(() => {
      const message = {
        payload: type === 'STATE' && shouldFilter() ? filterActions(state) : state,
        action: action || '',
        nextActionId: nextActionId || '',
        isExcess,
        type,
        source: 'redux-page',
        name: config.name || document.title
      };
      if (shouldSerialize || window.devToolsOptions.serialize) {
        relaySerialized(message);
      } else {
        try {
          window.postMessage(message, '*');
        } catch (err) {
          relaySerialized(message);
          shouldSerialize = true;
        }
      }
    }, 0);
  }

  function onMessage(event) {
    if (!event || event.source !== window) {
      return;
    }

    const message = event.data;

    if (!message || message.source !== 'redux-cs') {
      return;
    }

    if (message.type === 'DISPATCH') {
      liftedStore.dispatch(message.payload);
    } else if (message.type === 'UPDATE') {
      relay('STATE', liftedStore.getState());
    } else if (message.type === 'START') {
      isMonitored = true;
      relay('STATE', liftedStore.getState());
    } else if (message.type === 'STOP') {
      isMonitored = false;
    }
  }

  const shouldFilter = () => localFilter || window.devToolsOptions.filter;
  function isFiltered(action) {
    if (!localFilter && !window.devToolsOptions.filter) return false;
    const { whitelist, blacklist } = localFilter || window.devToolsOptions;
    return (
      whitelist && !action.type.match(whitelist) ||
      blacklist && action.type.match(blacklist)
    );
  }
  function filterActions(state) {
    const filteredStagedActionIds = [];
    const filteredComputedStates = [];
    state.stagedActionIds.forEach((id, idx) => {
      if (!isFiltered(state.actionsById[id].action)) {
        filteredStagedActionIds.push(id);
        filteredComputedStates.push(state.computedStates[idx]);
      }
    });

    return { ...state,
      stagedActionIds: filteredStagedActionIds,
      computedStates: filteredComputedStates
    };
  }

  function init() {
    window.addEventListener('message', onMessage, false);
    relay('INIT_INSTANCE');
    notifyErrors(() => {
      errorOccurred = true;
      const state = liftedStore.getState();
      if (state.computedStates[state.currentStateIndex].error) {
        relay('STATE', state);
        return false;
      }
      return true;
    });

    // Detect when the tab is reactivated
    document.addEventListener('visibilitychange', function() {
      if (document.visibilityState === 'visible' && isMonitored) {
        relay('STATE', liftedStore.getState());
      }
    }, false);
  }

  function monitorReducer(state = {}, action) {
    if (!isMonitored) return state;
    lastAction = action.type;
    if (lastAction === '@@redux/INIT' && liftedStore) {
      // Send new lifted state on hot-reloading
      setTimeout(() => {
        relay('STATE', liftedStore.getState());
      }, 0);
    }
    return logMonitorReducer({}, state, action);
  }

  function handleChange(state, liftedState) {
    if (!isMonitored) return;
    const nextActionId = liftedState.nextActionId;
    const liftedAction = liftedState.actionsById[nextActionId - 1];
    const action = liftedAction.action;
    if (action.type === '@@INIT') {
      relay('INIT', state, { timestamp: Date.now() });
    } else if (!errorOccurred && monitorActions.indexOf(lastAction) === -1) {
      if (lastAction === 'JUMP_TO_STATE' || shouldFilter() && isFiltered(action)) return;
      const { maxAge } = window.devToolsOptions;
      const isExcess = maxAge && liftedState.stagedActionIds.length === maxAge;
      relay('ACTION', state, liftedAction, nextActionId, isExcess);
    } else {
      if (errorOccurred && !liftedState.computedStates[liftedState.currentStateIndex].error) errorOccurred = false;
      relay('STATE', liftedState);
    }
  }

  function extEnhancer(next) {
    return (reducer, initialState, enhancer) => {
      init();
      const store = next(reducer, initialState, enhancer);
      liftedStore = store.liftedStore;
      store.subscribe(() => {
        if (liftedStore !== store.liftedStore) liftedStore = store.liftedStore;
        handleChange(store.getState(), liftedStore.getState());
      });
      return store;
    };
  }

  if (!isAllowed(window.devToolsOptions)) return f => f;
  const { deserializeState, deserializeAction } = config;
  return configureStore(extEnhancer, monitorReducer, {
    deserializeState,
    deserializeAction
  });
};

window.devToolsExtension.open = function(position) {
  window.postMessage({
    source: 'redux-page',
    type: 'OPEN',
    position: position || ''
  }, '*');
};

window.devToolsExtension.notifyErrors = notifyErrors;
<commit_msg>Remove setTimeout<commit_after>import jsan from 'jsan';
import logMonitorReducer from 'redux-devtools-log-monitor/lib/reducers';
import configureStore from '../../../app/store/configureStore';
import { isAllowed } from '../options/syncOptions';
import notifyErrors from '../utils/notifyErrors';

const monitorActions = [
  'TOGGLE_ACTION', 'SWEEP', 'SET_ACTIONS_ACTIVE',
  '@@redux-devtools-log-monitor/START_CONSECUTIVE_TOGGLE'
];

window.devToolsExtension = function(config = {}) {
  let liftedStore;
  if (!window.devToolsOptions) window.devToolsOptions = {};

  let localFilter;
  if (config.actionsBlacklist || config.actionsWhitelist) {
    localFilter = {
      whitelist: config.actionsWhitelist && config.actionsWhitelist.join('|'),
      blacklist: config.actionsBlacklist && config.actionsBlacklist.join('|')
    };
  }

  let shouldSerialize = false;
  let lastAction;
  let errorOccurred = false;
  let isMonitored = false;

  function stringify(obj) {
    return jsan.stringify(obj);
    /*
    return jsan.stringify(obj, function(key, value) {
      if (value && value.toJS) { return value.toJS(); }
      return value;
    }, null, true);
    */
  }

  function relaySerialized(message) {
    if (message.payload) message.payload = stringify(message.payload);
    if (message.action !== '') message.action = stringify(message.action);
    window.postMessage(message, '*');
  }

  function relay(type, state, action, nextActionId, isExcess) {
    const message = {
      payload: type === 'STATE' && shouldFilter() ? filterActions(state) : state,
      action: action || '',
      nextActionId: nextActionId || '',
      isExcess,
      type,
      source: 'redux-page',
      name: config.name || document.title
    };
    if (shouldSerialize || window.devToolsOptions.serialize) {
      relaySerialized(message);
    } else {
      try {
        window.postMessage(message, '*');
      } catch (err) {
        relaySerialized(message);
        shouldSerialize = true;
      }
    }
  }

  function onMessage(event) {
    if (!event || event.source !== window) {
      return;
    }

    const message = event.data;

    if (!message || message.source !== 'redux-cs') {
      return;
    }

    if (message.type === 'DISPATCH') {
      liftedStore.dispatch(message.payload);
    } else if (message.type === 'UPDATE') {
      relay('STATE', liftedStore.getState());
    } else if (message.type === 'START') {
      isMonitored = true;
      relay('STATE', liftedStore.getState());
    } else if (message.type === 'STOP') {
      isMonitored = false;
    }
  }

  const shouldFilter = () => localFilter || window.devToolsOptions.filter;
  function isFiltered(action) {
    if (!localFilter && !window.devToolsOptions.filter) return false;
    const { whitelist, blacklist } = localFilter || window.devToolsOptions;
    return (
      whitelist && !action.type.match(whitelist) ||
      blacklist && action.type.match(blacklist)
    );
  }
  function filterActions(state) {
    const filteredStagedActionIds = [];
    const filteredComputedStates = [];
    state.stagedActionIds.forEach((id, idx) => {
      if (!isFiltered(state.actionsById[id].action)) {
        filteredStagedActionIds.push(id);
        filteredComputedStates.push(state.computedStates[idx]);
      }
    });

    return { ...state,
      stagedActionIds: filteredStagedActionIds,
      computedStates: filteredComputedStates
    };
  }

  function init() {
    window.addEventListener('message', onMessage, false);
    relay('INIT_INSTANCE');
    notifyErrors(() => {
      errorOccurred = true;
      const state = liftedStore.getState();
      if (state.computedStates[state.currentStateIndex].error) {
        relay('STATE', state);
        return false;
      }
      return true;
    });

    // Detect when the tab is reactivated
    document.addEventListener('visibilitychange', function() {
      if (document.visibilityState === 'visible' && isMonitored) {
        relay('STATE', liftedStore.getState());
      }
    }, false);
  }

  function monitorReducer(state = {}, action) {
    if (!isMonitored) return state;
    lastAction = action.type;
    if (lastAction === '@@redux/INIT' && liftedStore) {
      // Send new lifted state on hot-reloading
      setTimeout(() => {
        relay('STATE', liftedStore.getState());
      }, 0);
    }
    return logMonitorReducer({}, state, action);
  }

  function handleChange(state, liftedState) {
    if (!isMonitored) return;
    const nextActionId = liftedState.nextActionId;
    const liftedAction = liftedState.actionsById[nextActionId - 1];
    const action = liftedAction.action;
    if (action.type === '@@INIT') {
      relay('INIT', state, { timestamp: Date.now() });
    } else if (!errorOccurred && monitorActions.indexOf(lastAction) === -1) {
      if (lastAction === 'JUMP_TO_STATE' || shouldFilter() && isFiltered(action)) return;
      const { maxAge } = window.devToolsOptions;
      const isExcess = maxAge && liftedState.stagedActionIds.length === maxAge;
      relay('ACTION', state, liftedAction, nextActionId, isExcess);
    } else {
      if (errorOccurred && !liftedState.computedStates[liftedState.currentStateIndex].error) errorOccurred = false;
      relay('STATE', liftedState);
    }
  }

  function extEnhancer(next) {
    return (reducer, initialState, enhancer) => {
      init();
      const store = next(reducer, initialState, enhancer);
      liftedStore = store.liftedStore;
      store.subscribe(() => {
        if (liftedStore !== store.liftedStore) liftedStore = store.liftedStore;
        handleChange(store.getState(), liftedStore.getState());
      });
      return store;
    };
  }

  if (!isAllowed(window.devToolsOptions)) return f => f;
  const { deserializeState, deserializeAction } = config;
  return configureStore(extEnhancer, monitorReducer, {
    deserializeState,
    deserializeAction
  });
};

window.devToolsExtension.open = function(position) {
  window.postMessage({
    source: 'redux-page',
    type: 'OPEN',
    position: position || ''
  }, '*');
};

window.devToolsExtension.notifyErrors = notifyErrors;
<|endoftext|>


<commit_before>var TextHelper = require('../src/text-helper').TextHelper;

// simple terminal w no padding
var terminal = function() {
  return {
    getOffset: function() { return { l:0, t:0 }; },
    getLineOffset: function() { return { l:0, t:0 }; },
    getWidth: function() { return 400; },
    getCharDimes: function() { return { x:10, y:10 }; }
  };
};

var envStore = function(ctx) {
  return {
    latest: function() { return { ctx:ctx }; }
  };
};

var noop = function() {

};

describe('TextHelper', function() {
  describe('token help', function() {
    // just some integration tests

    it('should clear text and indication prior to new help', function() {
      var displayedMessages = [], events = [];
      var t = terminal();
      t.getText = function() { return "age is '1'\n"; };
      var th = new TextHelper(t, {}, envStore({ age: "1" }), {
        displayMessage: function(m) { displayedMessages.push(m); },
        indicate: function(__, e, __) {
          events.push(e);
        }
      });

      th.write({ event:"mousemove", point:{ x:5, y:5 } }); // in a of age
      expect(displayedMessages[0]).toEqual("");
      expect(events[0]).toEqual("clear");
    });

    it('should display var value in text box when hover over', function() {
      var displayedMessage;
      var t = terminal();
      t.getText = function() { return "age is '1'\n"; };
      var th = new TextHelper(t, {}, envStore({ age: "1" }), {
        displayMessage: function(m) { displayedMessage = m; },
        indicate:noop
      });

      th.write({ event:"mousemove", point:{ x:5, y:5 } }); // in a of age
      expect(displayedMessage).toEqual("'1'");
    });

    it('should indicate var value when hover over', function() {
      var event, data;
      var t = terminal();
      t.getText = function() { return "age is '1'\n"; };
      var th = new TextHelper(t, {}, envStore({ age: "1" }), {
        displayMessage: noop,
        indicate: function(__, e, d) {
          event = e;
          data = d;
        }
      });

      th.write({ event:"mousemove", point:{ x:5, y:5 } }); // in a of age
      expect(event).toEqual("indicate");
      expect(data.thing).toEqual("token");
      expect(data.index).toEqual(0);
    });
  });

  describe('line help', function() {
    // just some integration tests

    it('should clear text and indication prior to new help', function() {
      var displayedMessages = [], events = [];
      var t = terminal();
      t.getText = function() { return "age is '1'\n"; };
      var th = new TextHelper(t, {}, envStore({ age: "1" }), {
        displayMessage: function(m) { displayedMessages.push(m); },
        indicate: function(__, e, __) {
          events.push(e);
        }
      });

      th.write({ event:"mousemove", point:{ x:200, y:5 } }); // line position
      expect(displayedMessages[0]).toEqual("");
      expect(events[0]).toEqual("clear");
    });

    it('should display var value in text box when hover over', function() {
      var displayedMessage;
      var t = terminal();
      t.getText = function() { return "age is '1'\n"; };
      var th = new TextHelper(t, {}, envStore({ age: "1" }), {
        displayMessage: function(m) { displayedMessage = m; },
        indicate:noop
      });

      th.write({ event:"mousemove", point:{ x:200, y:5 } }); // line position
      expect(displayedMessage).toEqual("age is now '1'.");
    });

    it('should indicate var value when hover over', function() {
      var event, data;
      var t = terminal();
      t.getText = function() { return "age is '1'\n"; };
      var th = new TextHelper(t, {}, envStore({ age: "1" }), {
        displayMessage: noop,
        indicate: function(__, e, d) {
          event = e;
          data = d;
        }
      });

      th.write({ event:"mousemove", point:{ x:200, y:5 } }); // line position
      expect(event).toEqual("indicate");
      expect(data.thing).toEqual("line");
      expect(data.lineNumber).toEqual(0);
    });
  });

  describe('secondary help', function() {
    it('should clear text and indication prior to new help', function() {
      var displayedMessages = [], events = [];
      var th = new TextHelper({
        getText: function() { return ["1", "2"]; }
      }, {}, {}, {
        displayMessage: function(m) { displayedMessages.push(m); },
        indicate: function(__, e, __) {
          events.push(e);
        }
      });

      th.write({ event:"text:new" });
      expect(displayedMessages[0]).toEqual("");
      expect(events[0]).toEqual("clear");
    });

    it('should display if new console text if console has >1 line', function() {
      var displayed = false;
      var th = new TextHelper({
        getText: function() { return ["1", "2"]; }
      }, {}, {}, {
        displayMessage: function() { displayed = true; },
        indicate:noop
      });
      th.write({ event:"text:new" });
      expect(displayed).toEqual(true);
    });

    it('should display on mouseout if console has >1 line', function() {
      var displayed = false;
      var th = new TextHelper({
        getText: function() { return ["1", "2"]; }
      }, {}, {}, {
        displayMessage: function() { displayed = true; },
        indicate:noop
      });
      th.write({ event:"mouseout" });
      expect(displayed).toEqual(true);
    });

    it('should not display if console has 1 line', function() {
      var displayed = false;
      var th = new TextHelper({
        getText: function() { return ["1"]; }
      }, {}, {}, {
        displayMessage: function(m) { displayed = true; },
        indicate:noop
      });
      th.write({ event:"text:new" });
      th.write({ event:"mouseout" });
      expect(displayed).toEqual(false);
    });
  });
});
<commit_msg>Make terminal creation fn do more.<commit_after>var TextHelper = require('../src/text-helper').TextHelper;

// simple terminal w no padding
var term = function(text) {
  return {
    getText: function() { return text; },
    getOffset: function() { return { l:0, t:0 }; },
    getLineOffset: function() { return { l:0, t:0 }; },
    getWidth: function() { return 400; },
    getCharDimes: function() { return { x:10, y:10 }; }
  };
};

var envStore = function(ctx) {
  return {
    latest: function() { return { ctx:ctx }; }
  };
};

var noop = function() {

};

describe('TextHelper', function() {
  describe('token help', function() {
    // just some integration tests

    it('should clear text and indication prior to new help', function() {
      var displayedMessages = [], events = [];
      var th = new TextHelper(term("a is '1'\n"), {}, envStore({ a:"1" }), {
        displayMessage: function(m) { displayedMessages.push(m); },
        indicate: function(__, e, __) {
          events.push(e);
        }
      });

      th.write({ event:"mousemove", point:{ x:5, y:5 } }); // in a of age
      expect(displayedMessages[0]).toEqual("");
      expect(events[0]).toEqual("clear");
    });

    it('should display var value in text box when hover over', function() {
      var displayedMessage;
      var th = new TextHelper(term("a is '1'\n"), {}, envStore({ a:"1" }), {
        displayMessage: function(m) { displayedMessage = m; },
        indicate:noop
      });

      th.write({ event:"mousemove", point:{ x:5, y:5 } }); // in a of age
      expect(displayedMessage).toEqual("'1'");
    });

    it('should indicate var value when hover over', function() {
      var event, data;
      var th = new TextHelper(term("a is '1'\n"), {}, envStore({ a:"1" }), {
        displayMessage: noop,
        indicate: function(__, e, d) {
          event = e;
          data = d;
        }
      });

      th.write({ event:"mousemove", point:{ x:5, y:5 } }); // in a of age
      expect(event).toEqual("indicate");
      expect(data.thing).toEqual("token");
      expect(data.index).toEqual(0);
    });
  });

  describe('line help', function() {
    // just some integration tests

    it('should clear text and indication prior to new help', function() {
      var displayedMessages = [], events = [];
      var th = new TextHelper(term("a is '1'\n"), {}, envStore({ a:"1" }), {
        displayMessage: function(m) { displayedMessages.push(m); },
        indicate: function(__, e, __) {
          events.push(e);
        }
      });

      th.write({ event:"mousemove", point:{ x:200, y:5 } }); // line position
      expect(displayedMessages[0]).toEqual("");
      expect(events[0]).toEqual("clear");
    });

    it('should display var value in text box when hover over', function() {
      var displayedMessage;
      var th = new TextHelper(term("a is '1'\n"), {}, envStore({ a:"1" }), {
        displayMessage: function(m) { displayedMessage = m; },
        indicate:noop
      });

      th.write({ event:"mousemove", point:{ x:200, y:5 } }); // line position
      expect(displayedMessage).toEqual("a is now '1'.");
    });

    it('should indicate var value when hover over', function() {
      var event, data;
      var th = new TextHelper(term("a is '1'\n"), {}, envStore({ a:"1" }), {
        displayMessage: noop,
        indicate: function(__, e, d) {
          event = e;
          data = d;
        }
      });

      th.write({ event:"mousemove", point:{ x:200, y:5 } }); // line position
      expect(event).toEqual("indicate");
      expect(data.thing).toEqual("line");
      expect(data.lineNumber).toEqual(0);
    });
  });

  describe('secondary help', function() {
    it('should clear text and indication prior to new help', function() {
      var displayedMessages = [], events = [];
      var th = new TextHelper(term("1\n2"), {}, {}, {
        displayMessage: function(m) { displayedMessages.push(m); },
        indicate: function(__, e, __) {
          events.push(e);
        }
      });

      th.write({ event:"text:new" });
      expect(displayedMessages[0]).toEqual("");
      expect(events[0]).toEqual("clear");
    });

    it('should display if new console text if console has >1 line', function() {
      var displayed = false;
      var th = new TextHelper(term("1\n2"), {}, {}, {
        displayMessage: function() { displayed = true; },
        indicate:noop
      });
      th.write({ event:"text:new" });
      expect(displayed).toEqual(true);
    });

    it('should display on mouseout if console has >1 line', function() {
      var displayed = false;
      var th = new TextHelper(term("1\n2"), {}, {}, {
        displayMessage: function() { displayed = true; },
        indicate:noop
      });
      th.write({ event:"mouseout" });
      expect(displayed).toEqual(true);
    });

    it('should not display if console has 1 line', function() {
      var displayed = false;
      var th = new TextHelper(term("1"), {}, {}, {
        displayMessage: function(m) { displayed = true; },
        indicate:noop
      });
      th.write({ event:"text:new" });
      th.write({ event:"mouseout" });
      expect(displayed).toEqual(false);
    });
  });
});
<|endoftext|>


<commit_before>import { toArray } from './util'

export function $ (selector, el = document) {
  return el.querySelector(selector)
}

export function $$ (selector, el = document) {
  return toArray(el.querySelectorAll(selector))
}

export function id (name, el = document) {
  return el.getElementById(name)
}

export function replace (oldElement, newElement) {
  oldElement.parentNode.replaceChild(newElement, oldElement)
  return newElement
}

export function index (el) {
  let idx = 0
  while (el) {
    idx++
    el = el.previousElementSibling
  }
  return idx
}

export function remove (el) {
  el.parentNode && el.parentNode.removeChild(el)
  return el
}

export function rect (el) {
  const rect = el.getBoundingClientRect()

  return {
    top: window.pageYOffset + rect.top,
    left: window.pageXOffset + rect.left,
    width: rect.width,
    height: rect.height
  }
}

export function className (name) {
  return {
    add: (el, ...args) => {
      addClass(el, args.concat(name))
      return this
    },
    remove: (el, ...args) => {
      removeClass(el, args.concat(name))
      return this
    },
    toggle: (el, force = undefined) => {
      toggleClass(el, name, force)
      return this
    },
    selector: () => `.${name}`,
    toString: () => name
  }
}

export function addClass (el, ...args) {
  el.classList.add(...args)
  return el
}

export function toggleClass (el, className, force = undefined) {
  el.classList.toggle(className, force)
  return el
}

export function removeClass (el, ...args) {
  el.classList.remove(...args)
  return el
}

export function attr (name) {
  return {
    get: (el, ...args) => {
      getAttr(el, name, ...args)
      return this
    },
    set: (el, ...args) => {
      setAttr(el, name, ...args)
      return this
    },
    has: el => hasAttr(el, name),
    selector: (value = undefined) => `[${name}${value ? `="${value}"` : ''}]`,
    toString: () => name
  }
}

export function getAttr (el, name, defaultValue = undefined) {
  return el && el.hasAttribute(name) ? el.getAttribute(name) : defaultValue
}

export function setAttr (el, name, value, append = false) {
  const attrValue = append ? getAttr(el, name, '') + ' ' + value : value
  el.setAttribute(name, attrValue)
  return el
}

export function hasAttr (el, name) {
  return el && el.hasAttribute(name)
}

export function insertElement (position, node, newNode) {
  switch (position) {
    case 'beforebegin':
      node.parentNode.insertBefore(newNode, node)
      break
    case 'afterend':
      node.parentNode.insertBefore(newNode, node.nextElementSibling)
      break
  }

  return newNode
}

insertElement.before = (node, newNode) => insertElement('beforebegin', node, newNode)
insertElement.after = (node, newNode) => insertElement('afterend', node, newNode)

export default {
  $,
  $$,
  id,
  replace,
  index,
  remove,
  rect,
  className,
  addClass,
  toggleClass,
  removeClass,
  attr,
  getAttr,
  setAttr,
  hasAttr,
  insertElement
}
<commit_msg>apply addClass/removeClass args as argument list not single array arg<commit_after>import { toArray } from './util'

export function $ (selector, el = document) {
  return el.querySelector(selector)
}

export function $$ (selector, el = document) {
  return toArray(el.querySelectorAll(selector))
}

export function id (name, el = document) {
  return el.getElementById(name)
}

export function replace (oldElement, newElement) {
  oldElement.parentNode.replaceChild(newElement, oldElement)
  return newElement
}

export function index (el) {
  let idx = 0
  while (el) {
    idx++
    el = el.previousElementSibling
  }
  return idx
}

export function remove (el) {
  el.parentNode && el.parentNode.removeChild(el)
  return el
}

export function rect (el) {
  const rect = el.getBoundingClientRect()

  return {
    top: window.pageYOffset + rect.top,
    left: window.pageXOffset + rect.left,
    width: rect.width,
    height: rect.height
  }
}

export function className (name) {
  return {
    add: (el, ...args) => {
      addClass(el, ...args.concat(name))
      return this
    },
    remove: (el, ...args) => {
      removeClass(el, ...args.concat(name))
      return this
    },
    toggle: (el, force = undefined) => {
      toggleClass(el, name, force)
      return this
    },
    selector: () => `.${name}`,
    toString: () => name
  }
}

export function addClass (el, ...args) {
  el.classList.add(...args)
  return el
}

export function toggleClass (el, className, force = undefined) {
  el.classList.toggle(className, force)
  return el
}

export function removeClass (el, ...args) {
  el.classList.remove(...args)
  return el
}

export function attr (name) {
  return {
    get: (el, ...args) => {
      getAttr(el, name, ...args)
      return this
    },
    set: (el, ...args) => {
      setAttr(el, name, ...args)
      return this
    },
    has: el => hasAttr(el, name),
    selector: (value = undefined) => `[${name}${value ? `="${value}"` : ''}]`,
    toString: () => name
  }
}

export function getAttr (el, name, defaultValue = undefined) {
  return el && el.hasAttribute(name) ? el.getAttribute(name) : defaultValue
}

export function setAttr (el, name, value, append = false) {
  const attrValue = append ? getAttr(el, name, '') + ' ' + value : value
  el.setAttribute(name, attrValue)
  return el
}

export function hasAttr (el, name) {
  return el && el.hasAttribute(name)
}

export function insertElement (position, node, newNode) {
  switch (position) {
    case 'beforebegin':
      node.parentNode.insertBefore(newNode, node)
      break
    case 'afterend':
      node.parentNode.insertBefore(newNode, node.nextElementSibling)
      break
  }

  return newNode
}

insertElement.before = (node, newNode) => insertElement('beforebegin', node, newNode)
insertElement.after = (node, newNode) => insertElement('afterend', node, newNode)

export default {
  $,
  $$,
  id,
  replace,
  index,
  remove,
  rect,
  className,
  addClass,
  toggleClass,
  removeClass,
  attr,
  getAttr,
  setAttr,
  hasAttr,
  insertElement
}
<|endoftext|>


<commit_before>import * as vegaUtil from 'vega-util'
import createVegaLiteMixin from 'src/mixin/createVegaLiteMixin';
import MarkOptionMissedError from 'src/error/MarkOptionMissedError';
import EncodingOptionMissedError from 'src/error/EncodingOptionMissedError';

const sandbox = sinon.sandbox.create()

describe('createVegaLiteMixin', () => {
  let vegaLiteMixin

  beforeEach(() => {
    vegaLiteMixin = createVegaLiteMixin()
  })

  afterEach(() => {
    sandbox.restore()
  })

  describe('beforeCreate', () => {
    let $options
    let context

    beforeEach(() => {
      $options = {
        data () {
          return {values: [1, 2, 3]}
        }
      }
    })

    it('should throw MarkOptionMissedError if option doesn\'t contain `mark` field', () => {
      context = {
        $options: Object.assign({encoding: {}}, $options)
      }

      expect(() => {
        vegaLiteMixin.beforeCreate.call(context)
      }).to.throw(MarkOptionMissedError)
    })

    it('should throw MarkOptionMissedError if option doesn\'t contain `encoding` field', () => {
      context = {
        $options: Object.assign({mark: 'blabla'}, $options)
      }

      expect(() => {
        vegaLiteMixin.beforeCreate.call(context)
      }).to.throw(EncodingOptionMissedError)
    })

    it('should create vega spec object from options', () => {
      context = {
        $options: Object.assign({encoding: {}, mark: 'blabla'}, $options)
      }

      vegaLiteMixin.beforeCreate.call(context)

      expect(context.$spec).to.deep.equal({
        '$schema': 'https://vega.github.io/schema/vega-lite/v2.json',
        data: {
          values: [1, 2, 3]
        },
        mark: 'blabla',
        encoding: {}
      })
    })
  })

  describe('created', () => {
    let View
    let view
    let parse
    let compile
    let spec
    let context
    let compilerOutput
    let runtime
    let logLevel = vegaUtil.Debug

    beforeEach(() => {
      spec = {
        data: {
          values: [
            {a: 'A', b: 28}, {a: 'B', b: 55}, {a: 'C', b: 43},
            {a: 'D', b: 91}, {a: 'E', b: 81}, {a: 'F', b: 53},
            {a: 'G', b: 19}, {a: 'H', b: 87}, {a: 'I', b: 52}
          ]
        },
        mark: 'bar',
        encoding: {
          x: {field: 'a', type: 'ordinal'},
          y: {field: 'b', type: 'quantitative'}
        }
      }
      context = {
        description: 'description',
        $spec: spec
      }
      compilerOutput = {
        spec: 'vegaSpecFromVegaLite'
      }
      runtime = 'runtime'

      View = sandbox.stub()
      parse = sandbox.stub()
      compile = sandbox.stub()

      view = {
        logLevel: sandbox.stub(),
        renderer: sandbox.stub(),
        hover: sandbox.stub()
      }

      view.logLevel.withArgs(logLevel).returns(view)
      view.renderer.withArgs('svg').returns(view)
      view.hover.returns(view)

      compile.withArgs(context.$spec).returns(compilerOutput)
      parse.withArgs(compilerOutput.spec).returns(runtime)
      View.withArgs(runtime).returns(view)

      vegaLiteMixin = createVegaLiteMixin({
        compile,
        parse,
        View,
        logLevel
      })
    })

    it('should add description to spec', () => {
      vegaLiteMixin.created.call(context)

      expect(context.$spec.description).to.equal(context.description)
    })

    it('should compile spec from context and pass it to parser', () => {
      vegaLiteMixin.created.call(context)

      expect(parse).to.have.been.calledWith(compilerOutput.spec)
    })

    it('should parse spec and create View within generated runtime', () => {
      vegaLiteMixin.created.call(context)

      expect(View).to.have.been.calledWith(runtime)
    })

    it('should create View instance as $vg in a context', () => {
      vegaLiteMixin.created.call(context)

      expect(context.$vg).to.equal(view)
    })

    it('should set proper log level, renderer and enable hover', () => {
      vegaLiteMixin.created.call(context)

      expect(view.logLevel).to.have.been.calledWith(logLevel)
      expect(view.renderer).to.have.been.calledWith('svg')
      expect(view.hover).to.have.been.called
    })
  })
})
<commit_msg>- cover mount and destroy hooks<commit_after>import * as vegaUtil from 'vega-util'
import createVegaLiteMixin from 'src/mixin/createVegaLiteMixin';
import MarkOptionMissedError from 'src/error/MarkOptionMissedError';
import EncodingOptionMissedError from 'src/error/EncodingOptionMissedError';

const sandbox = sinon.sandbox.create()

describe('createVegaLiteMixin', () => {
  let vegaLiteMixin

  beforeEach(() => {
    vegaLiteMixin = createVegaLiteMixin()
  })

  afterEach(() => {
    sandbox.restore()
  })

  describe('beforeCreate', () => {
    let $options
    let context

    beforeEach(() => {
      $options = {
        data () {
          return {values: [1, 2, 3]}
        }
      }
    })

    it('should throw MarkOptionMissedError if option doesn\'t contain `mark` field', () => {
      context = {
        $options: Object.assign({encoding: {}}, $options)
      }

      expect(() => {
        vegaLiteMixin.beforeCreate.call(context)
      }).to.throw(MarkOptionMissedError)
    })

    it('should throw MarkOptionMissedError if option doesn\'t contain `encoding` field', () => {
      context = {
        $options: Object.assign({mark: 'blabla'}, $options)
      }

      expect(() => {
        vegaLiteMixin.beforeCreate.call(context)
      }).to.throw(EncodingOptionMissedError)
    })

    it('should create vega spec object from options', () => {
      context = {
        $options: Object.assign({encoding: {}, mark: 'blabla'}, $options)
      }

      vegaLiteMixin.beforeCreate.call(context)

      expect(context.$spec).to.deep.equal({
        '$schema': 'https://vega.github.io/schema/vega-lite/v2.json',
        data: {
          values: [1, 2, 3]
        },
        mark: 'blabla',
        encoding: {}
      })
    })
  })

  describe('created', () => {
    let View
    let view
    let parse
    let compile
    let spec
    let context
    let compilerOutput
    let runtime
    let logLevel = vegaUtil.Debug

    beforeEach(() => {
      spec = {
        data: {
          values: [
            {a: 'A', b: 28}, {a: 'B', b: 55}, {a: 'C', b: 43},
            {a: 'D', b: 91}, {a: 'E', b: 81}, {a: 'F', b: 53},
            {a: 'G', b: 19}, {a: 'H', b: 87}, {a: 'I', b: 52}
          ]
        },
        mark: 'bar',
        encoding: {
          x: {field: 'a', type: 'ordinal'},
          y: {field: 'b', type: 'quantitative'}
        }
      }
      context = {
        description: 'description',
        $spec: spec
      }
      compilerOutput = {
        spec: 'vegaSpecFromVegaLite'
      }
      runtime = 'runtime'

      View = sandbox.stub()
      parse = sandbox.stub()
      compile = sandbox.stub()

      view = {
        logLevel: sandbox.stub(),
        renderer: sandbox.stub(),
        hover: sandbox.stub()
      }

      view.logLevel.withArgs(logLevel).returns(view)
      view.renderer.withArgs('svg').returns(view)
      view.hover.returns(view)

      compile.withArgs(context.$spec).returns(compilerOutput)
      parse.withArgs(compilerOutput.spec).returns(runtime)
      View.withArgs(runtime).returns(view)

      vegaLiteMixin = createVegaLiteMixin({
        compile,
        parse,
        View,
        logLevel
      })
    })

    it('should add description to spec', () => {
      vegaLiteMixin.created.call(context)

      expect(context.$spec.description).to.equal(context.description)
    })

    it('should compile spec from context and pass it to parser', () => {
      vegaLiteMixin.created.call(context)

      expect(parse).to.have.been.calledWith(compilerOutput.spec)
    })

    it('should parse spec and create View within generated runtime', () => {
      vegaLiteMixin.created.call(context)

      expect(View).to.have.been.calledWith(runtime)
    })

    it('should create View instance as $vg in a context', () => {
      vegaLiteMixin.created.call(context)

      expect(context.$vg).to.equal(view)
    })

    it('should set proper log level, renderer and enable hover', () => {
      vegaLiteMixin.created.call(context)

      expect(view.logLevel).to.have.been.calledWith(logLevel)
      expect(view.renderer).to.have.been.calledWith('svg')
      expect(view.hover).to.have.been.called
    })
  })

  describe('mounted', () => {
    it('should initialize view within dom elem of component and run', () => {
      let view = {
        initialize: sandbox.stub(),
        run: sandbox.stub()
      }
      let context = {
        $vg: view,
        $el: 'el'
      }

      view.initialize.returns(view)
      view.run.returns(view)

      vegaLiteMixin.mounted.call(context)

      expect(view.initialize).to.have.been.calledWith(context.$el)
      expect(view.run).to.have.been.called
    })
  })

  describe('destroyed', () => {
    it('should finilize view', () => {
      let view = {
        finalize: sandbox.stub()
      }
      let context = {
        $vg: view
      }

      vegaLiteMixin.destroyed.call(context)

      expect(view.finalize).to.have.been.called
    })
  })
})
<|endoftext|>


<commit_before>/**
 * Created by GUY on 2017/2/8.
 *
 * @class BI.BubblePopupView
 * @extends BI.PopupView
 */
BI.BubblePopupView = BI.inherit(BI.PopupView, {
    _defaultConfig: function () {
        var config = BI.BubblePopupView.superclass._defaultConfig.apply(this, arguments);
        return BI.extend(config, {
            baseCls: config.baseCls + " bi-bubble-popup-view",
            minWidth: 70,
            maxWidth: 300,
            minHeight: 50
        });
    }
});

BI.shortcut("bi.bubble_popup_view", BI.BubblePopupView);

/**
 * Created by GUY on 2017/2/8.
 *
 * @class BI.BubblePopupBarView
 * @extends BI.BubblePopupView
 */
BI.BubblePopupBarView = BI.inherit(BI.BubblePopupView, {
    _defaultConfig: function () {
        return BI.extend(BI.BubblePopupBarView.superclass._defaultConfig.apply(this, arguments), {
            extraCls: "bi-bubble-bar-popup-view",
            buttons: [{
                value: false,
                text: BI.i18nText("BI-Basic_Cancel"),
                ghost: true
            }, {
                text: BI.i18nText(BI.i18nText("BI-Basic_OK")),
                value: true
            }]
        });
    },
    _init: function () {
        BI.BubblePopupBarView.superclass._init.apply(this, arguments);
    },
    _createToolBar: function () {
        var o = this.options, self = this;

        var items = [];
        BI.each(o.buttons, function (i, buttonOpt) {
            if (BI.isWidget(buttonOpt)) {
                items.push({
                    el: buttonOpt,
                    lgap: i === 0 ? 20 : 15,
                    rgap: i === o.buttons.length - 1 ? 20 : 0
                });
            } else {
                items.push({
                    el: BI.extend({
                        type: "bi.button",
                        height: 24,
                        handler: function (v) {
                            self.fireEvent(BI.BubblePopupBarView.EVENT_CLICK_TOOLBAR_BUTTON, v);
                        }
                    }, buttonOpt),
                    lgap: i === 0 ? 20 : 15,
                    rgap: i === o.buttons.length - 1 ? 20 : 0
                });
            }
        });
        return BI.createWidget({
            type: "bi.right_vertical_adapt",
            height: 54,
            items: items
        });
    },

    _createView: function () {
        var o = this.options;

        var button = BI.createWidget({
            type: "bi.button_group",
            items: [o.el],
            layouts: [{
                type: "bi.vertical",
                cls: "bar-popup-container",
                hgap: BI.SIZE_CONSANTS.H_GAP_SIZE,
                tgap: BI.SIZE_CONSANTS.V_GAP_SIZE
            }]
        });

        button.element.css("min-height", o.minHeight - 54);

        return button;
    }
});
BI.BubblePopupBarView.EVENT_CLICK_TOOLBAR_BUTTON = "EVENT_CLICK_TOOLBAR_BUTTON";
BI.shortcut("bi.bubble_bar_popup_view", BI.BubblePopupBarView);

/**
 * Created by Windy on 2018/2/2.
 *
 * @class BI.TextBubblePopupBarView
 * @extends BI.BubblePopupView
 */
BI.TextBubblePopupBarView = BI.inherit(BI.Widget, {

    props: function () {
        return {
            baseCls: "bi-text-bubble-bar-popup-view",
            text: "",
            buttons: [{
                level: "ignore",
                value: false,
                stopPropagation: true,
                text: BI.i18nText("BI-Basic_Cancel")
            }, {
                value: true,
                stopPropagation: true,
                text: BI.i18nText("BI-Basic_OK")
            }]
        };
    },

    render: function () {
        var self = this, o = this.options;
        var buttons = BI.map(o.buttons, function (index, buttonOpt) {
            if (BI.isWidget(buttonOpt)) {
                return buttonOpt;
            }
            return BI.extend({
                type: "bi.button",
                height: 24,
                handler: function (v) {
                    self.fireEvent(BI.TextBubblePopupBarView.EVENT_CHANGE, v);
                }
            }, buttonOpt);

        });
        return {
            type: "bi.bubble_bar_popup_view",
            primary: o.primary,
            showArrow: o.showArrow,
            minWidth: o.minWidth,
            maxWidth: o.maxWidth,
            minHeight: o.minHeight,
            ref: function () {
                self.popup = this;
            },
            el: {
                type: "bi.label",
                text: o.text,
                whiteSpace: "normal",
                textAlign: "left",
                ref: function () {
                    self.text = this;
                }
            },
            buttons: buttons
        };
    },

    setDirection: function (direction, position) {
        this.popup.setDirection(direction, position);
    },
    populate: function (v) {
        this.text.setText(v || this.options.text);
    }
});
BI.TextBubblePopupBarView.EVENT_CHANGE = "EVENT_CLICK_TOOLBAR_BUTTON";
BI.shortcut("bi.text_bubble_bar_popup_view", BI.TextBubblePopupBarView);
<commit_msg>update<commit_after>/**
 * Created by GUY on 2017/2/8.
 *
 * @class BI.BubblePopupView
 * @extends BI.PopupView
 */
BI.BubblePopupView = BI.inherit(BI.PopupView, {
    _defaultConfig: function () {
        var config = BI.BubblePopupView.superclass._defaultConfig.apply(this, arguments);
        return BI.extend(config, {
            baseCls: config.baseCls + " bi-bubble-popup-view",
            minWidth: 70,
            maxWidth: 300,
            minHeight: 50
        });
    }
});

BI.shortcut("bi.bubble_popup_view", BI.BubblePopupView);

/**
 * Created by GUY on 2017/2/8.
 *
 * @class BI.BubblePopupBarView
 * @extends BI.BubblePopupView
 */
BI.BubblePopupBarView = BI.inherit(BI.BubblePopupView, {
    _defaultConfig: function () {
        return BI.extend(BI.BubblePopupBarView.superclass._defaultConfig.apply(this, arguments), {
            extraCls: "bi-bubble-bar-popup-view",
            buttons: [{
                value: false,
                text: BI.i18nText("BI-Basic_Cancel"),
                level: "ignore"
            }, {
                text: BI.i18nText(BI.i18nText("BI-Basic_OK")),
                value: true
            }]
        });
    },
    _init: function () {
        BI.BubblePopupBarView.superclass._init.apply(this, arguments);
    },
    _createToolBar: function () {
        var o = this.options, self = this;

        var items = [];
        BI.each(o.buttons, function (i, buttonOpt) {
            if (BI.isWidget(buttonOpt)) {
                items.push({
                    el: buttonOpt,
                    lgap: i === 0 ? 20 : 15,
                    rgap: i === o.buttons.length - 1 ? 20 : 0
                });
            } else {
                items.push({
                    el: BI.extend({
                        type: "bi.button",
                        height: 24,
                        handler: function (v) {
                            self.fireEvent(BI.BubblePopupBarView.EVENT_CLICK_TOOLBAR_BUTTON, v);
                        }
                    }, buttonOpt),
                    lgap: i === 0 ? 20 : 15,
                    rgap: i === o.buttons.length - 1 ? 20 : 0
                });
            }
        });
        return BI.createWidget({
            type: "bi.right_vertical_adapt",
            height: 54,
            items: items
        });
    },

    _createView: function () {
        var o = this.options;

        var button = BI.createWidget({
            type: "bi.button_group",
            items: [o.el],
            layouts: [{
                type: "bi.vertical",
                cls: "bar-popup-container",
                hgap: BI.SIZE_CONSANTS.H_GAP_SIZE,
                tgap: BI.SIZE_CONSANTS.V_GAP_SIZE
            }]
        });

        button.element.css("min-height", o.minHeight - 54);

        return button;
    }
});
BI.BubblePopupBarView.EVENT_CLICK_TOOLBAR_BUTTON = "EVENT_CLICK_TOOLBAR_BUTTON";
BI.shortcut("bi.bubble_bar_popup_view", BI.BubblePopupBarView);

/**
 * Created by Windy on 2018/2/2.
 *
 * @class BI.TextBubblePopupBarView
 * @extends BI.BubblePopupView
 */
BI.TextBubblePopupBarView = BI.inherit(BI.Widget, {

    props: function () {
        return {
            baseCls: "bi-text-bubble-bar-popup-view",
            text: "",
            buttons: [{
                level: "ignore",
                value: false,
                stopPropagation: true,
                text: BI.i18nText("BI-Basic_Cancel")
            }, {
                value: true,
                stopPropagation: true,
                text: BI.i18nText("BI-Basic_OK")
            }]
        };
    },

    render: function () {
        var self = this, o = this.options;
        var buttons = BI.map(o.buttons, function (index, buttonOpt) {
            if (BI.isWidget(buttonOpt)) {
                return buttonOpt;
            }
            return BI.extend({
                type: "bi.button",
                height: 24,
                handler: function (v) {
                    self.fireEvent(BI.TextBubblePopupBarView.EVENT_CHANGE, v);
                }
            }, buttonOpt);

        });
        return {
            type: "bi.bubble_bar_popup_view",
            primary: o.primary,
            showArrow: o.showArrow,
            minWidth: o.minWidth,
            maxWidth: o.maxWidth,
            minHeight: o.minHeight,
            ref: function () {
                self.popup = this;
            },
            el: {
                type: "bi.label",
                text: o.text,
                whiteSpace: "normal",
                textAlign: "left",
                ref: function () {
                    self.text = this;
                }
            },
            buttons: buttons
        };
    },

    setDirection: function (direction, position) {
        this.popup.setDirection(direction, position);
    },
    populate: function (v) {
        this.text.setText(v || this.options.text);
    }
});
BI.TextBubblePopupBarView.EVENT_CHANGE = "EVENT_CLICK_TOOLBAR_BUTTON";
BI.shortcut("bi.text_bubble_bar_popup_view", BI.TextBubblePopupBarView);
<|endoftext|>


<commit_before>import _ from 'lodash';

import * as types from 'reduxes/actionTypes';
import DEFAULT_MENU from 'examples/config.menu';

function filterMenu(menu, filter) {

    if (!menu || !filter) {
        return;
    }

    for (let i = 0; i < menu.length; i++) {

        let item = menu[i];

        if (item.children && item.children.length > 0) { // group

            filterMenu(item.children, filter);

            if (item.children.length < 1) {
                menu.splice(i, 1);
                i--;
            }

        } else {
            if (!item.text || !item.text.toUpperCase().includes(filter.toUpperCase())) {
                menu.splice(i, 1);
                i--;
            }
        }

    }

}

function sortMenu(arrays) {

    let len = arrays.length;

    for (let i = 0; i < len - 1; i++) {

        if (i < arrays.length - 1) {
            for (let j = i + 1; j < len; j++) {
                if (arrays[i].text.charAt(0).toUpperCase() > arrays[j].text.charAt(0).toUpperCase()) {
                    let obj = arrays[i];
                    arrays[i] = arrays[j];
                    arrays[j] = obj;
                }
            }
        }

        if (arrays[i].hasOwnProperty('children')) {
            sortMenu(arrays[i].children);
        }

    }

}

function getMenu(filter) {

    let menu = _.cloneDeep(DEFAULT_MENU);

    if (filter) {
        filterMenu(menu, filter);
    }

    sortMenu(menu);

    return menu;

}

const initialState = {

    filter: '',

    menu: getMenu(),

    navMenuCollapsed: true,

    expandMenuName: 'buttons'

};

function navMenu(state = initialState, action) {
    switch (action.type) {

        case types.COLLAPSE_NAV_MENU: {

            localStorage.setItem('navMenuCollapsed', '1');

            return {
                ...state,
                navMenuCollapsed: true
            };

        }

        case types.EXPAND_NAV_MENU: {

            localStorage.setItem('navMenuCollapsed', '0');

            return {
                ...state,
                navMenuCollapsed: false
            };

        }

        case types.TOGGLE_NAV_MENU: {

            const navMenuCollapsed = !state.navMenuCollapsed;
            localStorage.setItem('navMenuCollapsed', navMenuCollapsed ? '1' : '0');

            return {
                ...state,
                navMenuCollapsed
            };

        }

        case types.EXPAND_MENU: {
            return {
                ...state,
                expandMenuName: action.menuName
            };
        }

        case types.EXPAND_ACTIVATED_MENU: {

            let expandMenuName;
            for (let i = 0, len = state.menu.length; i < len; i++) {
                if (state.menu[i].children
                    && state.menu[i].children.findIndex(item => item.route === action.route) > -1) {
                    expandMenuName = state.menu[i].text;
                }
            }

            return {
                ...state,
                expandMenuName
            };

        }

        case types.UPDATE_FILTER: {

            return {
                ...state,
                filter: action.filter,
                menu: getMenu(action.filter)
            };

        }

        default:
            return state;

    }
}

export default navMenu;<commit_msg>update menu<commit_after>import _ from 'lodash';

import * as types from 'reduxes/actionTypes';
import DEFAULT_MENU from 'examples/config.menu';

function filterMenu(menu, filter) {

    if (!menu || !filter) {
        return;
    }

    for (let i = 0; i < menu.length; i++) {

        let item = menu[i];

        if (item.children && item.children.length > 0) { // group

            filterMenu(item.children, filter);

            if (item.children.length < 1) {
                menu.splice(i, 1);
                i--;
            }

        } else {
            if (!item.text || !item.text.toUpperCase().includes(filter.toUpperCase())) {
                menu.splice(i, 1);
                i--;
            }
        }

    }

}

function sortMenu(arrays) {

    let len = arrays.length;

    for (let i = 0; i < len - 1; i++) {

        if (i < arrays.length - 1) {
            for (let j = i + 1; j < len; j++) {
                if (arrays[i].text.charAt(0).toUpperCase() > arrays[j].text.charAt(0).toUpperCase()) {
                    let obj = arrays[i];
                    arrays[i] = arrays[j];
                    arrays[j] = obj;
                }
            }
        }

        if (arrays[i].hasOwnProperty('children')) {
            sortMenu(arrays[i].children);
        }

    }

}

function getMenu(filter) {

    let menu = _.cloneDeep(DEFAULT_MENU);

    if (filter) {
        filterMenu(menu, filter);
    }

    sortMenu(menu);

    return menu;

}

function calExpandMenuName(menu, lastValue) {

    let result;

    for (let item of menu) {

        if (lastValue && item.text === lastValue) {
            return lastValue;
        }

        if (item.children && item.children.length > 0) {
            result = item.text;
        }

    }

    return result;

}

const initialMenu = getMenu(),
    initialState = {

        filter: '',

        menu: initialMenu,

        navMenuCollapsed: true,

        expandMenuName: calExpandMenuName(initialMenu)

    };

function navMenu(state = initialState, action) {
    switch (action.type) {

        case types.COLLAPSE_NAV_MENU: {

            localStorage.setItem('navMenuCollapsed', '1');

            return {
                ...state,
                navMenuCollapsed: true
            };

        }

        case types.EXPAND_NAV_MENU: {

            localStorage.setItem('navMenuCollapsed', '0');

            return {
                ...state,
                navMenuCollapsed: false
            };

        }

        case types.TOGGLE_NAV_MENU: {

            const navMenuCollapsed = !state.navMenuCollapsed;
            localStorage.setItem('navMenuCollapsed', navMenuCollapsed ? '1' : '0');

            return {
                ...state,
                navMenuCollapsed
            };

        }

        case types.EXPAND_MENU: {
            return {
                ...state,
                expandMenuName: action.menuName
            };
        }

        case types.EXPAND_ACTIVATED_MENU: {

            let expandMenuName;
            for (let i = 0, len = state.menu.length; i < len; i++) {
                if (state.menu[i].children
                    && state.menu[i].children.findIndex(item => item.route === action.route) > -1) {
                    expandMenuName = state.menu[i].text;
                }
            }

            return {
                ...state,
                expandMenuName
            };

        }

        case types.UPDATE_FILTER: {

            const filter = action.filter,
                menu = getMenu(action.filter),
                expandMenuName = calExpandMenuName(menu, state.expandMenuName);

            return {
                ...state,
                filter,
                menu,
                expandMenuName
            };

        }

        default:
            return state;

    }
}

export default navMenu;<|endoftext|>


<commit_before>// @flow
// FIXME: this module relies heavily on `document.querySelector` and `document.querySelectorAll`
// methods but suffers from the limitation that it (presently) runs the queries on the wider
// document's DOM.  This means that, should there ever be a use case for multiple HTML Highlighter
// instances running concurrently within the same DOM on separate subtrees, the result of doing so
// would most certainly be undefined and most likely confusing and resulting in loss or corruption
// of (perceived) state.

import { Css } from './consts';

export type Position = {| x: number, y: number |};

function classNameToSet(el: HTMLElement): Set<string> {
  return new Set(
    el.className
      .split(' ')
      .map(c => c.trim())
      .filter(Boolean)
  );
}

function ensureIterable(
  elementOrCollection: HTMLElement | NodeList<HTMLElement>
): Iterable<HTMLElement> {
  return elementOrCollection instanceof NodeList
    ? elementOrCollection
    : new Set([elementOrCollection]);
}

function addClass(coll: HTMLElement | NodeList<HTMLElement>, name: string): void {
  for (const el of ensureIterable(coll)) {
    const set = classNameToSet(el);
    set.add(name);
    el.className = Array.from(set).join(' ');
  }
}

function removeClass(coll: HTMLElement | NodeList<HTMLElement>, name: string): void {
  for (const el of ensureIterable(coll)) {
    const set = classNameToSet(el);
    set.delete(name);
    el.className = Array.from(set).join(' ');
  }
}

function getHighlightElements(id: number): NodeList<HTMLElement> {
  return document.querySelectorAll(`.${Css.highlight}-id-${id}`);
}

function getForQuerySet(qid: number): NodeList<HTMLElement> {
  return document.querySelectorAll(`.${Css.highlight}-${qid}`);
}

function getAllHighlightElements(additionalClass: string | null = null): NodeList<HTMLElement> {
  const otherClass = additionalClass ? `.${additionalClass}` : '';
  return document.querySelectorAll(`.${Css.highlight}${otherClass}`);
}

function createHighlightElement(node: HTMLElement | Node, className: string): HTMLElement {
  const span = document.createElement('span');
  span.className = className;
  (node.parentNode: any).insertBefore(span, node);
  span.appendChild(node);
  return span;
}

function insertBefore(newNode: Node, beforeNode: Node): Node {
  (beforeNode.parentNode: any).insertBefore(newNode, beforeNode);
  return newNode;
}

function insertAfter(newNode: Node, afterNode: Node): Node {
  // Automatically adds to the end of the list when `nextSibling` is `null`
  (afterNode.parentNode: any).insertBefore(newNode, afterNode.nextSibling);
  return newNode;
}

// Taken with a few alterations from:
// https://www.kirupa.com/html5/get_element_position_using_javascript.htm
function getOffset(el: HTMLElement): Position {
  let x = 0;
  let y = 0;

  while (el != null) {
    if (el.tagName.toLowerCase() === 'body') {
      // deal with browser quirks with body/window/document and page scroll
      const scrollX = el.scrollLeft || (document.documentElement: any).scrollLeft;
      const scrollY = el.scrollTop || (document.documentElement: any).scrollTop;

      x += el.offsetLeft - scrollX + el.clientLeft;
      y += el.offsetTop - scrollY + el.clientTop;
    } else {
      // for all other non-BODY elements
      x += el.offsetLeft - el.scrollLeft + el.clientLeft;
      y += el.offsetTop - el.scrollTop + el.clientTop;
    }

    el = (el: any).offsetParent;
  }

  return { x, y };
}

function isInView(el: HTMLElement): boolean {
  const bbox = el.getBoundingClientRect();
  return bbox.top >= 0 && bbox.top + bbox.height < window.innerHeight;
}

// Note that flow does not expose a formal Window type, which means we are unable to validate the
// type.
function scrollIntoView(el: HTMLElement, container: any): void {
  if (container == null) {
    container = window;
  }

  let containerTop;
  let containerHeight;
  if (container === window) {
    containerHeight = window.innerHeight;
    containerTop = window.scrollY;
  } else {
    containerHeight = container.getBoundingClientRect().height;
    containerTop = container.scrollTop;
  }

  const bbox = el.getBoundingClientRect();
  container.scrollTo({ top: bbox.top + containerTop - (containerHeight - bbox.height) / 2 });
}

// Export "private" functions so they too can be tested.
export { classNameToSet, ensureIterable };

export {
  addClass,
  removeClass,
  getHighlightElements,
  getForQuerySet,
  getAllHighlightElements,
  createHighlightElement,
  insertBefore,
  insertAfter,
  getOffset,
  isInView,
  scrollIntoView,
};
<commit_msg>dom: backwards-compatible call to `scrollTo`<commit_after>// @flow
// FIXME: this module relies heavily on `document.querySelector` and `document.querySelectorAll`
// methods but suffers from the limitation that it (presently) runs the queries on the wider
// document's DOM.  This means that, should there ever be a use case for multiple HTML Highlighter
// instances running concurrently within the same DOM on separate subtrees, the result of doing so
// would most certainly be undefined and most likely confusing and resulting in loss or corruption
// of (perceived) state.

import { Css } from './consts';

export type Position = {| x: number, y: number |};

function classNameToSet(el: HTMLElement): Set<string> {
  return new Set(
    el.className
      .split(' ')
      .map(c => c.trim())
      .filter(Boolean)
  );
}

function ensureIterable(
  elementOrCollection: HTMLElement | NodeList<HTMLElement>
): Iterable<HTMLElement> {
  return elementOrCollection instanceof NodeList
    ? elementOrCollection
    : new Set([elementOrCollection]);
}

function addClass(coll: HTMLElement | NodeList<HTMLElement>, name: string): void {
  for (const el of ensureIterable(coll)) {
    const set = classNameToSet(el);
    set.add(name);
    el.className = Array.from(set).join(' ');
  }
}

function removeClass(coll: HTMLElement | NodeList<HTMLElement>, name: string): void {
  for (const el of ensureIterable(coll)) {
    const set = classNameToSet(el);
    set.delete(name);
    el.className = Array.from(set).join(' ');
  }
}

function getHighlightElements(id: number): NodeList<HTMLElement> {
  return document.querySelectorAll(`.${Css.highlight}-id-${id}`);
}

function getForQuerySet(qid: number): NodeList<HTMLElement> {
  return document.querySelectorAll(`.${Css.highlight}-${qid}`);
}

function getAllHighlightElements(additionalClass: string | null = null): NodeList<HTMLElement> {
  const otherClass = additionalClass ? `.${additionalClass}` : '';
  return document.querySelectorAll(`.${Css.highlight}${otherClass}`);
}

function createHighlightElement(node: HTMLElement | Node, className: string): HTMLElement {
  const span = document.createElement('span');
  span.className = className;
  (node.parentNode: any).insertBefore(span, node);
  span.appendChild(node);
  return span;
}

function insertBefore(newNode: Node, beforeNode: Node): Node {
  (beforeNode.parentNode: any).insertBefore(newNode, beforeNode);
  return newNode;
}

function insertAfter(newNode: Node, afterNode: Node): Node {
  // Automatically adds to the end of the list when `nextSibling` is `null`
  (afterNode.parentNode: any).insertBefore(newNode, afterNode.nextSibling);
  return newNode;
}

// Taken with a few alterations from:
// https://www.kirupa.com/html5/get_element_position_using_javascript.htm
function getOffset(el: HTMLElement): Position {
  let x = 0;
  let y = 0;

  while (el != null) {
    if (el.tagName.toLowerCase() === 'body') {
      // deal with browser quirks with body/window/document and page scroll
      const scrollX = el.scrollLeft || (document.documentElement: any).scrollLeft;
      const scrollY = el.scrollTop || (document.documentElement: any).scrollTop;

      x += el.offsetLeft - scrollX + el.clientLeft;
      y += el.offsetTop - scrollY + el.clientTop;
    } else {
      // for all other non-BODY elements
      x += el.offsetLeft - el.scrollLeft + el.clientLeft;
      y += el.offsetTop - el.scrollTop + el.clientTop;
    }

    el = (el: any).offsetParent;
  }

  return { x, y };
}

function isInView(el: HTMLElement): boolean {
  const bbox = el.getBoundingClientRect();
  return bbox.top >= 0 && bbox.top + bbox.height < window.innerHeight;
}

// Note that flow does not expose a formal Window type, which means we are unable to validate the
// type.
function scrollIntoView(el: HTMLElement, container: any): void {
  if (container == null) {
    container = window;
  }

  let containerTop;
  let containerHeight;
  if (container === window) {
    containerHeight = window.innerHeight;
    containerTop = window.scrollY;
  } else {
    containerHeight = container.getBoundingClientRect().height;
    containerTop = container.scrollTop;
  }

  const bbox = el.getBoundingClientRect();
  container.scrollTo(window.scrollX, bbox.top + containerTop - (containerHeight - bbox.height) / 2);
}

// Export "private" functions so they too can be tested.
export { classNameToSet, ensureIterable };

export {
  addClass,
  removeClass,
  getHighlightElements,
  getForQuerySet,
  getAllHighlightElements,
  createHighlightElement,
  insertBefore,
  insertAfter,
  getOffset,
  isInView,
  scrollIntoView,
};
<|endoftext|>