<commit_before>// @flow
import React from 'react'
import {Platform, StyleSheet, View} from 'react-native'
import * as c from '../colors'
import {Touchable} from '../touchable'
import {DisclosureArrow} from './disclosure-arrow'
import noop from 'lodash/noop'
import isNil from 'lodash/isNil'

const styles = StyleSheet.create({
  childWrapper: {
    flex: 1,
  },
  container: {
    flexDirection: 'row',
    paddingLeft: 15,
    backgroundColor: c.white,
    ...Platform.select({
      ios: {
        paddingVertical: 8,
        paddingRight: 8,
      },
      android: {
        paddingVertical: 16,
        paddingRight: 15,
      },
    }),
  },
  fullWidth: {
    paddingLeft: 0,
  },
  fullHeight: {
    paddingVertical: 0,
  },
})

type PropsType = {
  style?: any,
  contentContainerStyle?: any,
  arrowPosition?: 'center' | 'top' | 'none',
  fullWidth?: boolean,
  fullHeight?: boolean,
  spacing?: {left?: number, right?: number},
  onPress?: () => any,
  children?: any,
}
export function ListRow(props: PropsType) {
  const {
    style,
    contentContainerStyle,
    children,
    onPress,
    spacing: {left: leftSpacing = 15, right: rightSpacing = null} = {},
    fullWidth = false,
    fullHeight = false,
  } = props

  const Component = onPress ? Touchable : View
  const callback = onPress || noop

  const arrowPosition = props.arrowPosition || (onPress ? 'center' : 'none')
  const arrowPositionStyle = {
    alignSelf: arrowPosition === 'center' ? 'center' : 'flex-start',
  }
  const arrow = arrowPosition === 'none' || Platform.OS === 'android'
    ? null
    : <DisclosureArrow style={arrowPositionStyle} />

  return (
    <Component
      style={[
        styles.container,
        !isNil(leftSpacing) && {paddingLeft: leftSpacing},
        !isNil(rightSpacing) && {paddingRight: rightSpacing},
        fullWidth && styles.fullWidth,
        fullHeight && styles.fullHeight,
        contentContainerStyle,
      ]}
      onPress={callback}
    >
      <View style={[styles.childWrapper, style]}>
        {children}
      </View>
      {arrow}
    </Component>
  )
}
<commit_msg>whitelist ListRow's props<commit_after>// @flow
import React from 'react'
import {Platform, StyleSheet, View} from 'react-native'
import * as c from '../colors'
import {Touchable} from '../touchable'
import {DisclosureArrow} from './disclosure-arrow'
import noop from 'lodash/noop'
import isNil from 'lodash/isNil'

const styles = StyleSheet.create({
  childWrapper: {
    flex: 1,
  },
  container: {
    flexDirection: 'row',
    paddingLeft: 15,
    backgroundColor: c.white,
    ...Platform.select({
      ios: {
        paddingVertical: 8,
        paddingRight: 8,
      },
      android: {
        paddingVertical: 16,
        paddingRight: 15,
      },
    }),
  },
  fullWidth: {
    paddingLeft: 0,
  },
  fullHeight: {
    paddingVertical: 0,
  },
})

type PropsType = {|
  style?: any,
  contentContainerStyle?: any,
  arrowPosition?: 'center' | 'top' | 'none',
  fullWidth?: boolean,
  fullHeight?: boolean,
  spacing?: {left?: number, right?: number},
  onPress?: () => any,
  children?: any,
|}
export function ListRow(props: PropsType) {
  const {
    style,
    contentContainerStyle,
    children,
    onPress,
    spacing: {left: leftSpacing = 15, right: rightSpacing = null} = {},
    fullWidth = false,
    fullHeight = false,
  } = props

  const Component = onPress ? Touchable : View
  const callback = onPress || noop

  const arrowPosition = props.arrowPosition || (onPress ? 'center' : 'none')
  const arrowPositionStyle = {
    alignSelf: arrowPosition === 'center' ? 'center' : 'flex-start',
  }
  const arrow = arrowPosition === 'none' || Platform.OS === 'android'
    ? null
    : <DisclosureArrow style={arrowPositionStyle} />

  return (
    <Component
      style={[
        styles.container,
        !isNil(leftSpacing) && {paddingLeft: leftSpacing},
        !isNil(rightSpacing) && {paddingRight: rightSpacing},
        fullWidth && styles.fullWidth,
        fullHeight && styles.fullHeight,
        contentContainerStyle,
      ]}
      onPress={callback}
    >
      <View style={[styles.childWrapper, style]}>
        {children}
      </View>
      {arrow}
    </Component>
  )
}
<|endoftext|>


<commit_before>var debug = require('debug')('webtorrent:append-to')
var dezalgo = require('dezalgo')
var MediaSourceStream = require('./media-stream')
var path = require('path')
var videostream = require('videostream')

var VIDEOSTREAM_EXTS = [ '.mp4', '.m4v', '.m4a' ]

var MEDIASOURCE_VIDEO_EXTS = [ '.mp4', '.m4v', '.webm' ]
var MEDIASOURCE_AUDIO_EXTS = [ '.m4a', '.mp3' ]
var MEDIASOURCE_EXTS = MEDIASOURCE_VIDEO_EXTS.concat(MEDIASOURCE_AUDIO_EXTS)

var AUDIO_EXTS = [ '.wav', '.aac', '.ogg', '.oga' ]
var IMAGE_EXTS = [ '.jpg', '.png', '.gif', '.bmp' ]
var IFRAME_EXTS = [ '.css', '.html', '.js', '.md', '.pdf', '.txt' ]

var MediaSource = typeof window !== 'undefined' && window.MediaSource

module.exports = function appendTo (file, rootElem, cb) {
  cb = dezalgo(cb || function () {})
  var elem
  var extname = path.extname(file.name).toLowerCase()

  if (rootElem && (rootElem.nodeName === 'VIDEO' || rootElem.nodeName === 'AUDIO')) {
    throw new Error(
      'Invalid video/audio node argument. Argument must be root element that ' +
      'video/audio tag will be appended to.'
    )
  }

  if (MEDIASOURCE_EXTS.indexOf(extname) >= 0) appendToMediaSource()
  else if (AUDIO_EXTS.indexOf(extname) >= 0) appendToAudio()
  else if (IMAGE_EXTS.indexOf(extname) >= 0) appendToImage()
  else if (IFRAME_EXTS.indexOf(extname) >= 0) appendToIframe()
  else cb(new Error('Unsupported file type "' + extname + '": Cannot append to DOM'))

  // TODO: re-use the same video tag
  function appendToMediaSource () {
    if (!MediaSource) {
      return cb(new Error(
        'Video/audio streaming is not supported in your browser. You can still share ' +
        'or download ' + file.name + ' (once it\'s fully downloaded). Use Chrome for ' +
        'MediaSource support.'
      ))
    }

    var tagName = MEDIASOURCE_VIDEO_EXTS.indexOf(extname) >= 0 ? 'video' : 'audio'

    if (VIDEOSTREAM_EXTS.indexOf(extname) >= 0) useVideostream()
    else useMediaSource()

    function useVideostream () {
      debug('Use `videostream` package for ' + file.name)
      createElem()
      elem.addEventListener('error', fallbackToMediaSource)
      elem.addEventListener('playing', onPlaying)
      videostream(file, elem)
      appendElem()
    }

    function useMediaSource () {
      debug('Use MediaSource API for ' + file.name)
      createElem()
      elem.addEventListener('error', fallbackToBlobURL)
      elem.addEventListener('playing', onPlaying)
      file.createReadStream().pipe(new MediaSourceStream(elem, { extname: extname }))
      appendElem()
    }

    function useBlobURL () {
      debug('Use Blob URL for ' + file.name)
      createElem()
      elem.addEventListener('error', fatalError)
      elem.addEventListener('playing', onPlaying)
      appendElem()
      file.getBlobURL(function (err, url) {
        if (err) return fatalError(err)
        elem.src = url
      })
    }

    function fallbackToMediaSource (err) {
      debug('videostream error: fallback to MediaSource API: %o', err.message || err)
      elem.removeEventListener('error', fallbackToMediaSource)
      elem.removeEventListener('playing', onPlaying)

      useMediaSource()
    }

    function fallbackToBlobURL (err) {
      debug('MediaSource API error: fallback to Blob URL: %o', err.message || err)
      elem.removeEventListener('error', fallbackToBlobURL)
      elem.removeEventListener('playing', onPlaying)

      useBlobURL()
    }

    function createElem () {
      if (elem) elem.remove()
      elem = window.elem = document.createElement(tagName)
      elem.controls = true
      elem.autoplay = true
    }

    function appendElem () {
      rootElem.appendChild(elem)
      elem.play()
    }
  }

  function onPlaying () {
    elem.removeEventListener('playing', onPlaying)
    cb(null, elem)
  }

  function appendToAudio () {
    elem = document.createElement('audio')
    elem.controls = true
    elem.autoplay = true
    rootElem.appendChild(elem)
    file.getBlobURL(function (err, url) {
      if (err) return fatalError(err)
      elem.addEventListener('error', fatalError)
      elem.addEventListener('playing', onPlaying)
      elem.src = url
      elem.play()
    })
  }

  function appendToImage () {
    file.getBlobURL(function (err, url) {
      if (err) return fatalError(err)
      elem = document.createElement('img')
      elem.src = url
      elem.alt = file.name
      rootElem.appendChild(elem)
      cb(null)
    })
  }

  function appendToIframe () {
    file.getBlobURL(function (err, url) {
      if (err) return fatalError(err)
      elem = document.createElement('iframe')
      elem.src = url
      if (extname !== '.pdf') elem.sandbox = 'allow-forms allow-scripts'
      rootElem.appendChild(elem)
      cb(null)
    })
  }

  function fatalError (err) {
    if (elem) elem.remove()
    err.message = 'Error appending file "' + file.name + '" to DOM: ' + err.message
    debug(err.message)
    if (cb) cb(err)
  }
}
<commit_msg>append-to: resume from currentTime on fallback<commit_after>var debug = require('debug')('webtorrent:append-to')
var dezalgo = require('dezalgo')
var MediaSourceStream = require('./media-stream')
var path = require('path')
var videostream = require('videostream')

var VIDEOSTREAM_EXTS = [ '.mp4', '.m4v', '.m4a' ]

var MEDIASOURCE_VIDEO_EXTS = [ '.mp4', '.m4v', '.webm' ]
var MEDIASOURCE_AUDIO_EXTS = [ '.m4a', '.mp3' ]
var MEDIASOURCE_EXTS = MEDIASOURCE_VIDEO_EXTS.concat(MEDIASOURCE_AUDIO_EXTS)

var AUDIO_EXTS = [ '.wav', '.aac', '.ogg', '.oga' ]
var IMAGE_EXTS = [ '.jpg', '.png', '.gif', '.bmp' ]
var IFRAME_EXTS = [ '.css', '.html', '.js', '.md', '.pdf', '.txt' ]

var MediaSource = typeof window !== 'undefined' && window.MediaSource

module.exports = function appendTo (file, rootElem, cb) {
  cb = dezalgo(cb || function () {})
  var elem
  var extname = path.extname(file.name).toLowerCase()
  var currentTime = 0

  if (rootElem && (rootElem.nodeName === 'VIDEO' || rootElem.nodeName === 'AUDIO')) {
    throw new Error(
      'Invalid video/audio node argument. Argument must be root element that ' +
      'video/audio tag will be appended to.'
    )
  }

  if (MEDIASOURCE_EXTS.indexOf(extname) >= 0) appendToMediaSource()
  else if (AUDIO_EXTS.indexOf(extname) >= 0) appendToAudio()
  else if (IMAGE_EXTS.indexOf(extname) >= 0) appendToImage()
  else if (IFRAME_EXTS.indexOf(extname) >= 0) appendToIframe()
  else cb(new Error('Unsupported file type "' + extname + '": Cannot append to DOM'))

  function appendToMediaSource () {
    if (!MediaSource) {
      return cb(new Error(
        'Video/audio streaming is not supported in your browser. You can still share ' +
        'or download ' + file.name + ' (once it\'s fully downloaded). Use Chrome for ' +
        'MediaSource support.'
      ))
    }

    var tagName = MEDIASOURCE_VIDEO_EXTS.indexOf(extname) >= 0 ? 'video' : 'audio'

    if (VIDEOSTREAM_EXTS.indexOf(extname) >= 0) useVideostream()
    else useMediaSource()

    function useVideostream () {
      debug('Use `videostream` package for ' + file.name)
      createElem()
      elem.addEventListener('error', fallbackToMediaSource)
      elem.addEventListener('playing', onPlaying)
      videostream(file, elem)
    }

    function useMediaSource () {
      debug('Use MediaSource API for ' + file.name)
      createElem()
      elem.addEventListener('error', fallbackToBlobURL)
      elem.addEventListener('playing', onPlaying)

      file.createReadStream().pipe(new MediaSourceStream(elem, { extname: extname }))
      if (currentTime) elem.currentTime = currentTime
    }

    function useBlobURL () {
      debug('Use Blob URL for ' + file.name)
      createElem()
      elem.addEventListener('error', fatalError)
      elem.addEventListener('playing', onPlaying)
      file.getBlobURL(function (err, url) {
        if (err) return fatalError(err)
        elem.src = url
        if (currentTime) elem.currentTime = currentTime
      })
    }

    function fallbackToMediaSource (err) {
      debug('videostream error: fallback to MediaSource API: %o', err.message || err)
      elem.removeEventListener('error', fallbackToMediaSource)
      elem.removeEventListener('playing', onPlaying)

      useMediaSource()
    }

    function fallbackToBlobURL (err) {
      debug('MediaSource API error: fallback to Blob URL: %o', err.message || err)
      elem.removeEventListener('error', fallbackToBlobURL)
      elem.removeEventListener('playing', onPlaying)

      useBlobURL()
    }

    function createElem (time) {
      if (!elem) {
        elem = document.createElement(tagName)
        elem.controls = true
        elem.autoplay = true // for chrome
        elem.play() // for firefox

        elem.addEventListener('progress', function () {
          currentTime = elem.currentTime
        })

        rootElem.appendChild(elem)
      }
    }
  }

  function onPlaying () {
    elem.removeEventListener('playing', onPlaying)
    cb(null, elem)
  }

  function appendToAudio () {
    elem = document.createElement('audio')
    elem.controls = true
    elem.autoplay = true
    rootElem.appendChild(elem)
    file.getBlobURL(function (err, url) {
      if (err) return fatalError(err)
      elem.addEventListener('error', fatalError)
      elem.addEventListener('playing', onPlaying)
      elem.src = url
      elem.play()
    })
  }

  function appendToImage () {
    file.getBlobURL(function (err, url) {
      if (err) return fatalError(err)
      elem = document.createElement('img')
      elem.src = url
      elem.alt = file.name
      rootElem.appendChild(elem)
      cb(null)
    })
  }

  function appendToIframe () {
    file.getBlobURL(function (err, url) {
      if (err) return fatalError(err)
      elem = document.createElement('iframe')
      elem.src = url
      if (extname !== '.pdf') elem.sandbox = 'allow-forms allow-scripts'
      rootElem.appendChild(elem)
      cb(null)
    })
  }

  function fatalError (err) {
    if (elem) elem.remove()
    err.message = 'Error appending file "' + file.name + '" to DOM: ' + err.message
    debug(err.message)
    if (cb) cb(err)
  }
}
<|endoftext|>


<commit_before>/*
 * FRAME module/Object that consists of Core methods
 * @author Zafar Saleem
 */
var FRAME = {
    /**
     * Main object that consists details for all modules upon registering.
     * Modules are stored as
     * modulesData: {
     * 		modules: {
     *			nameofmodule: object
     * 		}
     * }
     */
    modulesData: {},

    /**
     * Register method registers modules and stores them in
     * modulesData object.
     * @param {string} name of module
     * @param {object} object of module
     * @return {}
     */
    register: function (module, obj) {
        if (this._isString(module) && this._isObject(obj)) {
            if (!this._isMethod(obj.init) && !this._isMethod(obj.destroy)) {
                this._log(3, 'Module does not have init and/or destroy methods');
                return;
            }
            // If modules object inside modulesData does not exist then create one
            if (typeof this.modulesData.modules === 'undefined') this.modulesData['modules'] = {};
            this.modulesData.modules[module] = obj; // Store module's object inside modulesData.modules

            if (typeof this.modulesData.$container === 'undefined') this.modulesData['$container'] = {};
            this.modulesData['$container'] = $('#' + module);

            if (obj.events && typeof obj.events === 'object') {
                FRAME.AggregatedEvents.init(obj);
            }
        } else {
            this._log(3, 'Module name should be String & obj should be Object');
        }
    },

    /**
     * This method initialize single module stored in modulesData: {}
     * @param {object} Module that includes all methods
     * @return {}
     */
    start: function (module) {
        var mod = null;

        if (typeof module === 'string') {
            if (!this.modulesData.modules.hasOwnProperty(module)) return;
            mod = this.modulesData.modules[module];
            mod.init.bind(mod).apply();
            return;
        }
        if (this._isMethod(module)) return;
        module.init.bind(module).apply();
    },

    /**
     * This method initializes all modules stored in modulesData: {}
     * by calling start method
     * @param {object} Module that includes all methods
     * @return {}
     */
    startAll: function () {
        var modules = this.modulesData.modules;

        if (typeof modules === 'undefined') return;
        if (Object.keys(modules).length <= 0) return; // if there are no modules registered then do nothing
        for (var keys in modules) {
            if (!modules.hasOwnProperty(keys)) return;
            this.start(modules[keys]);
        }
    },

    /**
     * Creates new DOM element and apply all attributes such as
     * Class name, id, href etc.
     * @param {object} DOM element that will be created by this method
     * @param {object} List of attributes i.e. id, class, href etc.
     * @return {object} Created DOM element
     */
    createElement: function (el, config) {
        var i, text, child;
        el = this._create(el);
        if (!config) return;
        if (config.children && this._isArray(config.children)) {
            i = 0;
            while(child = config.children[i]) {
                el.appendChild(child);
                i++;
            }
            delete config.children;
        }
        if (config.text) {
            el.appendChild(document.createTextNode(config.text));
            delete config.text;
        }
        this._applyAttrs(el, config);
        return el;
    },

    /**
     * Object that adds events to selectors and call callback functions
     * in a module. This modules is for following events object
     * events: {
     *		'click #selector': 'callback'
     * }
     */
    AggregatedEvents: {
        config: {
            context: null,
            callback: null
        },

        /**
        * constructor function that initialize AggregatedEvents object,
        * it gets callback function from events object and store it in config.callback,
        * then calls applyEvents method.
        * @param {object} context of modules where events object is defined
        */
        init: function (context) {
            var events = context.events, keys;
            if (!FRAME._isObject(events)) return;

            this.config.context = context;
            for (keys in events) {
                if (!events.hasOwnProperty(keys)) return;
                this.config.callback = context[events[keys]];
                if (!FRAME._isMethod(this.config.callback)) return;
                this.applyEvents(keys);
            }
        },

        /**
        * Checks for multiple selectors, if given then seperate them and put them
        * in an array then bind events to all those selectors.
        * If single selector is given then apply event to that selector.
        * @param {string} properties of events object
        * @return {}
        */
        applyEvents: function (keys) {
            var i, len, selectors = this.getSelectors(keys.split(" "));

            if (selectors.length === 1) {
                this.bindEvents(keys.split(" ")[0], keys.split(" ")[1]);
                return;
            }

            for (i = 0, len = selectors.length; i < len; i++) {
                if (selectors[i] === 0) continue;
                this.bindEvents(keys.split(" ")[0], selectors[i]);
            }
    	},

    	/**
    	 * It binds event to selector and call callback function
    	 * @param {evt} event name e.g. click, hover etc that needs to attached to selector
    	 * @param {string} selector on which event needs to be attached.
    	 */
        bindEvents: function (evt, selector) {
            $(document).on(evt, selector, this.config.callback.bind(this.config.context));
        },

        getSelectors: function (arr) {
            return arr.filter(function (item, index) {
                return (index !== 0) || item;
            });
        }
    },

    /**
     * Pub/Sub system that stores events in object and then triggers them when
     * requires.
     */
    Events: {
        eventsData: {}, // all events are stored here

        /**
         * Stores events and context in eventsData object as key value pair
         * @param {String} Event name
         * @param {Object} Context(this) to module from where this events is triggered
         * @return {}
         */
        trigger: function (events, context) {
            if (!events && typeof events !== 'string') return;
            if (this.eventsData.hasOwnProperty(events)) {
                FRAME._log(2, 'This event name is already taken!');
                return;
            }
            this.eventsData[events] = context;
        },

        /**
         * Calls events when required
         * @param {String} Event name
         * @param {Function} Callback function that needs to be called upon
         * @param {Object} Context(this) to module from where listen() function id called
         * @return {}
         */
        listen: function (events, callback, context) {
            if (!events && typeof events !== 'string') return;
            if (this.eventsData.hasOwnProperty(events)) {
                if (typeof callback !== 'function') return;
                callback.bind(this.getContext(this.eventsData[events], context)).apply();
            }
        },

        /**
         * Get context
         * @param {Object} Context(this) to module from where this events is triggered
         * @return {}
         */
        getContext: function (context, module) {
            if (context && typeof context === 'object') return context;
            if (module && typeof module === 'object') return module;
        }
    },

    /**
     * Creates DOM element.
     * @private
     * @param {object} DOM element that will be created by this method
     * @return {object} Created DOM element
     */
    _create: function (el) {
        return document.createElement(el);
    },

    /**
     * Apply attributes i.e. id, class, href etc to create element.
     * @private
     * @param {object} DOM element to which attributes will be applied
     * @param {object} attributes object that has id, class etc.
     * @return {}
     */
    _applyAttrs: function (el, attrs) {
        $(el).attr(attrs);
    },

    /**
     * Check for array.
     * @private
     * @param {array} Array to check if it is array.
     * @return {boolean} true/false
     */
    _isArray: function (arr) {
        return (Array.isArray(arr));
    },

    /**
     * Check if paramter is a function
     * @private
     * @param {Function}
     * @return {Boolean} true/false
     */
    _isMethod: function (method) {
        return (method && typeof method === 'function');
    },

    /**
     * Check if paramter is an Object
     * @private
     * @param {Object}
     * @return {Boolean} true/false
     */
    _isObject: function (object) {
        return (object && typeof object === 'object');
    },

    /**
     * Check if paramter is a String
     * @private
     * @param {String}
     * @return {Boolean} true/false
     */
    _isString: function (str) {
        return (str && typeof str === 'string');
    },

    /**
     * Log function
     * @private
     * @param {Number} 1-3
     * @param {String} Message
     */
    _log: function (severity, message) {
        console[(severity === 1 ? 'log' : (severity === 2) ? 'warn' : 'error')](message);
    }
};
<commit_msg>removed check for destroy method<commit_after>/*
 * FRAME module/Object that consists of Core methods
 * @author Zafar Saleem
 */
var FRAME = {
    /**
     * Main object that consists details for all modules upon registering.
     * Modules are stored as
     * modulesData: {
     * 		modules: {
     *			nameofmodule: object
     * 		}
     * }
     */
    modulesData: {},

    /**
     * Register method registers modules and stores them in
     * modulesData object.
     * @param {string} name of module
     * @param {object} object of module
     * @return {}
     */
    register: function (module, obj) {
        if (this._isString(module) && this._isObject(obj)) {
            if (!this._isMethod(obj.init)) {
                this._log(3, 'Module does not have init method');
                return;
            }
            // If modules object inside modulesData does not exist then create one
            if (typeof this.modulesData.modules === 'undefined') this.modulesData['modules'] = {};
            this.modulesData.modules[module] = obj; // Store module's object inside modulesData.modules

            if (typeof this.modulesData.$container === 'undefined') this.modulesData['$container'] = {};
            this.modulesData['$container'] = $('#' + module);

            if (obj.events && typeof obj.events === 'object') {
                FRAME.AggregatedEvents.init(obj);
            }
        } else {
            this._log(3, 'Module name should be String & obj should be Object');
        }
    },

    /**
     * This method initialize single module stored in modulesData: {}
     * @param {object} Module that includes all methods
     * @return {}
     */
    start: function (module) {
        var mod = null;

        if (typeof module === 'string') {
            if (!this.modulesData.modules.hasOwnProperty(module)) return;
            mod = this.modulesData.modules[module];
            mod.init.bind(mod).apply();
            return;
        }
        if (this._isMethod(module)) return;
        module.init.bind(module).apply();
    },

    /**
     * This method initializes all modules stored in modulesData: {}
     * by calling start method
     * @param {object} Module that includes all methods
     * @return {}
     */
    startAll: function () {
        var modules = this.modulesData.modules;

        if (typeof modules === 'undefined') return;
        if (Object.keys(modules).length <= 0) return; // if there are no modules registered then do nothing
        for (var keys in modules) {
            if (!modules.hasOwnProperty(keys)) return;
            this.start(modules[keys]);
        }
    },

    /**
     * Creates new DOM element and apply all attributes such as
     * Class name, id, href etc.
     * @param {object} DOM element that will be created by this method
     * @param {object} List of attributes i.e. id, class, href etc.
     * @return {object} Created DOM element
     */
    createElement: function (el, config) {
        var i, text, child;
        el = this._create(el);
        if (!config) return;
        if (config.children && this._isArray(config.children)) {
            i = 0;
            while(child = config.children[i]) {
                el.appendChild(child);
                i++;
            }
            delete config.children;
        }
        if (config.text) {
            el.appendChild(document.createTextNode(config.text));
            delete config.text;
        }
        this._applyAttrs(el, config);
        return el;
    },

    /**
     * Object that adds events to selectors and call callback functions
     * in a module. This modules is for following events object
     * events: {
     *		'click #selector': 'callback'
     * }
     */
    AggregatedEvents: {
        config: {
            context: null,
            callback: null
        },

        /**
        * constructor function that initialize AggregatedEvents object,
        * it gets callback function from events object and store it in config.callback,
        * then calls applyEvents method.
        * @param {object} context of modules where events object is defined
        */
        init: function (context) {
            var events = context.events, keys;
            if (!FRAME._isObject(events)) return;

            this.config.context = context;
            for (keys in events) {
                if (!events.hasOwnProperty(keys)) return;
                this.config.callback = context[events[keys]];
                if (!FRAME._isMethod(this.config.callback)) return;
                this.applyEvents(keys);
            }
        },

        /**
        * Checks for multiple selectors, if given then seperate them and put them
        * in an array then bind events to all those selectors.
        * If single selector is given then apply event to that selector.
        * @param {string} properties of events object
        * @return {}
        */
        applyEvents: function (keys) {
            var i, len, selectors = this.getSelectors(keys.split(" "));

            if (selectors.length === 1) {
                this.bindEvents(keys.split(" ")[0], keys.split(" ")[1]);
                return;
            }

            for (i = 0, len = selectors.length; i < len; i++) {
                if (selectors[i] === 0) continue;
                this.bindEvents(keys.split(" ")[0], selectors[i]);
            }
    	},

    	/**
    	 * It binds event to selector and call callback function
    	 * @param {evt} event name e.g. click, hover etc that needs to attached to selector
    	 * @param {string} selector on which event needs to be attached.
    	 */
        bindEvents: function (evt, selector) {
            $(document).on(evt, selector, this.config.callback.bind(this.config.context));
        },

        getSelectors: function (arr) {
            return arr.filter(function (item, index) {
                return (index !== 0) || item;
            });
        }
    },

    /**
     * Pub/Sub system that stores events in object and then triggers them when
     * requires.
     */
    Events: {
        eventsData: {}, // all events are stored here

        /**
         * Stores events and context in eventsData object as key value pair
         * @param {String} Event name
         * @param {Object} Context(this) to module from where this events is triggered
         * @return {}
         */
        trigger: function (events, context) {
            if (!events && typeof events !== 'string') return;
            if (this.eventsData.hasOwnProperty(events)) {
                FRAME._log(2, 'This event name is already taken!');
                return;
            }
            this.eventsData[events] = context;
        },

        /**
         * Calls events when required
         * @param {String} Event name
         * @param {Function} Callback function that needs to be called upon
         * @param {Object} Context(this) to module from where listen() function id called
         * @return {}
         */
        listen: function (events, callback, context) {
            if (!events && typeof events !== 'string') return;
            if (this.eventsData.hasOwnProperty(events)) {
                if (typeof callback !== 'function') return;
                callback.bind(this.getContext(this.eventsData[events], context)).apply();
            }
        },

        /**
         * Get context
         * @param {Object} Context(this) to module from where this events is triggered
         * @return {}
         */
        getContext: function (context, module) {
            if (context && typeof context === 'object') return context;
            if (module && typeof module === 'object') return module;
        }
    },

    /**
     * Creates DOM element.
     * @private
     * @param {object} DOM element that will be created by this method
     * @return {object} Created DOM element
     */
    _create: function (el) {
        return document.createElement(el);
    },

    /**
     * Apply attributes i.e. id, class, href etc to create element.
     * @private
     * @param {object} DOM element to which attributes will be applied
     * @param {object} attributes object that has id, class etc.
     * @return {}
     */
    _applyAttrs: function (el, attrs) {
        $(el).attr(attrs);
    },

    /**
     * Check for array.
     * @private
     * @param {array} Array to check if it is array.
     * @return {boolean} true/false
     */
    _isArray: function (arr) {
        return (Array.isArray(arr));
    },

    /**
     * Check if paramter is a function
     * @private
     * @param {Function}
     * @return {Boolean} true/false
     */
    _isMethod: function (method) {
        return (method && typeof method === 'function');
    },

    /**
     * Check if paramter is an Object
     * @private
     * @param {Object}
     * @return {Boolean} true/false
     */
    _isObject: function (object) {
        return (object && typeof object === 'object');
    },

    /**
     * Check if paramter is a String
     * @private
     * @param {String}
     * @return {Boolean} true/false
     */
    _isString: function (str) {
        return (str && typeof str === 'string');
    },

    /**
     * Log function
     * @private
     * @param {Number} 1-3
     * @param {String} Message
     */
    _log: function (severity, message) {
        console[(severity === 1 ? 'log' : (severity === 2) ? 'warn' : 'error')](message);
    }
};
<|endoftext|>


<commit_before>module.exports = function(dbUrl, hostUrl) {
  var nano = require('nano')(dbUrl);
  var db = nano.db.use('todo');
  var _ = require('lodash');

  var todo = {};

  todo.getAll = function(callback) {
    var results = [];
    db.view('todos', 'all_todos', function(err, body) {
      for (var row of body.rows) {
        results.push(row.value);
      }
      callback(err, results);
    });
  };

  todo.addTask = function(task, callback) {
    db.insert(task, function(err, body) {
      if (err) {
        callback(err);
      } else {
        task._id = body.id;
        task._rev  = body.rev;
        // assign a url based on id and commit back to database
        task.url = hostUrl + '/' + body.id;
        db.insert(task, function(err, body) {
          callback(err, task);
        });
      }
    });
  };

  todo.deleteAll = function(callback) {
    db.view('todos', 'all_todos', function(err, body) {
      if (body.rows.length !== 0) {
        body.rows.forEach(function(curr, index, array) {
          db.destroy(curr.value._id, curr.value._rev, function(err, body) {
            if (index === array.length - 1) {
              callback(err, []);
            }
          });
        });
      } else {
        callback(err, []);
      }
    });
  };

  todo.getTask = function(url, callback) {
    db.get(url, function(err, body) {
      callback(err, body);
    });
  };

  todo.updateTask = function(url, updates, callback) {
    this.getTask(url, function(err, task) {
      _.assign(task, updates);
      db.insert(task, function(err , updated) {
        callback(err, task);
      });
    });
  };

  todo.deleteTask = function(url, callback) {
    this.getTask(url, function(err, task) {
      db.destroy(task._id, task._rev, function(err, body) {
        callback(err, []);
      });
    });
  };

  return todo;
};
<commit_msg>Create function to set the database views.<commit_after>module.exports = function(dbUrl, hostUrl) {
  var nano = require('nano')(dbUrl);
  var db = nano.db.use('todo');
  var _ = require('lodash');

  var todo = {};

  todo.getAll = function(callback) {
    var results = [];
    db.view('todos', 'all_todos', function(err, body) {
      for (var row of body.rows) {
        results.push(row.value);
      }
      callback(err, results);
    });
  };

  todo.addTask = function(task, callback) {
    db.insert(task, function(err, body) {
      if (err) {
        callback(err);
      } else {
        task._id = body.id;
        task._rev  = body.rev;
        // assign a url based on id and commit back to database
        task.url = hostUrl + '/' + body.id;
        db.insert(task, function(err, body) {
          callback(err, task);
        });
      }
    });
  };

  todo.deleteAll = function(callback) {
    db.view('todos', 'all_todos', function(err, body) {
      if (body.rows.length !== 0) {
        body.rows.forEach(function(curr, index, array) {
          db.destroy(curr.value._id, curr.value._rev, function(err, body) {
            if (index === array.length - 1) {
              callback(err, []);
            }
          });
        });
      } else {
        callback(err, []);
      }
    });
  };

  todo.getTask = function(url, callback) {
    db.get(url, function(err, body) {
      callback(err, body);
    });
  };

  todo.updateTask = function(url, updates, callback) {
    this.getTask(url, function(err, task) {
      _.assign(task, updates);
      db.insert(task, function(err , updated) {
        callback(err, task);
      });
    });
  };

  todo.deleteTask = function(url, callback) {
    this.getTask(url, function(err, task) {
      db.destroy(task._id, task._rev, function(err, body) {
        callback(err, []);
      });
    });
  };

  todo.setupDB = function() {
    db.insert(
      { "views":
        { "all_todos":
          { "map": function(doc) { if ('title' in doc) { emit(doc._id, doc); }}}
        }
      }, '_design/todos', function (error, response) {
        console.log("adding schema");
        if (error) {
          console.log('error: ' + error);
        } else {
          console.log('schema response ' + response);
        }
      });
  };

  return todo;
};
<|endoftext|>


<commit_before>import React from 'react'
import {
  Box,
  Heading,
  Container,
  Text,
  Flex,
  Button,
  BackgroundImage,
  Link as A
} from '@hackclub/design-system'
import Link from 'gatsby-link'
import Module from 'components/Module'
import Stat from 'components/Stat'
import { stats } from 'data.json'

const Grid = Box.extend`
  display: grid;
  grid-gap: ${props => props.theme.space[2]}px;
  width: 100%;

  > div {
    width: 100%;
    box-shadow: ${props => props.theme.boxShadows[3]};
    border-radius: ${props => props.theme.radius};
    max-width: 100%;
  }

  ${props => props.theme.mediaQueries.md} {
    grid-template-columns: repeat(2, 1fr);
    grid-gap: ${props => props.theme.space[4]}px;
  }
`

const Super = Box.extend`
  background: ${props => props.theme.colors.warning} url(/diagonal.svg) top
    center repeat-x;
  background-size: auto 100%;
  position: relative;

  &:before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: url(/diagonal.svg) top right repeat-x;
    background-size: auto 100%;
    background-position-x: 32%;
  }

  > * {
    position: relative;
  }
`

const Graph = Box.extend`
  background: ${props => props.theme.colors.accent} url(/graph.svg) no-repeat;
  background-size: 100% 100%;
`

const Modules = Box.extend`
  display: grid;
  grid-gap: ${props => props.theme.space[3]}px;
  ${props => props.theme.mediaQueries.md} {
    grid-template-columns: repeat(2, 1fr);
  }
`

const Stats = Box.extend`
  column-count: 2;
  div {
    text-align: left;
  }
`

const like = {
  underline: true,
  color: 'inherit',
  target: '_blank'
}
A.link = A.withComponent(Link)
const Like = props => <A {...like} {...props} />
const LikeLink = props => <A.link {...like} {...props} />

const Photo = BackgroundImage.extend.attrs({ role: 'img' })`
  overflow: hidden;
  transition: all 0.125s ease-out;
  background-size: auto 100%;
  &:hover {
    background-size: auto 108%;
  }
  @media (prefers-reduced-motion: reduce) {
    transition: none;
    background-size: cover !important;
  }
`

const Action = Button.withComponent(Link).extend`
  transition: transform 0.125s ease-out;
  will-change: transform;
  transform: scale(1);
  &:hover,
  &:focus {
    transform: scale(1.06);
  }
  @media (prefers-reduced-motion: reduce) {
    transform: none !important;
  }
`

export default () => (
  <Container w={1} px={[3, 4, null, 2]} mt={[4, 5]}>
    <Box mx={0} mt={5} color="black">
      <Heading.h2 f={[5, 6]} mb={3}>
        Join the largest community of students building the class they wish they
        had.
      </Heading.h2>
      <Container maxWidth={48} mx={0}>
        <Text f={[3, 4]}>
          At Hack Club, every week you and 20 other students come together to
          make. Meetings are like mini-hackathons. People are working on
          projects, you lead workshops introducing new technologies, you and
          your co-leads mentor. Members typically start with no experience.
        </Text>
      </Container>
    </Box>
    <Grid mt={4} mb={[4, 6]} color="white">
      <Box bg="primary" p={[3, 4]}>
        <Heading.h3 f={4} my={0} caps>
          So much more than a club
        </Heading.h3>
        <Text f={3} my={3}>
          Hack Clubs attend and run hackathons like{' '}
          <Like href="https://www.sfchronicle.com/bayarea/article/Hack-the-Fog-makes-history-as-San-12729895.php">
            Hack the Fog
          </Like>. They run summer programs like{' '}
          <Like href="http://thecspn.com/?p=43434">Hack Camp</Like>. They
          compete in competitions like the{' '}
          <Like href="http://www.congressionalappchallenge.us">
            Congressional App Challenge
          </Like>{' '}
          (and <LikeLink to="/challenge">our Challenge</LikeLink>!). This is no
          ordinary club.
        </Text>
        <Action to="/meetings" inverted>
          Learn more »
        </Action>
      </Box>
      <Photo src="/about_1.jpg" scale />
      <Photo src="/about_2.jpg" scale />
      <Box bg="info" p={[3, 4]}>
        <Heading.h3 f={4} my={0} caps>
          The power of a network
        </Heading.h3>
        <Modules my={3} w={1}>
          <Module
            icon="forum"
            heading="Online community"
            body="Talk to thousands of club leaders/members on Slack."
          />
          <Module
            icon="chrome_reader_mode"
            heading="Curriculum"
            body="Dozens of tutorials for your members to make projects."
          />
          <Module
            icon="voice_chat"
            heading="Mentorship"
            body="Talk to our team online for guidance anytime."
          />
          <Module
            icon="wallpaper"
            heading="Marketing"
            body="Get materials and ideas for spreading your club."
          />
        </Modules>
      </Box>
    </Grid>
    <Box mx={0} mt={5} color="black">
      <Heading.h2 f={[5, 6]} mb={3}>
        We believe people learn best when they take control of their own
        education.
      </Heading.h2>
      <Container maxWidth={48} mx={0}>
        <Text f={[3, 4]}>
          Hack Club is heavily inspired by unschooling. At Hack Club, there are
          no teachers or lectures—members work at their own pace on their own
          projects. Your job as a club leader is to facilitate and provide
          guidance through mentoring and the power of community.
        </Text>
      </Container>
    </Box>
    <Grid my={[4, 5]} color="white" pb={3}>
      <Super p={[3, 4]}>
        <Heading.h3 f={4} my={0} caps>
          Coding is a superpower
        </Heading.h3>
        <Text f={3} my={3}>
          Learning to code is uniquely empowering: you go from a consumer to a
          creator. The goal of Hack Club is to help you become that creator. We
          want a space for hacking at every high school, every week.
        </Text>
        <Action to="/philosophy" bg="warning" inverted>
          Our philosophy »
        </Action>
      </Super>
      <Photo src="/about_3.jpg" scale />
      <Photo src="/about_4.jpg" scale />
      <Graph p={[3, 4]}>
        <Heading.h3 f={4} my={0} caps>
          Grassroots and growing
        </Heading.h3>
        <Stats mb={2}>
          <Stat f={7} value={stats.school_count} label="schools" />
          <Stat f={7} value={stats.country_count} label="countries" />
          <Stat f={7} value={stats.state_count} label="states" />
          <Stat f={7} value={stats.approximate_members} label="members" />
        </Stats>
        <Action to="/team" bg="accent" inverted>
          Our team »
        </Action>
      </Graph>
    </Grid>
  </Container>
)
<commit_msg>Promote Hack Chicago<commit_after>import React from 'react'
import {
  Box,
  Heading,
  Container,
  Text,
  Flex,
  Button,
  BackgroundImage,
  Link as A
} from '@hackclub/design-system'
import Link from 'gatsby-link'
import Module from 'components/Module'
import Stat from 'components/Stat'
import { stats } from 'data.json'

const Grid = Box.extend`
  display: grid;
  grid-gap: ${props => props.theme.space[2]}px;
  width: 100%;

  > div {
    width: 100%;
    box-shadow: ${props => props.theme.boxShadows[3]};
    border-radius: ${props => props.theme.radius};
    max-width: 100%;
  }

  ${props => props.theme.mediaQueries.md} {
    grid-template-columns: repeat(2, 1fr);
    grid-gap: ${props => props.theme.space[4]}px;
  }
`

const Super = Box.extend`
  background: ${props => props.theme.colors.warning} url(/diagonal.svg) top
    center repeat-x;
  background-size: auto 100%;
  position: relative;

  &:before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: url(/diagonal.svg) top right repeat-x;
    background-size: auto 100%;
    background-position-x: 32%;
  }

  > * {
    position: relative;
  }
`

const Graph = Box.extend`
  background: ${props => props.theme.colors.accent} url(/graph.svg) no-repeat;
  background-size: 100% 100%;
`

const Modules = Box.extend`
  display: grid;
  grid-gap: ${props => props.theme.space[3]}px;
  ${props => props.theme.mediaQueries.md} {
    grid-template-columns: repeat(2, 1fr);
  }
`

const Stats = Box.extend`
  column-count: 2;
  div {
    text-align: left;
  }
`

const like = {
  underline: true,
  color: 'inherit',
  target: '_blank'
}
A.link = A.withComponent(Link)
const Like = props => <A {...like} {...props} />
const LikeLink = props => <A.link {...like} {...props} />

const Photo = BackgroundImage.extend.attrs({ role: 'img' })`
  overflow: hidden;
  transition: all 0.125s ease-out;
  background-size: auto 100%;
  &:hover {
    background-size: auto 108%;
  }
  @media (prefers-reduced-motion: reduce) {
    transition: none;
    background-size: cover !important;
  }
`

const Action = Button.withComponent(Link).extend`
  transition: transform 0.125s ease-out;
  will-change: transform;
  transform: scale(1);
  &:hover,
  &:focus {
    transform: scale(1.06);
  }
  @media (prefers-reduced-motion: reduce) {
    transform: none !important;
  }
`

export default () => (
  <Container w={1} px={[3, 4, null, 2]} mt={[4, 5]}>
    <Box mx={0} mt={5} color="black">
      <Heading.h2 f={[5, 6]} mb={3}>
        Join the largest community of students building the class they wish they
        had.
      </Heading.h2>
      <Container maxWidth={48} mx={0}>
        <Text f={[3, 4]}>
          At Hack Club, every week you and 20 other students come together to
          make. Meetings are like mini-hackathons. People are working on
          projects, you lead workshops introducing new technologies, you and
          your co-leads mentor. Members typically start with no experience.
        </Text>
      </Container>
    </Box>
    <Grid mt={4} mb={[4, 6]} color="white">
      <Box bg="primary" p={[3, 4]}>
        <Heading.h3 f={4} my={0} caps>
          So much more than a club
        </Heading.h3>
        <Text f={3} my={3}>
          Hack Clubs attend and run hackathons like{' '}
          <Like href="https://hackchicago.io">
            Hack Chicago
          </Like> and{' '}
          <Like href="https://www.sfchronicle.com/bayarea/article/Hack-the-Fog-makes-history-as-San-12729895.php">
            Hack the Fog
          </Like>. They run summer programs like{' '}
          <Like href="http://thecspn.com/?p=43434">Hack Camp</Like>. They
          compete in competitions like the{' '}
          <Like href="http://www.congressionalappchallenge.us">
            Congressional App Challenge
          </Like>{' '}
          (and <LikeLink to="/challenge">our Challenge</LikeLink>!). This is no
          ordinary club.
        </Text>
        <Action to="/meetings" inverted>
          Learn more »
        </Action>
      </Box>
      <Photo src="/about_1.jpg" scale />
      <Photo src="/about_2.jpg" scale />
      <Box bg="info" p={[3, 4]}>
        <Heading.h3 f={4} my={0} caps>
          The power of a network
        </Heading.h3>
        <Modules my={3} w={1}>
          <Module
            icon="forum"
            heading="Online community"
            body="Talk to thousands of club leaders/members on Slack."
          />
          <Module
            icon="chrome_reader_mode"
            heading="Curriculum"
            body="Dozens of tutorials for your members to make projects."
          />
          <Module
            icon="voice_chat"
            heading="Mentorship"
            body="Talk to our team online for guidance anytime."
          />
          <Module
            icon="wallpaper"
            heading="Marketing"
            body="Get materials and ideas for spreading your club."
          />
        </Modules>
      </Box>
    </Grid>
    <Box mx={0} mt={5} color="black">
      <Heading.h2 f={[5, 6]} mb={3}>
        We believe people learn best when they take control of their own
        education.
      </Heading.h2>
      <Container maxWidth={48} mx={0}>
        <Text f={[3, 4]}>
          Hack Club is heavily inspired by unschooling. At Hack Club, there are
          no teachers or lectures—members work at their own pace on their own
          projects. Your job as a club leader is to facilitate and provide
          guidance through mentoring and the power of community.
        </Text>
      </Container>
    </Box>
    <Grid my={[4, 5]} color="white" pb={3}>
      <Super p={[3, 4]}>
        <Heading.h3 f={4} my={0} caps>
          Coding is a superpower
        </Heading.h3>
        <Text f={3} my={3}>
          Learning to code is uniquely empowering: you go from a consumer to a
          creator. The goal of Hack Club is to help you become that creator. We
          want a space for hacking at every high school, every week.
        </Text>
        <Action to="/philosophy" bg="warning" inverted>
          Our philosophy »
        </Action>
      </Super>
      <Photo src="/about_3.jpg" scale />
      <Photo src="/about_4.jpg" scale />
      <Graph p={[3, 4]}>
        <Heading.h3 f={4} my={0} caps>
          Grassroots and growing
        </Heading.h3>
        <Stats mb={2}>
          <Stat f={7} value={stats.school_count} label="schools" />
          <Stat f={7} value={stats.country_count} label="countries" />
          <Stat f={7} value={stats.state_count} label="states" />
          <Stat f={7} value={stats.approximate_members} label="members" />
        </Stats>
        <Action to="/team" bg="accent" inverted>
          Our team »
        </Action>
      </Graph>
    </Grid>
  </Container>
)
<|endoftext|>


<commit_before>'use strict'

const createTorrent = require('create-torrent')
const ffmpeg = require('fluent-ffmpeg')
const fs = require('fs')
const magnetUtil = require('magnet-uri')
const parallel = require('async/parallel')
const parseTorrent = require('parse-torrent')
const pathUtils = require('path')
const mongoose = require('mongoose')

const constants = require('../initializers/constants')
const customVideosValidators = require('../helpers/custom-validators').videos
const logger = require('../helpers/logger')
const modelUtils = require('./utils')

// ---------------------------------------------------------------------------

// TODO: add indexes on searchable columns
const VideoSchema = mongoose.Schema({
  name: String,
  extname: {
    type: String,
    enum: [ '.mp4', '.webm', '.ogv' ]
  },
  remoteId: mongoose.Schema.Types.ObjectId,
  description: String,
  magnet: {
    infoHash: String
  },
  podHost: String,
  author: String,
  duration: Number,
  tags: [ String ],
  createdDate: {
    type: Date,
    default: Date.now
  }
})

VideoSchema.path('name').validate(customVideosValidators.isVideoNameValid)
VideoSchema.path('description').validate(customVideosValidators.isVideoDescriptionValid)
VideoSchema.path('podHost').validate(customVideosValidators.isVideoPodHostValid)
VideoSchema.path('author').validate(customVideosValidators.isVideoAuthorValid)
VideoSchema.path('duration').validate(customVideosValidators.isVideoDurationValid)
VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid)

VideoSchema.methods = {
  generateMagnetUri,
  getVideoFilename,
  getThumbnailName,
  getPreviewName,
  getTorrentName,
  isOwned,
  toFormatedJSON,
  toRemoteJSON
}

VideoSchema.statics = {
  generateThumbnailFromBase64,
  getDurationFromFile,
  listForApi,
  listByHostAndRemoteId,
  listByHost,
  listOwned,
  listOwnedByAuthor,
  listRemotes,
  load,
  search
}

VideoSchema.pre('remove', function (next) {
  const video = this
  const tasks = []

  tasks.push(
    function (callback) {
      removeThumbnail(video, callback)
    }
  )

  if (video.isOwned()) {
    tasks.push(
      function (callback) {
        removeFile(video, callback)
      },
      function (callback) {
        removeTorrent(video, callback)
      },
      function (callback) {
        removePreview(video, callback)
      }
    )
  }

  parallel(tasks, next)
})

VideoSchema.pre('save', function (next) {
  const video = this
  const tasks = []

  if (video.isOwned()) {
    const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
    this.podHost = constants.CONFIG.WEBSERVER.HOST

    tasks.push(
      // TODO: refractoring
      function (callback) {
        const options = {
          announceList: [
            [ constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
          ],
          urlList: [
            constants.CONFIG.WEBSERVER.URL + constants.STATIC_PATHS.WEBSEED + video.getVideoFilename()
          ]
        }

        createTorrent(videoPath, options, function (err, torrent) {
          if (err) return callback(err)

          fs.writeFile(constants.CONFIG.STORAGE.TORRENTS_DIR + video.getTorrentName(), torrent, function (err) {
            if (err) return callback(err)

            const parsedTorrent = parseTorrent(torrent)
            video.magnet.infoHash = parsedTorrent.infoHash

            callback(null)
          })
        })
      },
      function (callback) {
        createThumbnail(video, videoPath, callback)
      },
      function (callback) {
        createPreview(video, videoPath, callback)
      }
    )

    return parallel(tasks, next)
  }

  return next()
})

mongoose.model('Video', VideoSchema)

// ------------------------------ METHODS ------------------------------

function generateMagnetUri () {
  let baseUrlHttp, baseUrlWs

  if (this.isOwned()) {
    baseUrlHttp = constants.CONFIG.WEBSERVER.URL
    baseUrlWs = constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT
  } else {
    baseUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + this.podHost
    baseUrlWs = constants.REMOTE_SCHEME.WS + this.podHost
  }

  const xs = baseUrlHttp + constants.STATIC_PATHS.TORRENTS + this.getTorrentName()
  const announce = baseUrlWs + '/tracker/socket'
  const urlList = [ baseUrlHttp + constants.STATIC_PATHS.WEBSEED + this.getVideoFilename() ]

  const magnetHash = {
    xs,
    announce,
    urlList,
    infoHash: this.magnet.infoHash,
    name: this.name
  }

  return magnetUtil.encode(magnetHash)
}

function getVideoFilename () {
  if (this.isOwned()) return this._id + this.extname

  return this.remoteId + this.extname
}

function getThumbnailName () {
  // We always have a copy of the thumbnail
  return this._id + '.jpg'
}

function getPreviewName () {
  const extension = '.jpg'

  if (this.isOwned()) return this._id + extension

  return this.remoteId + extension
}

function getTorrentName () {
  const extension = '.torrent'

  if (this.isOwned()) return this._id + extension

  return this.remoteId + extension
}

function isOwned () {
  return this.remoteId === null
}

function toFormatedJSON () {
  const json = {
    id: this._id,
    name: this.name,
    description: this.description,
    podHost: this.podHost,
    isLocal: this.isOwned(),
    magnetUri: this.generateMagnetUri(),
    author: this.author,
    duration: this.duration,
    tags: this.tags,
    thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.getThumbnailName(),
    createdDate: this.createdDate
  }

  return json
}

function toRemoteJSON (callback) {
  const self = this

  // Convert thumbnail to base64
  const thumbnailPath = pathUtils.join(constants.CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
  fs.readFile(thumbnailPath, function (err, thumbnailData) {
    if (err) {
      logger.error('Cannot read the thumbnail of the video')
      return callback(err)
    }

    const remoteVideo = {
      name: self.name,
      description: self.description,
      magnet: self.magnet,
      remoteId: self._id,
      author: self.author,
      duration: self.duration,
      thumbnailBase64: new Buffer(thumbnailData).toString('base64'),
      tags: self.tags,
      createdDate: self.createdDate,
      podHost: self.podHost,
      extname: self.extname
    }

    return callback(null, remoteVideo)
  })
}

// ------------------------------ STATICS ------------------------------

function generateThumbnailFromBase64 (video, thumbnailData, callback) {
  // Creating the thumbnail for a remote video

  const thumbnailName = video.getThumbnailName()
  const thumbnailPath = constants.CONFIG.STORAGE.THUMBNAILS_DIR + thumbnailName
  fs.writeFile(thumbnailPath, thumbnailData, { encoding: 'base64' }, function (err) {
    if (err) return callback(err)

    return callback(null, thumbnailName)
  })
}

function getDurationFromFile (videoPath, callback) {
  ffmpeg.ffprobe(videoPath, function (err, metadata) {
    if (err) return callback(err)

    return callback(null, Math.floor(metadata.format.duration))
  })
}

function listForApi (start, count, sort, callback) {
  const query = {}
  return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback)
}

function listByHostAndRemoteId (fromHost, remoteId, callback) {
  this.find({ podHost: fromHost, remoteId: remoteId }, callback)
}

function listByHost (fromHost, callback) {
  this.find({ podHost: fromHost }, callback)
}

function listOwned (callback) {
  // If remoteId is null this is *our* video
  this.find({ remoteId: null }, callback)
}

function listOwnedByAuthor (author, callback) {
  this.find({ remoteId: null, author: author }, callback)
}

function listRemotes (callback) {
  this.find({ remoteId: { $ne: null } }, callback)
}

function load (id, callback) {
  this.findById(id, callback)
}

function search (value, field, start, count, sort, callback) {
  const query = {}
  // Make an exact search with the magnet
  if (field === 'magnetUri') {
    const infoHash = magnetUtil.decode(value).infoHash
    query.magnet = {
      infoHash
    }
  } else if (field === 'tags') {
    query[field] = value
  } else {
    query[field] = new RegExp(value, 'i')
  }

  modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback)
}

// ---------------------------------------------------------------------------

function removeThumbnail (video, callback) {
  fs.unlink(constants.CONFIG.STORAGE.THUMBNAILS_DIR + video.getThumbnailName(), callback)
}

function removeFile (video, callback) {
  fs.unlink(constants.CONFIG.STORAGE.VIDEOS_DIR + video.getVideoFilename(), callback)
}

function removeTorrent (video, callback) {
  fs.unlink(constants.CONFIG.STORAGE.TORRENTS_DIR + video.getTorrentName(), callback)
}

function removePreview (video, callback) {
  // Same name than video thumnail
  fs.unlink(constants.CONFIG.STORAGE.PREVIEWS_DIR + video.getPreviewName(), callback)
}

function createPreview (video, videoPath, callback) {
  generateImage(video, videoPath, constants.CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName(), callback)
}

function createThumbnail (video, videoPath, callback) {
  generateImage(video, videoPath, constants.CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName(), constants.THUMBNAILS_SIZE, callback)
}

function generateImage (video, videoPath, folder, imageName, size, callback) {
  const options = {
    filename: imageName,
    count: 1,
    folder
  }

  if (!callback) {
    callback = size
  } else {
    options.size = size
  }

  ffmpeg(videoPath)
    .on('error', callback)
    .on('end', function () {
      callback(null, imageName)
    })
    .thumbnail(options)
}
<commit_msg>Server: fix magnet tracker websocket url for remote videos<commit_after>'use strict'

const createTorrent = require('create-torrent')
const ffmpeg = require('fluent-ffmpeg')
const fs = require('fs')
const magnetUtil = require('magnet-uri')
const parallel = require('async/parallel')
const parseTorrent = require('parse-torrent')
const pathUtils = require('path')
const mongoose = require('mongoose')

const constants = require('../initializers/constants')
const customVideosValidators = require('../helpers/custom-validators').videos
const logger = require('../helpers/logger')
const modelUtils = require('./utils')

// ---------------------------------------------------------------------------

// TODO: add indexes on searchable columns
const VideoSchema = mongoose.Schema({
  name: String,
  extname: {
    type: String,
    enum: [ '.mp4', '.webm', '.ogv' ]
  },
  remoteId: mongoose.Schema.Types.ObjectId,
  description: String,
  magnet: {
    infoHash: String
  },
  podHost: String,
  author: String,
  duration: Number,
  tags: [ String ],
  createdDate: {
    type: Date,
    default: Date.now
  }
})

VideoSchema.path('name').validate(customVideosValidators.isVideoNameValid)
VideoSchema.path('description').validate(customVideosValidators.isVideoDescriptionValid)
VideoSchema.path('podHost').validate(customVideosValidators.isVideoPodHostValid)
VideoSchema.path('author').validate(customVideosValidators.isVideoAuthorValid)
VideoSchema.path('duration').validate(customVideosValidators.isVideoDurationValid)
VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid)

VideoSchema.methods = {
  generateMagnetUri,
  getVideoFilename,
  getThumbnailName,
  getPreviewName,
  getTorrentName,
  isOwned,
  toFormatedJSON,
  toRemoteJSON
}

VideoSchema.statics = {
  generateThumbnailFromBase64,
  getDurationFromFile,
  listForApi,
  listByHostAndRemoteId,
  listByHost,
  listOwned,
  listOwnedByAuthor,
  listRemotes,
  load,
  search
}

VideoSchema.pre('remove', function (next) {
  const video = this
  const tasks = []

  tasks.push(
    function (callback) {
      removeThumbnail(video, callback)
    }
  )

  if (video.isOwned()) {
    tasks.push(
      function (callback) {
        removeFile(video, callback)
      },
      function (callback) {
        removeTorrent(video, callback)
      },
      function (callback) {
        removePreview(video, callback)
      }
    )
  }

  parallel(tasks, next)
})

VideoSchema.pre('save', function (next) {
  const video = this
  const tasks = []

  if (video.isOwned()) {
    const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
    this.podHost = constants.CONFIG.WEBSERVER.HOST

    tasks.push(
      // TODO: refractoring
      function (callback) {
        const options = {
          announceList: [
            [ constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
          ],
          urlList: [
            constants.CONFIG.WEBSERVER.URL + constants.STATIC_PATHS.WEBSEED + video.getVideoFilename()
          ]
        }

        createTorrent(videoPath, options, function (err, torrent) {
          if (err) return callback(err)

          fs.writeFile(constants.CONFIG.STORAGE.TORRENTS_DIR + video.getTorrentName(), torrent, function (err) {
            if (err) return callback(err)

            const parsedTorrent = parseTorrent(torrent)
            video.magnet.infoHash = parsedTorrent.infoHash

            callback(null)
          })
        })
      },
      function (callback) {
        createThumbnail(video, videoPath, callback)
      },
      function (callback) {
        createPreview(video, videoPath, callback)
      }
    )

    return parallel(tasks, next)
  }

  return next()
})

mongoose.model('Video', VideoSchema)

// ------------------------------ METHODS ------------------------------

function generateMagnetUri () {
  let baseUrlHttp, baseUrlWs

  if (this.isOwned()) {
    baseUrlHttp = constants.CONFIG.WEBSERVER.URL
    baseUrlWs = constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT
  } else {
    baseUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + this.podHost
    baseUrlWs = constants.REMOTE_SCHEME.WS + '://' + this.podHost
  }

  const xs = baseUrlHttp + constants.STATIC_PATHS.TORRENTS + this.getTorrentName()
  const announce = baseUrlWs + '/tracker/socket'
  const urlList = [ baseUrlHttp + constants.STATIC_PATHS.WEBSEED + this.getVideoFilename() ]

  const magnetHash = {
    xs,
    announce,
    urlList,
    infoHash: this.magnet.infoHash,
    name: this.name
  }

  return magnetUtil.encode(magnetHash)
}

function getVideoFilename () {
  if (this.isOwned()) return this._id + this.extname

  return this.remoteId + this.extname
}

function getThumbnailName () {
  // We always have a copy of the thumbnail
  return this._id + '.jpg'
}

function getPreviewName () {
  const extension = '.jpg'

  if (this.isOwned()) return this._id + extension

  return this.remoteId + extension
}

function getTorrentName () {
  const extension = '.torrent'

  if (this.isOwned()) return this._id + extension

  return this.remoteId + extension
}

function isOwned () {
  return this.remoteId === null
}

function toFormatedJSON () {
  const json = {
    id: this._id,
    name: this.name,
    description: this.description,
    podHost: this.podHost,
    isLocal: this.isOwned(),
    magnetUri: this.generateMagnetUri(),
    author: this.author,
    duration: this.duration,
    tags: this.tags,
    thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.getThumbnailName(),
    createdDate: this.createdDate
  }

  return json
}

function toRemoteJSON (callback) {
  const self = this

  // Convert thumbnail to base64
  const thumbnailPath = pathUtils.join(constants.CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
  fs.readFile(thumbnailPath, function (err, thumbnailData) {
    if (err) {
      logger.error('Cannot read the thumbnail of the video')
      return callback(err)
    }

    const remoteVideo = {
      name: self.name,
      description: self.description,
      magnet: self.magnet,
      remoteId: self._id,
      author: self.author,
      duration: self.duration,
      thumbnailBase64: new Buffer(thumbnailData).toString('base64'),
      tags: self.tags,
      createdDate: self.createdDate,
      podHost: self.podHost,
      extname: self.extname
    }

    return callback(null, remoteVideo)
  })
}

// ------------------------------ STATICS ------------------------------

function generateThumbnailFromBase64 (video, thumbnailData, callback) {
  // Creating the thumbnail for a remote video

  const thumbnailName = video.getThumbnailName()
  const thumbnailPath = constants.CONFIG.STORAGE.THUMBNAILS_DIR + thumbnailName
  fs.writeFile(thumbnailPath, thumbnailData, { encoding: 'base64' }, function (err) {
    if (err) return callback(err)

    return callback(null, thumbnailName)
  })
}

function getDurationFromFile (videoPath, callback) {
  ffmpeg.ffprobe(videoPath, function (err, metadata) {
    if (err) return callback(err)

    return callback(null, Math.floor(metadata.format.duration))
  })
}

function listForApi (start, count, sort, callback) {
  const query = {}
  return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback)
}

function listByHostAndRemoteId (fromHost, remoteId, callback) {
  this.find({ podHost: fromHost, remoteId: remoteId }, callback)
}

function listByHost (fromHost, callback) {
  this.find({ podHost: fromHost }, callback)
}

function listOwned (callback) {
  // If remoteId is null this is *our* video
  this.find({ remoteId: null }, callback)
}

function listOwnedByAuthor (author, callback) {
  this.find({ remoteId: null, author: author }, callback)
}

function listRemotes (callback) {
  this.find({ remoteId: { $ne: null } }, callback)
}

function load (id, callback) {
  this.findById(id, callback)
}

function search (value, field, start, count, sort, callback) {
  const query = {}
  // Make an exact search with the magnet
  if (field === 'magnetUri') {
    const infoHash = magnetUtil.decode(value).infoHash
    query.magnet = {
      infoHash
    }
  } else if (field === 'tags') {
    query[field] = value
  } else {
    query[field] = new RegExp(value, 'i')
  }

  modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback)
}

// ---------------------------------------------------------------------------

function removeThumbnail (video, callback) {
  fs.unlink(constants.CONFIG.STORAGE.THUMBNAILS_DIR + video.getThumbnailName(), callback)
}

function removeFile (video, callback) {
  fs.unlink(constants.CONFIG.STORAGE.VIDEOS_DIR + video.getVideoFilename(), callback)
}

function removeTorrent (video, callback) {
  fs.unlink(constants.CONFIG.STORAGE.TORRENTS_DIR + video.getTorrentName(), callback)
}

function removePreview (video, callback) {
  // Same name than video thumnail
  fs.unlink(constants.CONFIG.STORAGE.PREVIEWS_DIR + video.getPreviewName(), callback)
}

function createPreview (video, videoPath, callback) {
  generateImage(video, videoPath, constants.CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName(), callback)
}

function createThumbnail (video, videoPath, callback) {
  generateImage(video, videoPath, constants.CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName(), constants.THUMBNAILS_SIZE, callback)
}

function generateImage (video, videoPath, folder, imageName, size, callback) {
  const options = {
    filename: imageName,
    count: 1,
    folder
  }

  if (!callback) {
    callback = size
  } else {
    options.size = size
  }

  ffmpeg(videoPath)
    .on('error', callback)
    .on('end', function () {
      callback(null, imageName)
    })
    .thumbnail(options)
}
<|endoftext|>


<commit_before>var debug = require('debug')('webtorrent:append-to')
var dezalgo = require('dezalgo')
var MediaSourceStream = require('./media-stream')
var path = require('path')
var videostream = require('videostream')

var VIDEOSTREAM_EXTS = [ '.mp4', '.m4v', '.m4a' ]

var MEDIASOURCE_VIDEO_EXTS = [ '.mp4', '.m4v', '.webm' ]
var MEDIASOURCE_AUDIO_EXTS = [ '.m4a', '.mp3' ]
var MEDIASOURCE_EXTS = MEDIASOURCE_VIDEO_EXTS.concat(MEDIASOURCE_AUDIO_EXTS)

var AUDIO_EXTS = [ '.wav', '.aac', '.ogg', '.oga' ]
var IMAGE_EXTS = [ '.jpg', '.png', '.gif', '.bmp' ]
var IFRAME_EXTS = [ '.css', '.html', '.js', '.md', '.pdf', '.txt' ]

var MediaSource = typeof window !== 'undefined' && window.MediaSource

module.exports = function appendTo (file, rootElem, cb) {
  cb = dezalgo(cb)
  var elem
  var extname = path.extname(file.name).toLowerCase()

  if (rootElem && (rootElem.nodeName === 'VIDEO' || rootElem.nodeName === 'AUDIO')) {
    throw new Error(
      'Invalid video/audio node argument. Argument must be root element that ' +
      'video/audio tag will be appended to.'
    )
  }

  if (MEDIASOURCE_EXTS.indexOf(extname) >= 0) appendToMediaSource()
  else if (AUDIO_EXTS.indexOf(extname) >= 0) appendToAudio()
  else if (IMAGE_EXTS.indexOf(extname) >= 0) appendToImage()
  else if (IFRAME_EXTS.indexOf(extname) >= 0) appendToIframe()
  else cb(new Error('Unsupported file type "' + extname + '": Cannot append to DOM'))

  // TODO: re-use the same video tag
  function appendToMediaSource () {
    if (!MediaSource) {
      return cb(new Error(
        'Video/audio streaming is not supported in your browser. You can still share ' +
        'or download ' + file.name + ' (once it\'s fully downloaded). Use Chrome for ' +
        'MediaSource support.'
      ))
    }

    var tagName = MEDIASOURCE_VIDEO_EXTS.indexOf(extname) >= 0 ? 'video' : 'audio'

    if (VIDEOSTREAM_EXTS.indexOf(extname) >= 0) useVideostream()
    else useMediaSource()

    function useVideostream () {
      debug('Use `videostream` package for ' + file.name)
      createElem()
      elem.addEventListener('error', fallbackToMediaSource)
      elem.addEventListener('playing', onPlaying)
      videostream(file, elem)
      appendElem()
    }

    function useMediaSource () {
      debug('Use MediaSource API for ' + file.name)
      createElem()
      elem.addEventListener('error', fallbackToBlobURL)
      elem.addEventListener('playing', onPlaying)
      file.createReadStream().pipe(new MediaSourceStream(elem, { extname: extname }))
      appendElem()
    }

    function useBlobURL () {
      debug('Use Blob URL for ' + file.name)
      createElem()
      elem.addEventListener('error', fatalError)
      elem.addEventListener('playing', onPlaying)
      appendElem()
      file.getBlobURL(function (err, url) {
        if (err) return fatalError(err)
        elem.src = url
      })
    }

    function fallbackToMediaSource (err) {
      debug('videostream error: fallback to MediaSource API: %o', err.message || err)
      elem.removeEventListener('error', fallbackToMediaSource)
      elem.removeEventListener('playing', onPlaying)

      useMediaSource()
    }

    function fallbackToBlobURL (err) {
      debug('MediaSource API error: fallback to Blob URL: %o', err.message || err)
      elem.removeEventListener('error', fallbackToBlobURL)
      elem.removeEventListener('playing', onPlaying)

      useBlobURL()
    }

    function createElem () {
      if (elem) elem.remove()
      elem = window.elem = document.createElement(tagName)
      elem.controls = true
      elem.autoplay = true
    }

    function appendElem () {
      rootElem.appendChild(elem)
      elem.play()
    }
  }

  function onPlaying () {
    elem.removeEventListener('playing', onPlaying)
    cb(null, elem)
  }

  function appendToAudio () {
    elem = document.createElement('audio')
    elem.controls = true
    elem.autoplay = true
    rootElem.appendChild(elem)
    file.getBlobURL(function (err, url) {
      if (err) return fatalError(err)
      elem.addEventListener('error', fatalError)
      elem.addEventListener('playing', onPlaying)
      elem.src = url
      elem.play()
    })
  }

  function appendToImage () {
    file.getBlobURL(function (err, url) {
      if (err) return fatalError(err)
      elem = document.createElement('img')
      elem.src = url
      elem.alt = file.name
      rootElem.appendChild(elem)
      cb(null)
    })
  }

  function appendToIframe () {
    file.getBlobURL(function (err, url) {
      if (err) return fatalError(err)
      elem = document.createElement('iframe')
      elem.src = url
      if (extname !== '.pdf') elem.sandbox = 'allow-forms allow-scripts'
      rootElem.appendChild(elem)
      cb(null)
    })
  }

  function fatalError (err) {
    if (elem) elem.remove()
    err.message = 'Error appending file "' + file.name + '" to DOM: ' + err.message
    debug(err.message)
    if (cb) cb(err)
  }
}
<commit_msg>file.appendTo() does not require a callback<commit_after>var debug = require('debug')('webtorrent:append-to')
var dezalgo = require('dezalgo')
var MediaSourceStream = require('./media-stream')
var path = require('path')
var videostream = require('videostream')

var VIDEOSTREAM_EXTS = [ '.mp4', '.m4v', '.m4a' ]

var MEDIASOURCE_VIDEO_EXTS = [ '.mp4', '.m4v', '.webm' ]
var MEDIASOURCE_AUDIO_EXTS = [ '.m4a', '.mp3' ]
var MEDIASOURCE_EXTS = MEDIASOURCE_VIDEO_EXTS.concat(MEDIASOURCE_AUDIO_EXTS)

var AUDIO_EXTS = [ '.wav', '.aac', '.ogg', '.oga' ]
var IMAGE_EXTS = [ '.jpg', '.png', '.gif', '.bmp' ]
var IFRAME_EXTS = [ '.css', '.html', '.js', '.md', '.pdf', '.txt' ]

var MediaSource = typeof window !== 'undefined' && window.MediaSource

module.exports = function appendTo (file, rootElem, cb) {
  cb = dezalgo(cb || function () {})
  var elem
  var extname = path.extname(file.name).toLowerCase()

  if (rootElem && (rootElem.nodeName === 'VIDEO' || rootElem.nodeName === 'AUDIO')) {
    throw new Error(
      'Invalid video/audio node argument. Argument must be root element that ' +
      'video/audio tag will be appended to.'
    )
  }

  if (MEDIASOURCE_EXTS.indexOf(extname) >= 0) appendToMediaSource()
  else if (AUDIO_EXTS.indexOf(extname) >= 0) appendToAudio()
  else if (IMAGE_EXTS.indexOf(extname) >= 0) appendToImage()
  else if (IFRAME_EXTS.indexOf(extname) >= 0) appendToIframe()
  else cb(new Error('Unsupported file type "' + extname + '": Cannot append to DOM'))

  // TODO: re-use the same video tag
  function appendToMediaSource () {
    if (!MediaSource) {
      return cb(new Error(
        'Video/audio streaming is not supported in your browser. You can still share ' +
        'or download ' + file.name + ' (once it\'s fully downloaded). Use Chrome for ' +
        'MediaSource support.'
      ))
    }

    var tagName = MEDIASOURCE_VIDEO_EXTS.indexOf(extname) >= 0 ? 'video' : 'audio'

    if (VIDEOSTREAM_EXTS.indexOf(extname) >= 0) useVideostream()
    else useMediaSource()

    function useVideostream () {
      debug('Use `videostream` package for ' + file.name)
      createElem()
      elem.addEventListener('error', fallbackToMediaSource)
      elem.addEventListener('playing', onPlaying)
      videostream(file, elem)
      appendElem()
    }

    function useMediaSource () {
      debug('Use MediaSource API for ' + file.name)
      createElem()
      elem.addEventListener('error', fallbackToBlobURL)
      elem.addEventListener('playing', onPlaying)
      file.createReadStream().pipe(new MediaSourceStream(elem, { extname: extname }))
      appendElem()
    }

    function useBlobURL () {
      debug('Use Blob URL for ' + file.name)
      createElem()
      elem.addEventListener('error', fatalError)
      elem.addEventListener('playing', onPlaying)
      appendElem()
      file.getBlobURL(function (err, url) {
        if (err) return fatalError(err)
        elem.src = url
      })
    }

    function fallbackToMediaSource (err) {
      debug('videostream error: fallback to MediaSource API: %o', err.message || err)
      elem.removeEventListener('error', fallbackToMediaSource)
      elem.removeEventListener('playing', onPlaying)

      useMediaSource()
    }

    function fallbackToBlobURL (err) {
      debug('MediaSource API error: fallback to Blob URL: %o', err.message || err)
      elem.removeEventListener('error', fallbackToBlobURL)
      elem.removeEventListener('playing', onPlaying)

      useBlobURL()
    }

    function createElem () {
      if (elem) elem.remove()
      elem = window.elem = document.createElement(tagName)
      elem.controls = true
      elem.autoplay = true
    }

    function appendElem () {
      rootElem.appendChild(elem)
      elem.play()
    }
  }

  function onPlaying () {
    elem.removeEventListener('playing', onPlaying)
    cb(null, elem)
  }

  function appendToAudio () {
    elem = document.createElement('audio')
    elem.controls = true
    elem.autoplay = true
    rootElem.appendChild(elem)
    file.getBlobURL(function (err, url) {
      if (err) return fatalError(err)
      elem.addEventListener('error', fatalError)
      elem.addEventListener('playing', onPlaying)
      elem.src = url
      elem.play()
    })
  }

  function appendToImage () {
    file.getBlobURL(function (err, url) {
      if (err) return fatalError(err)
      elem = document.createElement('img')
      elem.src = url
      elem.alt = file.name
      rootElem.appendChild(elem)
      cb(null)
    })
  }

  function appendToIframe () {
    file.getBlobURL(function (err, url) {
      if (err) return fatalError(err)
      elem = document.createElement('iframe')
      elem.src = url
      if (extname !== '.pdf') elem.sandbox = 'allow-forms allow-scripts'
      rootElem.appendChild(elem)
      cb(null)
    })
  }

  function fatalError (err) {
    if (elem) elem.remove()
    err.message = 'Error appending file "' + file.name + '" to DOM: ' + err.message
    debug(err.message)
    if (cb) cb(err)
  }
}
<|endoftext|>


<commit_before>///////////////////////////////////////////////////
// I would like to make squares dance to music. //
/////////////////////////////////////////////////

// What is my MVP for these dancing squares?
// - appear centered in the window
// - work in a few different window sizes
// - be colorful
// - change visually according to beat information
// - change without lagging noticeably

///// notes
// https://en.wikipedia.org/wiki/Highly_composite_number
// Largely composite numbers - http://oeis.org/A067128/internal
// write a function that finds the container sizes that allow for the highest number of square sizes
// 1440x900 = [10,12,15,18,20,30,36,45,60,90,180];
// 1440x720 = [10,12,15,16,18,20,24,30,36,40,45,48,60,72,80,90,120,144,180,240,360,720];
// 1260x720 = [10,12,15,18,20,30,36,45,60,90,180];
// 720x480 = [10,12,15,16,20,24,30,40,48,60,80,120,240];

// listeners
$(function() {
  console.log('jQuery and the document are loaded');
  setContainerSize();
  drawSquares();
});

$(window).resize(function() {
  setContainerSize();
  drawSquares();
});

$('#btn').click(drawSquares);

$('#btn2').click(freshen);

///////////////

function setContainerSize() {
  var width = $(window).width();
  var height = $(window).height();
  var w = 720;
  var h = 480;

  if (width >= 1440 && height >= 900) {
    w = 1440;
    h = 900;
  } else if (width >= 1440 && height >= 720) {
    w = 1440;
    h = 720;
  } else if (width >= 1260 && height >= 720) {
    w = 1260;
    h = 720;
  }
  $('.squares').css({width: w, height: h});
}

function getRandSquare() {
  var w = $('.squares').width() + 'x' + $('.squares').height();

  if (w === '1440x900') {
    // return pickOne([10,12,15,18,20,30,36,45,60,90,180]);
    return pickOne([20,30,36,45,60,90,180]);
  } else if (w === '1440x720') {
    // return pickOne([10,12,15,16,18,20,24,30,36,40,45,48,60,72,80,90,120,144,180,240,360,720]);
    return pickOne([20,24,30,36,40,45,48,60,72,80,90,120,144,180,240,360,720]);
  } else if (w === '1260x720') {
    // return pickOne([10,12,15,18,20,30,36,45,60,90,180]);
    return pickOne([20,30,36,45,60,90,180]);
  } else {
    // return pickOne([10,12,15,16,20,24,30,40,48,60,80,120,240]);
    return pickOne([20,24,30,40,48,60,80,120,240]);
  }

}

function drawSquares() {
  var square = getRandSquare();
  var w = $('.squares').width();
  var h = $('.squares').height();
  var cols = w / square;
  var rows = h / square;
  console.log('square: ' + square, 'width: ' + w, 'height: ' + h);
  console.log('cols: ' + cols, 'rows: ' + rows);
  var num = cols * rows;
  var squaresArr = [];

  for (var i = 0; i < num; i++) {
    squaresArr.push(makeSquare(square));
  }

  $('.squares').empty().append(squaresArr);
}

function makeSquare(square) {
  var border = new Border();
  var squareDiv = document.createElement('div');
  $(squareDiv)
    .addClass('square')
    .css({
      'height': square,
      'width': square,
      'background': randomColor({luminosity: 'bright'}),
      'border-style': border.styles.join(' '),
      'border-color': border.colors.join(' '),
      'border-width': square / 10
    });
  return squareDiv;
}

function Border() {
  var styles = ['none', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'];
  this.styles = [];
  this.colors = randomColor({count: 4, luminosity: 'bright'});
  for (var i = 0; i < 4; i++) {
    this.styles.push(styles[Math.floor(Math.random() * 9)]);
  }
}

function freshen() {
  var rot = $('.square').data('rotate') || 0;
  console.log(rot);
  rot += rand(0,360);
  console.log(rot);
  $('.square').css({
    transition: '0.5s',
    '-webkit-filter': 'hue-rotate(' + rot + 'deg)',
    transform: 'rotate(' + rot + 'deg)'
  });
  // $('.square').toggleClass('rotate');
}

// utility functions

function rand(min, max) {
  return Math.random() * (max - min) + min;
}

function pickOne(arr) {
  var rand = Math.floor(Math.random() * arr.length);
  return arr[rand];
}
<commit_msg>added scale to the mix<commit_after>///////////////////////////////////////////////////
// I would like to make squares dance to music. //
/////////////////////////////////////////////////

// What is my MVP for these dancing squares?
// - appear centered in the window
// - work in a few different window sizes
// - be colorful
// - change visually according to beat information
// - change without lagging noticeably

///// notes
// https://en.wikipedia.org/wiki/Highly_composite_number
// Largely composite numbers - http://oeis.org/A067128/internal
// write a function that finds the container sizes that allow for the highest number of square sizes
// 1440x900 = [10,12,15,18,20,30,36,45,60,90,180];
// 1440x720 = [10,12,15,16,18,20,24,30,36,40,45,48,60,72,80,90,120,144,180,240,360,720];
// 1260x720 = [10,12,15,18,20,30,36,45,60,90,180];
// 720x480 = [10,12,15,16,20,24,30,40,48,60,80,120,240];

// listeners
$(function() {
  console.log('jQuery and the document are loaded');
  setContainerSize();
  drawSquares();
});

$(window).resize(function() {
  setContainerSize();
  drawSquares();
});

$('#btn').click(drawSquares);

$('#btn2').click(freshen);

///////////////

function setContainerSize() {
  var width = $(window).width();
  var height = $(window).height();
  var w = 720;
  var h = 480;

  if (width >= 1440 && height >= 900) {
    w = 1440;
    h = 900;
  } else if (width >= 1440 && height >= 720) {
    w = 1440;
    h = 720;
  } else if (width >= 1260 && height >= 720) {
    w = 1260;
    h = 720;
  }
  $('.squares').css({width: w, height: h});
}

function getRandSquare() {
  var w = $('.squares').width() + 'x' + $('.squares').height();

  if (w === '1440x900') {
    // return pickOne([10,12,15,18,20,30,36,45,60,90,180]);
    return pickOne([20,30,36,45,60,90,180]);
  } else if (w === '1440x720') {
    // return pickOne([10,12,15,16,18,20,24,30,36,40,45,48,60,72,80,90,120,144,180,240,360,720]);
    return pickOne([20,24,30,36,40,45,48,60,72,80,90,120,144,180,240,360,720]);
  } else if (w === '1260x720') {
    // return pickOne([10,12,15,18,20,30,36,45,60,90,180]);
    return pickOne([20,30,36,45,60,90,180]);
  } else {
    // return pickOne([10,12,15,16,20,24,30,40,48,60,80,120,240]);
    return pickOne([20,24,30,40,48,60,80,120,240]);
  }

}

function drawSquares() {
  var square = getRandSquare();
  var w = $('.squares').width();
  var h = $('.squares').height();
  var cols = w / square;
  var rows = h / square;
  console.log('square: ' + square, 'width: ' + w, 'height: ' + h);
  console.log('cols: ' + cols, 'rows: ' + rows);
  var num = cols * rows;
  var squaresArr = [];

  for (var i = 0; i < num; i++) {
    squaresArr.push(makeSquare(square));
  }

  $('.squares').empty().append(squaresArr);
}

function makeSquare(square) {
  var border = new Border();
  var squareDiv = document.createElement('div');
  $(squareDiv)
    .addClass('square')
    .css({
      'height': square,
      'width': square,
      'background': randomColor({luminosity: 'bright'}),
      'border-style': border.styles.join(' '),
      'border-color': border.colors.join(' '),
      'border-width': square / 10
    });
  return squareDiv;
}

function Border() {
  var styles = ['none', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'];
  this.styles = [];
  this.colors = randomColor({count: 4, luminosity: 'bright'});
  for (var i = 0; i < 4; i++) {
    this.styles.push(styles[Math.floor(Math.random() * 9)]);
  }
}

function freshen() {
  if (Math.round(Math.random())) {
    var rot = $('.square').data('rotate') || 0;
    console.log(rot);
    rot += rand(-360,360);
    console.log(rot);
    $('.square')
      .css({
        transition: rand(0.1,2) + 's',
        '-webkit-filter': 'hue-rotate(' + rot + 'deg)',
        transform: 'rotate(' + rot + 'deg)',
        'transform-origin': rand(-100,100) + '% ' + rand(-100,100) + '%'
      })
      .data('rotate', rot);
  } else {
    var scaleX = rand(-1,1);
    var scaleY = rand(-1,1);
    $('.square')
      .css({
        transition: rand(0.1,2) + 's',
        '-webkit-filter': 'hue-rotate(' + rand(0,360) + 'deg)',
        transform: 'scale(' + scaleX + ', ' + scaleY + ')',
        // 'transform-origin': rand(-100,100) + '% ' + rand(-100,100) + '%'
      });
  }
}

// utility functions

function rand(min, max) {
  return Math.random() * (max - min) + min;
}

function pickOne(arr) {
  var rand = Math.floor(Math.random() * arr.length);
  return arr[rand];
}
<|endoftext|>