<commit_before>/**

   Copyright 2013-2017 Cris Stringfellow / DOSAYGO CREATIVE INTANGIBLE HOLDINGS (UK) LTD

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

**/

"use strict";
{
  const utils = require('./utils.js');
  const MAX_DEPTH = 10000;
  const CLASS_MATCHER = /(\.[^#\.:]+)/g;
  const ID_MATCHER = /#([^#\.:]+)/g;
  const TAG_MATCHER = /^([^#\.:]+)/g;
  const ANY_MATCHER = /:\w+\(\s*([^\)]+)\s*\)/g;
  const NTH_MATCHER = /(:nth-of-type\([^)]*\))/g;

  let vendor;
  let current_code = 2;

  const path_lcs = {
    any_mode : false,
    get_canonical_path(node) {
      const path = [];
      const class_path = [];
      const canonical_path = [];
      let code = next_code();
      while(!!node && node.tagName != 'HTML') {
        if(!node.parentNode && !!node.host) {
          // Issue: this is only here for reference when we add in updated shadow dom support, or remove this code
          node = node.host;
          const shadow_level = {};
          shadow_level["TAG:"+node.tagName+"::shadow"] = 1;
          canonical_path.unshift(shadow_level);
        } else {
          const canonical_level = {
            tags: new Set(),
            geometry: new Set(),
            classes: new Set(),
            ids: new Set()
          };

          // make the level
            // get index_name ( like nth-of-type(n) )
              let index_name = '';
              if(!!node.parentNode) {
                let count = 0;
                const siblings = Array.from( node.parentNode.childNodes );
                for( const sibling of siblings ) {
                  if(sibling.tagName == node.tagName) {
                    count += 1;
                  }
                  if(sibling === node) {
                    index_name = `:nth-of-type(${count})`;
                    break;
                  }
                }
              }

            // add id ( if any ) to canonical level
              if(!!node.id && node.id.length > 0) {
                canonical_level.ids.add( node.id );
              }

            // get classes
              let classes;
              try {
                classes = node.getAttribute('class');
              } catch(e) {
                classes = node.className;
              }
              if(typeof classes !== 'string') {
                // fallback for SVG the className is an object.
                try {
                  classes = Array.from( node.classList );
                } catch(e) {
                  classes = []
                }
              } else {
                classes = classes.split(/\s+/);
              }

            // add class words to canonical_level
              classes.forEach( classword => {
                if(classword.length > 0) {
                  canonical_level.classes.add(`.${safe(classword)}`);
                }
              });

            // add tag and index_name to canonical level
              canonical_level.tags.add(node.tagName);
              canonical_level.geometry.add(index_name);

            // add code
              canonical_level.code = code;
              code += 1;

          canonical_path.unshift(canonical_level);
        }
        node = node.parentNode;
      }
      return {canonical:canonical_path};
    },
    path_from_sel(sel) {
      const levels = sel.split(/\s*>\s*/g);
      const path = [];
      let code = next_code();
      for( const level of levels ) {
        const canonical_level = {
          tags: new Set(),
          geometry: new Set(),
          classes: new Set(),
          ids: new Set()
        };

        const classes = new Set( run_match(CLASS_MATCHER,level).map( c => safe(c) ));
        let anys = run_match(ANY_MATCHER,level)[0];
        if ( anys ) {
          anys = new Set( anys.split(/\s*,\s*/g) );
        } else {
          anys = new Set();
        }

        const tag = run_match(TAG_MATCHER,level)[0];
        const id = safe(run_match(ID_MATCHER,level)[0] || '');
        const geometry = run_match(NTH_MATCHER,level)[0];

        canonical_level.classes = classes;
        canonical_level.tags = anys;

        if ( tag ) {
          if ( anys.size == 0 ) {
            canonical_level.tags.add( tag );
          } else if ( tag !== '*' ) {
            canonical_level.tags.add( tag );
          }
        }
        if ( id ) {
          canonical_level.ids.add(id);
        }
        if ( geometry ) {
          canonical_level.geometry.add(geometry);
        }

        canonical_level.code = code;
        code -= 1;
        path.push( canonical_level );
      }
      return path;
    },
    selector_from_canonical_path(path) {
      vendor = vendor || require('./vendor.js').get_prefix();
      path = Array.from(path);
      if ( path.length == 0 ) {
        return '';
      }
      const selector = [];

      // add direct descendent combinators
        let last_level = path[0];
        for( let i = 1; i < path.length; i++ ) {
          const level = path[i];
          const diff = get_code_diff(last_level,level);
          if ( diff == 1 ) {
            path.splice( i, 0, {'>': 1});
          }
          last_level = level;
        }

      path.forEach( levelset => {
        if(!!levelset['>']) {
          const last = selector[selector.length-1];
          if(last !== '>') {
            selector.push('>');
          }
          return;
        }
        const { tags, classes, ids, geometry } = levelset;
        let level_sel = '';

        if ( path_lcs.any_mode && tags.size > 1 ) {
          level_sel += `:-${vendor}-any(${[...tags].join(',')})`;
        } else {
          level_sel += `${[...tags].join(',')}`;
        }

        if ( ids.size ) {
          level_sel += `#${[...ids].map(i => safe(i)).join('#')}`;
        }

        if ( classes.size) {
          level_sel += `${[...classes].map(c => safe(c)).join('')}`;
        }

        if ( geometry.size ) {
          level_sel += `${[...geometry].join('')}`;
        }

        if(!!level_sel) {
          selector.push(level_sel);
        }
      });

      const selector_str = selector.join(' ');
      return selector_str;
    },
    basic_multiple_lcs_from_canonical_path_list(list) {
      if(list.length == 0) {
        return [];
      }
      let path2 = list[0];
      let path1;
      for(let i = 1; i < list.length; i+=1 ) {
        path1 = list[i];
        const lcs = lcs_from_canonical_path_pair(path1,path2);
        if(!!lcs) {
          path2 = lcs.value;
        }
      }
      return path2;
    }
  };

  // helpers
    function safe(s) {
      // CSS escapes
      return s.replace(/\\\\([\[\]])/, '$1' ).replace(/([\[\]])/g, '\\$1' );
    }
    function run_match(R,s) {
      const m = [];
      let match;
      while(match = R.exec(s)) {
        m.push(match[1]);
      }
      return m;
    }
    function find_max_value_index(s,x,y) {
      let value = 0;
      let max_index = s.length-1;
      for(let i = 0; i < s.length; i += 1) {
        if(s[i] >= value) {
          value = s[i];
          max_index = i;
        }
      }
      if ( y.length ) {
        const column = max_index % y.length;
        const row = (max_index-column)/y.length;
        return { row, column, value };
      } else {
        return { row: 0, column: 0, value };
      }
    }

    function lcs_read(s,x,y,i,j) {
      if ( i <= 0 || j <= 0 ) {
        return [];
      }

      let xy_intersection;
      if ( path_lcs.any_mode ) {
        xy_intersection = utils.any_intersection(x[i],y[j]);
      } else {
        xy_intersection = utils.intersection(x[i],y[j]);
      }
      const order = utils.order(xy_intersection, path_lcs.any_mode);
      if(!!order) {
        const xcode = utils.get_code(x[i]);
        const ycode = utils.get_code(y[j]);
        Object.assign( xy_intersection, {
          xcode, ycode
        });
        return lcs_read(s,x,y,i-1,j-1).concat([xy_intersection]);
      } else {
        let score_insert_y = s[i*y.length+j-1];
        let score_insert_x = s[(i-1)*y.length+j];
        if(score_insert_y > score_insert_x) {
          return lcs_read(s,x,y,i,j-1);
        } else {
          return lcs_read(s,x,y,i-1,j);
        }
      }
    }

    function next_code() {
      current_code += MAX_DEPTH;
      return current_code;
    }

    function get_code_diff(last_level, level) {
      const lcode = utils.get_code(last_level);
      const code = utils.get_code(level);
      return lcode - code;
    }

    function diffs( last_level, level ) {
      const {xcode,ycode} = last_level;
      const xdif = xcode - level.xcode;
      const ydif = ycode - level.ycode;
      return [xdif, ydif];
    }

    function isOneLevel( last_level, level ) {
      const [xdif, ydif] = [...diffs(last_level, level )];
      return xdif == 1 && ydif == 1;
    }

    function max_diff( last_level, level ) {
      return Math.max( ...diffs( last_level, level ) );
    }

    function lcs_from_canonical_path_pair(path1,path2) {
      const score_matrix = new Float32Array(new ArrayBuffer(4*path1.length*path2.length));
      const row_offset = path2.length;
      let i1,i2;
      let address;
      let path1_path2_match_score, path1_insert_score, path2_insert_score;

      for(i1 = 1; i1 < path1.length; i1 += 1 ) {
        for(i2 = 1; i2 < path2.length; i2 += 1 ) {
          const union_order = utils.order(utils.union(path1[i1],path2[i2]), path_lcs.any_mode);
          let quotient = 0;
          if ( union_order ) {
            quotient = utils.order(utils.intersection(path1[i1],path2[i2]), path_lcs.any_mode)/union_order;
          }
          address = path2.length*i1+i2;
          path1_path2_match_score = score_matrix[address-path2.length-1]+quotient;
          path1_insert_score = score_matrix[address-1];
          path2_insert_score = score_matrix[address-path2.length];
          score_matrix[address] = Math.max(path1_insert_score,path1_path2_match_score,path2_insert_score);
        }
      }

      const max_value_index = find_max_value_index(score_matrix,path1,path2);
      let last_match_i = max_value_index.row;
      let last_match_j = max_value_index.column;
      const lcs_selector = lcs_read(score_matrix,path1,path2,last_match_i, last_match_j);

      // add codes
        if ( lcs_selector.length ) {
          let code = next_code();
          let last_level = lcs_selector[0];
          last_level.code = code;
          for( let i = 1; i < lcs_selector.length; i++ ) {
            const level = lcs_selector[i];
            const is_one_level = isOneLevel( last_level, level );
            if ( is_one_level ) {
              code -= 1;
            } else {
              code -= max_diff( last_level, level );
            }
            level.code = code;
            last_level = level;
          }
        }

        lcs_selector.forEach( level => {
          delete level.xcode;
          delete level.ycode;
        });

      return {value:lcs_selector,score:max_value_index.value};
    }

  module.exports = path_lcs;
}
<commit_msg>Update css escapes for numeric ids etc<commit_after>/**

   Copyright 2013-2017 Cris Stringfellow / DOSAYGO CREATIVE INTANGIBLE HOLDINGS (UK) LTD

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

**/

"use strict";
{
  const utils = require('./utils.js');
  const MAX_DEPTH = 10000;
  const CLASS_MATCHER = /(\.[^#\.:]+)/g;
  const ID_MATCHER = /#([^#\.:]+)/g;
  const TAG_MATCHER = /^([^#\.:]+)/g;
  const ANY_MATCHER = /:\w+\(\s*([^\)]+)\s*\)/g;
  const NTH_MATCHER = /(:nth-of-type\([^)]*\))/g;

  let vendor;
  let current_code = 2;

  const path_lcs = {
    any_mode : false,
    get_canonical_path(node) {
      const path = [];
      const class_path = [];
      const canonical_path = [];
      let code = next_code();
      while(!!node && node.tagName != 'HTML') {
        if(!node.parentNode && !!node.host) {
          // Issue: this is only here for reference when we add in updated shadow dom support, or remove this code
          node = node.host;
          const shadow_level = {};
          shadow_level["TAG:"+node.tagName+"::shadow"] = 1;
          canonical_path.unshift(shadow_level);
        } else {
          const canonical_level = {
            tags: new Set(),
            geometry: new Set(),
            classes: new Set(),
            ids: new Set()
          };

          // make the level
            // get index_name ( like nth-of-type(n) )
              let index_name = '';
              if(!!node.parentNode) {
                let count = 0;
                const siblings = Array.from( node.parentNode.childNodes );
                for( const sibling of siblings ) {
                  if(sibling.tagName == node.tagName) {
                    count += 1;
                  }
                  if(sibling === node) {
                    index_name = `:nth-of-type(${count})`;
                    break;
                  }
                }
              }

            // add id ( if any ) to canonical level
              if(!!node.id && node.id.length > 0) {
                canonical_level.ids.add( node.id );
              }

            // get classes
              let classes;
              try {
                classes = node.getAttribute('class');
              } catch(e) {
                classes = node.className;
              }
              if(typeof classes !== 'string') {
                // fallback for SVG the className is an object.
                try {
                  classes = Array.from( node.classList );
                } catch(e) {
                  classes = []
                }
              } else {
                classes = classes.split(/\s+/);
              }

            // add class words to canonical_level
              classes.forEach( classword => {
                if(classword.length > 0) {
                  canonical_level.classes.add(`.${safe(classword)}`);
                }
              });

            // add tag and index_name to canonical level
              canonical_level.tags.add(node.tagName);
              canonical_level.geometry.add(index_name);

            // add code
              canonical_level.code = code;
              code += 1;

          canonical_path.unshift(canonical_level);
        }
        node = node.parentNode;
      }
      return {canonical:canonical_path};
    },
    path_from_sel(sel) {
      const levels = sel.split(/\s*>\s*/g);
      const path = [];
      let code = next_code();
      for( const level of levels ) {
        const canonical_level = {
          tags: new Set(),
          geometry: new Set(),
          classes: new Set(),
          ids: new Set()
        };

        const classes = new Set( run_match(CLASS_MATCHER,level).map( c => safe(c) ));
        let anys = run_match(ANY_MATCHER,level)[0];
        if ( anys ) {
          anys = new Set( anys.split(/\s*,\s*/g) );
        } else {
          anys = new Set();
        }

        const tag = run_match(TAG_MATCHER,level)[0];
        const id = safe(run_match(ID_MATCHER,level)[0] || '');
        const geometry = run_match(NTH_MATCHER,level)[0];

        canonical_level.classes = classes;
        canonical_level.tags = anys;

        if ( tag ) {
          if ( anys.size == 0 ) {
            canonical_level.tags.add( tag );
          } else if ( tag !== '*' ) {
            canonical_level.tags.add( tag );
          }
        }
        if ( id ) {
          canonical_level.ids.add(id);
        }
        if ( geometry ) {
          canonical_level.geometry.add(geometry);
        }

        canonical_level.code = code;
        code -= 1;
        path.push( canonical_level );
      }
      return path;
    },
    selector_from_canonical_path(path) {
      vendor = vendor || require('./vendor.js').get_prefix();
      path = Array.from(path);
      if ( path.length == 0 ) {
        return '';
      }
      const selector = [];

      // add direct descendent combinators
        let last_level = path[0];
        for( let i = 1; i < path.length; i++ ) {
          const level = path[i];
          const diff = get_code_diff(last_level,level);
          if ( diff == 1 ) {
            path.splice( i, 0, {'>': 1});
          }
          last_level = level;
        }

      path.forEach( levelset => {
        if(!!levelset['>']) {
          const last = selector[selector.length-1];
          if(last !== '>') {
            selector.push('>');
          }
          return;
        }
        const { tags, classes, ids, geometry } = levelset;
        let level_sel = '';

        if ( path_lcs.any_mode && tags.size > 1 ) {
          level_sel += `:-${vendor}-any(${[...tags].join(',')})`;
        } else {
          level_sel += `${[...tags].join(',')}`;
        }

        if ( ids.size ) {
          level_sel += `#${[...ids].map(i => safe(i)).join('#')}`;
        }

        if ( classes.size) {
          level_sel += `${[...classes].map(c => safe(c)).join('')}`;
        }

        if ( geometry.size ) {
          level_sel += `${[...geometry].join('')}`;
        }

        if(!!level_sel) {
          selector.push(level_sel);
        }
      });

      const selector_str = selector.join(' ');
      return selector_str;
    },
    basic_multiple_lcs_from_canonical_path_list(list) {
      if(list.length == 0) {
        return [];
      }
      let path2 = list[0];
      let path1;
      for(let i = 1; i < list.length; i+=1 ) {
        path1 = list[i];
        const lcs = lcs_from_canonical_path_pair(path1,path2);
        if(!!lcs) {
          path2 = lcs.value;
        }
      }
      return path2;
    }
  };

  // helpers
    function safe(s) {
      // CSS escapes
      if ( Number.isInteger(parseInt(s)) && parseInt(s).toString() == s) {
        s = s.split('').map( c => c.charCodeAt(0).toString(16) ).join('\\');
        return '\\'+s;
      } else {
        return s.replace(/\\\\([\[\]])/, '$1' ).replace(/([\[\]])/g, '\\$1' ).replace(/\\(\d\d)/g,'\\\\$1');
      }
    }
    function run_match(R,s) {
      const m = [];
      let match;
      while(match = R.exec(s)) {
        m.push(match[1]);
      }
      return m;
    }
    function find_max_value_index(s,x,y) {
      let value = 0;
      let max_index = s.length-1;
      for(let i = 0; i < s.length; i += 1) {
        if(s[i] >= value) {
          value = s[i];
          max_index = i;
        }
      }
      if ( y.length ) {
        const column = max_index % y.length;
        const row = (max_index-column)/y.length;
        return { row, column, value };
      } else {
        return { row: 0, column: 0, value };
      }
    }

    function lcs_read(s,x,y,i,j) {
      if ( i <= 0 || j <= 0 ) {
        return [];
      }

      let xy_intersection;
      if ( path_lcs.any_mode ) {
        xy_intersection = utils.any_intersection(x[i],y[j]);
      } else {
        xy_intersection = utils.intersection(x[i],y[j]);
      }
      const order = utils.order(xy_intersection, path_lcs.any_mode);
      if(!!order) {
        const xcode = utils.get_code(x[i]);
        const ycode = utils.get_code(y[j]);
        Object.assign( xy_intersection, {
          xcode, ycode
        });
        return lcs_read(s,x,y,i-1,j-1).concat([xy_intersection]);
      } else {
        let score_insert_y = s[i*y.length+j-1];
        let score_insert_x = s[(i-1)*y.length+j];
        if(score_insert_y > score_insert_x) {
          return lcs_read(s,x,y,i,j-1);
        } else {
          return lcs_read(s,x,y,i-1,j);
        }
      }
    }

    function next_code() {
      current_code += MAX_DEPTH;
      return current_code;
    }

    function get_code_diff(last_level, level) {
      const lcode = utils.get_code(last_level);
      const code = utils.get_code(level);
      return lcode - code;
    }

    function diffs( last_level, level ) {
      const {xcode,ycode} = last_level;
      const xdif = xcode - level.xcode;
      const ydif = ycode - level.ycode;
      return [xdif, ydif];
    }

    function isOneLevel( last_level, level ) {
      const [xdif, ydif] = [...diffs(last_level, level )];
      return xdif == 1 && ydif == 1;
    }

    function max_diff( last_level, level ) {
      return Math.max( ...diffs( last_level, level ) );
    }

    function lcs_from_canonical_path_pair(path1,path2) {
      const score_matrix = new Float32Array(new ArrayBuffer(4*path1.length*path2.length));
      const row_offset = path2.length;
      let i1,i2;
      let address;
      let path1_path2_match_score, path1_insert_score, path2_insert_score;

      for(i1 = 1; i1 < path1.length; i1 += 1 ) {
        for(i2 = 1; i2 < path2.length; i2 += 1 ) {
          const union_order = utils.order(utils.union(path1[i1],path2[i2]), path_lcs.any_mode);
          let quotient = 0;
          if ( union_order ) {
            quotient = utils.order(utils.intersection(path1[i1],path2[i2]), path_lcs.any_mode)/union_order;
          }
          address = path2.length*i1+i2;
          path1_path2_match_score = score_matrix[address-path2.length-1]+quotient;
          path1_insert_score = score_matrix[address-1];
          path2_insert_score = score_matrix[address-path2.length];
          score_matrix[address] = Math.max(path1_insert_score,path1_path2_match_score,path2_insert_score);
        }
      }

      const max_value_index = find_max_value_index(score_matrix,path1,path2);
      let last_match_i = max_value_index.row;
      let last_match_j = max_value_index.column;
      const lcs_selector = lcs_read(score_matrix,path1,path2,last_match_i, last_match_j);

      // add codes
        if ( lcs_selector.length ) {
          let code = next_code();
          let last_level = lcs_selector[0];
          last_level.code = code;
          for( let i = 1; i < lcs_selector.length; i++ ) {
            const level = lcs_selector[i];
            const is_one_level = isOneLevel( last_level, level );
            if ( is_one_level ) {
              code -= 1;
            } else {
              code -= max_diff( last_level, level );
            }
            level.code = code;
            last_level = level;
          }
        }

        lcs_selector.forEach( level => {
          delete level.xcode;
          delete level.ycode;
        });

      return {value:lcs_selector,score:max_value_index.value};
    }

  module.exports = path_lcs;
}
<|endoftext|>


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

  //Polyfill global objects
  if (typeof Map != 'undefined') {
    if (typeof Map.prototype.keys == 'undefined') {
      Map.prototype.keys = mapKeys;
    }
  }

  function mapToArray() {
    var values = new Array(this.size);
    var keys = new Array(this.size);
    var i = 0;
    this.forEach(function(value, key) { values[i] = value; keys[i++] = key; });
    return { values: values, keys: keys };
  }

  function mapKeys() {
    var entries = mapToArray.call(this);
    return sharedIterator(entries.keys);
  }

  function sharedIterator(array1, array2) {
    var p = 0, done = false;
    return {
      next: function() {
        var v;
        if (!done && p < array1.length) {
          v = array2 ? [array1[p], array2[p]]: array1[p];
          p++;
        } else {
          done = true;
          v = undefined;
        }
        return { done: done, value: v };
      }
    };
  }
})(typeof exports != 'undefined' && typeof global != 'undefined' ? global : window );
<commit_msg>Implement Map.prototype.values()<commit_after>(function (exports) {'use strict';

  //Polyfill global objects
  if (typeof Map != 'undefined') {
    if (typeof Map.prototype.values == 'undefined') {
      Map.prototype.values = mapValues;
    }
    if (typeof Map.prototype.keys == 'undefined') {
      Map.prototype.keys = mapKeys;
    }
  }

  function mapToArray() {
    var values = new Array(this.size);
    var keys = new Array(this.size);
    var i = 0;
    this.forEach(function(value, key) { values[i] = value; keys[i++] = key; });
    return { values: values, keys: keys };
  }

  function mapValues() {
    var entries = mapToArray.call(this);
    return sharedIterator(entries.values);
  }

  function mapKeys() {
    var entries = mapToArray.call(this);
    return sharedIterator(entries.keys);
  }

  function sharedIterator(array1, array2) {
    var p = 0, done = false;
    return {
      next: function() {
        var v;
        if (!done && p < array1.length) {
          v = array2 ? [array1[p], array2[p]]: array1[p];
          p++;
        } else {
          done = true;
          v = undefined;
        }
        return { done: done, value: v };
      }
    };
  }
})(typeof exports != 'undefined' && typeof global != 'undefined' ? global : window );
<|endoftext|>


<commit_before>var im = require('imagemagick')
  , fs = require('fs')
  , path = require('path')
  , mime = require('mime')
  , cloudfiles = require('cloudfiles')
  , http  = require('http')
  , url   = require('url')
  , async = require('async')
  , _ = require('underscore')

var Imager = exports = module.exports = function Imager(config, storage) {

  if (config === undefined)
    throw new Error('Please provide the config')

  if (storage === undefined)
    throw new Error('Please specify the storage')

  if (config.storage[storage] === undefined)
    throw new Error('The storage you have specified does not exist in the config you have provided')

  this.config = config
  this.storage = storage
  this.uploadedFiles = []

  // if (storage === 'Rackspace') this.setupCloudfiles()
}

Imager.prototype.upload = function (files, callback, variant) {
  var self = this

  if (typeof files !== 'string' && typeof files !== 'object')
    throw new Error('Please provide a valid file')

  if (typeof callback !== 'function')
    throw new Error('Please provide a callback')

  if (typeof variant !== 'string' && !this.config.variants.default)
    throw new Error('Please specify a proper variant OR provide a default variant in your imager config')

  if (typeof variant === 'string' && !this.config.variants[variant])
    throw new Error('Please provide a variant which you have specified in the config file')

  var startUpload = function (file, iteratorCallback) {
    var filename = Math.round(new Date().getTime())
    self.startUpload(file, filename, variant, iteratorCallback)
  }

  async.forEach(files, startUpload, function (err) {
    if (err) return callback(err)
    callback(null, self.cdnUri, self.uploadedFiles)
  })
}

Imager.prototype.startUpload = function (file, filename, variant, iteratorCallback) {
  if (!file.size) return iteratorCallback();

  var variants = this.config.variants[variant]
    , asyncArr = []
    , self = this

  if (variants.resize) {
    Object.keys(variants.resize).forEach(function (name) {
      var processFiles = function (cb) {
        self.resizeFile(file, { name: name, size: variants.resize[name] }, filename, cb)
      }

      asyncArr.push(processFiles)
    })
  }

  if (variants.crop) {
    Object.keys(variants.crop).forEach(function (name) {
      var processFiles = function (cb) {
        self.cropFile(file, { name: name, size: variants.crop[name] }, filename, cb)
      }

      asyncArr.push(processFiles)
    })
  }

  async.parallel(asyncArr, function (err, results) {
    self.uploadedFiles.push(_.uniq(results))
    iteratorCallback(err)
  })
}

Imager.prototype.resizeFile = function (file, preset, fname, cb) {
  var ct = { 'image/jpeg': '.jpg', 'image/png': '.png', 'image/gif': '.gif' }
    , fileType = ct[file['type']]
    , filename = fname + fileType
    , self = this

  var remoteFile = preset.name + '_' + filename
    , dest = '/tmp/' + remoteFile

  var options = {
      srcPath: file['path']
    , dstPath: dest
    , width: preset.size.split('x')[0]
  }

  im.resize(options, function(err, stdout, stderr){
    if (err) return cb(err)
    self['pushTo' + self.storage](dest, remoteFile, filename, file['type'], cb)
  })
}

Imager.prototype.cropFile = function (file, preset, fname, cb) {
  var ct = { 'image/jpeg': '.jpg', 'image/png': '.png', 'image/gif': '.gif' }
    , fileType = ct[file['type']]
    , filename = fname + fileType
    , self = this

  var remoteFile = preset.name + '_' + filename
    , dest = '/tmp/' + remoteFile

  var options = {
      srcPath: file['path']
    , dstPath: dest
    , width: preset.size.split('x')[0]
    , height:  preset.size.split('x')[1]
  }

  im.crop(options, function(err, stdout, stderr){
    if (err) return cb(err)
    self['pushTo' + self.storage](dest, remoteFile, filename, file['type'], cb)
  })
}

Imager.prototype.pushToRackspace = function (file, remoteFile, filename, type, cb) {
  var self = this
    , client = cloudfiles.createClient(this.config['storage'][this.storage])
    , rsContainer = this.config['storage'][this.storage].container

  fs.readFile(file, function (err, buf) {
    if (err) return cb(err)

    var onUploadToContainer = function (err, uploaded) {
      if (err) return cb(err)
      if (uploaded) {
        cb(null, filename)
      }
    }

    var addFileToContainer = function (err, container) {
      if (err) return cb(err)
      var options = {
          remote: remoteFile
        , headers: {
              'Content-Type': type
            , 'Content-Length': buf.length
          }
        , local: file }

      self.cdnUri = container.cdnUri
      client.addFile(rsContainer, options, onUploadToContainer)
    }

    client.setAuth(function() {
      client.getContainer(rsContainer, true, addFileToContainer)
    })
  })
}

<commit_msg>add back remove functionality<commit_after>var im = require('imagemagick')
  , fs = require('fs')
  , path = require('path')
  , mime = require('mime')
  , cloudfiles = require('cloudfiles')
  , http  = require('http')
  , url   = require('url')
  , async = require('async')
  , _ = require('underscore')

var Imager = exports = module.exports = function Imager(config, storage) {

  if (config === undefined)
    throw new Error('Please provide the config')

  if (storage === undefined)
    throw new Error('Please specify the storage')

  if (config.storage[storage] === undefined)
    throw new Error('The storage you have specified does not exist in the config you have provided')

  this.config = config
  this.storage = storage
  this.uploadedFiles = []

  // if (storage === 'Rackspace') this.setupCloudfiles()
}

Imager.prototype.upload = function (files, callback, variant) {
  var self = this

  if (typeof files !== 'string' && typeof files !== 'object')
    throw new Error('Please provide a valid file')

  if (typeof callback !== 'function')
    throw new Error('Please provide a callback')

  if (typeof variant !== 'string' && !this.config.variants.default)
    throw new Error('Please specify a proper variant OR provide a default variant in your imager config')

  if (typeof variant === 'string' && !this.config.variants[variant])
    throw new Error('Please provide a variant which you have specified in the config file')

  var startUpload = function (file, iteratorCallback) {
    var filename = Math.round(new Date().getTime())
    self.startUpload(file, filename, variant, iteratorCallback)
  }

  async.forEach(files, startUpload, function (err) {
    if (err) return callback(err)
    callback(null, self.cdnUri, self.uploadedFiles)
  })
}

Imager.prototype.startUpload = function (file, filename, variant, iteratorCallback) {
  if (!file.size) return iteratorCallback();

  var variants = this.config.variants[variant]
    , asyncArr = []
    , self = this

  if (variants.resize) {
    Object.keys(variants.resize).forEach(function (name) {
      var processFiles = function (cb) {
        self.resizeFile(file, { name: name, size: variants.resize[name] }, filename, cb)
      }

      asyncArr.push(processFiles)
    })
  }

  if (variants.crop) {
    Object.keys(variants.crop).forEach(function (name) {
      var processFiles = function (cb) {
        self.cropFile(file, { name: name, size: variants.crop[name] }, filename, cb)
      }

      asyncArr.push(processFiles)
    })
  }

  async.parallel(asyncArr, function (err, results) {
    self.uploadedFiles.push(_.uniq(results))
    iteratorCallback(err)
  })
}

Imager.prototype.resizeFile = function (file, preset, fname, cb) {
  var ct = { 'image/jpeg': '.jpg', 'image/png': '.png', 'image/gif': '.gif' }
    , fileType = ct[file['type']]
    , filename = fname + fileType
    , self = this

  var remoteFile = preset.name + '_' + filename
    , dest = '/tmp/' + remoteFile

  var options = {
      srcPath: file['path']
    , dstPath: dest
    , width: preset.size.split('x')[0]
  }

  im.resize(options, function(err, stdout, stderr) {
    if (err) return cb(err)
    self['pushTo' + self.storage](dest, remoteFile, filename, file['type'], cb)
  })
}

Imager.prototype.cropFile = function (file, preset, fname, cb) {
  var ct = { 'image/jpeg': '.jpg', 'image/png': '.png', 'image/gif': '.gif' }
    , fileType = ct[file['type']]
    , filename = fname + fileType
    , self = this

  var remoteFile = preset.name + '_' + filename
    , dest = '/tmp/' + remoteFile

  var options = {
      srcPath: file['path']
    , dstPath: dest
    , width: preset.size.split('x')[0]
    , height:  preset.size.split('x')[1]
  }

  im.crop(options, function(err, stdout, stderr){
    if (err) return cb(err)
    self['pushTo' + self.storage](dest, remoteFile, filename, file['type'], cb)
  })
}

Imager.prototype.pushToRackspace = function (file, remoteFile, filename, type, cb) {
  var self = this
    , client = cloudfiles.createClient(this.config['storage'][this.storage])
    , rsContainer = this.config['storage'][this.storage].container

  fs.readFile(file, function (err, buf) {
    if (err) return cb(err)

    var onUploadToContainer = function (err, uploaded) {
      if (err) return cb(err)
      if (uploaded) {
        cb(null, filename)
      }
    }

    var addFileToContainer = function (err, container) {
      if (err) return cb(err)
      var options = {
          remote: remoteFile
        , headers: {
              'Content-Type': type
            , 'Content-Length': buf.length
          }
        , local: file }

      self.cdnUri = container.cdnUri
      client.addFile(rsContainer, options, onUploadToContainer)
    }

    client.setAuth(function() {
      client.getContainer(rsContainer, true, addFileToContainer)
    })
  })
}

Imager.prototype.remove = function (files, callback, variant) {
  var self = this

  var prepareRemove = function (file, iteratorCallback) {
    self.prepareRemove(file, iteratorCallback, variant)
  }

  async.forEach(files, prepareRemove, function (err) {
    if (err) return callback(err)
    callback(null)
  })
}

Imager.prototype.prepareRemove = function (file, iteratorCallback, variant) {
  var variants = this.config.variants[variant]
    , asyncArr = []
    , self = this

  if (variants.resize) {
    Object.keys(variants.resize).forEach(function (preset) {
      var removeFiles = function (cb) {
        self['removeFrom' + self.storage](file, preset, cb)
      }

      asyncArr.push(removeFiles)
    })
  }

  if (variants.crop) {
    Object.keys(variants.crop).forEach(function (preset) {
      var removeFiles = function (cb) {
        self['removeFrom' + self.storage](file, preset, cb)
      }

      asyncArr.push(removeFiles)
    })
  }

  async.parallel(asyncArr, function (err, results) {
    iteratorCallback(err)
  })
}

Imager.prototype.removeFromRackspace = function (file, preset, cb) {
  var self = this
    , client = cloudfiles.createClient(this.config['storage'][this.storage])
    , rsContainer = this.config['storage'][this.storage].container
    , filename = preset + '_' + file

  client.setAuth(function() {
    client.getContainer(rsContainer, true, function (err, container) {
      if (err) return callback(err)

      container.removeFile(filename, function(err){
        if (err) console.log(err)
        cb(err)
      })
    })
  })
}
<|endoftext|>


<commit_before>const Promise = require("bluebird");
const rancherFn = require( "../../src/rancher" );
const format = require( "util" ).format;
const rp = require( "request-promise" );

function shouldUpgradeStack ( stack, catalog, branch, version ) {
	if (stack.externalId) {
		const stackInfo = stack.externalId.split("://")[1].split(":");
		const stackCatalog = stackInfo[0];
		const stackBranch = stackInfo[1];
		const stackVersion = stackInfo[2];
		return stackCatalog === catalog &&
			stackBranch === branch &&
			parseInt(stackVersion) < parseInt(version);
	} else {
		return false;
	}
}

const getTemplate = Promise.coroutine(function* ( token, catalogOwner, catalog, branch, version ) {

	let response =  yield rp( format( "https://api.github.com/repos/%s/%s/contents/templates/%s/%s", catalogOwner, catalog, branch, version ), {
		qs: {
			access_token: token, // eslint-disable-line
			ref: "master"
		},
		headers: {
			"User-Agent": "cowpoke"
		},
		json: true
	} );

	const templateResult = {version};

	for ( var i = 0; i < response.length; i++ ) {
		templateResult[response[i].name] = yield rp( response[i].download_url, {
			qs: {
				access_token: token // eslint-disable-line
			},
			headers: {
				"User-Agent": "cowpoke"
			}
		} );
	}

	return templateResult;

});

const upgradeStack = Promise.coroutine(function* ( rancherUrl, rancherUser, slack, envelope ) {

	//read args
	const githubInfo = envelope.data.catalog ? envelope.data.catalog.split("/") : [];
	const githubOwner = githubInfo[0];
	const githubRepo = githubInfo[1];
	const rancherCatalogName = envelope.data.rancherCatalogName;
	const branch = envelope.data.branch;
	const catalogNum = envelope.data.catalogVersion;
	const githubToken = envelope.data.githubToken;
	//check args
	if (!githubToken || !githubInfo || !githubOwner || !githubRepo || !branch || !catalogNum || !rancherCatalogName || isNaN(catalogNum)) {
		return {status: 401, data: {message: "Invaild arguments"}};
	}

	//get the template
	const template = yield getTemplate( githubToken, githubOwner, githubRepo, branch, catalogNum).catch( () => undefined );
	if ( template === undefined ) {
		return {status: 404, data: {message: "Unable to get template yaml files from github. Check repository and token"}};
	}

	//get the environments
	const enviorments = yield rancherFn(rancherUrl, rancherUser).then(elm => elm.listEnvironments()).catch(() => undefined);
	if ( enviorments === undefined ) {
		return {status: 500, data: {message: "Unable to get enviorments from the rancher"}};
	}

	//loop through the stacks and upgrade those that match
	const upgraded = [];
	for (let i = 0; i < enviorments.length; i++) {
		const upgradedStacks = [];
		const stacks = yield enviorments[i].listStacks();
		for ( let j = 0; j < stacks.length; j++ ) {
			if (shouldUpgradeStack( stacks[j], rancherCatalogName, branch, catalogNum )) {
				upgradedStacks.push( {
					name: stacks[j].name,
					id: stacks[j].id
				});
				slack.send(format("Starting upgrade of stack %s in %s.", stacks[j].name, enviorments[i].name));
				stacks[j].upgrade(template).then(
					() => slack.send(format("Finished upgrade of stack %s in %s.", stacks[j].name, enviorments[i].name))
				).catch(
					() => slack.send(format("There was an error during upgrade of stack %s in %s.", stacks[j].name, enviorments[i].name))
				);
			}
		}
		if (upgradedStacks.length !== 0) {
			upgraded.push( {
				environment: enviorments[i].name,
				upgraded: upgradedStacks
			});
		}
	}
	if (upgraded.length !== 0) {
		return {
			upgraded_stacks_by_environment: upgraded // eslint-disable-line
		};
	} else {
		return {message: "Nothing eligible for upgrading"};
	}
});

module.exports = {upgradeStack};
<commit_msg>Adding error message for no enviorments found<commit_after>const Promise = require("bluebird");
const rancherFn = require( "../../src/rancher" );
const format = require( "util" ).format;
const rp = require( "request-promise" );

function shouldUpgradeStack ( stack, catalog, branch, version ) {
	if (stack.externalId) {
		const stackInfo = stack.externalId.split("://")[1].split(":");
		const stackCatalog = stackInfo[0];
		const stackBranch = stackInfo[1];
		const stackVersion = stackInfo[2];
		return stackCatalog === catalog &&
			stackBranch === branch &&
			parseInt(stackVersion) < parseInt(version);
	} else {
		return false;
	}
}

const getTemplate = Promise.coroutine(function* ( token, catalogOwner, catalog, branch, version ) {

	let response =  yield rp( format( "https://api.github.com/repos/%s/%s/contents/templates/%s/%s", catalogOwner, catalog, branch, version ), {
		qs: {
			access_token: token, // eslint-disable-line
			ref: "master"
		},
		headers: {
			"User-Agent": "cowpoke"
		},
		json: true
	} );

	const templateResult = {version};

	for ( var i = 0; i < response.length; i++ ) {
		templateResult[response[i].name] = yield rp( response[i].download_url, {
			qs: {
				access_token: token // eslint-disable-line
			},
			headers: {
				"User-Agent": "cowpoke"
			}
		} );
	}

	return templateResult;

});

const upgradeStack = Promise.coroutine(function* ( rancherUrl, rancherUser, slack, envelope ) {

	//read args
	const githubInfo = envelope.data.catalog ? envelope.data.catalog.split("/") : [];
	const githubOwner = githubInfo[0];
	const githubRepo = githubInfo[1];
	const rancherCatalogName = envelope.data.rancherCatalogName;
	const branch = envelope.data.branch;
	const catalogNum = envelope.data.catalogVersion;
	const githubToken = envelope.data.githubToken;
	//check args
	if (!githubToken || !githubInfo || !githubOwner || !githubRepo || !branch || !catalogNum || !rancherCatalogName || isNaN(catalogNum)) {
		return {status: 401, data: {message: "Invaild arguments"}};
	}

	//get the template
	const template = yield getTemplate( githubToken, githubOwner, githubRepo, branch, catalogNum).catch( () => undefined );
	if ( template === undefined ) {
		return {status: 404, data: {message: "Unable to get template yaml files from github. Check repository and token"}};
	}

	//get the environments
	const enviorments = yield rancherFn(rancherUrl, rancherUser).then(elm => elm.listEnvironments()).catch(() => undefined);
	if ( enviorments === undefined ) {
		return {status: 500, data: {message: "Unable to get enviorments from the rancher"}};
	}
	if ( enviorments === undefined ) {
		return {status: 500, data: {message: "Unable to get enviorments from the rancher"}};
	}
	if ( enviorments.length === 0 ) {
		return {status: 500, data: {message: "No rancher enviorments found. Please check the cowpoke user permissions"}};
	}

	//loop through the stacks and upgrade those that match
	const upgraded = [];
	for (let i = 0; i < enviorments.length; i++) {
		const upgradedStacks = [];
		const stacks = yield enviorments[i].listStacks();
		for ( let j = 0; j < stacks.length; j++ ) {
			if (shouldUpgradeStack( stacks[j], rancherCatalogName, branch, catalogNum )) {
				upgradedStacks.push( {
					name: stacks[j].name,
					id: stacks[j].id
				});
				slack.send(format("Starting upgrade of stack %s in %s.", stacks[j].name, enviorments[i].name));
				stacks[j].upgrade(template).then(
					() => slack.send(format("Finished upgrade of stack %s in %s.", stacks[j].name, enviorments[i].name))
				).catch(
					() => slack.send(format("There was an error during upgrade of stack %s in %s.", stacks[j].name, enviorments[i].name))
				);
			}
		}
		if (upgradedStacks.length !== 0) {
			upgraded.push( {
				environment: enviorments[i].name,
				upgraded: upgradedStacks
			});
		}
	}
	if (upgraded.length !== 0) {
		return {
			upgraded_stacks_by_environment: upgraded // eslint-disable-line
		};
	} else {
		return {message: "Nothing eligible for upgrading"};
	}
});

module.exports = {upgradeStack};
<|endoftext|>


<commit_before>var sortColumns = {
  'fulfilled-last': 'fulfilled_at',
  'top': 'post.num_votes',
  'recent': 'post.updated_at',
  'suggested': 'suggested'
}

var findPosts = function (req, res, opts) {
  var params = _.merge(
    _.pick(req.allParams(), ['sort', 'limit', 'offset', 'type', 'start_time', 'end_time']),
    _.pick(opts, 'sort')
  )

  Promise.props({
    communities: opts.communities,
    project: opts.project,
    users: opts.users,
    type: params.type,
    limit: params.limit,
    offset: params.offset,
    start_time: params.start_time,
    end_time: params.end_time,
    visibility: opts.visibility,
    sort: sortColumns[params.sort || 'recent'],
    forUser: req.session.userId
  })
  .then(args => Search.forPosts(args).fetchAll({
    withRelated: PostPresenter.relations(req.session.userId, opts.relationsOpts)
  }))
  .then(PostPresenter.mapPresentWithTotal)
  .then(res.ok, res.serverError)
}

var newPostAttrs = function (userId, params) {
  return _.merge(Post.newPostAttrs(), {
    name: RichText.sanitize(params.name),
    description: RichText.sanitize(params.description),
    type: params.type,
    user_id: userId
  })
}

var afterSavingPost = function (post, opts) {
  var userId = post.get('user_id')
  var mentioned = RichText.getUserMentions(post.get('description'))
  var followerIds = _.uniq(mentioned.concat(userId))

  return Promise.all(_.flatten([
    // Attach post to communities
    opts.communities.map(id => new Community({id: id}).posts().attach(post.id, _.pick(opts, 'transacting'))),

    // Add mentioned users and creator as followers
    post.addFollowers(followerIds, userId, _.pick(opts, 'transacting')),

    // create activity and send notification to all mentioned users except the creator
    Promise.map(_.without(mentioned, userId), mentionedUserId => Post.notifyAboutMention(post, mentionedUserId, _.pick(opts, 'transacting'))),

    // Add image, if any
    (opts.imageUrl ? Media.create({
      postId: post.id,
      url: opts.imageUrl,
      transacting: opts.transacting
    }) : null)
  ])).then(() => mentioned)
}

module.exports = {
  findOne: function (req, res) {
    res.locals.post.load(PostPresenter.relations(req.session.userId))
    .then(PostPresenter.present)
    .then(res.ok)
    .catch(res.serverError)
  },

  findForUser: function (req, res) {
    findPosts(req, res, {
      users: [req.param('userId')],
      communities: Membership.activeCommunityIds(req.session.userId),
      visibility: (req.session.userId ? null : Post.Visibility.PUBLIC_READABLE)
    })
  },

  findForCommunity: function (req, res) {
    if (TokenAuth.isAuthenticated(res)) {
      if (!RequestValidation.requireTimeRange(req, res)) return
    }

    findPosts(req, res, {
      communities: [req.param('communityId')],
      visibility: (req.session.userId ? null : Post.Visibility.PUBLIC_READABLE)
    })
  },

  findForProject: function (req, res) {
    findPosts(req, res, {
      project: req.param('projectId'),
      relationsOpts: {fromProject: true},
      sort: 'fulfilled-last'
    })
  },

  findForNetwork: function (req, res) {
    Community.where({network_id: req.param('networkId')}).fetchAll()
    .then(communities => {
      findPosts(req, res, {
        communities: communities.map(c => c.id),
        visibility: [Post.Visibility.DEFAULT, Post.Visibility.PUBLIC_READABLE]
      })
    })
  },

  findFollowed: function (req, res) {
    Search.forPosts({
      follower: req.session.userId,
      limit: req.param('limit') || 10,
      offset: req.param('offset'),
      sort: 'post.updated_at',
      type: 'all+welcome'
    }).fetchAll({
      withRelated: PostPresenter.relations(req.session.userId)
    })
    .then(PostPresenter.mapPresentWithTotal)
    .then(res.ok)
    .catch(res.serverError)
  },

  findAllForUser: function (req, res) {
    Membership.activeCommunityIds(req.session.userId)
    .then(function (communityIds) {
      return Search.forPosts({
        communities: communityIds,
        limit: req.param('limit') || 10,
        offset: req.param('offset'),
        sort: sortColumns[req.param('sort') || 'recent'],
        type: req.param('type') || 'all+welcome',
        forUser: req.session.userId
      }).fetchAll({
        withRelated: PostPresenter.relations(req.session.userId)
      })
    })
    .then(PostPresenter.mapPresentWithTotal)
    .then(res.ok)
    .catch(res.serverError)
  },

  create: function (req, res) {
    var attrs = newPostAttrs(req.session.userId, req.allParams())

    return bookshelf.transaction(trx => {
      return new Post(attrs).save(null, {transacting: trx})
      .tap(post => afterSavingPost(post, {
        communities: req.param('communities'),
        imageUrl: req.param('imageUrl'),
        transacting: trx
      }))
    })
    .then(post => post.load(PostPresenter.relations(req.session.userId)))
    .then(PostPresenter.present)
    .then(res.ok)
    .catch(res.serverError)
  },

  createForProject: function (req, res) {
    var attrs = newPostAttrs(req.session.userId, req.allParams())
    var projectId = req.param('projectId')

    return Project.find(projectId)
    .tap(project => {
      if (project.isDraft()) attrs.visibility = Post.Visibility.DRAFT_PROJECT
    })
    .then(project => bookshelf.transaction(trx => {
      return new Post(attrs).save(null, {transacting: trx})
      .tap(post => PostProjectMembership.create(post.id, projectId, {transacting: trx}))
      .tap(post => afterSavingPost(post, {
        communities: [req.param('communityId')],
        imageUrl: req.param('imageUrl'),
        transacting: trx
      })
      .then(mentioned => {
        // Send notifications to project contributors
        Queue.classMethod('Project', 'notifyAboutNewPost', {
          projectId: projectId,
          postId: post.id,
          exclude: mentioned
        })
      }))
    }))
    .then(post => post.load(PostPresenter.relations(req.session.userId)))
    .then(PostPresenter.present)
    .then(res.ok)
    .catch(res.serverError)
  },

  addFollowers: function (req, res) {
    res.locals.post.load('followers').then(function (post) {
      var added = req.param('userIds')
      var existing = post.relations.followers.pluck('user_id')

      return bookshelf.transaction(function (trx) {
        return post.addFollowers(_.difference(added, existing), req.session.userId, {
          transacting: trx,
          createActivity: true
        })
      })
    })
    .then(() => res.ok({}), res.serverError)
  },

  follow: function (req, res) {
    var userId = req.session.userId
    var post = res.locals.post
    Follower.query().where({user_id: userId, post_id: post.id}).count()
    .then(function (rows) {
      if (Number(rows[0].count) > 0 || req.param('force') === 'unfollow') {
        return post.removeFollower(userId, {createActivity: true})
        .then(() => res.ok({}))
      }

      return post.addFollowers([userId], userId, {createActivity: true}).then(function (follows) {
        return User.find(req.session.userId)
      }).then(function (user) {
        res.ok(_.pick(user.attributes, 'id', 'name', 'avatar_url'))
      })
    })
    .catch(res.serverError.bind(res))
  },

  update: function (req, res) {
    var post = res.locals.post
    var attrs = _.extend(
      _.pick(req.allParams(), 'name', 'description', 'type'),
      {edited: true, edited_timestamp: new Date()}
    )

    bookshelf.transaction(function (trx) {
      return post.save(attrs, {patch: true, transacting: trx})
      .tap(() => {
        var newIds = req.param('communities').sort()
        var oldIds = post.relations.communities.pluck('id').sort()
        if (newIds !== oldIds) {
          return Promise.join(
            Promise.map(_.difference(newIds, oldIds), id =>
              post.communities().attach(id, {transacting: trx})),
            Promise.map(_.difference(oldIds, newIds), id =>
              post.communities().detach(id, {transacting: trx}))
          )
        }
      })
      .tap(function () {
        var imageUrl = req.param('imageUrl')
        var imageRemoved = req.param('imageRemoved')

        if (!imageUrl && !imageRemoved) return

        return post.load('media').then(function (post) {
          var media = post.relations.media.first()

          if (media && imageRemoved) { // remove media
            return media.destroy({transacting: trx})

          } else if (media) { // replace url in existing media
            return media.save({url: imageUrl}, {patch: true, transacting: trx})

          } else if (imageUrl) { // create new media
            return Media.create({
              postId: post.id,
              url: imageUrl,
              transacting: trx
            })
          }
        })
      })
    })
    .then(() => res.ok({}))
    .catch(res.serverError)
  },

  fulfill: function (req, res) {
    bookshelf.transaction(function (trx) {
      return res.locals.post.save({
        fulfilled_at: new Date()
      }, {patch: true, transacting: trx})
      .tap(function () {
        return Promise.map(req.param('contributors'), function (userId) {
          return new Contribution({
            post_id: res.locals.post.id,
            user_id: userId,
            date_contributed: new Date()
          }).save(null, {transacting: trx})
        })
      })
    })
    .then(() => res.ok({}))
    .catch(res.serverError)
  },

  vote: function (req, res) {
    var post = res.locals.post

    post.votes().query({where: {user_id: req.session.userId}}).fetchOne()
    .then(vote => bookshelf.transaction(trx => {
      var inc = delta => () => Post.query().where('id', post.id).increment('num_votes', delta).transacting(trx)

      return (vote ?
        vote.destroy({transacting: trx}).then(inc(-1)) :
        new Vote({
          post_id: res.locals.post.id,
          user_id: req.session.userId
        }).save().then(inc(1)))
    }))
    .then(() => res.ok({}), res.serverError)
  },

  destroy: function (req, res) {
    return bookshelf.transaction(function (trx) {
      // FIXME this post will still be accessible via activity about a comment
      return Promise.join(
        Activity.where('post_id', res.locals.post.id).destroy({transacting: trx}),
        res.locals.post.save({active: false}, {patch: true, transacting: trx})
      )

    })
    .then(() => res.ok({}), res.serverError)
  },

  complain: function (req, res) {
    var post = res.locals.post
    var community = post.relations.communities.first()

    User.find(req.session.userId)
    .then(user => Email.sendRawEmail('hello@hylo.com', {
      subject: 'Objectionable content report',
      body: format(
        '%s <%s> has flagged %s as objectionable',
        user.get('name'), user.get('email'),
        Frontend.Route.post(post, community)
      )
    }))
    .then(() => res.ok({}), res.serverError)
  }

}
<commit_msg>set type='image' for attached images<commit_after>var sortColumns = {
  'fulfilled-last': 'fulfilled_at',
  'top': 'post.num_votes',
  'recent': 'post.updated_at',
  'suggested': 'suggested'
}

var findPosts = function (req, res, opts) {
  var params = _.merge(
    _.pick(req.allParams(), ['sort', 'limit', 'offset', 'type', 'start_time', 'end_time']),
    _.pick(opts, 'sort')
  )

  Promise.props({
    communities: opts.communities,
    project: opts.project,
    users: opts.users,
    type: params.type,
    limit: params.limit,
    offset: params.offset,
    start_time: params.start_time,
    end_time: params.end_time,
    visibility: opts.visibility,
    sort: sortColumns[params.sort || 'recent'],
    forUser: req.session.userId
  })
  .then(args => Search.forPosts(args).fetchAll({
    withRelated: PostPresenter.relations(req.session.userId, opts.relationsOpts)
  }))
  .then(PostPresenter.mapPresentWithTotal)
  .then(res.ok, res.serverError)
}

var newPostAttrs = function (userId, params) {
  return _.merge(Post.newPostAttrs(), {
    name: RichText.sanitize(params.name),
    description: RichText.sanitize(params.description),
    type: params.type,
    user_id: userId
  })
}

var afterSavingPost = function (post, opts) {
  var userId = post.get('user_id')
  var mentioned = RichText.getUserMentions(post.get('description'))
  var followerIds = _.uniq(mentioned.concat(userId))

  return Promise.all(_.flatten([
    // Attach post to communities
    opts.communities.map(id => new Community({id: id}).posts().attach(post.id, _.pick(opts, 'transacting'))),

    // Add mentioned users and creator as followers
    post.addFollowers(followerIds, userId, _.pick(opts, 'transacting')),

    // create activity and send notification to all mentioned users except the creator
    Promise.map(_.without(mentioned, userId), mentionedUserId => Post.notifyAboutMention(post, mentionedUserId, _.pick(opts, 'transacting'))),

    // Add image, if any
    (opts.imageUrl ? Media.create({
      postId: post.id,
      url: opts.imageUrl,
      type: 'image',
      transacting: opts.transacting
    }) : null)
  ])).then(() => mentioned)
}

module.exports = {
  findOne: function (req, res) {
    res.locals.post.load(PostPresenter.relations(req.session.userId))
    .then(PostPresenter.present)
    .then(res.ok)
    .catch(res.serverError)
  },

  findForUser: function (req, res) {
    findPosts(req, res, {
      users: [req.param('userId')],
      communities: Membership.activeCommunityIds(req.session.userId),
      visibility: (req.session.userId ? null : Post.Visibility.PUBLIC_READABLE)
    })
  },

  findForCommunity: function (req, res) {
    if (TokenAuth.isAuthenticated(res)) {
      if (!RequestValidation.requireTimeRange(req, res)) return
    }

    findPosts(req, res, {
      communities: [req.param('communityId')],
      visibility: (req.session.userId ? null : Post.Visibility.PUBLIC_READABLE)
    })
  },

  findForProject: function (req, res) {
    findPosts(req, res, {
      project: req.param('projectId'),
      relationsOpts: {fromProject: true},
      sort: 'fulfilled-last'
    })
  },

  findForNetwork: function (req, res) {
    Community.where({network_id: req.param('networkId')}).fetchAll()
    .then(communities => {
      findPosts(req, res, {
        communities: communities.map(c => c.id),
        visibility: [Post.Visibility.DEFAULT, Post.Visibility.PUBLIC_READABLE]
      })
    })
  },

  findFollowed: function (req, res) {
    Search.forPosts({
      follower: req.session.userId,
      limit: req.param('limit') || 10,
      offset: req.param('offset'),
      sort: 'post.updated_at',
      type: 'all+welcome'
    }).fetchAll({
      withRelated: PostPresenter.relations(req.session.userId)
    })
    .then(PostPresenter.mapPresentWithTotal)
    .then(res.ok)
    .catch(res.serverError)
  },

  findAllForUser: function (req, res) {
    Membership.activeCommunityIds(req.session.userId)
    .then(function (communityIds) {
      return Search.forPosts({
        communities: communityIds,
        limit: req.param('limit') || 10,
        offset: req.param('offset'),
        sort: sortColumns[req.param('sort') || 'recent'],
        type: req.param('type') || 'all+welcome',
        forUser: req.session.userId
      }).fetchAll({
        withRelated: PostPresenter.relations(req.session.userId)
      })
    })
    .then(PostPresenter.mapPresentWithTotal)
    .then(res.ok)
    .catch(res.serverError)
  },

  create: function (req, res) {
    var attrs = newPostAttrs(req.session.userId, req.allParams())

    return bookshelf.transaction(trx => {
      return new Post(attrs).save(null, {transacting: trx})
      .tap(post => afterSavingPost(post, {
        communities: req.param('communities'),
        imageUrl: req.param('imageUrl'),
        transacting: trx
      }))
    })
    .then(post => post.load(PostPresenter.relations(req.session.userId)))
    .then(PostPresenter.present)
    .then(res.ok)
    .catch(res.serverError)
  },

  createForProject: function (req, res) {
    var attrs = newPostAttrs(req.session.userId, req.allParams())
    var projectId = req.param('projectId')

    return Project.find(projectId)
    .tap(project => {
      if (project.isDraft()) attrs.visibility = Post.Visibility.DRAFT_PROJECT
    })
    .then(project => bookshelf.transaction(trx => {
      return new Post(attrs).save(null, {transacting: trx})
      .tap(post => PostProjectMembership.create(post.id, projectId, {transacting: trx}))
      .tap(post => afterSavingPost(post, {
        communities: [req.param('communityId')],
        imageUrl: req.param('imageUrl'),
        transacting: trx
      })
      .then(mentioned => {
        // Send notifications to project contributors
        Queue.classMethod('Project', 'notifyAboutNewPost', {
          projectId: projectId,
          postId: post.id,
          exclude: mentioned
        })
      }))
    }))
    .then(post => post.load(PostPresenter.relations(req.session.userId)))
    .then(PostPresenter.present)
    .then(res.ok)
    .catch(res.serverError)
  },

  addFollowers: function (req, res) {
    res.locals.post.load('followers').then(function (post) {
      var added = req.param('userIds')
      var existing = post.relations.followers.pluck('user_id')

      return bookshelf.transaction(function (trx) {
        return post.addFollowers(_.difference(added, existing), req.session.userId, {
          transacting: trx,
          createActivity: true
        })
      })
    })
    .then(() => res.ok({}), res.serverError)
  },

  follow: function (req, res) {
    var userId = req.session.userId
    var post = res.locals.post
    Follower.query().where({user_id: userId, post_id: post.id}).count()
    .then(function (rows) {
      if (Number(rows[0].count) > 0 || req.param('force') === 'unfollow') {
        return post.removeFollower(userId, {createActivity: true})
        .then(() => res.ok({}))
      }

      return post.addFollowers([userId], userId, {createActivity: true}).then(function (follows) {
        return User.find(req.session.userId)
      }).then(function (user) {
        res.ok(_.pick(user.attributes, 'id', 'name', 'avatar_url'))
      })
    })
    .catch(res.serverError.bind(res))
  },

  update: function (req, res) {
    var post = res.locals.post
    var attrs = _.extend(
      _.pick(req.allParams(), 'name', 'description', 'type'),
      {edited: true, edited_timestamp: new Date()}
    )

    bookshelf.transaction(function (trx) {
      return post.save(attrs, {patch: true, transacting: trx})
      .tap(() => {
        var newIds = req.param('communities').sort()
        var oldIds = post.relations.communities.pluck('id').sort()
        if (newIds !== oldIds) {
          return Promise.join(
            Promise.map(_.difference(newIds, oldIds), id =>
              post.communities().attach(id, {transacting: trx})),
            Promise.map(_.difference(oldIds, newIds), id =>
              post.communities().detach(id, {transacting: trx}))
          )
        }
      })
      .tap(function () {
        var imageUrl = req.param('imageUrl')
        var imageRemoved = req.param('imageRemoved')

        if (!imageUrl && !imageRemoved) return

        return post.load('media').then(function (post) {
          var media = post.relations.media.first()

          if (media && imageRemoved) { // remove media
            return media.destroy({transacting: trx})

          } else if (media) { // replace url in existing media
            return media.save({url: imageUrl}, {patch: true, transacting: trx})

          } else if (imageUrl) { // create new media
            return Media.create({
              postId: post.id,
              url: imageUrl,
              type: 'image',
              transacting: trx
            })
          }
        })
      })
    })
    .then(() => res.ok({}))
    .catch(res.serverError)
  },

  fulfill: function (req, res) {
    bookshelf.transaction(function (trx) {
      return res.locals.post.save({
        fulfilled_at: new Date()
      }, {patch: true, transacting: trx})
      .tap(function () {
        return Promise.map(req.param('contributors'), function (userId) {
          return new Contribution({
            post_id: res.locals.post.id,
            user_id: userId,
            date_contributed: new Date()
          }).save(null, {transacting: trx})
        })
      })
    })
    .then(() => res.ok({}))
    .catch(res.serverError)
  },

  vote: function (req, res) {
    var post = res.locals.post

    post.votes().query({where: {user_id: req.session.userId}}).fetchOne()
    .then(vote => bookshelf.transaction(trx => {
      var inc = delta => () => Post.query().where('id', post.id).increment('num_votes', delta).transacting(trx)

      return (vote ?
        vote.destroy({transacting: trx}).then(inc(-1)) :
        new Vote({
          post_id: res.locals.post.id,
          user_id: req.session.userId
        }).save().then(inc(1)))
    }))
    .then(() => res.ok({}), res.serverError)
  },

  destroy: function (req, res) {
    return bookshelf.transaction(function (trx) {
      // FIXME this post will still be accessible via activity about a comment
      return Promise.join(
        Activity.where('post_id', res.locals.post.id).destroy({transacting: trx}),
        res.locals.post.save({active: false}, {patch: true, transacting: trx})
      )

    })
    .then(() => res.ok({}), res.serverError)
  },

  complain: function (req, res) {
    var post = res.locals.post
    var community = post.relations.communities.first()

    User.find(req.session.userId)
    .then(user => Email.sendRawEmail('hello@hylo.com', {
      subject: 'Objectionable content report',
      body: format(
        '%s <%s> has flagged %s as objectionable',
        user.get('name'), user.get('email'),
        Frontend.Route.post(post, community)
      )
    }))
    .then(() => res.ok({}), res.serverError)
  }

}
<|endoftext|>


<commit_before>/**
 * -------------------------- Módulo PlayFile --------------------------------------------------
 * @author Diego Alberto Molina Vera
 *
 * Encargada de reproducir la canción, tocar la siguiente canción o la anterior.
 * Acá se usa la Audio Web API para reproducir la canción y también usar datos
 * obtenidos mediante el buffer.
 */
/* --------------------------------------- Módulos ------------------------------------------- */
const worker = new Worker('../../assets/js/timeWorker.js');
const { dialog } = require('electron').remote;
const path = require('path');
require('./commons');

/* --------------------------------------- Variables ------------------------------------------- */
let isMovingForward = false; // Si se está tratando de adelantar la cación a un tiempo determinado
let isNextAble = true;
let isSongPlaying = false; // Se ejecutó play sobre el AudioNode
let position = null; // Posición de la canción actual
let tmpPosition = []; // Posición de la canción anteriormente reproducida
let filePath = ''; // Ruta de la canción
let songs = {}; // Listado de canciones
let infoSong = {};

// Variables necesarias para trabajar sobre el AudioContext
const audioContext = new window.AudioContext(); // Objeto AudioContext
const xhtr = new XMLHttpRequest(); // Objeto XMLHttpRequest

// Frecuencias
const hrz = [
 40, 80, 90, 100, 120, 150, 200,
 300, 400, 500, 600, 800, 1000,
 1600, 2000, 3000, 4000, 5000, 6000,
 7000, 8000, 10000, 16000
];

let filter = []; // Array con el filtro a usar en distintas frecuencias
let _duration = 0; // Duración máxima de la canción
let _buffer = {}; // Buffer devuelto por decodeAudioData
let source = null; // Objeto AudioNode

// Variables para generar el calculo del tiempo transcurrido
// let millisecond = 1;
let interval = null; // Función interval para crear el tiempo de reproducción
let _minute = 0; // Final
let _second = 0; // Final
let forward = 0; // tiempo estimado dónde debería de seguir corriendo la canción al adelantarla
let lapse = 0;
let time = 0; // Tiempo total final
let minute = 0;
let second = 0;
let millisecond = 0;
let percent = 0;
let time_m = 0;

// Notificación
let notification = null; // Despliega una notificación de la canción que se va a reproducir
let notfi = {
  lang: 'US',
  tag: 'song',
  silent: false,
  icon: path.join(__dirname, '..', 'img', 'play.png')
};

// Para generar la animación del botón play
const anim = {
  from: [
    'M 5.827315,6.7672041 62.280287,48.845328 62.257126,128.62684 5.8743095,170.58995 Z',
    'm 61.189203,48.025 56.296987,40.520916 0,0.0028 -56.261916,40.850634 z'
  ],
  to: [
    'M 5.827315,6.7672041 39.949651,6.9753863 39.92649,170.36386 5.8743095,170.58995 Z',
    'm 83.814203,6.9000001 34.109487,0.037583 -0.0839,163.399307 -33.899661,0.16304 z'
  ]
};

/** --------------------------------------- Funciones --------------------------------------- **/
// Recibe el listado de canciones desde el archivo listSongs.js
function setSongs(_songs) {
  songs = _songs;
  filters();
}

// Retorna un número aleatorio entre 0 y el total de canciones
function shuffle() { return Math.floor(Math.random() * songs.length); }

function animPlay() {
  $('.anim-play').each((v, i) => {
    v.attr({ 'from': anim.from[i], 'to': anim.to[i] }).element.beginElement();
  })
}

function animPause() {
  $('.anim-play').each((v, i) => {
    v.attr({ 'from': anim.to[i], 'to': anim.from[i] }).element.beginElement();
  });
}

// Reproduce una cancion o la pausará
// Responde a todos los posibles estados de una canción
function playSong() {
  if (!isSongPlaying && audioContext.state === 'running') { // Primera vez
    if (position === null) dataSong(shuffle());
    play();
    animPlay();

    return 'resume';
  } else if (isSongPlaying && audioContext.state === 'running') { // Ya reproduciendo
    audioContext.suspend().then(() => {
      isSongPlaying = false;
      cancelAnimationFrame(interval);
    });
    animPause();

    return 'paused';
  } else if (!isSongPlaying && audioContext.state === 'suspended') { // Pausado
    isSongPlaying = true;
    startTimer();
    audioContext.resume();
    animPlay();

    return 'resume';
  }
}

// Genera el tiempo que lleva reproduciendose la canción
function startTimer() {
  worker.addEventListener('message', e => {
    $('#time-start').text(e.data.time);
    $('#progress-bar').css(e.data.w);
  });

  const iter = () => {
    worker.postMessage({ action:'start', per: lapse });
    interval = requestAnimationFrame(iter);
  }
  interval = requestAnimationFrame(iter);

}

// Límpia el tiempo transcurrido
function stopTimer() {
  if (!isMovingForward) {
    isSongPlaying = false;

    cancelAnimationFrame(interval);
    worker.postMessage({ action: 'stop' });

    _duration = _minute = _second = time =
    minute = second = millisecond = percent = 0;

    if (isNextAble && !isMovingForward) nextSong();
  } else if (isMovingForward) {
    /**
     * La función stop tarda unos milesegundos más que ejecutar la función moveForward.
     * Por lo tanto lo que continua después de detener la canción deberá ser ejecutado
     * dentro de la función onended
     */
    source = audioContext.createBufferSource(); // Se debe crear un nuevo AudioNode, ya que al dar stop el nodo se elimina
    source.onended = stopTimer; // Evento que se gatilla al terminar la canción

    // Conectar todos los nodos
    source.buffer = _buffer;
    source.connect(filter[0])
    filter.reduce((p, c) => p.connect(c))
    .connect(audioContext.destination);

    startTimer();
    source.start(0, forward);
    isMovingForward = false;
    isSongPlaying = true;
  }
}

// Recibe la posición de la canción a buscar en el objeto song
// para desplegarlos en la interfaz
function dataSong(_position) {
  $('#time-start').text('00:00');
  $('#progress-bar').css('width:0;');

  infoSong = songs[(position = parseInt(_position, 10))];
  filePath = infoSong.filename; // Ruta donde se encuentra el archivo a reproducir

  $('#song-title').data({position}).child().each(v => { v.text(infoSong.title); });
  $('#artist').child().each(v => { v.text(infoSong.artist); });
  $('#album').child().each(v => { v.text(infoSong.album); });

  // Mostrar notificación
  if (notification !== null) {
    notification.close();
    notification = null
  }

  notfi.body = `${infoSong.artist.replace(/\ /g, ' ')} from ${infoSong.album.replace(/\ /g, ' ')}`;
  notification = new Notification(infoSong.title.replace(/\ /g, ' '), notfi);
}

// EJecuta, por medio de la Audio Web API, la canción.
// Se obtiene un array buffer con info útil para usar
function play() {
  // Creamos un Buffer que contendrá la canción
  source = audioContext.createBufferSource();

  // Leer erl achivo de audio
  xhtr.open('GET', `file://${filePath}`, true);
  xhtr.responseType = 'arraybuffer';
  xhtr.onload = () => {
    audioContext.decodeAudioData(xhtr.response).then(buffer => {
      // Creamos un Buffer que contendrá la canción
      source = audioContext.createBufferSource();

      // Para ser usado al momento de querer adelantar la canción
      // El buffer nos entrega la duración de la canción.
      // La duración de la cación está en segundos, por ende hay que pasarla a minutos.
      _buffer = buffer;
      time = ((_duration = _buffer.duration) / 60).toString();
      _minute = parseInt(time.slice(0, time.lastIndexOf('.')), 10);
      _second = Math.floor(time.slice(time.lastIndexOf('.')) * 60);
      lapse = 100 / _duration; // Porcentaje a usar por cada segundo en la barra de progreso
      $('#time-end').text(`${_minute > 9 ? `${_minute}` : `0${_minute}`}${_second > 9 ? `:${_second}` : `:0${_second}`}`);

      // Evento que se gatilla al terminar la canción
      source.onended = stopTimer;

      // Conectar todos los nodos
      source.buffer = _buffer;
      source.connect(filter[0]);
      filter.reduce((p, c) => p.connect(c))
      .connect(audioContext.destination);

      // Inicializar el tiempo y la canción
      startTimer();
      source.start(0);
      isNextAble = isSongPlaying = true;
    }, reason => {
      dialog.showErrorBox('Error [002]', `${jread(LANG_FILE)[jread(CONFIG_FILE).lang].alerts.playSong}\n${reason}`);
      return;
    });
  };
  xhtr.send(null);
}

// Reproduce la siguiente canción.
// Esta función se comparte cuando se genera la lista de canciones,
// ya que al dar click sobre una canción, la que se reproduce es otra ("siguiente").
function nextSong(_position = -1) {
  if (isNextAble) {
    if (!isSongPlaying && audioContext.state === 'suspended') audioContext.resume();
    tmpPosition.push(position);

    isNextAble = isSongPlaying = false;
    if(source !== null) {
      source.stop(0);
      source = null;
    }

    dataSong(_position !== -1 ? _position : (
      jread(CONFIG_FILE).shuffle ? shuffle() : (songs.length - 1 > position ? position + 1 : 0)
    ));

    playSong();
  }
}

// Reproducirá la canción anterior
function prevSong() {
  if (isNextAble && (tmpPosition.length > 0 || tmpPosition[0] !== null)) {
    dataSong(tmpPosition.pop());

    // Verificar si el contexto está pausado o no.
    // Si está pausado no se reproducirá una nueva pista
    if (!isSongPlaying && audioContext.state === 'suspended') audioContext.resume();

    isNextAble = isSongPlaying = false;
    if(source !== null) {
      source.stop(0);
      source = null;
    }

    playSong();
  }
}

// Cambia los valores en la frecuencia específica
function setFilterVal(a, b) {
  filter[a].gain.setValueAtTime(b, audioContext.currentTime);
}

// Crea y asigna BiquadFilter de tipo peaking para las siguientes frequencias
// [50, 100, 156, 220, 331, 440, 622, 880, 1250, 1750, 2500, 3500, 5000, 10000, 20000]
function filters() {
  let f = null;
  let db = jread(CONFIG_FILE).equalizer.map(v =>
    v !== 0 ? parseFloat((v < 130 ? 121 - v : - (v - 140)) / 10) : 0
  );

  filter = hrz.map((v, i) =>
      (f = audioContext.createBiquadFilter(),
      f.type = 'peaking',
      f.frequency.value = v,
      f.Q.value = 1,
      f.gain.value = db[i], f)
  );
}

function moveForward(event, element) {
  forward = _duration * event.offsetX / element.clientWidth;
  time_m = (forward / 60).toString();

  // Recalcular el tiempo
  minute = parseInt(time_m.slice(0, time_m.lastIndexOf('.')), 10);
  second = Math.floor(time_m.slice(time_m.lastIndexOf('.')) * 60);
  millisecond = forward * 100 + 1;
  cancelAnimationFrame(interval);

  // Recalcular el porcentaje de la barra de tiempo
  percent = forward * (100 / _duration);
  isMovingForward = true;
  worker.postMessage({
    action: 'forward',
    d: [minute, second, millisecond, percent]
  });
  source.stop(0);
}

module.exports = Object.freeze({
  setSongs,
  playSong,
  prevSong,
  nextSong,
  setFilterVal,
  moveForward
});<commit_msg>replaced element by get()<commit_after>/**
 * -------------------------- Módulo PlayFile --------------------------------------------------
 * @author Diego Alberto Molina Vera
 *
 * Encargada de reproducir la canción, tocar la siguiente canción o la anterior.
 * Acá se usa la Audio Web API para reproducir la canción y también usar datos
 * obtenidos mediante el buffer.
 */
/* --------------------------------------- Módulos ------------------------------------------- */
const worker = new Worker('../../assets/js/timeWorker.js');
const { dialog } = require('electron').remote;
const path = require('path');
require('./commons');

/* --------------------------------------- Variables ------------------------------------------- */
let isMovingForward = false; // Si se está tratando de adelantar la cación a un tiempo determinado
let isNextAble = true;
let isSongPlaying = false; // Se ejecutó play sobre el AudioNode
let position = null; // Posición de la canción actual
let tmpPosition = []; // Posición de la canción anteriormente reproducida
let filePath = ''; // Ruta de la canción
let songs = {}; // Listado de canciones
let infoSong = {};

// Variables necesarias para trabajar sobre el AudioContext
const audioContext = new window.AudioContext(); // Objeto AudioContext
const xhtr = new XMLHttpRequest(); // Objeto XMLHttpRequest

// Frecuencias
const hrz = [
 40, 80, 90, 100, 120, 150, 200,
 300, 400, 500, 600, 800, 1000,
 1600, 2000, 3000, 4000, 5000, 6000,
 7000, 8000, 10000, 16000
];

let filter = []; // Array con el filtro a usar en distintas frecuencias
let _duration = 0; // Duración máxima de la canción
let _buffer = {}; // Buffer devuelto por decodeAudioData
let source = null; // Objeto AudioNode

// Variables para generar el calculo del tiempo transcurrido
// let millisecond = 1;
let interval = null; // Función interval para crear el tiempo de reproducción
let _minute = 0; // Final
let _second = 0; // Final
let forward = 0; // tiempo estimado dónde debería de seguir corriendo la canción al adelantarla
let lapse = 0;
let time = 0; // Tiempo total final
let minute = 0;
let second = 0;
let millisecond = 0;
let percent = 0;
let time_m = 0;

// Notificación
let notification = null; // Despliega una notificación de la canción que se va a reproducir
let notfi = {
  lang: 'US',
  tag: 'song',
  silent: false,
  icon: path.join(__dirname, '..', 'img', 'play.png')
};

// Para generar la animación del botón play
const anim = {
  from: [
    'M 5.827315,6.7672041 62.280287,48.845328 62.257126,128.62684 5.8743095,170.58995 Z',
    'm 61.189203,48.025 56.296987,40.520916 0,0.0028 -56.261916,40.850634 z'
  ],
  to: [
    'M 5.827315,6.7672041 39.949651,6.9753863 39.92649,170.36386 5.8743095,170.58995 Z',
    'm 83.814203,6.9000001 34.109487,0.037583 -0.0839,163.399307 -33.899661,0.16304 z'
  ]
};

/** --------------------------------------- Funciones --------------------------------------- **/
// Recibe el listado de canciones desde el archivo listSongs.js
function setSongs(_songs) {
  songs = _songs;
  filters();
}

// Retorna un número aleatorio entre 0 y el total de canciones
function shuffle() { return Math.floor(Math.random() * songs.length); }

function animPlay() {
  $('.anim-play').each((v, i) => {
    v.attr({ 'from': anim.from[i], 'to': anim.to[i] }).get().beginElement();
  })
}

function animPause() {
  $('.anim-play').each((v, i) => {
    v.attr({ 'from': anim.to[i], 'to': anim.from[i] }).get().beginElement();
  });
}

// Reproduce una cancion o la pausará
// Responde a todos los posibles estados de una canción
function playSong() {
  if (!isSongPlaying && audioContext.state === 'running') { // Primera vez
    if (position === null) dataSong(shuffle());
    play();
    animPlay();

    return 'resume';
  } else if (isSongPlaying && audioContext.state === 'running') { // Ya reproduciendo
    audioContext.suspend().then(() => {
      isSongPlaying = false;
      cancelAnimationFrame(interval);
    });
    animPause();

    return 'paused';
  } else if (!isSongPlaying && audioContext.state === 'suspended') { // Pausado
    isSongPlaying = true;
    startTimer();
    audioContext.resume();
    animPlay();

    return 'resume';
  }
}

// Genera el tiempo que lleva reproduciendose la canción
function startTimer() {
  worker.addEventListener('message', e => {
    $('#time-start').text(e.data.time);
    $('#progress-bar').css(e.data.w);
  });

  const iter = () => {
    worker.postMessage({ action:'start', per: lapse });
    interval = requestAnimationFrame(iter);
  }
  interval = requestAnimationFrame(iter);

}

// Límpia el tiempo transcurrido
function stopTimer() {
  if (!isMovingForward) {
    isSongPlaying = false;

    cancelAnimationFrame(interval);
    worker.postMessage({ action: 'stop' });

    _duration = _minute = _second = time =
    minute = second = millisecond = percent = 0;

    if (isNextAble && !isMovingForward) nextSong();
  } else if (isMovingForward) {
    /**
     * La función stop tarda unos milesegundos más que ejecutar la función moveForward.
     * Por lo tanto lo que continua después de detener la canción deberá ser ejecutado
     * dentro de la función onended
     */
    source = audioContext.createBufferSource(); // Se debe crear un nuevo AudioNode, ya que al dar stop el nodo se elimina
    source.onended = stopTimer; // Evento que se gatilla al terminar la canción

    // Conectar todos los nodos
    source.buffer = _buffer;
    source.connect(filter[0])
    filter.reduce((p, c) => p.connect(c))
    .connect(audioContext.destination);

    startTimer();
    source.start(0, forward);
    isMovingForward = false;
    isSongPlaying = true;
  }
}

// Recibe la posición de la canción a buscar en el objeto song
// para desplegarlos en la interfaz
function dataSong(_position) {
  $('#time-start').text('00:00');
  $('#progress-bar').css('width:0;');

  infoSong = songs[(position = parseInt(_position, 10))];
  filePath = infoSong.filename; // Ruta donde se encuentra el archivo a reproducir

  $('#song-title').data({position}).child().each(v => { v.text(infoSong.title); });
  $('#artist').child().each(v => { v.text(infoSong.artist); });
  $('#album').child().each(v => { v.text(infoSong.album); });

  // Mostrar notificación
  if (notification !== null) {
    notification.close();
    notification = null
  }

  notfi.body = `${infoSong.artist.replace(/\ /g, ' ')} from ${infoSong.album.replace(/\ /g, ' ')}`;
  notification = new Notification(infoSong.title.replace(/\ /g, ' '), notfi);
}

// EJecuta, por medio de la Audio Web API, la canción.
// Se obtiene un array buffer con info útil para usar
function play() {
  // Creamos un Buffer que contendrá la canción
  source = audioContext.createBufferSource();

  // Leer erl achivo de audio
  xhtr.open('GET', `file://${filePath}`, true);
  xhtr.responseType = 'arraybuffer';
  xhtr.onload = () => {
    audioContext.decodeAudioData(xhtr.response).then(buffer => {
      // Creamos un Buffer que contendrá la canción
      source = audioContext.createBufferSource();

      // Para ser usado al momento de querer adelantar la canción
      // El buffer nos entrega la duración de la canción.
      // La duración de la cación está en segundos, por ende hay que pasarla a minutos.
      _buffer = buffer;
      time = ((_duration = _buffer.duration) / 60).toString();
      _minute = parseInt(time.slice(0, time.lastIndexOf('.')), 10);
      _second = Math.floor(time.slice(time.lastIndexOf('.')) * 60);
      lapse = 100 / _duration; // Porcentaje a usar por cada segundo en la barra de progreso
      $('#time-end').text(`${_minute > 9 ? `${_minute}` : `0${_minute}`}${_second > 9 ? `:${_second}` : `:0${_second}`}`);

      // Evento que se gatilla al terminar la canción
      source.onended = stopTimer;

      // Conectar todos los nodos
      source.buffer = _buffer;
      source.connect(filter[0]);
      filter.reduce((p, c) => p.connect(c))
      .connect(audioContext.destination);

      // Inicializar el tiempo y la canción
      startTimer();
      source.start(0);
      isNextAble = isSongPlaying = true;
    }, reason => {
      dialog.showErrorBox('Error [002]', `${jread(LANG_FILE)[jread(CONFIG_FILE).lang].alerts.playSong}\n${reason}`);
      return;
    });
  };
  xhtr.send(null);
}

// Reproduce la siguiente canción.
// Esta función se comparte cuando se genera la lista de canciones,
// ya que al dar click sobre una canción, la que se reproduce es otra ("siguiente").
function nextSong(_position = -1) {
  if (isNextAble) {
    if (!isSongPlaying && audioContext.state === 'suspended') audioContext.resume();
    tmpPosition.push(position);

    isNextAble = isSongPlaying = false;
    if(source !== null) {
      source.stop(0);
      source = null;
    }

    dataSong(_position !== -1 ? _position : (
      jread(CONFIG_FILE).shuffle ? shuffle() : (songs.length - 1 > position ? position + 1 : 0)
    ));

    playSong();
  }
}

// Reproducirá la canción anterior
function prevSong() {
  if (isNextAble && (tmpPosition.length > 0 || tmpPosition[0] !== null)) {
    dataSong(tmpPosition.pop());

    // Verificar si el contexto está pausado o no.
    // Si está pausado no se reproducirá una nueva pista
    if (!isSongPlaying && audioContext.state === 'suspended') audioContext.resume();

    isNextAble = isSongPlaying = false;
    if(source !== null) {
      source.stop(0);
      source = null;
    }

    playSong();
  }
}

// Cambia los valores en la frecuencia específica
function setFilterVal(a, b) {
  filter[a].gain.setValueAtTime(b, audioContext.currentTime);
}

// Crea y asigna BiquadFilter de tipo peaking para las siguientes frequencias
// [50, 100, 156, 220, 331, 440, 622, 880, 1250, 1750, 2500, 3500, 5000, 10000, 20000]
function filters() {
  let f = null;
  let db = jread(CONFIG_FILE).equalizer.map(v =>
    v !== 0 ? parseFloat((v < 130 ? 121 - v : - (v - 140)) / 10) : 0
  );

  filter = hrz.map((v, i) =>
      (f = audioContext.createBiquadFilter(),
      f.type = 'peaking',
      f.frequency.value = v,
      f.Q.value = 1,
      f.gain.value = db[i], f)
  );
}

function moveForward(event, element) {
  forward = _duration * event.offsetX / element.clientWidth;
  time_m = (forward / 60).toString();

  // Recalcular el tiempo
  minute = parseInt(time_m.slice(0, time_m.lastIndexOf('.')), 10);
  second = Math.floor(time_m.slice(time_m.lastIndexOf('.')) * 60);
  millisecond = forward * 100 + 1;
  cancelAnimationFrame(interval);

  // Recalcular el porcentaje de la barra de tiempo
  percent = forward * (100 / _duration);
  isMovingForward = true;
  worker.postMessage({
    action: 'forward',
    d: [minute, second, millisecond, percent]
  });
  source.stop(0);
}

module.exports = Object.freeze({
  setSongs,
  playSong,
  prevSong,
  nextSong,
  setFilterVal,
  moveForward
});<|endoftext|>


<commit_before>var gulp = require('gulp');
var paths = require('../paths');
var browserSync = require('browser-sync');

function changed(event) {
  console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
}

gulp.task('watch', ['build-dev'], function () {
  gulp.watch([ paths.source ], [ 'es6', browserSync.reload ]).on('change', changed);
  gulp.watch([ paths.html ], [ 'html', browserSync.reload ]).on('change', changed);
  gulp.watch([ paths.less ], [ 'less-dev' ]).on('change', changed);
});
<commit_msg>Update watch.js<commit_after>var gulp = require('gulp');
var paths = require('../paths');
var browserSync = require('browser-sync');

function changed(event) {
  console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
}

gulp.task('watch', ['build'], function () {
  gulp.watch([ paths.source ], [ 'es6', browserSync.reload ]).on('change', changed);
  gulp.watch([ paths.html ], [ 'html', browserSync.reload ]).on('change', changed);
  gulp.watch([ paths.less ], [ 'less-dev' ]).on('change', changed);
});
<|endoftext|>


<commit_before>import axios from 'axios'
import { $, getAttr } from 'luett'
import delegate from 'delegate'

const classLoading = 'calendar-loading'

function load (url, selector) {
  return axios.get(url, { responseType: 'document' })
    .then(res => Promise.resolve($(selector, res.data)))
}

function Calendar (el, opts) {
  const calendarId = el.id
  const reinit = opts.conf.init

  function replaceWith (sourceUrl) {
    el.classList.add(classLoading)

    load(sourceUrl, `#${calendarId}`)
      .then(newCalendar => {
        el.innerHTML = newCalendar.innerHTML
        el.classList.remove(classLoading)
        reinit(el)
      })
  }

  const onNavigate = delegate(el, '.calendar-month a', 'click', event => {
    event.preventDefault()
    replaceWith(getAttr(event.delegateTarget, 'href'))
  })

  return {
    remove () {
      onNavigate.destroy()
    }
  }
}

export default (el, opts) => Calendar(el, opts)
<commit_msg>use new luett apis in calendar<commit_after>import axios from 'axios'
import { $, attr, className } from 'luett'
import delegate from 'delegate'

const classLoading = className('calendar-loading')
const href = attr('href')

function load (url, selector) {
  return axios.get(url, { responseType: 'document' })
    .then(res => Promise.resolve($(selector, res.data)))
}

function Calendar (el, opts) {
  const calendarId = el.id
  const reinit = opts.conf.init

  function replaceWith (sourceUrl) {
    classLoading.add(el)

    load(sourceUrl, `#${calendarId}`)
      .then(newCalendar => {
        el.innerHTML = newCalendar.innerHTML
        classLoading.remove(el)
        reinit(el)
      })
  }

  const onNavigate = delegate(el, '.calendar-month a', 'click', event => {
    event.preventDefault()
    replaceWith(href.get(event.delegateTarget))
  })

  return {
    remove () {
      onNavigate.destroy()
    }
  }
}

export default (el, opts) => Calendar(el, opts)
<|endoftext|>


<commit_before>var child_process = require('child_process'),
    request = require('request'),
    AWS = require('aws-sdk'),
    moment = require('moment'),
    Q = require('q');

require('toml-require').install();

var aaa_cmd_path = './main';

var slack_incoming_webhook_url;
var kms = new AWS.KMS();
var lambda = new AWS.Lambda();

/*
 * Configuration
 */
var config = require('./aaa_lambda.toml');

exports.handler = function(event, context) {
  var encrypted_buf = new Buffer(
      config.schedular.encrypted_slack_incoming_webhook_url, 'base64');
  var cipher_text = {CiphertextBlob: encrypted_buf};

  kms.decrypt(cipher_text, function (err, data) {
    if (err) {
      console.log('decrypt error: ' + err);
      context.fail(err);
    } else {
      slack_incoming_webhook_url = data.Plaintext.toString('ascii');
      process_event(event, context);
    }
  });
};

var process_event = function(event, context) {
  var candidates = [];

  do_aaa_ls()
  .then(function(json_buf) {
    console.log('aaa ls ok: ' + json_buf);
    var domains = JSON.parse(json_buf);

    domains.forEach(function(domain) {
      var after_1m = moment().add(config.schedular.cert_renewal_days_before, "days");

      var cert_not_after = moment(domain.certificate.not_after);
      var authz_expires = moment(domain.authorization.expires);

      var candidate = {
        'email': domain.email,
        'domains': [domain.domain],
        'renewal': true,
        'event': {
          'response_url': slack_incoming_webhook_url,
        }
      };

      domain.certificate.san.forEach(function(domain) {
        if (!is_domain_in(domain, candidate.domains)) {
          candidate.domains.push(domain);
        }
      });

      if (moment().isAfter(authz_expires)) {
        candidate.command = 'authz';
      } else if (after_1m.isAfter(cert_not_after)) {
        candidate.command = 'cert';
      } else {
        return;
      }

      if (candidate.command) {
        candidates.push(candidate);
      }
    });

    console.log('slack incoming webhook url: ' + slack_incoming_webhook_url);
    console.log('candidates: ' + JSON.stringify(candidates));

    // firing renewal event for each certificate
    var promises = [];
    candidates.forEach(function(candidate) {
      var deferred = Q.defer();
      console.log('invoking: ' + JSON.stringify(candidate));
      lambda.invoke({
        'FunctionName': config.lambda.executor_function_name,
        'InvocationType': 'Event',
        'Payload': JSON.stringify(candidate),
      }, function(error) {
        if (error) {
          console.log('failed to invoke executore: ' + error);
          deferred.reject(error);
        } else {
          deferred.resolve();
        }
      });
      promises.push(deferred.promise);
    });

    return Q.all(promises);
  })
  .then(function() {
    var text;
    if (candidates.length === 0) {
      text = 'checking certificates renewal but no certificates found to be renewal';
    } else {
      text = "renewal processes have been invoked\n" +
        "```\n" +
        JSON.stringify(candidates, null, 2) +
        "\n```";
    }

    post_slack(text)
    .then(function() {
      context.succeed();
    });
  })
  .fail(function(buf) {
    console.log('failed to invoke lambda functions');
    post_slack("```\n" + buf + "\n```")
    context.fail(buf);
  })
  .done();
};

var do_aaa_ls = function() {
  var deferred = Q.defer();

  var cmd = [
    aaa_cmd_path, 'ls',
    '--s3-bucket', config.executor.s3_bucket,
    '--s3-kms-key', config.executor.kms_key,
  ];

  var json_buf = '';
  var log_buf = '';
  var child = child_process.exec(cmd.join(' '));

  child.stdout.on('data', function(data) {
    console.log(data);
    json_buf += data;
  });
  child.stderr.on('data', function(data) {
    console.log(data);
    log_buf += data;
  });

  child.on('exit', function(code) {
    if (code !== 0) {
      console.log('aaa exited with non-zero code: '+code);
      deferred.reject(log_buf);
    } else {
      console.log('aaa exited successuflly');
      deferred.resolve(json_buf);
    }
  });

  return deferred.promise;
};

var post_slack = function(text) {
  var deferred = Q.defer();

  request.post(slack_incoming_webhook_url, {
    'json': true,
    'body': {
      'text': text,
    },
    'headers': {
      'Content-Type': 'application/json',
    },
  }, function(error, response) {
    if (response.statusCode !== 200) {
      deferred.reject(response);
    } else {
      deferred.resolve(response);
    }
  });

  return deferred.promise;
};

var is_domain_in = function(domain, domains) {
  for (var i = 0; i < domains.length; i++) {
    if (domain === domains[i]) {
      return true;
    }
  }

  return false;
};
<commit_msg>get rid of nodejs-based schedule<commit_after><|endoftext|>


<commit_before>'use strict';

const Path = require('path');
const Glue = require('glue');
const Labbable = require('labbable');
const Config = require('./config');
const ServerConfig = Config.server.<%= projectName %>;

ServerConfig.uri = (ServerConfig.tls ? 'https://' : 'http://') +
  `${ServerConfig.host}:${ServerConfig.port}`;

const manifest = {
  server: {
    app: Config,
    debug: Config.debug
  },

  connections: [
    {
      host: ServerConfig.host,
      port: ServerConfig.port,
      labels: ['api']
    }
  ],

  registrations: [
    {
      plugin: {
        register: 'hapi-auth-bearer-token'
      }
    },
    {
      plugin: {
        register: 'chairo',
        options: Config.chairo
      }
    },
    {
      plugin: {
        register: 'dogwater',
        options: Config.dogwater
      }
    },
    {
      plugin: {
        register: 'good',
        options: Config.good
      }
    },
    {
      plugin: {
        register: '../lib'
      }
    }
  ]
};

// set cache config as array to allow multiple caches to be used
if (Config.cache) {
  const caches = [];
  Object.keys(Config.cache).forEach((key) => {

    caches.push(Config.cache[key]);
  });

  manifest.server.cache = caches;
}

// export a labbable server for testing purposes
const labbable = module.exports = new Labbable();

const opts = { relativeTo: Path.join(__dirname, 'node_modules') };

Glue.compose(manifest, opts, (err, server) => {

  if (err) {
    throw err;
  }

  const bootMessage = `Server started with success! ${Config.product.name} is listening on ${ServerConfig.uri}. Environment: ${Config.env}`;

  labbable.using(server);

  server.initialize((err) => {

    if (err) {
      throw err;
    }

    server.start((err) => {

      if (err) {
        throw err;
      }

      setTimeout(() => {

        server.log(['info', 'bootstrap'], bootMessage);
      }, 2000);
    });
  });
});
<commit_msg>Enable security headers by default<commit_after>'use strict';

const Path = require('path');
const Glue = require('glue');
const Labbable = require('labbable');
const Config = require('./config');
const ServerConfig = Config.server.<%= projectName %>;

ServerConfig.uri = (ServerConfig.tls ? 'https://' : 'http://') +
  `${ServerConfig.host}:${ServerConfig.port}`;

const manifest = {
  server: {
    app: Config,
    debug: Config.debug
  },

  connections: [
    {
      host: ServerConfig.host,
      port: ServerConfig.port,
      labels: ['api'],
      routes: {
        security: true
      }
    }
  ],

  registrations: [
    {
      plugin: {
        register: 'hapi-auth-bearer-token'
      }
    },
    {
      plugin: {
        register: 'chairo',
        options: Config.chairo
      }
    },
    {
      plugin: {
        register: 'dogwater',
        options: Config.dogwater
      }
    },
    {
      plugin: {
        register: 'good',
        options: Config.good
      }
    },
    {
      plugin: {
        register: '../lib'
      }
    }
  ]
};

// set cache config as array to allow multiple caches to be used
if (Config.cache) {
  const caches = [];
  Object.keys(Config.cache).forEach((key) => {

    caches.push(Config.cache[key]);
  });

  manifest.server.cache = caches;
}

// export a labbable server for testing purposes
const labbable = module.exports = new Labbable();

const opts = { relativeTo: Path.join(__dirname, 'node_modules') };

Glue.compose(manifest, opts, (err, server) => {

  if (err) {
    throw err;
  }

  const bootMessage = `Server started with success! ${Config.product.name} is listening on ${ServerConfig.uri}. Environment: ${Config.env}`;

  labbable.using(server);

  server.initialize((err) => {

    if (err) {
      throw err;
    }

    server.start((err) => {

      if (err) {
        throw err;
      }

      setTimeout(() => {

        server.log(['info', 'bootstrap'], bootMessage);
      }, 2000);
    });
  });
});
<|endoftext|>