<commit_before>var Util = require("./utils.js")

var UpdateApp = function() {
  var util = new Util();
  this.runScript = function(client, userOptions) {

    util.action("Fill in Store Listing", function() {

      selectFields = ['app_type', 'category', 'rating'];
      textFields = ['subtext', 'promo', 'promo_vid', 'website', 'email', 'phone'];

      for (option in userOptions) {
        if (selectFields.indexOf(option) !== -1) {
          client.click('#selectArea_' + option + ' option[value = ' + userOptions[option] + ']')
        }

        else if (textFields.indexOf(option) !== -1) {
          client.setValue('#textArea_' + option, userOptions[option]);
        }

        else if (option === 'privacy') {
          if (option) {
            client.setValue("#textArea_" + option, userOptions[option]);
          }
          else {
            client.click("#privacy_opt_out_id");
          }
        }
      }
    });

    util.action("Remove graphic", function(title) {

      var upload_id = searchForChild('h5', 'innerText', title, 2, 'input', (title+'_online_id').replace(' ', '_'), client);

      client.execute(function(id) {
        var input = document.getElementById(id);
        var x_divs = input.parentElement.parentElement.getElementsByTagName('div');

        for (i = 0; i < x_divs.length; i++) {
          if (x_divs[i].innerHTML.charCodeAt(0) === 215) {
            x_divs[i].click();
          }
        }
      }, [upload_id]);
    });

    util.action("Upload graphic", function(title, id, path) {

      var upload_id = searchForChild('h5', 'innerText', title, 2, 'input', id+'_upload', client);

      client.chooseFile("#" + upload_id, path, function(err, res){

      });

      client.execute(function(upload_id, waiting_id){
        var input = document.querySelector("#" + upload_id);
        toWatch = input.parentElement.parentElement.children[2];
        toWatch.id = waiting_id;
      }, [upload_id, id]);
    });

    util.action("Remove screenshots", function(tag) {

      client.execute(function(type) {
        var divs = document.querySelectorAll('b');
        var correct_div = null;
        for (var i =0; i <divs.length; i++) {
          if (divs[i].innerText.trim() === type) {
            correct_div = divs[i];
            break;
          }
        }

        var x_divs = correct_div.parentElement.parentElement.getElementsByTagName('div');
        for (i = 0; i < x_divs.length; i++) {
          if (x_divs[i].innerHTML.charCodeAt(0) === 215) {
            x_divs[i].click();
          }
        }
      }, [tag]);
    });

    util.action("Upload screenshot", function(type, id, path) {

      client.execute(function(type, id){

        var divs = document.querySelectorAll('b');
        var correct_div = null;
        for (var i =0; i <divs.length; i++) {
          if (divs[i].innerText.trim() === type) {
            correct_div = divs[i];
            break;
          }
        }
        var parent = correct_div.parentElement.parentElement;
        var inputs = parent.querySelectorAll('input');
        var input = inputs[inputs.length-1];

        input.id = id + '_upload';

        waiting = input.parentElement.parentElement.children[2];
        waiting.id = id;

      }, [type, id]);

      client.chooseFile("#" + id + '_upload', path, function(err, res){

      });
    });

    util.init(client, userOptions);

    util.step("Log in to page", function() {

    }, function() {
      util.action("Login");
    });

    util.step("Wait for login to complete", function() {
      client.waitFor('table tr a', util.TIMEOUT, util.onTimeout("Wait for login to complete"));
    }, function() {
      util.action("Click on element", ['a', userOptions.title]);
    });

    util.step("Wait for app page to load", function() {
      client.waitFor('ol', util.TIMEOUT, util.onTimeout("Wait for app page to load"));
    }, function() {

    });

    //APK Steps begin here -> might have apk uploaded might not!!
    /*
    util.step("Update APK commence", function() {

    }, function() {
      util.action("Click on element", ['a', 'APK']);
    });

    util.step("Wait for APK page to load", function() {
      client.waitFor('thead', util.TIMEOUT, util.onTimeout("Wait for APK page to load"));
    }, function() {
      util.action("Click on element", ['button', ['Upload new APK to Production', 'Upload your first APK to Production']]);
    });

    util.step("Wait for APK box", function() {
      client.waitFor('input[type="file"]', util.TIMEOUT, util.onTimeout("Wait for APK box"));
    }, function() {
      //util.action("Upload APK");
    });

    util.step("Wait for APK upload", function() {
      //client.waitForVisible('#apk_uploading_id', util.TIMEOUT * 10, util.onTimeout("Wait for APK upload"));
    }, function() {

    });
*/
    //Store Listing Steps begin here
    util.step("Update Store Listing commence", function() {

    }, function() {
      util.action("Click on element", ['a', 'Store Listing']);
    });

    util.step("Wait for Store Listing page", function() {
      client.waitFor('select', util.TIMEOUT, util.onTimeout("Wait for Store Listing page"));

    }, function() {
      util.action("Set ids for Store Listing");
      util.action("Fill in Store Listing");
    });

    util.step("Upload screenshots and graphics", function() {

    }, function() {

      var screenshot_tags = {
        phone: 'Phone',
        7: '7-inch tablet',
        10: '10-inch tablet'
      };

      for (tag in screenshot_tags) {
        var pathString = userOptions['screenshots_' + tag];

        if (pathString !== undefined) {
          var label = screenshot_tags[tag];
          util.action('Remove screenshots', [label]);
          var paths = pathString.split(',');

          for (i in paths) {
            var id = ('screenshot_' + label + '_' + i).replace(' ', '_');
            path = paths[i];
            util.action('Upload screenshot', [label, id, path]);
          }
        }
      }

      var graphic_tags = {
        'Hi-res icon': userOptions.hi_res,
        'Feature Graphic': userOptions.feat_graphic,
        'Promo Graphic': userOptions.promo_graphic
      }

      for (tag in graphic_tags) {
        graphicsCount = 0;
        var path = graphic_tags[tag];

        if (path !== undefined) {
          util.action("Remove graphic", [tag]);

          var id = "graphic_" + graphicsCount;
          util.action("Upload graphic", [tag, id, path]);
          graphicsCount++;
        }
      }

    });

    util.step("Wait for screenshots and graphics to finish uploading", function() {

      var waiting_id_list = [];
      splitter = ','

      var graphic_items = [userOptions.hi_res, userOptions.feat_graphic, userOptions.promo_graphic];

      for (i in graphic_items) {
        if (graphic_items[i] !== undefined) {
          waiting_id_list[waiting_id_list.length] = 'graphic_' + i;
        }
      }

      var screenshot_items = {
        "Phone" : userOptions.screenshots_phone,
        "7-inch tablet" : userOptions.screenshots_7,
        "10-inch tablet" : userOptions.screenshots_10
      };

      for (i in screenshot_items) {
        if (screenshot_items[i] !== undefined) {
          for (j in screenshot_items[i].split(',')){
            waiting_id_list[waiting_id_list.length] = ('screenshot_' + i + '_' + j).replace(' ', '_');
          }
        }
      }

      for (i in waiting_id_list) {
        var id = waiting_id_list[i];
        client.waitForVisible('#' + id, util.TIMEOUT * 10,
          util.onVisibleTimeout("Wait for screenshots and graphics to finish uploading id = " + id));
      }

    }, function() {
      util.action("Click on element", ['div', 'Save']);
    });

    util.step("Wait for completely saved document", function() {
      client.waitFor('div[data-notification-type="INFO"][aria-hidden="false"]', util.TIMEOUT, util.onTimeout("Wait for completely saved document"));
    }, function() {

    });

    //Pricing & Distribution Steps begin here
    util.step("Update Pricing & Distribution commence", function() {

    }, function() {
      util.action("Click on element", ['a', 'Pricing & Distribution']);
    });

    util.step("Wait for Pricing & Distribution page", function() {
      client.waitFor('colgroup', util.TIMEOUT, util.onTimeout("Wait for Pricing & Distribution page"));
    }, function() {

      //??
      userOptions['opt in'] = '';

      var attribs = ['education', 'opt in', 'locations'];

      for (option in userOptions) {
        if (attribs.indexOf(option) !== -1) {
          util.action("Fill in Pricing & Distribution information - " + option);
        }
      }

      util.action("Click on element", ['div', 'Save']);
    });

    util.step("Wait for completely saved document", function() {
      client.waitFor('div[data-notification-type="INFO"][aria-hidden="false"]', util.TIMEOUT, util.onTimeout("Wait for completely saved document"));
    }, function() {

    });

  }

  return this;
}

exports = module.exports = UpdateApp;<commit_msg>apk update fix<commit_after>var Util = require("./utils.js")

var UpdateApp = function() {
  var util = new Util();
  this.runScript = function(client, userOptions) {

    util.action("Fill in Store Listing", function() {

      selectFields = ['app_type', 'category', 'rating'];
      textFields = ['subtext', 'promo', 'promo_vid', 'website', 'email', 'phone'];

      for (option in userOptions) {
        if (selectFields.indexOf(option) !== -1) {
          client.click('#selectArea_' + option + ' option[value = ' + userOptions[option] + ']')
        }

        else if (textFields.indexOf(option) !== -1) {
          client.setValue('#textArea_' + option, userOptions[option]);
        }

        else if (option === 'privacy') {
          if (option) {
            client.setValue("#textArea_" + option, userOptions[option]);
          }
          else {
            client.click("#privacy_opt_out_id");
          }
        }
      }
    });

    util.action("Remove graphic", function(title) {

      var upload_id = searchForChild('h5', 'innerText', title, 2, 'input', (title+'_online_id').replace(' ', '_'), client);

      client.execute(function(id) {
        var input = document.getElementById(id);
        var x_divs = input.parentElement.parentElement.getElementsByTagName('div');

        for (i = 0; i < x_divs.length; i++) {
          if (x_divs[i].innerHTML.charCodeAt(0) === 215) {
            x_divs[i].click();
          }
        }
      }, [upload_id]);
    });

    util.action("Upload graphic", function(title, id, path) {

      var upload_id = searchForChild('h5', 'innerText', title, 2, 'input', id+'_upload', client);

      client.chooseFile("#" + upload_id, path, function(err, res){

      });

      client.execute(function(upload_id, waiting_id){
        var input = document.querySelector("#" + upload_id);
        toWatch = input.parentElement.parentElement.children[2];
        toWatch.id = waiting_id;
      }, [upload_id, id]);
    });

    util.action("Remove screenshots", function(tag) {

      client.execute(function(type) {
        var divs = document.querySelectorAll('b');
        var correct_div = null;
        for (var i =0; i <divs.length; i++) {
          if (divs[i].innerText.trim() === type) {
            correct_div = divs[i];
            break;
          }
        }

        var x_divs = correct_div.parentElement.parentElement.getElementsByTagName('div');
        for (i = 0; i < x_divs.length; i++) {
          if (x_divs[i].innerHTML.charCodeAt(0) === 215) {
            x_divs[i].click();
          }
        }
      }, [tag]);
    });

    util.action("Upload screenshot", function(type, id, path) {

      client.execute(function(type, id){

        var divs = document.querySelectorAll('b');
        var correct_div = null;
        for (var i =0; i <divs.length; i++) {
          if (divs[i].innerText.trim() === type) {
            correct_div = divs[i];
            break;
          }
        }
        var parent = correct_div.parentElement.parentElement;
        var inputs = parent.querySelectorAll('input');
        var input = inputs[inputs.length-1];

        input.id = id + '_upload';

        waiting = input.parentElement.parentElement.children[2];
        waiting.id = id;

      }, [type, id]);

      client.chooseFile("#" + id + '_upload', path, function(err, res){

      });
    });

    util.init(client, userOptions);

    util.step("Log in to page", function() {

    }, function() {
      util.action("Login");
    });

    util.step("Wait for login to complete", function() {
      client.waitFor('table tr a', util.TIMEOUT, util.onTimeout("Wait for login to complete"));
    }, function() {
      util.action("Click on element", ['a', userOptions.title]);
    });

    util.step("Wait for app page to load", function() {
      client.waitFor('ol', util.TIMEOUT, util.onTimeout("Wait for app page to load"));
    }, function() {

    });

    //APK Steps begin here - think of better way to do this
    if userOptions.hasOwnProperty("apk_path") {
      util.step("Update APK commence", function() {

      }, function() {
        util.action("Click on element", ['a', 'APK']);
      });

      util.step("Wait for APK page to load", function() {
        client.waitFor('thead', util.TIMEOUT, util.onTimeout("Wait for APK page to load"));
      }, function() {
        util.action("Click on element", ['button', ['Upload new APK to Production', 'Upload your first APK to Production']]);
      });

      util.step("Wait for APK box", function() {
        client.waitFor('input[type="file"]', util.TIMEOUT, util.onTimeout("Wait for APK box"));
      }, function() {
        util.action("Upload APK");
      });

      util.step("Wait for APK upload", function() {
        client.waitForVisible('#apk_uploading_id', util.TIMEOUT * 10, util.onTimeout("Wait for APK upload"));
      }, function() {

      });
    }

    //Store Listing Steps begin here
    util.step("Update Store Listing commence", function() {

    }, function() {
      util.action("Click on element", ['a', 'Store Listing']);
    });

    util.step("Wait for Store Listing page", function() {
      client.waitFor('select', util.TIMEOUT, util.onTimeout("Wait for Store Listing page"));

    }, function() {
      util.action("Set ids for Store Listing");
      util.action("Fill in Store Listing");
    });

    util.step("Upload screenshots and graphics", function() {

    }, function() {

      var screenshot_tags = {
        phone: 'Phone',
        7: '7-inch tablet',
        10: '10-inch tablet'
      };

      for (tag in screenshot_tags) {
        var pathString = userOptions['screenshots_' + tag];

        if (pathString !== undefined) {
          var label = screenshot_tags[tag];
          util.action('Remove screenshots', [label]);
          var paths = pathString.split(',');

          for (i in paths) {
            var id = ('screenshot_' + label + '_' + i).replace(' ', '_');
            path = paths[i];
            util.action('Upload screenshot', [label, id, path]);
          }
        }
      }

      var graphic_tags = {
        'Hi-res icon': userOptions.hi_res,
        'Feature Graphic': userOptions.feat_graphic,
        'Promo Graphic': userOptions.promo_graphic
      }

      for (tag in graphic_tags) {
        graphicsCount = 0;
        var path = graphic_tags[tag];

        if (path !== undefined) {
          util.action("Remove graphic", [tag]);

          var id = "graphic_" + graphicsCount;
          util.action("Upload graphic", [tag, id, path]);
          graphicsCount++;
        }
      }

    });

    util.step("Wait for screenshots and graphics to finish uploading", function() {

      var waiting_id_list = [];
      splitter = ','

      var graphic_items = [userOptions.hi_res, userOptions.feat_graphic, userOptions.promo_graphic];

      for (i in graphic_items) {
        if (graphic_items[i] !== undefined) {
          waiting_id_list[waiting_id_list.length] = 'graphic_' + i;
        }
      }

      var screenshot_items = {
        "Phone" : userOptions.screenshots_phone,
        "7-inch tablet" : userOptions.screenshots_7,
        "10-inch tablet" : userOptions.screenshots_10
      };

      for (i in screenshot_items) {
        if (screenshot_items[i] !== undefined) {
          for (j in screenshot_items[i].split(',')){
            waiting_id_list[waiting_id_list.length] = ('screenshot_' + i + '_' + j).replace(' ', '_');
          }
        }
      }

      for (i in waiting_id_list) {
        var id = waiting_id_list[i];
        client.waitForVisible('#' + id, util.TIMEOUT * 10,
          util.onVisibleTimeout("Wait for screenshots and graphics to finish uploading id = " + id));
      }

    }, function() {
      util.action("Click on element", ['div', 'Save']);
    });

    util.step("Wait for completely saved document", function() {
      client.waitFor('div[data-notification-type="INFO"][aria-hidden="false"]', util.TIMEOUT, util.onTimeout("Wait for completely saved document"));
    }, function() {

    });

    //Pricing & Distribution Steps begin here
    util.step("Update Pricing & Distribution commence", function() {

    }, function() {
      util.action("Click on element", ['a', 'Pricing & Distribution']);
    });

    util.step("Wait for Pricing & Distribution page", function() {
      client.waitFor('colgroup', util.TIMEOUT, util.onTimeout("Wait for Pricing & Distribution page"));
    }, function() {

      //??
      userOptions['opt in'] = '';

      var attribs = ['education', 'opt in', 'locations'];

      for (option in userOptions) {
        if (attribs.indexOf(option) !== -1) {
          util.action("Fill in Pricing & Distribution information - " + option);
        }
      }

      util.action("Click on element", ['div', 'Save']);
    });

    util.step("Wait for completely saved document", function() {
      client.waitFor('div[data-notification-type="INFO"][aria-hidden="false"]', util.TIMEOUT, util.onTimeout("Wait for completely saved document"));
    }, function() {

    });

  }

  return this;
}

exports = module.exports = UpdateApp;<|endoftext|>


<commit_before>/**
 * The reveal.js markdown plugin. Handles parsing of
 * markdown inside of presentations as well as loading
 * of external markdown documents.
 */
(function( root, factory ) {
	if( typeof exports === 'object' ) {
		module.exports = factory( require( './marked' ) );
	}
	else {
		// Browser globals (root is window)
		root.RevealMarkdown = factory( root.marked );
		root.RevealMarkdown.initialize();
	}
}( this, function( marked ) {

	if( typeof marked === 'undefined' ) {
		throw 'The reveal.js Markdown plugin requires marked to be loaded';
	}

	if( typeof hljs !== 'undefined' ) {
		marked.setOptions({
			highlight: function( lang, code ) {
				return hljs.highlightAuto( lang, code ).value;
			}
		});
	}

	var DEFAULT_SLIDE_SEPARATOR = '^\n---\n$',
		DEFAULT_NOTES_SEPARATOR = 'note:',
		DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\\.element\\\s*?(.+?)$',
		DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\\.slide:\\\s*?(\\\S.+?)$';

	/**
	 * Retrieves the markdown contents of a slide section
	 * element. Normalizes leading tabs/whitespace.
	 */
	function getMarkdownFromSlide( section ) {

		var template = section.querySelector( 'script' );

		// strip leading whitespace so it isn't evaluated as code
		var text = ( template || section ).textContent;

		var leadingWs = text.match( /^\n?(\s*)/ )[1].length,
			leadingTabs = text.match( /^\n?(\t*)/ )[1].length;

		if( leadingTabs > 0 ) {
			text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' );
		}
		else if( leadingWs > 1 ) {
			text = text.replace( new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n' );
		}

		return text;

	}

	/**
	 * Given a markdown slide section element, this will
	 * return all arguments that aren't related to markdown
	 * parsing. Used to forward any other user-defined arguments
	 * to the output markdown slide.
	 */
	function getForwardedAttributes( section ) {

		var attributes = section.attributes;
		var result = [];

		for( var i = 0, len = attributes.length; i < len; i++ ) {
			var name = attributes[i].name,
				value = attributes[i].value;

			// disregard attributes that are used for markdown loading/parsing
			if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue;

			if( value ) {
				result.push( name + '=' + value );
			}
			else {
				result.push( name );
			}
		}

		return result.join( ' ' );

	}

	/**
	 * Inspects the given options and fills out default
	 * values for what's not defined.
	 */
	function getSlidifyOptions( options ) {

		options = options || {};
		options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR;
		options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR;
		options.attributes = options.attributes || '';

		return options;

	}

	/**
	 * Helper function for constructing a markdown slide.
	 */
	function createMarkdownSlide( content, options ) {

		options = getSlidifyOptions( options );

		var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) );

		if( notesMatch.length === 2 ) {
			content = notesMatch[0] + '' + notesMatch[1].trim() + '';
		}

		return '';

	}

	/**
	 * Parses a data string into multiple slides based
	 * on the passed in separator arguments.
	 */
	function slidify( markdown, options ) {

		options = getSlidifyOptions( options );

		var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ),
			horizontalSeparatorRegex = new RegExp( options.separator );

		var matches,
			lastIndex = 0,
			isHorizontal,
			wasHorizontal = true,
			content,
			sectionStack = [];

		// iterate until all blocks between separators are stacked up
		while( matches = separatorRegex.exec( markdown ) ) {
			notes = null;

			// determine direction (horizontal by default)
			isHorizontal = horizontalSeparatorRegex.test( matches[0] );

			if( !isHorizontal && wasHorizontal ) {
				// create vertical stack
				sectionStack.push( [] );
			}

			// pluck slide content from markdown input
			content = markdown.substring( lastIndex, matches.index );

			if( isHorizontal && wasHorizontal ) {
				// add to horizontal stack
				sectionStack.push( content );
			}
			else {
				// add to vertical stack
				sectionStack[sectionStack.length-1].push( content );
			}

			lastIndex = separatorRegex.lastIndex;
			wasHorizontal = isHorizontal;
		}

		// add the remaining slide
		( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) );

		var markdownSections = '';

		// flatten the hierarchical stack, and insert
 tags
		for( var i = 0, len = sectionStack.length; i < len; i++ ) {
			// vertical
			if( sectionStack[i] instanceof Array ) {
				markdownSections += '
';

				sectionStack[i].forEach( function( child ) {
					markdownSections += '
' +  createMarkdownSlide( child, options ) + '
';
				} );

				markdownSections += '
';
			}
			else {
				markdownSections += '
' + createMarkdownSlide( sectionStack[i], options ) + '
';
			}
		}

		return markdownSections;

	}

	/**
	 * Parses any current data-markdown slides, splits
	 * multi-slide markdown into separate sections and
	 * handles loading of external markdown.
	 */
	function processSlides() {

		var sections = document.querySelectorAll( '[data-markdown]'),
			section;

		for( var i = 0, len = sections.length; i < len; i++ ) {

			section = sections[i];

			if( section.getAttribute( 'data-markdown' ).length ) {

				var xhr = new XMLHttpRequest(),
					url = section.getAttribute( 'data-markdown' );

				datacharset = section.getAttribute( 'data-charset' );

				// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
				if( datacharset != null && datacharset != '' ) {
					xhr.overrideMimeType( 'text/html; charset=' + datacharset );
				}

				xhr.onreadystatechange = function() {
					if( xhr.readyState === 4 ) {
						// file protocol yields status code 0 (useful for local debug, mobile applications etc.)
						if ( ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 0 ) {

							section.outerHTML = slidify( xhr.responseText, {
								separator: section.getAttribute( 'data-separator' ),
								verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
								notesSeparator: section.getAttribute( 'data-separator-notes' ),
								attributes: getForwardedAttributes( section )
							});

						}
						else {

							section.outerHTML = '
' +
								'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
								'Check your browser\'s JavaScript console for more details.' +
								'
Remember that you need to serve the presentation HTML from a HTTP server.
' +
								'
';

						}
					}
				};

				xhr.open( 'GET', url, false );

				try {
					xhr.send();
				}
				catch ( e ) {
					alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e );
				}

			}
			else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-separator-vertical' ) || section.getAttribute( 'data-separator-notes' ) ) {

				section.outerHTML = slidify( getMarkdownFromSlide( section ), {
					separator: section.getAttribute( 'data-separator' ),
					verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
					notesSeparator: section.getAttribute( 'data-separator-notes' ),
					attributes: getForwardedAttributes( section )
				});

			}
			else {
				section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) );
			}
		}

	}

	/**
	 * Check if a node value has the attributes pattern.
	 * If yes, extract it and add that value as one or several attributes
	 * the the terget element.
	 *
	 * You need Cache Killer on Chrome to see the effect on any FOM transformation
	 * directly on refresh (F5)
	 * http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277
	 */
	function addAttributeInElement( node, elementTarget, separator ) {

		var mardownClassesInElementsRegex = new RegExp( separator, 'mg' );
		var mardownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"=]+?)\"", 'mg' );
		var nodeValue = node.nodeValue;
		if( matches = mardownClassesInElementsRegex.exec( nodeValue ) ) {

			var classes = matches[1];
			nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( mardownClassesInElementsRegex.lastIndex );
			node.nodeValue = nodeValue;
			while( matchesClass = mardownClassRegex.exec( classes ) ) {
				elementTarget.setAttribute( matchesClass[1], matchesClass[2] );
			}
			return true;
		}
		return false;
	}

	/**
	 * Add attributes to the parent element of a text node,
	 * or the element of an attribute node.
	 */
	function addAttributes( section, element, previousElement, separatorElementAttributes, separatorSectionAttributes ) {

		if ( element != null && element.childNodes != undefined && element.childNodes.length > 0 ) {
			previousParentElement = element;
			for( var i = 0; i < element.childNodes.length; i++ ) {
				childElement = element.childNodes[i];
				if ( i > 0 ) {
					j = i - 1;
					while ( j >= 0 ) {
						aPreviousChildElement = element.childNodes[j];
						if ( typeof aPreviousChildElement.setAttribute == 'function' && aPreviousChildElement.tagName != "BR" ) {
							previousParentElement = aPreviousChildElement;
							break;
						}
						j = j - 1;
					}
				}
				parentSection = section;
				if( childElement.nodeName ==  "section" ) {
					parentSection = childElement ;
					previousParentElement = childElement ;
				}
				if ( typeof childElement.setAttribute == 'function' || childElement.nodeType == Node.COMMENT_NODE ) {
					addAttributes( parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes );
				}
			}
		}

		if ( element.nodeType == Node.COMMENT_NODE ) {
			if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) == false ) {
				addAttributeInElement( element, section, separatorSectionAttributes );
			}
		}
	}

	/**
	 * Converts any current data-markdown slides in the
	 * DOM to HTML.
	 */
	function convertSlides() {

		var sections = document.querySelectorAll( '[data-markdown]');

		for( var i = 0, len = sections.length; i < len; i++ ) {

			var section = sections[i];

			// Only parse the same slide once
			if( !section.getAttribute( 'data-markdown-parsed' ) ) {

				section.setAttribute( 'data-markdown-parsed', true )

				var notes = section.querySelector( 'aside.notes' );
				var markdown = getMarkdownFromSlide( section );

				section.innerHTML = marked( markdown );
				addAttributes( 	section, section, null, section.getAttribute( 'data-element-attributes' ) ||
								section.parentNode.getAttribute( 'data-element-attributes' ) ||
								DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,
								section.getAttribute( 'data-attributes' ) ||
								section.parentNode.getAttribute( 'data-attributes' ) ||
								DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR);

				// If there were notes, we need to re-add them after
				// having overwritten the section's HTML
				if( notes ) {
					section.appendChild( notes );
				}

			}

		}

	}

	// API
	return {

		initialize: function() {
			processSlides();
			convertSlides();
		},

		// TODO: Do these belong in the API?
		processSlides: processSlides,
		convertSlides: convertSlides,
		slidify: slidify

	};

}));
<commit_msg>updated md slide separator regex #929<commit_after>/**
 * The reveal.js markdown plugin. Handles parsing of
 * markdown inside of presentations as well as loading
 * of external markdown documents.
 */
(function( root, factory ) {
	if( typeof exports === 'object' ) {
		module.exports = factory( require( './marked' ) );
	}
	else {
		// Browser globals (root is window)
		root.RevealMarkdown = factory( root.marked );
		root.RevealMarkdown.initialize();
	}
}( this, function( marked ) {

	if( typeof marked === 'undefined' ) {
		throw 'The reveal.js Markdown plugin requires marked to be loaded';
	}

	if( typeof hljs !== 'undefined' ) {
		marked.setOptions({
			highlight: function( lang, code ) {
				return hljs.highlightAuto( lang, code ).value;
			}
		});
	}

	var DEFAULT_SLIDE_SEPARATOR = '^\r?\n---\r?\n$',
		DEFAULT_NOTES_SEPARATOR = 'note:',
		DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\\.element\\\s*?(.+?)$',
		DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\\.slide:\\\s*?(\\\S.+?)$';

	/**
	 * Retrieves the markdown contents of a slide section
	 * element. Normalizes leading tabs/whitespace.
	 */
	function getMarkdownFromSlide( section ) {

		var template = section.querySelector( 'script' );

		// strip leading whitespace so it isn't evaluated as code
		var text = ( template || section ).textContent;

		var leadingWs = text.match( /^\n?(\s*)/ )[1].length,
			leadingTabs = text.match( /^\n?(\t*)/ )[1].length;

		if( leadingTabs > 0 ) {
			text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' );
		}
		else if( leadingWs > 1 ) {
			text = text.replace( new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n' );
		}

		return text;

	}

	/**
	 * Given a markdown slide section element, this will
	 * return all arguments that aren't related to markdown
	 * parsing. Used to forward any other user-defined arguments
	 * to the output markdown slide.
	 */
	function getForwardedAttributes( section ) {

		var attributes = section.attributes;
		var result = [];

		for( var i = 0, len = attributes.length; i < len; i++ ) {
			var name = attributes[i].name,
				value = attributes[i].value;

			// disregard attributes that are used for markdown loading/parsing
			if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue;

			if( value ) {
				result.push( name + '=' + value );
			}
			else {
				result.push( name );
			}
		}

		return result.join( ' ' );

	}

	/**
	 * Inspects the given options and fills out default
	 * values for what's not defined.
	 */
	function getSlidifyOptions( options ) {

		options = options || {};
		options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR;
		options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR;
		options.attributes = options.attributes || '';

		return options;

	}

	/**
	 * Helper function for constructing a markdown slide.
	 */
	function createMarkdownSlide( content, options ) {

		options = getSlidifyOptions( options );

		var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) );

		if( notesMatch.length === 2 ) {
			content = notesMatch[0] + '' + notesMatch[1].trim() + '';
		}

		return '';

	}

	/**
	 * Parses a data string into multiple slides based
	 * on the passed in separator arguments.
	 */
	function slidify( markdown, options ) {

		options = getSlidifyOptions( options );

		var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ),
			horizontalSeparatorRegex = new RegExp( options.separator );

		var matches,
			lastIndex = 0,
			isHorizontal,
			wasHorizontal = true,
			content,
			sectionStack = [];

		// iterate until all blocks between separators are stacked up
		while( matches = separatorRegex.exec( markdown ) ) {
			notes = null;

			// determine direction (horizontal by default)
			isHorizontal = horizontalSeparatorRegex.test( matches[0] );

			if( !isHorizontal && wasHorizontal ) {
				// create vertical stack
				sectionStack.push( [] );
			}

			// pluck slide content from markdown input
			content = markdown.substring( lastIndex, matches.index );

			if( isHorizontal && wasHorizontal ) {
				// add to horizontal stack
				sectionStack.push( content );
			}
			else {
				// add to vertical stack
				sectionStack[sectionStack.length-1].push( content );
			}

			lastIndex = separatorRegex.lastIndex;
			wasHorizontal = isHorizontal;
		}

		// add the remaining slide
		( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) );

		var markdownSections = '';

		// flatten the hierarchical stack, and insert
 tags
		for( var i = 0, len = sectionStack.length; i < len; i++ ) {
			// vertical
			if( sectionStack[i] instanceof Array ) {
				markdownSections += '
';

				sectionStack[i].forEach( function( child ) {
					markdownSections += '
' +  createMarkdownSlide( child, options ) + '
';
				} );

				markdownSections += '
';
			}
			else {
				markdownSections += '
' + createMarkdownSlide( sectionStack[i], options ) + '
';
			}
		}

		return markdownSections;

	}

	/**
	 * Parses any current data-markdown slides, splits
	 * multi-slide markdown into separate sections and
	 * handles loading of external markdown.
	 */
	function processSlides() {

		var sections = document.querySelectorAll( '[data-markdown]'),
			section;

		for( var i = 0, len = sections.length; i < len; i++ ) {

			section = sections[i];

			if( section.getAttribute( 'data-markdown' ).length ) {

				var xhr = new XMLHttpRequest(),
					url = section.getAttribute( 'data-markdown' );

				datacharset = section.getAttribute( 'data-charset' );

				// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
				if( datacharset != null && datacharset != '' ) {
					xhr.overrideMimeType( 'text/html; charset=' + datacharset );
				}

				xhr.onreadystatechange = function() {
					if( xhr.readyState === 4 ) {
						// file protocol yields status code 0 (useful for local debug, mobile applications etc.)
						if ( ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 0 ) {

							section.outerHTML = slidify( xhr.responseText, {
								separator: section.getAttribute( 'data-separator' ),
								verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
								notesSeparator: section.getAttribute( 'data-separator-notes' ),
								attributes: getForwardedAttributes( section )
							});

						}
						else {

							section.outerHTML = '
' +
								'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
								'Check your browser\'s JavaScript console for more details.' +
								'
Remember that you need to serve the presentation HTML from a HTTP server.
' +
								'
';

						}
					}
				};

				xhr.open( 'GET', url, false );

				try {
					xhr.send();
				}
				catch ( e ) {
					alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e );
				}

			}
			else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-separator-vertical' ) || section.getAttribute( 'data-separator-notes' ) ) {

				section.outerHTML = slidify( getMarkdownFromSlide( section ), {
					separator: section.getAttribute( 'data-separator' ),
					verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
					notesSeparator: section.getAttribute( 'data-separator-notes' ),
					attributes: getForwardedAttributes( section )
				});

			}
			else {
				section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) );
			}
		}

	}

	/**
	 * Check if a node value has the attributes pattern.
	 * If yes, extract it and add that value as one or several attributes
	 * the the terget element.
	 *
	 * You need Cache Killer on Chrome to see the effect on any FOM transformation
	 * directly on refresh (F5)
	 * http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277
	 */
	function addAttributeInElement( node, elementTarget, separator ) {

		var mardownClassesInElementsRegex = new RegExp( separator, 'mg' );
		var mardownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"=]+?)\"", 'mg' );
		var nodeValue = node.nodeValue;
		if( matches = mardownClassesInElementsRegex.exec( nodeValue ) ) {

			var classes = matches[1];
			nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( mardownClassesInElementsRegex.lastIndex );
			node.nodeValue = nodeValue;
			while( matchesClass = mardownClassRegex.exec( classes ) ) {
				elementTarget.setAttribute( matchesClass[1], matchesClass[2] );
			}
			return true;
		}
		return false;
	}

	/**
	 * Add attributes to the parent element of a text node,
	 * or the element of an attribute node.
	 */
	function addAttributes( section, element, previousElement, separatorElementAttributes, separatorSectionAttributes ) {

		if ( element != null && element.childNodes != undefined && element.childNodes.length > 0 ) {
			previousParentElement = element;
			for( var i = 0; i < element.childNodes.length; i++ ) {
				childElement = element.childNodes[i];
				if ( i > 0 ) {
					j = i - 1;
					while ( j >= 0 ) {
						aPreviousChildElement = element.childNodes[j];
						if ( typeof aPreviousChildElement.setAttribute == 'function' && aPreviousChildElement.tagName != "BR" ) {
							previousParentElement = aPreviousChildElement;
							break;
						}
						j = j - 1;
					}
				}
				parentSection = section;
				if( childElement.nodeName ==  "section" ) {
					parentSection = childElement ;
					previousParentElement = childElement ;
				}
				if ( typeof childElement.setAttribute == 'function' || childElement.nodeType == Node.COMMENT_NODE ) {
					addAttributes( parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes );
				}
			}
		}

		if ( element.nodeType == Node.COMMENT_NODE ) {
			if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) == false ) {
				addAttributeInElement( element, section, separatorSectionAttributes );
			}
		}
	}

	/**
	 * Converts any current data-markdown slides in the
	 * DOM to HTML.
	 */
	function convertSlides() {

		var sections = document.querySelectorAll( '[data-markdown]');

		for( var i = 0, len = sections.length; i < len; i++ ) {

			var section = sections[i];

			// Only parse the same slide once
			if( !section.getAttribute( 'data-markdown-parsed' ) ) {

				section.setAttribute( 'data-markdown-parsed', true )

				var notes = section.querySelector( 'aside.notes' );
				var markdown = getMarkdownFromSlide( section );

				section.innerHTML = marked( markdown );
				addAttributes( 	section, section, null, section.getAttribute( 'data-element-attributes' ) ||
								section.parentNode.getAttribute( 'data-element-attributes' ) ||
								DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,
								section.getAttribute( 'data-attributes' ) ||
								section.parentNode.getAttribute( 'data-attributes' ) ||
								DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR);

				// If there were notes, we need to re-add them after
				// having overwritten the section's HTML
				if( notes ) {
					section.appendChild( notes );
				}

			}

		}

	}

	// API
	return {

		initialize: function() {
			processSlides();
			convertSlides();
		},

		// TODO: Do these belong in the API?
		processSlides: processSlides,
		convertSlides: convertSlides,
		slidify: slidify

	};

}));
<|endoftext|>


<commit_before>'use strict';

var debug               = require('debug-ext')('business-process-data-forms-print')
  , normalizeOptions    = require('es5-ext/object/normalize-options')
  , isString            = require('es5-ext/string/is-string')
  , repeat              = require('es5-ext/string/#/repeat')
  , template            = require('es6-template-strings')
  , encode              = require('ent').encode
  , renderers           = {};

var e = function (data) {
	return (data && isString(data)) ? encode(data) : data;
};

renderers.value = function (data) {
	if (data && data.kind) {
		if (exports.customRenderers[data.kind]) {
			return exports.customRenderers[data.kind](data, renderers);
		}
		debug("Could not resolve " + JSON.stringify(data.kind) + " customisation renderer");
	}
	if (Array.isArray(data)) return e(data.map(renderers.value).join(", "));
	return e(data);
};

renderers.field = function (data) {
	return template('${ label }${ value }	\n', {
		label: e(data.label),
		value: renderers.value(data.value)
	});
};

renderers.fields = function (data) {
	var result  = ''
	  , missing = 0;

	while (data.length) {
		missing = Math.max(4 - data.length, 0);
		result += '
\n' + data.splice(0, 4).map(renderers.field).join('')
			+ repeat.call('	', missing) + '
\n';
	}

	return result;
};

renderers.entity = function (data/*, options*/) {
	var options     = normalizeOptions(arguments[1])
	  , disableName = Boolean(options.disableName)
	  , kind        = data.kind
	  , result;

	delete options.disableName;

	if (kind && exports.customRenderers[kind]) {
		return exports.customRenderers[kind](data, options, renderers);
	}

	result = '';

	if (!disableName && data.name) {
		result += template('
\n${ name }' +
			'	\n
\n', { name: e(data.name) });
	}

	return result + renderers.sections(data.sections, options);
};

renderers.entities = function (data/*, options*/) {
	return data.map(function (entityData) {
		return renderers.entity(entityData, arguments[1]);
	}).join('');
};

renderers.section = function (data/*, options*/) {
	var options      = normalizeOptions(arguments[1])
	  , disableLabel = Boolean(options.disableLabel)
	  , kind         = data.kind
	  , result;

	delete options.disableLabel;

	if (kind && exports.customRenderers[kind]) {
		return exports.customRenderers[kind](data, options, renderers);
	}

	result = '';

	if (!disableLabel && data.label) {
		result += template('
\n${ label }' +
			'	\n
\n', { label: e(data.label) });
	}

	if (data.fields) result += renderers.fields(data.fields);
	if (data.entities) result += renderers.entities(data.entities, options);
	if (data.sections) result += renderers.sections(data.sections, options);

	return result;
};

renderers.sections = function (data/*, options*/) {
	return data.map(function (sectionData) {
		return renderers.section(sectionData, arguments[1]);
	}).join('');
};

renderers.mainSection = function (data/*, options*/) {
	var options      = normalizeOptions(arguments[1])
	  , disableLabel = Boolean(options.disableLabel)
	  , kind         = data.kind
	  , result;

	delete options.disableLabel;

	if (kind && exports.customRenderers[kind]) {
		return exports.customRenderers[kind](data, options, renderers);
	}

	result = '
\n';

	if (!disableLabel && data.label) {
		result += template('
\n
${ label }' +
			'
\n
\n', { label: e(data.label) });
	}

	result += '
\n';
	if (data.fields) result += renderers.fields(data.fields);
	if (data.entities) result += renderers.entities(data.entities, options);
	if (data.sections) result += renderers.sections(data.sections, options);
	result += '
\n';

	return result + '
\n';
};

renderers.mainSections = function (data/*, options*/) {
	return data.map(function (sectionData) {
		return renderers.mainSection(sectionData, arguments[1]);
	}).join('');
};

module.exports = exports = function (dataSnapshot/*, options*/) {
	var options = arguments[1];

	return renderers.mainSections(dataSnapshot.sections, options);
};

exports.renderers = renderers;
exports.customRenderers = {
	fileValue: function (data) {
		return e(data.name);
	}
};
<commit_msg>Issue #1389. Merge empty cells with last full one<commit_after>'use strict';

var debug               = require('debug-ext')('business-process-data-forms-print')
  , normalizeOptions    = require('es5-ext/object/normalize-options')
  , isString            = require('es5-ext/string/is-string')
  , template            = require('es6-template-strings')
  , encode              = require('ent').encode
  , renderers           = {}
  , cellsInARow         = 4;

var e = function (data) {
	return (data && isString(data)) ? encode(data) : data;
};

renderers.value = function (data) {
	if (data && data.kind) {
		if (exports.customRenderers[data.kind]) {
			return exports.customRenderers[data.kind](data, renderers);
		}
		debug("Could not resolve " + JSON.stringify(data.kind) + " customisation renderer");
	}
	if (Array.isArray(data)) return e(data.map(renderers.value).join(", "));
	return e(data);
};

renderers.field = function (data, colspan) {
	return template('${ label }${ value }	\n', {
		colspan: colspan ? 'colspan="' + colspan + '"' : '',
		label: e(data.label),
		value: renderers.value(data.value)
	});
};

renderers.fields = function (data) {
	var result  = '';

	var renderCell = function (cell, index, ar) {
		var missing = Math.max(cellsInARow - ar.length, 0);

		if (missing && (index === ar.length - 1)) return renderers.field(cell, missing + 1);

		return renderers.field(cell);
	};

	while (data.length) {
		result += '
\n' + data.splice(0, cellsInARow).map(renderCell).join('') + '
\n';
	}

	return result;
};

renderers.entity = function (data/*, options*/) {
	var options     = normalizeOptions(arguments[1])
	  , disableName = Boolean(options.disableName)
	  , kind        = data.kind
	  , result;

	delete options.disableName;

	if (kind && exports.customRenderers[kind]) {
		return exports.customRenderers[kind](data, options, renderers);
	}

	result = '';

	if (!disableName && data.name) {
		result += template('
\n${ name }' +
			'	\n
\n', { name: e(data.name) });
	}

	return result + renderers.sections(data.sections, options);
};

renderers.entities = function (data/*, options*/) {
	return data.map(function (entityData) {
		return renderers.entity(entityData, arguments[1]);
	}).join('');
};

renderers.section = function (data/*, options*/) {
	var options      = normalizeOptions(arguments[1])
	  , disableLabel = Boolean(options.disableLabel)
	  , kind         = data.kind
	  , result;

	delete options.disableLabel;

	if (kind && exports.customRenderers[kind]) {
		return exports.customRenderers[kind](data, options, renderers);
	}

	result = '';

	if (!disableLabel && data.label) {
		result += template('
\n${ label }' +
			'	\n
\n', { label: e(data.label) });
	}

	if (data.fields) result += renderers.fields(data.fields);
	if (data.entities) result += renderers.entities(data.entities, options);
	if (data.sections) result += renderers.sections(data.sections, options);

	return result;
};

renderers.sections = function (data/*, options*/) {
	return data.map(function (sectionData) {
		return renderers.section(sectionData, arguments[1]);
	}).join('');
};

renderers.mainSection = function (data/*, options*/) {
	var options      = normalizeOptions(arguments[1])
	  , disableLabel = Boolean(options.disableLabel)
	  , kind         = data.kind
	  , result;

	delete options.disableLabel;

	if (kind && exports.customRenderers[kind]) {
		return exports.customRenderers[kind](data, options, renderers);
	}

	result = '
\n';

	if (!disableLabel && data.label) {
		result += template('
\n
${ label }' +
			'
\n
\n', { label: e(data.label) });
	}

	result += '
\n';
	if (data.fields) result += renderers.fields(data.fields);
	if (data.entities) result += renderers.entities(data.entities, options);
	if (data.sections) result += renderers.sections(data.sections, options);
	result += '
\n';

	return result + '
\n';
};

renderers.mainSections = function (data/*, options*/) {
	return data.map(function (sectionData) {
		return renderers.mainSection(sectionData, arguments[1]);
	}).join('');
};

module.exports = exports = function (dataSnapshot/*, options*/) {
	var options = arguments[1];

	return renderers.mainSections(dataSnapshot.sections, options);
};

exports.renderers = renderers;
exports.customRenderers = {
	fileValue: function (data) {
		return e(data.name);
	}
};
<|endoftext|>


<commit_before>(function($) {

    var ActivityFilters = {};

    ActivityFilters.list = $('.friends-activity-filters-list');

    ActivityFilters.setCookie = function(value) {
        var expires = new Date();
        expires.setHours(24,0,0,0);
        document.cookie = 'activityfilters=' + encodeURIComponent(value) + '; path=/; expires=' + expires.toUTCString();
    };

    ActivityFilters.getCookie = function() {
        var value = document.cookie.match('(^|;) ?activityfilters=([^;]*)(;|$)' );
        return value ? decodeURIComponent(value[2]) : null;
    };

    ActivityFilters.isAllSelected = function() {
        return this.list.find('.friends-activity-filter.inactive').length == 0;
    };

    ActivityFilters.sendUpdate = function(page) {
        if (typeof page == 'undefined') {
            page = 1;
        }

        // Initialize filters array with default values
        var active_filters = {
            categories: 'all',
            search: ''
        };

        // generate a list of active categories if not all categories are active
        if (!this.isAllSelected()) {
            active_filters.categories = this.list
                                            .find('.friends-activity-filter.active')
                                            .map(function(i, e) {
                                                return $(e).data('filter-name');
                                            })
                                            .get();
        }

        active_filters = JSON.stringify(active_filters);

        this.setCookie(active_filters);
        var url = window.location.pathname;
        url = url + (page > 1 ? '?page=' + page : '');

        // initialize options for AJAX request
        var options = {
            url: url,
            data: { filters: active_filters }
        };

        // Send the AJAX request to update the page
        $.request(this.list.data('filter-component'), options);
    };

    ActivityFilters.updateState = function() {
        var active_filters = JSON.parse(this.getCookie());

        if (!active_filters || active_filters.categories == 'all') return false;

        if (active_filters.categories instanceof Array) {
            // update list state
            $('a.friends-activity-filter').each(function() {
                var link = $(this);
                if ($.inArray(link.data('filter-name'), active_filters.categories) > -1) {
                    link.removeClass('inactive').addClass('active');
                }
                else {
                    link.removeClass('active').addClass('inactive');
                }
            });
            return true;
        }
        return false;
    };

    ActivityFilters.listenToPageButtons = function() {
        $('.pagination a').click(function() {
            var page = $(this).attr('href').match(/(\?|&)page=(\d+)/);
            page = page ? page[2] : 1;

            ActivityFilters.sendUpdate(page);

            return false;
        });
    };

    ActivityFilters.listen = function() {
        // If we have a saved filter state, update the newly loaded list to reflect it
        if (this.updateState()) this.sendUpdate();

        $('a.friends-activity-filter-all').click(function() {
            if (ActivityFilters.isAllSelected()) {
                // Deselect all
                ActivityFilters.list.find('a.active').removeClass('active').addClass('inactive');
            }
            else {
                // Select all
                ActivityFilters.list.find('a.inactive').removeClass('inactive').addClass('active');
            }

            ActivityFilters.sendUpdate();
            return false;
        });

        $('a.friends-activity-filter').click(function() {
            var filter = $(this);

            // Toggle active/inactive filter state for display
            if (filter.hasClass('active')) {
                filter.removeClass('active').addClass('inactive');
            }
            else {
                filter.removeClass('inactive').addClass('active');
            }

            ActivityFilters.sendUpdate();
            return false;
        });

        this.listenToPageButtons();
        $('.filtered-activity-list').on('ajaxUpdate', function(e) { ActivityFilters.listenToPageButtons(); });
    };

    ActivityFilters.listen();

})(jQuery);<commit_msg>Allow themes to customize ActivityFilters component interface behavior<commit_after>(function($) {

    var ActivityFilters = {};

    ActivityFilters.filters = {
        categories: 'all',
        search: '',
        sort: ''
    };

    ActivityFilters.list = $('.friends-activity-filters-list');

    ActivityFilters.setCookie = function(value) {
        var expires = new Date();
        expires.setHours(24,0,0,0);
        document.cookie = 'activityfilters=' + encodeURIComponent(value) + '; path=/; expires=' + expires.toUTCString();
    };

    ActivityFilters.getCookie = function() {
        var value = document.cookie.match('(^|;) ?activityfilters=([^;]*)(;|$)' );
        return value ? decodeURIComponent(value[2]) : null;
    };

    ActivityFilters.isAllSelected = function() {
        return this.list.find('.friends-activity-filter.inactive').length == 0;
    };

    ActivityFilters.sendUpdate = function(page) {
        if (typeof page == 'undefined') {
            page = 1;
        }

        // generate a list of active categories if not all categories are active
        if (!this.isAllSelected()) {
            this.filters.categories = this.list
                                            .find('.friends-activity-filter.active')
                                            .map(function(i, e) {
                                                return $(e).data('filter-name');
                                            })
                                            .get();
        }
        else {
            this.filters.categories = 'all';
        }

        var active_filters = JSON.stringify(this.filters);

        this.setCookie(active_filters);
        var url = window.location.pathname;
        url = url + (page > 1 ? '?page=' + page : '');

        // initialize options for AJAX request
        var options = {
            url: url,
            data: { filters: active_filters }
        };

        // Send the AJAX request to update the page
        $.request(this.list.data('filter-component'), options);
    };

    ActivityFilters.updateState = function() {
        var active_filters = JSON.parse(this.getCookie());

        if (!active_filters || active_filters.categories == 'all') return false;

        if (active_filters.categories instanceof Array) {
            // update list state
            $('a.friends-activity-filter').each(function() {
                var link = $(this);
                if ($.inArray(link.data('filter-name'), active_filters.categories) > -1) {
                    link.removeClass('inactive').addClass('active');
                }
                else {
                    link.removeClass('active').addClass('inactive');
                }
            });
            return true;
        }
        return false;
    };

    ActivityFilters.listenToPageButtons = function() {
        $('.pagination a').click(function() {
            var page = $(this).attr('href').match(/(\?|&)page=(\d+)/);
            page = page ? page[2] : 1;

            ActivityFilters.sendUpdate(page);

            return false;
        });
    };

    ActivityFilters.listen = function() {
        // If we have a saved filter state, update the newly loaded list to reflect it
        if (this.updateState()) this.sendUpdate();

        $('a.friends-activity-filter-all').click(function() {
            if (ActivityFilters.isAllSelected()) {
                // Deselect all
                ActivityFilters.list.find('a.active').removeClass('active').addClass('inactive');
            }
            else {
                // Select all
                ActivityFilters.list.find('a.inactive').removeClass('inactive').addClass('active');
            }

            ActivityFilters.sendUpdate();
            return false;
        });

        $('a.friends-activity-filter').click(function() {
            var filter = $(this);

            // Toggle active/inactive filter state for display
            if (filter.hasClass('active')) {
                filter.removeClass('active').addClass('inactive');
            }
            else {
                filter.removeClass('inactive').addClass('active');
            }

            ActivityFilters.sendUpdate();
            return false;
        });

        this.listenToPageButtons();
        $('.filtered-activity-list').on('ajaxUpdate', function(e) { ActivityFilters.listenToPageButtons(); });
    };

    ActivityFilters.listen();

})(jQuery);<|endoftext|>


<commit_before>
import winston from "winston";
import zmq from "zmq";
import _ from "underscore";

import InputVertex from "./InputVertex";
import SK from "../../sk";
import m from "../MagicFilters";

const MAIN_SWITCHER_LABEL = "mainSwitcher";
const SPLIT_SCREEN_TOP_SWITCHER_LABEL = "splitScreenTopSwitcher";
const SPLIT_SCREEN_BOTTOM_SWITCHER_LABEL = "splitScreenBottomSwitcher";
const SPLIT_SCREEN_BOTTOM_CROP_LABEL = "splitScreenBottomCrop";
const SPLIT_SCREEN_BOTTOM_OVERLAY_LABEL = "splitScreenBottomOverlay";

// This is hacky as hell but it doesn't seem to work if I send all messages at once.
const ZMQ_SEND_INTERVAL = 1000;

export default class MagicVertex extends InputVertex {
  constructor({id}) {
    super({id});
    this.rewriteStream = false;
    // this.debug = true;
    this.zmqQueue = [];
    this.zmqIsRunning = false;
    this.videoOutputURL = this.getUDPOutput();
    this.audioOutputURL = this.getUDPOutput();
  }

  cleanup() {
    super.cleanup();
    this.positionVertexHandle.stop();

    if (this.zmqSocket) {
      try {
        this.zmqSocket.unmonitor();
        this.zmqSocket.disconnect(this.zmqAddress);
      }
      catch (e) {
        this.error("Error disconnecting from ZMQ: ", e.stack);
      }
    }
  }

  handleInitialPull() {
    this.currentPositions = this.doc.params.positions;
    this.positionVertexHandle = SK.vertices.watch({id: this.id}).on("updated", ([vertex]) => {
      const newPositions = vertex.params.positions;
      Object.keys(vertex.params.positions).forEach((inputName) => {
        if (!_(this.currentPositions[inputName]).isEqual(newPositions[inputName])) {
          this.currentPositions[inputName] = newPositions[inputName];
          this.doZMQUpdate(inputName);
        }
      });
      this.currentPositions = newPositions;
    });
    this.doc.inputs.forEach((input) => {
      input.sockets.forEach((socket) => {
        socket.url = this.getUDPInput();
      });
    });
    const newVertex = {
      inputs: this.doc.inputs,
      outputs: [{
        name: "default",
        sockets: [{
          url: this.videoOutputURL,
          type: "video",
        }, {
          url: this.audioOutputURL,
          type: "audio",
        }]
      }]

    };
    SK.vertices.update(this.doc.id, newVertex)
    .then(() => {
      this.init();
    })
    .catch((err) => {
      winston.error(err);
    });
  }

  _sendNextZMQMessage() {
    // If there's nothing to do, we're not running anymore.
    if (this.zmqQueue.length === 0) {
      this.zmqIsRunning = false;
      return;
    }
    // If we're not connected yet, chill. We'll get called again when we do.
    if (!this.zmqSocket) {
      this.zmqIsRunning = false;
      return;
    }
    // Otherwise, send a message and queue the next one.
    const msg = this.zmqQueue.pop();
    this.info(`ZMQ: ${msg}`);
    this.zmqSocket.send(msg);
    this.zmqIsRunning = true;
    setTimeout(this._sendNextZMQMessage.bind(this), ZMQ_SEND_INTERVAL);
  }

  doZMQUpdate(inputName) {
    const send = (msg) => {
      this.zmqQueue.push(msg);
    };
    const pos = this.currentPositions[inputName];
    const overlayLabel = this.ffmpeg.filterLabels[`${inputName}-overlay`];
    const scaleLabel = this.ffmpeg.filterLabels[`${inputName}-scale`];
    send(`${overlayLabel} x ${pos.x}`);
    send(`${overlayLabel} y ${pos.y}`);
    send(`${scaleLabel} width ${pos.width}`);
    send(`${scaleLabel} height ${pos.height}`);
    if (!this.zmqIsRunning) {
      this._sendNextZMQMessage();
    }
  }

  init() {
    super.init();
    try {
      this.ffmpeg = this.createffmpeg();
      this.zmqPort = this.getTCP();
      this.zmqAddress = `tcp://*:${this.zmqPort}`;

      const videoInputSockets = [];
      const audioInputSockets = [];
      let currentIdx = 0;

      this.doc.inputs.forEach((input, inputIdx) => {
        input.sockets.forEach((socket, socketIdx) => {
          socket.name = `${input.name}-${socketIdx}`;
          socket.inputName = input.name;
          this.ffmpeg
            .input(socket.url)
            .inputFormat("mpegts")
            .inputOptions([
              "-thread_queue_size 16384",
              // "-avioflags direct",
            ]);

          // Set up video input
          if (socket.type === "video") {
            videoInputSockets.push(socket);
            if (input.name === "background") {
              this.ffmpeg.magic(
                `${currentIdx}:v`,
                m.framerate("30"),
                m.scale(1920, 1080),
                `${socket.name}`
              );
            }
            else {
              const pos = this.doc.params.positions[input.name];
              this.ffmpeg.magic(
                `${currentIdx}:v`,
                m.framerate("30"),
                m.scale([pos.width, pos.height], {_label: `${input.name}-scale`}),
                `${socket.name}`
              );
            }
          }

          // Set up audio input
          else if (socket.type === "audio") {
            audioInputSockets.push(socket);
            this.ffmpeg.magic(
              `${currentIdx}:a`,
              m.volume({
                _label: `${socket.name}-volume`
              }),
              `${socket.name}-adjusted`
            );
          }

          else {
            throw new Error(`Unknown input type: ${input.type}`);
          }
          currentIdx += 1;
        });
      });

      // Do a series of overlays for the input
      let currentOverlayBG = "background-0";
      videoInputSockets.forEach((socket, i) => {
        if (socket.name === currentOverlayBG) {
          // First one doesn't need to overlay onto nothing. Return.
          return;
        }
        const newOverlayBG = `${socket.name}-overlay`;
        const pos = this.doc.params.positions[socket.inputName];
        this.ffmpeg.magic(
          currentOverlayBG,
          socket.name,
          m.overlay({
            x: pos.x,
            y: pos.y,
            _label: `${socket.inputName}-overlay`
          }),
          newOverlayBG
        );
        currentOverlayBG = newOverlayBG;
      });

      this.ffmpeg
        .outputOptions([
          "-copyts",
          "-start_at_zero",
          "-copytb 1",
          "-async 1",
          "-vsync passthrough",
          // "-sws_flags +neighbor",
          "-probesize 2147483647",
          "-pix_fmt yuv420p",
          // "-profile:v baseline",
          // "-use_wallclock_as_timestamps 1",
          "-fflags +igndts",
          "-loglevel verbose",
        ])
        .magic(
          currentOverlayBG,
          m.zmq({bind_address: this.zmqAddress}),
          m.framerate("30"),
          "videoOutput"
        )
        .outputOptions([
          "-map [videoOutput]",
          "-b:v 4000k",
          "-allow_skip_frames 1",
          // "-preset veryfast",
          // "-x264opts keyint=60",
          // "-b:v 4000k",
          // "-minrate 4000k",
          // "-maxrate 4000k",
          // "-bufsize 1835k",
          // "-frame_drop_threshold 60",
        ])
        .once("progress", () => {
          const socket = zmq.socket("req");
          socket.on("connect", (fd, ep) => {
            this.zmqSocket = socket;
            if (!this.zmqIsRunning) {
              this._sendNextZMQMessage();
            }
            let idx = 0;
            let label = this.ffmpeg.filterLabels[MAIN_SWITCHER_LABEL];
          });
          socket.on("connect", (fd, ep) => {this.info("connect, endpoint:", ep);});
          socket.on("connect_delay", (fd, ep) => {this.info("connect_delay, endpoint:", ep);});
          socket.on("connect_retry", (fd, ep) => {this.info("connect_retry, endpoint:", ep);});
          socket.on("listen", (fd, ep) => {this.info("listen, endpoint:", ep);});
          socket.on("bind_error", (fd, ep) => {this.info("bind_error, endpoint:", ep);});
          socket.on("accept", (fd, ep) => {this.info("accept, endpoint:", ep);});
          socket.on("accept_error", (fd, ep) => {this.info("accept_error, endpoint:", ep);});
          socket.on("close", (fd, ep) => {this.info("close, endpoint:", ep);});
          socket.on("close_error", (fd, ep) => {this.info("close_error, endpoint:", ep);});
          socket.on("disconnect", (fd, ep) => {this.info("disconnect, endpoint:", ep);});
          socket.on("message", (msg) => {this.info("message: ", msg.toString());});
          socket.monitor(500, 0);
          socket.connect(`tcp://127.0.0.1:${this.zmqPort}`);
        })
        .output(this.videoOutputURL)
        .outputFormat("mpegts")
        .videoCodec("libopenh264")

        .magic(
          ...audioInputSockets.map(s => `${s.name}-adjusted`),
          m.amix({
            inputs: audioInputSockets.length
          }),
          "audioOutput"
        )

        .output(this.audioOutputURL)
        .outputOptions([
          "-map [audioOutput]"
        ])
        .outputFormat("mpegts")
        .audioCodec("aac");

        // .input(this.inputURL)
        // .inputFormat("mpegts")
        // // .inputOptions("-itsoffset 00:00:05")
        // .outputOptions([
        // ])
        // .videoCodec("libx264")
        // .audioCodec("pcm_s16le")
        // .outputOptions([
        //   "-preset ultrafast",
        //   "-tune zerolatency",
        //   "-x264opts keyint=5:min-keyint=",
        //   "-pix_fmt yuv420p",
        //   "-filter_complex",
        //   [
        //     `[0:a]asetpts='(RTCTIME - ${this.SERVER_START_TIME}) / (TB * 1000000)'[out_audio]`,
        //     `[0:v]setpts='(RTCTIME - ${this.SERVER_START_TIME}) / (TB * 1000000)'[out_video]`,
        //   ].join(";")
        // ])

        // // Video output
        // .output(this.videoOutputURL)
        // .outputOptions([
        //   "-map [out_video]",
        // ])
        // .outputFormat("mpegts")

        // // Audio output
        // .output(this.audioOutputURL)
        // .outputOptions([
        //   "-map [out_audio]",
        // ])
        // .outputFormat("mpegts");

      this.ffmpeg.run();
    }
    catch (err) {
      this.error(err);
      this.retry();
    }
  }
}
<commit_msg>Use manual aresamples instead of the deprecated -async option<commit_after>
import winston from "winston";
import zmq from "zmq";
import _ from "underscore";

import InputVertex from "./InputVertex";
import SK from "../../sk";
import m from "../MagicFilters";

const MAIN_SWITCHER_LABEL = "mainSwitcher";
const SPLIT_SCREEN_TOP_SWITCHER_LABEL = "splitScreenTopSwitcher";
const SPLIT_SCREEN_BOTTOM_SWITCHER_LABEL = "splitScreenBottomSwitcher";
const SPLIT_SCREEN_BOTTOM_CROP_LABEL = "splitScreenBottomCrop";
const SPLIT_SCREEN_BOTTOM_OVERLAY_LABEL = "splitScreenBottomOverlay";

// This is hacky as hell but it doesn't seem to work if I send all messages at once.
const ZMQ_SEND_INTERVAL = 1000;

export default class MagicVertex extends InputVertex {
  constructor({id}) {
    super({id});
    this.rewriteStream = false;
    // this.debug = true;
    this.zmqQueue = [];
    this.zmqIsRunning = false;
    this.videoOutputURL = this.getUDPOutput();
    this.audioOutputURL = this.getUDPOutput();
  }

  cleanup() {
    super.cleanup();
    this.positionVertexHandle.stop();

    if (this.zmqSocket) {
      try {
        this.zmqSocket.unmonitor();
        this.zmqSocket.disconnect(this.zmqAddress);
      }
      catch (e) {
        this.error("Error disconnecting from ZMQ: ", e.stack);
      }
    }
  }

  handleInitialPull() {
    this.currentPositions = this.doc.params.positions;
    this.positionVertexHandle = SK.vertices.watch({id: this.id}).on("updated", ([vertex]) => {
      const newPositions = vertex.params.positions;
      Object.keys(vertex.params.positions).forEach((inputName) => {
        if (!_(this.currentPositions[inputName]).isEqual(newPositions[inputName])) {
          this.currentPositions[inputName] = newPositions[inputName];
          this.doZMQUpdate(inputName);
        }
      });
      this.currentPositions = newPositions;
    });
    this.doc.inputs.forEach((input) => {
      input.sockets.forEach((socket) => {
        socket.url = this.getUDPInput();
      });
    });
    const newVertex = {
      inputs: this.doc.inputs,
      outputs: [{
        name: "default",
        sockets: [{
          url: this.videoOutputURL,
          type: "video",
        }, {
          url: this.audioOutputURL,
          type: "audio",
        }]
      }]

    };
    SK.vertices.update(this.doc.id, newVertex)
    .then(() => {
      this.init();
    })
    .catch((err) => {
      winston.error(err);
    });
  }

  _sendNextZMQMessage() {
    // If there's nothing to do, we're not running anymore.
    if (this.zmqQueue.length === 0) {
      this.zmqIsRunning = false;
      return;
    }
    // If we're not connected yet, chill. We'll get called again when we do.
    if (!this.zmqSocket) {
      this.zmqIsRunning = false;
      return;
    }
    // Otherwise, send a message and queue the next one.
    const msg = this.zmqQueue.pop();
    this.info(`ZMQ: ${msg}`);
    this.zmqSocket.send(msg);
    this.zmqIsRunning = true;
    setTimeout(this._sendNextZMQMessage.bind(this), ZMQ_SEND_INTERVAL);
  }

  doZMQUpdate(inputName) {
    const send = (msg) => {
      this.zmqQueue.push(msg);
    };
    const pos = this.currentPositions[inputName];
    const overlayLabel = this.ffmpeg.filterLabels[`${inputName}-overlay`];
    const scaleLabel = this.ffmpeg.filterLabels[`${inputName}-scale`];
    send(`${overlayLabel} x ${pos.x}`);
    send(`${overlayLabel} y ${pos.y}`);
    send(`${scaleLabel} width ${pos.width}`);
    send(`${scaleLabel} height ${pos.height}`);
    if (!this.zmqIsRunning) {
      this._sendNextZMQMessage();
    }
  }

  init() {
    super.init();
    try {
      this.ffmpeg = this.createffmpeg();
      this.zmqPort = this.getTCP();
      this.zmqAddress = `tcp://*:${this.zmqPort}`;

      const videoInputSockets = [];
      const audioInputSockets = [];
      let currentIdx = 0;

      this.doc.inputs.forEach((input, inputIdx) => {
        input.sockets.forEach((socket, socketIdx) => {
          socket.name = `${input.name}-${socketIdx}`;
          socket.inputName = input.name;
          this.ffmpeg
            .input(socket.url)
            .inputFormat("mpegts")
            .inputOptions([
              "-thread_queue_size 16384",
              // "-avioflags direct",
            ]);

          // Set up video input
          if (socket.type === "video") {
            videoInputSockets.push(socket);
            if (input.name === "background") {
              this.ffmpeg.magic(
                `${currentIdx}:v`,
                m.framerate("30"),
                m.scale(1920, 1080),
                `${socket.name}`
              );
            }
            else {
              const pos = this.doc.params.positions[input.name];
              this.ffmpeg.magic(
                `${currentIdx}:v`,
                m.framerate("30"),
                m.scale([pos.width, pos.height], {_label: `${input.name}-scale`}),
                `${socket.name}`
              );
            }
          }

          // Set up audio input
          else if (socket.type === "audio") {
            audioInputSockets.push(socket);
            this.ffmpeg.magic(
              `${currentIdx}:a`,
              m.aresample({
                async: 1,
                min_hard_comp: 0.100000,
                first_pts: 0
              }),
              m.volume({
                _label: `${socket.name}-volume`
              }),
              `${socket.name}-adjusted`
            );
          }

          else {
            throw new Error(`Unknown input type: ${input.type}`);
          }
          currentIdx += 1;
        });
      });

      // Do a series of overlays for the input
      let currentOverlayBG = "background-0";
      videoInputSockets.forEach((socket, i) => {
        if (socket.name === currentOverlayBG) {
          // First one doesn't need to overlay onto nothing. Return.
          return;
        }
        const newOverlayBG = `${socket.name}-overlay`;
        const pos = this.doc.params.positions[socket.inputName];
        this.ffmpeg.magic(
          currentOverlayBG,
          socket.name,
          m.overlay({
            x: pos.x,
            y: pos.y,
            _label: `${socket.inputName}-overlay`
          }),
          newOverlayBG
        );
        currentOverlayBG = newOverlayBG;
      });

      this.ffmpeg
        .outputOptions([
          "-copyts",
          "-copytb 1",
          "-vsync passthrough",
          // "-sws_flags +neighbor",
          "-probesize 2147483647",
          "-pix_fmt yuv420p",
          // "-profile:v baseline",
          // "-use_wallclock_as_timestamps 1",
          "-fflags +igndts",
          "-loglevel verbose",
        ])
        .magic(
          currentOverlayBG,
          m.zmq({bind_address: this.zmqAddress}),
          m.framerate("30"),
          "videoOutput"
        )
        .outputOptions([
          "-map [videoOutput]",
          "-b:v 4000k",
          "-allow_skip_frames 1",
          // "-preset veryfast",
          // "-x264opts keyint=60",
          // "-b:v 4000k",
          // "-minrate 4000k",
          // "-maxrate 4000k",
          // "-bufsize 1835k",
          // "-frame_drop_threshold 60",
        ])
        .once("progress", () => {
          const socket = zmq.socket("req");
          socket.on("connect", (fd, ep) => {
            this.zmqSocket = socket;
            if (!this.zmqIsRunning) {
              this._sendNextZMQMessage();
            }
            let idx = 0;
            let label = this.ffmpeg.filterLabels[MAIN_SWITCHER_LABEL];
          });
          socket.on("connect", (fd, ep) => {this.info("connect, endpoint:", ep);});
          socket.on("connect_delay", (fd, ep) => {this.info("connect_delay, endpoint:", ep);});
          socket.on("connect_retry", (fd, ep) => {this.info("connect_retry, endpoint:", ep);});
          socket.on("listen", (fd, ep) => {this.info("listen, endpoint:", ep);});
          socket.on("bind_error", (fd, ep) => {this.info("bind_error, endpoint:", ep);});
          socket.on("accept", (fd, ep) => {this.info("accept, endpoint:", ep);});
          socket.on("accept_error", (fd, ep) => {this.info("accept_error, endpoint:", ep);});
          socket.on("close", (fd, ep) => {this.info("close, endpoint:", ep);});
          socket.on("close_error", (fd, ep) => {this.info("close_error, endpoint:", ep);});
          socket.on("disconnect", (fd, ep) => {this.info("disconnect, endpoint:", ep);});
          socket.on("message", (msg) => {this.info("message: ", msg.toString());});
          socket.monitor(500, 0);
          socket.connect(`tcp://127.0.0.1:${this.zmqPort}`);
        })
        .output(this.videoOutputURL)
        .outputFormat("mpegts")
        .videoCodec("libopenh264")

        .magic(
          ...audioInputSockets.map(s => `${s.name}-adjusted`),
          m.amix({
            inputs: audioInputSockets.length
          }),
          "audioOutput"
        )

        .output(this.audioOutputURL)
        .outputOptions([
          "-map [audioOutput]"
        ])
        .outputFormat("mpegts")
        .audioCodec("aac");

        // .input(this.inputURL)
        // .inputFormat("mpegts")
        // // .inputOptions("-itsoffset 00:00:05")
        // .outputOptions([
        // ])
        // .videoCodec("libx264")
        // .audioCodec("pcm_s16le")
        // .outputOptions([
        //   "-preset ultrafast",
        //   "-tune zerolatency",
        //   "-x264opts keyint=5:min-keyint=",
        //   "-pix_fmt yuv420p",
        //   "-filter_complex",
        //   [
        //     `[0:a]asetpts='(RTCTIME - ${this.SERVER_START_TIME}) / (TB * 1000000)'[out_audio]`,
        //     `[0:v]setpts='(RTCTIME - ${this.SERVER_START_TIME}) / (TB * 1000000)'[out_video]`,
        //   ].join(";")
        // ])

        // // Video output
        // .output(this.videoOutputURL)
        // .outputOptions([
        //   "-map [out_video]",
        // ])
        // .outputFormat("mpegts")

        // // Audio output
        // .output(this.audioOutputURL)
        // .outputOptions([
        //   "-map [out_audio]",
        // ])
        // .outputFormat("mpegts");

      this.ffmpeg.run();
    }
    catch (err) {
      this.error(err);
      this.retry();
    }
  }
}
<|endoftext|>


<commit_before>'use strict';
((rb, document, $) => {
  const activeClass = 'uploader-dragged-on';
  const maxFilesClass = 'uploader-max-files';

  // Prevent default drag behaviors
  ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
    document.body.addEventListener(eventName, preventDefaults);
  });

  rb.uploaders = rb.uploaders || {};

  rb.uploaders.addUploader = (uploaderConfig) => {
    if (uploaderConfig &&
        uploaderConfig.selector) {
      const uploader = document.querySelector(uploaderConfig.selector);
      uploader.id = uploader.id || `rb_${rb.getShortId()}`;
      uploaderConfig.id = uploader.id;

      if (!uploader.classList.contains('uploader-expanded')) {
        uploader.setAttribute(rb.aria.role, 'button');
        uploader.setAttribute('tabindex', 0);
      }

      if (!uploader.classList.contains('uploader-mobile')) {
        uploader.addEventListener('click', expandClickHandler);

        ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
          uploader.addEventListener(eventName, preventDefaults);
        });

        ['dragenter', 'dragover'].forEach(eventName => {
          uploader.addEventListener(eventName, draggedOn);
        });

        ['dragleave', 'drop'].forEach(eventName => {
          uploader.addEventListener(eventName, draggedOff);
        });

        uploader.addEventListener('drop', handleDrop);

        // Add scrim elements
        const scrim = document.createElement('div');
        scrim.classList.add('scrim');
        uploader.insertAdjacentElement('afterbegin', scrim);

        // Register drag-related listeners
        const scrimIcon = document.createElement('div');
        scrimIcon.classList.add('scrim_icon');
        scrim.insertAdjacentElement('afterend', scrimIcon);
      }

      rb.uploaders.config = uploaderConfig;

      // Register listener for keyboard input
      $(uploader).find('.uploader_file-label').keydown((e) => {
        // Since the browser's file input UI is hidden, we need to
        // allow the label it's replaced with to be triggered via
        // keyboard.
        if ((e.which === 13 || e.which === 32)) {
          $(e.target).click();
        }
      });

      $(uploader).find('input[type="file"]').change(fileInputOnchange);
    }
  };

  function expandClickHandler(e) {
    const actualUploader =
      e.target.classList.contains('uploader') ? e.target : e.target.closest('.uploader');
    expandUploader(actualUploader);
  }

  function expandUploader(uploader) {
    uploader.classList.add('uploader-expanded');
    uploader.removeAttribute(rb.aria.role);
    uploader.removeAttribute('tabindex');

    uploader.removeEventListener('click', expandClickHandler);
  }

  function fileInputOnchange(e) {
    rb.uploaders.handleFiles(this.files, rb.uploaders.config);
  }

  function preventDefaults(e) {
    e.preventDefault();
    e.stopPropagation();
  }

  function draggedOn(e) {
    const $dropArea =
      $(e.target).hasClass(activeClass) ? $(e.target) : $(e.target).closest('.uploader');

    if (!$dropArea.hasClass(activeClass)) {
      $dropArea.find('.scrim, .scrim_icon').css('z-index', '1');
      $dropArea.addClass(activeClass);
    }
  }

  function draggedOff(e) {
    const $dropArea =
      $(e.target).hasClass(activeClass) ? $(e.target) : $(e.target).closest('.uploader');

    if ($dropArea.hasClass(activeClass)) {
      $dropArea.removeClass(activeClass);
      $dropArea.find('.scrim, .scrim_icon').css('z-index', '-1');
    }
  }

  rb.uploaders.numCanAdd = () => {
    const numFiles = rb.uploaders.files ? rb.uploaders.files.length : 0;
    const maxFiles = rb.uploaders.config.maxFiles || Number.POSITIVE_INFINITY;

    return maxFiles - numFiles;
  };

  function handleDrop(e) {
    if (rb.uploaders.numCanAdd() > 0) {
      const dt = e.dataTransfer;
      const files = dt.files;

      rb.uploaders.handleFiles(files, rb.uploaders.config);
    }
  }

  let uploadProgress = [];
  const progressBar = document.querySelector('.uploader progress');

  function initializeProgress(numFiles) {
    progressBar.value = 0;
    uploadProgress = [];

    for (let i = numFiles; i > 0; i--) {
      uploadProgress.push(0);
    }
  }

  function updateProgress(fileNumber, percent) {
    uploadProgress[fileNumber] = percent;
    const total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length;

    progressBar.value = total;
  }

  rb.uploaders.handleFiles = function handleFiles(files) {
    const numCanAdd = rb.uploaders.numCanAdd();

    const filesToAdd = [...files].slice(0, numCanAdd);

    initializeProgress(filesToAdd.length);

    if (rb.uploaders.config.autoUpload) {
      filesToAdd.forEach(uploadFile);
    }

    filesToAdd.forEach(previewFile);
  };

  function previewFile(file) {
    rb.uploaders.files = rb.uploaders.files || [];

    const reader = new FileReader();
    reader.size = file.size;

    document.getElementById(rb.uploaders.config.id)
      .dispatchEvent(new CustomEvent('rb.uploaders.addingFile'));

    reader.readAsDataURL(file);
    reader.onloadend = () => {
      const img = document.createElement('img');
      img.src = reader.result;
      img.id = `rb_${rb.getShortId()}`;
      img.classList.add('uploader_thumb');

      const f = { id: img.id, file: reader.result, size: reader.size };
      rb.uploaders.files.push(f);

      document.getElementById(rb.uploaders.config.id)
        .dispatchEvent(new CustomEvent('rb.uploaders.fileAdded', { detail: f }));

      const button = document.createElement('button');
      button.dataset.targetId = img.id;
      button.classList.add('uploader_thumbs_remove-button');
      button.innerHTML = '×';
      button.setAttribute(rb.aria.label, 'Remove this image');

      rb.once(button, 'click', (e) => {
        const id = e.target.dataset.targetId;
        const imgToRemove = document.querySelector(`#${id}`);
        const container = e.target.closest('.uploader');

        imgToRemove.parentNode.removeChild(imgToRemove);
        e.target.parentNode.removeChild(e.target);

        if (container.querySelectorAll('.uploader_thumb').length === 0) {
          container.classList.remove('uploader-has-thumbs');
        }

        rb.uploaders.files = rb.uploaders.files.filter((el) => {
          return el.id !== id;
        });

        if (rb.uploaders.numCanAdd() > 0) {
          const $dropArea = $(`#${rb.uploaders.config.id}`);

          $('.uploader #uploader_file-input, .uploader .uploader_file-label')
            .removeAttr('disabled');

          $dropArea.removeClass(maxFilesClass);
        }

        const i = { removedId: id };
        document.getElementById(rb.uploaders.config.id)
          .dispatchEvent(new CustomEvent('rb.uploaders.fileRemoved', { detail: i }));
      });

      document.querySelector('.uploader_thumbs').appendChild(img);
      document.querySelector(`#${img.id}`).insertAdjacentElement('afterend', button);

      document.querySelector(`#${rb.uploaders.config.id}`).classList.add('uploader-has-thumbs');

      if (!rb.uploaders.numCanAdd()) {
        const $dropArea = $(`#${rb.uploaders.config.id}`);

        $('.uploader #uploader_file-input, .uploader .uploader_file-label').attr('disabled', '');

        $dropArea.addClass(maxFilesClass);
      }
    };
  }

  function uploadFile(file, i) {
    let formData = new FormData();
    let xhr = new XMLHttpRequest();

    formData = rb.uploaders.config.prepFormData(formData);

    if (rb.uploaders.config.prepXhr) {
      xhr = rb.uploaders.config.prepXhr(xhr);
    }

    xhr.open('POST', rb.uploaders.config.postUri, true);
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

    xhr.upload.addEventListener('progress', (e) => {
      updateProgress(i, (e.loaded * 100.0 / e.total) || 100);
    });

    xhr.addEventListener('readystatechange', (e) => {
      if (xhr.readyState === 4 && xhr.status === 200) {
        updateProgress(i, 100);
        // rb.uploaders.onSuccess();
      } else if (xhr.readyState === 4 && xhr.status !== 200) {
        // rb.uploaders.onError(e);
      }
    });

    xhr.send(formData);
  }
})(window.rb, document, jQuery);

<commit_msg>feat(Uploaders): Allow native interactions on mobile/tablet.<commit_after>'use strict';
((rb, document, $) => {
  const activeClass = 'uploader-dragged-on';
  const maxFilesClass = 'uploader-max-files';

  // Prevent default drag behaviors
  ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
    document.body.addEventListener(eventName, preventDefaults);
  });

  rb.uploaders = rb.uploaders || {};

  rb.uploaders.addUploader = (uploaderConfig) => {
    if (uploaderConfig &&
        uploaderConfig.selector) {
      const uploader = document.querySelector(uploaderConfig.selector);
      uploader.id = uploader.id || `rb_${rb.getShortId()}`;
      uploaderConfig.id = uploader.id;

      uploaderConfig.isMobile =
        uploaderConfig.isMobile ||
        uploader.classList.contains('uploader-mobile') ||
        document.querySelectorAll('.mobile .uploader, .tablet .uploader').length > 0;

      if (uploaderConfig.isMobile) {
        rb.once(uploader, 'click', () => {
          $(`#${rb.uploaders.config.id} input[type="file"]`).click();
        });
      }

      if (!uploader.classList.contains('uploader-expanded')) {
        uploader.setAttribute(rb.aria.role, 'button');
        uploader.setAttribute('tabindex', 0);
      }

      if (!uploaderConfig.isMobile) {
        uploader.addEventListener('click', expandClickHandler);

        ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
          uploader.addEventListener(eventName, preventDefaults);
        });

        ['dragenter', 'dragover'].forEach(eventName => {
          uploader.addEventListener(eventName, draggedOn);
        });

        ['dragleave', 'drop'].forEach(eventName => {
          uploader.addEventListener(eventName, draggedOff);
        });

        uploader.addEventListener('drop', handleDrop);

        // Add scrim elements
        const scrim = document.createElement('div');
        scrim.classList.add('scrim');
        uploader.insertAdjacentElement('afterbegin', scrim);

        // Register drag-related listeners
        const scrimIcon = document.createElement('div');
        scrimIcon.classList.add('scrim_icon');
        scrim.insertAdjacentElement('afterend', scrimIcon);
      }

      rb.uploaders.config = uploaderConfig;

      // Register listener for keyboard input
      $(uploader).find('.uploader_file-label').keydown((e) => {
        // Since the browser's file input UI is hidden, we need to
        // allow the label it's replaced with to be triggered via
        // keyboard.
        if ((e.which === 13 || e.which === 32)) {
          $(e.target).click();
        }
      });

      $(uploader).find('input[type="file"]').change(fileInputOnchange);
    }
  };

  function expandClickHandler(e) {
    const actualUploader =
      e.target.classList.contains('uploader') ? e.target : e.target.closest('.uploader');
    expandUploader(actualUploader);
  }

  function expandUploader(uploader) {
    uploader.classList.add('uploader-expanded');
    uploader.removeAttribute(rb.aria.role);
    uploader.removeAttribute('tabindex');

    uploader.removeEventListener('click', expandClickHandler);
  }

  function fileInputOnchange(e) {
    if (rb.uploaders.config.isMobile) {
      expandClickHandler(e);
    }

    rb.uploaders.handleFiles(this.files, rb.uploaders.config);
  }

  function preventDefaults(e) {
    e.preventDefault();
    e.stopPropagation();
  }

  function draggedOn(e) {
    const $dropArea =
      $(e.target).hasClass(activeClass) ? $(e.target) : $(e.target).closest('.uploader');

    if (!$dropArea.hasClass(activeClass)) {
      $dropArea.find('.scrim, .scrim_icon').css('z-index', '1');
      $dropArea.addClass(activeClass);
    }
  }

  function draggedOff(e) {
    const $dropArea =
      $(e.target).hasClass(activeClass) ? $(e.target) : $(e.target).closest('.uploader');

    if ($dropArea.hasClass(activeClass)) {
      $dropArea.removeClass(activeClass);
      $dropArea.find('.scrim, .scrim_icon').css('z-index', '-1');
    }
  }

  rb.uploaders.numCanAdd = () => {
    const numFiles = rb.uploaders.files ? rb.uploaders.files.length : 0;
    const maxFiles = rb.uploaders.config.maxFiles || Number.POSITIVE_INFINITY;

    return maxFiles - numFiles;
  };

  function handleDrop(e) {
    if (rb.uploaders.numCanAdd() > 0) {
      const dt = e.dataTransfer;
      const files = dt.files;

      rb.uploaders.handleFiles(files, rb.uploaders.config);
    }
  }

  let uploadProgress = [];
  const progressBar = document.querySelector('.uploader progress');

  function initializeProgress(numFiles) {
    progressBar.value = 0;
    uploadProgress = [];

    for (let i = numFiles; i > 0; i--) {
      uploadProgress.push(0);
    }
  }

  function updateProgress(fileNumber, percent) {
    uploadProgress[fileNumber] = percent;
    const total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length;

    progressBar.value = total;
  }

  rb.uploaders.handleFiles = function handleFiles(files) {
    const numCanAdd = rb.uploaders.numCanAdd();

    const filesToAdd = [...files].slice(0, numCanAdd);

    initializeProgress(filesToAdd.length);

    if (rb.uploaders.config.autoUpload) {
      filesToAdd.forEach(uploadFile);
    }

    filesToAdd.forEach(previewFile);
  };

  function previewFile(file) {
    rb.uploaders.files = rb.uploaders.files || [];

    const reader = new FileReader();
    reader.size = file.size;

    document.getElementById(rb.uploaders.config.id)
      .dispatchEvent(new CustomEvent('rb.uploaders.addingFile'));

    reader.readAsDataURL(file);
    reader.onloadend = () => {
      const img = document.createElement('img');
      img.src = reader.result;
      img.id = `rb_${rb.getShortId()}`;
      img.classList.add('uploader_thumb');

      const f = { id: img.id, file: reader.result, size: reader.size };
      rb.uploaders.files.push(f);

      document.getElementById(rb.uploaders.config.id)
        .dispatchEvent(new CustomEvent('rb.uploaders.fileAdded', { detail: f }));

      const button = document.createElement('button');
      button.dataset.targetId = img.id;
      button.classList.add('uploader_thumbs_remove-button');
      button.innerHTML = '×';
      button.setAttribute(rb.aria.label, 'Remove this image');

      rb.once(button, 'click', (e) => {
        const id = e.target.dataset.targetId;
        const imgToRemove = document.querySelector(`#${id}`);
        const container = e.target.closest('.uploader');

        imgToRemove.parentNode.removeChild(imgToRemove);
        e.target.parentNode.removeChild(e.target);

        if (container.querySelectorAll('.uploader_thumb').length === 0) {
          container.classList.remove('uploader-has-thumbs');
        }

        rb.uploaders.files = rb.uploaders.files.filter((el) => {
          return el.id !== id;
        });

        if (rb.uploaders.numCanAdd() > 0) {
          const $dropArea = $(`#${rb.uploaders.config.id}`);

          $('.uploader #uploader_file-input, .uploader .uploader_file-label')
            .removeAttr('disabled');

          $dropArea.removeClass(maxFilesClass);
        }

        const i = { removedId: id };
        document.getElementById(rb.uploaders.config.id)
          .dispatchEvent(new CustomEvent('rb.uploaders.fileRemoved', { detail: i }));
      });

      document.querySelector('.uploader_thumbs').appendChild(img);
      document.querySelector(`#${img.id}`).insertAdjacentElement('afterend', button);

      document.querySelector(`#${rb.uploaders.config.id}`).classList.add('uploader-has-thumbs');

      if (!rb.uploaders.numCanAdd()) {
        const $dropArea = $(`#${rb.uploaders.config.id}`);

        $('.uploader #uploader_file-input, .uploader .uploader_file-label').attr('disabled', '');

        $dropArea.addClass(maxFilesClass);
      }
    };
  }

  function uploadFile(file, i) {
    let formData = new FormData();
    let xhr = new XMLHttpRequest();

    formData = rb.uploaders.config.prepFormData(formData);

    if (rb.uploaders.config.prepXhr) {
      xhr = rb.uploaders.config.prepXhr(xhr);
    }

    xhr.open('POST', rb.uploaders.config.postUri, true);
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

    xhr.upload.addEventListener('progress', (e) => {
      updateProgress(i, (e.loaded * 100.0 / e.total) || 100);
    });

    xhr.addEventListener('readystatechange', (e) => {
      if (xhr.readyState === 4 && xhr.status === 200) {
        updateProgress(i, 100);
        // rb.uploaders.onSuccess();
      } else if (xhr.readyState === 4 && xhr.status !== 200) {
        // rb.uploaders.onError(e);
      }
    });

    xhr.send(formData);
  }
})(window.rb, document, jQuery);

<|endoftext|>