<commit_before>var solutionNumber;

Percolator = {

    isZooming: false,
    solutionNumber: undefined,
    stage2SolutionNumber: undefined,
    currentState: undefined,
    isDetailWindowOpen: false,

    zoomIn: function (target) {

        var posX;
        var posY;
        if (target) {
            posX = target.attributes[0].value - ((BubbleGraph.width / 2) * BubbleGraph.ZOOM_MAX);
            posY = target.attributes[1].value - ((BubbleGraph.height / 2) * BubbleGraph.ZOOM_MAX);
            $("span.upvote").attr("id", "upvote");
            $("span.downvote").attr("id", "downvote");

            // Solution number is only needed once: to assign problem title to synopsis--
            // other state changes are handled by ajax requests/adaptive parsing
            solutionNumber = $(target).attr("id");
            console.log(solutionNumber)
            if (!BubbleMenu.zoomCount)
            if (solutionNumber) // missing left { but still works
                $("#synopsis").html($.parseJSON(window.data).solutions[solutionNumber].description);
        }
        else {
            posX = (BubbleGraph.width / 2) - ((BubbleGraph.width / 2) * BubbleGraph.ZOOM_MAX);
            posY = (BubbleGraph.height / 2) - ((BubbleGraph.height / 2) * BubbleGraph.ZOOM_MAX);
            $("span.upvote").attr("id", "problem_upvote");
            $("span.downvote").attr("id", "problem_downvote");
            $("#synopsis").html($.parseJSON(window.data).description)
        }
        Percolator.isZooming = true;
        BubbleGraph.zoomIn(posX, posY, zoomInComplete);
        BubbleGraph.hideSolutions();
    },
    zoomOutAndZoomInOnSolution: function () {
        var target = BubbleGraph.solutions[Percolator.solutionNumber];
        var posX = target.sprite.attrs.cx - ((BubbleGraph.width / 2) * BubbleGraph.ZOOM_MAX);
        var posY = target.sprite.attrs.cy - ((BubbleGraph.height / 2) * BubbleGraph.ZOOM_MAX);
        if (target) {
            $("span.upvote").attr("id", "upvote");
            $("span.downvote").attr("id", "downvote");
            Percolator.isZooming = true;
            Percolator.currentState = "solution";
            BubbleGraph.zoomOut(0, 0, zoomInOnSolution);
            BubbleGraph.showSolutions();
            hideDetailWindow()
        }
    }
};

function zoomInOnSolution() {
    var target = BubbleGraph.solutions[Percolator.solutionNumber];
    var posX = target.sprite.attrs.cx - ((BubbleGraph.width / 2) * BubbleGraph.ZOOM_MAX);
    var posY = target.sprite.attrs.cy - ((BubbleGraph.height / 2) * BubbleGraph.ZOOM_MAX);
    BubbleGraph.zoomIn(posX, posY, showDetailWindow);
}

jQuery.ajaxSetup({
    'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
});

function addEventListeners() {

    $('#chart-popup button#back').unbind('click').click(function () {
        zoomOut();
        hideDetailWindow();
    });
    $('#chart-popup button#render-solution-form').unbind('click').click(function () {
        renderSolutionForm();
    });

    $('form').on("submit", "#new_solution", function (e) {
        e.preventDefault();
        $(this.solution_title).val("");
        $(this.solution_description).val("");
        $(this).hide();
    });

    $('#improvement-button').on('click',function () {
        console.log("getting there");
        improvements(solutionNumber);
    });

}
// GTG

function showDetailWindow() {
    Percolator.isDetailWindowOpen = true;
    $('#chart-popup').hide().slideDown(500);
    BubbleMenu.init($.parseJSON(window.data).solutions);
    if (Percolator.currentState == "problem") {
        $('#render-solution-form').show();
        $("#improvement-button").hide();
    } else {
        $('#render-solution-form').hide();
        $("#improvement-button").show();
    }
    ;
}

function hideDetailWindow() {
    Percolator.isDetailWindowOpen = false;
    $('#chart-popup').show().slideUp(500);
}

function renderSolutionForm() {
    var solutionForm = $('#solution-form').detach();
    $(solutionForm).appendTo("#problem-container");
    $("#solution-form").show();
    $("#new_solution").show();
}

function improvements(solutionNumber) {

    $('#improvement-form').show();

    id = $.parseJSON(window.data).solutions[solutionNumber].id;
    $('.Improve').on("click",function(e){
        e.preventDefault();
        var args = {};
        args.title = $("#improvement_title").val();
        args.description = $("#improvement_description").val();
        $.ajax({
            type: "post",
            url: "/solutions/"+id+"/improvements/create",
            data: args
        });
        $('#improvement-form').hide();
    });
}

function zoomOut(){
    BubbleGraph.zoomOut(0, 0, zoomOutComplete);
    BubbleGraph.showSolutions();
    Percolator.isZooming = true;
}

function zoomInComplete()
{
    showDetailWindow();
    Percolator.isZooming = false;
}

function zoomOutComplete()
{
    Percolator.isZooming = false;
}

function upvote() {

    $("#upvote").on("click",function(){

        if (Percolator.currentState === 'solution'){

            var solutionId = $.parseJSON(window.data).solutions[solutionNumber].id

            url = "/solution_upvote";

            dart =  {id: solutionId}

        } else if(Percolator.currentState === 'problem'){

            url = "/problem_upvote"

            dart = {id: document.URL.substring(document.URL.lastIndexOf('/') + 1)}

        }

        $.ajax({

            beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},

            url: url,

            type: "POST",

            data: dart

        }).done(function(r){

            r = $.parseJSON(r)

            $("#count").html(""+r[0]+"  "+r[1]);

        });

    });

}

function downvote() {

    $("#downvote").on("click",function(){

        console.log(solutionNumber)

        if (Percolator.currentState === 'solution'){

            var solutionId = $.parseJSON(window.data).solutions[solutionNumber].id

            url = "/solution_downvote";

            dart =  {id: solutionId}

        } else if(Percolator.currentState === 'problem'){

            url = "/problem_downvote"

            dart = {id: document.URL.substring(document.URL.lastIndexOf('/') + 1)}

        }
            console.log(dart.id)

        $.ajax({

            beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},

            url: url,

            type: "POST",

            data: dart

        }).done(function(r){

           r = $.parseJSON(r)

            $("#count").html(""+r[0]+"   "+r[1]);

        });

    });

}

$(document).ready(function () {
    if ($("#canvas_container").length) {
        var problem = $.parseJSON(window.data);
        $('#problem-container').removeClass('hidden');
        $('#bubble-container').removeClass('hidden');
        $('#solution-form').hide();
        $("#improvement-form").hide();
        $('#chart-popup').hide();
        $('#synopsis')[0].innerHTML = problem.description;
        upvote();
        downvote();
        addEventListeners();
        BubbleGraph.init();
    }
    $('.problem_title').keypress(function(){

        if(this.value.length > 87){
            return false;
        }
        if(this.value.length === 87){
            $("#too_many_chars").html("Max characters for title: 88").css({"margin-left": "auto", "margin-right": "auto", "color": "red"});
        };
    });
});

$(document).on("ajax:complete", function(event, xhr){
    if (xhr.readyState === 4 && xhr.status === 200) {
        $("target").innerHTML = xhr.responseText
        var parsedText = $.parseJSON(xhr.responseText)
        console.log(parsedText)
        if (parsedText.saved === true) {
            Comments.showCommentMessage(true)
            $(".comment-form").append(Comments.commentHTML(parsedText.commentable_type, parsedText.description,
                parsedText.username))
        } else if (parsedText.saved === false) {
            Comments.showCommentMessage(false)
        } else if (parsedText.save_status === true) {
            $("#solution-form").find("input[type=text], textarea").val("");
            $("#solution-form").hide();
            window.data = parsedText.problem;
            var solutions = $.parseJSON(window.data).solutions;
            BubbleGraph.init(window.data)
            BubbleMenu.init(solutions);
            $("#improvement-form").find("input[type=text], textarea").val("");
            $("#improvement-form").hide();
        } else {
        }
    }
});

$(window).resize(function () {
    if ($("#canvas_container").length) {
        BubbleGraph.init();
    }
    if ($("#bubble-container").length) {
        BubbleMenu.init(BubbleMenu.data);
    }
});
<commit_msg>fix improvement form display<commit_after>var solutionNumber;

Percolator = {

    isZooming: false,
    solutionNumber: undefined,
    stage2SolutionNumber: undefined,
    currentState: undefined,
    isDetailWindowOpen: false,

    zoomIn: function (target) {

        var posX;
        var posY;
        if (target) {
            posX = target.attributes[0].value - ((BubbleGraph.width / 2) * BubbleGraph.ZOOM_MAX);
            posY = target.attributes[1].value - ((BubbleGraph.height / 2) * BubbleGraph.ZOOM_MAX);
            $("span.upvote").attr("id", "upvote");
            $("span.downvote").attr("id", "downvote");

            // Solution number is only needed once: to assign problem title to synopsis--
            // other state changes are handled by ajax requests/adaptive parsing
            solutionNumber = $(target).attr("id");
            console.log(solutionNumber)
            if (!BubbleMenu.zoomCount)
            if (solutionNumber) // missing left { but still works
                $("#synopsis").html($.parseJSON(window.data).solutions[solutionNumber].description);
        }
        else {
            posX = (BubbleGraph.width / 2) - ((BubbleGraph.width / 2) * BubbleGraph.ZOOM_MAX);
            posY = (BubbleGraph.height / 2) - ((BubbleGraph.height / 2) * BubbleGraph.ZOOM_MAX);
            $("span.upvote").attr("id", "problem_upvote");
            $("span.downvote").attr("id", "problem_downvote");
            $("#synopsis").html($.parseJSON(window.data).description)
        }
        Percolator.isZooming = true;
        BubbleGraph.zoomIn(posX, posY, zoomInComplete);
        BubbleGraph.hideSolutions();
    },
    zoomOutAndZoomInOnSolution: function () {
        var target = BubbleGraph.solutions[Percolator.solutionNumber];
        var posX = target.sprite.attrs.cx - ((BubbleGraph.width / 2) * BubbleGraph.ZOOM_MAX);
        var posY = target.sprite.attrs.cy - ((BubbleGraph.height / 2) * BubbleGraph.ZOOM_MAX);
        if (target) {
            $("span.upvote").attr("id", "upvote");
            $("span.downvote").attr("id", "downvote");
            Percolator.isZooming = true;
            Percolator.currentState = "solution";
            BubbleGraph.zoomOut(0, 0, zoomInOnSolution);
            BubbleGraph.showSolutions();
            hideDetailWindow()
        }
    }
};

function zoomInOnSolution() {
    var target = BubbleGraph.solutions[Percolator.solutionNumber];
    var posX = target.sprite.attrs.cx - ((BubbleGraph.width / 2) * BubbleGraph.ZOOM_MAX);
    var posY = target.sprite.attrs.cy - ((BubbleGraph.height / 2) * BubbleGraph.ZOOM_MAX);
    BubbleGraph.zoomIn(posX, posY, showDetailWindow);
}

jQuery.ajaxSetup({
    'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
});

function addEventListeners() {

    $('#chart-popup button#back').unbind('click').click(function () {
        zoomOut();
        hideDetailWindow();
    });
    $('#chart-popup button#render-solution-form').unbind('click').click(function () {
        renderSolutionForm();
    });

    $('form').on("submit", "#new_solution", function (e) {
        e.preventDefault();
        $(this.solution_title).val("");
        $(this.solution_description).val("");
        $(this).hide();
    });

    $('#improvement-button').on('click',function () {
        console.log("getting there");
        improvements(solutionNumber);
    });

}
// GTG

function showDetailWindow() {
    Percolator.isDetailWindowOpen = true;
    $('#chart-popup').hide().slideDown(500);
    BubbleMenu.init($.parseJSON(window.data).solutions);
    if (Percolator.currentState == "problem") {
        $('#render-solution-form').show();
        $("#improvement-button").hide();
    } else {
        $('#render-solution-form').hide();
        $("#improvement-button").show();
    }
    ;
}

function hideDetailWindow() {
    Percolator.isDetailWindowOpen = false;
    $('#chart-popup').show().slideUp(500);
    $('#improvement-form').hide();
}

function renderSolutionForm() {
    $(solutionForm).before("#problem-container");
    $("#solution-form").show();
    $("#new_solution").show();
}

function improvements(solutionNumber) {

    $('#improvement-form').show();

    id = $.parseJSON(window.data).solutions[solutionNumber].id;
    $('.Improve').on("click",function(e){
        e.preventDefault();
        var args = {};
        args.title = $("#improvement_title").val();
        args.description = $("#improvement_description").val();
        $.ajax({
            type: "post",
            url: "/solutions/"+id+"/improvements/create",
            data: args
        });
        $('#improvement-form').hide();
    });
}

function zoomOut(){
    BubbleGraph.zoomOut(0, 0, zoomOutComplete);
    BubbleGraph.showSolutions();
    Percolator.isZooming = true;
}

function zoomInComplete()
{
    showDetailWindow();
    Percolator.isZooming = false;
}

function zoomOutComplete()
{
    Percolator.isZooming = false;
}

function upvote() {

    $("#upvote").on("click",function(){

        if (Percolator.currentState === 'solution'){

            var solutionId = $.parseJSON(window.data).solutions[solutionNumber].id

            url = "/solution_upvote";

            dart =  {id: solutionId}

        } else if(Percolator.currentState === 'problem'){

            url = "/problem_upvote"

            dart = {id: document.URL.substring(document.URL.lastIndexOf('/') + 1)}

        }

        $.ajax({

            beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},

            url: url,

            type: "POST",

            data: dart

        }).done(function(r){

            r = $.parseJSON(r)

            $("#count").html(""+r[0]+"  "+r[1]);

        });

    });

}

function downvote() {

    $("#downvote").on("click",function(){

        console.log(solutionNumber)

        if (Percolator.currentState === 'solution'){

            var solutionId = $.parseJSON(window.data).solutions[solutionNumber].id

            url = "/solution_downvote";

            dart =  {id: solutionId}

        } else if(Percolator.currentState === 'problem'){

            url = "/problem_downvote"

            dart = {id: document.URL.substring(document.URL.lastIndexOf('/') + 1)}

        }
            console.log(dart.id)

        $.ajax({

            beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},

            url: url,

            type: "POST",

            data: dart

        }).done(function(r){

           r = $.parseJSON(r)

            $("#count").html(""+r[0]+"   "+r[1]);

        });

    });

}

$(document).ready(function () {
    if ($("#canvas_container").length) {
        var problem = $.parseJSON(window.data);
        $('#problem-container').removeClass('hidden');
        $('#bubble-container').removeClass('hidden');
        $('#solution-form').hide();
        $("#improvement-form").hide();
        $('#chart-popup').hide();
        $('#synopsis')[0].innerHTML = problem.description;
        upvote();
        downvote();
        addEventListeners();
        BubbleGraph.init();
    }
    $('.problem_title').keypress(function(){

        if(this.value.length > 87){
            return false;
        }
        if(this.value.length === 87){
            $("#too_many_chars").html("Max characters for title: 88").css({"margin-left": "auto", "margin-right": "auto", "color": "red"});
        };
    });
});

$(document).on("ajax:complete", function(event, xhr){
    if (xhr.readyState === 4 && xhr.status === 200) {
        $("target").innerHTML = xhr.responseText
        var parsedText = $.parseJSON(xhr.responseText)
        console.log(parsedText)
        if (parsedText.saved === true) {
            Comments.showCommentMessage(true)
            $(".comment-form").append(Comments.commentHTML(parsedText.commentable_type, parsedText.description,
                parsedText.username))
        } else if (parsedText.saved === false) {
            Comments.showCommentMessage(false)
        } else if (parsedText.save_status === true) {
            $("#solution-form").find("input[type=text], textarea").val("");
            $("#solution-form").hide();
            window.data = parsedText.problem;
            var solutions = $.parseJSON(window.data).solutions;
            BubbleGraph.init(window.data)
            BubbleMenu.init(solutions);
            $("#improvement-form").find("input[type=text], textarea").val("");
            $("#improvement-form").hide();
        } else {
        }
    }
});

// $(window).resize(function () {
//     if ($("#canvas_container").length) {
//         BubbleGraph.init();
//     }
//     if ($("#bubble-container").length) {
//         BubbleMenu.init(BubbleMenu.data);
//     }
// });
<|endoftext|>


<commit_before>/**
 * @license AngularJS v1.5.3
 * (c) 2010-2016 Google, Inc. http://angularjs.org
 * License: MIT
 */
(function(window, angular, undefined) {'use strict';

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *     Any commits to this file should be reviewed with security in mind.  *
 *   Changes to this file can potentially create security vulnerabilities. *
 *          An approval from 2 Core members with history of modifying      *
 *                         this file is required.                          *
 *                                                                         *
 *  Does the change somehow allow for arbitrary javascript to be executed? *
 *    Or allows for someone to change the prototype of built-in objects?   *
 *     Or gives undesired access to variables likes document or window?    *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

var $sanitizeMinErr = angular.$$minErr('$sanitize');

/**
 * @ngdoc module
 * @name ngSanitize
 * @description
 *
 * # ngSanitize
 *
 * The `ngSanitize` module provides functionality to sanitize HTML.
 *
 *
 *

 *
 * See {@link ngSanitize.$sanitize `$sanitize`} for usage.
 */

/**
 * @ngdoc service
 * @name $sanitize
 * @kind function
 *
 * @description
 *   Sanitizes an html string by stripping all potentially dangerous tokens.
 *
 *   The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
 *   then serialized back to properly escaped html string. This means that no unsafe input can make
 *   it into the returned string.
 *
 *   The whitelist for URL sanitization of attribute values is configured using the functions
 *   `aHrefSanitizationWhitelist` and `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider
 *   `$compileProvider`}.
 *
 *   The input may also contain SVG markup if this is enabled via {@link $sanitizeProvider}.
 *
 * @param {string} html HTML input.
 * @returns {string} Sanitized HTML.
 *
 * @example
   <example module="sanitizeExample" deps="angular-sanitize.js">
   <file name="index.html">

        Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>

           Directive
           How
           Source
           Rendered

           ng-bind-html
           Automatically uses $sanitize

<div ng-bind-html="snippet">
</div>

           ng-bind-html
           Bypass $sanitize by explicitly trusting the dangerous value

<div ng-bind-html="deliberatelyTrustDangerousSnippet()">
</div>

           ng-bind
           Automatically escapes

<div ng-bind="snippet">
</div>

   </file>
   <file name="protractor.js" type="protractor">
     it('should sanitize the html snippet by default', function() {
       expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
         toBe('
an html\nclick here\nsnippet
');
     });

     it('should inline raw snippet if bound to a trusted value', function() {
       expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
         toBe("
an html\n" +
              "click here\n" +
              "snippet
");
     });

     it('should escape snippet without any filter', function() {
       expect(element(by.css('#bind-default div')).getInnerHtml()).
         toBe("<p style=\"color:blue\">an html\n" +
              "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
              "snippet</p>");
     });

     it('should update', function() {
       element(by.model('snippet')).clear();
       element(by.model('snippet')).sendKeys('new text');
       expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
         toBe('new text');
       expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
         'new text');
       expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
         "new <b onclick=\"alert(1)\">text</b>");
     });
   </file>
   </example>
 */

/**
 * @ngdoc provider
 * @name $sanitizeProvider
 *
 * @description
 * Creates and configures {@link $sanitize} instance.
 */
function $SanitizeProvider() {
  var svgEnabled = false;

  this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
    if (svgEnabled) {
      angular.extend(validElements, svgElements);
    }
    return function(html) {
      var buf = [];
      htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
        return !/^unsafe:/.test($$sanitizeUri(uri, isImage));
      }));
      return buf.join('');
    };
  }];

  /**
   * @ngdoc method
   * @name $sanitizeProvider#enableSvg
   * @kind function
   *
   * @description
   * Enables a subset of svg to be supported by the sanitizer.
   *
   *

   *
By enabling this setting without taking other precautions, you might expose your
   *   application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned
   *   outside of the containing element and be rendered over other elements on the page (e.g. a login
   *   link). Such behavior can then result in phishing incidents.

   *
   *
To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg
   *   tags within the sanitized content:

   *
   *

   *
   *

   *   .rootOfTheIncludedContent svg {
   *     overflow: hidden !important;
   *   }
   *

   *

   *
   * @param {boolean=} regexp New regexp to whitelist urls with.
   * @returns {boolean|ng.$sanitizeProvider} Returns the currently configured value if called
   *    without an argument or self for chaining otherwise.
   */
  this.enableSvg = function(enableSvg) {
    if (angular.isDefined(enableSvg)) {
      svgEnabled = enableSvg;
      return this;
    } else {
      return svgEnabled;
    }
  };
}

function sanitizeText(chars) {
  var buf = [];
  var writer = htmlSanitizeWriter(buf, angular.noop);
  writer.chars(chars);
  return buf.join('');
}

// Regular Expressions for parsing tags and attributes
var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
  // Match everything outside of normal chars and " (quote character)
  NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;

// Good source of info about elements and attributes
// http://dev.w3.org/html5/spec/Overview.html#semantics
// http://simon.html5.org/html-elements

// Safe Void Elements - HTML5
// http://dev.w3.org/html5/spec/Overview.html#void-elements
var voidElements = toMap("area,br,col,hr,img,wbr");

// Elements that you can, intentionally, leave open (and which close themselves)
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
    optionalEndTagInlineElements = toMap("rp,rt"),
    optionalEndTagElements = angular.extend({},
                                            optionalEndTagInlineElements,
                                            optionalEndTagBlockElements);

// Safe Block Elements - HTML5
var blockElements = angular.extend({}, optionalEndTagBlockElements, toMap("address,article," +
        "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
        "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul"));

// Inline Elements - HTML5
var inlineElements = angular.extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," +
        "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
        "samp,small,span,strike,strong,sub,sup,time,tt,u,var"));

// SVG Elements
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
// They can potentially allow for arbitrary javascript to be executed. See #11290
var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
        "hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
        "radialGradient,rect,stop,svg,switch,text,title,tspan");

// Blocked Elements (will be stripped)
var blockedElements = toMap("script,style");

var validElements = angular.extend({},
                                   voidElements,
                                   blockElements,
                                   inlineElements,
                                   optionalEndTagElements);

//Attributes that have href and hence need to be sanitized
var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href");

var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
    'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
    'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
    'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
    'valign,value,vspace,width');

// SVG attributes (without "id" and "name" attributes)
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
    'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
    'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
    'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
    'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
    'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
    'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
    'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
    'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
    'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
    'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
    'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
    'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
    'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
    'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);

var validAttrs = angular.extend({},
                                uriAttrs,
                                svgAttrs,
                                htmlAttrs);

function toMap(str, lowercaseKeys) {
  var obj = {}, items = str.split(','), i;
  for (i = 0; i < items.length; i++) {
    obj[lowercaseKeys ? angular.lowercase(items[i]) : items[i]] = true;
  }
  return obj;
}

var inertBodyElement;
(function(window) {
  var doc;
  if (window.document && window.document.implementation) {
    doc = window.document.implementation.createHTMLDocument("inert");
  } else {
    throw $sanitizeMinErr('noinert', "Can't create an inert html document");
  }
  var docElement = doc.documentElement || doc.getDocumentElement();
  var bodyElements = docElement.getElementsByTagName('body');

  // usually there should be only one body element in the document, but IE doesn't have any, so we need to create one
  if (bodyElements.length === 1) {
    inertBodyElement = bodyElements[0];
  } else {
    var html = doc.createElement('html');
    inertBodyElement = doc.createElement('body');
    html.appendChild(inertBodyElement);
    doc.appendChild(html);
  }
})(window);

/**
 * @example
 * htmlParser(htmlString, {
 *     start: function(tag, attrs) {},
 *     end: function(tag) {},
 *     chars: function(text) {},
 *     comment: function(text) {}
 * });
 *
 * @param {string} html string
 * @param {object} handler
 */
function htmlParser(html, handler) {
  if (html === null || html === undefined) {
    html = '';
  } else if (typeof html !== 'string') {
    html = '' + html;
  }
  inertBodyElement.innerHTML = html;

  //mXSS protection
  var mXSSAttempts = 5;
  do {
    if (mXSSAttempts === 0) {
      throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable");
    }
    mXSSAttempts--;

    // strip custom-namespaced attributes on IE<=11
    if (document.documentMode <= 11) {
      stripCustomNsAttrs(inertBodyElement);
    }
    html = inertBodyElement.innerHTML; //trigger mXSS
    inertBodyElement.innerHTML = html;
  } while (html !== inertBodyElement.innerHTML);

  var node = inertBodyElement.firstChild;
  while (node) {
    switch (node.nodeType) {
      case 1: // ELEMENT_NODE
        handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
        break;
      case 3: // TEXT NODE
        handler.chars(node.textContent);
        break;
    }

    var nextNode;
    if (!(nextNode = node.firstChild)) {
      if (node.nodeType == 1) {
        handler.end(node.nodeName.toLowerCase());
      }
      nextNode = node.nextSibling;
      if (!nextNode) {
        while (nextNode == null) {
          node = node.parentNode;
          if (node === inertBodyElement) break;
          nextNode = node.nextSibling;
          if (node.nodeType == 1) {
            handler.end(node.nodeName.toLowerCase());
          }
        }
      }
    }
    node = nextNode;
  }

  while (node = inertBodyElement.firstChild) {
    inertBodyElement.removeChild(node);
  }
}

function attrToMap(attrs) {
  var map = {};
  for (var i = 0, ii = attrs.length; i < ii; i++) {
    var attr = attrs[i];
    map[attr.name] = attr.value;
  }
  return map;
}

/**
 * Escapes all potentially dangerous characters, so that the
 * resulting string can be safely inserted into attribute or
 * element text.
 * @param value
 * @returns {string} escaped text
 */
function encodeEntities(value) {
  return value.
    replace(/&/g, '&').
    replace(SURROGATE_PAIR_REGEXP, function(value) {
      var hi = value.charCodeAt(0);
      var low = value.charCodeAt(1);
      return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
    }).
    replace(NON_ALPHANUMERIC_REGEXP, function(value) {
      return '&#' + value.charCodeAt(0) + ';';
    }).
    replace(/</g, '<').
    replace(/>/g, '>');
}

/**
 * create an HTML/XML writer which writes to buffer
 * @param {Array} buf use buf.join('') to get out sanitized html string
 * @returns {object} in the form of {
 *     start: function(tag, attrs) {},
 *     end: function(tag) {},
 *     chars: function(text) {},
 *     comment: function(text) {}
 * }
 */
function htmlSanitizeWriter(buf, uriValidator) {
  var ignoreCurrentElement = false;
  var out = angular.bind(buf, buf.push);
  return {
    start: function(tag, attrs) {
      tag = angular.lowercase(tag);
      if (!ignoreCurrentElement && blockedElements[tag]) {
        ignoreCurrentElement = tag;
      }
      if (!ignoreCurrentElement && validElements[tag] === true) {
        out('<');
        out(tag);
        angular.forEach(attrs, function(value, key) {
          var lkey=angular.lowercase(key);
          var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
          if (validAttrs[lkey] === true &&
            (uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
            out(' ');
            out(key);
            out('="');
            out(encodeEntities(value));
            out('"');
          }
        });
        out('>');
      }
    },
    end: function(tag) {
      tag = angular.lowercase(tag);
      if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
        out('</');
        out(tag);
        out('>');
      }
      if (tag == ignoreCurrentElement) {
        ignoreCurrentElement = false;
      }
    },
    chars: function(chars) {
      if (!ignoreCurrentElement) {
        out(encodeEntities(chars));
      }
    }
  };
}

/**
 * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
 * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
 * to allow any of these custom attributes. This method strips them all.
 *
 * @param node Root element to process
 */
function stripCustomNsAttrs(node) {
  if (node.nodeType === Node.ELEMENT_NODE) {
    var attrs = node.attributes;
    for (var i = 0, l = attrs.length; i < l; i++) {
      var attrNode = attrs[i];
      var attrName = attrNode.name.toLowerCase();
      if (attrName === 'xmlns:ns1' || attrName.indexOf('ns1:') === 0) {
        node.removeAttributeNode(attrNode);
        i--;
        l--;
      }
    }
  }

  var nextNode = node.firstChild;
  if (nextNode) {
    stripCustomNsAttrs(nextNode);
  }

  nextNode = node.nextSibling;
  if (nextNode) {
    stripCustomNsAttrs(nextNode);
  }
}

// define ngSanitize module and register $sanitize service
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);

/* global sanitizeText: false */

/**
 * @ngdoc filter
 * @name linky
 * @kind function
 *
 * @description
 * Finds links in text input and turns them into html links. Supports `http/https/ftp/mailto` and
 * plain email address links.
 *
 * Requires the {@link ngSanitize `ngSanitize`} module to be installed.
 *
 * @param {string} text Input text.
 * @param {string} target Window (`_blank|_self|_parent|_top`) or named frame to open links in.
 * @param {object|function(url)} [attributes] Add custom attributes to the link element.
 *
 *    Can be one of:
 *
 *    - `object`: A map of attributes
 *    - `function`: Takes the url as a parameter and returns a map of attributes
 *
 *    If the map of attributes contains a value for `target`, it overrides the value of
 *    the target parameter.
 *
 *
 * @returns {string} Html-linkified and {@link $sanitize sanitized} text.
 *
 * @usage

 *
 * @example
   <example module="linkyExample" deps="angular-sanitize.js">
     <file name="index.html">

       Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>

           Filter
           Source
           Rendered

           linky filter

<div ng-bind-html="snippet | linky">
</div>

          linky target

<div ng-bind-html="snippetWithSingleURL | linky:'_blank'">
</div>

          linky custom attributes

<div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}">
</div>

           no filter

<div ng-bind="snippet">
</div>

     </file>
     <file name="script.js">
       angular.module('linkyExample', ['ngSanitize'])
         .controller('ExampleController', ['$scope', function($scope) {
           $scope.snippet =
             'Pretty text with some links:\n'+
             'http://angularjs.org/,\n'+
             'mailto:us@somewhere.org,\n'+
             'another@somewhere.org,\n'+
             'and one more: ftp://127.0.0.1/.';
           $scope.snippetWithSingleURL = 'http://angularjs.org/';
         }]);
     </file>
     <file name="protractor.js" type="protractor">
       it('should linkify the snippet with urls', function() {
         expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
             toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
                  'another@somewhere.org, and one more: ftp://127.0.0.1/.');
         expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
       });

       it('should not linkify snippet without the linky filter', function() {
         expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
             toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
                  'another@somewhere.org, and one more: ftp://127.0.0.1/.');
         expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
       });

       it('should update', function() {
         element(by.model('snippet')).clear();
         element(by.model('snippet')).sendKeys('new http://link.');
         expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
             toBe('new http://link.');
         expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
         expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
             .toBe('new http://link.');
       });

       it('should work with the target property', function() {
        expect(element(by.id('linky-target')).
            element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()).
            toBe('http://angularjs.org/');
        expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
       });

       it('should optionally add custom attributes', function() {
        expect(element(by.id('linky-custom-attributes')).
            element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()).
            toBe('http://angularjs.org/');
        expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow');
       });
     </file>
   </example>
 */
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
  var LINKY_URL_REGEXP =
        /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,
      MAILTO_REGEXP = /^mailto:/i;

  var linkyMinErr = angular.$$minErr('linky');
  var isString = angular.isString;

  return function(text, target, attributes) {
    if (text == null || text === '') return text;
    if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);

    var match;
    var raw = text;
    var html = [];
    var url;
    var i;
    while ((match = raw.match(LINKY_URL_REGEXP))) {
      // We can not end in these as they are sometimes found at the end of the sentence
      url = match[0];
      // if we did not match ftp/http/www/mailto then assume mailto
      if (!match[2] && !match[4]) {
        url = (match[3] ? 'http://' : 'mailto:') + url;
      }
      i = match.index;
      addText(raw.substr(0, i));
      addLink(url, match[0].replace(MAILTO_REGEXP, ''));
      raw = raw.substring(i + match[0].length);
    }
    addText(raw);
    return $sanitize(html.join(''));

    function addText(text) {
      if (!text) {
        return;
      }
      html.push(sanitizeText(text));
    }

    function addLink(url, text) {
      var key;
      html.push('<a ');
      if (angular.isFunction(attributes)) {
        attributes = attributes(url);
      }
      if (angular.isObject(attributes)) {
        for (key in attributes) {
          html.push(key + '="' + attributes[key] + '" ');
        }
      } else {
        attributes = {};
      }
      if (angular.isDefined(target) && !('target' in attributes)) {
        html.push('target="',
                  target,
                  '" ');
      }
      html.push('href="',
                url.replace(/"/g, '"'),
                '">');
      addText(text);
      html.push('');
    }
  };
}]);

})(window, window.angular);
<commit_msg>Delete angular-sanitize.js<commit_after><|endoftext|>


<commit_before>import Ember from 'ember';

/**
* Add controller
*
* @class AddController
* @extends Ember.Controller
* @namespace Dasht
* @returns Class
*/

export default Ember.Controller.extend({
  needs: ['dashtboard','application'],
  /**
  Sort default

  @property sortVisible
  @type Array
  */
  sortVisible: ['visible'],
  /**
  Channel lib sorted by visible (added) channels first

  @property librarySortVisible
  @type Array
  */
  librarySortVisible: Ember.computed.sort('controllers.application.model.library', 'sortVisible'),
  /**
  Signal if filtering is open

  @property filtering
  @type Bool
  @default false
  */
  filtering: false,
  /**
  Cached version of filters that hold users choices

  @property cachedFilters
  @type Object
  @default null
  */
  cachedFilters: null,
  /**
  Bool to signal if filters have been applied

  @property currentlyFiltered
  @type Bool
  */
  currentlyFiltered: Ember.computed.notEmpty('cachedFilters'),
  /**
  Signal if message is an error message

  @property error
  @type Bool
  @default false
  */
  error: false,
  /**
  Message to be sent to message field

  @property message
  @type String
  @default null
  */
  message: null,
  /**
  Normalized object of message strings

  @property messages
  @type Object
  */
  messages: {
    noEntry: "Nothing to find.",
    noMatch: "Sorry. We couldn't find that channel.",
    serviceDown: "Oops. The service is down temporarily. Please try back in a few minutes.",
    noUrl: "Hmm, try searching for a url instead ('ie: pbs.org').",
    prevInstalled: "Yep, that's already been added.",
    successInstalled: "Successfully added.",
    addingNew: "Thanks...we\'re adding this channel to your library. If you think this channel should be added to our library, please email us at ideas@evolutionaryapps.com"
  },
  /**
  Hides messaging still visible after 10s delay

  @method messageOut
  */
  messageOut: function() {
    Ember.run.later(this, function() {
      this.set('message', null);
    }, 10000);
  }.observes('message'),
  /**
  Push new channel to library as Ember Object

  @method addNewChannel
  */
  addNewChannel: function(newchannel) {
    var currentModel = this.get('controllers.application').get('model'),
        lib = currentModel.get('library'),
        libModel = Ember.Object.extend({
          init: function() {
            this._super();
          }
        }),
        newChannelModel = libModel.create();

    newChannelModel.setProperties({
      title: newchannel.replace(/(\.*)\.[^.]+/, ''),
      icon: "/assets/icons/new.png",
      url: "//"+newchannel,
      //free is a assumed, but we want to add at least one filterable tag
      tags: ["free"],
      new: true,
      visible: true,
      isfiltered: false
    });

    lib.push(newChannelModel);
    this.saveToLocal(currentModel);

  },
  /**
  Update local storage item with latest model

  @method saveToLocal
  */
  saveToLocal: function(model) {
    localStorage.setItem("dasht-channels", JSON.stringify(model));
  },
  actions: {
    /**
    Action, search for channels in model

    @method findChannel
    */
    findChannel: function() {
      var currentModel = this.get('controllers.application').get('model'),
          channelsLib = currentModel.library,
          query = this.get('channel');

      //match in db
      var exactURLMatch = channelsLib.findBy('url','//'+query),
          exactTitleMatch = channelsLib.findBy('title', query);

      //clear any previous errors
      if(this.get('message') != null) {
        this.set('error', false);
        this.set('message', null);
      }

      /*
      find a channel messaging
      */

      //nothing typed
      if(!query) {
        this.set('error', true);
        return this.set('message', this.messages.noEntry);
      }else {

        //not a url
        if(!(/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/).test(query)) {
          if(!exactTitleMatch) {
            this.set('error', true);
            return this.set('message', this.messages.noUrl);
          }
        }

        //found a match
        if(exactURLMatch || exactTitleMatch){
          var match = exactURLMatch || exactTitleMatch;
          if(match.get('visible')) {
            //already added to dashtboard
            this.set('error', false);
            return this.set('message', this.messages.prevInstalled);
          }else {
            //successfully installed found channel
            this.send('addChannel', match.get('title'));
            this.set('error', false);
            return this.set('message', this.messages.successInstalled);
          }
        }else {
          //new channel being added to user lib
          this.set('error', false);
          this.addNewChannel(query);
          return this.set('message', this.messages.addingNew);
        }
      }

    },
    /**
    Clear out messages block

    @method closeMessage
    */
    closeMessage: function() {
      this.set('message', null);
    },
    /**
    Adds existing lib channel to dashtboard

    @method addChannel
    */
    addChannel: function(channel) {
      this.set('error', false);
      this.get('controllers.dashtboard').toggleChannel(channel, true);
      this.set('message', this.messages.successInstalled);
    },
    /**
    Toggles filtering open and closed

    @method toggleFilters
    */
    toggleFilters: function() {
      this.toggleProperty('filtering');
    }
  }
});
<commit_msg>Additional sort param for /add<commit_after>import Ember from 'ember';

/**
* Add controller
*
* @class AddController
* @extends Ember.Controller
* @namespace Dasht
* @returns Class
*/

export default Ember.Controller.extend({
  needs: ['dashtboard','application'],
  /**
  Sort default

  @property sortVisible
  @type Array
  */
  sortVisible: ['visible','title'],
  /**
  Channel lib sorted by visible (added) channels first, the alphabetical

  @property librarySortVisible
  @type Array
  */
  librarySortVisible: Ember.computed.sort('controllers.application.model.library', 'sortVisible'),
  /**
  Signal if filtering is open

  @property filtering
  @type Bool
  @default false
  */
  filtering: false,
  /**
  Cached version of filters that hold users choices

  @property cachedFilters
  @type Object
  @default null
  */
  cachedFilters: null,
  /**
  Bool to signal if filters have been applied

  @property currentlyFiltered
  @type Bool
  */
  currentlyFiltered: Ember.computed.notEmpty('cachedFilters'),
  /**
  Signal if message is an error message

  @property error
  @type Bool
  @default false
  */
  error: false,
  /**
  Message to be sent to message field

  @property message
  @type String
  @default null
  */
  message: null,
  /**
  Normalized object of message strings

  @property messages
  @type Object
  */
  messages: {
    noEntry: "Nothing to find.",
    noMatch: "Sorry. We couldn't find that channel.",
    serviceDown: "Oops. The service is down temporarily. Please try back in a few minutes.",
    noUrl: "Hmm, try searching for a url instead ('ie: pbs.org').",
    prevInstalled: "Yep, that's already been added.",
    successInstalled: "Successfully added.",
    addingNew: "Thanks...we\'re adding this channel to your library. If you think this channel should be added to our library, please email us at ideas@evolutionaryapps.com"
  },
  /**
  Hides messaging still visible after 10s delay

  @method messageOut
  */
  messageOut: function() {
    Ember.run.later(this, function() {
      this.set('message', null);
    }, 10000);
  }.observes('message'),
  /**
  Push new channel to library as Ember Object

  @method addNewChannel
  */
  addNewChannel: function(newchannel) {
    var currentModel = this.get('controllers.application').get('model'),
        lib = currentModel.get('library'),
        libModel = Ember.Object.extend({
          init: function() {
            this._super();
          }
        }),
        newChannelModel = libModel.create();

    newChannelModel.setProperties({
      title: newchannel.replace(/(\.*)\.[^.]+/, ''),
      icon: "/assets/icons/new.png",
      url: "//"+newchannel,
      //free is a assumed, but we want to add at least one filterable tag
      tags: ["free"],
      new: true,
      visible: true,
      isfiltered: false
    });

    lib.push(newChannelModel);
    this.saveToLocal(currentModel);

  },
  /**
  Update local storage item with latest model

  @method saveToLocal
  */
  saveToLocal: function(model) {
    localStorage.setItem("dasht-channels", JSON.stringify(model));
  },
  actions: {
    /**
    Action, search for channels in model

    @method findChannel
    */
    findChannel: function() {
      var currentModel = this.get('controllers.application').get('model'),
          channelsLib = currentModel.library,
          query = this.get('channel');

      //match in db
      var exactURLMatch = channelsLib.findBy('url','//'+query),
          exactTitleMatch = channelsLib.findBy('title', query);

      //clear any previous errors
      if(this.get('message') != null) {
        this.set('error', false);
        this.set('message', null);
      }

      /*
      find a channel messaging
      */

      //nothing typed
      if(!query) {
        this.set('error', true);
        return this.set('message', this.messages.noEntry);
      }else {

        //not a url
        if(!(/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/).test(query)) {
          if(!exactTitleMatch) {
            this.set('error', true);
            return this.set('message', this.messages.noUrl);
          }
        }

        //found a match
        if(exactURLMatch || exactTitleMatch){
          var match = exactURLMatch || exactTitleMatch;
          if(match.get('visible')) {
            //already added to dashtboard
            this.set('error', false);
            return this.set('message', this.messages.prevInstalled);
          }else {
            //successfully installed found channel
            this.send('addChannel', match.get('title'));
            this.set('error', false);
            return this.set('message', this.messages.successInstalled);
          }
        }else {
          //new channel being added to user lib
          this.set('error', false);
          this.addNewChannel(query);
          return this.set('message', this.messages.addingNew);
        }
      }

    },
    /**
    Clear out messages block

    @method closeMessage
    */
    closeMessage: function() {
      this.set('message', null);
    },
    /**
    Adds existing lib channel to dashtboard

    @method addChannel
    */
    addChannel: function(channel) {
      this.set('error', false);
      this.get('controllers.dashtboard').toggleChannel(channel, true);
      this.set('message', this.messages.successInstalled);
    },
    /**
    Toggles filtering open and closed

    @method toggleFilters
    */
    toggleFilters: function() {
      this.toggleProperty('filtering');
    }
  }
});
<|endoftext|>


<commit_before>function Levenshtein(options) {
    options = typeof options === 'object' ? options : {};

    if (!(this instanceof Levenshtein)) {
        return new Levenshtein(options);
    }

    this.MAX_LEN = options.maxLength || 50;
    this.caseSensitive = !!options.caseSensitive;
    this.m = [];
    this._init();
    return this;
}

Levenshtein.prototype = {
    MATCH: 0,
    INSERT: 1,
    DELETE: 2,

    _init: function() {
        var i, j;
        var m = this.m;
        m.length = this.MAX_LEN+1;

        for (i = 0; i < m.length; ++i) {
            if (!Array.isArray(m[i])) {
                m[i] = [];
                for (j = 0; j < m.length; ++j) {
                    m[i][j] = {cost: 0, parent: 0};
                }
            }
            this._initFirstRow(i);
            this._initFirstColumn(i);
        }
    },

    _initFirstRow: function(i) {
        var m = this.m;
        m[0][i].cost = i;
        if (i > 0) m[0][i].parent = this.INSERT;
        else m[0][i].parent = -1;
    },

    _initFirstColumn: function(i) {
        var m = this.m;
        m[i][0].cost = i;
        if (i > 0) m[i][0].parent = this.DELETE;
        else m[i][0].parent = -1;
    },

    _matchType: function(i, j) {
        if (this.s1[i] === this.s2[j]) return 'M';
        else return 'S';
    },

    _reconstructPath: function(i, j) {
        var self = this;
        var m = this.m;
        var path = [];
        var s1 = this.s1;
        var s2 = this.s2;
        var MATCH = this.MATCH;
        var INSERT = this.INSERT;
        var DELETE = this.DELETE;

        function reconstruct(i, j) {
            if (m[i][j].parent === -1) return;

            var matchtype = self._matchType(i, j);

            if (m[i][j].parent === MATCH) {
                reconstruct(i-1, j-1);
                path.push({
                    i: i,
                    j: j,
                    op: matchtype,
                    letter: matchtype === 'M' ? s2[j] : s1[i] + ' => ' + s2[j],
                });
                return;
            }
            if (m[i][j].parent === INSERT) {
                reconstruct(i, j-1);
                path.push({
                    i: i,
                    j: j,
                    op: 'I',
                    letter: s2[j],
                });
                return;
            }
            if (m[i][j].parent === DELETE) {
                reconstruct(i-1, j);
                path.push({
                    i: i,
                    j: j,
                    op: 'D',
                    letter: s1[i],
                });
                return;
            }
        }

        reconstruct(i, j);
        return path;
    },

    _levenshtein: function() {
        var opt = [];
        var s1 = this.s1;
        var s2 = this.s2;
        var MATCH = this.MATCH;
        var INSERT = this.INSERT;
        var DELETE = this.DELETE;
        var m = this.m;
        var i, j, k;

        if (s1.length <= 1) return s2.length-1;
        if (s2.length <= 1) return s1.length-1;

        for (i = 1; i < s1.length; ++i) {
            for (j = 1; j < s2.length; ++j) {
                opt[MATCH]  = m[i-1][j-1].cost + this.matchCost(s1[i], s2[j]);
                opt[INSERT] = m[i][j-1].cost + this.insertCost(s2[j]);
                opt[DELETE] = m[i-1][j].cost + this.deleteCost(s1[i]);

                m[i][j].cost = opt[MATCH];
                m[i][j].parent = MATCH;

                for (k = INSERT; k <= DELETE; ++k) {
                    if (opt[k] < m[i][j].cost) {
                        m[i][j].cost = opt[k];
                        m[i][j].parent = k;
                    }
                }
            }
        }

        // goal_cell(s, t, i, j);
        return m[i-1][j-1].cost;
    },

    // override to provide a different cost for mis/matching
    // receives characters to be compared
    matchCost: function(c1, c2) {
        if (c1 === c2) return 0;
        return 1;
    },

    // override to provide a different cost for insertion
    // receives character to be inserted
    insertCost: function(c) {
        return 1;
    },

    // override to provide a different cost for deletion
    // receives character to be deleted
    deleteCost: function(c) {
        return 1;
    },

    process: function(s1, s2) {
        var len1, len2, numSteps, path;

        // strings are prepended with a space to make
        // the code for indexing the matrix easier to read
        s1 = ' ' + s1.slice(0, this.MAX_LEN);
        s2 = ' ' + s2.slice(0, this.MAX_LEN);

        if (!this.caseSensitive) {
            s1 = s1.toLowerCase();
            s2 = s2.toLowerCase();
        }

        this.s1 = s1;
        this.s2 = s2;

        len1 = s1.length-1;
        len2 = s2.length-1;

        numSteps = this._levenshtein();
        path = this._reconstructPath(len1, len2);

        // aid user inspection of the processed strings
        // by removing the prepended space for each
        this.s1 = s1.slice(1);
        this.s2 = s2.slice(1);

        this.results = {
            path: path,
            numSteps: numSteps,
        };

        return this;
    },
};

export default Levenshtein;
<commit_msg>ES6ify Levenshtein<commit_after>const MATCH  = 0;
const INSERT = 1;
const DELETE = 2;

class Levenshtein {
    constructor(options) {
        options = typeof options === 'object' ? options : {};

        if (!(this instanceof Levenshtein)) {
            return new Levenshtein(options);
        }

        this.MAX_LEN = options.maxLength || 50;
        this.caseSensitive = !!options.caseSensitive;
        this.m = [];
        this._init();
        return this;
    }

    _init() {
        var i, j;
        var m = this.m;
        var MATRIX_LEN = this.MAX_LEN+1;

        for (i = 0; i < MATRIX_LEN; ++i) {
            if (!Array.isArray(m[i])) {
                m[i] = [];
                for (j = 0; j < MATRIX_LEN; ++j) {
                    m[i][j] = {cost: 0, parent: 0};
                }
            }
            this._initFirstRow(i);
            this._initFirstColumn(i);
        }
    }

    _initFirstRow(i) {
        var m = this.m;

        m[0][i].cost = i;

        if (i > 0) {
            m[0][i].parent = INSERT;
        }
        else {
            m[0][i].parent = -1;
        }
    }

    _initFirstColumn(i) {
        var m = this.m;

        m[i][0].cost = i;

        if (i > 0) {
            m[i][0].parent = DELETE;
        } else {
            m[i][0].parent = -1;
        }
    }

    _matchType(i, j) {
        if (this.s1[i] === this.s2[j]) {
            return 'M';
        } else {
            return 'S';
        }
    }

    _reconstructPath(i, j) {
        var self = this;
        var m = this.m;
        var path = [];
        var s1 = this.s1;
        var s2 = this.s2;

        function reconstruct(i, j) {
            if (m[i][j].parent === -1) return;

            var matchtype = self._matchType(i, j);

            if (m[i][j].parent === MATCH) {
                reconstruct(i-1, j-1);
                path.push({
                    i: i,
                    j: j,
                    op: matchtype,
                    letter: matchtype === 'M' ? s2[j] : s1[i] + ' => ' + s2[j],
                });
                return;
            }
            if (m[i][j].parent === INSERT) {
                reconstruct(i, j-1);
                path.push({
                    i: i,
                    j: j,
                    op: 'I',
                    letter: s2[j],
                });
                return;
            }
            if (m[i][j].parent === DELETE) {
                reconstruct(i-1, j);
                path.push({
                    i: i,
                    j: j,
                    op: 'D',
                    letter: s1[i],
                });
                return;
            }
        }

        reconstruct(i, j);
        return path;
    }

    _levenshtein() {
        var opt = [];
        var s1 = this.s1;
        var s2 = this.s2;
        var m = this.m;
        var i, j, k;

        if (s1.length <= 1) return s2.length-1;
        if (s2.length <= 1) return s1.length-1;

        for (i = 1; i < s1.length; ++i) {
            for (j = 1; j < s2.length; ++j) {
                opt[MATCH]  = m[i-1][j-1].cost + this.matchCost(s1[i], s2[j]);
                opt[INSERT] = m[i][j-1].cost + this.insertCost(s2[j]);
                opt[DELETE] = m[i-1][j].cost + this.deleteCost(s1[i]);

                m[i][j].cost = opt[MATCH];
                m[i][j].parent = MATCH;

                for (k = INSERT; k <= DELETE; ++k) {
                    if (opt[k] < m[i][j].cost) {
                        m[i][j].cost = opt[k];
                        m[i][j].parent = k;
                    }
                }
            }
        }

        // goal_cell(s, t, i, j);
        return m[i-1][j-1].cost;
    }

    // override to provide a different cost for mis/matching
    // receives characters to be compared
    matchCost(c1, c2) {
        if (c1 === c2) {
            return 0;
        }

        return 1;
    }

    // override to provide a different cost for insertion
    // receives character to be inserted
    insertCost(c) {
        return 1;
    }

    // override to provide a different cost for deletion
    // receives character to be deleted
    deleteCost(c) {
        return 1;
    }

    process(s1, s2) {
        var len1, len2, numSteps, path;

        // strings are prepended with a space to make
        // the code for indexing the matrix easier to read
        s1 = ' ' + s1.slice(0, this.MAX_LEN);
        s2 = ' ' + s2.slice(0, this.MAX_LEN);

        if (!this.caseSensitive) {
            s1 = s1.toLowerCase();
            s2 = s2.toLowerCase();
        }

        this.s1 = s1;
        this.s2 = s2;

        len1 = s1.length-1;
        len2 = s2.length-1;

        numSteps = this._levenshtein();
        path = this._reconstructPath(len1, len2);

        // aid user inspection of the processed strings
        // by removing the prepended space for each
        this.s1 = s1.slice(1);
        this.s2 = s2.slice(1);

        this.results = {
            path: path,
            numSteps: numSteps,
        };

        return this;
    }
}

export default Levenshtein;
<|endoftext|>


<commit_before>/*!
 * SWIF-OpenSTC
 * Copyright 2013-2014 Siclic <contact@siclic.fr>
 * Licensed under AGPL-3.0 (https://www.gnu.org/licenses/agpl.txt)
 */

define([
	'app',
	'headerView',
	'footerView',
	'loginView',
	'notFoundView',
	'placesListView',
	'claimersListView',
	'teamsListView',
	'servicesListView',
	'equipmentsListView',
	'claimersTypesListView',
	'aboutView',
	'usersListView'

], function(app, HeaderView, FooterView, LoginView, NotFoundView, PlacesListView, ClaimersListView, TeamsListView, ServicesListView, EquipmentsListView, ClaimersTypesListView, AboutView, UsersListView){

	'use strict';

	/******************************************
	* Application Router
	*/
	var router = Backbone.Router.extend({

		/******************************************
		* GENERIC FUNCTION
		*/

		/** Router Initialization
		*/
		initialize: function () {
			var self = this;

			// Create all the Routes of the app //
			_.each(app.routes, function(route){
				self.route(route.url, route.function);
			});

			// Header, Footer Initialize //
			app.views.headerView = new HeaderView();
			app.views.footerView = new FooterView();

			this.bind('route', this.routeChange);
		},

		/** Render a view by undelegating all Event
		*/
		render: function (view) {

			// Close the current view //
			if (this.currentView) {
				if (this.currentView.$el){
					this.currentView.undelegateEvents();
				}
				this.currentView.$el = (this.currentView.el instanceof $) ? this.currentView.el : $(this.currentView.el);
				this.currentView.el = this.currentView.$el[0];
			}

			// render the new view //
			view.render();

			//Set the current view //
			this.currentView = view;

			return this;
		},

		/** Check if the User is connect
		*/
		checkConnect: function () {
			if (localStorage.getItem('current_user') === null) {
				return false;
			}
			else {
				return true;
			}
		},

		/** Change the Title of the page
		*/
		setPageTitle: function(title){
			$(document).attr('title', title);
		},

		/** Set the City Wallpaper on the application body
		*/
		toggleCityWallpaper: function(){

			if(!_.isUndefined(app.config.medias.cityWallpaper) && app.config.medias.cityWallpaper !== ''){

				$('body').toggleClass('city-wallpaper');

				if($('body').hasClass('city-wallpaper')){
					$('body').css('backgroundImage','url('+app.config.medias.cityWallpaper+')');
				}
				else{
					$('body').css('backgroundImage','none');
				}
			}
		},

		/** Change the Title of the page
		*/
		routeChange: function(){
			app.views.headerView.render();
		},

		/** Load a RequireJS module
		*/
		loadModule: function (module) {
			require([module], function (module) {
				module();
			});
		},

		/** Set the context
		*/
		setContext: function(params){

			var returnParams = {};

			_.each(params, function(val, args){

				if(!_.isNull(val))  { returnParams[args] = val;}
			});

			return returnParams;
		},

		/******************************************
		* ROUTES FUNCTION
		*/

		/** Redirect to the home page
		*/
		homePageRedirect: function(){
			var self = this;

			if(!this.checkConnect()){
				this.navigate(app.routes.login.url, {trigger: true, replace: true});
			}
			else{

				// Redirect to the firt Menu page //

				var userMenus = app.current_user.getMenus();
				_.find(app.config.menus, function (moduleName, shortName){

					if(!_.isUndefined(userMenus[shortName])) {

						var module = _.first(userMenus[shortName].children);
						var modulePage = _.slugify(_.first(module.children).tag);
						var url = _.join('/', moduleName, _.slugify(modulePage));

						return self.navigate(url, {trigger: true, replace: true});
					}

				});

			}
		},

		/** Login View
		*/
		login: function(){
			//var current_token = app.current_user
			// Check if the user is connect //
			if(!this.checkConnect()){
				app.views.loginView = new LoginView({ model: app.current_user });
				this.render(app.views.loginView);
			}
			else{
				this.navigate(app.routes.home.url, {trigger: true, replace: true});
			}
		},

		/** Logout the user
		*/
		logout: function(){
			app.current_user.logout();
		},

		/** About the App
		*/
		about: function(){
			app.views.aboutView = new AboutView();
		},

		/** 404 Not Found
		*/
		notFound: function(){

			app.views.notFoundView = new NotFoundView();
			this.render(app.views.notFoundView);
		},

		/** Load The Interventions Module
		*/
		loadAppInterventions: function(){

			if(!require.defined('app-interventions')){
				this.loadModule('app-interventions');
			}
			else{
				this.notFound();
			}
		},

		/** Load The Bookings Module
		*/
		loadAppReservations: function(){

			if(!require.defined('app-reservations')){
				this.loadModule('app-reservations');
			}
			else{
				this.notFound();
			}
		},

		/** Places List
		*/
		places: function(search, filter, sort, page){

			var params = this.setContext({ search: search, filter: filter, sort: sort, page: page });

			app.views.placesListView = new PlacesListView(params);
		},

		/** Services management
		*/
		services: function(search, sort, page){

			var params = this.setContext({search: search, sort: sort, page: page});

			app.views.servicesListView = new ServicesListView(params);
		},

		/** Services management
		*/
		users: function(search, sort, page){

			var params = this.setContext({search: search, sort: sort, page: page});

			app.views.usersListView = new UsersListView(params);
		},

		/** Teams List
		*/
		teams: function(id, search, sort, page){

			var params = this.setContext({id: id, search: search, sort : sort, page : page});

			app.views.teamsListView = new TeamsListView(params);
		},

		/** Claimers List
		*/
		claimers: function(search, sort, page){
			var params = this.setContext({search: search, sort : sort, page : page});
			app.views.claimersListView = new ClaimersListView(params);
		},

		/** Claimer Type List
		*/
		claimerTypes: function(search, sort, page){
			var params = this.setContext({search: search, sort : sort, page : page});
			app.views.claimersTypesListView = new ClaimersTypesListView(params);
		},

		/** Equipments List
		*/

		equipments: function(search, filter, sort, page){

			var params = this.setContext({search: search, filter: filter, sort : sort, page : page});

			app.views.equipmentsListView = new EquipmentsListView(params);
		}

	});

	return router;

});<commit_msg>refs #2501:Evènements de la précédente vue toujours actifs lors d'un changement d'app (router)<commit_after>/*!
 * SWIF-OpenSTC
 * Copyright 2013-2014 Siclic <contact@siclic.fr>
 * Licensed under AGPL-3.0 (https://www.gnu.org/licenses/agpl.txt)
 */

define([
	'app',
	'headerView',
	'footerView',
	'loginView',
	'notFoundView',
	'placesListView',
	'claimersListView',
	'teamsListView',
	'servicesListView',
	'equipmentsListView',
	'claimersTypesListView',
	'aboutView',
	'usersListView'

], function(app, HeaderView, FooterView, LoginView, NotFoundView, PlacesListView, ClaimersListView, TeamsListView, ServicesListView, EquipmentsListView, ClaimersTypesListView, AboutView, UsersListView){

	'use strict';

	/******************************************
	* Application Router
	*/
	var router = Backbone.Router.extend({

		/******************************************
		* GENERIC FUNCTION
		*/

		/** Router Initialization
		*/
		initialize: function () {
			var self = this;

			// Create all the Routes of the app //
			_.each(app.routes, function(route){
				self.route(route.url, route.function);
			});

			// Header, Footer Initialize //
			app.views.headerView = new HeaderView();
			app.views.footerView = new FooterView();

			this.bind('route', this.routeChange);
		},

		closeCurrentView: function() {
			if (this.currentView) {
				if (this.currentView) {
					if (this.currentView.$el){
						this.currentView.undelegateEvents();
					}
					this.currentView.$el = (this.currentView.el instanceof $) ? this.currentView.el : $(this.currentView.el);
					this.currentView.el = this.currentView.$el[0];
				}

				if (this.currentView.$el){
					this.currentView.undelegateEvents();
				}
				this.currentView.$el = (this.currentView.el instanceof $) ? this.currentView.el : $(this.currentView.el);
				this.currentView.el = this.currentView.$el[0];
			}
		},

		/** Render a view by undelegating all Event
		*/
		render: function (view) {

			// Close the current view //
			this.closeCurrentView();

			// render the new view //
			view.render();

			//Set the current view //
			this.currentView = view;

			return this;
		},

		/** Check if the User is connect
		*/
		checkConnect: function () {
			if (localStorage.getItem('current_user') === null) {
				return false;
			}
			else {
				return true;
			}
		},

		/** Change the Title of the page
		*/
		setPageTitle: function(title){
			$(document).attr('title', title);
		},

		/** Set the City Wallpaper on the application body
		*/
		toggleCityWallpaper: function(){

			if(!_.isUndefined(app.config.medias.cityWallpaper) && app.config.medias.cityWallpaper !== ''){

				$('body').toggleClass('city-wallpaper');

				if($('body').hasClass('city-wallpaper')){
					$('body').css('backgroundImage','url('+app.config.medias.cityWallpaper+')');
				}
				else{
					$('body').css('backgroundImage','none');
				}
			}
		},

		/** Change the Title of the page
		*/
		routeChange: function(){
			app.views.headerView.render();
		},

		/** Load a RequireJS module
		*/
		loadModule: function (module) {
			require([module], function (module) {
				module();
			});
		},

		/** Set the context
		*/
		setContext: function(params){

			var returnParams = {};

			_.each(params, function(val, args){

				if(!_.isNull(val))  { returnParams[args] = val;}
			});

			return returnParams;
		},

		/******************************************
		* ROUTES FUNCTION
		*/

		/** Redirect to the home page
		*/
		homePageRedirect: function(){
			var self = this;

			if(!this.checkConnect()){
				this.navigate(app.routes.login.url, {trigger: true, replace: true});
			}
			else{

				// Redirect to the firt Menu page //

				var userMenus = app.current_user.getMenus();
				_.find(app.config.menus, function (moduleName, shortName){

					if(!_.isUndefined(userMenus[shortName])) {

						var module = _.first(userMenus[shortName].children);
						var modulePage = _.slugify(_.first(module.children).tag);
						var url = _.join('/', moduleName, _.slugify(modulePage));

						return self.navigate(url, {trigger: true, replace: true});
					}

				});

			}
		},

		/** Login View
		*/
		login: function(){
			//var current_token = app.current_user
			// Check if the user is connect //
			if(!this.checkConnect()){
				app.views.loginView = new LoginView({ model: app.current_user });
				this.render(app.views.loginView);
			}
			else{
				this.navigate(app.routes.home.url, {trigger: true, replace: true});
			}
		},

		/** Logout the user
		*/
		logout: function(){
			app.current_user.logout();
		},

		/** About the App
		*/
		about: function(){
			app.views.aboutView = new AboutView();
		},

		/** 404 Not Found
		*/
		notFound: function(){

			app.views.notFoundView = new NotFoundView();
			this.render(app.views.notFoundView);
		},

		/** Load The Interventions Module
		*/
		loadAppInterventions: function(){
			this.closeCurrentView();
			if(!require.defined('app-interventions')){
				this.loadModule('app-interventions');
			}
			else{
				this.notFound();
			}
		},

		/** Load The Bookings Module
		*/
		loadAppReservations: function(){
			this.closeCurrentView();
			if(!require.defined('app-reservations')){
				this.loadModule('app-reservations');
			}
			else{
				this.notFound();
			}
		},

		/** Places List
		*/
		places: function(search, filter, sort, page){

			var params = this.setContext({ search: search, filter: filter, sort: sort, page: page });

			app.views.placesListView = new PlacesListView(params);
		},

		/** Services management
		*/
		services: function(search, sort, page){

			var params = this.setContext({search: search, sort: sort, page: page});

			app.views.servicesListView = new ServicesListView(params);
		},

		/** Services management
		*/
		users: function(search, sort, page){

			var params = this.setContext({search: search, sort: sort, page: page});

			app.views.usersListView = new UsersListView(params);
		},

		/** Teams List
		*/
		teams: function(id, search, sort, page){

			var params = this.setContext({id: id, search: search, sort : sort, page : page});

			app.views.teamsListView = new TeamsListView(params);
		},

		/** Claimers List
		*/
		claimers: function(search, sort, page){
			var params = this.setContext({search: search, sort : sort, page : page});
			app.views.claimersListView = new ClaimersListView(params);
		},

		/** Claimer Type List
		*/
		claimerTypes: function(search, sort, page){
			var params = this.setContext({search: search, sort : sort, page : page});
			app.views.claimersTypesListView = new ClaimersTypesListView(params);
		},

		/** Equipments List
		*/

		equipments: function(search, filter, sort, page){

			var params = this.setContext({search: search, filter: filter, sort : sort, page : page});

			app.views.equipmentsListView = new EquipmentsListView(params);
		}

	});

	return router;

});<|endoftext|>


<commit_before>app.controller('SettingsCtrl', function(VehicleSrv, $scope, $state, $ionicModal, localStorageService, $interval, $timeout, blePerpheralsService) {

  /****************************
   * VARIABLES
   ***************************/

  $scope.localVehicles = getItem("vehicles");

  var seconds = 5;
  var tempArray = [];
  var timeReduction;

  $scope.scanning = false;
  $scope.isScanBtnDisabled = false;

  /****************************
   * BLUETOOTH
   ***************************/

  /*
   * Description: Add discovered device to tempArray
   */
  var onDiscoverDevice = function(device) {
    if(device.name.indexOf("spark") == 0) {
      var similar = false;
      for(var i=0; i<tempArray.length; i++) {
        if(tempArray[i].id == device.id) {
          similar = true;
          break;
        }
      }
      if(!similar && device.name != null) {
        device.name = device.name.substring(5, device.name.length);
        tempArray.push(device);
      }
    }
  };

  /*
   * Description: Set device list and make visible
   */
  var setDeviceList = function(){
    $scope.scanText = "Scan Complete";
    $interval.cancel(timeReduction)
    $scope.blePeripherals = tempArray;
    $scope.scanning = false;
    $scope.isScanBtnDisabled = false;
  }

  /*
   * Description: Scan for all BLE peripheral devices or for devices with a specific service UUID
   */
  $scope.scan = function() {

    // Initialize variables
    $scope.isScanBtnDisabled = true;
    seconds = 5;
    tempArray = [];
    $scope.scanText = "Scanning (5s left)";
    $scope.scanning = true;

    // Scan withouth specifications
    ble.scan([], seconds, onDiscoverDevice, blePerpheralsService.onError);

    // Prepare variables
    var secondsLeft = seconds;

    // Set interval to visibly display the remaining seconds
    var timeReduction = $interval(function() {
      secondsLeft = secondsLeft - 1;
      if(secondsLeft > 0) {
        $scope.scanText = "Scanning (" + secondsLeft + "s left)";
      }
    }, 1000)

    // Set Timeout for applying device info to cards
    $timeout(setDeviceList, (seconds*1000));
  }

  // Select beacon
  $scope.selectBeacon = function(name) {
    $scope.beacon = name;
  }

  /****************************
   * VEHICLE DATA
   ***************************/

  /*
   * Description : Call get makes function from Vehicle service
   */
  VehicleSrv.getMakes().then(
    function(ret) {
      $scope.makes = ret.Makes;
    }
  );

  $scope.inputMakeChange = function(newValue, oldValue) {
    $scope.models = $scope.trims = $scope.infos = null;
    if(newValue != "" && newValue != null) {
      VehicleSrv.getModels(newValue).then(
        function(ret) {
          $scope.models = ret.Models;
        }
      );
    }
  };

  $scope.inputModelChange = function(maker, newValue, oldValue) {
    $scope.trims = $scope.infos = null;
    if(newValue != "" && newValue != null) {
      VehicleSrv.getTrims(maker, newValue).then(
        function(ret) {
          $scope.trims = ret.Trims;
        }
      );
    }
  };

  $scope.inputTrimChange = function(newValue, oldValue) {
    $scope.infos = null;
    if(newValue != "" && newValue != null) {
      VehicleSrv.getInfos(newValue).then(
        function(ret) {
          $scope.infos = ret[0];
        }
      );
    }
  };

  /****************************
   * VALIDATION PROCESS
   ***************************/

  $scope.validationData = function(make, model, id, beacon) {
    // Local storage
    if(localStorageService.isSupported) {
      // Create vehicle
      var vehicle = {
        id:id,
        make:make,
        model:model,
        beacon:beacon
      };

      // Retrieve and update list of vehicles
      var vehicles = getItem("vehicles");
      if(vehicles == null || vehicles == undefined) {
        vehicles = [];
      }
      vehicles.push(vehicle);
      submit("vehicles",vehicles);

      // Update view
      $scope.localVehicles = getItem("vehicles");
      $scope.closeVehicle();
    }
  }

  /**********************************
   * [MODAL] VEHICLE
   *********************************/

  $ionicModal.fromTemplateUrl('templates/settings/vehicle.html', {
    scope: $scope,
    animation: 'slide-in-up'
  }).then(function(modal) {
    $scope.modalVehicle = modal;
  });

  $scope.openVehicle = function() {
    $scope.modalVehicle.show();
  };

  $scope.closeVehicle = function() {
    $scope.modalVehicle.hide();
  };

  /****************************
   * UTILS
   ***************************/

  $scope.changeState = function(location) {
    $state.go(location);
  };

  function submit(key, val) {
   return localStorageService.set(key, val);
  }

  function getItem(key) {
   return localStorageService.get(key);
  }
});
<commit_msg>Added comments for Settings controller<commit_after>app.controller('SettingsCtrl', function(VehicleSrv, $scope, $state, $ionicModal, localStorageService, $interval, $timeout, blePerpheralsService) {

  /****************************
   * VARIABLES
   ***************************/

  $scope.localVehicles = getItem("vehicles");

  var seconds = 5;      // Scan delay
  var tempArray = [];   // Temporary array used to store beacons during the scanning process
  var timeReduction;    // Interval for manage the time during the scanning process

  $scope.scanning = false;            // Boolean representing the scanning status
  $scope.isScanBtnDisabled = false;   // Boolean representing the status of the button in the UI

  /****************************
   * BLUETOOTH
   ***************************/

  /*
   * Description: Add discovered device to tempArray
   */
  var onDiscoverDevice = function(device) {
    if(device.name.indexOf("spark") == 0) {
      var similar = false;
      for(var i=0; i<tempArray.length; i++) {
        if(tempArray[i].id == device.id) {
          similar = true;
          break;
        }
      }
      if(!similar && device.name != null) {
        device.name = device.name.substring(5, device.name.length);
        tempArray.push(device);
      }
    }
  };

  /*
   * Description: Set device list and make visible
   */
  var setDeviceList = function(){
    $scope.scanText = "Scan Complete";
    $interval.cancel(timeReduction)
    $scope.blePeripherals = tempArray;
    $scope.scanning = false;
    $scope.isScanBtnDisabled = false;
  }

  /*
   * Description: Scan for all BLE peripheral devices or for devices with a specific service UUID
   */
  $scope.scan = function() {

    // Initialize variables
    $scope.isScanBtnDisabled = true;
    seconds = 5;
    tempArray = [];
    $scope.scanText = "Scanning (5s left)";
    $scope.scanning = true;

    // Scan withouth specifications
    ble.scan([], seconds, onDiscoverDevice, blePerpheralsService.onError);

    // Prepare variables
    var secondsLeft = seconds;

    // Set interval to visibly display the remaining seconds
    var timeReduction = $interval(function() {
      secondsLeft = secondsLeft - 1;
      if(secondsLeft > 0) {
        $scope.scanText = "Scanning (" + secondsLeft + "s left)";
      }
    }, 1000)

    // Set Timeout for applying device info to cards
    $timeout(setDeviceList, (seconds*1000));
  }

  // Select beacon
  $scope.selectBeacon = function(name) {
    $scope.beacon = name;
  }

  /****************************
   * VEHICLE DATA
   ***************************/

  /*
   * Description : Call get makes function from Vehicle service
   */
  VehicleSrv.getMakes().then(
    function(ret) {
      $scope.makes = ret.Makes;
    }
  );

  /*
   * Description : Save new maker value and call models assiociated
   */
  $scope.inputMakeChange = function(newValue, oldValue) {
    $scope.models = $scope.trims = $scope.infos = null;
    if(newValue != "" && newValue != null) {
      VehicleSrv.getModels(newValue).then(
        function(ret) {
          $scope.models = ret.Models;
        }
      );
    }
  };

  /*
   * Description : Save new model value and call trims assiociated
   */
  $scope.inputModelChange = function(maker, newValue, oldValue) {
    $scope.trims = $scope.infos = null;
    if(newValue != "" && newValue != null) {
      VehicleSrv.getTrims(maker, newValue).then(
        function(ret) {
          $scope.trims = ret.Trims;
        }
      );
    }
  };

  /*
   * Description : Save new trims value and get info assiocated
   */
  $scope.inputTrimChange = function(newValue, oldValue) {
    $scope.infos = null;
    if(newValue != "" && newValue != null) {
      VehicleSrv.getInfos(newValue).then(
        function(ret) {
          $scope.infos = ret[0];
        }
      );
    }
  };

  /****************************
   * VALIDATION PROCESS
   ***************************/

  /*
   * Description : Save the data selected by the user on the local storage of the smartphone
   */
  $scope.validationData = function(make, model, id, beacon) {
    // Local storage
    if(localStorageService.isSupported) {
      // Create vehicle
      var vehicle = {
        id:id,
        make:make,
        model:model,
        beacon:beacon
      };

      // Retrieve and update list of vehicles
      var vehicles = getItem("vehicles");
      if(vehicles == null || vehicles == undefined) {
        vehicles = [];
      }
      vehicles.push(vehicle);
      submit("vehicles",vehicles);

      // Update view
      $scope.localVehicles = getItem("vehicles");
      $scope.closeVehicle();
    }
  }

  /**********************************
   * [MODAL] VEHICLE
   *********************************/

  /*
   * Description : Create a modal for add a new car
   */
  $ionicModal.fromTemplateUrl('templates/settings/vehicle.html', {
    scope: $scope,
    animation: 'slide-in-up'
  }).then(function(modal) {
    $scope.modalVehicle = modal;
  });

  /*
   * Description : Open the modal
   */
  $scope.openVehicle = function() {
    $scope.modalVehicle.show();
  };

  /*
   * Description : Close the modal
   */
  $scope.closeVehicle = function() {
    $scope.modalVehicle.hide();
  };

  /****************************
   * UTILS
   ***************************/

  /*
   * Description : Go to another state
   */
  $scope.changeState = function(location) {
    $state.go(location);
  };

  /*
   * Description : Save a value at a key on the local storage
   */
  function submit(key, val) {
   return localStorageService.set(key, val);
  }

  /*
   * Description : Get a value at a key on the local storage
   */
  function getItem(key) {
   return localStorageService.get(key);
  }
});
<|endoftext|>


<commit_before>'use strict';

var path = process.cwd();
const UserController = require(path + '/app/controllers/userController.server.js');
const PollController = require(path + '/app/controllers/pollController.server.js');

var _log = function(msg) {
    console.log("\t" + msg);
};

/** Function for debug logging request.*/
var reqDebug = function(req) {
	_log("Headers: " + JSON.stringify(req.headers));
	_log("Body: "    + JSON.stringify(req.body));
	_log("Params: "  + JSON.stringify(req.params));
	_log("Url:"      + JSON.stringify(req.url));
	_log("Text:"     + JSON.stringify(req.text));
	_log("Content:"  + JSON.stringify(req.content));
	_log("Query:"    + JSON.stringify(req.query));
};

module.exports = function (app, passport) {

    /** Renders a pug template.*/
    var renderPug = function(req, res, pugFile) {
        var isAuth = req.isAuthenticated();
        res.render(path + "/pug/" + pugFile, {isAuth: isAuth});
    };

    /** loggedIn func from clementine.js. */
	var isLoggedIn = function(req, res, next) {
		if (req.isAuthenticated()) {
			return next();
		} else {
			res.redirect('/login');
		}
	}

    var userController = new UserController();
	var pollController = new PollController(path);

	app.route('/')
		.get(function (req, res) {
            renderPug(req, res, "index.pug");
		});

	app.route('/signup')
		.get(function (req, res) {
            renderPug(req, res, "signup.pug");
		});

	app.route('/login')
		.get(function (req, res) {
            renderPug(req, res, "login.pug");
		});

	app.route('/loginFailed')
		.get(function (req, res) {
            res.render(path + "/pug/login.pug",
                {isAuth: false, loginFailed: true});
        });

    // If a user logs out, return to main page
	app.route('/logout')
		.get(function (req, res) {
			req.logout();
			res.redirect('/');
		});

    app.route('/create')
        .get(isLoggedIn, function(req, res) {
            renderPug(req, res, "create.pug");
        });

	app.route('/profile')
		.get(isLoggedIn, function (req, res) {
            renderPug(req, res, "profile.pug");
		});

    // Handle registration of user
    app.route('/forms/signup')
        .post(function(req, res) {
            console.log("Got a signup form GET request..");
			reqDebug(req);
            userController.addLocalUser(req, res);
        });

    //----------------------------------------------------
    // Routes for getting/creating/deleting/updating polls
    //----------------------------------------------------

    app.route('/polls')
        .get(function(req, res) {
            pollController.getPolls(req, res);
        });

    app.route('/polls/create')
        .post(isLoggedIn, function(req, res) {
            pollController.addPoll(req, res);
        });

    app.route('/polls/vote/:id')
        .post(function(req, res) {
            pollController.addVoteOnPoll(req, res);
        });

    app.route('/polls/update/:id')
        .post(function(req, res) {
            pollController.updatePollById(req, res);
        });
app.route('/polls/delete/:id')
        .get(function(req, res) {
            pollController.deletePollById(req, res);
        });

    app.route('/polls/getpolls/:id')
        .get(function(req, res) {
            pollController.getPollAsJSON(req, res);
        });

    app.route('/polls/:id')
        .get(function(req, res) {
            pollController.getPollById(req, res);
        });

    //--------------------------------------
    // User registration and authentication
    //--------------------------------------

	app.route('/api/:id')
		.get(function (req, res) {
            userController.getUser(req, res);
		});

    // Logs user in via form (after successful authentication
	app.route('/auth/userlogin')
        .post(passport.authenticate('local',
            { failureRedirect: '/loginFailed' }),
		function(req, res) {
			res.redirect('/');
		});

	app.route('/auth/github')
		.get(passport.authenticate('github'));

	app.route('/auth/github/callback')
		.get(passport.authenticate('github', {
			successRedirect: '/',
			failureRedirect: '/loginFailed'
		}));

};
<commit_msg>Cleaning up.<commit_after>'use strict';

var path = process.cwd();
const UserController = require(path + '/app/controllers/userController.server.js');
const PollController = require(path + '/app/controllers/pollController.server.js');

var _log = function(msg) {
    console.log("\t" + msg);
};

/** Function for debug logging request.*/
var reqDebug = function(req) {
	_log("Headers: " + JSON.stringify(req.headers));
	_log("Body: "    + JSON.stringify(req.body));
	_log("Params: "  + JSON.stringify(req.params));
	_log("Url:"      + JSON.stringify(req.url));
	_log("Text:"     + JSON.stringify(req.text));
	_log("Content:"  + JSON.stringify(req.content));
	_log("Query:"    + JSON.stringify(req.query));
};

module.exports = function (app, passport) {

    /** Renders a pug template.*/
    var renderPug = function(req, res, pugFile) {
        var isAuth = req.isAuthenticated();
        res.render(path + "/pug/" + pugFile, {isAuth: isAuth});
    };

    /** loggedIn func from clementine.js. */
	var isLoggedIn = function(req, res, next) {
		if (req.isAuthenticated()) {
			return next();
		} else {
			res.redirect('/login');
		}
	}

    var userController = new UserController(path);
	var pollController = new PollController(path);

	app.route('/')
		.get(function (req, res) {
            renderPug(req, res, "index.pug");
		});

	app.route('/signup')
		.get(function (req, res) {
            renderPug(req, res, "signup.pug");
		});

	app.route('/login')
		.get(function (req, res) {
            renderPug(req, res, "login.pug");
		});

	app.route('/loginFailed')
		.get(function (req, res) {
            res.render(path + "/pug/login.pug",
                {isAuth: false, loginFailed: true});
        });

    // If a user logs out, return to main page
	app.route('/logout')
		.get(function (req, res) {
			req.logout();
			res.redirect('/');
		});

    app.route('/create')
        .get(isLoggedIn, function(req, res) {
            renderPug(req, res, "create.pug");
        });

	app.route('/profile')
		.get(isLoggedIn, function (req, res) {
            renderPug(req, res, "profile.pug");
		});

    // Handle registration of user
    app.route('/forms/signup')
        .post(function(req, res) {
            console.log("Got a signup form GET request..");
			reqDebug(req);
            userController.addLocalUser(req, res);
        });

    //----------------------------------------------------
    // Routes for getting/creating/deleting/updating polls
    //----------------------------------------------------

    app.route('/polls')
        .get(function(req, res) {
            pollController.getPolls(req, res);
        });

    app.route('/polls/create')
        .post(isLoggedIn, function(req, res) {
            pollController.addPoll(req, res);
        });

    app.route('/polls/vote/:id')
        .post(function(req, res) {
            pollController.addVoteOnPoll(req, res);
        });

    app.route('/polls/update/:id')
        .post(function(req, res) {
            pollController.updatePollById(req, res);
        });

    app.route('/polls/delete/:id')
        .get(function(req, res) {
            pollController.deletePollById(req, res);
        });

    app.route('/polls/getpolls/:id')
        .get(function(req, res) {
            pollController.getPollAsJSON(req, res);
        });

    app.route('/polls/:id')
        .get(function(req, res) {
            pollController.getPollById(req, res);
        });

    //--------------------------------------
    // User registration and authentication
    //--------------------------------------

	app.route('/api/:id')
		.get(function (req, res) {
            userController.getUser(req, res);
		});

    // Logs user in via form (after successful authentication
	app.route('/auth/userlogin')
        .post(passport.authenticate('local',
            { failureRedirect: '/loginFailed' }),
		function(req, res) {
			res.redirect('/');
		});

	app.route('/auth/github')
		.get(passport.authenticate('github'));

	app.route('/auth/github/callback')
		.get(passport.authenticate('github', {
			successRedirect: '/',
			failureRedirect: '/loginFailed'
		}));

};
<|endoftext|>