<commit_before>{% comment %}
 coding: utf-8

 maposmatic, the web front-end of the MapOSMatic city map generation system
 Copyright (C) 2012  David Decotigny
 Copyright (C) 2012  Frédéric Lehobey
 Copyright (C) 2012  Pierre Mauduit
 Copyright (C) 2012  David Mentré
 Copyright (C) 2012  Maxime Petazzoni
 Copyright (C) 2012  Thomas Petazzoni
 Copyright (C) 2012  Gaël Utard

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 License, or any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
{% endcomment %}
{% load i18n %}
{% load extratags %}

/**
 * Map creation wizard.
 */

var BBOX_MAXIMUM_LENGTH_IN_KM = {{ BBOX_MAXIMUM_LENGTH_IN_METERS }} / 1000;

var locationFilter = null;
var map = wizardmap($('#step-location-map'));
var country = null;
var languages = $('#id_map_language').html();

jQuery.fn.reverse = [].reverse;

$('#wizard').carousel({'interval': false});

/**
 * When the carousel initiates its slide, trigger the 'prepare' event on the
 * slide that is about to be activated.
 */
$('#wizard').bind('slide', function(e) {
  $(e.relatedTarget).trigger('prepare');
});

/**
 * The 'slid' event is triggered when the carousel finishes a slide in either
 * direction. This function is called by the 'slid' event handler to make sure
 * the prev/next links are in the * correct state based on the position in the
 * carousel.
 */
$('#wizard').bind('slid', setPrevNextLinks);

$('#wizard-step-location label').click(function(e) {
  $('#id_administrative_city').val('');
  $('#id_administrative_osmid').val('');
  country = null;
  $(this).tab('show');
  setPrevNextLinks();

  // If we're switching to the administrative boundary / city search tab, reset
  // the focus inside the input field.
  if ($(this).attr('for') == 'id_mode_0') {
    $('#id_administrative_city').focus();
  }

  // If it's the first time we switch to the bounding box tab, initialize the
  // minimap.
  if ($(this).attr('for') == 'id_mode_1' && !map) {
    map = wizardmap($('#step-location-map'));
  }
  if ($(this).attr('for') == 'id_mode_2' && !map) {
    map = wizardmap($('#step-location-map'));
  }
});

/**
 * Bind the keyup event on the map title field to control the disabled state of
 * the final submit button. The button can only be pressed if a map title is
 * present. The rest of the validation is assumed to have been taken care of at
 * each step boundary.
 */
$('#id_maptitle').bind('keyup', function(e) {
  if ($(this).val()) {
    $('#id_go_next_btn').removeAttr('disabled');
  } else {
    $('#id_go_next_btn').attr('disabled', 'disabled');
  }
});

function setPrevNextLinks() {
  var current = $('#wizard .carousel-inner div.item.active');
  var first   = $('#wizard .carousel-inner div.item:first-child');
  var last    = $('#wizard .carousel-inner div.item:last-child');

  $('#prevlink').hide();
  $('#nextlink').hide();
  if (current.attr('id') == first.attr('id')) {
    if ($('#id_administrative_osmid').val()) {
      $('#nextlink').show();
    }
  } else if (current.attr('id') == last.attr('id')) {
    $('#prevlink').show();
  } else {
    $('#prevlink').show();
    $('#nextlink').show();
  }
}

$('#wizard-step-paper-size').bind('prepare', function(e) {
  $('#paper-selection').hide();
  $('#paper-size-loading-error').hide();
  $('#paper-size-loading').show();
  $('#nextlink').hide();

  var args = null;
  if ($('#id_administrative_osmid').val()) {
    args = {
      osmid: $('#id_administrative_osmid').val(),
    };
  } else {
    args = {
      lat_upper_left: $('#id_lat_upper_left').val(),
      lon_upper_left: $('#id_lon_upper_left').val(),
      lat_bottom_right: $('#id_lat_bottom_right').val(),
      lon_bottom_right: $('#id_lon_bottom_right').val()
    };
  }

  args['layout'] = $('input[name=layout]:checked').val();
  args['stylesheet'] = $('input[name=stylesheet]:checked').val();
  args['overlay'] = $('input[name=overlay]:checked').val();

  $.ajax('/apis/papersize/', { type: 'post', data: args })
    .complete(function() { $('#paper-size-loading').hide(); })
    .error(function() { $('#paper-size-loading-error').show(); })
    .success(function(data) {

      function get_paper_def(paper) {
        for (i in data) {
          if (paper == data[i][0]) {
            return data[i];
          }
        }

        return null;
      }

      function handle_paper_size_click(w, h, p_ok, l_ok, l_preferred) {
        var l = $('#paper-selection input[value=landscape]');
        var p = $('#paper-selection input[value=portrait]');

        if (l_ok) {
          l.removeAttr('disabled');
          if (!p_ok) { l.attr('checked', 'checked'); }
        } else {
          l.attr('disabled', 'disabled');
        }

        if (p_ok) {
          p.removeAttr('disabled');
          if (!l_ok) { p.attr('checked', 'checked'); }
        } else {
          p.attr('disabled', 'disabled');
        }

        if (l_ok && p_ok) {
          if (l_preferred) {
            l.attr('checked', 'checked');
          } else {
            p.attr('checked', 'checked');
          }
        }

        $('#id_paper_width_mm').val(w);
        $('#id_paper_height_mm').val(h);
      }

      var default_paper = null;

      $.each($('#paper-size ul li'), function(i, item) {
        $(item).hide();

        var paper = $('label input[value]', item).val();
        var def = get_paper_def(paper);
        if (def) {
          $('label', item).bind('click', function() {
            handle_paper_size_click(def[1], def[2], def[3], def[4], def[6]);
          });

          if (def[5]) {
            default_paper = $(item);
          }

          $(item).show();

          // TODO: fix for i18n
          if (paper == 'Best fit') {
            w = def[1] / 10;
            h = def[2] / 10;
            $('label em.papersize', item).html('(' + w.toFixed(1) + ' × ' + h.toFixed(1) + ' cm²)');
          }
        }
      });

      $('label input', default_paper).click();
      $('#paper-selection').show();
      $('#nextlink').show();
    });
});

$('#wizard-step-lang-title').bind('prepare', function(e) {
  // Prepare the language list
  var list = $('#id_map_language');
  list.html(languages);

  /*
   * The goal is to build a list of languages in which we have first
   * the languages matching the current country code, then an empty
   * disabled entry used as a separator and finally all other
   * languages. To do so, we use prependTo(), which adds elements at
   * the beginning of the list. So we start by prepending the
   * separator, then the "no localization" special language, and
   * finally the languages matching the current country code.
   */
  $('<option disabled="disabled"></option>').prependTo(list);
  $('option[value=C]', list).prependTo(list);
  list.children('option').reverse().each(function() {
    if ($(this).val().match('.._' + country.toUpperCase() + '\..*') != null) {
      $(this).prependTo(list);
    }
  });
  $('option:first', list).attr('selected', 'selected');

  // Seed the summary fields
  if ($('#id_administrative_osmid').val()) {
    $('#summary-location').text($('#id_administrative_city').val());
  } else {
    $('#summary-location').html(
      '(' + $('#id_lat_upper_left').val() + ', ' +
            $('#id_lon_upper_left').val() + ')' +
      ' → ' +
      '(' + $('#id_lat_bottom_right').val() + ', ' +
            $('#id_lon_bottom_right').val() + ')');
  }

  $('#summary-layout').text($('input[name=layout]:checked').parent().text().trim());
  $('#summary-stylesheet').text($('input[name=stylesheet]:checked').parent().text().trim());
  $('#summary-overlay').text($('input[name=overlay]:checked').parent().text().trim());
  $('#summary-paper-size').text(
      ($('input[value=landscape]').is(':checked')
          ? '{% trans "Landscape" %}'
          : '{% trans "Portrait" %}'
      ) + ', ' + $('input[name=papersize]:checked').parent().text().trim());
});

function wizardmap(elt) {
  var map = create_map($('#step-location-map'));
  var lock = false;
  var bbox = null;
  var bbox_style = {
    fill: true,
    fillColor: "#FFFFFF",
    fillOpacity: 0.5,
    stroke: true,
    strokeOpacity: 0.8,
    strokeColor: "#FF0000",
    strokeWidth: 2
  };
  var countryquery = null;
  locationFilter = new L.LocationFilter({buttonPosition: 'topright'});
  locationFilter.on("change", function (e) {
      bbox = e.target.getBounds();
      map.fitBounds(bbox);
      update_fields();
  });
  locationFilter.on("enabled", function (e) {
      bbox = e.target.getBounds();
      map.fitBounds(bbox);
      update_fields();
  });
  locationFilter.on("disabled", function (e) {
      bbox = null;
      update_fields();
  });
  locationFilter.addTo(map);

  // locate client position
  L.control.locate().addTo(map);

  // search button
  map.addControl( new L.Control.Search({
      url: '//nominatim.openstreetmap.org/search?format=json&q={s}',
      jsonpParam: 'json_callback',
      propertyName: 'display_name',
      propertyLoc: ['lat','lon'],
      circleLocation: true,
      markerLocation: false,
      autoType: false,
      autoCollapse: true,
      minLength: 2,
      zoom: 17
  }) );

  /**
   * Update the 4 text fields with the area coordinates.
   *
   * If a feature has been drawned (bbox != null), the bounding box of the
   * feature is used, otherwise the map extent is used.
   */
  var update_fields = function() {
    if (lock) {
      return;
    }

    var bounds = (bbox != null) ? bbox : map.getBounds();

    $('#id_lat_upper_left').val(bounds.getNorth().toFixed(4));
    $('#id_lon_upper_left').val(bounds.getWest().toFixed(4));
    $('#id_lat_bottom_right').val(bounds.getSouth().toFixed(4));
    $('#id_lon_bottom_right').val(bounds.getEast().toFixed(4));

    var center = bounds.getCenter();

    var upper_left   = bounds.getNorthWest();
    var upper_right  = bounds.getNorthEast();
    var bottom_left  = bounds.getSouthWest();
    var bottom_right = bounds.getSouthEast();

    var width  = upper_left.distanceTo(upper_right);
    var height = upper_right.distanceTo(bottom_right);

    if (width < {{ BBOX_MAXIMUM_LENGTH_IN_METERS }} &&
        height < {{ BBOX_MAXIMUM_LENGTH_IN_METERS }}) {
      $('#area-size-alert').hide();
      $('#nextlink').show();

      // Attempt to get the country by reverse geo lookup
      if (countryquery != null) { countryquery.abort(); }
      countryquery = $.getJSON(
        '/apis/reversegeo/' + center.lat + '/' + center.lng + '/',
        function(data) {
          $.each(data, function(i, item) {
            if (typeof item.country_code != 'undefined') {
              country = item.country_code;
            }
          });
        });
    } else {
      $('#area-size-alert').show();
      $('#nextlink').hide();
    }
  };

  /**
   * Set the map bounds and extent to the current values given by the 4 text
   * fields.
   */
  var set_map_bounds_from_fields = function() {
    lock = true;
    set_map_bounds(map, [
      [$('#id_lat_upper_left').val(), $('#id_lon_upper_left').val()],
      [$('#id_lat_bottom_right').val(), $('#id_lon_bottom_right').val()]
    ]);
    lock = false;
  };

  // Bind events.
  map.on('moveend', update_fields);
  map.on('zoomend', update_fields);

  $('#step-location-bbox input').bind('keydown', function(e) {
    if (bbox) {
      return;
    }

    if (e.keyCode == 38 || e.keyCode == 40) {
      var v = parseFloat($(e.target).val()) + (0.01 * (e.keyCode == 38 ? 1 : -1));
      $(e.target).val(v.toFixed(4));
    }

    set_map_bounds_from_fields();
    update_fields();
  });

  set_map_bounds_from_fields();
  update_fields();
  return map;
}

/* general file upload event handler */
function loadFile(input, onload_func) {
  var file, fr;
  if (typeof window.FileReader !== 'function') {
    console.log("The file API isn't supported on this browser yet.");
    return;
  }
  if (!input) {
    console.log("Um, couldn't find the fileinput element.");
  }
  else if (!input.files) {
    console.log("This browser doesn't seem to support the `files` property of file inputs.");
  }
  else if (!input.files[0]) {
    console.log("Please select a file before clicking 'Load'");
  }
  else {
    file = input.files[0];
    fr = new FileReader();
    fr.onload = receivedText;
    fr.readAsText(file);
  }
  function receivedText() {
    onload_func(fr.result);
  }
}

/* handle upload of GPX files*/
$("#id_track").change(function() {
  loadFile($("#id_track")[0], function(xml) {
    var parser = new DOMParser();
    var parsererrorNS = parser.parseFromString('INVALID', 'text/xml').getElementsByTagName("parsererror")[0].namespaceURI;
    var dom = parser.parseFromString(xml, 'text/xml');
    if(dom.getElementsByTagNameNS(parsererrorNS, 'parsererror').length > 0) {
      alert("not a valid XML file");
      $("#id_track")[0].value = '';
      return false;
    }

    var gpx = new L.GPX(xml, { async: false,
                     marker_options: {
                       startIconUrl: false,
                       endIconUrl: false,
                       shadowUrl: false
                     }
                   }
              ).addTo(map);

     var new_bbox = gpx.getBounds();

     if ('_northEast' in new_bbox === false) {
       alert("Not a GPX file");
       $("#id_track")[0].value = '';
       return false;
     }

     $('#locTabs li:nth-child(2) label').tab('show') // Select geo location tab
     $('input:radio[name=mode]').val(['bbox']);

     map.fitBounds(new_bbox);
     locationFilter.setBounds(new_bbox);
     locationFilter.enable();

     return true;
  });
});
<commit_msg>do not fail if country can't be detected<commit_after>{% comment %}
 coding: utf-8

 maposmatic, the web front-end of the MapOSMatic city map generation system
 Copyright (C) 2012  David Decotigny
 Copyright (C) 2012  Frédéric Lehobey
 Copyright (C) 2012  Pierre Mauduit
 Copyright (C) 2012  David Mentré
 Copyright (C) 2012  Maxime Petazzoni
 Copyright (C) 2012  Thomas Petazzoni
 Copyright (C) 2012  Gaël Utard

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 License, or any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
{% endcomment %}
{% load i18n %}
{% load extratags %}

/**
 * Map creation wizard.
 */

var BBOX_MAXIMUM_LENGTH_IN_KM = {{ BBOX_MAXIMUM_LENGTH_IN_METERS }} / 1000;

var locationFilter = null;
var map = wizardmap($('#step-location-map'));
var country = null;
var languages = $('#id_map_language').html();

jQuery.fn.reverse = [].reverse;

$('#wizard').carousel({'interval': false});

/**
 * When the carousel initiates its slide, trigger the 'prepare' event on the
 * slide that is about to be activated.
 */
$('#wizard').bind('slide', function(e) {
  $(e.relatedTarget).trigger('prepare');
});

/**
 * The 'slid' event is triggered when the carousel finishes a slide in either
 * direction. This function is called by the 'slid' event handler to make sure
 * the prev/next links are in the * correct state based on the position in the
 * carousel.
 */
$('#wizard').bind('slid', setPrevNextLinks);

$('#wizard-step-location label').click(function(e) {
  $('#id_administrative_city').val('');
  $('#id_administrative_osmid').val('');
  country = null;
  $(this).tab('show');
  setPrevNextLinks();

  // If we're switching to the administrative boundary / city search tab, reset
  // the focus inside the input field.
  if ($(this).attr('for') == 'id_mode_0') {
    $('#id_administrative_city').focus();
  }

  // If it's the first time we switch to the bounding box tab, initialize the
  // minimap.
  if ($(this).attr('for') == 'id_mode_1' && !map) {
    map = wizardmap($('#step-location-map'));
  }
  if ($(this).attr('for') == 'id_mode_2' && !map) {
    map = wizardmap($('#step-location-map'));
  }
});

/**
 * Bind the keyup event on the map title field to control the disabled state of
 * the final submit button. The button can only be pressed if a map title is
 * present. The rest of the validation is assumed to have been taken care of at
 * each step boundary.
 */
$('#id_maptitle').bind('keyup', function(e) {
  if ($(this).val()) {
    $('#id_go_next_btn').removeAttr('disabled');
  } else {
    $('#id_go_next_btn').attr('disabled', 'disabled');
  }
});

function setPrevNextLinks() {
  var current = $('#wizard .carousel-inner div.item.active');
  var first   = $('#wizard .carousel-inner div.item:first-child');
  var last    = $('#wizard .carousel-inner div.item:last-child');

  $('#prevlink').hide();
  $('#nextlink').hide();
  if (current.attr('id') == first.attr('id')) {
    if ($('#id_administrative_osmid').val()) {
      $('#nextlink').show();
    }
  } else if (current.attr('id') == last.attr('id')) {
    $('#prevlink').show();
  } else {
    $('#prevlink').show();
    $('#nextlink').show();
  }
}

$('#wizard-step-paper-size').bind('prepare', function(e) {
  $('#paper-selection').hide();
  $('#paper-size-loading-error').hide();
  $('#paper-size-loading').show();
  $('#nextlink').hide();

  var args = null;
  if ($('#id_administrative_osmid').val()) {
    args = {
      osmid: $('#id_administrative_osmid').val(),
    };
  } else {
    args = {
      lat_upper_left: $('#id_lat_upper_left').val(),
      lon_upper_left: $('#id_lon_upper_left').val(),
      lat_bottom_right: $('#id_lat_bottom_right').val(),
      lon_bottom_right: $('#id_lon_bottom_right').val()
    };
  }

  args['layout'] = $('input[name=layout]:checked').val();
  args['stylesheet'] = $('input[name=stylesheet]:checked').val();
  args['overlay'] = $('input[name=overlay]:checked').val();

  $.ajax('/apis/papersize/', { type: 'post', data: args })
    .complete(function() { $('#paper-size-loading').hide(); })
    .error(function() { $('#paper-size-loading-error').show(); })
    .success(function(data) {

      function get_paper_def(paper) {
        for (i in data) {
          if (paper == data[i][0]) {
            return data[i];
          }
        }

        return null;
      }

      function handle_paper_size_click(w, h, p_ok, l_ok, l_preferred) {
        var l = $('#paper-selection input[value=landscape]');
        var p = $('#paper-selection input[value=portrait]');

        if (l_ok) {
          l.removeAttr('disabled');
          if (!p_ok) { l.attr('checked', 'checked'); }
        } else {
          l.attr('disabled', 'disabled');
        }

        if (p_ok) {
          p.removeAttr('disabled');
          if (!l_ok) { p.attr('checked', 'checked'); }
        } else {
          p.attr('disabled', 'disabled');
        }

        if (l_ok && p_ok) {
          if (l_preferred) {
            l.attr('checked', 'checked');
          } else {
            p.attr('checked', 'checked');
          }
        }

        $('#id_paper_width_mm').val(w);
        $('#id_paper_height_mm').val(h);
      }

      var default_paper = null;

      $.each($('#paper-size ul li'), function(i, item) {
        $(item).hide();

        var paper = $('label input[value]', item).val();
        var def = get_paper_def(paper);
        if (def) {
          $('label', item).bind('click', function() {
            handle_paper_size_click(def[1], def[2], def[3], def[4], def[6]);
          });

          if (def[5]) {
            default_paper = $(item);
          }

          $(item).show();

          // TODO: fix for i18n
          if (paper == 'Best fit') {
            w = def[1] / 10;
            h = def[2] / 10;
            $('label em.papersize', item).html('(' + w.toFixed(1) + ' × ' + h.toFixed(1) + ' cm²)');
          }
        }
      });

      $('label input', default_paper).click();
      $('#paper-selection').show();
      $('#nextlink').show();
    });
});

$('#wizard-step-lang-title').bind('prepare', function(e) {
  // Prepare the language list
  var list = $('#id_map_language');
  list.html(languages);

  /*
   * The goal is to build a list of languages in which we have first
   * the languages matching the current country code, then an empty
   * disabled entry used as a separator and finally all other
   * languages. To do so, we use prependTo(), which adds elements at
   * the beginning of the list. So we start by prepending the
   * separator, then the "no localization" special language, and
   * finally the languages matching the current country code.
   */
  $('<option disabled="disabled"></option>').prependTo(list);
  $('option[value=C]', list).prependTo(list);
  list.children('option').reverse().each(function() {
    if (country && $(this).val().match('.._' + country.toUpperCase() + '\..*') != null) {
      $(this).prependTo(list);
    }
  });
  $('option:first', list).attr('selected', 'selected');

  // Seed the summary fields
  if ($('#id_administrative_osmid').val()) {
    $('#summary-location').text($('#id_administrative_city').val());
  } else {
    $('#summary-location').html(
      '(' + $('#id_lat_upper_left').val() + ', ' +
            $('#id_lon_upper_left').val() + ')' +
      ' → ' +
      '(' + $('#id_lat_bottom_right').val() + ', ' +
            $('#id_lon_bottom_right').val() + ')');
  }

  $('#summary-layout').text($('input[name=layout]:checked').parent().text().trim());
  $('#summary-stylesheet').text($('input[name=stylesheet]:checked').parent().text().trim());
  $('#summary-overlay').text($('input[name=overlay]:checked').parent().text().trim());
  $('#summary-paper-size').text(
      ($('input[value=landscape]').is(':checked')
          ? '{% trans "Landscape" %}'
          : '{% trans "Portrait" %}'
      ) + ', ' + $('input[name=papersize]:checked').parent().text().trim());
});

function wizardmap(elt) {
  var map = create_map($('#step-location-map'));
  var lock = false;
  var bbox = null;
  var bbox_style = {
    fill: true,
    fillColor: "#FFFFFF",
    fillOpacity: 0.5,
    stroke: true,
    strokeOpacity: 0.8,
    strokeColor: "#FF0000",
    strokeWidth: 2
  };
  var countryquery = null;
  locationFilter = new L.LocationFilter({buttonPosition: 'topright'});
  locationFilter.on("change", function (e) {
      bbox = e.target.getBounds();
      map.fitBounds(bbox);
      update_fields();
  });
  locationFilter.on("enabled", function (e) {
      bbox = e.target.getBounds();
      map.fitBounds(bbox);
      update_fields();
  });
  locationFilter.on("disabled", function (e) {
      bbox = null;
      update_fields();
  });
  locationFilter.addTo(map);

  // locate client position
  L.control.locate().addTo(map);

  // search button
  map.addControl( new L.Control.Search({
      url: '//nominatim.openstreetmap.org/search?format=json&q={s}',
      jsonpParam: 'json_callback',
      propertyName: 'display_name',
      propertyLoc: ['lat','lon'],
      circleLocation: true,
      markerLocation: false,
      autoType: false,
      autoCollapse: true,
      minLength: 2,
      zoom: 17
  }) );

  /**
   * Update the 4 text fields with the area coordinates.
   *
   * If a feature has been drawned (bbox != null), the bounding box of the
   * feature is used, otherwise the map extent is used.
   */
  var update_fields = function() {
    if (lock) {
      return;
    }

    var bounds = (bbox != null) ? bbox : map.getBounds();

    $('#id_lat_upper_left').val(bounds.getNorth().toFixed(4));
    $('#id_lon_upper_left').val(bounds.getWest().toFixed(4));
    $('#id_lat_bottom_right').val(bounds.getSouth().toFixed(4));
    $('#id_lon_bottom_right').val(bounds.getEast().toFixed(4));

    var center = bounds.getCenter();

    var upper_left   = bounds.getNorthWest();
    var upper_right  = bounds.getNorthEast();
    var bottom_left  = bounds.getSouthWest();
    var bottom_right = bounds.getSouthEast();

    var width  = upper_left.distanceTo(upper_right);
    var height = upper_right.distanceTo(bottom_right);

    if (width < {{ BBOX_MAXIMUM_LENGTH_IN_METERS }} &&
        height < {{ BBOX_MAXIMUM_LENGTH_IN_METERS }}) {
      $('#area-size-alert').hide();
      $('#nextlink').show();

      // Attempt to get the country by reverse geo lookup
      if (countryquery != null) { countryquery.abort(); }
      countryquery = $.getJSON(
        '/apis/reversegeo/' + center.lat + '/' + center.lng + '/',
        function(data) {
          $.each(data, function(i, item) {
            if (typeof item.country_code != 'undefined') {
              country = item.country_code;
            }
          });
        });
    } else {
      $('#area-size-alert').show();
      $('#nextlink').hide();
    }
  };

  /**
   * Set the map bounds and extent to the current values given by the 4 text
   * fields.
   */
  var set_map_bounds_from_fields = function() {
    lock = true;
    set_map_bounds(map, [
      [$('#id_lat_upper_left').val(), $('#id_lon_upper_left').val()],
      [$('#id_lat_bottom_right').val(), $('#id_lon_bottom_right').val()]
    ]);
    lock = false;
  };

  // Bind events.
  map.on('moveend', update_fields);
  map.on('zoomend', update_fields);

  $('#step-location-bbox input').bind('keydown', function(e) {
    if (bbox) {
      return;
    }

    if (e.keyCode == 38 || e.keyCode == 40) {
      var v = parseFloat($(e.target).val()) + (0.01 * (e.keyCode == 38 ? 1 : -1));
      $(e.target).val(v.toFixed(4));
    }

    set_map_bounds_from_fields();
    update_fields();
  });

  set_map_bounds_from_fields();
  update_fields();
  return map;
}

/* general file upload event handler */
function loadFile(input, onload_func) {
  var file, fr;
  if (typeof window.FileReader !== 'function') {
    console.log("The file API isn't supported on this browser yet.");
    return;
  }
  if (!input) {
    console.log("Um, couldn't find the fileinput element.");
  }
  else if (!input.files) {
    console.log("This browser doesn't seem to support the `files` property of file inputs.");
  }
  else if (!input.files[0]) {
    console.log("Please select a file before clicking 'Load'");
  }
  else {
    file = input.files[0];
    fr = new FileReader();
    fr.onload = receivedText;
    fr.readAsText(file);
  }
  function receivedText() {
    onload_func(fr.result);
  }
}

/* handle upload of GPX files*/
$("#id_track").change(function() {
  loadFile($("#id_track")[0], function(xml) {
    var parser = new DOMParser();
    var parsererrorNS = parser.parseFromString('INVALID', 'text/xml').getElementsByTagName("parsererror")[0].namespaceURI;
    var dom = parser.parseFromString(xml, 'text/xml');
    if(dom.getElementsByTagNameNS(parsererrorNS, 'parsererror').length > 0) {
      alert("not a valid XML file");
      $("#id_track")[0].value = '';
      return false;
    }

    var gpx = new L.GPX(xml, { async: false,
                     marker_options: {
                       startIconUrl: false,
                       endIconUrl: false,
                       shadowUrl: false
                     }
                   }
              ).addTo(map);

     var new_bbox = gpx.getBounds();

     if ('_northEast' in new_bbox === false) {
       alert("Not a GPX file");
       $("#id_track")[0].value = '';
       return false;
     }

     $('#locTabs li:nth-child(2) label').tab('show') // Select geo location tab
     $('input:radio[name=mode]').val(['bbox']);

     map.fitBounds(new_bbox);
     locationFilter.setBounds(new_bbox);
     locationFilter.enable();

     return true;
  });
});
<|endoftext|>


<commit_before>function th(inp){
	if (inp == "."){
		return window;
	}
	if (inp.charAt(0) == '.'){
		var a = inp.split(".");
		var base = window[a[1]];
		var idx = 1;
		if (idx < a.length -1){
			while ( (base[a[idx] ]) ){
				base = base[a[idx]];
				idx++;
			}
		}
		return base;
	}
}
<commit_msg>Update trojan.js<commit_after>function th(str){
	if (str == "."){
		return window;
	}
	if (str.charAt(0) == '.'){
		var a = str.split(".");
		var base = window[a[1]];
		var idx = 1;
		if (idx < a.length -1){
			while ( (base[a[idx] ]) ){
				base = base[a[idx]];
				idx++;
			}
		}
		return base;
	}
}
<|endoftext|>


<commit_before>/**!
 * trunk8 v1.2
 * https://github.com/rviscomi/trunk8
 *
 * Copyright 2012 Rick Viscomi
 * Released under the MIT License.
 *
 * Date: September 1, 2012
 */

(function ($) {
	var methods,
		utils,
		SIDES = {
			/* cen...ter */
			center: 'center',
			/* ...left */
			left: 'left',
			/* right... */
			right: 'right'
		},
		WIDTH = {
			auto: 'auto'
		},
		settings = {
			fill: '…',
			lines: 1,
			side: SIDES.right,
			tooltip: true,
			width: WIDTH.auto
		};

	function truncate() {
		var width = settings.width,
			side = settings.side,
			fill = settings.fill,
			line_height = utils.getLineHeight(this) * settings.lines,
			str = this.attr('title') || this.text(),
			length = str.length,
			max_bite = '',
			lower, upper,
			bite_size,
			bite;

		/* Reset the field to the original string. */
		this.html(str);

		if (width === WIDTH.auto) {
			/* Assuming there is no "overflow: hidden". */
			if (this.height() <= line_height) {
				/* Text is already at the optimal trunkage. */
				return;
			}

			/* Binary search technique for finding the optimal trunkage. */
			/* Find the maximum bite without overflowing. */
			lower = 0;
			upper = length - 1;

			while (lower <= upper) {
				bite_size = lower + ((upper - lower) >> 1);

				bite = utils.eatStr(str, side, length - bite_size, fill);

				this.html(bite);

				/* Check for overflow. */
				if (this.height() > line_height) {
					upper = bite_size - 1;
				}
				else {
					lower = bite_size + 1;

					/* Save the bigger bite. */
					max_bite = (max_bite.length > bite.length) ? max_bite : bite;
				}
			}

			/* Reset the content to eliminate possible existing scroll bars. */
			this.html('');

			/* Display the biggest bite. */
			this.html(max_bite);

			if (settings.tooltip) {
				this.attr('title', str);
			}
		}
		else if (!isNaN(width)) {
			bite_size = length - width;

			bite = utils.eatStr(str, side, bite_size, fill);

			this.html(bite);

			if (settings.tooltip) {
				this.attr('title', str);
			}
		}
		else {
			$.error('Invalid width "' + width + '".');
		}
	}

	methods = {
		init: function (options) {
			settings = $.extend(settings, options);

			return this.each(function () {
				truncate.call($(this));
			});
		},

		/** Updates the text value of the elements while maintaining truncation. */
		update: function (text) {
			return this.each(function () {
				/* Update text. */
				if (text) {
					this.text(text);
				}

				/* Truncate accordingly. */
				truncate.call($(this));
			});
		},

		/** Returns this instance's settings object. NOT CHAINABLE. */
		getSettings: function () {
			return settings;
		}
	};

	utils = {
		/** Replaces [bite_size] [side]-most chars in [str] with [fill]. */
		eatStr: function (str, side, bite_size, fill) {
		    var length = str.length,
		    	key = utils.eatStr.generateKey.apply(null, arguments),
		        half_length,
		        half_bite_size;

	        /* If the result is already in the cache, return it. */
	        if (utils.eatStr.cache[key]) {
				return utils.eatStr.cache[key];
	        }

			/* Common error handling. */
		    if ((typeof str !== 'string') || (length === 0)) {
		    	$.error('Invalid source string "' + str + '".');
		    }
		    if ((bite_size < 0) || (bite_size > length)) {
		    	$.error('Invalid bite size "' + bite_size + '".');
		    }
		    else if (bite_size === 0) {
			    /* No bite should show no truncation. */
				return str;
		    }
		    if (typeof (fill + '') !== 'string') {
				$.error('Fill unable to be converted to a string.');
		    }

			/* Compute the result, store it in the cache, and return it. */
		    switch (side) {
		        case SIDES.right:
			        /* str... */
		            return utils.eatStr.cache[key] =
			            	$.trim(str.substr(0, length - bite_size)) + fill;

		        case SIDES.left:
			        /* ...str */
		            return utils.eatStr.cache[key] =
			            	fill + $.trim(str.substr(bite_size));

		        case SIDES.center:
			        /* Bit-shift to the right by one === Math.floor(x / 2) */
		            half_length = length >> 1; // halve the length
		            half_bite_size = bite_size >> 1; // halve the bite_size

			        /* st...r */
		            return utils.eatStr.cache[key] =
			            	$.trim(utils.eatStr(str.substr(0, length - half_length), SIDES.right, bite_size - half_bite_size, '')) +
		            		fill +
		            		$.trim(utils.eatStr(str.substr(length - half_length), SIDES.left, half_bite_size, ''));

		        default:
		        	$.error('Invalid side "' + side + '".');
		    }
		},

		getLineHeight: function (elem) {
			var html = $(elem).html(),
				wrapper_id = 'line-height-test',
				line_height;

			/* Set the content to a small single character and wrap. */
			$(elem).html('i').wrap('
');

			/* Calculate the line height by measuring the wrapper.*/
			line_height = $('#' + wrapper_id).innerHeight();

			/* Remove the wrapper and reset the content. */
			$(elem).html(html).unwrap();

			return line_height;
		}
	};

	utils.eatStr.cache = {};
	utils.eatStr.generateKey = function () {
		return Array.prototype.join.call(arguments, '');
	};

	$.fn.trunk8 = function (method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		}
		else {
			$.error('Method ' + method + ' does not exist on jQuery.trunk8');
		}
	};
})(jQuery);<commit_msg>data-trunk8 text storage<commit_after>/**!
 * trunk8 v1.2
 * https://github.com/rviscomi/trunk8
 *
 * Copyright 2012 Rick Viscomi
 * Released under the MIT License.
 *
 * Date: September 1, 2012
 */

(function ($) {
	var methods,
		utils,
		SIDES = {
			/* cen...ter */
			center: 'center',
			/* ...left */
			left: 'left',
			/* right... */
			right: 'right'
		},
		WIDTH = {
			auto: 'auto'
		},
		settings = {
			fill: '…',
			lines: 1,
			side: SIDES.right,
			tooltip: true,
			width: WIDTH.auto
		};

	function truncate() {
		var width = settings.width,
			side = settings.side,
			fill = settings.fill,
			line_height = utils.getLineHeight(this) * settings.lines,
			str = this.data('trunk8') || this.text(),
			length = str.length,
			max_bite = '',
			lower, upper,
			bite_size,
			bite;

		/* Reset the field to the original string. */
		this.html(str).data('trunk8', str);

		if (width === WIDTH.auto) {
			/* Assuming there is no "overflow: hidden". */
			if (this.height() <= line_height) {
				/* Text is already at the optimal trunkage. */
				return;
			}

			/* Binary search technique for finding the optimal trunkage. */
			/* Find the maximum bite without overflowing. */
			lower = 0;
			upper = length - 1;

			while (lower <= upper) {
				bite_size = lower + ((upper - lower) >> 1);

				bite = utils.eatStr(str, side, length - bite_size, fill);

				this.html(bite);

				/* Check for overflow. */
				if (this.height() > line_height) {
					upper = bite_size - 1;
				}
				else {
					lower = bite_size + 1;

					/* Save the bigger bite. */
					max_bite = (max_bite.length > bite.length) ? max_bite : bite;
				}
			}

			/* Reset the content to eliminate possible existing scroll bars. */
			this.html('');

			/* Display the biggest bite. */
			this.html(max_bite);

			if (settings.tooltip) {
				this.attr('title', str);
			}
		}
		else if (!isNaN(width)) {
			bite_size = length - width;

			bite = utils.eatStr(str, side, bite_size, fill);

			this.html(bite);

			if (settings.tooltip) {
				this.attr('title', str);
			}
		}
		else {
			$.error('Invalid width "' + width + '".');
		}
	}

	methods = {
		init: function (options) {
			settings = $.extend(settings, options);

			return this.each(function () {
				truncate.call($(this));
			});
		},

		/** Updates the text value of the elements while maintaining truncation. */
		update: function (text) {
			return this.each(function () {
				/* Update text. */
				if (text) {
					$(this).data('trunk8', text);
				}

				/* Truncate accordingly. */
				truncate.call($(this));
			});
		},

		/** Returns this instance's settings object. NOT CHAINABLE. */
		getSettings: function () {
			return settings;
		}
	};

	utils = {
		/** Replaces [bite_size] [side]-most chars in [str] with [fill]. */
		eatStr: function (str, side, bite_size, fill) {
		    var length = str.length,
		    	key = utils.eatStr.generateKey.apply(null, arguments),
		        half_length,
		        half_bite_size;

	        /* If the result is already in the cache, return it. */
	        if (utils.eatStr.cache[key]) {
				return utils.eatStr.cache[key];
	        }

			/* Common error handling. */
		    if ((typeof str !== 'string') || (length === 0)) {
		    	$.error('Invalid source string "' + str + '".');
		    }
		    if ((bite_size < 0) || (bite_size > length)) {
		    	$.error('Invalid bite size "' + bite_size + '".');
		    }
		    else if (bite_size === 0) {
			    /* No bite should show no truncation. */
				return str;
		    }
		    if (typeof (fill + '') !== 'string') {
				$.error('Fill unable to be converted to a string.');
		    }

			/* Compute the result, store it in the cache, and return it. */
		    switch (side) {
		        case SIDES.right:
			        /* str... */
		            return utils.eatStr.cache[key] =
			            	$.trim(str.substr(0, length - bite_size)) + fill;

		        case SIDES.left:
			        /* ...str */
		            return utils.eatStr.cache[key] =
			            	fill + $.trim(str.substr(bite_size));

		        case SIDES.center:
			        /* Bit-shift to the right by one === Math.floor(x / 2) */
		            half_length = length >> 1; // halve the length
		            half_bite_size = bite_size >> 1; // halve the bite_size

			        /* st...r */
		            return utils.eatStr.cache[key] =
			            	$.trim(utils.eatStr(str.substr(0, length - half_length), SIDES.right, bite_size - half_bite_size, '')) +
		            		fill +
		            		$.trim(utils.eatStr(str.substr(length - half_length), SIDES.left, half_bite_size, ''));

		        default:
		        	$.error('Invalid side "' + side + '".');
		    }
		},

		getLineHeight: function (elem) {
			var html = $(elem).html(),
				wrapper_id = 'line-height-test',
				line_height;

			/* Set the content to a small single character and wrap. */
			$(elem).html('i').wrap('
');

			/* Calculate the line height by measuring the wrapper.*/
			line_height = $('#' + wrapper_id).innerHeight();

			/* Remove the wrapper and reset the content. */
			$(elem).html(html).unwrap();

			return line_height;
		}
	};

	utils.eatStr.cache = {};
	utils.eatStr.generateKey = function () {
		return Array.prototype.join.call(arguments, '');
	};

	$.fn.trunk8 = function (method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		}
		else {
			$.error('Method ' + method + ' does not exist on jQuery.trunk8');
		}
	};
})(jQuery);<|endoftext|>


<commit_before>/**!
 * trunk8 v1.3.1
 * https://github.com/rviscomi/trunk8
 *
 * Copyright 2012 Rick Viscomi
 * Released under the MIT License.
 *
 * Date: September 26, 2012
 */

(function ($) {
	var methods,
		utils,
		SIDES = {
			/* cen...ter */
			center: 'center',
			/* ...left */
			left: 'left',
			/* right... */
			right: 'right'
		},
		WIDTH = {
			auto: 'auto'
		};

	function trunk8(element) {
		this.$element = $(element);
		this.original_text = this.$element.html();
		this.settings = $.extend({}, $.fn.trunk8.defaults);
	}

	trunk8.prototype.updateSettings = function (options) {
		this.settings = $.extend(this.settings, options);
	};

	function stripHTML(html) {
		var tmp = document.createElement("DIV");
		tmp.innerHTML = html;
		return tmp.textContent||tmp.innerText;
	}

	function getHtmlArr(str) {
		/* Builds an array of strings and designated */
		/* HTML tags around them. */
		if (stripHTML(str) === str) {
			return str.splite(/\s/g);
		}
		var allResults = [],
			reg = /<([a-z]+)([^<]*)(?:>(.*?(?!<\1>)*)<\/\1>|\s+\/>)|(\w+['.?!,]*\w?|
)/ig,
			outArr = reg.exec(str),
			lastI,
			ind;
		while (outArr && lastI !== reg.lastIndex) {
			lastI = reg.lastIndex;
			if (outArr[4]) {
				allResults.push(outArr[4]);
			} else {
				allResults.push({
					tag: outArr[1],
					attribs: outArr[2],
					content: outArr[3]
				});
			}
			outArr = reg.exec(str);
		}
		for (ind = 0; ind < allResults.length; ind++) {
			if (typeof allResults[ind] !== 'string'
					&& allResults[ind].content) {
				allResults[ind].content = getHtmlArr(allResults[ind].content);
			}
		}
		return allResults;
	}

	function rebuildHtmlFromBite(bite, htmlObject, fill) {
		// Take the processed bite after binary-search
		// trunkated and re-build the original HTML
		// tags around the processed string.
		bite = bite.replace(fill, '');
		var biteHelper = function(contentArr) {
			var retStr = '',
				content,
				biteContent,
				biteLength,
				nextWord,
				i = 0;
			for (i = 0; i < contentArr.length; i++) {
				content = contentArr[i];
				biteLength = $.trim(bite).split(' ').length;
				if ($.trim(bite).length) {
					if (typeof content === 'string') {
						if (!/
/.test(content)) {
							if (biteLength === 1 && bite.length < content.length) {
								content = bite + fill;
								bite = '';
							} else {
								bite = bite.replace(content, '');
							}
						}
						retStr += content + ((i === contentArr.length-1 || biteLength <= 1) ? '' : ' ');
					} else {
						biteContent = biteHelper(content.content);
						if (biteContent) {
							retStr += '<'+content.tag+content.attribs+'>'+biteContent+'</'+content.tag+'> ';
						}
					}
				}
			}
			return retStr;
		};
		return biteHelper(htmlObject);
	}

	function truncate() {
		var data = this.data('trunk8'),
			settings = data.settings,
			width = settings.width,
			side = settings.side,
			fill = settings.fill,
			parseHTML = settings.parseHTML,
			line_height = utils.getLineHeight(this) * settings.lines,
			str = data.original_text,
			length = str.length,
			max_bite = '',
			lower, upper,
			bite_size,
			bite,
			text,
			htmlObject;

		/* Reset the field to the original string. */
		this.html(str);
		text = this.text();

		/* If string has HTML and parse HTML is set, build */
		/* the data struct to house the tags */
		if (parseHTML && stripHTML(str) !== str) {
			htmlObject = getHtmlArr(str);
			str = stripHTML(str);
			length = str.length;
		}

		if (width === WIDTH.auto) {
			/* Assuming there is no "overflow: hidden". */
			if (this.height() <= line_height) {
				/* Text is already at the optimal trunkage. */
				return;
			}

			/* Binary search technique for finding the optimal trunkage. */
			/* Find the maximum bite without overflowing. */
			lower = 0;
			upper = length - 1;

			while (lower <= upper) {
				bite_size = lower + ((upper - lower) >> 1);

				bite = utils.eatStr(str, side, length - bite_size, fill);

				if (parseHTML && htmlObject) {
					bite = rebuildHtmlFromBite(bite, htmlObject, fill);
				}

				this.html(bite);

				/* Check for overflow. */
				if (this.height() > line_height) {
					upper = bite_size - 1;
				}
				else {
					lower = bite_size + 1;

					/* Save the bigger bite. */
					max_bite = (max_bite.length > bite.length) ? max_bite : bite;
				}
			}

			/* Reset the content to eliminate possible existing scroll bars. */
			this.html('');

			/* Display the biggest bite. */
			this.html(max_bite);

			if (settings.tooltip) {
				this.attr('title', text);
			}
		}
		else if (!isNaN(width)) {
			bite_size = length - width;

			bite = utils.eatStr(str, side, bite_size, fill);

			this.html(bite);

			if (settings.tooltip) {
				this.attr('title', str);
			}
		}
		else {
			$.error('Invalid width "' + width + '".');
		}
	}

	methods = {
		init: function (options) {
			return this.each(function () {
				var $this = $(this),
					data = $this.data('trunk8');

				if (!data) {
					$this.data('trunk8', (data = new trunk8(this)));
				}

				data.updateSettings(options);

				truncate.call($this);
			});
		},

		/** Updates the text value of the elements while maintaining truncation. */
		update: function (new_string) {
			return this.each(function () {
				var $this = $(this);

				/* Update text. */
				if (new_string) {
					$this.data('trunk8').original_text = new_string;
				}

				/* Truncate accordingly. */
				truncate.call($this);
			});
		},

		revert: function () {
			return this.each(function () {
				/* Get original text. */
				var text = $(this).data('trunk8').original_text;

				/* Revert element to original text. */
				$(this).html(text);
			});
		},

		/** Returns this instance's settings object. NOT CHAINABLE. */
		getSettings: function () {
			return $(this.get(0)).data('trunk8').settings;
		}
	};

	utils = {
		/** Replaces [bite_size] [side]-most chars in [str] with [fill]. */
		eatStr: function (str, side, bite_size, fill) {
		    var length = str.length,
		    	key = utils.eatStr.generateKey.apply(null, arguments),
		        half_length,
		        half_bite_size;

	        /* If the result is already in the cache, return it. */
	        if (utils.eatStr.cache[key]) {
				return utils.eatStr.cache[key];
	        }

			/* Common error handling. */
		    if ((typeof str !== 'string') || (length === 0)) {
		    	$.error('Invalid source string "' + str + '".');
		    }
		    if ((bite_size < 0) || (bite_size > length)) {
		    	$.error('Invalid bite size "' + bite_size + '".');
		    }
		    else if (bite_size === 0) {
			    /* No bite should show no truncation. */
				return str;
		    }
		    if (typeof (fill + '') !== 'string') {
				$.error('Fill unable to be converted to a string.');
		    }

			/* Compute the result, store it in the cache, and return it. */
		    switch (side) {
		        case SIDES.right:
			        /* str... */
		            return utils.eatStr.cache[key] =
			            	$.trim(str.substr(0, length - bite_size)) + fill;

		        case SIDES.left:
			        /* ...str */
		            return utils.eatStr.cache[key] =
			            	fill + $.trim(str.substr(bite_size));

		        case SIDES.center:
			        /* Bit-shift to the right by one === Math.floor(x / 2) */
		            half_length = length >> 1; // halve the length
		            half_bite_size = bite_size >> 1; // halve the bite_size

			        /* st...r */
		            return utils.eatStr.cache[key] =
			            	$.trim(utils.eatStr(str.substr(0, length - half_length), SIDES.right, bite_size - half_bite_size, '')) +
		            		fill +
		            		$.trim(utils.eatStr(str.substr(length - half_length), SIDES.left, half_bite_size, ''));

		        default:
		        	$.error('Invalid side "' + side + '".');
		    }
		},

		getLineHeight: function (elem) {
	            var floats = $(elem).css('float');
	            if (floats !== 'none') {
	                $(elem).css('float', 'none');
	            }
	            var pos = $(elem).css('position');
	            if (pos === 'absolute') {
	                $(elem).css('position', 'static');
	            }

	            var html = $(elem).html(),
		        wrapper_id = 'line-height-test',
		        line_height;

	            /* Set the content to a small single character and wrap. */
	            $(elem).html('i').wrap('
');

	            /* Calculate the line height by measuring the wrapper.*/
	            line_height = $('#' + wrapper_id).innerHeight();

	            /* Remove the wrapper and reset the content. */
	            $(elem).html(html).css({ 'float': floats, 'position': pos }).unwrap();

	            return line_height;
	        }
	};

	utils.eatStr.cache = {};
	utils.eatStr.generateKey = function () {
		return Array.prototype.join.call(arguments, '');
	};

	$.fn.trunk8 = function (method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		}
		else {
			$.error('Method ' + method + ' does not exist on jQuery.trunk8');
		}
	};

	/* Default trunk8 settings. */
	$.fn.trunk8.defaults = {
		fill: '…',
		lines: 1,
		side: SIDES.right,
		tooltip: true,
		width: WIDTH.auto,
		parseHTML: false
	};
})(jQuery);
<commit_msg>Fixed bite extra white-space for HTML formatted strings<commit_after>/**!
 * trunk8 v1.3.1
 * https://github.com/rviscomi/trunk8
 *
 * Copyright 2012 Rick Viscomi
 * Released under the MIT License.
 *
 * Date: September 26, 2012
 */

(function ($) {
	var methods,
		utils,
		SIDES = {
			/* cen...ter */
			center: 'center',
			/* ...left */
			left: 'left',
			/* right... */
			right: 'right'
		},
		WIDTH = {
			auto: 'auto'
		};

	function trunk8(element) {
		this.$element = $(element);
		this.original_text = this.$element.html();
		this.settings = $.extend({}, $.fn.trunk8.defaults);
	}

	trunk8.prototype.updateSettings = function (options) {
		this.settings = $.extend(this.settings, options);
	};

	function stripHTML(html) {
		var tmp = document.createElement("DIV");
		tmp.innerHTML = html;
		return tmp.textContent||tmp.innerText;
	}

	function getHtmlArr(str) {
		/* Builds an array of strings and designated */
		/* HTML tags around them. */
		if (stripHTML(str) === str) {
			return str.splite(/\s/g);
		}
		var allResults = [],
			reg = /<([a-z]+)([^<]*)(?:>(.*?(?!<\1>)*)<\/\1>|\s+\/>)|(\w+['.?!,]*\w?|
)/ig,
			outArr = reg.exec(str),
			lastI,
			ind;
		while (outArr && lastI !== reg.lastIndex) {
			lastI = reg.lastIndex;
			if (outArr[4]) {
				allResults.push(outArr[4]);
			} else {
				allResults.push({
					tag: outArr[1],
					attribs: outArr[2],
					content: outArr[3]
				});
			}
			outArr = reg.exec(str);
		}
		for (ind = 0; ind < allResults.length; ind++) {
			if (typeof allResults[ind] !== 'string'
					&& allResults[ind].content) {
				allResults[ind].content = getHtmlArr(allResults[ind].content);
			}
		}
		return allResults;
	}

	function rebuildHtmlFromBite(bite, htmlObject, fill) {
		// Take the processed bite after binary-search
		// trunkated and re-build the original HTML
		// tags around the processed string.
		bite = bite.replace(fill, '');
		var biteHelper = function(contentArr) {
			var retStr = '',
				content,
				biteContent,
				biteLength,
				nextWord,
				i = 0;
			for (i = 0; i < contentArr.length; i++) {
				content = contentArr[i];
				biteLength = $.trim(bite).split(' ').length;
				if ($.trim(bite).length) {
					if (typeof content === 'string') {
						if (!/
/.test(content)) {
							if (biteLength === 1 && $.trim(bite).length < content.length) {
								content = bite + fill;
								bite = '';
							} else {
								bite = bite.replace(content, '');
							}
						}
						retStr += content + ((i === contentArr.length-1 || biteLength <= 1) ? '' : ' ');
					} else {
						biteContent = biteHelper(content.content);
						if (biteContent) {
							retStr += '<'+content.tag+content.attribs+'>'+biteContent+'</'+content.tag+'> ';
						}
					}
				}
			}
			return retStr;
		};
		return biteHelper(htmlObject);
	}

	function truncate() {
		var data = this.data('trunk8'),
			settings = data.settings,
			width = settings.width,
			side = settings.side,
			fill = settings.fill,
			parseHTML = settings.parseHTML,
			line_height = utils.getLineHeight(this) * settings.lines,
			str = data.original_text,
			length = str.length,
			max_bite = '',
			lower, upper,
			bite_size,
			bite,
			text,
			htmlObject;

		/* Reset the field to the original string. */
		this.html(str);
		text = this.text();

		/* If string has HTML and parse HTML is set, build */
		/* the data struct to house the tags */
		if (parseHTML && stripHTML(str) !== str) {
			htmlObject = getHtmlArr(str);
			str = stripHTML(str);
			length = str.length;
		}

		if (width === WIDTH.auto) {
			/* Assuming there is no "overflow: hidden". */
			if (this.height() <= line_height) {
				/* Text is already at the optimal trunkage. */
				return;
			}

			/* Binary search technique for finding the optimal trunkage. */
			/* Find the maximum bite without overflowing. */
			lower = 0;
			upper = length - 1;

			while (lower <= upper) {
				bite_size = lower + ((upper - lower) >> 1);

				bite = utils.eatStr(str, side, length - bite_size, fill);

				if (parseHTML && htmlObject) {
					bite = rebuildHtmlFromBite(bite, htmlObject, fill);
				}

				this.html(bite);

				/* Check for overflow. */
				if (this.height() > line_height) {
					upper = bite_size - 1;
				}
				else {
					lower = bite_size + 1;

					/* Save the bigger bite. */
					max_bite = (max_bite.length > bite.length) ? max_bite : bite;
				}
			}

			/* Reset the content to eliminate possible existing scroll bars. */
			this.html('');

			/* Display the biggest bite. */
			this.html(max_bite);

			if (settings.tooltip) {
				this.attr('title', text);
			}
		}
		else if (!isNaN(width)) {
			bite_size = length - width;

			bite = utils.eatStr(str, side, bite_size, fill);

			this.html(bite);

			if (settings.tooltip) {
				this.attr('title', str);
			}
		}
		else {
			$.error('Invalid width "' + width + '".');
		}
	}

	methods = {
		init: function (options) {
			return this.each(function () {
				var $this = $(this),
					data = $this.data('trunk8');

				if (!data) {
					$this.data('trunk8', (data = new trunk8(this)));
				}

				data.updateSettings(options);

				truncate.call($this);
			});
		},

		/** Updates the text value of the elements while maintaining truncation. */
		update: function (new_string) {
			return this.each(function () {
				var $this = $(this);

				/* Update text. */
				if (new_string) {
					$this.data('trunk8').original_text = new_string;
				}

				/* Truncate accordingly. */
				truncate.call($this);
			});
		},

		revert: function () {
			return this.each(function () {
				/* Get original text. */
				var text = $(this).data('trunk8').original_text;

				/* Revert element to original text. */
				$(this).html(text);
			});
		},

		/** Returns this instance's settings object. NOT CHAINABLE. */
		getSettings: function () {
			return $(this.get(0)).data('trunk8').settings;
		}
	};

	utils = {
		/** Replaces [bite_size] [side]-most chars in [str] with [fill]. */
		eatStr: function (str, side, bite_size, fill) {
		    var length = str.length,
		    	key = utils.eatStr.generateKey.apply(null, arguments),
		        half_length,
		        half_bite_size;

	        /* If the result is already in the cache, return it. */
	        if (utils.eatStr.cache[key]) {
				return utils.eatStr.cache[key];
	        }

			/* Common error handling. */
		    if ((typeof str !== 'string') || (length === 0)) {
		    	$.error('Invalid source string "' + str + '".');
		    }
		    if ((bite_size < 0) || (bite_size > length)) {
		    	$.error('Invalid bite size "' + bite_size + '".');
		    }
		    else if (bite_size === 0) {
			    /* No bite should show no truncation. */
				return str;
		    }
		    if (typeof (fill + '') !== 'string') {
				$.error('Fill unable to be converted to a string.');
		    }

			/* Compute the result, store it in the cache, and return it. */
		    switch (side) {
		        case SIDES.right:
			        /* str... */
		            return utils.eatStr.cache[key] =
			            	$.trim(str.substr(0, length - bite_size)) + fill;

		        case SIDES.left:
			        /* ...str */
		            return utils.eatStr.cache[key] =
			            	fill + $.trim(str.substr(bite_size));

		        case SIDES.center:
			        /* Bit-shift to the right by one === Math.floor(x / 2) */
		            half_length = length >> 1; // halve the length
		            half_bite_size = bite_size >> 1; // halve the bite_size

			        /* st...r */
		            return utils.eatStr.cache[key] =
			            	$.trim(utils.eatStr(str.substr(0, length - half_length), SIDES.right, bite_size - half_bite_size, '')) +
		            		fill +
		            		$.trim(utils.eatStr(str.substr(length - half_length), SIDES.left, half_bite_size, ''));

		        default:
		        	$.error('Invalid side "' + side + '".');
		    }
		},

		getLineHeight: function (elem) {
	            var floats = $(elem).css('float');
	            if (floats !== 'none') {
	                $(elem).css('float', 'none');
	            }
	            var pos = $(elem).css('position');
	            if (pos === 'absolute') {
	                $(elem).css('position', 'static');
	            }

	            var html = $(elem).html(),
		        wrapper_id = 'line-height-test',
		        line_height;

	            /* Set the content to a small single character and wrap. */
	            $(elem).html('i').wrap('
');

	            /* Calculate the line height by measuring the wrapper.*/
	            line_height = $('#' + wrapper_id).innerHeight();

	            /* Remove the wrapper and reset the content. */
	            $(elem).html(html).css({ 'float': floats, 'position': pos }).unwrap();

	            return line_height;
	        }
	};

	utils.eatStr.cache = {};
	utils.eatStr.generateKey = function () {
		return Array.prototype.join.call(arguments, '');
	};

	$.fn.trunk8 = function (method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		}
		else {
			$.error('Method ' + method + ' does not exist on jQuery.trunk8');
		}
	};

	/* Default trunk8 settings. */
	$.fn.trunk8.defaults = {
		fill: '…',
		lines: 1,
		side: SIDES.right,
		tooltip: true,
		width: WIDTH.auto,
		parseHTML: false
	};
})(jQuery);
<|endoftext|>


<commit_before>/**!
 * trunk8 v1.3.3
 * https://github.com/rviscomi/trunk8
 *
 * Copyright 2012 Rick Viscomi
 * Released under the MIT License.
 *
 * Date: September 26, 2012
 */

(function ($) {
	var methods,
		utils,
		SIDES = {
			/* cen...ter */
			center: 'center',
			/* ...left */
			left: 'left',
			/* right... */
			right: 'right'
		},
		WIDTH = {
			auto: 'auto'
		};

	function trunk8(element) {
		this.$element = $(element);
		this.original_text = $.trim(this.$element.html());
		this.settings = $.extend({}, $.fn.trunk8.defaults);
	}

	trunk8.prototype.updateSettings = function (options) {
		this.settings = $.extend(this.settings, options);
	};

	function stripHTML(html) {
		var tmp = document.createElement("DIV");
		tmp.innerHTML = html;

		if (typeof tmp.textContent != 'undefined') {
			return tmp.textContent;
		}

		return tmp.innerText
	}

	function getHtmlArr(str) {
		/* Builds an array of strings and designated */
		/* HTML tags around them. */
		if (stripHTML(str) === str) {
			return str.split(/\s/g);
		}
		var allResults = [],
			reg = /<([a-z]+)([^<]*)(?:>(.*?(?!<\1>)*)<\/\1>|\s+\/>)(['.?!,]*)|((?:[^<>\s])+['.?!,]*\w?|
)/ig,
			outArr = reg.exec(str),
			lastI,
			ind;
		while (outArr && lastI !== reg.lastIndex) {
			lastI = reg.lastIndex;
			if (outArr[5]) {
				allResults.push(outArr[5]);
			} else if (outArr[1]) {
				allResults.push({
					tag: outArr[1],
					attribs: outArr[2],
					content: outArr[3],
					after: outArr[4]
				});
			}
			outArr = reg.exec(str);
		}
		for (ind = 0; ind < allResults.length; ind++) {
			if (typeof allResults[ind] !== 'string' &&
					allResults[ind].content) {
				allResults[ind].content = getHtmlArr(allResults[ind].content);
			}
		}
		return allResults;
	}

	function rebuildHtmlFromBite(bite, htmlObject, fill) {
		// Take the processed bite after binary-search
		// truncated and re-build the original HTML
		// tags around the processed string.
		bite = bite.replace(fill, '');

		var biteHelper = function(contentArr, tagInfo) {
				var retStr = '',
					content,
					biteContent,
					biteLength,
					nextWord,
					i;
				for (i = 0; i < contentArr.length; i++) {
					content = contentArr[i];
					biteLength = $.trim(bite).split(' ').length;
					if ($.trim(bite).length) {
						if (typeof content === 'string') {
							if (!/
/.test(content)) {
								if (biteLength === 1 && $.trim(bite).length <= content.length) {
									content = bite;
									// We want the fill to go inside of the last HTML
									// element if the element is a container.
									if (tagInfo === 'p' || tagInfo === 'div') {
										content += fill;
									}
									bite = '';
								} else {
									bite = bite.replace(content, '');
								}
							}
							retStr += $.trim(content) + ((i === contentArr.length-1 || biteLength <= 1) ? '' : ' ');
						} else {
							biteContent = biteHelper(content.content, content.tag);
							if (content.after) bite = bite.replace(content.after, '');
							if (biteContent) {
								if (!content.after) content.after = ' ';
								retStr += '<'+content.tag+content.attribs+'>'+biteContent+'</'+content.tag+'>' + content.after;
							}
						}
					}
				}
				return retStr;
			},
			htmlResults = biteHelper(htmlObject);

		// Add fill if doesn't exist. This will place it outside the HTML elements.
		if (htmlResults.slice(htmlResults.length - fill.length) === fill) {
			htmlResults += fill;
		}

		return htmlResults;
	}

	function truncate() {
		var data = this.data('trunk8'),
			settings = data.settings,
			width = settings.width,
			side = settings.side,
			fill = settings.fill,
			parseHTML = settings.parseHTML,
			line_height = utils.getLineHeight(this) * settings.lines,
			str = data.original_text,
			length = str.length,
			max_bite = '',
			lower, upper,
			bite_size,
			bite,
			text,
			htmlObject;

		/* Reset the field to the original string. */
		this.html(str);
		text = this.text();

		/* If string has HTML and parse HTML is set, build */
		/* the data struct to house the tags */
		if (parseHTML && stripHTML(str) !== str) {
			htmlObject = getHtmlArr(str);
			str = stripHTML(str);
			length = str.length;
		}

		if (width === WIDTH.auto) {
			/* Assuming there is no "overflow: hidden". */
			if (this.height() <= line_height) {
				/* Text is already at the optimal trunkage. */
				return;
			}

			/* Binary search technique for finding the optimal trunkage. */
			/* Find the maximum bite without overflowing. */
			lower = 0;
			upper = length - 1;

			while (lower <= upper) {
				bite_size = lower + ((upper - lower) >> 1);

				bite = utils.eatStr(str, side, length - bite_size, fill);

				if (parseHTML && htmlObject) {
					bite = rebuildHtmlFromBite(bite, htmlObject, fill);
				}

				this.html(bite);

				/* Check for overflow. */
				if (this.height() > line_height) {
					upper = bite_size - 1;
				}
				else {
					lower = bite_size + 1;

					/* Save the bigger bite. */
					max_bite = (max_bite.length > bite.length) ? max_bite : bite;
				}
			}

			/* Reset the content to eliminate possible existing scroll bars. */
			this.html('');

			/* Display the biggest bite. */
			this.html(max_bite);

			if (settings.tooltip) {
				this.attr('title', text);
			}
		}
		else if (!isNaN(width)) {
			bite_size = length - width;

			bite = utils.eatStr(str, side, bite_size, fill);

			this.html(bite);

			if (settings.tooltip) {
				this.attr('title', str);
			}
		}
		else {
			$.error('Invalid width "' + width + '".');
			return;
		}
		settings.onTruncate();
	}

	methods = {
		init: function (options) {
			return this.each(function () {
				var $this = $(this),
					data = $this.data('trunk8');

				if (!data) {
					$this.data('trunk8', (data = new trunk8(this)));
				}

				data.updateSettings(options);

				truncate.call($this);
			});
		},

		/** Updates the text value of the elements while maintaining truncation. */
		update: function (new_string) {
			return this.each(function () {
				var $this = $(this);

				/* Update text. */
				if (new_string) {
					$this.data('trunk8').original_text = new_string;
				}

				/* Truncate accordingly. */
				truncate.call($this);
			});
		},

		revert: function () {
			return this.each(function () {
				/* Get original text. */
				var text = $(this).data('trunk8').original_text;

				/* Revert element to original text. */
				$(this).html(text);
			});
		},

		/** Returns this instance's settings object. NOT CHAINABLE. */
		getSettings: function () {
			return $(this.get(0)).data('trunk8').settings;
		}
	};

	utils = {
		/** Replaces [bite_size] [side]-most chars in [str] with [fill]. */
		eatStr: function (str, side, bite_size, fill) {
			var length = str.length,
				key = utils.eatStr.generateKey.apply(null, arguments),
				half_length,
				half_bite_size;

			/* If the result is already in the cache, return it. */
			if (utils.eatStr.cache[key]) {
				return utils.eatStr.cache[key];
			}

			/* Common error handling. */
			if ((typeof str !== 'string') || (length === 0)) {
				$.error('Invalid source string "' + str + '".');
			}
			if ((bite_size < 0) || (bite_size > length)) {
				$.error('Invalid bite size "' + bite_size + '".');
			}
			else if (bite_size === 0) {
				/* No bite should show no truncation. */
				return str;
			}
			if (typeof (fill + '') !== 'string') {
				$.error('Fill unable to be converted to a string.');
			}

			/* Compute the result, store it in the cache, and return it. */
			switch (side) {
				case SIDES.right:
					/* str... */
					return utils.eatStr.cache[key] =
							$.trim(str.substr(0, length - bite_size)) + fill;

				case SIDES.left:
					/* ...str */
					return utils.eatStr.cache[key] =
							fill + $.trim(str.substr(bite_size));

				case SIDES.center:
					/* Bit-shift to the right by one === Math.floor(x / 2) */
					half_length = length >> 1; // halve the length
					half_bite_size = bite_size >> 1; // halve the bite_size

					/* st...r */
					return utils.eatStr.cache[key] =
							$.trim(utils.eatStr(str.substr(0, length - half_length), SIDES.right, bite_size - half_bite_size, '')) +
							fill +
							$.trim(utils.eatStr(str.substr(length - half_length), SIDES.left, half_bite_size, ''));

				default:
					$.error('Invalid side "' + side + '".');
			}
		},

		getLineHeight: function (elem) {
				var floats = $(elem).css('float');
				if (floats !== 'none') {
					$(elem).css('float', 'none');
				}
				var pos = $(elem).css('position');
				if (pos === 'absolute') {
					$(elem).css('position', 'static');
				}

				var html = $(elem).html(),
				wrapper_id = 'line-height-test',
				line_height;

				/* Set the content to a small single character and wrap. */
				$(elem).html('i').wrap('
');

				/* Calculate the line height by measuring the wrapper.*/
				line_height = $('#' + wrapper_id).innerHeight();

				/* Remove the wrapper and reset the content. */
				$(elem).html(html).css({ 'float': floats, 'position': pos }).unwrap();

				return line_height;
			}
	};

	utils.eatStr.cache = {};
	utils.eatStr.generateKey = function () {
		return Array.prototype.join.call(arguments, '');
	};

	$.fn.trunk8 = function (method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		}
		else {
			$.error('Method ' + method + ' does not exist on jQuery.trunk8');
		}
	};

	/* Default trunk8 settings. */
	$.fn.trunk8.defaults = {
		fill: '…',
		lines: 1,
		side: SIDES.right,
		tooltip: true,
		width: WIDTH.auto,
		parseHTML: false,
		onTruncate: function () {}
	};
})(jQuery);
<commit_msg>fixed syntax error in regex<commit_after>/**!
 * trunk8 v1.3.3
 * https://github.com/rviscomi/trunk8
 *
 * Copyright 2012 Rick Viscomi
 * Released under the MIT License.
 *
 * Date: September 26, 2012
 */

(function ($) {
	var methods,
		utils,
		SIDES = {
			/* cen...ter */
			center: 'center',
			/* ...left */
			left: 'left',
			/* right... */
			right: 'right'
		},
		WIDTH = {
			auto: 'auto'
		};

	function trunk8(element) {
		this.$element = $(element);
		this.original_text = $.trim(this.$element.html());
		this.settings = $.extend({}, $.fn.trunk8.defaults);
	}

	trunk8.prototype.updateSettings = function (options) {
		this.settings = $.extend(this.settings, options);
	};

	function stripHTML(html) {
		var tmp = document.createElement("DIV");
		tmp.innerHTML = html;

		if (typeof tmp.textContent != 'undefined') {
			return tmp.textContent;
		}

		return tmp.innerText
	}

	function getHtmlArr(str) {
		/* Builds an array of strings and designated */
		/* HTML tags around them. */
		if (stripHTML(str) === str) {
			return str.split(/\s/g);
		}
		var allResults = [],
			reg = /<([a-z]+)([^<]*)(?:>(.*?(?!<\1>))<\/\1>|\s+\/>)(['.?!,]*)|((?:[^<>\s])+['.?!,]*\w?|
)/ig,
			outArr = reg.exec(str),
			lastI,
			ind;
		while (outArr && lastI !== reg.lastIndex) {
			lastI = reg.lastIndex;
			if (outArr[5]) {
				allResults.push(outArr[5]);
			} else if (outArr[1]) {
				allResults.push({
					tag: outArr[1],
					attribs: outArr[2],
					content: outArr[3],
					after: outArr[4]
				});
			}
			outArr = reg.exec(str);
		}
		for (ind = 0; ind < allResults.length; ind++) {
			if (typeof allResults[ind] !== 'string' &&
					allResults[ind].content) {
				allResults[ind].content = getHtmlArr(allResults[ind].content);
			}
		}
		return allResults;
	}

	function rebuildHtmlFromBite(bite, htmlObject, fill) {
		// Take the processed bite after binary-search
		// truncated and re-build the original HTML
		// tags around the processed string.
		bite = bite.replace(fill, '');

		var biteHelper = function(contentArr, tagInfo) {
				var retStr = '',
					content,
					biteContent,
					biteLength,
					nextWord,
					i;
				for (i = 0; i < contentArr.length; i++) {
					content = contentArr[i];
					biteLength = $.trim(bite).split(' ').length;
					if ($.trim(bite).length) {
						if (typeof content === 'string') {
							if (!/
/.test(content)) {
								if (biteLength === 1 && $.trim(bite).length <= content.length) {
									content = bite;
									// We want the fill to go inside of the last HTML
									// element if the element is a container.
									if (tagInfo === 'p' || tagInfo === 'div') {
										content += fill;
									}
									bite = '';
								} else {
									bite = bite.replace(content, '');
								}
							}
							retStr += $.trim(content) + ((i === contentArr.length-1 || biteLength <= 1) ? '' : ' ');
						} else {
							biteContent = biteHelper(content.content, content.tag);
							if (content.after) bite = bite.replace(content.after, '');
							if (biteContent) {
								if (!content.after) content.after = ' ';
								retStr += '<'+content.tag+content.attribs+'>'+biteContent+'</'+content.tag+'>' + content.after;
							}
						}
					}
				}
				return retStr;
			},
			htmlResults = biteHelper(htmlObject);

		// Add fill if doesn't exist. This will place it outside the HTML elements.
		if (htmlResults.slice(htmlResults.length - fill.length) === fill) {
			htmlResults += fill;
		}

		return htmlResults;
	}

	function truncate() {
		var data = this.data('trunk8'),
			settings = data.settings,
			width = settings.width,
			side = settings.side,
			fill = settings.fill,
			parseHTML = settings.parseHTML,
			line_height = utils.getLineHeight(this) * settings.lines,
			str = data.original_text,
			length = str.length,
			max_bite = '',
			lower, upper,
			bite_size,
			bite,
			text,
			htmlObject;

		/* Reset the field to the original string. */
		this.html(str);
		text = this.text();

		/* If string has HTML and parse HTML is set, build */
		/* the data struct to house the tags */
		if (parseHTML && stripHTML(str) !== str) {
			htmlObject = getHtmlArr(str);
			str = stripHTML(str);
			length = str.length;
		}

		if (width === WIDTH.auto) {
			/* Assuming there is no "overflow: hidden". */
			if (this.height() <= line_height) {
				/* Text is already at the optimal trunkage. */
				return;
			}

			/* Binary search technique for finding the optimal trunkage. */
			/* Find the maximum bite without overflowing. */
			lower = 0;
			upper = length - 1;

			while (lower <= upper) {
				bite_size = lower + ((upper - lower) >> 1);

				bite = utils.eatStr(str, side, length - bite_size, fill);

				if (parseHTML && htmlObject) {
					bite = rebuildHtmlFromBite(bite, htmlObject, fill);
				}

				this.html(bite);

				/* Check for overflow. */
				if (this.height() > line_height) {
					upper = bite_size - 1;
				}
				else {
					lower = bite_size + 1;

					/* Save the bigger bite. */
					max_bite = (max_bite.length > bite.length) ? max_bite : bite;
				}
			}

			/* Reset the content to eliminate possible existing scroll bars. */
			this.html('');

			/* Display the biggest bite. */
			this.html(max_bite);

			if (settings.tooltip) {
				this.attr('title', text);
			}
		}
		else if (!isNaN(width)) {
			bite_size = length - width;

			bite = utils.eatStr(str, side, bite_size, fill);

			this.html(bite);

			if (settings.tooltip) {
				this.attr('title', str);
			}
		}
		else {
			$.error('Invalid width "' + width + '".');
			return;
		}
		settings.onTruncate();
	}

	methods = {
		init: function (options) {
			return this.each(function () {
				var $this = $(this),
					data = $this.data('trunk8');

				if (!data) {
					$this.data('trunk8', (data = new trunk8(this)));
				}

				data.updateSettings(options);

				truncate.call($this);
			});
		},

		/** Updates the text value of the elements while maintaining truncation. */
		update: function (new_string) {
			return this.each(function () {
				var $this = $(this);

				/* Update text. */
				if (new_string) {
					$this.data('trunk8').original_text = new_string;
				}

				/* Truncate accordingly. */
				truncate.call($this);
			});
		},

		revert: function () {
			return this.each(function () {
				/* Get original text. */
				var text = $(this).data('trunk8').original_text;

				/* Revert element to original text. */
				$(this).html(text);
			});
		},

		/** Returns this instance's settings object. NOT CHAINABLE. */
		getSettings: function () {
			return $(this.get(0)).data('trunk8').settings;
		}
	};

	utils = {
		/** Replaces [bite_size] [side]-most chars in [str] with [fill]. */
		eatStr: function (str, side, bite_size, fill) {
			var length = str.length,
				key = utils.eatStr.generateKey.apply(null, arguments),
				half_length,
				half_bite_size;

			/* If the result is already in the cache, return it. */
			if (utils.eatStr.cache[key]) {
				return utils.eatStr.cache[key];
			}

			/* Common error handling. */
			if ((typeof str !== 'string') || (length === 0)) {
				$.error('Invalid source string "' + str + '".');
			}
			if ((bite_size < 0) || (bite_size > length)) {
				$.error('Invalid bite size "' + bite_size + '".');
			}
			else if (bite_size === 0) {
				/* No bite should show no truncation. */
				return str;
			}
			if (typeof (fill + '') !== 'string') {
				$.error('Fill unable to be converted to a string.');
			}

			/* Compute the result, store it in the cache, and return it. */
			switch (side) {
				case SIDES.right:
					/* str... */
					return utils.eatStr.cache[key] =
							$.trim(str.substr(0, length - bite_size)) + fill;

				case SIDES.left:
					/* ...str */
					return utils.eatStr.cache[key] =
							fill + $.trim(str.substr(bite_size));

				case SIDES.center:
					/* Bit-shift to the right by one === Math.floor(x / 2) */
					half_length = length >> 1; // halve the length
					half_bite_size = bite_size >> 1; // halve the bite_size

					/* st...r */
					return utils.eatStr.cache[key] =
							$.trim(utils.eatStr(str.substr(0, length - half_length), SIDES.right, bite_size - half_bite_size, '')) +
							fill +
							$.trim(utils.eatStr(str.substr(length - half_length), SIDES.left, half_bite_size, ''));

				default:
					$.error('Invalid side "' + side + '".');
			}
		},

		getLineHeight: function (elem) {
				var floats = $(elem).css('float');
				if (floats !== 'none') {
					$(elem).css('float', 'none');
				}
				var pos = $(elem).css('position');
				if (pos === 'absolute') {
					$(elem).css('position', 'static');
				}

				var html = $(elem).html(),
				wrapper_id = 'line-height-test',
				line_height;

				/* Set the content to a small single character and wrap. */
				$(elem).html('i').wrap('
');

				/* Calculate the line height by measuring the wrapper.*/
				line_height = $('#' + wrapper_id).innerHeight();

				/* Remove the wrapper and reset the content. */
				$(elem).html(html).css({ 'float': floats, 'position': pos }).unwrap();

				return line_height;
			}
	};

	utils.eatStr.cache = {};
	utils.eatStr.generateKey = function () {
		return Array.prototype.join.call(arguments, '');
	};

	$.fn.trunk8 = function (method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		}
		else {
			$.error('Method ' + method + ' does not exist on jQuery.trunk8');
		}
	};

	/* Default trunk8 settings. */
	$.fn.trunk8.defaults = {
		fill: '…',
		lines: 1,
		side: SIDES.right,
		tooltip: true,
		width: WIDTH.auto,
		parseHTML: false,
		onTruncate: function () {}
	};
})(jQuery);
<|endoftext|>


<commit_before>var f4js = require('fuse4js');
var fs = require('fs');
var obj = null;   // The JSON object we'll be exposing as a file system
var options = {};  // See parseArgs()
var udManager = require('./helper/udManager');
require('./helper/ObjectExtend');

function getattr(path, cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line + " " + path);
	udManager.getFileMeta(path, function (error, response){
		var stat = {};
		var err = 0; // assume success
		if( !response.data || !response.data.list){
			err = -2; // -ENOENT
		}else if( response.data.list[0].isdir == 1 ){
			stat.size = 4096;   // standard size of a directory
			stat.mode = 040770; // directory with 777 permissions
			stat.mtime = new Date(response.data.list[0].mtime * 1000);
			stat.atime = new Date(response.data.list[0].mtime * 1000);
			stat.ctime = new Date(response.data.list[0].ctime * 1000);
			stat.uid = process.getuid();
			stat.gid = process.getgid();
		}else{
			stat.size = response.data.list[0].size;
			stat.mode = 0100660; // file with 666 permissions
			stat.mtime = new Date(response.data.list[0].mtime * 1000);
			stat.atime = new Date(response.data.list[0].mtime * 1000);
			stat.ctime = new Date(response.data.list[0].ctime * 1000);
			stat.uid = process.getuid();
			stat.gid = process.getgid();
		}
		cb( err, stat );
	});
};

function readdir(path, cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line + " " + path);
	udManager.getFileList(path, function(error, response){
		var names = [];
		var err = 0; // assume success
		if( !response.data ){
			err = -2; // -ENOENT
		}else{
			for(var fp in response.data.list){
				var filePathSplited = response.data.list[fp].path.split("/");
				var fileName = filePathSplited[filePathSplited.length - 1];
				names.push(fileName);
			}
		}
		cb( err, names );
	});
}

function open(path, flags, cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line + " " + path);
	udManager.getFileMeta(path, function (error, response){
		var stat = {};
		var err = 0; // assume success
		if( !response.data || !response.data.list){
			err = -2; // -ENOENT
		}else if( response.data.list[0].isdir == 1 ){
		}else{
		}
		cb(err); // we don't return a file handle, so fuse4js will initialize it to 0
	});
}

function read(path, offset, len, buf, fh, cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line + " " + path);
	udManager.getFileMeta(path, function (error, response){
		var err = 0; // assume success
		if( !response.data || !response.data.list){
			err = -2; // -ENOENT
			cb( err );
		}else if( response.data.list[0].isdir == 1 ){
			// directory
			err = -1; // -EPERM
			cb( err );
		}else{
			udManager.downloadFileInRange(path, offset, len, function(error, response){
				response.data.copy(buf);
				cb(len);
			});
		}
	});
}

function write(path, offset, len, buf, fh, cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line + " " + path);
	var err = 0; // assume success
	var info = lookup(obj, path);
	var file = info.node;
	var name = info.name;
	var parent = info.parent;
	var beginning, blank = '', data, ending='', numBlankChars;

	switch (typeof file) {
	case 'undefined':
		err = -2; // -ENOENT
		break;

	case 'object': // directory
		err = -1; // -EPERM
		break;

	case 'string': // a string treated as ASCII characters
		data = buf.toString('ascii'); // read the new data
		if (offset < file.length) {
			beginning = file.substring(0, offset);
			if (offset + data.length < file.length) {
				ending = file.substring(offset + data.length, file.length)
			}
		} else {
			beginning = file;
			numBlankChars = offset - file.length;
			while (numBlankChars--) blank += ' ';
		}
		delete parent[name];
		parent[name] = beginning + blank + data + ending;
		err = data.length;
		break;

	default:
		break;
	}
	cb(err);
}

function release(path, fh, cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line + " " + path);
	cb(0);
}

function create (path, mode, cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line + " " + path);
	var err = 0; // assume success
	var info = lookup(obj, path);

	switch (typeof info.node) {
	case 'undefined':
		if (info.parent !== null) {
			info.parent[info.name] = '';
		} else {
			err = -2; // -ENOENT
		}
		break;

	case 'string': // existing file
	case 'object': // existing directory
		err = -17; // -EEXIST
		break;

	default:
		break;
	}
	cb(err);
}

function unlink(path, cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line + " " + path);
	var err = 0; // assume success
	var info = lookup(obj, path);

	switch (typeof info.node) {
	case 'undefined':
		err = -2; // -ENOENT
		break;

	case 'object': // existing directory
		err = -1; // -EPERM
		break;

	case 'string': // existing file
		delete info.parent[info.name];
		break;

	default:
		break;
	}
	cb(err);
}

function rename(src, dst, cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line);
	var err = -2; // -ENOENT assume failure
	var source = lookup(obj, src), dest;

	if (typeof source.node !== 'undefined') { // existing file or directory
		dest = lookup(obj, dst);
		if (typeof dest.node === 'undefined' && dest.parent !== null) {
			dest.parent[dest.name] = source.node;
			delete source.parent[source.name];
			err = 0;
		} else {
			err = -17; // -EEXIST
		}
	}
	cb(err);
}

function mkdir(path, mode, cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line + " " + path);
	var err = -2; // -ENOENT assume failure
	var dst = lookup(obj, path), dest;
	if (typeof dst.node === 'undefined' && dst.parent != null) {
		dst.parent[dst.name] = {};
		err = 0;
	}
	cb(err);
}

function rmdir(path, cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line + " " + path);
	var err = -2; // -ENOENT assume failure
	var dst = lookup(obj, path), dest;
	if (typeof dst.node === 'object' && dst.parent != null) {
		delete dst.parent[dst.name];
		err = 0;
	}
	cb(err);
}

function init(cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line);
	console.log("File system started at " + options.mountPoint);
	console.log("To stop it, type this in another shell: fusermount -u " + options.mountPoint);
	cb();
}

function setxattr(path, name, value, size, a, b, c) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line + " " + path);
	console.log("Setxattr called:", path, name, value, size, a, b, c)
	cb(0);
}

function statfs(cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line);
	udManager.showStat(function(error, response){
		var block_size = 4096;
		//f_bsize = block_size;
		//f_blocks = (fsblkcnt_t) (quota/block_size);
		//f_bfree = (fsblkcnt_t) ( baidu_data->statistic_cache->f_blocks - ( used / block_size ));
		//f_bavail = baidu_data->statistic_cache->f_bfree;     // normal user should has no different

		cb(0, {
				bsize: block_size,
				//frsize: 1000000,
				blocks: (response.data.quota / block_size),
				bfree: ((response.data.quota / block_size) - (response.data.used / block_size) ),
				bavail: ((response.data.quota / block_size) - (response.data.used / block_size) ),
				//files: 1000000,
				//ffree: 1000000,
				//favail: 1000000,
				//fsid: 1000000,
				//flag: 1000000,
				namemax: 1000
		});
	});
}

function destroy(cb) {
	console.log("[DEBUG] " + new Date() + " " + __function + " : " + __line);
	if (options.outJson) {
		try {
			fs.writeFileSync(options.outJson, JSON.stringify(obj, null, '  '), 'utf8');
		} catch (e) {
			console.log("Exception when writing file: " + e);
		}
	}
	console.log("File system stopped");
	cb();
}

var handlers = {
	getattr: getattr,
	readdir: readdir,
	open: open,
	read: read,
	write: write,
	release: release,
	create: create,
	unlink: unlink,
	rename: rename,
	mkdir: mkdir,
	rmdir: rmdir,
	init: init,
	destroy: destroy,
	setxattr: setxattr,
	statfs: statfs
};

function usage() {
	console.log();
	console.log("Usage: node jsonFS.js [options] inputJsonFile mountPoint");
	console.log("(Ensure the mount point is empty and you have wrx permissions to it)\n")
	console.log("Options:");
	console.log("-o outputJsonFile  : save modified data to new JSON file. Input file is never modified.");
	console.log("-d                 : make FUSE print debug statements.");
	console.log("-a                 : add allow_other option to mount (might need user_allow_other in system fuse config file).");
	console.log();
	console.log("Example:");
	console.log("node example/jsonFS.fs -d -o /tmp/output.json example/sample.json /tmp/mnt");
	console.log();
}

function parseArgs() {
	var i, remaining;
	var args = process.argv;
	if (args.length < 4) {
		return false;
	}
	options.mountPoint = args[args.length - 1];
	options.inJson = args[args.length - 2];
	remaining = args.length - 4;
	i = 2;
	while (remaining--) {
		if (args[i] === '-d') {
			options.debugFuse = true;
			++i;
		} else if (args[i] === '-o') {
			if (remaining) {
				options.outJson = args[i+1];
				i += 2;
				--remaining;
			} else return false;
		} else if (args[i] === '-a') {
			options.allowOthers = true;
			++i;
		} else return false;
	}
	return true;
}

(function main() {
	if (parseArgs()) {
		console.log("\nInput file: " + options.inJson);
		console.log("Mount point: " + options.mountPoint);
		if (options.outJson)
			console.log("Output file: " + options.outJson);
		if (options.debugFuse)
			console.log("FUSE debugging enabled");
		content = fs.readFileSync(options.inJson, 'utf8');
		obj = JSON.parse(content);
		try {
			var opts = [];
			if (options.allowOthers) {
				opts.push('-o');
				opts.push('allow_other');
			}
			f4js.start(options.mountPoint, handlers, options.debugFuse, opts);
		} catch (e) {
			console.log("Exception when starting file system: " + e);
		}
	} else {
		usage();
	}
})();
<commit_msg>Shorten the debug log.<commit_after>var f4js = require('fuse4js');
var fs = require('fs');
var obj = null;   // The JSON object we'll be exposing as a file system
var options = {};  // See parseArgs()
var udManager = require('./helper/udManager');
require('./helper/ObjectExtend');

function getattr(path, cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line + " " + path);
	udManager.getFileMeta(path, function (error, response){
		var stat = {};
		var err = 0; // assume success
		if( !response.data || !response.data.list){
			err = -2; // -ENOENT
		}else if( response.data.list[0].isdir == 1 ){
			stat.size = 4096;   // standard size of a directory
			stat.mode = 040770; // directory with 777 permissions
			stat.mtime = new Date(response.data.list[0].mtime * 1000);
			stat.atime = new Date(response.data.list[0].mtime * 1000);
			stat.ctime = new Date(response.data.list[0].ctime * 1000);
			stat.uid = process.getuid();
			stat.gid = process.getgid();
		}else{
			stat.size = response.data.list[0].size;
			stat.mode = 0100660; // file with 666 permissions
			stat.mtime = new Date(response.data.list[0].mtime * 1000);
			stat.atime = new Date(response.data.list[0].mtime * 1000);
			stat.ctime = new Date(response.data.list[0].ctime * 1000);
			stat.uid = process.getuid();
			stat.gid = process.getgid();
		}
		cb( err, stat );
	});
};

function readdir(path, cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line + " " + path);
	udManager.getFileList(path, function(error, response){
		var names = [];
		var err = 0; // assume success
		if( !response.data ){
			err = -2; // -ENOENT
		}else{
			for(var fp in response.data.list){
				var filePathSplited = response.data.list[fp].path.split("/");
				var fileName = filePathSplited[filePathSplited.length - 1];
				names.push(fileName);
			}
		}
		cb( err, names );
	});
}

function open(path, flags, cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line + " " + path);
	udManager.getFileMeta(path, function (error, response){
		var stat = {};
		var err = 0; // assume success
		if( !response.data || !response.data.list){
			err = -2; // -ENOENT
		}else if( response.data.list[0].isdir == 1 ){
		}else{
		}
		cb(err); // we don't return a file handle, so fuse4js will initialize it to 0
	});
}

function read(path, offset, len, buf, fh, cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line + " " + path);
	udManager.getFileMeta(path, function (error, response){
		var err = 0; // assume success
		if( !response.data || !response.data.list){
			err = -2; // -ENOENT
			cb( err );
		}else if( response.data.list[0].isdir == 1 ){
			// directory
			err = -1; // -EPERM
			cb( err );
		}else{
			udManager.downloadFileInRange(path, offset, len, function(error, response){
				response.data.copy(buf);
				cb(len);
			});
		}
	});
}

function write(path, offset, len, buf, fh, cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line + " " + path);
	var err = 0; // assume success
	var info = lookup(obj, path);
	var file = info.node;
	var name = info.name;
	var parent = info.parent;
	var beginning, blank = '', data, ending='', numBlankChars;

	switch (typeof file) {
	case 'undefined':
		err = -2; // -ENOENT
		break;

	case 'object': // directory
		err = -1; // -EPERM
		break;

	case 'string': // a string treated as ASCII characters
		data = buf.toString('ascii'); // read the new data
		if (offset < file.length) {
			beginning = file.substring(0, offset);
			if (offset + data.length < file.length) {
				ending = file.substring(offset + data.length, file.length)
			}
		} else {
			beginning = file;
			numBlankChars = offset - file.length;
			while (numBlankChars--) blank += ' ';
		}
		delete parent[name];
		parent[name] = beginning + blank + data + ending;
		err = data.length;
		break;

	default:
		break;
	}
	cb(err);
}

function release(path, fh, cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line + " " + path);
	cb(0);
}

function create (path, mode, cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line + " " + path);
	var err = 0; // assume success
	var info = lookup(obj, path);

	switch (typeof info.node) {
	case 'undefined':
		if (info.parent !== null) {
			info.parent[info.name] = '';
		} else {
			err = -2; // -ENOENT
		}
		break;

	case 'string': // existing file
	case 'object': // existing directory
		err = -17; // -EEXIST
		break;

	default:
		break;
	}
	cb(err);
}

function unlink(path, cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line + " " + path);
	var err = 0; // assume success
	var info = lookup(obj, path);

	switch (typeof info.node) {
	case 'undefined':
		err = -2; // -ENOENT
		break;

	case 'object': // existing directory
		err = -1; // -EPERM
		break;

	case 'string': // existing file
		delete info.parent[info.name];
		break;

	default:
		break;
	}
	cb(err);
}

function rename(src, dst, cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line);
	var err = -2; // -ENOENT assume failure
	var source = lookup(obj, src), dest;

	if (typeof source.node !== 'undefined') { // existing file or directory
		dest = lookup(obj, dst);
		if (typeof dest.node === 'undefined' && dest.parent !== null) {
			dest.parent[dest.name] = source.node;
			delete source.parent[source.name];
			err = 0;
		} else {
			err = -17; // -EEXIST
		}
	}
	cb(err);
}

function mkdir(path, mode, cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line + " " + path);
	var err = -2; // -ENOENT assume failure
	var dst = lookup(obj, path), dest;
	if (typeof dst.node === 'undefined' && dst.parent != null) {
		dst.parent[dst.name] = {};
		err = 0;
	}
	cb(err);
}

function rmdir(path, cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line + " " + path);
	var err = -2; // -ENOENT assume failure
	var dst = lookup(obj, path), dest;
	if (typeof dst.node === 'object' && dst.parent != null) {
		delete dst.parent[dst.name];
		err = 0;
	}
	cb(err);
}

function init(cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line);
	console.log("File system started at " + options.mountPoint);
	console.log("To stop it, type this in another shell: fusermount -u " + options.mountPoint);
	cb();
}

function setxattr(path, name, value, size, a, b, c) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line + " " + path);
	console.log("Setxattr called:", path, name, value, size, a, b, c)
	cb(0);
}

function statfs(cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line);
	udManager.showStat(function(error, response){
		var block_size = 4096;
		//f_bsize = block_size;
		//f_blocks = (fsblkcnt_t) (quota/block_size);
		//f_bfree = (fsblkcnt_t) ( baidu_data->statistic_cache->f_blocks - ( used / block_size ));
		//f_bavail = baidu_data->statistic_cache->f_bfree;     // normal user should has no different

		cb(0, {
				bsize: block_size,
				//frsize: 1000000,
				blocks: (response.data.quota / block_size),
				bfree: ((response.data.quota / block_size) - (response.data.used / block_size) ),
				bavail: ((response.data.quota / block_size) - (response.data.used / block_size) ),
				//files: 1000000,
				//ffree: 1000000,
				//favail: 1000000,
				//fsid: 1000000,
				//flag: 1000000,
				namemax: 1000
		});
	});
}

function destroy(cb) {
	console.log("[DEBUG] " + new Date().getTime() + " " + __function + " : " + __line);
	if (options.outJson) {
		try {
			fs.writeFileSync(options.outJson, JSON.stringify(obj, null, '  '), 'utf8');
		} catch (e) {
			console.log("Exception when writing file: " + e);
		}
	}
	console.log("File system stopped");
	cb();
}

var handlers = {
	getattr: getattr,
	readdir: readdir,
	open: open,
	read: read,
	write: write,
	release: release,
	create: create,
	unlink: unlink,
	rename: rename,
	mkdir: mkdir,
	rmdir: rmdir,
	init: init,
	destroy: destroy,
	setxattr: setxattr,
	statfs: statfs
};

function usage() {
	console.log();
	console.log("Usage: node jsonFS.js [options] inputJsonFile mountPoint");
	console.log("(Ensure the mount point is empty and you have wrx permissions to it)\n")
	console.log("Options:");
	console.log("-o outputJsonFile  : save modified data to new JSON file. Input file is never modified.");
	console.log("-d                 : make FUSE print debug statements.");
	console.log("-a                 : add allow_other option to mount (might need user_allow_other in system fuse config file).");
	console.log();
	console.log("Example:");
	console.log("node example/jsonFS.fs -d -o /tmp/output.json example/sample.json /tmp/mnt");
	console.log();
}

function parseArgs() {
	var i, remaining;
	var args = process.argv;
	if (args.length < 4) {
		return false;
	}
	options.mountPoint = args[args.length - 1];
	options.inJson = args[args.length - 2];
	remaining = args.length - 4;
	i = 2;
	while (remaining--) {
		if (args[i] === '-d') {
			options.debugFuse = true;
			++i;
		} else if (args[i] === '-o') {
			if (remaining) {
				options.outJson = args[i+1];
				i += 2;
				--remaining;
			} else return false;
		} else if (args[i] === '-a') {
			options.allowOthers = true;
			++i;
		} else return false;
	}
	return true;
}

(function main() {
	if (parseArgs()) {
		console.log("\nInput file: " + options.inJson);
		console.log("Mount point: " + options.mountPoint);
		if (options.outJson)
			console.log("Output file: " + options.outJson);
		if (options.debugFuse)
			console.log("FUSE debugging enabled");
		content = fs.readFileSync(options.inJson, 'utf8');
		obj = JSON.parse(content);
		try {
			var opts = [];
			if (options.allowOthers) {
				opts.push('-o');
				opts.push('allow_other');
			}
			f4js.start(options.mountPoint, handlers, options.debugFuse, opts);
		} catch (e) {
			console.log("Exception when starting file system: " + e);
		}
	} else {
		usage();
	}
})();
<|endoftext|>