<commit_before>/*jslint nomen: true */
/*globals define: true */
/*globals console: true */

define([
  'jquery',
  'lib/lodash',
  'backbone',
  'lib/kissmetrics',

  // LocalData
  'settings',
  'api',

  'views/forms'
],

function($, _, Backbone, _kmq, settings, api, FormViews) {
  'use strict';

  var Builder = {};

  Builder.BuilderView = Backbone.View.extend({

    el: '#builder-view-container',

    boxAnswersByQuestionId: {},
    formQuestions: $('#editor'),
    questionsByParentId: {},
    repeatCounter: {},
    templates: {
      question: _.template($('#question-edit-template').html()),
      answer: _.template($('#answer-edit-template').html())
    },

    initialize: function(options) {
      _.bindAll(this, 'render', 'save', 'done', 'makeBlankQuestion', 'renderForm',
        'suffix', 'editQuestion', 'editQuestionType', 'deleteQuestion',
        'createQuestion', 'createAnswer', 'addSubQuestion', 'renderQuestion',
         'editAnswer', 'deleteAnswer', 'slugify', 'updatePreview');

      this.forms = options.forms;
    },

    render: function() {
      this.renderForm();
      this.updatePreview();
    },

    save: function(event) {
      event.preventDefault();
      console.log('Save clicked');
      _kmq.push(['record', 'Survey questions saved']);

      api.createForm(settings.formData, function(){
        console.log('Form successfully saved');
        $(".saved").fadeIn().css("display","inline-block").delay(2000).fadeOut();
      });
    },

    done: function(event) {
      event.preventDefault();
      console.log('Done editing');
      _kmq.push(['record', 'Done editing survey questions']);
      this.trigger("done");
    },

    // The blank question template.
    makeBlankQuestion: function(){
      return {
        "text": "",
        "value": "",
        "answers": [
          {
            "text": "",
            "value": ""
          },
          {
            "text": "",
            "value": ""
          }
        ]
      };
    },

    renderForm: function() {
      // Render the editor template
      $(this.el).html(_.template($('#form-editor-template').html(), {}));

      // Clear out the editor, just in case.
      this.formQuestions = $('#editor');
      this.formQuestions.html('');

      // Handle save events
      $(this.el).find('.save').click(this.save);
      $(this.el).find('.done').click(this.done);

      // Default to a blank question if the form is empty
      if (settings.formData === undefined) {
        settings.formData = {};
        settings.formData.questions = [];
        settings.formData.questions.push(this.makeBlankQuestion());
      }

      // Render form
      _.each(settings.formData.questions, function (question, questionIndex) {
        this.renderQuestion(question, undefined, undefined, undefined, undefined, questionIndex, settings.formData.questions);

        // last param was settings.formData.questions
      }, this);
      this.updatePreview();
    },

    suffix: function(name) {
      if(_.has(this.repeatCounter, name)) {
        this.repeatCounter[name] += 1;
        return '-' + this.repeatCounter[name].toString();
      }

      this.repeatCounter[name] = 1;
      return '';
    },

    editQuestion: function(question) {
      // TODO:
      // What if we do:
      // view = this;
      // return function(event) {
      //  view.slugify(text)
      //  ...
      return function(event) {
        console.log('Updating question');
        _kmq.push(['record', 'Question edited']);
        var text = $(event.target).val(); //$(this).val();
        var name = this.slugify(text);

        question.text = text;
        question.name = name;

        this.updatePreview();
      };
    },

    editQuestionType: function(question) {
      return function(event) {
        _kmq.push(['record', 'Question type changed']);

        var dataRole = $(event.target).attr('data-role');
        console.log(dataRole);
        if (dataRole === 'radio-question') {
          delete question.type;
        }
        if (dataRole === 'checkbox-question') {
          question.type = "checkbox";
        }

        // Update the styling
        var $pills = $(event.target).parent().find('.question-type');
        $pills.toggleClass('selected');

        this.updatePreview();
      };
    },

    deleteQuestion: function($question, parent, questionIndex) {
      return function(event) {
        console.log("removing question");
        _kmq.push(['record', 'Question deleted']);

        // Remove it from the DON.
        $question.remove();

        // Remove it from the json
        parent.splice(questionIndex, 1);
        this.updatePreview();
      };
    },

    createQuestion: function(parent, questionIndex) {
      return function(event) {
        console.log("Adding a new question");
        _kmq.push(['record', 'Question added']);

        var newQuestion = this.makeBlankQuestion();
        parent.splice(questionIndex + 1, 0, newQuestion);

        this.updatePreview();
        this.renderForm();
      };
    },

    createAnswer: function(question) {
      return function(event) {
        _kmq.push(['record', 'Answer added']);
        question.answers.push({
          'text': '',
          'value': ''
        });
        this.updatePreview();
        this.renderForm();
      };
    },

    addSubQuestion: function(question, index) {
      return function(event) {
        console.log("Adding sub-question");
        _kmq.push(['record', 'Sub-question added']);

        if (_.has(question.answers[index], 'questions')) {
          question.answers[index].questions.unshift(this.makeBlankQuestion());
        }else {
          question.answers[index].questions = [this.makeBlankQuestion()];
        }

        this.updatePreview();
        this.renderForm();
      };
    },

    editAnswer: function(question, index) {
      return function(event){
        _kmq.push(['record', 'Answer edited']);
        var text = $(event.target).val();
        question.answers[index].text = text;
        question.answers[index].value = this.slugify(text);
        this.updatePreview();
      };
    },

    deleteAnswer: function(question, index, $answer) {
      return function(event) {
        console.log("removing answer");
        _kmq.push(['record', 'Answer deleted']);

        // Remove it from the json
        question.answers.splice(index, 1);
        this.updatePreview();

        // Remove it from the DON.
        $answer.remove();
      };
    },

    renderQuestion: function(question, visible, parentID, triggerID, appendTo, questionIndex, parent) {
      // TODO:
      // This should pass around a well-document options object
      // Instead of 30 different parameters

      // Set default values for questions
      if (visible === undefined) {
        visible = true;
      }
      if (parentID === undefined) {
        parentID = '';
      }
      if (triggerID === undefined) {
        triggerID = '';
      }

      // Give the question an ID based on its name
      var id = _.uniqueId(question.name);

      // Render the questions .................................................
      var $question = $(this.templates.question({question: question}));

      // Listen for changes to the question ...................................
      // TODO: make it less verbose

      // Listen for changes to the question text
      var editQuestionProxy = $.proxy(this.editQuestion(question), this);
      $question.find('> input').keyup(editQuestionProxy);

      // Listen for changes of the question type
      var editQuestionTypeProxy = $.proxy(this.editQuestionType(question), this);
      $question.find('.question-type').click(editQuestionTypeProxy);

      // Listen for a request to remove a question
      var deleteQuestionProxy = $.proxy(this.deleteQuestion($question, parent, questionIndex), this);
      $question.find('> .remove').click(deleteQuestionProxy);

      // Listen for a request to add a question
      var createQuestionProxy = $.proxy(this.createQuestion(parent, questionIndex), this);
      $question.find('.add-question').click(createQuestionProxy);

      // Listen for a request to add an answer
      var createAnswerProxy = $.proxy(this.createAnswer(question), this);
      $question.find('.add-answer').click(createAnswerProxy);

      // Some stuff with siblings -- forget what this does.
      var siblings = this.questionsByParentId[parentID];
      if (siblings === undefined) {
        siblings = [];
        this.questionsByParentId[parentID] = siblings;
      }
      siblings.push($question);

      // Figure out the right place to append the rendered template
      if (appendTo !== undefined) {
        $(appendTo).append($question);
      }else {
        this.formQuestions.append($question);
      }

      var suffixed_name = question.name + this.suffix(question.name);

      // TODO Infoboxes (aka help text for questions)
      // if(question.info !== undefined) {
      // }

      // Deal with answers ....................................................
      var questionID = id;

      // Add each answer to the question
      _.each(question.answers, function (answer, index) {
        // The triggerID is used to hide/show other question groups
        var triggerID = _.uniqueId(question.name);

        if(question.type === "checkbox") {
          suffixed_name = answer.name + this.suffix(answer.name);
          triggerID = suffixed_name; //_.uniqueId(answer.name);
          id = suffixed_name; //_.uniqueId(answer.name);
        }

        // Set the data used to render the answer
        var data = {
          questionName: suffixed_name,
          id: triggerID,
          theme: (answer.theme || "c"),
          value: answer.value,
          text: answer.text
        };

        // Render the answer template...
        var $answer;
        var referencesToAnswersForQuestion;

        $answer = $(this.templates.answer(data));
        referencesToAnswersForQuestion = this.boxAnswersByQuestionId[questionID];
        if (referencesToAnswersForQuestion === undefined) {
          referencesToAnswersForQuestion = [];
          this.boxAnswersByQuestionId[questionID] = referencesToAnswersForQuestion;
        }

        // ... and append it to the fieldset.
        $question.find('> .answers').append($answer);

        // Tools related to editing answers ------------------------------------

        // Change the answer text when updated
        var $input = $answer.find('input');
        var editAnswerProxy = $.proxy(this.editAnswer(question, index), this);
        $input.keyup(editAnswerProxy);

        // Add a sub-question
        var $addSubQuestion = $answer.find('.add-sub-question');
        var addSubQuestionProxy = $.proxy(this.addSubQuestion(question, index), this);
        $addSubQuestion.click(addSubQuestionProxy);

        // Remove an answer
        var $removeButton = $answer.find('.remove');
        var deleteAnswerProxy = $.proxy(this.deleteAnswer(question, index, $answer), this);
        $removeButton.click(deleteAnswerProxy);

        // If there are conditional questions, add them. -----------------------
        if (answer.questions !== undefined) {
          appendTo = $answer;
          _.each(answer.questions, function (subq, questionIndex) {
            // Add the sub questions before the repeatButton
            if(appendTo !== undefined){
              this.renderQuestion(subq, false, id, triggerID, appendTo, questionIndex, answer.questions);
            }else {
              this.renderQuestion(subq, false, id, triggerID, undefined, questionIndex, answer.questions);
            }
          }, this);
        } // end check for sub-answers

      }, this);
    }, // end renderQuestion

    // Slugify a string
    // Used to generate the name attribute of forms
    slugify: function(text) {
      text = text.replace(/[^-a-zA-Z0-9,&\s]+/ig, '');
      text = text.replace(/-/gi, "_");
      text = text.replace(/\s/gi, "-");
      return text;
    },

    updatePreview: function() {
      this.trigger("formUpdated");
    }

  }); // end BuilderView{}

  return Builder;

});
<commit_msg>Automatically make small question groups horizontal<commit_after>/*jslint nomen: true */
/*globals define: true */
/*globals console: true */

define([
  'jquery',
  'lib/lodash',
  'backbone',
  'lib/kissmetrics',

  // LocalData
  'settings',
  'api',

  'views/forms'
],

function($, _, Backbone, _kmq, settings, api, FormViews) {
  'use strict';

  var Builder = {};

  Builder.BuilderView = Backbone.View.extend({

    el: '#builder-view-container',

    boxAnswersByQuestionId: {},
    formQuestions: $('#editor'),
    questionsByParentId: {},
    repeatCounter: {},
    templates: {
      question: _.template($('#question-edit-template').html()),
      answer: _.template($('#answer-edit-template').html())
    },

    initialize: function(options) {
      _.bindAll(this, 'render', 'save', 'done', 'makeBlankQuestion', 'renderForm',
        'suffix', 'editQuestion', 'editQuestionType', 'deleteQuestion',
        'createQuestion', 'createAnswer', 'addSubQuestion', 'renderQuestion',
         'editAnswer', 'deleteAnswer', 'slugify', 'updatePreview');

      this.forms = options.forms;
    },

    render: function() {
      this.renderForm();
      this.updatePreview();
    },

    save: function(event) {
      event.preventDefault();
      console.log('Saving form', settings.formData);
      _kmq.push(['record', 'Survey questions saved']);

      api.createForm(settings.formData, function(){
        console.log('Form successfully saved');
        $(".saved").fadeIn().css("display","inline-block").delay(2000).fadeOut();
      });
    },

    done: function(event) {
      event.preventDefault();
      console.log('Done editing');
      _kmq.push(['record', 'Done editing survey questions']);
      this.trigger("done");
    },

    // The blank question template.
    makeBlankQuestion: function(){
      return {
        "text": "",
        "value": "",
        "answers": [
          {
            "text": "",
            "value": ""
          },
          {
            "text": "",
            "value": ""
          }
        ]
      };
    },

    renderForm: function() {
      // Render the editor template
      $(this.el).html(_.template($('#form-editor-template').html(), {}));

      // Clear out the editor, just in case.
      this.formQuestions = $('#editor');
      this.formQuestions.html('');

      // Handle save events
      $(this.el).find('.save').click(this.save);
      $(this.el).find('.done').click(this.done);

      // Default to a blank question if the form is empty
      if (settings.formData === undefined) {
        settings.formData = {};
        settings.formData.questions = [];
        settings.formData.questions.push(this.makeBlankQuestion());
      }

      // Render form
      _.each(settings.formData.questions, function (question, questionIndex) {
        this.renderQuestion(question, undefined, undefined, undefined, undefined, questionIndex, settings.formData.questions);

        // last param was settings.formData.questions
      }, this);
      this.updatePreview();
    },

    suffix: function(name) {
      if(_.has(this.repeatCounter, name)) {
        this.repeatCounter[name] += 1;
        return '-' + this.repeatCounter[name].toString();
      }

      this.repeatCounter[name] = 1;
      return '';
    },

    editQuestion: function(question) {
      // TODO:
      // What if we do:
      // view = this;
      // return function(event) {
      //  view.slugify(text)
      //  ...
      return function(event) {
        console.log('Updating question');
        _kmq.push(['record', 'Question edited']);
        var text = $(event.target).val(); //$(this).val();
        var name = this.slugify(text);

        question.text = text;
        question.name = name;

        this.updatePreview();
      };
    },

    editQuestionType: function(question) {
      return function(event) {
        _kmq.push(['record', 'Question type changed']);

        var dataRole = $(event.target).attr('data-role');
        console.log(dataRole);
        if (dataRole === 'radio-question') {
          delete question.type;
        }
        if (dataRole === 'checkbox-question') {
          question.type = "checkbox";
        }

        // Update the styling
        var $pills = $(event.target).parent().find('.question-type');
        $pills.toggleClass('selected');

        this.updatePreview();
      };
    },

    deleteQuestion: function($question, parent, questionIndex) {
      return function(event) {
        console.log("removing question");
        _kmq.push(['record', 'Question deleted']);

        // Remove it from the DON.
        $question.remove();

        // Remove it from the json
        parent.splice(questionIndex, 1);
        this.updatePreview();
      };
    },

    createQuestion: function(parent, questionIndex) {
      return function(event) {
        console.log("Adding a new question");
        _kmq.push(['record', 'Question added']);

        var newQuestion = this.makeBlankQuestion();
        parent.splice(questionIndex + 1, 0, newQuestion);

        this.updatePreview();
        this.renderForm();
      };
    },

    createAnswer: function(question) {
      return function(event) {
        _kmq.push(['record', 'Answer added']);
        question.answers.push({
          'text': '',
          'value': ''
        });
        this.setQuestionLayout(question);

        this.updatePreview();
        this.renderForm();
      };
    },

    addSubQuestion: function(question, index) {
      return function(event) {
        console.log("Adding sub-question");
        _kmq.push(['record', 'Sub-question added']);

        if (_.has(question.answers[index], 'questions')) {
          question.answers[index].questions.unshift(this.makeBlankQuestion());
        }else {
          question.answers[index].questions = [this.makeBlankQuestion()];
        }

        this.updatePreview();
        this.renderForm();
      };
    },

    editAnswer: function(question, index) {
      return function(event){
        _kmq.push(['record', 'Answer edited']);
        var text = $(event.target).val();
        question.answers[index].text = text;
        question.answers[index].value = this.slugify(text);

        this.setQuestionLayout(question);

        this.updatePreview();
      };
    },

    deleteAnswer: function(question, index, $answer) {
      return function(event) {
        console.log("removing answer");
        _kmq.push(['record', 'Answer deleted']);

        // Remove it from the json
        question.answers.splice(index, 1);
        this.updatePreview();

        this.setQuestionLayout(question);

        // Remove it from the DON.
        $answer.remove();
      };
    },

    // Display a question horizontally if it doesn't have many answers.
    setQuestionLayout: function(question) {
      if (question.answers.length !== 2) {
        delete question.layout;
      }else {
        if (question.type !== 'checkbox') {
          question.layout = 'horizontal';
        }
      }
      console.log(question.layout);
    },

    renderQuestion: function(question, visible, parentID, triggerID, appendTo, questionIndex, parent) {
      // TODO:
      // This should pass around a well-document options object
      // Instead of 30 different parameters

      // Set default values for questions
      if (visible === undefined) {
        visible = true;
      }
      if (parentID === undefined) {
        parentID = '';
      }
      if (triggerID === undefined) {
        triggerID = '';
      }

      // Give the question an ID based on its name
      var id = _.uniqueId(question.name);

      // Render the questions .................................................
      var $question = $(this.templates.question({question: question}));

      // Listen for changes to the question ...................................
      // TODO: make it less verbose

      // Listen for changes to the question text
      var editQuestionProxy = $.proxy(this.editQuestion(question), this);
      $question.find('> input').keyup(editQuestionProxy);

      // Listen for changes of the question type
      var editQuestionTypeProxy = $.proxy(this.editQuestionType(question), this);
      $question.find('.question-type').click(editQuestionTypeProxy);

      // Listen for a request to remove a question
      var deleteQuestionProxy = $.proxy(this.deleteQuestion($question, parent, questionIndex), this);
      $question.find('> .remove').click(deleteQuestionProxy);

      // Listen for a request to add a question
      var createQuestionProxy = $.proxy(this.createQuestion(parent, questionIndex), this);
      $question.find('.add-question').click(createQuestionProxy);

      // Listen for a request to add an answer
      var createAnswerProxy = $.proxy(this.createAnswer(question), this);
      $question.find('.add-answer').click(createAnswerProxy);

      // Some stuff with siblings -- forget what this does.
      var siblings = this.questionsByParentId[parentID];
      if (siblings === undefined) {
        siblings = [];
        this.questionsByParentId[parentID] = siblings;
      }
      siblings.push($question);

      // Figure out the right place to append the rendered template
      if (appendTo !== undefined) {
        $(appendTo).append($question);
      }else {
        this.formQuestions.append($question);
      }

      var suffixed_name = question.name + this.suffix(question.name);

      // TODO Infoboxes (aka help text for questions)
      // if(question.info !== undefined) {
      // }

      // Deal with answers ....................................................
      var questionID = id;

      // Add each answer to the question
      _.each(question.answers, function (answer, index) {
        // The triggerID is used to hide/show other question groups
        var triggerID = _.uniqueId(question.name);

        if(question.type === "checkbox") {
          suffixed_name = answer.name + this.suffix(answer.name);
          triggerID = suffixed_name; //_.uniqueId(answer.name);
          id = suffixed_name; //_.uniqueId(answer.name);
        }

        // Set the data used to render the answer
        var data = {
          questionName: suffixed_name,
          id: triggerID,
          theme: (answer.theme || "c"),
          value: answer.value,
          text: answer.text
        };

        // Render the answer template...
        var $answer;
        var referencesToAnswersForQuestion;

        $answer = $(this.templates.answer(data));
        referencesToAnswersForQuestion = this.boxAnswersByQuestionId[questionID];
        if (referencesToAnswersForQuestion === undefined) {
          referencesToAnswersForQuestion = [];
          this.boxAnswersByQuestionId[questionID] = referencesToAnswersForQuestion;
        }

        // ... and append it to the fieldset.
        $question.find('> .answers').append($answer);

        // Tools related to editing answers ------------------------------------

        // Change the answer text when updated
        var $input = $answer.find('input');
        var editAnswerProxy = $.proxy(this.editAnswer(question, index), this);
        $input.keyup(editAnswerProxy);

        // Add a sub-question
        var $addSubQuestion = $answer.find('.add-sub-question');
        var addSubQuestionProxy = $.proxy(this.addSubQuestion(question, index), this);
        $addSubQuestion.click(addSubQuestionProxy);

        // Remove an answer
        var $removeButton = $answer.find('.remove');
        var deleteAnswerProxy = $.proxy(this.deleteAnswer(question, index, $answer), this);
        $removeButton.click(deleteAnswerProxy);

        // If there are conditional questions, add them. -----------------------
        if (answer.questions !== undefined) {
          appendTo = $answer;
          _.each(answer.questions, function (subq, questionIndex) {
            // Add the sub questions before the repeatButton
            if(appendTo !== undefined){
              this.renderQuestion(subq, false, id, triggerID, appendTo, questionIndex, answer.questions);
            }else {
              this.renderQuestion(subq, false, id, triggerID, undefined, questionIndex, answer.questions);
            }
          }, this);
        } // end check for sub-answers

      }, this);
    }, // end renderQuestion

    // Slugify a string
    // Used to generate the name attribute of forms
    slugify: function(text) {
      text = text.replace(/[^-a-zA-Z0-9,&\s]+/ig, '');
      text = text.replace(/-/gi, "_");
      text = text.replace(/\s/gi, "-");
      return text;
    },

    updatePreview: function() {
      this.trigger("formUpdated");
    }

  }); // end BuilderView{}

  return Builder;

});
<|endoftext|>


<commit_before>/*global $,app*/

app.views.results = function (parsedQuery) {
  console.debug('*results*', parsedQuery)

  // Breadcrumbs
  app.hooks.resultsCrumb.find('b').text(parsedQuery.label);
  app.hooks.crumbs.update(app.hooks.resultsCrumb);

  // Search
  app.hooks.searchRight.html(' ');
  app.hooks.searchLeft.removeClass('medium-14').addClass('medium-4').html(' ');
  app.hooks.searchBox.removeClass('medium-10 float-right').addClass('medium-16');

  // Empty both content areas
  app.hooks.content.children().detach();
  app.hooks.belowContent.children().detach();
  app.hooks.aboveContent.children().detach();

  var endpointMap = {
    'address': 'addresses',
    'account': 'account',
    'block': 'block',
    'owner': 'owner',
  };

  var endpoint = endpointMap[parsedQuery.type] + '/',
      isOwnerSearch = false;

  switch (parsedQuery.type) {
    case 'account':
      endpoint += encodeURI(parsedQuery.account);
      break;
    case 'block':
      endpoint += encodeURI(parsedQuery.address);
      break;
    case 'address':
      endpoint += encodeURI(parsedQuery.address);

      if (parsedQuery.unit) {
        endpoint += encodeURI(' UNIT ' + parsedQuery.unit);
      }
      break;
    case 'owner':
      isOwnerSearch = true;
      endpoint += encodeURI(parsedQuery.owner);
      break;
  }

  app.hooks.content.empty(); // Remove loading message
  app.hooks.ownerSearchDisclaimer.addClass('hide');

  var state = history.state;

  if (app.globals.historyState && state && state.ais) {
    didGetAisData(history.state.ais);
  } else {
    app.hooks.content.append(app.hooks.loading);
    getAisData();
  }

  function getAisData () {
    var params = {
      // gatekeeperKey: app.config['gatekeeperKey'],
      include_units: null,
      opa_only: null,
    };

    $.ajax({
      url: 'https://api.phila.gov/ais_ps/v1/' + endpoint,
      data: params,
      dataType: app.config.ajaxType,
      success: didGetAisData,
      error: function (jqXHR, textStatus, errorThrown) {
        console.debug('ais error', textStatus);
        var error = app.config.defaultError;
        history.replaceState({error: error}, '');
        render();
      },
    });
  }

  function didGetAisData (aisData) {
    var property, accountNumber, href, withUnit;

    if (!app.globals.historyState) history.state = {};

    // If we didn't get a good result from AIS (or rehydrate a feature from
    // history), show the error message.
    if (!aisData || (!aisData.features && !aisData.properties)) {
      console.debug('no ais features', aisData);
      var error = app.config.defaultError;
      history.replaceState({error: error}, '');
      render();
      return;
    }

    // For business reasons, owner searches need to always show on the
    // results page for the disclaimer.
    if (!isOwnerSearch && (
      aisData.type == 'Feature' ||
      aisData.features.length === 1)) {
        // If only one property go straight to property view
        feature = (aisData.features ? aisData.features[0] : aisData);
        attrs = feature.properties;
        accountNumber = attrs.opa_account_num;
        href = '?' + $.param({p: accountNumber});
        withUnit = feature.properties.street_address;

        history.replaceState({
          ais: feature,
          address: withUnit,
        }, withUnit, href);

        app.views.property(accountNumber);
    } else {
      // Fetch market_value, sale data from OPA dataset
      var opaUrl = constructOpaUrl(aisData.features);
      $.ajax(opaUrl, {dataType: app.config.ajaxType})
        .done(function (opaData) {
          var keyedOpaData = keyBy(opaData, 'parcel_number')
          $.each(aisData.features, function (index, feature) {
            $.extend(feature.properties, keyedOpaData[feature.properties.opa_account_num] || {})
          })

          var newState = $.extend({}, history.state);
          // Used for rendering a special owner search disclaimer
          if (isOwnerSearch) {
            aisData = $.extend({isOwnerSearch: true}, aisData);
          }
          newState = aisData

          if (!app.globals.historyState) {
            history.state = newState;
          } else {
            history.replaceState(newState, ''); // Second param not optional in IE10
          }
          render();
        });
    }
  }

  function constructOpaUrl (features) {
    var accountNumbers = $.map(features, function (feature) {
      return feature.properties.opa_account_num
    })

    var params = {
      select: [
        'parcel_number',
        'market_value',
        'sale_date',
        'sale_price'
      ].join(','),
      where: 'parcel_number in ("' + accountNumbers.join('","') + '")'
    };
    var query = 'SELECT parcel_number, market_value, sale_date, sale_price FROM '
      + app.config.carto.datasets.properties + " WHERE parcel_number IN ('"
      + accountNumbers.join("','") + "')"
    return app.config.carto.baseUrl + '?q=' + encodeURIComponent(query)
  }

  function keyBy (items, key) {
    var hash = {}
    for (var i = 0; i < items.length; i++) {
      hash[items[i][key]] = items[i]
    }
    return hash
  }

  function render () {
    console.debug('results render')

    var state = history.state;
    if (state.error) return app.hooks.content.text(state.error);
    var features = state.features;
    var total_size = state.total_size;
    var isOwnerSearch = state.isOwnerSearch;

    app.hooks.content.empty(); // Remove loading message

    if (total_size === 0) {
      return app.hooks.content.append(app.hooks.noResults);
    }

    if (isOwnerSearch) {
      renderOwnerSearchDisclaimer();
    }

    // TODO find a place for count
    //app.hooks.count.find('#total').text(state.total);
    //app.hooks.content.append(app.hooks.count);
    app.hooks.resultRows.empty(); // TODO reuse existing result nodes
    if (features) {
      $.each(features, addRow);

      if (total_size > features.length) {
        var seeMoreA = app.hooks.seeMore.find('a');
        seeMoreA.off('click'); // Drop previously created click events
        seeMoreA.on('click', function (e) {

          var params = {
            // gatekeeperKey: app.config['gatekeeperKey'],
            include_units: null,
            opa_only: null,
            page: state.page + 1
          };

          $.ajax('https://api.phila.gov/ais_ps/v1/' + endpoint,
                 {data: params, dataType: app.config.ajaxType})
            .done(function (aisData) {
              // Fetch market_value, sale data from OPA dataset
              var opaUrl = constructOpaUrl(aisData.features);
              $.ajax(opaUrl, {dataType: app.config.ajaxType})
              .done(function (opaData) {
                var keyedOpaData = keyBy(opaData, 'parcel_number')
                $.each(aisData.features, function (index, feature) {
                  $.extend(feature.properties, keyedOpaData[feature.properties.opa_account_num] || {})
                })

                state.features = state.features.concat(aisData.features);
                state.page = aisData.page;
                history.replaceState(state, ''); // Second param not optional in IE10

                $.each(aisData.features, addRow);
                if (state.total_size === state.features.length) app.hooks.seeMore.hide();

                // Remove old Tablesaw data and refresh
                $('[data-hook="results-table"]').table().data("table").destroy();
                $('[data-hook="results-table"]').table().data("table").refresh();
              });
            });
        });
        app.hooks.seeMore.show();
      } else {
        app.hooks.seeMore.hide();
      }
      app.hooks.content.append(app.hooks.results);

      // Update the Tablesaw responsive tables
      $(document).trigger('enhance.tablesaw');
    }
  }

  function renderOwnerSearchDisclaimer() {
    var now = new Date(),
        mins = now.getMinutes(),
        prettyMins = mins < 10 ? ('0' + mins) : mins,
        prettyNow = (now.getMonth() + 1) + '/' + now.getDate() + '/' +  now.getFullYear() +
                    ' ' + now.getHours() + ':' + prettyMins;

    app.hooks.ownerSearchDisclaimer.removeClass('hide');
    app.hooks.ownerSearchDisclaimerDatetime.text(prettyNow);
    app.hooks.ownerSearchDisclaimerQuery.text(parsedQuery.label);

    // Fetch IP & show it
    $.getJSON('https://api.ipify.org?format=json')
      .done(function(response) {
        if(response.ip) {
          app.hooks.ownerSearchDisclaimerIp.text(response.ip);
        }
      })
      .fail(function() {
        app.hooks.ownerSearchDisclaimerIp.text(app.hooks.ownerSearchDisclaimerIp.attr('data-default'));
      });
  }

  function addRow (index, property) {
    var row = app.hooks.resultRow.clone(),
        attrs = property.properties,
        accountNumber = attrs.opa_account_num,
        withUnit = attrs.street_address,
        href = '?' + $.param({p: accountNumber});

    row.append($('').append($('').text(withUnit)));
    row.append($('').text(accounting.formatMoney(attrs.market_value)));
    row.append($('').text(moment(attrs.sale_date).format('M/D/YYYY') + ', ' + accounting.formatMoney(attrs.sale_price)));
    row.append($('').text(attrs.opa_owners && attrs.opa_owners.join(', ')));
    row.append($('').html(''));

    row.on('click', function (e) {
        if (e.ctrlKey || e.altKey || e.shiftKey) return;
        e.preventDefault();
        history.pushState({ais: property, address: withUnit}, withUnit, href);
        window.scroll(0, 0);
        app.views.property(accountNumber);
      });
    app.hooks.resultRows.append(row);
  }
};
<commit_msg>results: use .rows property in carto response<commit_after>/*global $,app*/

app.views.results = function (parsedQuery) {
  console.debug('*results*', parsedQuery)

  // Breadcrumbs
  app.hooks.resultsCrumb.find('b').text(parsedQuery.label);
  app.hooks.crumbs.update(app.hooks.resultsCrumb);

  // Search
  app.hooks.searchRight.html(' ');
  app.hooks.searchLeft.removeClass('medium-14').addClass('medium-4').html(' ');
  app.hooks.searchBox.removeClass('medium-10 float-right').addClass('medium-16');

  // Empty both content areas
  app.hooks.content.children().detach();
  app.hooks.belowContent.children().detach();
  app.hooks.aboveContent.children().detach();

  var endpointMap = {
    'address': 'addresses',
    'account': 'account',
    'block': 'block',
    'owner': 'owner',
  };

  var endpoint = endpointMap[parsedQuery.type] + '/',
      isOwnerSearch = false;

  switch (parsedQuery.type) {
    case 'account':
      endpoint += encodeURI(parsedQuery.account);
      break;
    case 'block':
      endpoint += encodeURI(parsedQuery.address);
      break;
    case 'address':
      endpoint += encodeURI(parsedQuery.address);

      if (parsedQuery.unit) {
        endpoint += encodeURI(' UNIT ' + parsedQuery.unit);
      }
      break;
    case 'owner':
      isOwnerSearch = true;
      endpoint += encodeURI(parsedQuery.owner);
      break;
  }

  app.hooks.content.empty(); // Remove loading message
  app.hooks.ownerSearchDisclaimer.addClass('hide');

  var state = history.state;

  if (app.globals.historyState && state && state.ais) {
    didGetAisData(history.state.ais);
  } else {
    app.hooks.content.append(app.hooks.loading);
    getAisData();
  }

  function getAisData () {
    var params = {
      // gatekeeperKey: app.config['gatekeeperKey'],
      include_units: null,
      opa_only: null,
    };

    $.ajax({
      url: 'https://api.phila.gov/ais_ps/v1/' + endpoint,
      data: params,
      dataType: app.config.ajaxType,
      success: didGetAisData,
      error: function (jqXHR, textStatus, errorThrown) {
        console.debug('ais error', textStatus);
        var error = app.config.defaultError;
        history.replaceState({error: error}, '');
        render();
      },
    });
  }

  function didGetAisData (aisData) {
    var property, accountNumber, href, withUnit;

    if (!app.globals.historyState) history.state = {};

    // If we didn't get a good result from AIS (or rehydrate a feature from
    // history), show the error message.
    if (!aisData || (!aisData.features && !aisData.properties)) {
      console.debug('no ais features', aisData);
      var error = app.config.defaultError;
      history.replaceState({error: error}, '');
      render();
      return;
    }

    // For business reasons, owner searches need to always show on the
    // results page for the disclaimer.
    if (!isOwnerSearch && (
      aisData.type == 'Feature' ||
      aisData.features.length === 1)) {
        // If only one property go straight to property view
        feature = (aisData.features ? aisData.features[0] : aisData);
        attrs = feature.properties;
        accountNumber = attrs.opa_account_num;
        href = '?' + $.param({p: accountNumber});
        withUnit = feature.properties.street_address;

        history.replaceState({
          ais: feature,
          address: withUnit,
        }, withUnit, href);

        app.views.property(accountNumber);
    } else {
      // Fetch market_value, sale data from OPA dataset
      var opaUrl = constructOpaUrl(aisData.features);
      $.ajax(opaUrl, {dataType: app.config.ajaxType})
        .done(function (opaData) {
          var keyedOpaData = keyBy(opaData.rows, 'parcel_number')
          $.each(aisData.features, function (index, feature) {
            $.extend(feature.properties, keyedOpaData[feature.properties.opa_account_num] || {})
          })

          var newState = $.extend({}, history.state);
          // Used for rendering a special owner search disclaimer
          if (isOwnerSearch) {
            aisData = $.extend({isOwnerSearch: true}, aisData);
          }
          newState = aisData

          if (!app.globals.historyState) {
            history.state = newState;
          } else {
            history.replaceState(newState, ''); // Second param not optional in IE10
          }
          render();
        });
    }
  }

  function constructOpaUrl (features) {
    var accountNumbers = $.map(features, function (feature) {
      return feature.properties.opa_account_num
    })

    var params = {
      select: [
        'parcel_number',
        'market_value',
        'sale_date',
        'sale_price'
      ].join(','),
      where: 'parcel_number in ("' + accountNumbers.join('","') + '")'
    };
    var query = 'SELECT parcel_number, market_value, sale_date, sale_price FROM '
      + app.config.carto.datasets.properties + " WHERE parcel_number IN ('"
      + accountNumbers.join("','") + "')"
    return app.config.carto.baseUrl + '?q=' + encodeURIComponent(query)
  }

  function keyBy (items, key) {
    var hash = {}
    for (var i = 0; i < items.length; i++) {
      hash[items[i][key]] = items[i]
    }
    return hash
  }

  function render () {
    console.debug('results render')

    var state = history.state;
    if (state.error) return app.hooks.content.text(state.error);
    var features = state.features;
    var total_size = state.total_size;
    var isOwnerSearch = state.isOwnerSearch;

    app.hooks.content.empty(); // Remove loading message

    if (total_size === 0) {
      return app.hooks.content.append(app.hooks.noResults);
    }

    if (isOwnerSearch) {
      renderOwnerSearchDisclaimer();
    }

    // TODO find a place for count
    //app.hooks.count.find('#total').text(state.total);
    //app.hooks.content.append(app.hooks.count);
    app.hooks.resultRows.empty(); // TODO reuse existing result nodes
    if (features) {
      $.each(features, addRow);

      if (total_size > features.length) {
        var seeMoreA = app.hooks.seeMore.find('a');
        seeMoreA.off('click'); // Drop previously created click events
        seeMoreA.on('click', function (e) {

          var params = {
            // gatekeeperKey: app.config['gatekeeperKey'],
            include_units: null,
            opa_only: null,
            page: state.page + 1
          };

          $.ajax('https://api.phila.gov/ais_ps/v1/' + endpoint,
                 {data: params, dataType: app.config.ajaxType})
            .done(function (aisData) {
              // Fetch market_value, sale data from OPA dataset
              var opaUrl = constructOpaUrl(aisData.features);
              $.ajax(opaUrl, {dataType: app.config.ajaxType})
              .done(function (opaData) {
                var keyedOpaData = keyBy(opaData, 'parcel_number')
                $.each(aisData.features, function (index, feature) {
                  $.extend(feature.properties, keyedOpaData[feature.properties.opa_account_num] || {})
                })

                state.features = state.features.concat(aisData.features);
                state.page = aisData.page;
                history.replaceState(state, ''); // Second param not optional in IE10

                $.each(aisData.features, addRow);
                if (state.total_size === state.features.length) app.hooks.seeMore.hide();

                // Remove old Tablesaw data and refresh
                $('[data-hook="results-table"]').table().data("table").destroy();
                $('[data-hook="results-table"]').table().data("table").refresh();
              });
            });
        });
        app.hooks.seeMore.show();
      } else {
        app.hooks.seeMore.hide();
      }
      app.hooks.content.append(app.hooks.results);

      // Update the Tablesaw responsive tables
      $(document).trigger('enhance.tablesaw');
    }
  }

  function renderOwnerSearchDisclaimer() {
    var now = new Date(),
        mins = now.getMinutes(),
        prettyMins = mins < 10 ? ('0' + mins) : mins,
        prettyNow = (now.getMonth() + 1) + '/' + now.getDate() + '/' +  now.getFullYear() +
                    ' ' + now.getHours() + ':' + prettyMins;

    app.hooks.ownerSearchDisclaimer.removeClass('hide');
    app.hooks.ownerSearchDisclaimerDatetime.text(prettyNow);
    app.hooks.ownerSearchDisclaimerQuery.text(parsedQuery.label);

    // Fetch IP & show it
    $.getJSON('https://api.ipify.org?format=json')
      .done(function(response) {
        if(response.ip) {
          app.hooks.ownerSearchDisclaimerIp.text(response.ip);
        }
      })
      .fail(function() {
        app.hooks.ownerSearchDisclaimerIp.text(app.hooks.ownerSearchDisclaimerIp.attr('data-default'));
      });
  }

  function addRow (index, property) {
    var row = app.hooks.resultRow.clone(),
        attrs = property.properties,
        accountNumber = attrs.opa_account_num,
        withUnit = attrs.street_address,
        href = '?' + $.param({p: accountNumber});

    row.append($('').append($('').text(withUnit)));
    row.append($('').text(accounting.formatMoney(attrs.market_value)));
    row.append($('').text(moment(attrs.sale_date).format('M/D/YYYY') + ', ' + accounting.formatMoney(attrs.sale_price)));
    row.append($('').text(attrs.opa_owners && attrs.opa_owners.join(', ')));
    row.append($('').html(''));

    row.on('click', function (e) {
        if (e.ctrlKey || e.altKey || e.shiftKey) return;
        e.preventDefault();
        history.pushState({ais: property, address: withUnit}, withUnit, href);
        window.scroll(0, 0);
        app.views.property(accountNumber);
      });
    app.hooks.resultRows.append(row);
  }
};
<|endoftext|>


<commit_before>var canvas = document.getElementById("renderCanvas");
var engine = new BABYLON.Engine(canvas, true);
var scene;
var camera;
var light;
var ground;

var initScene = function ()
{
	var scene = new BABYLON.Scene(engine);
	scene.clearColor = new BABYLON.Color3(0, 0, 0.2);
	return scene;
};

var initCamera = function ()
{
	var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(-80, 50, -80), scene);
	camera.setTarget(BABYLON.Vector3.Zero());
	camera.attachControl(canvas, false);
	return camera;
};

var initLight = function ()
{
	var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
	light.intensity = .7;
	var d1 = new BABYLON.DirectionalLight("dir", new BABYLON.Vector3(1, -1, -2), scene);
	d1.position = new BABYLON.Vector3(-300,300,600);
	return light;
};

var createMountain = function ( positions, vertexPoint, mapDiv )
{
	var coefWidth = 1;
	var coefHeight = 0;
	for (var i = 0; i < positions.length; i+=3)
	{
	    if ( positions[i] == vertexPoint[0] && positions[i+2] == vertexPoint[2] )
	    {

			for ( var deg = 0; deg < 360; deg+= 0.1 )
			{
				for (var dist = 0; dist < vertexPoint[1]; dist+= 0.1)
				{
					var x = Math.round(dist * Math.cos(Math.PI * deg / 180));
					var z = Math.round(dist * Math.sin(Math.PI * deg / 180));

					var tabIndex = i + x *(mapDiv+1)*3 + z*3 + 1;

					var ymid = vertexPoint[1]/2;
					var ampl = vertexPoint[1]/2;

					var heigh = ymid+coefHeight + ampl*coefWidth * Math.cos( Math.PI / vertexPoint[1] * dist );

					if (positions[tabIndex] < heigh)
						positions[tabIndex] =  heigh;
				}
			}
	    }
	}
	return positions;
};

var createScene = function ()
{

scene = initScene();
camera  = initCamera();
light = initLight();

BABYLON.Engine.ShadersRepository = "shaders/";

var skybox = BABYLON.Mesh.CreateSphere("skyBox", 10, 500, scene);

var shader = new BABYLON.ShaderMaterial("gradient", scene, "gradient", {});
shader.setFloat("offset", 0);
shader.setFloat("exponent", 0.6);
shader.setColor3("topColor", BABYLON.Color3.FromInts(0,119,255));
shader.setColor3("bottomColor", BABYLON.Color3.FromInts(240,240, 255));
shader.backFaceCulling = false;
skybox.material = shader;

skybox.position.y = -80;
/***********************************************************/
// Fog
//    var box = new BABYLON.Mesh(name, scene);
//    var vertexData = BABYLON.VertexData.CreateBox(10);
//    vertexData.applyToMesh(box, false);
var colorMaterial = new BABYLON.StandardMaterial("ground", scene);
colorMaterial.diffuseColor = BABYLON.Color3.FromInts(193, 181, 151);
colorMaterial.specularColor = BABYLON.Color3.Black();

var waterColor = new BABYLON.StandardMaterial("ground", scene);
waterColor.diffuseColor = BABYLON.Color3.FromInts(70, 130, 180);
waterColor.specularColor = BABYLON.Color3.Black();

var wireMaterial = new BABYLON.StandardMaterial("texture1", scene);
wireMaterial.wireframe = true;
//    box.scaling.y = 0.1;
//
// // Ground
// var ground = BABYLON.Mesh.CreateGround("ground", 100, 100, 1, scene);
// ground.material = new BABYLON.StandardMaterial("ground", scene);
// ground.material.diffuseColor = BABYLON.Color3.FromInts(193, 181, 151);
// ground.material.specularColor = BABYLON.Color3.Black();

var mapSize = [100, 100, 100];
var mountain1 = [10, 20, -10];
var mountain2 = [20, 20, 0];
var mountain3 = [20, 40, 0];

ground = BABYLON.Mesh.CreateGround("ground", mapSize[0], mapSize[1], mapSize[2], scene);
// ground.material = wireMaterial;
ground.material = colorMaterial;

var positions = ground.getVerticesData("position");

positions = createMountain(positions, mountain1, mapSize[2]);

positions = createMountain(positions, mountain2, mapSize[2]);

ground.convertToFlatShadedMesh();

var water = BABYLON.Mesh.CreateGround("water", mapSize[0], mapSize[1], mapSize[2], scene);
water.material = waterColor;

water.position.y = -1;

// var animationBox = new BABYLON.Animation("myAnimation", "position.y", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
// var keys = [];
// //At the animation key 0, the value of scaling is "1"
// keys.push({
// 	frame: 0,
// 	value: -5
// });
// //At the animation key 20, the value of scaling is "0.2"
// keys.push({
// 	frame: 400,
// 	value: 15
// });
// animationBox.setKeys(keys);
// //Then add the animation object to box1
// water.animations.push(animationBox);
// //Finally, launch animations on box1, from key 0 to key 100 with loop activated
// scene.beginAnimation(water, 0, 2000, false);

// var animate = setInterval(function ()
// {
// 	water.position.y += 0.02;
// 	if (water.position.y > 3)
// 		clearInterval(animate);

// }, 1000/10);*/

var fountain = BABYLON.Mesh.CreateBox("foutain", 1.0, scene);
fountain.position.x = -45;
fountain.position.y = 20;

var particleSystem = new BABYLON.ParticleSystem("particles", 2000, scene);
particleSystem.particleTexture = new BABYLON.Texture("textures/flares.png", scene);
particleSystem.emitter = fountain;
particleSystem.minEmitBox = new BABYLON.Vector3(-1, 0, 0); // Starting all from
particleSystem.maxEmitBox = new BABYLON.Vector3(1, 0, 0);
particleSystem.emitRate = 200;

// particleSystem.gravity = new BABYLON.Vector3(0, -9.81, 0);
particleSystem.minEmitPower = 0.1;
particleSystem.maxEmitPower = 0.1;

var particlesSize = 1;
var radius = particlesSize/2;
particleSystem.minSize = particlesSize;
particleSystem.maxSize = particlesSize;

particleSystem.color1 = new BABYLON.Color4(0, 0.5, 1, 1);
particleSystem.color2 = new BABYLON.Color4(0, 0.2, 0.5, 1);

particleSystem.direction1 = new BABYLON.Vector3(3, 0, 3);

particleSystem.start();

particleSystem.updateFunction = function ( particles )
{
		for ( var i = 0; i < particles.length; i++ )
		{
			var particle1 = particles[i];
			if ( particle1.position.y + radius >= ground.position.y )
			{
				particle1.direction.y -= 0.005/2; // gravity
				particle1.position = particle1.position.add( particle1.direction );
				particle1.direction.y -= 0.005/2; // gravity
			}
			if ( particle1.position.y - radius <= ground.position.y )
			{
				particle1.position.y = ground.position.y + radius;
			}
			var right = -40;
			if( particle1.position.x + radius >= right )//50
			{
				particle1.direction.x *= -1;
			}
			var up = 10;
			if( particle1.position.z + radius >= up )//50
			{
				particle1.direction.z *= -1;
			}
			var down = -5;
			if( particle1.position.z - radius <= down )//-50
			{
				particle1.direction.z *= -1;
			}
			var left = -50;
			if( particle1.position.x - radius <= left )//-50
			{
				particle1.direction.x *= -1;
			}
			for (var j = i + 1; j < particles.length; j++)
			{
				var particle2 = particles[j];
				// console.log("fs");
				if (detectCollision(particle1, particle2))
				{
					var delta = particle1.position.subtract(particle2.position);
					var d = delta.length();
					// minimum translation distance to push balls apart after intersecting
					// Vector2d mtd = delta.multiply(((getRadius() + ball.getRadius())-d)/d);
					var tmp = ( (1) -d ) / d;
					var mtd = delta.multiplyByFloats( tmp, tmp, tmp );

					// resolve intersection --
					// inverse mass quantities
					var im1 = 1 / 5;
					var im2 = 1 / 5;

					var tmp1 = im1 / ( im1 + im2 );
					particle1.position.addInPlace( mtd.multiplyByFloats( tmp1, tmp1, tmp1 ) );
					var tmp2 = im2 / ( im1 + im2 );
					particle2.position.subtractInPlace( mtd.multiplyByFloats( tmp2, tmp2, tmp2 ) );

					var v = particle1.direction.subtract( particle2.direction );
					var vn = BABYLON.Vector3.Dot( v, mtd.normalize() );

					// sphere intersecting but moving away from each other already
					// if (vn > 0.0f) return;

					// collision impulse
					// float i = (-(1.0f + Constants.restitution) * vn) / (im1 + im2);
					//Vector2d impulse = mtd.multiply(i);

					// change in momentum
					//this.velocity = this.velocity.add(impulse.multiply(im1));
					//ball.velocity = ball.velocity.subtract(impulse.multiply(im2));

					if (vn < 0.0)
					{
						var reduction = 0.1;
						var i_ = ( -( 1.0 + reduction ) * vn ) / ( im1 + im2 );
						var impulse = mtd.normalize().multiplyByFloats( i_, i_, i_ );
						//var i = (-( 1.0 + 0.5 ) * vn ) / ( im1 + im2 );
						//var impulse = mtd.normalize().multiply( i, i, i );

						particle1.direction.addInPlace( impulse.multiplyByFloats( im1, im1, im1 ) );
						particle2.direction.subtractInPlace( impulse.multiplyByFloats( im2, im2, im2 ) );
					}
				}
			}

		}

};

/***********************************************************/
return scene;
};

var resolveCollision = function(part1, part2)
{

};

var detectCollision = function(elem1, elem2)
{
	var xd = elem1.position.x - elem2.position.x;
	var yd = elem1.position.y - elem2.position.y;
	var zd = elem1.position.z - elem2.position.z;

	var sumRadius = 1;
	var sqrRadius = sumRadius * sumRadius;

	var distSqr = (xd * xd) + (yd * yd) + (zd * zd);

	if (distSqr <= sqrRadius)
	{
    	return true;
	}

	return false;
};

var sleep = function (milliseconds)
{
	var start = new Date().getTime();
	for (var i = 0; i < 1e7; i++) {
		if ((new Date().getTime() - start) > milliseconds)
		{
			break;
		}
	}
};

var scene = createScene();
engine.runRenderLoop(function () {
	scene.render();
});

// Resize
window.addEventListener("resize", function () {
	engine.resize();
});

/*

for (var i = 0; i < numberPoints; i+=3)
{
    if (positions[i] == pos[0] && positions[i+2] == pos[2] )
    {
		for ( var deg = 0; deg < 180; deg++ )
		{
			for (var dist = 0; dist < 20; dist++)
			{
				var x = dist * Math.cos(deg);
				var y = dist * Math.sin(deg);
				console.log("x:"+x);
				positions[i - x * 101 * 3 + y + 1] = 10 + 10 * Math.cos( Math.PI / pos[1] * dist );
				positions[i + x * 101 * 3 + y + 1] = 10 + 10 * Math.cos( Math.PI / pos[1] * dist );
				positions[i - dist * 3 + 1 + 101 * 3 * x + y] = 10 + 10 * Math.cos( Math.PI / pos[1] * dist );
				positions[i + dist * 3 + 1 + 101 * 3 * x + y] = 10 + 10 * Math.cos( Math.PI / pos[1] * dist );
		//	var box = BABYLON.Mesh.CreateBox("box", 1.0, scene);
		//	box.position.x = x;
		//box.position.z = y;
				console.log("-------");
			}
		}

			//positions[i - y * 3 + 1] = 10 + 10 * Math.cos( Math.PI / pos[1] * y );
			//positions[i + y * 3 + 1] = 10 + 10 * Math.cos( Math.PI / pos[1] * y );

    }
}
*/

<commit_msg>gravity brain f**k<commit_after>var canvas = document.getElementById("renderCanvas");
var engine = new BABYLON.Engine(canvas, true);
var scene;
var camera;
var light;
var ground;

var particlesSize = 1;
var radius = particlesSize/2;

var initScene = function ()
{
	var scene = new BABYLON.Scene(engine);
	scene.clearColor = new BABYLON.Color3(0, 0, 0.2);
	return scene;
};

var initCamera = function ()
{
	var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(-80, 50, -80), scene);
	camera.setTarget(BABYLON.Vector3.Zero());
	camera.attachControl(canvas, false);
	return camera;
};

var initLight = function ()
{
	var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
	light.intensity = .7;
	var d1 = new BABYLON.DirectionalLight("dir", new BABYLON.Vector3(1, -1, -2), scene);
	d1.position = new BABYLON.Vector3(-300,300,600);
	return light;
};

var createMountain = function ( positions, vertexPoint, mapDiv )
{
	var coefWidth = 1;
	var coefHeight = 0;
	for (var i = 0; i < positions.length; i+=3)
	{
	    if ( positions[i] == vertexPoint[0] && positions[i+2] == vertexPoint[2] )
	    {

			for ( var deg = 0; deg < 360; deg+= 0.1 )
			{
				for (var dist = 0; dist < vertexPoint[1]; dist+= 0.1)
				{
					var x = Math.round(dist * Math.cos(Math.PI * deg / 180));
					var z = Math.round(dist * Math.sin(Math.PI * deg / 180));

					var tabIndex = i + x *(mapDiv+1)*3 + z*3 + 1;

					var ymid = vertexPoint[1]/2;
					var ampl = vertexPoint[1]/2;

					var heigh = ymid+coefHeight + ampl*coefWidth * Math.cos( Math.PI / vertexPoint[1] * dist );

					if (positions[tabIndex] < heigh)
						positions[tabIndex] =  heigh;
				}
			}
	    }
	}
	return positions;
};

var createScene = function ()
{

scene = initScene();
camera  = initCamera();
light = initLight();

BABYLON.Engine.ShadersRepository = "shaders/";

var skybox = BABYLON.Mesh.CreateSphere("skyBox", 10, 500, scene);

var shader = new BABYLON.ShaderMaterial("gradient", scene, "gradient", {});
shader.setFloat("offset", 0);
shader.setFloat("exponent", 0.6);
shader.setColor3("topColor", BABYLON.Color3.FromInts(0,119,255));
shader.setColor3("bottomColor", BABYLON.Color3.FromInts(240,240, 255));
shader.backFaceCulling = false;
skybox.material = shader;

skybox.position.y = -80;
/***********************************************************/
// Fog
//    var box = new BABYLON.Mesh(name, scene);
//    var vertexData = BABYLON.VertexData.CreateBox(10);
//    vertexData.applyToMesh(box, false);
var colorMaterial = new BABYLON.StandardMaterial("ground", scene);
colorMaterial.diffuseColor = BABYLON.Color3.FromInts(193, 181, 151);
colorMaterial.specularColor = BABYLON.Color3.Black();

var waterColor = new BABYLON.StandardMaterial("ground", scene);
waterColor.diffuseColor = BABYLON.Color3.FromInts(70, 130, 180);
waterColor.specularColor = BABYLON.Color3.Black();

var wireMaterial = new BABYLON.StandardMaterial("texture1", scene);
wireMaterial.wireframe = true;
//    box.scaling.y = 0.1;
//
// // Ground
// var ground = BABYLON.Mesh.CreateGround("ground", 100, 100, 1, scene);
// ground.material = new BABYLON.StandardMaterial("ground", scene);
// ground.material.diffuseColor = BABYLON.Color3.FromInts(193, 181, 151);
// ground.material.specularColor = BABYLON.Color3.Black();

var mapSize = [100, 100, 100];
var mountain1 = [10, 20, -10];
var mountain2 = [20, 20, 0];
var mountain3 = [20, 40, 0];

ground = BABYLON.Mesh.CreateGround("ground", mapSize[0], mapSize[1], mapSize[2], scene);
// ground.material = wireMaterial;
ground.material = colorMaterial;

var positions = ground.getVerticesData("position");

positions = createMountain(positions, mountain1, mapSize[2]);

positions = createMountain(positions, mountain2, mapSize[2]);

ground.convertToFlatShadedMesh();

var water = BABYLON.Mesh.CreateGround("water", mapSize[0], mapSize[1], mapSize[2], scene);
water.material = waterColor;

water.position.y = -1;

// var animationBox = new BABYLON.Animation("myAnimation", "position.y", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
// var keys = [];
// //At the animation key 0, the value of scaling is "1"
// keys.push({
// 	frame: 0,
// 	value: -5
// });
// //At the animation key 20, the value of scaling is "0.2"
// keys.push({
// 	frame: 400,
// 	value: 15
// });
// animationBox.setKeys(keys);
// //Then add the animation object to box1
// water.animations.push(animationBox);
// //Finally, launch animations on box1, from key 0 to key 100 with loop activated
// scene.beginAnimation(water, 0, 2000, false);

// var animate = setInterval(function ()
// {
// 	water.position.y += 0.02;
// 	if (water.position.y > 3)
// 		clearInterval(animate);

// }, 1000/10);*/

var fountain = BABYLON.Mesh.CreateBox("foutain", 1.0, scene);
fountain.position.x = -45;
fountain.position.y = 1;

var particleSystem = new BABYLON.ParticleSystem("particles", 2000, scene);
particleSystem.particleTexture = new BABYLON.Texture("textures/flares.png", scene);
particleSystem.emitter = fountain;
particleSystem.minEmitBox = new BABYLON.Vector3(-1, 0, 0); // Starting all from
particleSystem.maxEmitBox = new BABYLON.Vector3(1, 0, 0);
particleSystem.emitRate = 200;

particleSystem.minEmitPower = 0.1;
particleSystem.maxEmitPower = 0.1;

particleSystem.minSize = particlesSize;
particleSystem.maxSize = particlesSize;

particleSystem.color1 = new BABYLON.Color4(0, 0.5, 1, 1);
particleSystem.color2 = new BABYLON.Color4(0, 0.2, 0.5, 1);

particleSystem.direction1 = new BABYLON.Vector3(3, -3, 3);
particleSystem.direction2 = new BABYLON.Vector3(3, -1, 6);

particleSystem.gravity = new BABYLON.Vector3(-1, 0, 0);
particleSystem.maxAngularSpeed = 2;

particleSystem.start();

particleSystem.updateFunction = function ( particles )
{
		for ( var i = 0; i < particles.length; i++ )
		{
			var particle1 = particles[i];
			if ( particle1.position.y + radius >= ground.position.y )
			{
				//particle1.direction.y -= 0.0005/2; // gravity
				particle1.position = particle1.position.add( particle1.direction );
				particle1.direction.y = -0.005/2; // gravity
			}
			if ( particle1.position.y - radius <= ground.position.y )
			{
				particle1.position.y = ground.position.y + radius;
			}
			var right = -30;
			if( particle1.position.x + radius >= right )//50
			{
				particle1.position.x = right - radius;
				particle1.direction.x *= -1;
			}
			var up = 30;
			if( particle1.position.z + radius >= up )//50
			{
				particle1.position.z = up - radius;
				particle1.direction.z *= -1;
			}
			var down = -10;
			if( particle1.position.z - radius <= down )//-50
			{
				particle1.position.z = down + radius;
				particle1.direction.z *= -1;
			}
			var left = -50;
			if( particle1.position.x - radius <= left )//-50
			{
				particle1.position.x = left + radius;
				particle1.direction.x *= -1;
			}
			for (var j = i + 1; j < particles.length; j++)
			{
				var particle2 = particles[j];
				if (detectSphereCollision(particle1, particle2))
				{
					var delta = particle1.position.subtract(particle2.position);
					var d = delta.length();

					var tmp = ( (particlesSize) -d ) / d;
					var mtd = delta.multiplyByFloats( tmp, tmp, tmp );

					var im1 = 1 / 5;
					var im2 = 1 / 5;

					var tmp1 = im1 / ( im1 + im2 );
					particle1.position.addInPlace( mtd.multiplyByFloats( tmp1, tmp1, tmp1 ) );
					var tmp2 = im2 / ( im1 + im2 );
					particle2.position.subtractInPlace( mtd.multiplyByFloats( tmp2, tmp2, tmp2 ) );

					var v = particle1.direction.subtract( particle2.direction );
					var vn = BABYLON.Vector3.Dot( v, mtd.normalize() );

					if (vn < 0.0)
					{
						var reduction = 0.1;
						var i_ = ( -( 1.0 + reduction ) * vn ) / ( im1 + im2 );
						var impulse = mtd.normalize().multiplyByFloats( i_, i_, i_ );

						particle1.direction.addInPlace( impulse.multiplyByFloats( im1, im1, im1 ) );
						particle2.direction.subtractInPlace( impulse.multiplyByFloats( im2, im2, im2 ) );
					}
				}
			}

		}

};

/***********************************************************/
return scene;
};

var resolveCollision = function(part1, part2)
{

};

var resolveEnvCollision = function(part1, part2)
{

};

var detectSphereCollision = function(elem1, elem2)
{
	var xd = elem1.position.x - elem2.position.x;
	var yd = elem1.position.y - elem2.position.y;
	var zd = elem1.position.z - elem2.position.z;

	var sumRadius = particlesSize;
	var sqrRadius = sumRadius * sumRadius;

	var distSqr = (xd * xd) + (yd * yd) + (zd * zd);

	if (distSqr <= sqrRadius)
	{
    	return true;
	}

	return false;
};

var sleep = function (milliseconds)
{
	var start = new Date().getTime();
	for (var i = 0; i < 1e7; i++) {
		if ((new Date().getTime() - start) > milliseconds)
		{
			break;
		}
	}
};

var scene = createScene();
engine.runRenderLoop(function () {
	scene.render();
});

// Resize
window.addEventListener("resize", function () {
	engine.resize();
});

/*

for (var i = 0; i < numberPoints; i+=3)
{
    if (positions[i] == pos[0] && positions[i+2] == pos[2] )
    {
		for ( var deg = 0; deg < 180; deg++ )
		{
			for (var dist = 0; dist < 20; dist++)
			{
				var x = dist * Math.cos(deg);
				var y = dist * Math.sin(deg);
				console.log("x:"+x);
				positions[i - x * 101 * 3 + y + 1] = 10 + 10 * Math.cos( Math.PI / pos[1] * dist );
				positions[i + x * 101 * 3 + y + 1] = 10 + 10 * Math.cos( Math.PI / pos[1] * dist );
				positions[i - dist * 3 + 1 + 101 * 3 * x + y] = 10 + 10 * Math.cos( Math.PI / pos[1] * dist );
				positions[i + dist * 3 + 1 + 101 * 3 * x + y] = 10 + 10 * Math.cos( Math.PI / pos[1] * dist );
		//	var box = BABYLON.Mesh.CreateBox("box", 1.0, scene);
		//	box.position.x = x;
		//box.position.z = y;
				console.log("-------");
			}
		}

			//positions[i - y * 3 + 1] = 10 + 10 * Math.cos( Math.PI / pos[1] * y );
			//positions[i + y * 3 + 1] = 10 + 10 * Math.cos( Math.PI / pos[1] * y );

    }
}
*/

<|endoftext|>


<commit_before>/** Handle the creation of HTML links to documented symbols.
	@constructor
*/
function Link() {
	this.alias = "";
	this.src = "";
	this.file = "";
	this.text = "";
	this.innerName = "";
	this.classLink = false;
	this.targetName = "";

	this.target = function(targetName) {
		if (defined(targetName)) this.targetName = targetName;
		return this;
	}
	this.inner = function(inner) {
		if (defined(inner)) this.innerName = inner;
		return this;
	}
	this.withText = function(text) {
		if (defined(text)) this.text = text;
		return this;
	}
	this.toSrc = function(filename) {
		if (defined(filename)) this.src = filename;
		return this;
	}
	this.toSymbol = function(alias) {
		// 'Class[]' to 'Array of Class objects'
		if(/\[\]$/.test(alias)) {
			alias = alias.replace(/\[\]$/, '');
			this.prefix = 'Array of ';
			this.text = alias;
			this.suffix = ' objects';
		}
		if (defined(alias)) this.alias = new String(alias);
		return this;
	}
	this.toClass = function(alias) {
		this.classLink = true;
		return this.toSymbol(alias);
	}
	this.toFile = function(file) {
		if (defined(file)) this.file = file;
		return this;
	}

	this.toString = function() {
		var linkString;
		var thisLink = this;

		if (this.alias) {
			linkString = this.alias.replace(/(^|[^a-z$0-9_#.:^-])([|a-z$0-9_#.:^-]+)(\([^)]+\))*($|[^a-z$0-9_#.:^-])/i,
				function(match, prematch, symbolName, parameters, postmatch) {
					var symbolNames = symbolName.split("|");
					var links = [];
					for (var i = 0, l = symbolNames.length; i < l; i++) {
						thisLink.alias = symbolNames[i];
						links.push(thisLink._makeSymbolLink(symbolNames[i], parameters));
					}
					return prematch+links.join(" / ")+postmatch;
				}
			);
		}
		else if (this.src) {
			linkString = thisLink._makeSrcLink(this.src);
		}
		else if (this.file) {
			linkString = thisLink._makeFileLink(this.file);
		}

		return linkString;
	}
}

/** prefixed for hashes */
Link.hashPrefix = "";

/** Appended to the front of relative link paths. */
Link.base = "";

Link.symbolNameToLinkName = function(symbol) {
	var linker = "",
		ns = "";

	if (symbol.isStatic) linker = "-";
	else if (symbol.isInner) linker = "-";

	if (symbol.isEvent && !/^event:/.test(symbol.name)) {
		ns = "event:";
	}
	return Link.hashPrefix+linker+ns+symbol.name;
}

Link.getSymbol= function(alias) {
    var symbol= Link.symbolSet.getSymbol(alias);

    if (symbol)
        return symbol;

    if ('#'!==alias.charAt(0) || !Link.currentSymbol)
        return null;

    //  resolve relative name
    var container= Link.currentSymbol;

    while (container)
    {
        symbol= Link.symbolSet.getSymbol(container.alias + alias);
        if (symbol)
            return symbol;

        //  No superclass
        if (!container.augments.length)
            return null;

        container= Link.symbolSet.getSymbol(container.augments[0].desc);
    }

    return null;
}

/** Create a link to another symbol. */
Link.prototype._makeSymbolLink = function(alias, parameters) {
	var linkBase = Link.base+publish.conf.symbolsDir;
	var linkTo = Link.getSymbol(alias);
	var linkPath;
	var target = (this.targetName)? " target=\""+this.targetName+"\"" : "";
	// Expand (parameter,parameter) to (parameter, parameter)
	if (parameters) {
		parameters = parameters.replace(/\,([^\s])/, ', $1')
	}

	// if there is no symbol by that name just return the name unaltered
	if (!linkTo) {
	    return this.text || alias + (parameters || '');
	} else {
		// it's a symbol in another file
		if (!linkTo.is("CONSTRUCTOR") && !linkTo.isNamespace) { // it's a method or property
			linkPath= (Link.filemap) ? Link.filemap[linkTo.memberOf] :
				      escape(linkTo.memberOf) || "global";
				linkPath += publish.conf.ext + "#" + Link.symbolNameToLinkName(linkTo).toLowerCase();
			if (parameters) {
				linkPath += '-' + parameters.replace(/[()]+/g, '').split(', ').join('-').toLowerCase();
			}
		} else {
			linkPath = (Link.filemap)? Link.filemap[linkTo.alias] : escape(linkTo.alias);
			linkPath += publish.conf.ext;// + (this.classLink? "":"#" + Link.hashPrefix + "constructor");
		}
		linkPath = linkBase + linkPath
	}

	var linkText= (this.text || alias) + (parameters || '');

	var link = {
		linkPath: linkPath,
		linkText: linkText.replace(/^#/, ''),
		linkInner: (this.innerName? "#" + this.innerName : "")
	};

	if (typeof JSDOC.PluginManager != "undefined") {
		JSDOC.PluginManager.run("onSymbolLink", link);
	}
	if (/^[A-Z].+\#/.test(link.linkText)) {
		link.linkText = link.linkText.charAt(0).toLowerCase() + link.linkText.slice(1);
		link.linkText = link.linkText.replace(/#/g, '.');
	}
	var onClick = '';
	// If linking to a member of the same class, we also need to toggle it:
	if (alias.charAt(0) == '#') {
		var linkName = link.linkPath.replace(/^[^#]+#/, '');
		onClick = " onclick=\"return toggleMember('" + linkName + "', true);\"";
	}
	var html = "<tt>"+link.linkText+"</tt>";
	if (this.prefix || this.suffix)
		html = (this.prefix || '') + html + (this.suffix || '');
	return html;
}

/** Create a link to a source file. */
Link.prototype._makeSrcLink = function(srcFilePath) {
	var target = (this.targetName)? " target=\""+this.targetName+"\"" : "";

	// transform filepath into a filename
	var srcFile = srcFilePath.replace(/\.\.?[\\\/]/g, "").replace(/[:\\\/]/g, "_");
	var outFilePath = Link.base + publish.conf.srcDir + srcFile + publish.conf.ext;

	if (!this.text) this.text = FilePath.fileName(srcFilePath);
	return ""+this.text+"";
}

/** Create a link to a source file. */
Link.prototype._makeFileLink = function(filePath) {
	var target = (this.targetName)? " target=\""+this.targetName+"\"" : "";

	var outFilePath =  Link.base + filePath;

	if (!this.text) this.text = filePath;
	return ""+this.text+"";
}<commit_msg>Clean up code.<commit_after>/** Handle the creation of HTML links to documented symbols.
	@constructor
*/
function Link() {
	this.alias = "";
	this.src = "";
	this.file = "";
	this.text = "";
	this.innerName = "";
	this.classLink = false;
	this.targetName = "";

	this.target = function(targetName) {
		if (defined(targetName)) this.targetName = targetName;
		return this;
	}
	this.inner = function(inner) {
		if (defined(inner)) this.innerName = inner;
		return this;
	}
	this.withText = function(text) {
		if (defined(text)) this.text = text;
		return this;
	}
	this.toSrc = function(filename) {
		if (defined(filename)) this.src = filename;
		return this;
	}
	this.toSymbol = function(alias) {
		// 'Class[]' to 'Array of Class objects'
		if(/\[\]$/.test(alias)) {
			alias = alias.replace(/\[\]$/, '');
			this.prefix = 'Array of ';
			this.text = alias;
			this.suffix = ' objects';
		}
		if (defined(alias)) this.alias = new String(alias);
		return this;
	}
	this.toClass = function(alias) {
		this.classLink = true;
		return this.toSymbol(alias);
	}
	this.toFile = function(file) {
		if (defined(file)) this.file = file;
		return this;
	}

	this.toString = function() {
		var linkString;
		var thisLink = this;

		if (this.alias) {
			linkString = this.alias.replace(/(^|[^a-z$0-9_#.:^-])([|a-z$0-9_#.:^-]+)(\([^)]+\))*($|[^a-z$0-9_#.:^-])/i,
				function(match, prematch, symbolName, parameters, postmatch) {
					var symbolNames = symbolName.split("|");
					var links = [];
					for (var i = 0, l = symbolNames.length; i < l; i++) {
						thisLink.alias = symbolNames[i];
						links.push(thisLink._makeSymbolLink(symbolNames[i], parameters));
					}
					return prematch+links.join(" / ")+postmatch;
				}
			);
		}
		else if (this.src) {
			linkString = thisLink._makeSrcLink(this.src);
		}
		else if (this.file) {
			linkString = thisLink._makeFileLink(this.file);
		}

		return linkString;
	}
}

/** prefixed for hashes */
Link.hashPrefix = "";

/** Appended to the front of relative link paths. */
Link.base = "";

Link.symbolNameToLinkName = function(symbol) {
	var linker = "",
		ns = "";

	if (symbol.isStatic) linker = "-";
	else if (symbol.isInner) linker = "-";

	if (symbol.isEvent && !/^event:/.test(symbol.name)) {
		ns = "event:";
	}
	return Link.hashPrefix+linker+ns+symbol.name;
}

Link.getSymbol= function(alias) {
    var symbol= Link.symbolSet.getSymbol(alias);

    if (symbol)
        return symbol;

    if ('#'!==alias.charAt(0) || !Link.currentSymbol)
        return null;

    //  resolve relative name
    var container= Link.currentSymbol;

    while (container)
    {
        symbol= Link.symbolSet.getSymbol(container.alias + alias);
        if (symbol)
            return symbol;

        //  No superclass
        if (!container.augments.length)
            return null;

        container= Link.symbolSet.getSymbol(container.augments[0].desc);
    }

    return null;
}

/** Create a link to another symbol. */
Link.prototype._makeSymbolLink = function(alias, parameters) {
	var linkBase = Link.base + publish.conf.symbolsDir;
	var linkTo = Link.getSymbol(alias);
	var linkPath;
	var target = (this.targetName)? " target=\""+this.targetName+"\"" : "";
	// Expand (parameter,parameter) to (parameter, parameter)
	if (parameters) {
		parameters = parameters.replace(/\,([^\s])/, ', $1')
	}

	// if there is no symbol by that name just return the name unaltered
	if (!linkTo) {
	    return this.text || alias + (parameters || '');
	} else {
		// it's a symbol in another file
		if (!linkTo.is("CONSTRUCTOR") && !linkTo.isNamespace) { // it's a method or property
			linkPath= (Link.filemap) ? Link.filemap[linkTo.memberOf] :
				      escape(linkTo.memberOf) || "global";
				linkPath += publish.conf.ext + "#" + Link.symbolNameToLinkName(linkTo).toLowerCase();
			if (parameters) {
				linkPath += '-' + parameters.replace(/[()]+/g, '').split(', ').join('-').toLowerCase();
			}
		} else {
			linkPath = (Link.filemap)? Link.filemap[linkTo.alias] : escape(linkTo.alias);
			linkPath += publish.conf.ext;// + (this.classLink? "":"#" + Link.hashPrefix + "constructor");
		}
		linkPath = linkBase + linkPath
	}

	var linkText= (this.text || alias) + (parameters || '');

	var link = {
		linkPath: linkPath,
		linkText: linkText.replace(/^#/, ''),
		linkInner: (this.innerName? "#" + this.innerName : "")
	};

	if (typeof JSDOC.PluginManager != "undefined") {
		JSDOC.PluginManager.run("onSymbolLink", link);
	}
	if (/^[A-Z].+\#/.test(link.linkText)) {
		link.linkText = link.linkText.charAt(0).toLowerCase() + link.linkText.slice(1);
		link.linkText = link.linkText.replace(/#/g, '.');
	}
	var onClick = '';
	// If linking to a member of the same class, we also need to toggle it:
	if (alias.charAt(0) == '#') {
		var linkName = link.linkPath.replace(/^[^#]+#/, '');
		onClick = " onclick=\"return toggleMember('" + linkName + "', true);\"";
	}
	var html = "<tt>"+link.linkText+"</tt>";
	if (this.prefix || this.suffix)
		html = (this.prefix || '') + html + (this.suffix || '');
	return html;
}

/** Create a link to a source file. */
Link.prototype._makeSrcLink = function(srcFilePath) {
	var target = (this.targetName)? " target=\""+this.targetName+"\"" : "";

	// transform filepath into a filename
	var srcFile = srcFilePath.replace(/\.\.?[\\\/]/g, "").replace(/[:\\\/]/g, "_");
	var outFilePath = Link.base + publish.conf.srcDir + srcFile + publish.conf.ext;

	if (!this.text) this.text = FilePath.fileName(srcFilePath);
	return ""+this.text+"";
}

/** Create a link to a source file. */
Link.prototype._makeFileLink = function(filePath) {
	var target = (this.targetName)? " target=\""+this.targetName+"\"" : "";

	var outFilePath =  Link.base + filePath;

	if (!this.text) this.text = filePath;
	return ""+this.text+"";
}<|endoftext|>


<commit_before>module.exports = function(grunt, config, parameters, done) {
	var zipUrl = parameters.releaseSourceZip || 'https://github.com/concrete5/concrete5/archive/develop.zip';
	var workFolder = parameters.releaseWorkFolder || './release';
	function endForError(e) {
		process.stderr.write(e.message || e);
		done(false);
	}
	try {
		var fs = require('fs'),
			path = require('path')
			Download = require('download'),
			c5fs = require('../../libraries/fs'),
			shell = require('shelljs');
		if(c5fs.isDirectory(workFolder)) {
			process.stdout.write('Removing working folder... ');
			shell.rm('-rf', workFolder);
			if(c5fs.isDirectory(workFolder)) {
				throw new Error('Unable to remove ' + workFolder);
			}
			process.stdout.write('done.\n')
		}
		fs.mkdir(workFolder);
		process.stdout.write('Downloading & unzipping archive... ');
		var download = new Download({extract: true})
			.get(zipUrl)
			.dest(workFolder);
		download.run(function(err) {
			if (err) {
				endForError(err);
				return;
			}
			process.stdout.write('done.\n');
			var extractedFolder = null;
			fs.readdirSync(workFolder).forEach(function(item) {
				if(item.indexOf('.') !== 0) {
					if(extractedFolder === null) {
						extractedFolder = item;
					}
					else {
						throw new Error('Multiple items in the root of the extract archive!');
					}
				}
			});
			if(extractedFolder === null) {
				throw new Error('No items extracted!');
			}
			fs.renameSync(path.join(workFolder, extractedFolder), path.join(workFolder, 'source'));
			done();
		});
	}
	catch(e) {
		endForError(e);
		return;
	}
};<commit_msg>[grunt-build-release] Fix permissions extracting downloaded ZIP (#3847)<commit_after>module.exports = function(grunt, config, parameters, done) {
	var zipUrl = parameters.releaseSourceZip || 'https://github.com/concrete5/concrete5/archive/develop.zip';
	var workFolder = parameters.releaseWorkFolder || './release';
	function endForError(e) {
		process.stderr.write(e.message || e);
		done(false);
	}
	try {
		var fs = require('fs'),
			path = require('path')
			Download = require('download'),
			c5fs = require('../../libraries/fs'),
			shell = require('shelljs');
		if(c5fs.isDirectory(workFolder)) {
			process.stdout.write('Removing working folder... ');
			shell.rm('-rf', workFolder);
			if(c5fs.isDirectory(workFolder)) {
				throw new Error('Unable to remove ' + workFolder);
			}
			process.stdout.write('done.\n')
		}
		fs.mkdir(workFolder);
		process.stdout.write('Downloading & unzipping archive... ');
		var download = new Download({extract: true, mode: '755'})
			.get(zipUrl)
			.dest(workFolder);
		download.run(function(err) {
			if (err) {
				endForError(err);
				return;
			}
			process.stdout.write('done.\n');
			var extractedFolder = null;
			fs.readdirSync(workFolder).forEach(function(item) {
				if(item.indexOf('.') !== 0) {
					if(extractedFolder === null) {
						extractedFolder = item;
					}
					else {
						throw new Error('Multiple items in the root of the extract archive!');
					}
				}
			});
			if(extractedFolder === null) {
				throw new Error('No items extracted!');
			}
			fs.renameSync(path.join(workFolder, extractedFolder), path.join(workFolder, 'source'));
			done();
		});
	}
	catch(e) {
		endForError(e);
		return;
	}
};
<|endoftext|>


<commit_before>/* jshint unused:vars, undef:true, node:true */

module.exports = function(grunt, config, parameters, done) {
	var zipUrl = parameters.releaseSourceZip || 'https://github.com/concrete5/concrete5/archive/release/8.4.4.zip';
	var workFolder = parameters.releaseWorkFolder || './release';
	function endForError(e) {
		process.stderr.write(e.message || e);
		done(false);
	}
	try {
		var fs = require('fs'),
			path = require('path'),
			Download = require('download'),
			c5fs = require('../../libraries/fs'),
			shell = require('shelljs'),
			exec = require('child_process').exec;

		if(c5fs.isDirectory(workFolder)) {
			process.stdout.write('Removing working folder... ');
			shell.rm('-rf', workFolder);
			if(c5fs.isDirectory(workFolder)) {
				throw new Error('Unable to remove ' + workFolder);
			}
			process.stdout.write('done.\n');
		}
		fs.mkdir(workFolder);

		process.stdout.write('Downloading archive... ');
		(new Download({extract: false}))
			.get(zipUrl)
			.dest(workFolder)
			.rename('source.zip')
			.run(function(err) {
				if (err) {
					endForError(err);
					return;
				}
				process.stdout.write('done.\n');
				process.stdout.write('Extracting archive... ');
				exec(
					'unzip -q source.zip',
					{
						cwd: workFolder
					},
					function(error, stdout, stderr) {
						if(error) {
							endForError(stderr || error);
							return;
						}
						process.stdout.write('done.\n');
						process.stdout.write('Deleting archive... ');
						shell.rm('-f', path.join(workFolder, 'source.zip'));
						process.stdout.write('done.\n');
						var extractedFolder = null;
						fs.readdirSync(workFolder).forEach(function(item) {
							if(item.indexOf('.') !== 0) {
								if(extractedFolder === null) {
									extractedFolder = item;
								}
								else {
									throw new Error('Multiple items in the root of the extract archive!');
								}
							}
						});
						if(extractedFolder === null) {
							throw new Error('No items extracted!');
						}
						fs.renameSync(path.join(workFolder, extractedFolder), path.join(workFolder, 'source'));
						done();
					}
				);
			})
		;
	}
	catch(e) {
		endForError(e);
		return;
	}
};
<commit_msg>Updating download.js<commit_after>/* jshint unused:vars, undef:true, node:true */

module.exports = function(grunt, config, parameters, done) {
	var zipUrl = parameters.releaseSourceZip || 'https://github.com/concrete5/concrete5/archive/release/8.4.5.zip';
	var workFolder = parameters.releaseWorkFolder || './release';
	function endForError(e) {
		process.stderr.write(e.message || e);
		done(false);
	}
	try {
		var fs = require('fs'),
			path = require('path'),
			Download = require('download'),
			c5fs = require('../../libraries/fs'),
			shell = require('shelljs'),
			exec = require('child_process').exec;

		if(c5fs.isDirectory(workFolder)) {
			process.stdout.write('Removing working folder... ');
			shell.rm('-rf', workFolder);
			if(c5fs.isDirectory(workFolder)) {
				throw new Error('Unable to remove ' + workFolder);
			}
			process.stdout.write('done.\n');
		}
		fs.mkdir(workFolder);

		process.stdout.write('Downloading archive... ');
		(new Download({extract: false}))
			.get(zipUrl)
			.dest(workFolder)
			.rename('source.zip')
			.run(function(err) {
				if (err) {
					endForError(err);
					return;
				}
				process.stdout.write('done.\n');
				process.stdout.write('Extracting archive... ');
				exec(
					'unzip -q source.zip',
					{
						cwd: workFolder
					},
					function(error, stdout, stderr) {
						if(error) {
							endForError(stderr || error);
							return;
						}
						process.stdout.write('done.\n');
						process.stdout.write('Deleting archive... ');
						shell.rm('-f', path.join(workFolder, 'source.zip'));
						process.stdout.write('done.\n');
						var extractedFolder = null;
						fs.readdirSync(workFolder).forEach(function(item) {
							if(item.indexOf('.') !== 0) {
								if(extractedFolder === null) {
									extractedFolder = item;
								}
								else {
									throw new Error('Multiple items in the root of the extract archive!');
								}
							}
						});
						if(extractedFolder === null) {
							throw new Error('No items extracted!');
						}
						fs.renameSync(path.join(workFolder, extractedFolder), path.join(workFolder, 'source'));
						done();
					}
				);
			})
		;
	}
	catch(e) {
		endForError(e);
		return;
	}
};
<|endoftext|>