<commit_before>// Modification of original decisionTree.js for mobile

var treeData,
    branches,
    backendUrl = 'http://loyolalawtech.org/idt/', //url for server to handle logging
    treeId = '1374682207'; //id of the tree, as generated by IDT.

document.addEventListener('deviceready', onDeviceReady, false);

function onDeviceReady(){
    var networkState = navigator.connection.type;
    var states = {};
    if (networkState === 'none'){
        $('.panel-body').html('
You are offline

Your device appears to be offline. ' +
        'You need to be connected to the internet to use this application');
        return;
    } else {
        //Get rid of any previous sessions
        if (window.localStorage.getItem('idt-sess-id')){
            window.localStorage.removeItem('idt-sess-id');
        }
        var branches = [];
        var options = {};
        loadData();
    }
}

//For local testing
//$(document).ready(function () {
//    var branches = [];
//    var options = {};
//    loadData();
//});

function loadData() {
	$.ajax({
		type: 'GET',
		url: 'data/data.xml',
		dataType: 'xml',
		success: function(xml){
			buildNodes(xml, treeId );
		}
	});
}

function TreeBranch() {
	this.id = '';
	this.content = '';
	this.forkIDs = [];
	this.forkLabels = [];
}

function buildNodes(xmlData, id) {
	var maxDepth = 0;
    branches = [];
	treeData = xmlData;
	$(xmlData).find('branch').each(function(){
			var branch = new TreeBranch();
			branch.id = $(this).attr('id');
			branch.content = $(this).find('content').text();
			$(this).find('fork').each(
				function(){
					branch.forkIDs.push( $(this).attr('target') );
					branch.forkLabels.push( $(this).text() );
				}
			);
			branches.push( branch );
			var branchDepthParts = branch.id.split('.');
			if( branchDepthParts.length > maxDepth ){
				maxDepth = branchDepthParts.length;
			}
        });

	var resetText = $(xmlData).find('resetText').text();
    if (resetText !== ''){
        $('#tree-reset').html(resetText);
    } else {
        $('#tree-reset').remove();
    }

    //New code to show description and disclaimer
    $('.app-title, title ').text($(xmlData).find('title').text());
	$('.panel-body').html($(xmlData).find('description').text() );
    var existingUser = '';
    if (window.localStorage.getItem('idt-user')){ //this "user" has has done a tree before
        existingUser = window.localStorage.getItem('idt-user');
    } else {
        existingUser = '';
    }

    if ($(xmlData).find('disclaimer').length){
        $('.panel-footer').html('<button type="button" class="btn btn-info show-disclaimer">Please Read the Disclaimer</button>');

        $('#tree-window .show-disclaimer').on('click', function (event) {
            event.preventDefault();
            $('.panel-body').html($(xmlData).find('disclaimer').text());
            $('.panel-footer').html('
  <input type="checkbox" id="agree"> I agree.
');
            $('#agree').on('change', function (event) {
                $.post(backendUrl + 'private/backend.php', {'action': 'log', 'existing_user': existingUser, 'tree_id': treeId, 'mobile': '1'}, function(data) {
                    var resp = $.parseJSON(data);
                    window.localStorage.setItem('idt-user', resp.userid);
                    window.localStorage.setItem('idt-sess-id', resp.sessid);
                    showBranch(1);
                    $('.page-header').hide();
                    $('.navbar').removeClass('hidden');
                });
            });
        });
    } else {
        $('.panel-footer').html('<button type="button" class="btn btn-info begin-tree">Begin</button>');
        $('#tree-window .begin-tree').on('click', function (event) {
            event.preventDefault();
            $.post(backendUrl + 'private/backend.php', {'action':'log','existing_user':existingUser, 'tree_id': id}, function(data) {
                var resp = $.parseJSON(data);
                window.localStorage.setItem('idt-user', resp.userid);
                window.localStorage.setItem('idt-sess-id', resp.sessid);
                showBranch(1);
                $('.page-header').hide();
                $('.navbar').removeClass('hidden');
            });
        });
    }
    //Attempt to fix iOS 7 status bar issue
    if(window.device && parseFloat(window.device.version) >= 7){
        $('nav').addClass('fix-iOS');
    }
}

function resetActionLinks(){
	$('.decision-links a').unbind('click');
	$('a.back-link').unbind('click');

	$('.decision-links .btn').click(function(e){
		if( !$(this).attr('href') ){
            //JM track here
			showBranch( $(this).attr('id') );
            $.post(backendUrl + 'private/backend.php', {'action': 'progress', 'session_id': window.localStorage.getItem('idt-sess-id'),'last_link': $(this).attr('id')});
		}
	});
	$('a.back-link').click(function(){
        //Go to previous item in xml tree
        var lastQ = $('.panel-body').attr('data-branch').slice(0,-2);
        showBranch(lastQ);
	});
}

function showBranch(id){
    var currentBranch;

	for(var i = 0; i < branches.length; i++ ){
		if( branches[i].id == id ){
			currentBranch = branches[i];
			break;
		}
	}

	var decisionLinksHTML = '
';
	if(currentBranch.id !== '1'){
		decisionLinksHTML += '

   Back

';
    }
    else {
		decisionLinksHTML += '

';
    }

	for(var d = 0; d < currentBranch.forkIDs.length; d++ ){
		var link = '';
		var forkContent = $(treeData).find('branch[id="' + currentBranch.forkIDs[d] + '"]').find('content').text();
		if( forkContent.indexOf('http://') == 0 || forkContent.indexOf('https://') == 0 ){
			link = 'href="' + forkContent + '"';
		}
        if (d === 1){
            decisionLinksHTML += '
' + currentBranch.forkLabels[d] + '
';
        } else {
            decisionLinksHTML += '
' + currentBranch.forkLabels[d] + '
';
        }
	}

	decisionLinksHTML += '
';

    //insert referral link here
    var scanTxt = currentBranch.content.replace('{{','').replace('}}','');
	var branchHTML = '

' + scanTxt + '

';
	$('.panel-body').html(branchHTML).attr('data-branch',currentBranch.id);
    $('.panel-footer').html(decisionLinksHTML);

	resetActionLinks();
}

function addReferralListeners(){

    $('.panel-default').hide();

    $('.ref-toggle').click(function (e){
        $('.ref-chooser').removeClass('hidden');
    });

    //Add listener for user change
    $('#refSubmit').click(function (e) {
        e.preventDefault();
        generateReferralManual($('#zipCode').val(),$('#geoRange').val());

    });

    //Add listener for user referral click
    $('.click-through').click(function (e) {
        e.preventDefault();
        var url = $(this).attr('href');
        var refId = $(this).attr('data-id');
        var user = window.localStorage.getItem('idt-user');
        var sess = window.localStorage.getItem('idt-sess-id');
        var thisIsAPhone = false;
        if ($(this).hasClass('phone-link')){
            thisIsAPhone = true;
        }

        $.post(backendUrl + 'private/backend.php',{'action':'link_click_mobile','referral_id': refId, 'sess_id': sess, 'user_id':user}, function (data){

            if (thisIsAPhone){
                var strip = url.replace(/[^a-zA-Z\d:]/g, '');
                window.open(strip, '_system');
            } else {
                window.open(url, '_system', 'location=yes');
                //navigator.app.loadUrl(url, { openExternal:true });
            }
        });
    });

}

function generateReferralManual(zip, distance){

    var url = backendUrl + 'private/referral_mobile.php?zip=' + zip + '&geo_range=' + distance;
    $('#referral-results').load(url, function (){
        $('#zipCode').val(zip);
        $('#geoRange').val(distance);
        addReferralListeners();
    });
}

function generateReferralGeo() {
    var sessId = window.localStorage.getItem('idt-sess-id');

    $('.panel-body').html('Attempting to get your location.');
    navigator.geolocation.getCurrentPosition(function succcess(position){
        $.post(backendUrl + 'private/backend.php', {
            action: 'update_location',
            lat: position.coords.latitude,
            long: position.coords.longitude,
            user_id: window.localStorage.getItem('idt-user')
        },function (e){
            $('.panel-body').html('Finding referrals for you.');
        });
        $('#referral-results').load(backendUrl + 'private/referral_mobile.php?sess_id=' + sessId, function (){
            addReferralListeners();
            $('#zipCode').attr('placeholder', 'Please Provide Zip Code');
        });
    }, function fail(){
        var zipForm = '
Geolocation is unavailable.
' +
        '<form name="zip_only"><input type="number" name="zip" placeholder="Enter Your Zip Code"><button type="submit">Go</button></form>';
        $('.panel-body').html(zipForm);
        $('form[name="zip_only"]').submit(function (e) {
            e.preventDefault();
            var userZip = $(this).find('input').val();
            generateReferralManual(userZip,'105600');
        });
    },
    { maximumAge: 3000, timeout: 5000, enableHighAccuracy: true }
    );

}

$('.external-link').click(function (e){
    e.preventDefault();
    var url = $(this).attr('href');
    //navigator.app.loadUrl(url, { openExternal:true });
    window.open(url, '_system', 'location=yes');
});

//Any element in panel-body with the class "help-text" will load the help div.

$('.panel-body').on('click', '.help-text', function(e) {
    e.preventDefault();
    var url = $(this).attr('href');
    $('.help-body').load(url);
    $('#help').height(window.innerHeight);
    $('#help').slideDown();
    $('.back-nav').click(function (e){
        e.preventDefault();
        $('#help').slideUp();
    });
});
<commit_msg>Hey, let's have another go with the Status Bar issue again 4<commit_after>// Modification of original decisionTree.js for mobile

var treeData,
    branches,
    backendUrl = 'http://loyolalawtech.org/idt/', //url for server to handle logging
    treeId = '1374682207'; //id of the tree, as generated by IDT.

document.addEventListener('deviceready', onDeviceReady, false);

function onDeviceReady(){
    var networkState = navigator.connection.type;
    var states = {};
    if (networkState === 'none'){
        $('.panel-body').html('
You are offline

Your device appears to be offline. ' +
        'You need to be connected to the internet to use this application');
        return;
    } else {
        //Get rid of any previous sessions
        if (window.localStorage.getItem('idt-sess-id')){
            window.localStorage.removeItem('idt-sess-id');
        }
        var branches = [];
        var options = {};
        loadData();
    }
}

//For local testing
//$(document).ready(function () {
//    var branches = [];
//    var options = {};
//    loadData();
//});

function loadData() {
	$.ajax({
		type: 'GET',
		url: 'data/data.xml',
		dataType: 'xml',
		success: function(xml){
			buildNodes(xml, treeId );
		}
	});
}

function TreeBranch() {
	this.id = '';
	this.content = '';
	this.forkIDs = [];
	this.forkLabels = [];
}

function buildNodes(xmlData, id) {
	var maxDepth = 0;
    branches = [];
	treeData = xmlData;
	$(xmlData).find('branch').each(function(){
			var branch = new TreeBranch();
			branch.id = $(this).attr('id');
			branch.content = $(this).find('content').text();
			$(this).find('fork').each(
				function(){
					branch.forkIDs.push( $(this).attr('target') );
					branch.forkLabels.push( $(this).text() );
				}
			);
			branches.push( branch );
			var branchDepthParts = branch.id.split('.');
			if( branchDepthParts.length > maxDepth ){
				maxDepth = branchDepthParts.length;
			}
        });

	var resetText = $(xmlData).find('resetText').text();
    if (resetText !== ''){
        $('#tree-reset').html(resetText);
    } else {
        $('#tree-reset').remove();
    }

    //New code to show description and disclaimer
    $('.app-title, title ').text($(xmlData).find('title').text());
	$('.panel-body').html($(xmlData).find('description').text() );
    var existingUser = '';
    if (window.localStorage.getItem('idt-user')){ //this "user" has has done a tree before
        existingUser = window.localStorage.getItem('idt-user');
    } else {
        existingUser = '';
    }

    if ($(xmlData).find('disclaimer').length){
        $('.panel-footer').html('<button type="button" class="btn btn-info show-disclaimer">Please Read the Disclaimer</button>');

        $('#tree-window .show-disclaimer').on('click', function (event) {
            event.preventDefault();
            $('.panel-body').html($(xmlData).find('disclaimer').text());
            $('.panel-footer').html('
  <input type="checkbox" id="agree"> I agree.
');
            $('#agree').on('change', function (event) {
                $.post(backendUrl + 'private/backend.php', {'action': 'log', 'existing_user': existingUser, 'tree_id': treeId, 'mobile': '1'}, function(data) {
                    var resp = $.parseJSON(data);
                    window.localStorage.setItem('idt-user', resp.userid);
                    window.localStorage.setItem('idt-sess-id', resp.sessid);
                    showBranch(1);
                    $('.page-header').hide();
                    $('.navbar').removeClass('hidden');
                });
            });
        });
    } else {
        $('.panel-footer').html('<button type="button" class="btn btn-info begin-tree">Begin</button>');
        $('#tree-window .begin-tree').on('click', function (event) {
            event.preventDefault();
            $.post(backendUrl + 'private/backend.php', {'action':'log','existing_user':existingUser, 'tree_id': id}, function(data) {
                var resp = $.parseJSON(data);
                window.localStorage.setItem('idt-user', resp.userid);
                window.localStorage.setItem('idt-sess-id', resp.sessid);
                showBranch(1);
                $('.page-header').hide();
                $('.navbar').removeClass('hidden');
            });
        });
    }
    //Attempt to fix iOS 7 status bar issue
    if(window.device && parseFloat(window.device.version) >= 7){
        alert('yep');
        $('nav').addClass('fix-iOS');
    }
}

function resetActionLinks(){
	$('.decision-links a').unbind('click');
	$('a.back-link').unbind('click');

	$('.decision-links .btn').click(function(e){
		if( !$(this).attr('href') ){
            //JM track here
			showBranch( $(this).attr('id') );
            $.post(backendUrl + 'private/backend.php', {'action': 'progress', 'session_id': window.localStorage.getItem('idt-sess-id'),'last_link': $(this).attr('id')});
		}
	});
	$('a.back-link').click(function(){
        //Go to previous item in xml tree
        var lastQ = $('.panel-body').attr('data-branch').slice(0,-2);
        showBranch(lastQ);
	});
}

function showBranch(id){
    var currentBranch;

	for(var i = 0; i < branches.length; i++ ){
		if( branches[i].id == id ){
			currentBranch = branches[i];
			break;
		}
	}

	var decisionLinksHTML = '
';
	if(currentBranch.id !== '1'){
		decisionLinksHTML += '

   Back

';
    }
    else {
		decisionLinksHTML += '

';
    }

	for(var d = 0; d < currentBranch.forkIDs.length; d++ ){
		var link = '';
		var forkContent = $(treeData).find('branch[id="' + currentBranch.forkIDs[d] + '"]').find('content').text();
		if( forkContent.indexOf('http://') == 0 || forkContent.indexOf('https://') == 0 ){
			link = 'href="' + forkContent + '"';
		}
        if (d === 1){
            decisionLinksHTML += '
' + currentBranch.forkLabels[d] + '
';
        } else {
            decisionLinksHTML += '
' + currentBranch.forkLabels[d] + '
';
        }
	}

	decisionLinksHTML += '
';

    //insert referral link here
    var scanTxt = currentBranch.content.replace('{{','').replace('}}','');
	var branchHTML = '

' + scanTxt + '

';
	$('.panel-body').html(branchHTML).attr('data-branch',currentBranch.id);
    $('.panel-footer').html(decisionLinksHTML);

	resetActionLinks();
}

function addReferralListeners(){

    $('.panel-default').hide();

    $('.ref-toggle').click(function (e){
        $('.ref-chooser').removeClass('hidden');
    });

    //Add listener for user change
    $('#refSubmit').click(function (e) {
        e.preventDefault();
        generateReferralManual($('#zipCode').val(),$('#geoRange').val());

    });

    //Add listener for user referral click
    $('.click-through').click(function (e) {
        e.preventDefault();
        var url = $(this).attr('href');
        var refId = $(this).attr('data-id');
        var user = window.localStorage.getItem('idt-user');
        var sess = window.localStorage.getItem('idt-sess-id');
        var thisIsAPhone = false;
        if ($(this).hasClass('phone-link')){
            thisIsAPhone = true;
        }

        $.post(backendUrl + 'private/backend.php',{'action':'link_click_mobile','referral_id': refId, 'sess_id': sess, 'user_id':user}, function (data){

            if (thisIsAPhone){
                var strip = url.replace(/[^a-zA-Z\d:]/g, '');
                window.open(strip, '_system');
            } else {
                window.open(url, '_system', 'location=yes');
                //navigator.app.loadUrl(url, { openExternal:true });
            }
        });
    });

}

function generateReferralManual(zip, distance){

    var url = backendUrl + 'private/referral_mobile.php?zip=' + zip + '&geo_range=' + distance;
    $('#referral-results').load(url, function (){
        $('#zipCode').val(zip);
        $('#geoRange').val(distance);
        addReferralListeners();
    });
}

function generateReferralGeo() {
    var sessId = window.localStorage.getItem('idt-sess-id');

    $('.panel-body').html('Attempting to get your location.');
    navigator.geolocation.getCurrentPosition(function succcess(position){
        $.post(backendUrl + 'private/backend.php', {
            action: 'update_location',
            lat: position.coords.latitude,
            long: position.coords.longitude,
            user_id: window.localStorage.getItem('idt-user')
        },function (e){
            $('.panel-body').html('Finding referrals for you.');
        });
        $('#referral-results').load(backendUrl + 'private/referral_mobile.php?sess_id=' + sessId, function (){
            addReferralListeners();
            $('#zipCode').attr('placeholder', 'Please Provide Zip Code');
        });
    }, function fail(){
        var zipForm = '
Geolocation is unavailable.
' +
        '<form name="zip_only"><input type="number" name="zip" placeholder="Enter Your Zip Code"><button type="submit">Go</button></form>';
        $('.panel-body').html(zipForm);
        $('form[name="zip_only"]').submit(function (e) {
            e.preventDefault();
            var userZip = $(this).find('input').val();
            generateReferralManual(userZip,'105600');
        });
    },
    { maximumAge: 3000, timeout: 5000, enableHighAccuracy: true }
    );

}

$('.external-link').click(function (e){
    e.preventDefault();
    var url = $(this).attr('href');
    //navigator.app.loadUrl(url, { openExternal:true });
    window.open(url, '_system', 'location=yes');
});

//Any element in panel-body with the class "help-text" will load the help div.

$('.panel-body').on('click', '.help-text', function(e) {
    e.preventDefault();
    var url = $(this).attr('href');
    $('.help-body').load(url);
    $('#help').height(window.innerHeight);
    $('#help').slideDown();
    $('.back-nav').click(function (e){
        e.preventDefault();
        $('#help').slideUp();
    });
});
<|endoftext|>


<commit_before>var Disassembler = Disassembler || {};

(function() {
  'use strict';

  // Pad the number 'n' to with padding character 'z' to width 'width'.
  function pad(n, width, z, tail) {
    z = z || '0';
    n = n + '';
    return n.length >= width ? n : tail ?
      n + new Array(width - n.length + 1).join(z) :
      new Array(width - n.length + 1).join(z) + n;
  }

  var programContainsRoutines = false;

  // An instruction
  var Instruction = function(instruction, address, comment) {

    if (instruction.length != 8 || !instruction.match(/[0-9a-fA-F]{8}/)) {
      throw "Invalid instruction '" + instruction + "'";
    }

    var bytes = function(number) {
      var p = [];

      for (var i = Math.floor(number.length / 2); i-->0;)
        p[i] = number.slice(i * 2, (i + 1) * 2);

      return p;
    }(instruction);

    this.binary = instruction;
    this.address = address;
    this.next = [this.address + 1];

    if (comment !== undefined)
      this.comment = comment;

    var address = bytes[1] + bytes[2] + bytes[3];
    var jumpAddress = parseInt(address, 16) - 8;

    switch (parseInt(bytes[0], 16)) {
    case 0:
      this.mnemonic = 'iuc';
      this.instruction = this.mnemonic.toUpperCase();
      this.desc = 'No operation';
      break;
    case 1:
      this.mnemonic = 'huc';
      this.instruction = this.mnemonic.toUpperCase();
      this.desc = 'Terminate';
      break;
    case 2:
      this.mnemonic = 'buc';
      this.instruction = this.mnemonic.toUpperCase() + '  ' + address;
      this.desc = 'Jump to ' + address;
      this.next = [jumpAddress];
      break;
    case 3:
      this.mnemonic = 'bic';
      this.instruction = this.mnemonic.toUpperCase() + '  ' + address;
      this.desc = 'Jump to ' + address + ' if condition flag is set';
      this.next.push(jumpAddress);
      break;
    case 4:
      this.mnemonic = 'seto 0x' + bytes[1] + ', 0x' +
        bytes[2] + ', 0x' + bytes[3];
      this.instruction = 'SETO ' + bytes[1] + bytes[2] + bytes[3];
      this.desc = 'Set outputs ' + bytes[1] + ' AND ' + bytes[2] + ' XOR ' + bytes[3];
      break;
    case 5:
      this.mnemonic = 'tsti 0x' + bytes[1] + ', 0x' +
        bytes[2] + ', 0x' + bytes[3];
      this.instruction = 'TSTI ' + bytes[1] + bytes[2] + bytes[3];
      this.desc = 'Test input port ' + parseInt(bytes[1], 16) + ' AND ' +
        bytes[2] + ' XOR ' + bytes[3];
      break;
    case 6:
      this.mnemonic = 'bsr';
      this.instruction = this.mnemonic.toUpperCase() + '  ' + address;
      this.desc = 'Call subroutine ' + address;
      this.next = [jumpAddress];
      programContainsRoutines = true; // Shit!
      break;
    case 7:
      this.mnemonic = 'rsr';
      this.instruction = this.mnemonic.toUpperCase();
      this.desc = 'Return from subroutine';
      break;
    case 8:
      this.mnemonic = 'rir';
      this.instruction = this.mnemonic.toUpperCase();
      this.desc = 'Return from interrupt';
      break;
    case 9:
      this.mnemonic = 'sei';
      this.instruction = this.mnemonic.toUpperCase();
      this.desc = 'Enable interrupts';
      break;
    case 10:
      this.mnemonic = 'cli';
      this.instruction = this.mnemonic.toUpperCase();
      this.desc = 'Disable interrupts';
      break;
    default:
      throw "Invalid opcode '" + bytes[0] + "'";
    };

    this.getLabel = function(type) {
      if (this.label === undefined) {
        if (type == 'routine')
          this.label = new RoutineLabel();
        else if (type == 'interrupt')
          this.label = new HandlerLabel();
        else
          this.label = new Label();
      }

      return this.label;
    };

    this.toString = function(instructions) {
      var label = this.label ? this.getLabel().toString() + '\n' : '';
      var string = '        ';

      if (this.next[0] !== this.address + 1) {
        string += this.mnemonic + '  ';

        if (this.address < idtLength) // Interrupt handler
          string += instructions[this.next[0]].getLabel('interrupt').name;
        else if (this.mnemonic == 'bsr')
          string += instructions[this.next[0]].getLabel('routine').name;
        else // Jump instruction
          string += instructions[this.next[0]].getLabel().name;

      } else if (this.next[1] !== undefined) // Branch instruction
        string += this.mnemonic + '  ' + instructions[this.next[1]].getLabel().name;
      else
        string += this.mnemonic;

      if (this.comment) // Add inline comment at character 32
        string = pad(string, 30, ' ', true) + ' ; ' + this.comment;

      return label + string;
    };
  };

  var Section = function(name) {
    this.toString = function() {
      return 'SECTION .' + name;
    };
  };

  var Comment = function(text) {
    this.toString = function() {
      return ';; ' + text;
    };
  };

  var BlankLine = function() {
    this.toString = function() {
      return '  ';
    };
  };

  var _labelCounter = 0; // Used for automatic label naming
  var _routineCounter = 0; // Used for automatic interrupt label naming
  var _handlerCounter = 0; // Used for automatic interrupt label naming

  var Label = function(name) {
    this.name = name ? name : 'label' + _labelCounter++;

    this.toString = function() {
      return this.name + ':';
    };
  };

  var RoutineLabel = function(name) {
    this.name = name ? name : 'subroutine' + _routineCounter++;

    this.toString = function() {
      return this.name + ':';
    };
  };

  var HandlerLabel = function(name) {
    this.name = name ? name : 'irq' + _handlerCounter++;

    this.toString = function() {
      return this.name + ':';
    };
  };

  var $code = $('#code');
  var $errors = $('#errors');
  var $warnings = $('#warnings');
  var $output = $('#output');

  var idtLength = 8; // Interrupt Descriptor Table length (in words)

  // Decode an array of strings, one instruction per string
  var decode = function(text) {

    var instructions = [], idt = [], address = 0, string = '', i = 0;

    try { // Parse instructions
      for (i = 0; i < text.length; i++) {
        string = text[i].trim();

        if (string.length) {
          if (i < idtLength) { // Interrupt descriptor
            idt.push(new Instruction(string, address++,
                                     'Interrupt handler ' + i));
          } else { // Instruction
            instructions.push(new Instruction(string, address++));
          }
        }
      }
    } catch (err) { // Stop decoding on first error
      addError("At line " + i + ": " + err);
    }

    if (programContainsRoutines)
      addWarning("No code visualisation " +
                 "Sorry, the code visualiser can't handle programs which " +
                 "contain subroutines.");

    return {
      instructions: instructions,
      idt: idt
    };
  }

  var instructionsToChart = function(instructions) {
    var definitions = "st=>start: Start\ne=>end: End\n", links = "";

    var instructionDefinition = function(instruction, i) {
      if (instruction.next[1] !== undefined) // Conditional instruction
        return "i" + i + "=>condition: " + instruction.instruction + "\n";
      else // Unconditional instruction
        return "i" + i + "=>operation: " + instruction.instruction + "\n";
    };

    var instructionLinks = function(instruction, i) {
      if (instruction.next[1] !== undefined) { // Conditional instruction
        var links = "";

        if (instruction.next[0] !== -1) // Valid address
          links = "i" + i + "(no)->i" + instruction.next[0] + "\n";
        if (instruction.next[1] !== -1) // Valid address
          links += "i" + i + "(yes, right)->i" + instruction.next[1] + "\n";

        return links
      } else {
        if (instruction.next[0] !== -1) // Valid address
          return "i" + i + "->i" + instruction.next[0] + "\n";
        else
          return "";
      }
    }

    var lastAddress = instructions.length + idtLength, instruction = '';

    if (instructions.length) // Start and end symbols
      links += "st->i" + idtLength + "\ni" + (lastAddress - 1) + "->e\n";

    for (var i = idtLength; i < lastAddress; i++) {
      instruction = instructions[i - idtLength];

      // Check for valid addresses
      if (instruction.next[0] >= lastAddress)
        instruction.next[0] = -1;

      if (instruction.next[1] >= lastAddress)
        instruction.next[1] = -1;

      definitions += instructionDefinition(instruction, i);
      links += instructionLinks(instruction, i);
    }

    return definitions + links;
  }

  var showAssembly = function(data) {
    var instructions = data.instructions;
    var idt = data.idt;
    var prog = [new Comment('Generated assembly, see:'),
                new Comment('    http://chriscummins.cc/disassembler'),
                new Comment(''),
                new BlankLine(),
                new Section('data'),
                new BlankLine()];
    var string = '';

    if (idt.length) // Set interrupt table label
      idt[0].label = new Label('interrupt_vectors');

    idt.forEach(function(e) {
      prog.push(e);
    });

    prog.push(new BlankLine());
    prog.push(new Section('text'));
    prog.push(new BlankLine());

    if (instructions.length) // Set special "start" label
      instructions[0].label = new Label('start');

    instructions.forEach(function(e) {
      prog.push(e);
    });

    // Generate label cross references
    prog.forEach(function(e) {
      e.toString(instructions);
    });

    prog.push(new BlankLine());
    prog.push(new Comment('End of program code'))

    // Display table
    prog.forEach(function(e) {
      addInstruction(e, instructions);
    });
  };

  var _diagram; // The flowchart

  // Display an array of instructions
  var show = function(data) {

    var instructions = data.instructions;
    var idt = data.idt;

    showAssembly(data);

    if (idt.length || instructions.length)
      $('#code-output').show();
    else
      $('#code-output').hide();

    if (!programContainsRoutines && instructions.length) { // Only draw the flowchart if we can
      $('#diagram').show();

      if (_diagram)
        _diagram.clean();

      console.log(instructionsToChart(instructions));
      _diagram = flowchart.parse(instructionsToChart(instructions));
      _diagram.drawSVG('diagram');
    } else {
      $('#diagram').hide();
    }
  };

  var addError = function(msg) {
    $errors.append("
" + msg +
                   "×
");
  };

  var addWarning = function(msg) {
    $warnings.append("
" + msg +
                     "×
");
  };

  var addInstruction = function(instruction, instructions) {
    var addRow = function(address, instruction) {
      $output.append('

' + address +
                     '

' +
                     instruction + '

');
    };

    var address = instruction.address === undefined ? '' :
      pad(instruction.address.toString(16), 8).toUpperCase();
    var lines = instruction.toString(instructions).split('\n');

    if (lines.length > 1) { // Instruction contains label
      addRow('', lines[0]);
      addRow(address, lines[1]);
    } else {
      addRow(address, lines[0]);
    };
  };

  var refresh = function() { // Refresh display
    $errors.html('');
    $warnings.html('');
    $output.html('');
    _labelCounter = 0;
    _routineCounter = 0;
    _handlerCounter = 0;
    programContainsRoutines = false;

    show(decode($code.val().split("\n")));
  };

  // Update as the user types
  $code.bind('input propertychange', function() {
    refresh();
  });

  $('.switch').attr('data-original-title', 'Interrupt Table').tooltip('fixTitle');

  $('#idt-enable-switch').click(function() {
    idtLength = $('#idt-enable')[0].checked ? 8 : 0;
    refresh();
  });

}).call(Disassembler);
<commit_msg>js/disassembler: Only add DATA section if required<commit_after>var Disassembler = Disassembler || {};

(function() {
  'use strict';

  // Pad the number 'n' to with padding character 'z' to width 'width'.
  function pad(n, width, z, tail) {
    z = z || '0';
    n = n + '';
    return n.length >= width ? n : tail ?
      n + new Array(width - n.length + 1).join(z) :
      new Array(width - n.length + 1).join(z) + n;
  }

  var programContainsRoutines = false;

  // An instruction
  var Instruction = function(instruction, address, comment) {

    if (instruction.length != 8 || !instruction.match(/[0-9a-fA-F]{8}/)) {
      throw "Invalid instruction '" + instruction + "'";
    }

    var bytes = function(number) {
      var p = [];

      for (var i = Math.floor(number.length / 2); i-->0;)
        p[i] = number.slice(i * 2, (i + 1) * 2);

      return p;
    }(instruction);

    this.binary = instruction;
    this.address = address;
    this.next = [this.address + 1];

    if (comment !== undefined)
      this.comment = comment;

    var address = bytes[1] + bytes[2] + bytes[3];
    var jumpAddress = parseInt(address, 16) - 8;

    switch (parseInt(bytes[0], 16)) {
    case 0:
      this.mnemonic = 'iuc';
      this.instruction = this.mnemonic.toUpperCase();
      this.desc = 'No operation';
      break;
    case 1:
      this.mnemonic = 'huc';
      this.instruction = this.mnemonic.toUpperCase();
      this.desc = 'Terminate';
      break;
    case 2:
      this.mnemonic = 'buc';
      this.instruction = this.mnemonic.toUpperCase() + '  ' + address;
      this.desc = 'Jump to ' + address;
      this.next = [jumpAddress];
      break;
    case 3:
      this.mnemonic = 'bic';
      this.instruction = this.mnemonic.toUpperCase() + '  ' + address;
      this.desc = 'Jump to ' + address + ' if condition flag is set';
      this.next.push(jumpAddress);
      break;
    case 4:
      this.mnemonic = 'seto 0x' + bytes[1] + ', 0x' +
        bytes[2] + ', 0x' + bytes[3];
      this.instruction = 'SETO ' + bytes[1] + bytes[2] + bytes[3];
      this.desc = 'Set outputs ' + bytes[1] + ' AND ' + bytes[2] + ' XOR ' + bytes[3];
      break;
    case 5:
      this.mnemonic = 'tsti 0x' + bytes[1] + ', 0x' +
        bytes[2] + ', 0x' + bytes[3];
      this.instruction = 'TSTI ' + bytes[1] + bytes[2] + bytes[3];
      this.desc = 'Test input port ' + parseInt(bytes[1], 16) + ' AND ' +
        bytes[2] + ' XOR ' + bytes[3];
      break;
    case 6:
      this.mnemonic = 'bsr';
      this.instruction = this.mnemonic.toUpperCase() + '  ' + address;
      this.desc = 'Call subroutine ' + address;
      this.next = [jumpAddress];
      programContainsRoutines = true; // Shit!
      break;
    case 7:
      this.mnemonic = 'rsr';
      this.instruction = this.mnemonic.toUpperCase();
      this.desc = 'Return from subroutine';
      break;
    case 8:
      this.mnemonic = 'rir';
      this.instruction = this.mnemonic.toUpperCase();
      this.desc = 'Return from interrupt';
      break;
    case 9:
      this.mnemonic = 'sei';
      this.instruction = this.mnemonic.toUpperCase();
      this.desc = 'Enable interrupts';
      break;
    case 10:
      this.mnemonic = 'cli';
      this.instruction = this.mnemonic.toUpperCase();
      this.desc = 'Disable interrupts';
      break;
    default:
      throw "Invalid opcode '" + bytes[0] + "'";
    };

    this.getLabel = function(type) {
      if (this.label === undefined) {
        if (type == 'routine')
          this.label = new RoutineLabel();
        else if (type == 'interrupt')
          this.label = new HandlerLabel();
        else
          this.label = new Label();
      }

      return this.label;
    };

    this.toString = function(instructions) {
      var label = this.label ? this.getLabel().toString() + '\n' : '';
      var string = '        ';

      if (this.next[0] !== this.address + 1) {
        string += this.mnemonic + '  ';

        if (this.address < idtLength) // Interrupt handler
          string += instructions[this.next[0]].getLabel('interrupt').name;
        else if (this.mnemonic == 'bsr')
          string += instructions[this.next[0]].getLabel('routine').name;
        else // Jump instruction
          string += instructions[this.next[0]].getLabel().name;

      } else if (this.next[1] !== undefined) // Branch instruction
        string += this.mnemonic + '  ' + instructions[this.next[1]].getLabel().name;
      else
        string += this.mnemonic;

      if (this.comment) // Add inline comment at character 32
        string = pad(string, 30, ' ', true) + ' ; ' + this.comment;

      return label + string;
    };
  };

  var Section = function(name) {
    this.toString = function() {
      return 'SECTION .' + name;
    };
  };

  var Comment = function(text) {
    this.toString = function() {
      return ';; ' + text;
    };
  };

  var BlankLine = function() {
    this.toString = function() {
      return '  ';
    };
  };

  var _labelCounter = 0; // Used for automatic label naming
  var _routineCounter = 0; // Used for automatic interrupt label naming
  var _handlerCounter = 0; // Used for automatic interrupt label naming

  var Label = function(name) {
    this.name = name ? name : 'label' + _labelCounter++;

    this.toString = function() {
      return this.name + ':';
    };
  };

  var RoutineLabel = function(name) {
    this.name = name ? name : 'subroutine' + _routineCounter++;

    this.toString = function() {
      return this.name + ':';
    };
  };

  var HandlerLabel = function(name) {
    this.name = name ? name : 'irq' + _handlerCounter++;

    this.toString = function() {
      return this.name + ':';
    };
  };

  var $code = $('#code');
  var $errors = $('#errors');
  var $warnings = $('#warnings');
  var $output = $('#output');

  var idtLength = 8; // Interrupt Descriptor Table length (in words)

  // Decode an array of strings, one instruction per string
  var decode = function(text) {

    var instructions = [], idt = [], address = 0, string = '', i = 0;

    try { // Parse instructions
      for (i = 0; i < text.length; i++) {
        string = text[i].trim();

        if (string.length) {
          if (i < idtLength) { // Interrupt descriptor
            idt.push(new Instruction(string, address++,
                                     'Interrupt handler ' + i));
          } else { // Instruction
            instructions.push(new Instruction(string, address++));
          }
        }
      }
    } catch (err) { // Stop decoding on first error
      addError("At line " + i + ": " + err);
    }

    if (programContainsRoutines)
      addWarning("No code visualisation " +
                 "Sorry, the code visualiser can't handle programs which " +
                 "contain subroutines.");

    return {
      instructions: instructions,
      idt: idt
    };
  }

  var instructionsToChart = function(instructions) {
    var definitions = "st=>start: Start\ne=>end: End\n", links = "";

    var instructionDefinition = function(instruction, i) {
      if (instruction.next[1] !== undefined) // Conditional instruction
        return "i" + i + "=>condition: " + instruction.instruction + "\n";
      else // Unconditional instruction
        return "i" + i + "=>operation: " + instruction.instruction + "\n";
    };

    var instructionLinks = function(instruction, i) {
      if (instruction.next[1] !== undefined) { // Conditional instruction
        var links = "";

        if (instruction.next[0] !== -1) // Valid address
          links = "i" + i + "(no)->i" + instruction.next[0] + "\n";
        if (instruction.next[1] !== -1) // Valid address
          links += "i" + i + "(yes, right)->i" + instruction.next[1] + "\n";

        return links
      } else {
        if (instruction.next[0] !== -1) // Valid address
          return "i" + i + "->i" + instruction.next[0] + "\n";
        else
          return "";
      }
    }

    var lastAddress = instructions.length + idtLength, instruction = '';

    if (instructions.length) // Start and end symbols
      links += "st->i" + idtLength + "\ni" + (lastAddress - 1) + "->e\n";

    for (var i = idtLength; i < lastAddress; i++) {
      instruction = instructions[i - idtLength];

      // Check for valid addresses
      if (instruction.next[0] >= lastAddress)
        instruction.next[0] = -1;

      if (instruction.next[1] >= lastAddress)
        instruction.next[1] = -1;

      definitions += instructionDefinition(instruction, i);
      links += instructionLinks(instruction, i);
    }

    return definitions + links;
  }

  var showAssembly = function(data) {
    var instructions = data.instructions;
    var idt = data.idt;
    var prog = [new Comment('Generated assembly, see:'),
                new Comment('    http://chriscummins.cc/disassembler'),
                new Comment(''),
                new BlankLine()];
    var string = '';

    if (idt.length) { // Show interrupt table label
      prog.push(new Section('data'));
      prog.push(new BlankLine());

      idt[0].label = new Label('interrupt_vectors');
      idt.forEach(function(e) {
        prog.push(e);
      });

      prog.push(new BlankLine());
    }

    prog.push(new Section('text'));
    prog.push(new BlankLine());

    if (instructions.length) // Set special "start" label
      instructions[0].label = new Label('start');

    instructions.forEach(function(e) {
      prog.push(e);
    });

    // Generate label cross references
    prog.forEach(function(e) {
      e.toString(instructions);
    });

    prog.push(new BlankLine());
    prog.push(new Comment('End of program code'))

    // Display table
    prog.forEach(function(e) {
      addInstruction(e, instructions);
    });
  };

  var _diagram; // The flowchart

  // Display an array of instructions
  var show = function(data) {

    var instructions = data.instructions;
    var idt = data.idt;

    showAssembly(data);

    if (idt.length || instructions.length)
      $('#code-output').show();
    else
      $('#code-output').hide();

    if (!programContainsRoutines && instructions.length) { // Only draw the flowchart if we can
      $('#diagram').show();

      if (_diagram)
        _diagram.clean();

      console.log(instructionsToChart(instructions));
      _diagram = flowchart.parse(instructionsToChart(instructions));
      _diagram.drawSVG('diagram');
    } else {
      $('#diagram').hide();
    }
  };

  var addError = function(msg) {
    $errors.append("
" + msg +
                   "×
");
  };

  var addWarning = function(msg) {
    $warnings.append("
" + msg +
                     "×
");
  };

  var addInstruction = function(instruction, instructions) {
    var addRow = function(address, instruction) {
      $output.append('

' + address +
                     '

' +
                     instruction + '

');
    };

    var address = instruction.address === undefined ? '' :
      pad(instruction.address.toString(16), 8).toUpperCase();
    var lines = instruction.toString(instructions).split('\n');

    if (lines.length > 1) { // Instruction contains label
      addRow('', lines[0]);
      addRow(address, lines[1]);
    } else {
      addRow(address, lines[0]);
    };
  };

  var refresh = function() { // Refresh display
    $errors.html('');
    $warnings.html('');
    $output.html('');
    _labelCounter = 0;
    _routineCounter = 0;
    _handlerCounter = 0;
    programContainsRoutines = false;

    show(decode($code.val().split("\n")));
  };

  // Update as the user types
  $code.bind('input propertychange', function() {
    refresh();
  });

  $('.switch').attr('data-original-title', 'Interrupt Table').tooltip('fixTitle');

  $('#idt-enable-switch').click(function() {
    idtLength = $('#idt-enable')[0].checked ? 8 : 0;
    refresh();
  });

}).call(Disassembler);
<|endoftext|>


<commit_before>/**
 * Create a new WebRTC Client object.
 * @author Erin Spiceland <espiceland@digium.com>
 * @class webrtc.Client
 * @constructor
 * @augments webrtc.EventEmitter
 * @classdesc This is a top-level interface to the API. It handles authenticating the app to the
 * API server and receiving server-side app-specific information.
 * @param {object} params Object whose properties will be used to initialize this object and set
 * properties on the class.
 * @returns {webrtc.Client}
 * @property {object} clientSettings Client settings.
 * @property {webrtc.SignalingChannel} signalingChannel A reference to the signaling channel.
 * @property {webrtc.IdentityProvider} identityProvider A reference to the identity provider.
 * @property {function} chatMessage Class to use for chat messages.
 * @property {function} signalingMessage Class to use for signaling.
 * @property {function} presenceMessage Class to use for presence messages.
 * @property {webrtc.User} user Logged-in user's User object.
 */
/*global webrtc: false */
webrtc.Client = function (params) {
    "use strict";
    params = params || {};
    var client = webrtc.makeUniqueID().toString();
    var that = webrtc.EventEmitter(params);
    webrtc.instances[client] = that;
    that.className = 'webrtc.Client';

    var host = window.location.hostname;
    var port = window.location.port;
    var connected = false;
    var app = {};
    var signalingChannel = null;
    var turnRefresher = null;
    var clientSettings = params.clientSettings || {};
    var groups = [];
    var endpoints = [];
    var onMessage = function () {};
    var onCall = function () {};
    var onGroupJoin = function () {};
    var onGroupLeave = function () {};
    var onConnect = function () {};
    var onDisconnect = function () {};
    var onReconnect = function () {};

    if (!clientSettings.appId) {
        throw new Error("appId is a required parameter to Client.");
    }
    app.appId = clientSettings.appId;
    delete clientSettings.appId;

    log.debug("Client ID is ", client);

    var callSettings = {
        constraints: params.constraints || {
            video : true,
            audio : true,
            optional: [],
            mandatory: {}
        },
        servers: params.servers || {
            iceServers: []
        }
    };

    signalingChannel = webrtc.SignalingChannel({'client': client});
    that.identityProvider = webrtc.IdentityProvider({'client': client});
    that.user = null;
    log.debug(signalingChannel);

    /**
     * Connect to the Digium infrastructure and authenticate using the appkey.  Store
     * a token to be used in API requests.
     * @memberof! webrtc.Client
     * @method webrtc.Client.connect
     * @param {string} authToken
     */
    var connect = that.publicize('connect', function (params) {
        params = params || {};
        var deferred = webrtc.makeDeferred(params.onSuccess, params.onError);

        app.authToken = params.authToken;
        signalingChannel.open({
            appId: app.appId,
            token: app.authToken
        }).then(function () {
            return signalingChannel.authenticate({
                appId: app.appId
            });
        }, function (err) {
            deferred.reject("Couldn't connect to brightstream.");
            log.error(err.message);
        }).done(function (user) {
            connected = true;

            onMessage = params.onMessage || onMessage;
            onCall = params.onCall || onCall;
            onGroupJoin = params.onGroupJoin || onGroupJoin;
            onGroupLeave = params.onGroupLeave || onGroupLeave;
            onConnect = params.onConnect || onConnect;
            onDisconnect = params.onDisconnect || onDisconnect;
            onReconnect = params.onReconnect || onReconnect;

            user.setOnline(); // Initiates presence.
            user.listen('call', onCall);
            user.listen('join', onGroupJoin);
            user.listen('leave', onGroupLeave);
            that.listen('connect', onConnect);
            that.listen('disconnect', onDisconnect);
            that.listen('reconnect', onReconnect);
            that.user = user;
            log.info('logged in as user ' + user.getName());
            log.debug(user);
            //updateTurnCredentials(); // TODO fix TURN credentials with Endpoints instead of Users.

            that.fire('connect', user);
            deferred.resolve(user);
        }, function (err) {
            deferred.reject("Couldn't create an endpoint.");
            log.error(err.message);
        });
        return deferred.promise;
    });

    /**
     * Disconnect from the Digium infrastructure. Invalidates the API token.
     * @memberof! webrtc.Client
     * @method webrtc.Client.disconnect
     */
    var disconnect = that.publicize('disconnect', function () {
        // TODO: also call this on socket disconnect
        var disconnectPromise = signalingChannel.close();
        disconnectPromise.then(function () {
            connected = false;
        });
        return disconnectPromise;
    });

    /**
     * Get the client ID
     * @memberof! webrtc.Client
     * @method webrtc.Client.getID
     * @return {string}
     */
    var getID = that.publicize('getID', function () {
        return client;
    });

    /**
     * Update TURN credentials and set a timeout to do it again in 20 hours.
     * @memberof! webrtc.Client
     * @method webrtc.Client.updateTurnCredentials
     */
    var updateTurnCredentials = that.publicize('updateTurnCredentials', function () {
        if (callSettings.disableTurn === true) {
            return;
        }

        clearInterval(turnRefresher);
        signalingChannel.getTurnCredentials().done(function successHandler(creds) {
            callSettings.servers.iceServers = creds;
        }, function errorHandler(error) {
            throw error;
        });
        turnRefresher = setInterval(updateTurnCredentials, 20 * (60 * 60 * 1000)); // 20 hours
    });

    /**
     * Determine whether the Client has authenticated with its appKey against Digium services
     * by checking the validity of the apiToken.
     * @memberof! webrtc.Client
     * @method webrtc.Client.isConnected
     * @returns {boolean}
     */
    var isConnected = that.publicize('isConnected', function () {
        return !!connected;
    });

    /**
     * Get an object containing the client settings.
     * @memberof! webrtc.Client
     * @method webrtc.Client.getClientSettings
     * @returns {object} An object containing the client settings.
     */
    var getClientSettings = that.publicize('getClientSettings', function () {
        return clientSettings;
    });

    /**
     * Get an object containing the default media constraints and other media settings.
     * @memberof! webrtc.Client
     * @method webrtc.Client.getCallSettings
     * @returns {object} An object containing the media settings which will be used in
     * webrtc calls.
     */
    var getCallSettings = that.publicize('getCallSettings', function () {
        return callSettings;
    });

    /**
     * Set the default media constraints and other media settings.
     * @memberof! webrtc.Client
     * @method webrtc.Client.setDefaultCallSettings
     * @param {object} Object containing settings to modify.
     */
    var setDefaultCallSettings = that.publicize('setDefaultCallSettings', function (params) {
        params = params || {};
        if (params.constraints) {
            callSettings.constraints = params.constraints;
        }
        if (params.servers) {
            callSettings.servers = params.servers;
        }
    });

    /**
     * Get the SignalingChannel.
     * @memberof! webrtc.Client
     * @method webrtc.Client.getSignalingChannel
     * @returns {webrtc.SignalingChannel} The instance of the webrtc.SignalingChannel.
     */
    var getSignalingChannel = that.publicize('getSignalingChannel', function () {
        return signalingChannel;
    });

    /**
     * Get a Group
     * @memberof! webrtc.Client
     * @method webrtc.Client.join
     * @params {string} The name of the group.
     * @returns {webrtc.Group} The instance of the webrtc.Group which the user joined.
     */
    var join = that.publicize('join', function (params) {
        var deferred = webrtc.makeDeferred(params.onSuccess, params.onError);
        if (!params.id) {
            deferred.reject(new Error("Can't join a group with no group id."));
            return deferred.promise;
        }

        signalingChannel.joinGroup({
            id: params.id
        }).done(function () {
            var group = webrtc.Group({
                client: client,
                id: params.id,
                onMessage: params.onMessage,
                onEnter: params.onEnter,
                onLeave: params.onLeave,
                onPresence: params.onPresence
            });
            addGroup(group);
            deferred.resolve(group);
        }, function (err) {
            deferred.reject(err);
        });
        return deferred.promise;
    });

    /**
     * Add a Group
     * @memberof! webrtc.Client
     * @method webrtc.Client.addGroup
     * @params {id} the Group id
     * @private
     */
    var addGroup = function (newGroup) {
        var group;
        if (!newGroup || newGroup.className !== 'webrtc.Group') {
            throw new Error("Can't add group to internal tracking without a group.");
        }
        groups.every(function (grp) {
            if (grp.id === newGroup.id) {
                group = grp;
                return false;
            }
            return true;
        });

        if (!group) {
            groups.push(newGroup);
        }
    };

    /**
     * Remove a Group
     * @memberof! webrtc.Client
     * @method webrtc.Client.removeGroup
     * @params {id} the Group id
     * @private
     */
    var removeGroup = function (newGroup) {
        var index;
        var endpoints;
        if (!newGroup || !(newGroup instanceof webrtc.Group)) {
            throw new Error("Can't remove group to internal tracking without a group.");
        }

        groups.every(function (grp, i) {
            if (grp.id === newGroup.id) {
                index = i;
                return false;
            }
            return true;
        });

        if (index > -1) {
            groups[index].getEndpoints().done(function (list) {
                endpoints = list;
                groups.splice(index, 1);
                endpoints.forEach(function (endpoint) {
                    checkEndpointForRemoval(endpoint);
                });
            });
        }
    };

    /**
     * Find a group by id and return it.
     * @memberof! webrtc.Client
     * @method webrtc.Client.getGroup
     * @params {id} the Group id
     * @returns {webrtc.Group}
     */
    var getGroup = that.publicize('getGroup', function (params) {
        var group;
        if (!params || !params.id) {
            throw new Error("Can't get a group without group id.");
        }

        groups.every(function (grp) {
            if (grp.id === params.id) {
                group = grp;
                return false;
            }
            return true;
        });
        return group;
    });

    /**
     * Add an Endpoint
     * @memberof! webrtc.Client
     * @method webrtc.Client.addEndpoint
     * @params {id} the Endpoint id
     * @private
     */
    var addEndpoint = that.publicize('addEndpoint', function (newEndpoint) {
        var absent = false;
        if (!newEndpoint || !newEndpoint.id) {
            throw new Error("Can't add endpoint to internal tracking. No endpoint given.");
        }
        console.log('addEndpoints before', endpoints);
        absent = endpoints.every(function (ept) {
            if (ept.id === newEndpoint.id) {
                console.log('addEndpoints false');
                return false;
            }
            console.log('addEndpoints true');
            return true;
        });

        console.log('addEndpoints checking absent', absent);
        if (absent) {
            endpoints.push(newEndpoint);
        }
        console.log('addEndpoints after', endpoints);
    });

    /**
     * Remove an Endpoint
     * @memberof! webrtc.Client
     * @method webrtc.Client.removeEndpoint
     * @params {id} the Endpoint id
     * @private
     */
    var checkEndpointForRemoval = that.publicize('checkEndpointForRemoval', function (params) {
        var inAGroup;
        var index;
        if (!params || !params.id) {
            throw new Error("Can't remove endpoint from internal tracking without group id.");
        }

        console.log('checkEndpointForRemoval before', endpoints);
        Q.all(groups.map(function (group) {
            return group.getEndpoints();
        })).done(function (groupEndpoints) {
            groupEndpoints.forEach(function (endpoints) {
                endpoints.forEach(function (endpoint) {
                    if (endpoint.id === params.id) {
                        inAGroup = true;
                    }
                });
            });
            if (inAGroup) {
                endpoints.every(function (ept, i) {
                    if (ept.id === params.id) {
                        index = i;
                        return false;
                    }
                    return true;
                });
                if (index > -1) {
                    endpoints.splice(index, 1);
                }
            }
            console.log('checkEndpointForRemoval after', endpoints);
        });
    });

    /**
     * Find an endpoint by id and return it.
     * @memberof! webrtc.Client
     * @method webrtc.Client.getEndpoint
     * @params {id} the Endpoint id
     * @returns {webrtc.Contact}
     */
    var getEndpoint = that.publicize('getEndpoint', function (params) {
        var endpoint;
        if (!params || !params.id) {
            throw new Error("Can't get an endpoint without group id.");
        }

        console.log("looking for", params.id, 'in', endpoints);
        endpoints.every(function (ept) {
            console.log('comparing', ept.id, params.id);
            if (ept.id === params.id) {
                endpoint = ept;
                return false;
            }
            return true;
        });
        return endpoint;
    });

    return that;
}; // End webrtc.Client
<commit_msg>Change onEnter to onJoin.<commit_after>/**
 * Create a new WebRTC Client object.
 * @author Erin Spiceland <espiceland@digium.com>
 * @class webrtc.Client
 * @constructor
 * @augments webrtc.EventEmitter
 * @classdesc This is a top-level interface to the API. It handles authenticating the app to the
 * API server and receiving server-side app-specific information.
 * @param {object} params Object whose properties will be used to initialize this object and set
 * properties on the class.
 * @returns {webrtc.Client}
 * @property {object} clientSettings Client settings.
 * @property {webrtc.SignalingChannel} signalingChannel A reference to the signaling channel.
 * @property {webrtc.IdentityProvider} identityProvider A reference to the identity provider.
 * @property {function} chatMessage Class to use for chat messages.
 * @property {function} signalingMessage Class to use for signaling.
 * @property {function} presenceMessage Class to use for presence messages.
 * @property {webrtc.User} user Logged-in user's User object.
 */
/*global webrtc: false */
webrtc.Client = function (params) {
    "use strict";
    params = params || {};
    var client = webrtc.makeUniqueID().toString();
    var that = webrtc.EventEmitter(params);
    webrtc.instances[client] = that;
    that.className = 'webrtc.Client';

    var host = window.location.hostname;
    var port = window.location.port;
    var connected = false;
    var app = {};
    var signalingChannel = null;
    var turnRefresher = null;
    var clientSettings = params.clientSettings || {};
    var groups = [];
    var endpoints = [];
    var onMessage = function () {};
    var onCall = function () {};
    var onGroupJoin = function () {};
    var onGroupLeave = function () {};
    var onConnect = function () {};
    var onDisconnect = function () {};
    var onReconnect = function () {};

    if (!clientSettings.appId) {
        throw new Error("appId is a required parameter to Client.");
    }
    app.appId = clientSettings.appId;
    delete clientSettings.appId;

    log.debug("Client ID is ", client);

    var callSettings = {
        constraints: params.constraints || {
            video : true,
            audio : true,
            optional: [],
            mandatory: {}
        },
        servers: params.servers || {
            iceServers: []
        }
    };

    signalingChannel = webrtc.SignalingChannel({'client': client});
    that.identityProvider = webrtc.IdentityProvider({'client': client});
    that.user = null;
    log.debug(signalingChannel);

    /**
     * Connect to the Digium infrastructure and authenticate using the appkey.  Store
     * a token to be used in API requests.
     * @memberof! webrtc.Client
     * @method webrtc.Client.connect
     * @param {string} authToken
     */
    var connect = that.publicize('connect', function (params) {
        params = params || {};
        var deferred = webrtc.makeDeferred(params.onSuccess, params.onError);

        app.authToken = params.authToken;
        signalingChannel.open({
            appId: app.appId,
            token: app.authToken
        }).then(function () {
            return signalingChannel.authenticate({
                appId: app.appId
            });
        }, function (err) {
            deferred.reject("Couldn't connect to brightstream.");
            log.error(err.message);
        }).done(function (user) {
            connected = true;

            onMessage = params.onMessage || onMessage;
            onCall = params.onCall || onCall;
            onGroupJoin = params.onGroupJoin || onGroupJoin;
            onGroupLeave = params.onGroupLeave || onGroupLeave;
            onConnect = params.onConnect || onConnect;
            onDisconnect = params.onDisconnect || onDisconnect;
            onReconnect = params.onReconnect || onReconnect;

            user.setOnline(); // Initiates presence.
            user.listen('call', onCall);
            user.listen('join', onGroupJoin);
            user.listen('leave', onGroupLeave);
            that.listen('connect', onConnect);
            that.listen('disconnect', onDisconnect);
            that.listen('reconnect', onReconnect);
            that.user = user;
            log.info('logged in as user ' + user.getName());
            log.debug(user);
            //updateTurnCredentials(); // TODO fix TURN credentials with Endpoints instead of Users.

            that.fire('connect', user);
            deferred.resolve(user);
        }, function (err) {
            deferred.reject("Couldn't create an endpoint.");
            log.error(err.message);
        });
        return deferred.promise;
    });

    /**
     * Disconnect from the Digium infrastructure. Invalidates the API token.
     * @memberof! webrtc.Client
     * @method webrtc.Client.disconnect
     */
    var disconnect = that.publicize('disconnect', function () {
        // TODO: also call this on socket disconnect
        var disconnectPromise = signalingChannel.close();
        disconnectPromise.then(function () {
            connected = false;
        });
        return disconnectPromise;
    });

    /**
     * Get the client ID
     * @memberof! webrtc.Client
     * @method webrtc.Client.getID
     * @return {string}
     */
    var getID = that.publicize('getID', function () {
        return client;
    });

    /**
     * Update TURN credentials and set a timeout to do it again in 20 hours.
     * @memberof! webrtc.Client
     * @method webrtc.Client.updateTurnCredentials
     */
    var updateTurnCredentials = that.publicize('updateTurnCredentials', function () {
        if (callSettings.disableTurn === true) {
            return;
        }

        clearInterval(turnRefresher);
        signalingChannel.getTurnCredentials().done(function successHandler(creds) {
            callSettings.servers.iceServers = creds;
        }, function errorHandler(error) {
            throw error;
        });
        turnRefresher = setInterval(updateTurnCredentials, 20 * (60 * 60 * 1000)); // 20 hours
    });

    /**
     * Determine whether the Client has authenticated with its appKey against Digium services
     * by checking the validity of the apiToken.
     * @memberof! webrtc.Client
     * @method webrtc.Client.isConnected
     * @returns {boolean}
     */
    var isConnected = that.publicize('isConnected', function () {
        return !!connected;
    });

    /**
     * Get an object containing the client settings.
     * @memberof! webrtc.Client
     * @method webrtc.Client.getClientSettings
     * @returns {object} An object containing the client settings.
     */
    var getClientSettings = that.publicize('getClientSettings', function () {
        return clientSettings;
    });

    /**
     * Get an object containing the default media constraints and other media settings.
     * @memberof! webrtc.Client
     * @method webrtc.Client.getCallSettings
     * @returns {object} An object containing the media settings which will be used in
     * webrtc calls.
     */
    var getCallSettings = that.publicize('getCallSettings', function () {
        return callSettings;
    });

    /**
     * Set the default media constraints and other media settings.
     * @memberof! webrtc.Client
     * @method webrtc.Client.setDefaultCallSettings
     * @param {object} Object containing settings to modify.
     */
    var setDefaultCallSettings = that.publicize('setDefaultCallSettings', function (params) {
        params = params || {};
        if (params.constraints) {
            callSettings.constraints = params.constraints;
        }
        if (params.servers) {
            callSettings.servers = params.servers;
        }
    });

    /**
     * Get the SignalingChannel.
     * @memberof! webrtc.Client
     * @method webrtc.Client.getSignalingChannel
     * @returns {webrtc.SignalingChannel} The instance of the webrtc.SignalingChannel.
     */
    var getSignalingChannel = that.publicize('getSignalingChannel', function () {
        return signalingChannel;
    });

    /**
     * Get a Group
     * @memberof! webrtc.Client
     * @method webrtc.Client.join
     * @params {string} The name of the group.
     * @returns {webrtc.Group} The instance of the webrtc.Group which the user joined.
     */
    var join = that.publicize('join', function (params) {
        var deferred = webrtc.makeDeferred(params.onSuccess, params.onError);
        if (!params.id) {
            deferred.reject(new Error("Can't join a group with no group id."));
            return deferred.promise;
        }

        signalingChannel.joinGroup({
            id: params.id
        }).done(function () {
            var group = webrtc.Group({
                client: client,
                id: params.id,
                onMessage: params.onMessage,
                onJoin: params.onJoin,
                onLeave: params.onLeave,
                onPresence: params.onPresence
            });
            addGroup(group);
            deferred.resolve(group);
        }, function (err) {
            deferred.reject(err);
        });
        return deferred.promise;
    });

    /**
     * Add a Group
     * @memberof! webrtc.Client
     * @method webrtc.Client.addGroup
     * @params {id} the Group id
     * @private
     */
    var addGroup = function (newGroup) {
        var group;
        if (!newGroup || newGroup.className !== 'webrtc.Group') {
            throw new Error("Can't add group to internal tracking without a group.");
        }
        groups.every(function (grp) {
            if (grp.id === newGroup.id) {
                group = grp;
                return false;
            }
            return true;
        });

        if (!group) {
            groups.push(newGroup);
        }
    };

    /**
     * Remove a Group
     * @memberof! webrtc.Client
     * @method webrtc.Client.removeGroup
     * @params {id} the Group id
     * @private
     */
    var removeGroup = function (newGroup) {
        var index;
        var endpoints;
        if (!newGroup || !(newGroup instanceof webrtc.Group)) {
            throw new Error("Can't remove group to internal tracking without a group.");
        }

        groups.every(function (grp, i) {
            if (grp.id === newGroup.id) {
                index = i;
                return false;
            }
            return true;
        });

        if (index > -1) {
            groups[index].getEndpoints().done(function (list) {
                endpoints = list;
                groups.splice(index, 1);
                endpoints.forEach(function (endpoint) {
                    checkEndpointForRemoval(endpoint);
                });
            });
        }
    };

    /**
     * Find a group by id and return it.
     * @memberof! webrtc.Client
     * @method webrtc.Client.getGroup
     * @params {id} the Group id
     * @returns {webrtc.Group}
     */
    var getGroup = that.publicize('getGroup', function (params) {
        var group;
        if (!params || !params.id) {
            throw new Error("Can't get a group without group id.");
        }

        groups.every(function (grp) {
            if (grp.id === params.id) {
                group = grp;
                return false;
            }
            return true;
        });
        return group;
    });

    /**
     * Add an Endpoint
     * @memberof! webrtc.Client
     * @method webrtc.Client.addEndpoint
     * @params {id} the Endpoint id
     * @private
     */
    var addEndpoint = that.publicize('addEndpoint', function (newEndpoint) {
        var absent = false;
        if (!newEndpoint || !newEndpoint.id) {
            throw new Error("Can't add endpoint to internal tracking. No endpoint given.");
        }
        console.log('addEndpoints before', endpoints);
        absent = endpoints.every(function (ept) {
            if (ept.id === newEndpoint.id) {
                console.log('addEndpoints false');
                return false;
            }
            console.log('addEndpoints true');
            return true;
        });

        console.log('addEndpoints checking absent', absent);
        if (absent) {
            endpoints.push(newEndpoint);
        }
        console.log('addEndpoints after', endpoints);
    });

    /**
     * Remove an Endpoint
     * @memberof! webrtc.Client
     * @method webrtc.Client.removeEndpoint
     * @params {id} the Endpoint id
     * @private
     */
    var checkEndpointForRemoval = that.publicize('checkEndpointForRemoval', function (params) {
        var inAGroup;
        var index;
        if (!params || !params.id) {
            throw new Error("Can't remove endpoint from internal tracking without group id.");
        }

        console.log('checkEndpointForRemoval before', endpoints);
        Q.all(groups.map(function (group) {
            return group.getEndpoints();
        })).done(function (groupEndpoints) {
            groupEndpoints.forEach(function (endpoints) {
                endpoints.forEach(function (endpoint) {
                    if (endpoint.id === params.id) {
                        inAGroup = true;
                    }
                });
            });
            if (inAGroup) {
                endpoints.every(function (ept, i) {
                    if (ept.id === params.id) {
                        index = i;
                        return false;
                    }
                    return true;
                });
                if (index > -1) {
                    endpoints.splice(index, 1);
                }
            }
            console.log('checkEndpointForRemoval after', endpoints);
        });
    });

    /**
     * Find an endpoint by id and return it.
     * @memberof! webrtc.Client
     * @method webrtc.Client.getEndpoint
     * @params {id} the Endpoint id
     * @returns {webrtc.Contact}
     */
    var getEndpoint = that.publicize('getEndpoint', function (params) {
        var endpoint;
        if (!params || !params.id) {
            throw new Error("Can't get an endpoint without group id.");
        }

        console.log("looking for", params.id, 'in', endpoints);
        endpoints.every(function (ept) {
            console.log('comparing', ept.id, params.id);
            if (ept.id === params.id) {
                endpoint = ept;
                return false;
            }
            return true;
        });
        return endpoint;
    });

    return that;
}; // End webrtc.Client
<|endoftext|>


<commit_before>var SHORT_WORD_LEN = 3

// Compute the edit distance between the two given strings
// Taken from: http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript
function getEditDistance(a, b){
  if(a.length == 0) return b.length;
  if(b.length == 0) return a.length;

  var matrix = [];

  // increment along the first column of each row
  var i;
  for(i = 0; i <= b.length; i++){
    matrix[i] = [i];
  }

  // increment each column in the first row
  var j;
  for(j = 0; j <= a.length; j++){
    matrix[0][j] = j;
  }

  // Fill in the rest of the matrix
  for(i = 1; i <= b.length; i++){
    for(j = 1; j <= a.length; j++){
      if(b.charAt(i-1) == a.charAt(j-1)){
        matrix[i][j] = matrix[i-1][j-1];
      } else {
        matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
                                Math.min(matrix[i][j-1] + 1, // insertion
                                         matrix[i-1][j] + 1)); // deletion
      }
    }
  }

  return matrix[b.length][a.length];
};

function filter_word_list(a_word_list) {
    var result = []
    a_word_list.forEach(function(word) {
        if (word.length > SHORT_WORD_LEN) {
            result.push(word);
        }
    });

    return result;
}

// returns the minimum edit distance between any word in word_set1 and any word in word_set
function minEditDistanceForWords(word_set1, word_set2) {
	var minDist = Infinity;
	word_set1.forEach(function(word1) {
		var d = minEditDistanceForWord(word1, word_set2);
		if (d < minDist) {
            minDist = d;
		}
    });

	return minDist;
}

// returns the minimum edit distance between word and any word in word_set
function minEditDistanceForWord(word, word_set) {
    // i think this could be optimized with BK trees or somesuch
	var minDist = Infinity;
	word_set.forEach(function(word2) {
		var d = getEditDistance(word, word2);
		if (d < minDist) {
            minDist = d;
		}
    });
	return minDist;
}

var Features = {
  // Returns true if the given element is visible or else false
  // Adapted from https://www.altamiracorp.com/blog/employee-posts/selenium-mojo-a-faster-isvisib
  // TODO: match behavior of this code with W3C spec (or else make approximations clear)
  // http://www.w3.org/TR/webdriver/#determining-visibility
  isVisible: function(element) {
    var visible = true;
    // Check if this is a hidden input element
    if (element.type && element.type == "hidden") {
      return false;
    }

    // if 0 x 0 return false
    if (element.clientWidth === 0 || element.clientHeight === 0){
      return false;
    }

    // Check this element and all parents for hidden style
    while (element != null) {
      var style = window.getComputedStyle(element);
      if (style && (style['display'] == 'none' || style['visibility'] == 'hidden')) {
        return false;
      }
      element = element.parentNode;
    }

    return visible;
  },

  // return a list of filtered words from the text of an element
  getTextWords: function(elem) {
	var words = []
	if (elem.value && elem.type.toLowerCase() === "button") words.push(elem.value);
	if (elem.alt) words.push(elem.alt);
    return filter_word_list(elem.textContent.split(/\s+/).concat(words));
  },

  // produces a list of filtered words in text
  // in sibling tags
  getSiblingTextWords: function(element) {
	  if (element.parentNode) {
		  return Features.getTextWords(element.parentNode);
	  } else {
		  return ["farts"];
	  }
  },

  isTypable: function(elem){
    if (elem.tagName == 'TEXTAREA'){return true;}
    else if (elem.tagName == 'INPUT' && _.contains(['text', 'email', 'password', 'search', 'url'], elem.type.toLowerCase())){
      return true;
    }
    return false;
  },

  isClickable: function(elem){
    if (elem.tagName == 'BUTTON' || elem.tagName == 'A'){return true;}
    else if (elem.tagName == 'INPUT' && _.contains(['submit', 'button', 'checkbox', 'radio'], elem.type.toLowerCase())){
      return true;
    }
    return false;
  }
}

function elementToFeatureVector(elem) {
    var rect = elem.getBoundingClientRect();

    // FIX:
      // best relative_x is 0
      // edit distances are all best at 0
      // text size & n_children?

    return {
      width: rect.width,
      height: rect.height,

      // Note! relative_x and relative_y are both further modified in extend_and_norm_feature.

      // 0 = center of the page
      relative_x: Math.abs((rect.left + window.scrollX - document.body.clientWidth*0.5) / document.body.clientWidth),
      // higher = further down
      // 0 = top of page, 1 = at fold
      relative_y: ((rect.top +  window.scrollY) / window.innerHeight),

      clickable: Features.isClickable(elem)? 1:-1,
      typeable: Features.isTypable(elem)? 1:-1,

      tagname: elem.tagName,
      text_words: Features.getTextWords(elem),
      sibling_text_words: Features.getSiblingTextWords(elem),

      text_size: Features.getTextWords(elem).length,
      n_children: elem.children.length,
      tab_index: (elem.tabIndex == -1)? -1:1,

      id: elem.id,
      class_list: elem.classList,
      has_id: (elem.id === '')? 0:1,
      has_class: (elem.classList.length > 0)? 1:0,

      alreadyInteracted: elem.getAttribute("x-WebtalkInteracted") == "1"? -1:1,
    }
}

// produces a list of all Elements in the body of the page that are currently visible
function getAllElementFeatures(){
  function recGetAllElems(root) {
    var elements = [];
    for(var i=0;i<root.children.length;i++){
            var this_child = root.children[i];
            if (!Features.isVisible(this_child) || this_child.id.substring(0,18) == 'selenium-highlight'){
              continue;
            }
            elements.push([this_child, elementToFeatureVector(this_child)])
            elements = elements.concat(recGetAllElems(this_child));
    }
    return elements
  }

  return recGetAllElems(document.body);
}

function elementTree(){
  function rec(root) {
    var elements = [];
    for(var i=0;i<root.children.length;i++){
            var this_child = root.children[i];

            if (!Features.isVisible(this_child)){
              continue;
            }

            var label = this_child.tagName + (this_child.id? '#'+this_child.id:'');

            elements.push([label, rec(this_child)]);
    }
    return elements;
  }

  return ['BODY', rec(document.body)];
}

res = getAllElementFeatures()

return [
  res,
  elementTree(document.body)
]
<commit_msg>clickable means its a button<commit_after>var SHORT_WORD_LEN = 3

// Compute the edit distance between the two given strings
// Taken from: http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript
function getEditDistance(a, b){
  if(a.length == 0) return b.length;
  if(b.length == 0) return a.length;

  var matrix = [];

  // increment along the first column of each row
  var i;
  for(i = 0; i <= b.length; i++){
    matrix[i] = [i];
  }

  // increment each column in the first row
  var j;
  for(j = 0; j <= a.length; j++){
    matrix[0][j] = j;
  }

  // Fill in the rest of the matrix
  for(i = 1; i <= b.length; i++){
    for(j = 1; j <= a.length; j++){
      if(b.charAt(i-1) == a.charAt(j-1)){
        matrix[i][j] = matrix[i-1][j-1];
      } else {
        matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
                                Math.min(matrix[i][j-1] + 1, // insertion
                                         matrix[i-1][j] + 1)); // deletion
      }
    }
  }

  return matrix[b.length][a.length];
};

function filter_word_list(a_word_list) {
    var result = []
    a_word_list.forEach(function(word) {
        if (word.length > SHORT_WORD_LEN) {
            result.push(word);
        }
    });

    return result;
}

// returns the minimum edit distance between any word in word_set1 and any word in word_set
function minEditDistanceForWords(word_set1, word_set2) {
	var minDist = Infinity;
	word_set1.forEach(function(word1) {
		var d = minEditDistanceForWord(word1, word_set2);
		if (d < minDist) {
            minDist = d;
		}
    });

	return minDist;
}

// returns the minimum edit distance between word and any word in word_set
function minEditDistanceForWord(word, word_set) {
    // i think this could be optimized with BK trees or somesuch
	var minDist = Infinity;
	word_set.forEach(function(word2) {
		var d = getEditDistance(word, word2);
		if (d < minDist) {
            minDist = d;
		}
    });
	return minDist;
}

var Features = {
  // Returns true if the given element is visible or else false
  // Adapted from https://www.altamiracorp.com/blog/employee-posts/selenium-mojo-a-faster-isvisib
  // TODO: match behavior of this code with W3C spec (or else make approximations clear)
  // http://www.w3.org/TR/webdriver/#determining-visibility
  isVisible: function(element) {
    var visible = true;
    // Check if this is a hidden input element
    if (element.type && element.type == "hidden") {
      return false;
    }

    // if 0 x 0 return false
    if (element.clientWidth === 0 || element.clientHeight === 0){
      return false;
    }

    // Check this element and all parents for hidden style
    while (element != null) {
      var style = window.getComputedStyle(element);
      if (style && (style['display'] == 'none' || style['visibility'] == 'hidden')) {
        return false;
      }
      element = element.parentNode;
    }

    return visible;
  },

  // return a list of filtered words from the text of an element
  getTextWords: function(elem) {
	var words = []
	if (elem.value && this.isClickable(elem)) words.push(elem.value);
	if (elem.alt) words.push(elem.alt);
    return filter_word_list(elem.textContent.split(/\s+/).concat(words));
  },

  // produces a list of filtered words in text
  // in sibling tags
  getSiblingTextWords: function(element) {
	  if (element.parentNode) {
		  return Features.getTextWords(element.parentNode);
	  } else {
		  return ["farts"];
	  }
  },

  isTypable: function(elem){
    if (elem.tagName == 'TEXTAREA'){return true;}
    else if (elem.tagName == 'INPUT' && _.contains(['text', 'email', 'password', 'search', 'url'], elem.type.toLowerCase())){
      return true;
    }
    return false;
  },

  isClickable: function(elem){
    if (elem.tagName == 'BUTTON' || elem.tagName == 'A'){return true;}
    else if (elem.tagName == 'INPUT' && _.contains(['submit', 'button', 'checkbox', 'radio'], elem.type.toLowerCase())){
      return true;
    }
    return false;
  }
}

function elementToFeatureVector(elem) {
    var rect = elem.getBoundingClientRect();

    // FIX:
      // best relative_x is 0
      // edit distances are all best at 0
      // text size & n_children?

    return {
      width: rect.width,
      height: rect.height,

      // Note! relative_x and relative_y are both further modified in extend_and_norm_feature.

      // 0 = center of the page
      relative_x: Math.abs((rect.left + window.scrollX - document.body.clientWidth*0.5) / document.body.clientWidth),
      // higher = further down
      // 0 = top of page, 1 = at fold
      relative_y: ((rect.top +  window.scrollY) / window.innerHeight),

      clickable: Features.isClickable(elem)? 1:-1,
      typeable: Features.isTypable(elem)? 1:-1,

      tagname: elem.tagName,
      text_words: Features.getTextWords(elem),
      sibling_text_words: Features.getSiblingTextWords(elem),

      text_size: Features.getTextWords(elem).length,
      n_children: elem.children.length,
      tab_index: (elem.tabIndex == -1)? -1:1,

      id: elem.id,
      class_list: elem.classList,
      has_id: (elem.id === '')? 0:1,
      has_class: (elem.classList.length > 0)? 1:0,

      alreadyInteracted: elem.getAttribute("x-WebtalkInteracted") == "1"? -1:1,
    }
}

// produces a list of all Elements in the body of the page that are currently visible
function getAllElementFeatures(){
  function recGetAllElems(root) {
    var elements = [];
    for(var i=0;i<root.children.length;i++){
            var this_child = root.children[i];
            if (!Features.isVisible(this_child) || this_child.id.substring(0,18) == 'selenium-highlight'){
              continue;
            }
            elements.push([this_child, elementToFeatureVector(this_child)])
            elements = elements.concat(recGetAllElems(this_child));
    }
    return elements
  }

  return recGetAllElems(document.body);
}

function elementTree(){
  function rec(root) {
    var elements = [];
    for(var i=0;i<root.children.length;i++){
            var this_child = root.children[i];

            if (!Features.isVisible(this_child)){
              continue;
            }

            var label = this_child.tagName + (this_child.id? '#'+this_child.id:'');

            elements.push([label, rec(this_child)]);
    }
    return elements;
  }

  return ['BODY', rec(document.body)];
}

res = getAllElementFeatures()

return [
  res,
  elementTree(document.body)
]
<|endoftext|>


<commit_before>/* @author: Mark McCann (www.markmccann.me) @license: None, Public Domain */
;(function(){var e="Modality",t=document.querySelector("body"),n={autoBind:true,clickOffToClose:true,closeOnEscape:true,effect:"",innerClass:"mm-wrap",modalClass:"modality-modal",onClose:"",onOpen:"",openClass:"mm-show",openOnLoad:false,userClass:""},r=function(){var e=arguments;for(var t=1;t<e.length;t++)for(var n in e[t])if(e[t].hasOwnProperty(n))e[0][n]=e[t][n];return e[0]},i=function(e,t,n){if(e.attachEvent){e["e"+t+n]=n;e[t+n]=function(){e["e"+t+n](window.event)};e.attachEvent("on"+t,e[t+n])}else{e.addEventListener(t,n,false)}},s=function(e,t){return e.className.match(new RegExp("(\\s|^)"+t+"(\\s|$)"))},o=function(e,t){for(var n=0;n<e.length;n++)if(!s(e[n],t))e[n].className+=" "+t},u=function(e,t){for(var n=0;n<e.length;n++){if(s(e[n],t)){var r=new RegExp("(\\s|^)"+t+"(\\s|$)","g");e[n].className=e[n].className.replace(r,"")}}},a=function(e,t){var n=document.createElement("div");n.setAttribute("class",t.modalClass+" "+t.effect+" "+t.userClass);n.innerHTML='
'+e.outerHTML+"
";e.parentNode.replaceChild(n,e);return n},f=function(e,s){var o=this;o.defaults=n;o.id=e.getAttribute("id");o.settings=r({},n,s);o.wrapper=a(e,o.settings);o.triggers=document.querySelectorAll('a[href="#'+o.id+'"], [data-modality="#'+o.id+'"]');o.modal=document.getElementById(o.id);if(o.settings.autoBind){for(var u=0;u<o.triggers.length;u++)o.setTrigger(o.triggers[u])}if(o.settings.clickOffToClose){i(o.wrapper,"click",function(e){e.preventDefault();if(e.target==o.wrapper)o.close()},false)}if(o.settings.closeOnEscape){i(t,"keyup",function(e){if(e.keyCode==27)o.close()},false)}if(o.modal.style.display=="none")o.modal.style.display="";if(o.settings.openOnLoad)o.open();return o};r(f.prototype,{open:function(e){o([this.wrapper,t],this.settings.openClass);if(typeof this.settings.onOpen=="function")this.settings.onOpen();if(typeof e=="function")e();return this},close:function(e){u([this.wrapper,t],this.settings.openClass);if(typeof this.settings.onClose=="function")this.settings.onClose();if(typeof e=="function")e();return this},toggle:function(e){return this.isOpen()?this.close(e):this.open(e)},isOpen:function(){return s(this.wrapper,this.settings.openClass)},setTrigger:function(e){var t=this;i(e,"click",function(e){e.preventDefault();t.toggle()},false);return t}});r(f,{instances:{},extend:r,init:function(e,t){var n={},r=document.querySelectorAll(e);for(var i=0;i<r.length;i++){var s=new this(r[i],t);this.instances[s.id]=n[i]=s}return n[1]===undefined?n[0]:n}});window[e]=f})();<commit_msg>Update modality.min.js<commit_after>/* @author: Mark McCann (www.markmccann.me) @license: None, Public Domain */
;(function(){var e="Modality",t=document.querySelector("body"),n={autoBind:true,clickOffToClose:true,closeOnEscape:true,effect:"",innerClass:"mm-wrap",modalClass:"modality-modal",onClose:"",onOpen:"",openClass:"mm-show",openOnLoad:false,userClass:""},r=function(){var e=arguments;for(var t=1;t<e.length;t++)for(var n in e[t])if(e[t].hasOwnProperty(n))e[0][n]=e[t][n];return e[0]},i=function(e,t,n){if(e.attachEvent){e["e"+t+n]=n;e[t+n]=function(){e["e"+t+n](window.event)};e.attachEvent("on"+t,e[t+n])}else{e.addEventListener(t,n,false)}},s=function(e,t){return e.className.match(new RegExp("(\\s|^)"+t+"(\\s|$)"))},o=function(e,t){for(var n=0;n<e.length;n++)if(!s(e[n],t))e[n].className+=" "+t},u=function(e,t){for(var n=0;n<e.length;n++){if(s(e[n],t)){var r=new RegExp("(\\s|^)"+t+"(\\s|$)","g");e[n].className=e[n].className.replace(r,"")}}},a=function(e,t){var n=document.createElement("div");n.setAttribute("class",t.modalClass+" "+t.effect+" "+t.userClass);n.innerHTML='
'+e.outerHTML+"
";e.parentNode.replaceChild(n,e);return n},f=function(e,s){var o=this;o.defaults=n;o.id=e.getAttribute("id");o.settings=r({},n,s);o.wrapper=a(e,o.settings);o.triggers=document.querySelectorAll('a[href="#'+o.id+'"], [data-modality="#'+o.id+'"]');o.modal=document.getElementById(o.id);if(o.settings.autoBind){for(var u=0;u<o.triggers.length;u++)o.setTrigger(o.triggers[u])}if(o.settings.clickOffToClose){i(o.wrapper,"click",function(e){e.preventDefault?e.preventDefault():e.returnValue=false;if(e.target==o.wrapper)o.close()},false)}if(o.settings.closeOnEscape){i(t,"keyup",function(e){if(e.keyCode==27)o.close()},false)}if(o.modal.style.display=="none")o.modal.style.display="";if(o.settings.openOnLoad)o.open();return o};r(f.prototype,{open:function(e){o([this.wrapper,t],this.settings.openClass);if(typeof this.settings.onOpen=="function")this.settings.onOpen();if(typeof e=="function")e();return this},close:function(e){u([this.wrapper,t],this.settings.openClass);if(typeof this.settings.onClose=="function")this.settings.onClose();if(typeof e=="function")e();return this},toggle:function(e){return this.isOpen()?this.close(e):this.open(e)},isOpen:function(){return s(this.wrapper,this.settings.openClass)},setTrigger:function(e){var t=this;i(e,"click",function(e){e.preventDefault?e.preventDefault():e.returnValue=false;t.toggle()},false);return t}});r(f,{instances:{},extend:r,init:function(e,t){var n={},r=document.querySelectorAll(e);for(var i=0;i<r.length;i++){var s=new this(r[i],t);this.instances[s.id]=n[i]=s}return n[1]===undefined?n[0]:n}});window[e]=f})();
<|endoftext|>