<commit_before>var response_kakao = function (json) {
  var video_list = json.content;
    video_list.forEach(function(v, i) {
        var item = v;
        var shareLink = "http://vikicast.com/index.html?utm_source=kakaoLink&utm_medium=social#" + item.id;
        console.log(shareLink);
        // 카카오톡 링크 버튼을 생성합니다. 처음 한번만 호출하면 됩니다.
        Kakao.Link.sendTalkLink({
          label: item.description,
          image: {
            src: item.picture,
            width: item.width,
            height: item.height
          },
          webButton: {
            text: "vikicast x " + item.name,
            url: shareLink // 앱 설정의 웹 플랫폼에 등록한 도메인의 URL이어야 합니다.
          },
          fail: Materialize.toast('카카오톡 링크는 모바일 기기에서만 전송 가능합니다.', 4000, 'rounded')
          // webLink : {
          //   text: item.name,
          //   url: 'http://facebook.com/' + item.id
          // }
        });
        // Kakao.Link.cleanup();
        // 카카오 공유를 GA로 추적
        ga('send', 'event', "shareLink", "sendkakao", item.id );
      });
    }

    // 페이스북
    var windowHeight = 360;
    var windowWidth = 640;

    var response_facebook = function (json){
      var video_list = json.content;
        video_list.forEach(function(v, i) {
            var item = v;
            var REDIRECT_URL = "http://vikicast.com/index.html#" + item.id;
            console.log(REDIRECT_URL);

            // 페이스북 메타 데이터 변경
            $("meta[property=og\\:url]").attr("content", REDIRECT_URL);
            // $("meta[property=og\\:type]").attr("content", item.category);
            $("meta[property=og\\:title]").attr("content", "[VIKICAST x" + item.name + "]");
            $("meta[property=og\\:description]").attr("content", item.description);
            $("meta[property=og\\:image]").attr("content", item.picture);

            //  페이스북 로그인 시도
         FB.ui(
                {
                   method: 'share',
                   display: 'touch',
                   name: 'VIKICAST x ' + item.name,
                   picture: "http://vikicast.com/res/facebook/Untitled05.png",
                   caption: 'category: ' + item.category ,
                   description: item.description ,
                   message: '당신만 못 봤던 그 영상, 여기 다 있다!',
                   redirect_uri: "http://vikicast.com/responseSuccess.html",
                   link: REDIRECT_URL
                 },
                 function(response) {
                   if (response && response.post_id) {
                     console.log('Post was published.');
                     ga('send', 'event', "sendFBshareSuccess", item.id );

                   } else {
                     console.log('Post was not published.');
                     ga('send', 'event', "sendFBshareFail", item.id );

                   }
                 }
               );

            // var facebookUrl = "https://www.facebook.com/dialog/share?"+
            // "app_id=1463571523951964" +
            // "&display=touch" +
            // "&utm_source=facebookLink&utm_medium=social" +
            // // "&action_type=og.like"+
            // "$description=" + item.description +
            // "&pictire=" + item.pictire +
            // "&redirect_uri=http://vikicast.com/responseSuccess.html" +
            // // "&action_properties={'object':'"+ REDIRECT_URL +"'}"
            //
            // "&href=" + REDIRECT_URL;
            // // var $fbiFrame = "";
            //
            // // $('.modal-content').append($fbiFrame);
            // // $('#modal1').openModal();
            //
            // ga('send', 'event', "sendFBshareRequest", item.id );
            // window.open(facebookUrl, "_blank", "height=" + windowHeight + ",width=" + windowWidth);
          });
        }

var shareTwitter = function() {
  var twitterUrl = "https://twitter.com/intent/tweet?url=" + location.href;
  window.open(twitterUrl, "_blank", "height=" + windowHeight + ",width=" + windowWidth);
};
<commit_msg>오류 수정<commit_after>var response_kakao = function (json) {
  var video_list = json.content;
    video_list.forEach(function(v, i) {
        var item = v;
        var shareLink = "http://vikicast.com/index.html?utm_source=kakaoLink&utm_medium=social#" + item.id;
        console.log(shareLink);
        // 카카오톡 링크 버튼을 생성합니다. 처음 한번만 호출하면 됩니다.
        Kakao.Link.sendTalkLink({
          label: item.description,
          image: {
            src: item.picture,
            width: item.width,
            height: item.height
          },
          webButton: {
            text: "vikicast x " + item.name,
            url: shareLink // 앱 설정의 웹 플랫폼에 등록한 도메인의 URL이어야 합니다.
          },
          fail: Materialize.toast('카카오톡 링크는 모바일 기기에서만 전송 가능합니다.', 4000, 'rounded')
          // webLink : {
          //   text: item.name,
          //   url: 'http://facebook.com/' + item.id
          // }
        });
        // Kakao.Link.cleanup();
        // 카카오 공유를 GA로 추적
        ga('send', 'event', "shareLink", "sendkakao", item.id );
      });
    }

    // 페이스북
    var windowHeight = 360;
    var windowWidth = 640;

    var response_facebook = function (json){
      var video_list = json.content;
        video_list.forEach(function(v, i) {
            var item = v;
            var REDIRECT_URL = "http://vikicast.com/index.html#" + item.id;
            console.log(REDIRECT_URL);

            // 페이스북 메타 데이터 변경
            $("meta[property=og\\:url]").attr("content", REDIRECT_URL);
            // $("meta[property=og\\:type]").attr("content", item.category);
            $("meta[property=og\\:title]").attr("content", "[VIKICAST x" + item.name + "]");
            $("meta[property=og\\:description]").attr("content", item.description);
            $("meta[property=og\\:image]").attr("content", item.picture);

            //  페이스북 로그인 시도
         FB.ui(
                {
                   method: 'share',
                   display: 'touch',
                   name: 'VIKICAST x ' + item.name,
                   picture: "http://vikicast.com/res/facebook/Untitled05.png",
                   caption: 'category: ' + item.category ,
                   description: item.description ,
                   message: '당신만 못 봤던 그 영상, 여기 다 있다!',
                   redirect_uri: "http://vikicast.com/responseSuccess.html",
                   href: REDIRECT_URL
                 },
                 function(response) {
                   if (response && response.post_id) {
                     console.log('Post was published.');
                     ga('send', 'event', "sendFBshareSuccess", item.id );

                   } else {
                     console.log('Post was not published.');
                     ga('send', 'event', "sendFBshareFail", item.id );

                   }
                 }
               );

            // var facebookUrl = "https://www.facebook.com/dialog/share?"+
            // "app_id=1463571523951964" +
            // "&display=touch" +
            // "&utm_source=facebookLink&utm_medium=social" +
            // // "&action_type=og.like"+
            // "$description=" + item.description +
            // "&pictire=" + item.pictire +
            // "&redirect_uri=http://vikicast.com/responseSuccess.html" +
            // // "&action_properties={'object':'"+ REDIRECT_URL +"'}"
            //
            // "&href=" + REDIRECT_URL;
            // // var $fbiFrame = "";
            //
            // // $('.modal-content').append($fbiFrame);
            // // $('#modal1').openModal();
            //
            // ga('send', 'event', "sendFBshareRequest", item.id );
            // window.open(facebookUrl, "_blank", "height=" + windowHeight + ",width=" + windowWidth);
          });
        }

var shareTwitter = function() {
  var twitterUrl = "https://twitter.com/intent/tweet?url=" + location.href;
  window.open(twitterUrl, "_blank", "height=" + windowHeight + ",width=" + windowWidth);
};
<|endoftext|>


<commit_before>//calculate the sum  // PDC

var calculate = function(id) {
  var sum = 0;
  $('input[name^='+id+']').each(function() {
    sum += Number($(this).val());
  });
  return sum;
}

$('.calculate-me').on('click', function(event) {
  event.preventDefault();

  $('.hours').html(calculate('p'));
  $('.cost').html(calculate('c'));

  $("#conclusion").fadeIn( 300, "linear" );

});
<commit_msg>add clear value functionality<commit_after>//calculate the sum  // PDC

var calculate = function(id) {
  var sum = 0;
  $('input[name^='+id+']').each(function() {
    sum += Number($(this).val());
  });
  return sum;
}

$('.calculate-me').on('click', function(event) {
  event.preventDefault();

  $('.hours').html(calculate('p'));
  $('.cost').html(calculate('c'));

  $("#conclusion").fadeIn( 300, "linear" );

});

// Clear all input fields
$('.btn-ux-danger').on('click', function(event) {
	event.preventDefault();
	$('input[type="number"]').val('');
});
<|endoftext|>


<commit_before>/**
 * --------------------------------------------------------------------------
 * Figuration (v4.0.0-alpha.3): tab.js
 * Licensed under MIT (https://github.com/cast-org/figuration/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

(function($) {
    'use strict';

    var CFW_Widget_Tab = function(element, options) {
        this.$element = $(element);
        this.$target = null;
        this.$navElm = null;

        var parsedData = this.$element.CFW_parseData('tab', CFW_Widget_Tab.DEFAULTS);
        this.settings = $.extend({}, CFW_Widget_Tab.DEFAULTS, parsedData, options);

        this._init();
    };

    CFW_Widget_Tab.DEFAULTS = {
        target  : null,
        animate : true // If tabs should be allowed fade in and out
    };

    CFW_Widget_Tab.prototype = {
        _init : function() {
            var $selfRef = this;

            // Find nav and target elements
            this.$navElm = this.$element.closest('ul, ol, nav');
            this.$navElm.attr('role', 'tablist');

            var $selector = $(this.settings.target);
            if (!$selector.length) {
                $selector = $(this.$element.attr('href'));
            }
            this.$target = $($selector);

            if (!this.$target.length) { return; }

            this.$element.attr('data-cfw', 'tab');

            // Check for presence of trigger id - set if not present
            var triggerID = this.$element.CFW_getID('cfw-tab');

            // Target should have id already - set ARIA attributes
            this.$target.attr({
                'role': 'tabpanel',
                'aria-labelledby': triggerID
            });
            if (this.settings.animate) {
                this.animEnable();
            } else {
                this.animDisable();
            }

            // Set ARIA attributes on trigger
            this.$element.attr({
                'tabindex': -1,
                'role': 'tab',
                'aria-selected': 'false',
                'aria-controls': this.$target.attr('id')
            });

            // Bind click handler
            this.$element.on('click.cfw.tab', function(e) {
                e.preventDefault();
                $selfRef.show(e);
            });

            // Bind key handler
            this.$element.on('keydown.cfw.tab', function(e) {
                $selfRef._actionsKeydown(e, this);
            });

            // Display panel if trigger is marked active
            if (this.$element.hasClass('active')) {
                this.$element.attr({
                    'tabindex': 0,
                    'aria-selected': 'true'
                });
                this.$target.addClass('active');

                if (this.settings.animate) {
                    this.$target.addClass('in');
                }
            }

            // Check to see if there is an active element defined - if not set current one as active
            if (this.$navElm.find('.active').length <= 0) {
                this.$element.addClass('active');

                this.$element.attr({
                    'tabindex': 0,
                    'aria-selected': 'true'
                });
                this.$target.addClass('active');

                if (this.settings.animate) {
                    this.$target.addClass('in');
                }
            }

            this.$element.CFW_trigger('init.cfw.tab');
        },

        show : function(e) {
            if (e) {
                e.preventDefault();
            }

            var inTransition = this.$navElm.data('cfw.tab.inTransition');
            if (inTransition) { return; }

            if (this.$element.hasClass('active') ||
                this.$element.hasClass('disabled') ||
                this.$element[0].hasAttribute('disabled')) {
                return;
            }

            var $previous = this.$navElm.find('.active:last');
            var eventHideResult;
            var eventShowResult;

            if ($previous.length) {
                eventHideResult = $previous.CFW_trigger('beforeHide.cfw.tab', {
                    relatedTarget: this.$element[0]
                });
            }

            eventShowResult = this.$element.CFW_trigger('beforeShow.cfw.tab', {
                relatedTarget: $previous[0]
            });

            if (!eventHideResult || !eventShowResult) {
                return;
            }

            this.$navElm.data('cfw.tab.inTransition', true);

            if ($previous.length) {
                $previous
                    .attr({
                        'tabindex': -1,
                        'aria-selected': 'false'
                    })
                    .CFW_trigger('afterHide.cfw.tab', {
                        relatedTarget: this.$element[0]
                    });
            }

            this.$element.attr({
                'tabindex': 0,
                'aria-selected': 'true'
            });

            this._activateTab(this.$element, this.$navElm, false, $previous);
            this._activateTab(this.$target, this.$target.parent(), true, $previous);
        },

        animEnable : function() {
            this.$target.addClass('fade');
            if (this.$target.hasClass('active')) {
                this.$target.addClass('in');
            }
            this.settings.animate = true;
        },

        animDisable : function() {
            this.$target.removeClass('fade in');
            this.settings.animate = false;
        },

        _actionsKeydown : function(e, node) {
            var KEYCODE_UP = 38;    // Arrow up
            var KEYCODE_RIGHT = 39; // Arrow right
            var KEYCODE_DOWN = 40;  // Arrow down
            var KEYCODE_LEFT = 37;  // Arrow left
            var REGEX_KEYS = new RegExp('^(' + KEYCODE_UP + '|' + KEYCODE_RIGHT + '|' + KEYCODE_DOWN + '|' + KEYCODE_LEFT + ')$');

            if (!REGEX_KEYS.test(e.which)) { return; }

            e.stopPropagation();
            e.preventDefault();

            var $node = $(node);
            var $list = $node.closest('[role="tablist"]');
            var $items = $list.find('[role="tab"]:visible').not('.disabled');
            var index = $items.index($items.filter('[aria-selected="true"]'));

            if ((e.which === KEYCODE_UP || e.which === KEYCODE_LEFT) && index > 0) { index--; } // up & left
            if ((e.which === KEYCODE_RIGHT || e.which === KEYCODE_DOWN) && index < $items.length - 1) { index++; } // down & right
            /* eslint-disable-next-line no-bitwise */
            if (!~index) { index = 0; }   // force first item

            var nextTab = $items.eq(index);
            nextTab.CFW_Tab('show').trigger('focus');
        },

        _activateTab : function($node, container, isPanel, $previous) {
            var $selfRef = this;
            var $prevActive = container.find('.active');
            var doTransition = false;
            if (isPanel && this.settings.animate) {
                doTransition = true;
            }

            if (doTransition) {
                $.CFW_reflow($node[0]); // Reflow for transition
                $node.addClass('in');
            } else {
                if (isPanel) {
                    $selfRef.settings.animate = false;
                }
                $node.removeClass('fade');
            }

            var complete = function() {
                $prevActive.removeClass('active');
                $node.addClass('active');

                if (isPanel) {
                    $selfRef.$element.CFW_trigger('afterShow.cfw.tab', {
                        relatedTarget: $previous[0]
                    });
                    $node.CFW_mutateTrigger();
                    $prevActive.CFW_mutateTrigger();
                    $selfRef.$navElm.removeData('cfw.tab.inTransition');
                }
            };

            $node.CFW_transition(null, complete);

            $prevActive.removeClass('in');
        },

        dispose : function() {
            this.$element
                .off('.cfw.tab')
                .removeData('cfw.tab');

            this.$element = null;
            this.$target = null;
            this.$navElm = null;
            this.settings = null;
        }
    };

    var Plugin = function(option) {
        var args = [].splice.call(arguments, 1);
        return this.each(function() {
            var $this = $(this);
            var data = $this.data('cfw.tab');
            var options = typeof option === 'object' && option;

            if (!data) {
                $this.data('cfw.tab', data = new CFW_Widget_Tab(this, options));
            }
            if (typeof option === 'string') {
                data[option].apply(data, args);
            }
        });
    };

    $.fn.CFW_Tab = Plugin;
    $.fn.CFW_Tab.Constructor = CFW_Widget_Tab;
}(jQuery));
<commit_msg>V4 tab notransition (#342)<commit_after>/**
 * --------------------------------------------------------------------------
 * Figuration (v4.0.0-alpha.3): tab.js
 * Licensed under MIT (https://github.com/cast-org/figuration/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

(function($) {
    'use strict';

    var CFW_Widget_Tab = function(element, options) {
        this.$element = $(element);
        this.$target = null;
        this.$navElm = null;

        var parsedData = this.$element.CFW_parseData('tab', CFW_Widget_Tab.DEFAULTS);
        this.settings = $.extend({}, CFW_Widget_Tab.DEFAULTS, parsedData, options);

        this._init();
    };

    CFW_Widget_Tab.DEFAULTS = {
        target  : null,
        animate : true // If tabs should be allowed fade in and out
    };

    CFW_Widget_Tab.prototype = {
        _init : function() {
            var $selfRef = this;

            // Find nav and target elements
            this.$navElm = this.$element.closest('ul, ol, nav');
            this.$navElm.attr('role', 'tablist');

            var $selector = $(this.settings.target);
            if (!$selector.length) {
                $selector = $(this.$element.attr('href'));
            }
            this.$target = $($selector);

            if (!this.$target.length) { return; }

            this.$element.attr('data-cfw', 'tab');

            // Check for presence of trigger id - set if not present
            var triggerID = this.$element.CFW_getID('cfw-tab');

            // Target should have id already - set ARIA attributes
            this.$target.attr({
                'role': 'tabpanel',
                'aria-labelledby': triggerID
            });
            if (this.settings.animate) {
                this.animEnable();
            } else {
                this.animDisable();
            }

            // Set ARIA attributes on trigger
            this.$element.attr({
                'tabindex': -1,
                'role': 'tab',
                'aria-selected': 'false',
                'aria-controls': this.$target.attr('id')
            });

            // Bind click handler
            this.$element.on('click.cfw.tab', function(e) {
                e.preventDefault();
                $selfRef.show(e);
            });

            // Bind key handler
            this.$element.on('keydown.cfw.tab', function(e) {
                $selfRef._actionsKeydown(e, this);
            });

            // Display panel if trigger is marked active
            if (this.$element.hasClass('active')) {
                this.$element.attr({
                    'tabindex': 0,
                    'aria-selected': 'true'
                });
                this.$target.addClass('active');

                if (this.settings.animate) {
                    this.$target.addClass('in');
                }
            }

            // Check to see if there is an active element defined - if not set current one as active
            if (this.$navElm.find('.active').length <= 0) {
                this.$element.addClass('active');

                this.$element.attr({
                    'tabindex': 0,
                    'aria-selected': 'true'
                });
                this.$target.addClass('active');

                if (this.settings.animate) {
                    this.$target.addClass('in');
                }
            }

            this.$element.CFW_trigger('init.cfw.tab');
        },

        show : function(e) {
            if (e) {
                e.preventDefault();
            }

            if (this.$element.hasClass('active') ||
                this.$element.hasClass('disabled') ||
                this.$element[0].hasAttribute('disabled')) {
                return;
            }

            var $previous = this.$navElm.find('.active:last');
            var eventHideResult;
            var eventShowResult;

            if ($previous.length) {
                eventHideResult = $previous.CFW_trigger('beforeHide.cfw.tab', {
                    relatedTarget: this.$element[0]
                });
            }

            eventShowResult = this.$element.CFW_trigger('beforeShow.cfw.tab', {
                relatedTarget: $previous[0]
            });

            if (!eventHideResult || !eventShowResult) {
                return;
            }

            if ($previous.length) {
                $previous
                    .attr({
                        'tabindex': -1,
                        'aria-selected': 'false'
                    })
                    .CFW_trigger('afterHide.cfw.tab', {
                        relatedTarget: this.$element[0]
                    });
            }

            this.$element.attr({
                'tabindex': 0,
                'aria-selected': 'true'
            });

            this._activateTab(this.$element, this.$navElm, false, $previous);
            this._activateTab(this.$target, this.$target.parent(), true, $previous);
        },

        animEnable : function() {
            this.$target.addClass('fade');
            if (this.$target.hasClass('active')) {
                this.$target.addClass('in');
            }
            this.settings.animate = true;
        },

        animDisable : function() {
            this.$target.removeClass('fade in');
            this.settings.animate = false;
        },

        _actionsKeydown : function(e, node) {
            var KEYCODE_UP = 38;    // Arrow up
            var KEYCODE_RIGHT = 39; // Arrow right
            var KEYCODE_DOWN = 40;  // Arrow down
            var KEYCODE_LEFT = 37;  // Arrow left
            var REGEX_KEYS = new RegExp('^(' + KEYCODE_UP + '|' + KEYCODE_RIGHT + '|' + KEYCODE_DOWN + '|' + KEYCODE_LEFT + ')$');

            if (!REGEX_KEYS.test(e.which)) { return; }

            e.stopPropagation();
            e.preventDefault();

            var $node = $(node);
            var $list = $node.closest('[role="tablist"]');
            var $items = $list.find('[role="tab"]:visible').not('.disabled');
            var index = $items.index($items.filter('[aria-selected="true"]'));

            if ((e.which === KEYCODE_UP || e.which === KEYCODE_LEFT) && index > 0) { index--; } // up & left
            if ((e.which === KEYCODE_RIGHT || e.which === KEYCODE_DOWN) && index < $items.length - 1) { index++; } // down & right
            /* eslint-disable-next-line no-bitwise */
            if (!~index) { index = 0; }   // force first item

            var nextTab = $items.eq(index);
            nextTab.CFW_Tab('show').trigger('focus');
        },

        _activateTab : function($node, container, isPanel, $previous) {
            var $selfRef = this;
            var $prevActive = container.find('.active');
            var doTransition = false;
            if (isPanel && this.settings.animate) {
                doTransition = true;
            }

            if (doTransition) {
                $.CFW_reflow($node[0]); // Reflow for transition
                $node.addClass('in');
            } else {
                if (isPanel) {
                    $selfRef.settings.animate = false;
                }
                $node.removeClass('fade');
            }

            var complete = function() {
                $prevActive.removeClass('active');
                $node.addClass('active');

                if (isPanel) {
                    $selfRef.$element.CFW_trigger('afterShow.cfw.tab', {
                        relatedTarget: $previous[0]
                    });
                    $node.CFW_mutateTrigger();
                    $prevActive.CFW_mutateTrigger();
                }
            };

            $node.CFW_transition(null, complete);

            $prevActive.removeClass('in');
        },

        dispose : function() {
            this.$element
                .off('.cfw.tab')
                .removeData('cfw.tab');

            this.$element = null;
            this.$target = null;
            this.$navElm = null;
            this.settings = null;
        }
    };

    var Plugin = function(option) {
        var args = [].splice.call(arguments, 1);
        return this.each(function() {
            var $this = $(this);
            var data = $this.data('cfw.tab');
            var options = typeof option === 'object' && option;

            if (!data) {
                $this.data('cfw.tab', data = new CFW_Widget_Tab(this, options));
            }
            if (typeof option === 'string') {
                data[option].apply(data, args);
            }
        });
    };

    $.fn.CFW_Tab = Plugin;
    $.fn.CFW_Tab.Constructor = CFW_Widget_Tab;
}(jQuery));
<|endoftext|>


<commit_before>/* global chrome */

import { options } from "./options-storage.js";

// Session storage interface
// -----------------------------------------------------------------------------

const originWindowCache = {
  getOriginId(id) {
    return `popOrigin_${id}`;
  },

  has(tab) {
    return sessionStorage.hasOwnProperty(originWindowCache.getOriginId(tab.id));
  },

  set(tab, win) {
    sessionStorage[originWindowCache.getOriginId(tab.id)] = win.id;
  },

  get(tab) {
    return parseInt(sessionStorage[originWindowCache.getOriginId(tab.id)], 10);
  },

  remove(tab) {
    sessionStorage.removeItem(originWindowCache.getOriginId(tab.id));
  }
};

// Helper functions
// -----------------------------------------------------------------------------

function getSizeAndPos(winKey, displayBounds) {
  // Convert percentages to pixel values
  const values = {};
  ["left", "top", "width", "height"].forEach(propKey => {
    values[propKey] = options.getForWindow(winKey, propKey);
  });
  return {
    left:   Math.round(values.left   * displayBounds.width  + displayBounds.left),
    top:    Math.round(values.top    * displayBounds.height + displayBounds.top),
    width:  Math.round(values.width  * displayBounds.width),
    height: Math.round(values.height * displayBounds.height)
  };
}

function resizeOriginalWindow(originalWindow, displayBounds) {
  const vals = getSizeAndPos("original", displayBounds);
  return new Promise(resolve => {
    chrome.windows.update(originalWindow.id, {
      width:  vals.width,
      height: vals.height,
      left:   vals.left,
      top:    vals.top
    }, win => resolve(win));
  });
}

function getWindowBounds(win) {
  return { left: win.left, top: win.top, width: win.width, height: win.height };
}

function getNewWindowBounds(origWindow, displayBounds, cloneOriginal, clonePosition) {
  const bounds = { left: 0, top: 0, width: 0, height: 0 };

  // find the position that has the most space and return the position
  // and length to fill it.
  // e.g. when cloning horizontally and the window is left: 25% width: 25%
  // there is more space on the right side than the left, so use the right
  // pos is left/top opposite is right/bottom
  function getPosAndLength (winPos, winLength, displayPos, displayLength) {
    const normWinPos = winPos - displayPos;
    const oppositeEdge = normWinPos + winLength;
    const oppositeGap = displayLength - oppositeEdge;
    const useOppositeGap = normWinPos < oppositeGap;

    const pos = useOppositeGap
      ? displayPos + oppositeEdge
      : winPos - Math.min(winLength, normWinPos);

    const length = Math.min(winLength,
                            useOppositeGap ? oppositeGap : normWinPos);

    return { pos, length };
  }

  function getHorzPosAndLength() {
    return getPosAndLength(origWindow.left, origWindow.width,
                           displayBounds.left, displayBounds.width);
  }

  function getVertPosAndLength() {
    return getPosAndLength(origWindow.top, origWindow.height,
                           displayBounds.top, displayBounds.height);
  }

  if (cloneOriginal) {
    // copying all values covers the case of clone-position-same
    ["width", "height", "left", "top"].forEach(k => bounds[k] = origWindow[k]);

    if (clonePosition === "clone-position-horizontal") {
      const { pos, length } = getHorzPosAndLength();
      bounds.left = pos;
      bounds.width = length;
    }
    else if (clonePosition === "clone-position-vertical") {
      const { pos, length } = getVertPosAndLength();
      bounds.top = pos;
      bounds.height = length;
    }
  }
  else { // not cloning
    Object.entries(getSizeAndPos("new", displayBounds)).forEach(([k, v]) => {
      bounds[k] = v;
    });
  }

  // ensure all values are integers for Chrome APIs
  ["width", "height", "left", "top"].forEach(k => {
    bounds[k] = Math.round(bounds[k]);
  });

  return bounds;
}

function createNewWindow(tab, windowType, windowBounds, isFullscreen, focused) {
  // new window options
  const opts = {
    tabId: tab.id,
    type: windowType,
    focused,
    incognito: tab.incognito,
    state: isFullscreen ? "fullscreen" : "normal"
  };

  // shouldn't set width/height/left/top if fullscreen
  if (!isFullscreen) {
    Object.keys(windowBounds).forEach(key => opts[key] = windowBounds[key]);
  }

  // Move it to a new window
  return new Promise(resolve => {
    chrome.windows.create(opts, newWin => resolve([newWin, tab]));
  });
}

// Primary Functions
// -----------------------------------------------------------------------------

function tabToWindow(windowType) {
  const displaysPromise = new Promise(resolve => {
    chrome.system.display.getInfo(displays => resolve(displays));
  });

  const currentWindowPromise = new Promise(resolve => {
    chrome.windows.getCurrent({}, win => resolve(win));
  });

  const tabsPromise = new Promise(resolve => {
    chrome.tabs.query({
      currentWindow: true,
    }, tabs => {
      if (tabs.length > 0) { resolve(tabs); }
    });
  });

  const proms = [displaysPromise, currentWindowPromise, tabsPromise];

  Promise.all(proms).then(([displays, currentWindow, tabs]) => {
    const display = displays.find(display => {
      const displayLeft = display.bounds.left;
      const displayRight = displayLeft + display.bounds.width;
      const displayTop = display.bounds.top;
      const displayBottom = displayTop + display.bounds.height;
      const isLeftOfFirstDisplay = displayLeft === 0 && currentWindow.left < 0;

      return (currentWindow.left >= displayLeft || isLeftOfFirstDisplay) &&
              currentWindow.left <  displayRight &&
              currentWindow.top  >= displayTop &&
              currentWindow.top  <  displayBottom;
    });
    const isFullscreen = options.get("copyFullscreen") &&
                         currentWindow.state === "fullscreen";
    const isFocused = options.get("focus") === "new";

    const resizePromises = [];

    // (maybe) move and resize original window
    if (options.get("resizeOriginal") && !isFullscreen && tabs.length > 1) {
      resizePromises.push(resizeOriginalWindow(currentWindow, display.workArea));
    }

    // move and resize new window
    const bothMoved = Promise.all(resizePromises).then(([updatedWin]) => {
      const origWindow = updatedWin === undefined ? currentWindow : updatedWin;
      const activeTab = tabs.find(tab => tab.active);
      // if it's just one tab, the only use case is to convert it into a popup
      // window, so just leave it where it was
      const windowBounds = tabs.length === 1
        ? getWindowBounds(origWindow)
        : getNewWindowBounds(origWindow,
                             display.workArea,
                             options.get("cloneOriginal"),
                             options.get("clonePosition"));

      return createNewWindow(activeTab, windowType, windowBounds, isFullscreen,
                             isFocused);
    });

    // move highlighted tabs
    const othersMoved = bothMoved.then(([newWin, movedTab]) => {
      // save parent id in case we want to pop in
      originWindowCache.set(movedTab, currentWindow);

      // move other highlighted tabs
      const otherTabs = tabs.filter(tab => tab !== movedTab && tab.highlighted);
      if (otherTabs.length > 0) {
        if (windowType === "normal") {
          return new Promise(resolve => {
            // move all tabs at once
            chrome.tabs.move(otherTabs.map(tab => tab.id), {
              windowId: newWin.id,
              index: 1
            }, tabs => resolve(tabs));
          });
        }
        else if (windowType === "popup") {
          // can't move tabs to a popup window, so create individual ones
          const tabPromises = otherTabs.map(tab => {
            return createNewWindow(tab, windowType, getWindowBounds(newWin),
              isFullscreen, isFocused);
          });
          return Promise.all(tabPromises);
        }
      }
    });

    othersMoved.then(() => {
      // focus on original window if specified, and it still exists
      // (popping a single tab will destroy the original window)
      if (options.get("focus") === "original") {
        chrome.windows.get(currentWindow.id, {}, () => {
          if (!chrome.runtime.lastError) {
            chrome.windows.update(currentWindow.id, { focused: true });
          }
        });
      }
    });
  });
}

function windowToTab() {
  const getTabs = new Promise(resolve => {
    chrome.tabs.query({
      currentWindow: true,
      highlighted: true
    }, tabs => resolve(tabs));
  });

  const checkOriginalWindowsExist = getTabs.then(tabs => {
    const tabsWithWindow = tabs.filter(tab => originWindowCache.has(tab));
    const promises = tabsWithWindow.map(tab => {
      const originalWindowId = originWindowCache.get(tab);

      return new Promise(resolve => {
        chrome.windows.get(originalWindowId, {}, () => {
          if (!chrome.runtime.lastError) {  resolve([tab, originalWindowId]); }
        });
      });
    });

    return Promise.all(promises);
  });

  const moveTabs = checkOriginalWindowsExist.then(tabResults => {
    const movePromises = tabResults.map(([tab, windowId]) => {
      return new Promise(resolve => {
        chrome.tabs.move(tab.id, { windowId, index: -1 }, () => resolve(tab));
      });
    });

    return Promise.all(movePromises);
  });

  moveTabs.then(moveResults => {
    moveResults.forEach((tab) => {
      originWindowCache.remove(tab);
      if (tab.active) { chrome.tabs.update(tab.id, { active: true }); }
    });
  });
}

// Chrome Listeners
// -----------------------------------------------------------------------------

chrome.storage.onChanged.addListener(changes => {
  Object.entries(changes).forEach(([k, v]) => options.set(k, v.newValue));
});

chrome.commands.onCommand.addListener(command => {
       if (command === "tab-to-window-normal") { tabToWindow("normal"); }
  else if (command === "tab-to-window-popup")  { tabToWindow("popup"); }
  else if (command === "window-to-tab")        { windowToTab(); }
});

chrome.browserAction.onClicked.addListener(() => {
  tabToWindow(options.get("menuButtonType"));
});
<commit_msg>rename focused param to isFocused<commit_after>/* global chrome */

import { options } from "./options-storage.js";

// Session storage interface
// -----------------------------------------------------------------------------

const originWindowCache = {
  getOriginId(id) {
    return `popOrigin_${id}`;
  },

  has(tab) {
    return sessionStorage.hasOwnProperty(originWindowCache.getOriginId(tab.id));
  },

  set(tab, win) {
    sessionStorage[originWindowCache.getOriginId(tab.id)] = win.id;
  },

  get(tab) {
    return parseInt(sessionStorage[originWindowCache.getOriginId(tab.id)], 10);
  },

  remove(tab) {
    sessionStorage.removeItem(originWindowCache.getOriginId(tab.id));
  }
};

// Helper functions
// -----------------------------------------------------------------------------

function getSizeAndPos(winKey, displayBounds) {
  // Convert percentages to pixel values
  const values = {};
  ["left", "top", "width", "height"].forEach(propKey => {
    values[propKey] = options.getForWindow(winKey, propKey);
  });
  return {
    left:   Math.round(values.left   * displayBounds.width  + displayBounds.left),
    top:    Math.round(values.top    * displayBounds.height + displayBounds.top),
    width:  Math.round(values.width  * displayBounds.width),
    height: Math.round(values.height * displayBounds.height)
  };
}

function resizeOriginalWindow(originalWindow, displayBounds) {
  const vals = getSizeAndPos("original", displayBounds);
  return new Promise(resolve => {
    chrome.windows.update(originalWindow.id, {
      width:  vals.width,
      height: vals.height,
      left:   vals.left,
      top:    vals.top
    }, win => resolve(win));
  });
}

function getWindowBounds(win) {
  return { left: win.left, top: win.top, width: win.width, height: win.height };
}

function getNewWindowBounds(origWindow, displayBounds, cloneOriginal, clonePosition) {
  const bounds = { left: 0, top: 0, width: 0, height: 0 };

  // find the position that has the most space and return the position
  // and length to fill it.
  // e.g. when cloning horizontally and the window is left: 25% width: 25%
  // there is more space on the right side than the left, so use the right
  // pos is left/top opposite is right/bottom
  function getPosAndLength (winPos, winLength, displayPos, displayLength) {
    const normWinPos = winPos - displayPos;
    const oppositeEdge = normWinPos + winLength;
    const oppositeGap = displayLength - oppositeEdge;
    const useOppositeGap = normWinPos < oppositeGap;

    const pos = useOppositeGap
      ? displayPos + oppositeEdge
      : winPos - Math.min(winLength, normWinPos);

    const length = Math.min(winLength,
                            useOppositeGap ? oppositeGap : normWinPos);

    return { pos, length };
  }

  function getHorzPosAndLength() {
    return getPosAndLength(origWindow.left, origWindow.width,
                           displayBounds.left, displayBounds.width);
  }

  function getVertPosAndLength() {
    return getPosAndLength(origWindow.top, origWindow.height,
                           displayBounds.top, displayBounds.height);
  }

  if (cloneOriginal) {
    // copying all values covers the case of clone-position-same
    ["width", "height", "left", "top"].forEach(k => bounds[k] = origWindow[k]);

    if (clonePosition === "clone-position-horizontal") {
      const { pos, length } = getHorzPosAndLength();
      bounds.left = pos;
      bounds.width = length;
    }
    else if (clonePosition === "clone-position-vertical") {
      const { pos, length } = getVertPosAndLength();
      bounds.top = pos;
      bounds.height = length;
    }
  }
  else { // not cloning
    Object.entries(getSizeAndPos("new", displayBounds)).forEach(([k, v]) => {
      bounds[k] = v;
    });
  }

  // ensure all values are integers for Chrome APIs
  ["width", "height", "left", "top"].forEach(k => {
    bounds[k] = Math.round(bounds[k]);
  });

  return bounds;
}

function createNewWindow(tab, windowType, windowBounds, isFullscreen, isFocused) {
  // new window options
  const opts = {
    tabId:           tab.id,
    type:            windowType,
    focused:         isFocused,
    incognito:       tab.incognito,
    state:           isFullscreen ? "fullscreen" : "normal",
  };

  // shouldn't set width/height/left/top if fullscreen
  if (!isFullscreen) {
    Object.keys(windowBounds).forEach(key => opts[key] = windowBounds[key]);
  }

  // Move it to a new window
  return new Promise(resolve => {
    chrome.windows.create(opts, newWin => resolve([newWin, tab]));
  });
}

// Primary Functions
// -----------------------------------------------------------------------------

function tabToWindow(windowType) {
  const displaysPromise = new Promise(resolve => {
    chrome.system.display.getInfo(displays => resolve(displays));
  });

  const currentWindowPromise = new Promise(resolve => {
    chrome.windows.getCurrent({}, win => resolve(win));
  });

  const tabsPromise = new Promise(resolve => {
    chrome.tabs.query({
      currentWindow: true,
    }, tabs => {
      if (tabs.length > 0) { resolve(tabs); }
    });
  });

  const proms = [displaysPromise, currentWindowPromise, tabsPromise];

  Promise.all(proms).then(([displays, currentWindow, tabs]) => {
    const display = displays.find(display => {
      const displayLeft = display.bounds.left;
      const displayRight = displayLeft + display.bounds.width;
      const displayTop = display.bounds.top;
      const displayBottom = displayTop + display.bounds.height;
      const isLeftOfFirstDisplay = displayLeft === 0 && currentWindow.left < 0;

      return (currentWindow.left >= displayLeft || isLeftOfFirstDisplay) &&
              currentWindow.left <  displayRight &&
              currentWindow.top  >= displayTop &&
              currentWindow.top  <  displayBottom;
    });
    const isFullscreen = options.get("copyFullscreen") &&
                         currentWindow.state === "fullscreen";
    const isFocused = options.get("focus") === "new";

    const resizePromises = [];

    // (maybe) move and resize original window
    if (options.get("resizeOriginal") && !isFullscreen && tabs.length > 1) {
      resizePromises.push(resizeOriginalWindow(currentWindow, display.workArea));
    }

    // move and resize new window
    const bothMoved = Promise.all(resizePromises).then(([updatedWin]) => {
      const origWindow = updatedWin === undefined ? currentWindow : updatedWin;
      const activeTab = tabs.find(tab => tab.active);
      // if it's just one tab, the only use case is to convert it into a popup
      // window, so just leave it where it was
      const windowBounds = tabs.length === 1
        ? getWindowBounds(origWindow)
        : getNewWindowBounds(origWindow,
                             display.workArea,
                             options.get("cloneOriginal"),
                             options.get("clonePosition"));

      return createNewWindow(activeTab, windowType, windowBounds, isFullscreen,
                             isFocused);
    });

    // move highlighted tabs
    const othersMoved = bothMoved.then(([newWin, movedTab]) => {
      // save parent id in case we want to pop in
      originWindowCache.set(movedTab, currentWindow);

      // move other highlighted tabs
      const otherTabs = tabs.filter(tab => tab !== movedTab && tab.highlighted);
      if (otherTabs.length > 0) {
        if (windowType === "normal") {
          return new Promise(resolve => {
            // move all tabs at once
            chrome.tabs.move(otherTabs.map(tab => tab.id), {
              windowId: newWin.id,
              index: 1
            }, tabs => resolve(tabs));
          });
        }
        else if (windowType === "popup") {
          // can't move tabs to a popup window, so create individual ones
          const tabPromises = otherTabs.map(tab => {
            return createNewWindow(tab, windowType, getWindowBounds(newWin),
              isFullscreen, isFocused);
          });
          return Promise.all(tabPromises);
        }
      }
    });

    othersMoved.then(() => {
      // focus on original window if specified, and it still exists
      // (popping a single tab will destroy the original window)
      if (options.get("focus") === "original") {
        chrome.windows.get(currentWindow.id, {}, () => {
          if (!chrome.runtime.lastError) {
            chrome.windows.update(currentWindow.id, { focused: true });
          }
        });
      }
    });
  });
}

function windowToTab() {
  const getTabs = new Promise(resolve => {
    chrome.tabs.query({
      currentWindow: true,
      highlighted: true
    }, tabs => resolve(tabs));
  });

  const checkOriginalWindowsExist = getTabs.then(tabs => {
    const tabsWithWindow = tabs.filter(tab => originWindowCache.has(tab));
    const promises = tabsWithWindow.map(tab => {
      const originalWindowId = originWindowCache.get(tab);

      return new Promise(resolve => {
        chrome.windows.get(originalWindowId, {}, () => {
          if (!chrome.runtime.lastError) {  resolve([tab, originalWindowId]); }
        });
      });
    });

    return Promise.all(promises);
  });

  const moveTabs = checkOriginalWindowsExist.then(tabResults => {
    const movePromises = tabResults.map(([tab, windowId]) => {
      return new Promise(resolve => {
        chrome.tabs.move(tab.id, { windowId, index: -1 }, () => resolve(tab));
      });
    });

    return Promise.all(movePromises);
  });

  moveTabs.then(moveResults => {
    moveResults.forEach((tab) => {
      originWindowCache.remove(tab);
      if (tab.active) { chrome.tabs.update(tab.id, { active: true }); }
    });
  });
}

// Chrome Listeners
// -----------------------------------------------------------------------------

chrome.storage.onChanged.addListener(changes => {
  Object.entries(changes).forEach(([k, v]) => options.set(k, v.newValue));
});

chrome.commands.onCommand.addListener(command => {
       if (command === "tab-to-window-normal") { tabToWindow("normal"); }
  else if (command === "tab-to-window-popup")  { tabToWindow("popup"); }
  else if (command === "window-to-tab")        { windowToTab(); }
});

chrome.browserAction.onClicked.addListener(() => {
  tabToWindow(options.get("menuButtonType"));
});
<|endoftext|>


<commit_before>// Copyright © 2015 Bob W. Hogg. All Rights Reserved.
//
// This file is part of jsduck.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var Class = require("yajscf");
var which = require("which").sync;
var spawnSync = require("spawn-sync");
var _ = require("underscore");

/**
 * @class JSDuck
 * JSDuck is a JavaScript wrapper for [jsduck](https://github.com/senchalabs/jsduck)
 * It supports all the options the command-line version does
 */
module.exports = Class.extend(
{
    /**
     * @method constructor
     * Constructor.
     * @param {String[]} options Array of options to pass to the JSDuck gem
     * @throws
     * If we cannot find the jsduck binary
     */
    init: function(options)
    {
        /**
         * @property {String[]} options
         * Options for the jsduck gem
         * @private
         */
        this.options = options;

        // confirm jsduck exists
        /**
         * @property {String} binary
         * Path to the jsduck gem
         * @private
         */
        this.binary = which("jsduck");
    },

    /**
     * doc generates the documentation according to the specified options
     * @param {String[]} paths The list of paths to generate documentation for
     * @return {Object}
     * The result of the documentation request
     */
    doc: function(paths)
    {
        var result = spawnSync(this.binary, _.union(this.options, paths));
        if(result.error)
        {
            throw result.error;
        }
        return result;
    }
});
<commit_msg>fix whitespace<commit_after>// Copyright © 2015 Bob W. Hogg. All Rights Reserved.
//
// This file is part of jsduck.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var Class = require("yajscf");
var which = require("which").sync;
var spawnSync = require("spawn-sync");
var _ = require("underscore");

/**
 * @class JSDuck
 * JSDuck is a JavaScript wrapper for [jsduck](https://github.com/senchalabs/jsduck)
 * It supports all the options the command-line version does
 */
module.exports = Class.extend(
{
    /**
     * @method constructor
     * Constructor.
     * @param {String[]} options Array of options to pass to the JSDuck gem
     * @throws
     * If we cannot find the jsduck binary
     */
    init: function(options)
    {
        /**
         * @property {String[]} options
         * Options for the jsduck gem
         * @private
         */
        this.options = options;

        // confirm jsduck exists
        /**
         * @property {String} binary
         * Path to the jsduck gem
         * @private
         */
        this.binary = which("jsduck");
    },

    /**
     * doc generates the documentation according to the specified options
     * @param {String[]} paths The list of paths to generate documentation for
     * @return {Object}
     * The result of the documentation request
     */
    doc: function(paths)
    {
        var result = spawnSync(this.binary, _.union(this.options, paths));
        if(result.error)
        {
            throw result.error;
        }
        return result;
    }
});
<|endoftext|>


<commit_before>'use strict';

(function () {

  angular.module('mwFormValidators', [])

  /**
   * @ngdoc directive
   * @name mwFormValidators.directive:mwValidatePhone
   * @element input
   * @description
   *
   * Adds validation for phone numbers.
   * Valid Examples are: +491234567 or 00491234567
   *
   * Note: this directive requires `ngModel` to be present.
   *
   */
    .directive('mwValidatePhone', function () {
      return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elm, attr, ngModel) {
          var telExpression = /^(00[1-9]\d{8,}|^\+[1-9]\d{8,})$/;

          var removeNonDigitValues = function (value) {
            if (value) {
              value = value.replace(/[^0-9+]/g, '');
            }
            return value;
          };

          var validateNumber = function (value) {
            var isValid = true;
            if (value) {
              isValid = value.match(telExpression);
            }
            ngModel.$setValidity('phone', isValid);
            return value;
          };

          ngModel.$formatters.push(validateNumber, removeNonDigitValues);
          ngModel.$parsers.push(validateNumber);
        }
      };
    })

    .directive('mwValidateHex', function () {
      return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elm, attr, ngModel) {
          var hexExpression = /^(0x)?([0-9A-Fa-f])+$/;

          var validateHex = function (value) {
            var isValid = true;
            if (value) {
              isValid = value.match(hexExpression);
            }
            ngModel.$setValidity('hex', isValid);
            return value;
          };

          ngModel.$formatters.push(validateHex);
          ngModel.$parsers.push(validateHex);
        }
      };
    })

    .directive('mwValidateMatch', function () {
      return {
        require: 'ngModel',
        link: function (scope, elm, attr, ctrl) {
          var pwdWidget = elm.inheritedData('$formController')[attr.mwValidateMatch];

          ctrl.$parsers.push(function (value) {
            var isValid = false;
            if (value === pwdWidget.$viewValue) {
              isValid = true;
            }
            ctrl.$setValidity('match', isValid);
            return value;
          });

          pwdWidget.$parsers.push(function (value) {
            var isValid = false;
            if (value === ctrl.$viewValue) {
              isValid = true;
            }
            ctrl.$setValidity('match', isValid);
            return value;
          });
        }
      };
    })

  /**
   * @ngdoc directive
   * @name mwFormValidators.directive:mwValidateUniqueness
   * @element input
   * @description
   *
   * Adds validation of uniqueness for a given array of strings.
   *
   * @param {Array.<String>} mwValidateUniqueness Array of existing items to validate against
   *
   * Note: this directive requires `ngModel` to be present.
   *
   */
    .directive('mwValidateUniqueness', function () {
      return {
        require: 'ngModel',
        link: function (scope, elm, attr, ngModel) {
          var existingValues;

          scope.$watch(attr.mwValidateUniqueness, function (value) {
            existingValues = value;
          });

          /**
           * Add parser/formatter to model which checks if the model value is
           * a value that already exists and set validation state accordingly
           */
          var validateUniqueness = function (value) {
            var isValid = true;
            if (angular.isArray(existingValues) && existingValues.length > 0 && value && ngModel.$dirty) {
              isValid = (existingValues.indexOf(value) === -1);
            }
            ngModel.$setValidity('unique', isValid);
            return value;
          };
          ngModel.$parsers.unshift(validateUniqueness);
          ngModel.$formatters.unshift(validateUniqueness);
        }
      };
    })

  ;

})();<commit_msg>improved username and groupname validation by using a filter instead of loading all users and groups<commit_after>'use strict';

(function () {

  angular.module('mwFormValidators', [])

  /**
   * @ngdoc directive
   * @name mwFormValidators.directive:mwValidatePhone
   * @element input
   * @description
   *
   * Adds validation for phone numbers.
   * Valid Examples are: +491234567 or 00491234567
   *
   * Note: this directive requires `ngModel` to be present.
   *
   */
    .directive('mwValidatePhone', function () {
      return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elm, attr, ngModel) {
          var telExpression = /^(00[1-9]\d{8,}|^\+[1-9]\d{8,})$/;

          var removeNonDigitValues = function (value) {
            if (value) {
              value = value.replace(/[^0-9+]/g, '');
            }
            return value;
          };

          var validateNumber = function (value) {
            var isValid = true;
            if (value) {
              isValid = value.match(telExpression);
            }
            ngModel.$setValidity('phone', isValid);
            return value;
          };

          ngModel.$formatters.push(validateNumber, removeNonDigitValues);
          ngModel.$parsers.push(validateNumber);
        }
      };
    })

    .directive('mwValidateHex', function () {
      return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elm, attr, ngModel) {
          var hexExpression = /^(0x)?([0-9A-Fa-f])+$/;

          var validateHex = function (value) {
            var isValid = true;
            if (value) {
              isValid = value.match(hexExpression);
            }
            ngModel.$setValidity('hex', isValid);
            return value;
          };

          ngModel.$formatters.push(validateHex);
          ngModel.$parsers.push(validateHex);
        }
      };
    })

    .directive('mwValidateMatch', function () {
      return {
        require: 'ngModel',
        link: function (scope, elm, attr, ctrl) {
          var pwdWidget = elm.inheritedData('$formController')[attr.mwValidateMatch];

          ctrl.$parsers.push(function (value) {
            var isValid = false;
            if (value === pwdWidget.$viewValue) {
              isValid = true;
            }
            ctrl.$setValidity('match', isValid);
            return value;
          });

          pwdWidget.$parsers.push(function (value) {
            var isValid = false;
            if (value === ctrl.$viewValue) {
              isValid = true;
            }
            ctrl.$setValidity('match', isValid);
            return value;
          });
        }
      };
    })

  /**
   * @ngdoc directive
   * @name mwFormValidators.directive:mwValidateUniqueness
   * @element input
   * @description
   *
   * Adds validation of uniqueness for a given array of strings.
   *
   * @param {Array.<String>} mwValidateUniqueness Array of existing items to validate against
   *
   * Note: this directive requires `ngModel` to be present.
   *
   */
    .directive('mwValidateUniqueness', function () {
      return {
        require: 'ngModel',
        link: function (scope, elm, attr, ngModel) {
          var existingValues;

          scope.$watch(attr.mwValidateUniqueness, function (value) {
            existingValues = value;
          });

          /**
           * Add parser/formatter to model which checks if the model value is
           * a value that already exists and set validation state accordingly
           */
          var validateUniqueness = function (value) {
            var isValid = true;
            if (angular.isArray(existingValues) && existingValues.length > 0 && value && ngModel.$dirty) {
              isValid = (existingValues.indexOf(value) === -1);
            }
            ngModel.$setValidity('unique', isValid);
            return value;
          };
          ngModel.$parsers.unshift(validateUniqueness);
          ngModel.$formatters.unshift(validateUniqueness);
        }
      };
    });

})();<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint anon:true, sloppy:true, regexp:true, nomen:true, stupid:true*/

(function() {

    'use strict';

/**
 * @fileoverview Command logic for the Mojito 'jslint' command.
 */

//  ----------------------------------------------------------------------------
//  Prerequisites
//  ----------------------------------------------------------------------------

    var libfs = require('fs'),
        libpath = require('path'),
        existsSync = libfs.existsSync || libpath.existsSync,
        utils = require(libpath.join(__dirname, '../../management/utils'));

//  ----------------------------------------------------------------------------
//  Command Root Object
//  ----------------------------------------------------------------------------

    /**
     * The Command Line Interpreter object. All testable methods and attributes
     * are properties of this object.
     */
    function Command() {
    }

    /**
     * The usage string output for command help.
     * @type {string}
     */
    Command.usage = 'mojito jslint [app | mojit] [<path>] {options}\n' +
        '\t- target: app or mojit\n' +
        '\t- path (required for a mojit target): ' +
        'the path to the mojit to run jslint on\n\n' +
        'Run jslint on the app in the current directory:\n' +
        '\tmojito jslint app .\n' +
        'Run jslint on mojits/Bar:\n' +
        '\tmojito jslint mojit ./mojits/Bar\n' +
        'Run jslint on the mojito framework itself!\n' +
        '\tmojito jslint\n' +
        '\nOPTIONS: \n' +
        '\t  --print      :  print results to stdout \n' +
        '\t   -p          :  short for --print\n';

    /**
     * An array of option objects containing shortName, longName, and whether
     * the option expects a value (hasValue).
     * @type {Array}
     */
    Command.options = [
        {
            shortName: 'p',
            longName: 'print',
            hasValue: false
        }
    ];

//  ----------------------------------------------------------------------------
//  Generic File System Utilities
//  ----------------------------------------------------------------------------

    /*
     * A version of fs.mkdirSync that ensures that intermediate directories
     * are created.
     * @param {string} dir The leaf-level directory name to create.
     * @param {Number} mode The directory mode as an octal number.
     */
    Command._mkdirsSync = function(dir, mode) {
        var modeDefault = parseInt('755', 8); // Avoid octal per JSLint

        if (existsSync(dir)) {
            return;
        }
        mode = mode || modeDefault;
        Command._mkdirsSync(libpath.dirname(dir), mode);
        libfs.mkdirSync(dir, mode);
    };

    /*
     * Deletes a directory including any contained files and directories. This
     * is roughly equivalent to the Unix "rm -rf" command.
     * @param {string} file The file to remove.
     */
    Command._rmSync = function(file) {
        var failures = [],
            files,
            stat;

        try {
            stat = libfs.lstatSync(file);
        } catch (e1) {
            if (/^ENOENT/.test(e1.message)) {
                return null;
            }
            return [file];
        }

        if (stat.isFile() || stat.isSymbolicLink()) {
            try {
                libfs.unlinkSync(file);
            } catch (e2) {
                failures.push(file);
            }
        } else if (stat.isDirectory()) {
            files = libfs.readdirSync(file);
            files.forEach(function(f) {
                try {
                    failures.concat(Command._rmSync(libpath.join(file, f)));
                } catch (e3) {
                    failures.push(libpath.join(file, f));
                }
            });
        }

        return failures;
    };

    /*
     * Recursively processes all of the files in a directory tree. Each file is
     * passed to a 'processor' function that can do whatever it needs to do. An
     * optional 'filter' function allows files to be skipped (i.e. not passed to
     * the processor) by returning false.
     */
    Command._processDir = function(dir, processor, filter) {
        var files = libfs.readdirSync(dir),
            file,
            stat;

        filter = filter || function() { return true; };

        files.forEach(function(f) {
            file = libpath.join(dir, f);
            stat = libfs.statSync(file);
            if (filter(file, stat)) {
                if (stat.isDirectory()) {
                    Command._processDir(file, processor, filter);
                } else {
                    processor(file);
                }
            }
        });
    };

//  ----------------------------------------------------------------------------
//  Helper Classes
//  ----------------------------------------------------------------------------

    /*
     * A very simple class to allow writes to a file that is created only if it
     * needs to be written to, without the caller needing to check every write.
     * @param {String} filename The name of the file to write to.
     * @constructor
     */
    Command._OutputFile = function(filename) {
        var outstream = null;

        /*
         * Writes a string to the output file.
         * @param {string} s The string to write.
         */
        this.write = function(s) {
            if (!outstream) {
                Command._mkdirsSync(libpath.dirname(filename));
                outstream = libfs.createWriteStream(filename);
            }
            outstream.write(s);
        };

        /*
         * Closes the output stream.
         */
        this.done = function() {
            if (outstream) {
                outstream.end();
                outstream = null;
            }
        };
    };

    /*
     * A very simple class to allow writes to stdout.
     * @param {String} filename The name of the file to print as a "header".
     * @constructor
     */
    Command._OutputStdout = function(filename) {
        var printedName = false;

        /*
         * Writes a string to standard output.
         * @param {string} s The string to write.
         */
        this.write = function(s) {
            if (!printedName) {
                printedName = true;
                process.stdout.write(filename + '\n');
            }
            process.stdout.write(s);
        };

        /*
         * Closes the output stream. (Note that for stdout this is a noop.)
         */
        this.done = function() {};
    };

//  ----------------------------------------------------------------------------
//  Internal Helper Functions
//  ----------------------------------------------------------------------------

    /*
     * Run JSLint over a single file, writing any errors to the provided output,
     * and returning the number of errors encountered.
     */
    Command._lintOneFile = function(infile, outfile) {
        var jslint = require('jslint'),
            OPTS = {
                'continue': true, // Tolerate continue
                node: true,
                predef: [
                    // CommonJS
                    'exports',
                    // YUI
                    'YUI', 'YUI_config', 'YAHOO', 'YAHOO_config', 'Y',
                    // Node
                    'global', 'process', 'require', '__filename', 'module',
                    // Browser
                    'document', 'navigator', 'console', 'self', 'window'
                ]
            },
            errors = 0,
            i = 0,
            e,
            input,
            len,
            success,
            pad,
            data;

        input = libfs.readFileSync(infile);
        if (!input) {
            utils.error("Failed to open file '" + infile + "'.", Command.usage);
            return 0;
        }
        input = input.toString('utf8');

        // remove shebang (lifted from node.js)
        input = input.replace(/^\#\!.*/, '');

        success = jslint(input, OPTS);
        if (!success) {
            errors = len = jslint.errors.length;
            for (i = 0; i < len; i += 1) {
                pad = String(i + 1);
                while (pad.length < 4) {
                    pad = ' ' + pad;
                }
                e = jslint.errors[i];
                if (e) {
                    outfile.write(pad + ' ' + e.line + ',' + e.character +
                        ': ' + e.reason + '\n');
                    outfile.write('     ' +
                        (e.evidence || '').replace(/^\s+|\s+$/, '') + '\n');
                }
            }
        }

        data = jslint.data();
        data.functions.forEach(function(f) {
            if (f.name.charAt(0) === '_') {
                pad = String(i + 1);
                while (pad.length < 4) {
                    pad = ' ' + pad;
                }
                outfile.write(pad + ' ' + f.line + ': ' +
                        "Unexpected leading '_' in '" + f.name + "'.\n");
                i += 1;
                errors += 1;
            }
            if (f['var']) {
                f['var'].forEach(function(v) {
                    if (v.charAt(0) === '_') {
                        pad = String(i + 1);
                        while (pad.length < 4) {
                            pad = ' ' + pad;
                        }
                        outfile.write(pad + ' ' + f.line + ': ' +
                                "Unexpected leading '_' in '" + v + "'.\n");
                        outfile.write('     ' +
                                'function ' + f.name + '\n');
                        i += 1;
                        errors += 1;
                    }
                });
            }
        });

        return errors;
    };

    /*
     * Return the location of te Mojito framework. We're currently making an
     * assumption about relative location; we really need a way to validate that.
     */
    Command._getFrameworkDir = function() {
        return libpath.normalize(__dirname + '/../../../lib');
    };

    /*
     * Return the directory for the application. If no application name is given,
     * we look to see if we are inside an application already. If a name is given,
     * we check for an app in the current directory, and then look to see if we
     * are already there.
     */
    Command._getAppDir = function(appName) {
        var cwd = process.cwd(),
            dir,
            file,
            stat,
            parts;

        // If appName was not supplied, see if we're in an app.
        if (!appName) {
            file = libpath.join(cwd, 'server.js');
            return (existsSync(file) ? cwd : null);
        }

        // Look for the app where we are.
        dir = libpath.join(cwd, appName);
        if (existsSync(dir)) {
            stat = libfs.statSync(dir);
            if (stat.isDirectory()) {
                return dir;
            }
        }

        // See if we are already in the app.
        parts = cwd.split('/');
        if (parts[parts.length - 1] === appName) {
            return cwd;
        }

        // App not found.
        return null;
    };

    /*
     * Return the directory for the mojit. Currently assumes we are in the app.
     */
    Command._getMojitDir = function(mojitPath) {
        var cwd = process.cwd();

        return libpath.join(cwd, mojitPath);
    };

    /*
     * Return the name of the mojit, extracted from the path supplied.
     */
    Command._getMojitName = function(mojitPath) {
        var parts = mojitPath.split('/');

        return parts[parts.length - 1];
    };

    /*
     * Write an HTML page that makes it easy to navigate through the results. The
     * implementation of this is very "lazy" but also very simple in terms of file
     * system interaction.
     */
    Command._writePage = function(outDir, rowdata, totalErrors) {
        var outfile = libpath.join(outDir, 'jslint.html'),
            html = '' +
                '<title>JSLint Report</title>' +
                '
JSLint Report
' +
                '
Total of ' + totalErrors + ' errors in ' +
                rowdata.length + ' files.
' +
                '
\n';

        rowdata.forEach(function(row) {
            html += '
' +
                row.count +
                "	" +
                row.text +
                '
\n';
        });

        html += '
';

        libfs.writeFileSync(outfile, html);
    };

    /*
     * Process all of the files in the input directory, writing any JSLint errors
     * encountered to the output directory. Return the total count of errors.
     */
    Command._processFiles = function(inDir, outDir, excludeMatcher) {
        var totalErrors = 0,
            rowdata = [];

        inDir = libpath.normalize(inDir);
        if (outDir) {
            outDir = libpath.normalize(outDir);
        }

        Command._processDir(inDir,
            function(f) {
                var relname = f.replace(inDir, '').replace(/^\//, ''),
                    outname = relname.replace(/\.js$/, '.txt'),
                    out,
                    errors;

                if (!outDir) {
                    out = new Command._OutputStdout(relname);
                } else {
                    out = new Command._OutputFile(
                        libpath.join(outDir, outname)
                    );
                }

                errors = Command._lintOneFile(f, out);

                if (errors > 0) {
                    rowdata.push({count: errors, text: relname, url: outname});
                }
                totalErrors += errors;
                out.done();
            },
            function(f, s) {
                var p;
                if (excludeMatcher &&
                        excludeMatcher(f, s.isDirectory() ? 'dir' : 'file')) {
                    return false;
                }
                p = f.split('/');
                return (p[p.length - 1]).charAt(0) !== '.' &&
                    (s.isDirectory() || /\.js$/.test(p));
            });

        if (outDir && totalErrors > 0) {
            Command._writePage(outDir, rowdata, totalErrors);
        }

        return totalErrors;
    };

//  ----------------------------------------------------------------------------
//  Public Members
//  ----------------------------------------------------------------------------

    /*
     * Run this command. The options (framework, application, or mojit check, and
     * an optional path) are processed, and JSLint is run on each file. If there
     * are errors, a non-zero process exit is made.
     */
    Command.run = function(params, options) {
        var excludes,
            inDir,
            outDir,
            failures,
            errors,
            mojit_path,
            print = options && options.print;

        // Process params to determine input and output locations.
        if (!params || params.length === 0) {
            // framework
            inDir = Command._getFrameworkDir();
            outDir = libpath.join(process.cwd(), 'artifacts/framework/jslint');
            excludes = [
                /\/artifacts$/,
                /\/tests$/,
                /\/libs$/,
                /\/app\/libs$/,
                /\/node_modules$/,
                /\/mojito$/ // for the CI environment
            ];
        } else if (params[0] === 'app') {
            // application
            inDir = Command._getAppDir(params[1]);
            if (!inDir) {
                utils.error('Application not found.', Command.usage);
                return;
            }
            outDir = libpath.join(inDir, 'artifacts/jslint');
            excludes = [
                /\/tests$/,
                /\/artifacts$/,
                /\/node_modules$/
            ];
        } else if (params[0] === 'mojit') {
            // mojit
            mojit_path = params[1];
            if (!mojit_path) {
                utils.error('Please specify the path to mojit', Command.usage);
                return;
            }
            inDir = Command._getMojitDir(mojit_path);
            if (!existsSync(inDir)) {
                utils.error('Mojit ' + mojit_path + ' not found.',
                    Command.usage);
                return;
            }
            if (!inDir) {
                utils.error('Mojit ' + mojit_path + ' not found.',
                    Command.usage);
                return;
            }
            outDir = libpath.join(inDir, 'artifacts/jslint/mojits',
                Command._getMojitName(mojit_path));
            excludes = [
                /\/tests$/,
                /\/artifacts$/,
                /\/node_modules$/
            ];
        } else {
            utils.error('Unrecognized option: ' + params[0], Command.usage);
            return;
        }

        // Delete any files currently in the output location.
        failures = Command._rmSync(outDir);
        if (failures && failures.length) {
            console.log('Some files could not be deleted:\n');
            failures.forEach(function(f) {
                console.log('\t' + f);
            });
        }

        // Set outDir to null if we just plan on printing to stdout
        if (print) {
            outDir = null;
        }

        // Process the files with JSLint.
        errors = Command._processFiles(inDir, outDir,
            utils.getExclusionMatcher(excludes));
        if (!print && errors) {
            console.log('Lint report: ' + libpath.normalize(outDir));
        }
        process.stdout.write(errors + ' errors found.\n', 'utf8',
            function() {
                if (errors) {
                    // Make sure file streams have the opportunity to finish
                    setTimeout(function () {
                        process.exit(1);
                    });
                }
            });
    };

//  ----------------------------------------------------------------------------
//  EXPORT(S)
//  ----------------------------------------------------------------------------

    /**
     * Export the Command object, which includes all testable functions.
     * @type {Function}
     */
    module.exports = Command;

}());
<commit_msg>minor<commit_after>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint anon:true, sloppy:true, regexp:true, nomen:true, stupid:true*/

(function() {

    'use strict';

/**
 * @fileoverview Command logic for the Mojito 'jslint' command.
 */

//  ----------------------------------------------------------------------------
//  Prerequisites
//  ----------------------------------------------------------------------------

    var libfs = require('fs'),
        libpath = require('path'),
        existsSync = libfs.existsSync || libpath.existsSync,
        utils = require('../../management/utils');

//  ----------------------------------------------------------------------------
//  Command Root Object
//  ----------------------------------------------------------------------------

    /**
     * The Command Line Interpreter object. All testable methods and attributes
     * are properties of this object.
     */
    function Command() {
    }

    /**
     * The usage string output for command help.
     * @type {string}
     */
    Command.usage = 'mojito jslint [app | mojit] [<path>] {options}\n' +
        '\t- target: app or mojit\n' +
        '\t- path (required for a mojit target): ' +
        'the path to the mojit to run jslint on\n\n' +
        'Run jslint on the app in the current directory:\n' +
        '\tmojito jslint app .\n' +
        'Run jslint on mojits/Bar:\n' +
        '\tmojito jslint mojit ./mojits/Bar\n' +
        'Run jslint on the mojito framework itself!\n' +
        '\tmojito jslint\n' +
        '\nOPTIONS: \n' +
        '\t  --print      :  print results to stdout \n' +
        '\t   -p          :  short for --print\n';

    /**
     * An array of option objects containing shortName, longName, and whether
     * the option expects a value (hasValue).
     * @type {Array}
     */
    Command.options = [
        {
            shortName: 'p',
            longName: 'print',
            hasValue: false
        }
    ];

//  ----------------------------------------------------------------------------
//  Generic File System Utilities
//  ----------------------------------------------------------------------------

    /*
     * A version of fs.mkdirSync that ensures that intermediate directories
     * are created.
     * @param {string} dir The leaf-level directory name to create.
     * @param {Number} mode The directory mode as an octal number.
     */
    Command._mkdirsSync = function(dir, mode) {
        var modeDefault = parseInt('755', 8); // Avoid octal per JSLint

        if (existsSync(dir)) {
            return;
        }
        mode = mode || modeDefault;
        Command._mkdirsSync(libpath.dirname(dir), mode);
        libfs.mkdirSync(dir, mode);
    };

    /*
     * Deletes a directory including any contained files and directories. This
     * is roughly equivalent to the Unix "rm -rf" command.
     * @param {string} file The file to remove.
     */
    Command._rmSync = function(file) {
        var failures = [],
            files,
            stat;

        try {
            stat = libfs.lstatSync(file);
        } catch (e1) {
            if (/^ENOENT/.test(e1.message)) {
                return null;
            }
            return [file];
        }

        if (stat.isFile() || stat.isSymbolicLink()) {
            try {
                libfs.unlinkSync(file);
            } catch (e2) {
                failures.push(file);
            }
        } else if (stat.isDirectory()) {
            files = libfs.readdirSync(file);
            files.forEach(function(f) {
                try {
                    failures.concat(Command._rmSync(libpath.join(file, f)));
                } catch (e3) {
                    failures.push(libpath.join(file, f));
                }
            });
        }

        return failures;
    };

    /*
     * Recursively processes all of the files in a directory tree. Each file is
     * passed to a 'processor' function that can do whatever it needs to do. An
     * optional 'filter' function allows files to be skipped (i.e. not passed to
     * the processor) by returning false.
     */
    Command._processDir = function(dir, processor, filter) {
        var files = libfs.readdirSync(dir),
            file,
            stat;

        filter = filter || function() { return true; };

        files.forEach(function(f) {
            file = libpath.join(dir, f);
            stat = libfs.statSync(file);
            if (filter(file, stat)) {
                if (stat.isDirectory()) {
                    Command._processDir(file, processor, filter);
                } else {
                    processor(file);
                }
            }
        });
    };

//  ----------------------------------------------------------------------------
//  Helper Classes
//  ----------------------------------------------------------------------------

    /*
     * A very simple class to allow writes to a file that is created only if it
     * needs to be written to, without the caller needing to check every write.
     * @param {String} filename The name of the file to write to.
     * @constructor
     */
    Command._OutputFile = function(filename) {
        var outstream = null;

        /*
         * Writes a string to the output file.
         * @param {string} s The string to write.
         */
        this.write = function(s) {
            if (!outstream) {
                Command._mkdirsSync(libpath.dirname(filename));
                outstream = libfs.createWriteStream(filename);
            }
            outstream.write(s);
        };

        /*
         * Closes the output stream.
         */
        this.done = function() {
            if (outstream) {
                outstream.end();
                outstream = null;
            }
        };
    };

    /*
     * A very simple class to allow writes to stdout.
     * @param {String} filename The name of the file to print as a "header".
     * @constructor
     */
    Command._OutputStdout = function(filename) {
        var printedName = false;

        /*
         * Writes a string to standard output.
         * @param {string} s The string to write.
         */
        this.write = function(s) {
            if (!printedName) {
                printedName = true;
                process.stdout.write(filename + '\n');
            }
            process.stdout.write(s);
        };

        /*
         * Closes the output stream. (Note that for stdout this is a noop.)
         */
        this.done = function() {};
    };

//  ----------------------------------------------------------------------------
//  Internal Helper Functions
//  ----------------------------------------------------------------------------

    /*
     * Run JSLint over a single file, writing any errors to the provided output,
     * and returning the number of errors encountered.
     */
    Command._lintOneFile = function(infile, outfile) {
        var jslint = require('jslint'),
            OPTS = {
                'continue': true, // Tolerate continue
                node: true,
                predef: [
                    // CommonJS
                    'exports',
                    // YUI
                    'YUI', 'YUI_config', 'YAHOO', 'YAHOO_config', 'Y',
                    // Node
                    'global', 'process', 'require', '__filename', 'module',
                    // Browser
                    'document', 'navigator', 'console', 'self', 'window'
                ]
            },
            errors = 0,
            i = 0,
            e,
            input,
            len,
            success,
            pad,
            data;

        input = libfs.readFileSync(infile);
        if (!input) {
            utils.error("Failed to open file '" + infile + "'.", Command.usage);
            return 0;
        }
        input = input.toString('utf8');

        // remove shebang (lifted from node.js)
        input = input.replace(/^\#\!.*/, '');

        success = jslint(input, OPTS);
        if (!success) {
            errors = len = jslint.errors.length;
            for (i = 0; i < len; i += 1) {
                pad = String(i + 1);
                while (pad.length < 4) {
                    pad = ' ' + pad;
                }
                e = jslint.errors[i];
                if (e) {
                    outfile.write(pad + ' ' + e.line + ',' + e.character +
                        ': ' + e.reason + '\n');
                    outfile.write('     ' +
                        (e.evidence || '').replace(/^\s+|\s+$/, '') + '\n');
                }
            }
        }

        data = jslint.data();
        data.functions.forEach(function(f) {
            if (f.name.charAt(0) === '_') {
                pad = String(i + 1);
                while (pad.length < 4) {
                    pad = ' ' + pad;
                }
                outfile.write(pad + ' ' + f.line + ': ' +
                        "Unexpected leading '_' in '" + f.name + "'.\n");
                i += 1;
                errors += 1;
            }
            if (f['var']) {
                f['var'].forEach(function(v) {
                    if (v.charAt(0) === '_') {
                        pad = String(i + 1);
                        while (pad.length < 4) {
                            pad = ' ' + pad;
                        }
                        outfile.write(pad + ' ' + f.line + ': ' +
                                "Unexpected leading '_' in '" + v + "'.\n");
                        outfile.write('     ' +
                                'function ' + f.name + '\n');
                        i += 1;
                        errors += 1;
                    }
                });
            }
        });

        return errors;
    };

    /*
     * Return the location of te Mojito framework. We're currently making an
     * assumption about relative location; we really need a way to validate that.
     */
    Command._getFrameworkDir = function() {
        return libpath.normalize(__dirname + '/../../../lib');
    };

    /*
     * Return the directory for the application. If no application name is given,
     * we look to see if we are inside an application already. If a name is given,
     * we check for an app in the current directory, and then look to see if we
     * are already there.
     */
    Command._getAppDir = function(appName) {
        var cwd = process.cwd(),
            dir,
            file,
            stat,
            parts;

        // If appName was not supplied, see if we're in an app.
        if (!appName) {
            file = libpath.join(cwd, 'server.js');
            return (existsSync(file) ? cwd : null);
        }

        // Look for the app where we are.
        dir = libpath.join(cwd, appName);
        if (existsSync(dir)) {
            stat = libfs.statSync(dir);
            if (stat.isDirectory()) {
                return dir;
            }
        }

        // See if we are already in the app.
        parts = cwd.split('/');
        if (parts[parts.length - 1] === appName) {
            return cwd;
        }

        // App not found.
        return null;
    };

    /*
     * Return the directory for the mojit. Currently assumes we are in the app.
     */
    Command._getMojitDir = function(mojitPath) {
        var cwd = process.cwd();

        return libpath.join(cwd, mojitPath);
    };

    /*
     * Return the name of the mojit, extracted from the path supplied.
     */
    Command._getMojitName = function(mojitPath) {
        var parts = mojitPath.split('/');

        return parts[parts.length - 1];
    };

    /*
     * Write an HTML page that makes it easy to navigate through the results. The
     * implementation of this is very "lazy" but also very simple in terms of file
     * system interaction.
     */
    Command._writePage = function(outDir, rowdata, totalErrors) {
        var outfile = libpath.join(outDir, 'jslint.html'),
            html = '' +
                '<title>JSLint Report</title>' +
                '
JSLint Report
' +
                '
Total of ' + totalErrors + ' errors in ' +
                rowdata.length + ' files.
' +
                '
\n';

        rowdata.forEach(function(row) {
            html += '
' +
                row.count +
                "	" +
                row.text +
                '
\n';
        });

        html += '
';

        libfs.writeFileSync(outfile, html);
    };

    /*
     * Process all of the files in the input directory, writing any JSLint errors
     * encountered to the output directory. Return the total count of errors.
     */
    Command._processFiles = function(inDir, outDir, excludeMatcher) {
        var totalErrors = 0,
            rowdata = [];

        inDir = libpath.normalize(inDir);
        if (outDir) {
            outDir = libpath.normalize(outDir);
        }

        Command._processDir(inDir,
            function(f) {
                var relname = f.replace(inDir, '').replace(/^\//, ''),
                    outname = relname.replace(/\.js$/, '.txt'),
                    out,
                    errors;

                if (!outDir) {
                    out = new Command._OutputStdout(relname);
                } else {
                    out = new Command._OutputFile(
                        libpath.join(outDir, outname)
                    );
                }

                errors = Command._lintOneFile(f, out);

                if (errors > 0) {
                    rowdata.push({count: errors, text: relname, url: outname});
                }
                totalErrors += errors;
                out.done();
            },
            function(f, s) {
                var p;
                if (excludeMatcher &&
                        excludeMatcher(f, s.isDirectory() ? 'dir' : 'file')) {
                    return false;
                }
                p = f.split('/');
                return (p[p.length - 1]).charAt(0) !== '.' &&
                    (s.isDirectory() || /\.js$/.test(p));
            });

        if (outDir && totalErrors > 0) {
            Command._writePage(outDir, rowdata, totalErrors);
        }

        return totalErrors;
    };

//  ----------------------------------------------------------------------------
//  Public Members
//  ----------------------------------------------------------------------------

    /*
     * Run this command. The options (framework, application, or mojit check, and
     * an optional path) are processed, and JSLint is run on each file. If there
     * are errors, a non-zero process exit is made.
     */
    Command.run = function(params, options) {
        var excludes,
            inDir,
            outDir,
            failures,
            errors,
            mojit_path,
            print = options && options.print;

        // Process params to determine input and output locations.
        if (!params || params.length === 0) {
            // framework
            inDir = Command._getFrameworkDir();
            outDir = libpath.join(process.cwd(), 'artifacts/framework/jslint');
            excludes = [
                /\/artifacts$/,
                /\/tests$/,
                /\/libs$/,
                /\/app\/libs$/,
                /\/node_modules$/,
                /\/mojito$/ // for the CI environment
            ];
        } else if (params[0] === 'app') {
            // application
            inDir = Command._getAppDir(params[1]);
            if (!inDir) {
                utils.error('Application not found.', Command.usage);
                return;
            }
            outDir = libpath.join(inDir, 'artifacts/jslint');
            excludes = [
                /\/tests$/,
                /\/artifacts$/,
                /\/node_modules$/
            ];
        } else if (params[0] === 'mojit') {
            // mojit
            mojit_path = params[1];
            if (!mojit_path) {
                utils.error('Please specify the path to mojit', Command.usage);
                return;
            }
            inDir = Command._getMojitDir(mojit_path);
            if (!existsSync(inDir)) {
                utils.error('Mojit ' + mojit_path + ' not found.',
                    Command.usage);
                return;
            }
            if (!inDir) {
                utils.error('Mojit ' + mojit_path + ' not found.',
                    Command.usage);
                return;
            }
            outDir = libpath.join(inDir, 'artifacts/jslint/mojits',
                Command._getMojitName(mojit_path));
            excludes = [
                /\/tests$/,
                /\/artifacts$/,
                /\/node_modules$/
            ];
        } else {
            utils.error('Unrecognized option: ' + params[0], Command.usage);
            return;
        }

        // Delete any files currently in the output location.
        failures = Command._rmSync(outDir);
        if (failures && failures.length) {
            console.log('Some files could not be deleted:\n');
            failures.forEach(function(f) {
                console.log('\t' + f);
            });
        }

        // Set outDir to null if we just plan on printing to stdout
        if (print) {
            outDir = null;
        }

        // Process the files with JSLint.
        errors = Command._processFiles(inDir, outDir,
            utils.getExclusionMatcher(excludes));
        if (!print && errors) {
            console.log('Lint report: ' + libpath.normalize(outDir));
        }
        process.stdout.write(errors + ' errors found.\n', 'utf8',
            function() {
                if (errors) {
                    // Make sure file streams have the opportunity to finish
                    setTimeout(function () {
                        process.exit(1);
                    });
                }
            });
    };

//  ----------------------------------------------------------------------------
//  EXPORT(S)
//  ----------------------------------------------------------------------------

    /**
     * Export the Command object, which includes all testable functions.
     * @type {Function}
     */
    module.exports = Command;

}());
<|endoftext|>