From d28787930e51b173e9749d9bd98cc6dff71f0d29 Mon Sep 17 00:00:00 2001 From: Brian Ford Date: Tue, 9 Dec 2014 17:44:34 -0800 Subject: [PATCH] v0.5.0 --- dist/hint.js | 2399 ++++++++++++++++++++++++++++---------------------- 1 file changed, 1346 insertions(+), 1053 deletions(-) diff --git a/dist/hint.js b/dist/hint.js index 8d41d4b..522f741 100755 --- a/dist/hint.js +++ b/dist/hint.js @@ -1,31 +1,46 @@ (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o -1) || - angular.hint.logMessage('##General## Module ' + name + ' could not be found'); + angular.hint.logMessage('General', 'Module ' + name + ' could not be found', SEVERITY_WARNING); }); } @@ -530,160 +575,290 @@ function title(str) { return str[0].toUpperCase() + str.substr(1); } +var LEVELS = [ + 'error', + 'warning', + 'suggestion' +]; + function flush() { - var log = angular.hint.flush(), groups = Object.keys(log); - for(var i = 0, ii = groups.length; i < ii; i++) { - console.groupCollapsed? console.groupCollapsed('Angular Hint: ' + groups[i]) : - console.log('Angular Hint: ' + groups[i]); - var messages = Object.keys(log[groups[i]]); - for(var j = 0, jj = messages.length; j < jj; j++) { - console.log(messages[j]); - } + var log = angular.hint.flush(), + groups = Object.keys(log); + + groups.forEach(function (groupName) { + var group = log[groupName]; + var header = 'Angular Hint: ' + groupName; + + console.groupCollapsed ? + console.groupCollapsed(header) : + console.log(header); + + LEVELS.forEach(function (level) { + group[level] && logGroup(group[level], title(level)); + }); console.groupEnd && console.groupEnd(); + }); +} + +setInterval(flush, 2) + +angular.hint.onMessage = function () { + setTimeout(flush, 2); +}; + +function logGroup(group, type) { + console.group ? console.group(type) : console.log(type); + for(var i = 0, ii = group.length; i < ii; i++) { + console.log(group[i]); } + console.group && console.groupEnd(); } -setInterval(flush, 5); -},{"angular-hint-controllers":4,"angular-hint-directives":5,"angular-hint-dom":35,"angular-hint-events":37,"angular-hint-interpolation":48,"angular-hint-log":59,"angular-hint-modules":60}],4:[function(require,module,exports){ +},{"angular-hint-controllers":4,"angular-hint-directives":6,"angular-hint-events":42,"angular-hint-log":51,"angular-hint-modules":52,"angular-hint-scopes":70}],4:[function(require,module,exports){ 'use strict'; -var nameToControllerMatch = {}; -var controllers = {}; -var hintLog = angular.hint = require('angular-hint-log'); +var hint = angular.hint = require('angular-hint-log'); + +var MODULE_NAME = 'Controllers', + CATEGORY_CONTROLLER_NAME = 'Name controllers according to best practices', + CATEGORY_GLOBAL_CONTROLLER = 'Using global functions as controllers is against Angular best practices and depricated in Angular 1.3 and up', + SEVERITY_ERROR = 1, + SEVERITY_WARNING = 2; + +// local state +var nameToControllerMap = {}; /** * Decorates $controller with a patching function to * log a message if the controller is instantiated on the window */ angular.module('ngHintControllers', []). - config(function ($provide) { - $provide.decorator('$controller', function($delegate) { - return function(ctrl, locals) { - //If the controller name is passed, find the controller than matches it - if(typeof ctrl === 'string') { - ctrl = nameToControllerMatch[ctrl]; - } - locals = locals || {}; - //If the controller is not in the list of already registered controllers - //and it is not connected to the local scope, it must be instantiated on the window - if(!controllers[ctrl] && (!locals.$scope || !locals.$scope[ctrl])) { - if(angular.version.minor <= 2) { - hintLog.logMessage('##Controllers## It is against Angular best practices to ' + - 'instantiate a controller on the window. This behavior is deprecated in Angular' + - ' 1.3.0'); - } else { - hintLog.logMessage('##Controllers## Global instantiation of controllers was deprecated in Angular' + - ' 1.3.0. Define the controller on a module.'); - } - } - var ctrlInstance = $delegate.apply(this, [ctrl, locals]); - return ctrlInstance; - }; - }); -}); + config(['$provide', function ($provide) { + $provide.decorator('$controller', ['$delegate', controllerDecorator]); + }]); + +function controllerDecorator($delegate) { + return function(ctrl, locals) { + //If the controller name is passed, find the controller than matches it + if (typeof ctrl === 'string') { + var ctrlName = ctrl; + sendMessageForControllerName(ctrlName); + if (nameToControllerMap[ctrlName]) { + ctrl = nameToControllerMap[ctrlName]; + } else { + //If the controller function cannot be found, check for it on the window + ctrl = window[ctrlName] || ctrl; + if (typeof ctrl === 'function') { + sendMessageForGlobalController(ctrlName); + } else { + throw new Error('The controller function for ' + ctrl + ' could not be found.' + + ' Is the function registered under that name?'); + } + } + } + locals = locals || {}; + arguments[0] = ctrl; + arguments[1] = locals; + var ctrlInstance = $delegate.apply(this, arguments); + return ctrlInstance; + }; +} /** * Save details of the controllers as they are instantiated * for use in decoration. +* Hint about the best practices for naming controllers. */ var originalModule = angular.module; -angular.module = function() { - var module = originalModule.apply(this, arguments); - var originalController = module.controller; - module.controller = function(controllerName, controllerConstructor) { - nameToControllerMatch[controllerName] = controllerConstructor; - var firstLetter = controllerName.charAt(0); - if(firstLetter !== firstLetter.toUpperCase() && firstLetter === firstLetter.toLowerCase()) { - hintLog.logMessage('##Controllers## The best practice is to name controllers with an' + - ' uppercase first letter. Check the name of \'' + controllerName + '\'.'); - } +function sendMessageForGlobalController(name) { + hint.logMessage(MODULE_NAME, + 'add `' + name + '` to a module', + angular.version.minor <= 2 ? SEVERITY_WARNING : SEVERITY_ERROR, + CATEGORY_GLOBAL_CONTROLLER); +} - var splitName = controllerName.split('Controller'); - if(splitName.length === 1 || splitName[splitName.length - 1] !== '') { - hintLog.logMessage('##Controllers## The best practice is to name controllers ending with ' + - '\'Controller\'. Check the name of \'' + controllerName + '\''); - } +function sendMessageForControllerName(name) { + var newName = name; + if (!startsWithUpperCase(name)) { + newName = title(newName); + } + if (!endsWithController(name)) { + newName = addControllerSuffix(newName); + } + if (name !== newName) { + hint.logMessage(MODULE_NAME, + 'Consider renaming `' + name + '` to `' + newName + '`.', + SEVERITY_WARNING, + CATEGORY_CONTROLLER_NAME); + } +} + +function startsWithUpperCase(name) { + var firstChar = name.charAt(0); + return firstChar === firstChar.toUpperCase() && + firstChar !== firstChar.toLowerCase(); +} + +function title (name) { + return name[0].toUpperCase() + name.substr(1); +} - controllers[controllerConstructor] = controllerConstructor; +var CONTROLLER_RE = /Controller$/; +function endsWithController(name) { + return CONTROLLER_RE.test(name); +} + +var RE = /(Ctrl|Kontroller)?$/; +function addControllerSuffix(name) { + return name.replace(RE, 'Controller'); +} + +/* + * decorate angular module API + */ + +angular.module = function() { + var module = originalModule.apply(this, arguments), + originalController = module.controller; + + module.controller = function(controllerName, controllerConstructor) { + nameToControllerMap[controllerName] = controllerConstructor; + sendMessageForControllerName(controllerName); return originalController.apply(this, arguments); }; return module; }; -},{"angular-hint-log":59}],5:[function(require,module,exports){ +},{"angular-hint-log":5}],5:[function(require,module,exports){ +'use strict'; + +/* + * HintLog creates a queue of messages logged by ngHint modules. This object + * has a key for each ngHint module that corresponds to the messages + * from that module. + */ +var queuedMessages = {}, + MESSAGE_TYPES = [ + 'error', + 'warning', + 'suggestion' + ]; + +/* + * Add a message to the HintLog message queue. Messages are organized into categories + * according to their module name and severity. + */ +function logMessage(moduleName, message, severity, category) { + // If no severity was provided, categorize the message as a `suggestion` + severity = severity || 3; + var messageType = MESSAGE_TYPES[severity - 1]; + + moduleName = moduleName || 'General'; + + // If the category does not exist, initialize a new object + queuedMessages[moduleName] = queuedMessages[moduleName] || {}; + queuedMessages[moduleName][messageType] = queuedMessages[moduleName][messageType] || []; + + if (queuedMessages[moduleName][messageType].indexOf(message) < 0) { + queuedMessages[moduleName][messageType].push(message); + } + + this.onMessage(moduleName, message, messageType, category); +} + +/* + * Return and empty the current queue of messages. + */ +function flush() { + var flushMessages = queuedMessages; + queuedMessages = {}; + return flushMessages; +} + +module.exports.onMessage = function(message) {}; +module.exports.logMessage = logMessage; +module.exports.flush = flush; + +},{}],6:[function(require,module,exports){ 'use strict'; -var hintLog = angular.hint = require('angular-hint-log'); var ddLibData = require('./lib/ddLib-data'); var RESTRICT_REGEXP = /restrict\s*:\s*['"](.+?)['"]/; var customDirectives = []; -var dasherize = require('dasherize'); var search = require('./lib/search'); var checkPrelimErrors = require('./lib/checkPrelimErrors'); var getKeysAndValues = require('./lib/getKeysAndValues'); var defaultDirectives = ddLibData.directiveTypes['angular-default-directives'].directives; var htmlDirectives = ddLibData.directiveTypes['html-directives'].directives; -angular.module('ngHintDirectives', ['ngLocale']) +angular.module('ngHintDirectives', []) .config(['$provide', function($provide) { $provide.decorator('$compile', ['$delegate', function($delegate) { - return function(elem) { + + var newCompile = function(elem) { elem = angular.element(elem); - for(var i = 0; i < elem.length; i+=2){ - if(elem[i].getElementsByTagName){ + for(var i = 0, length = elem.length; i < length; i+=2) { + if(elem[i].getElementsByTagName) { var toSend = Array.prototype.slice.call(elem[i].getElementsByTagName('*')); search(toSend, customDirectives); } } return $delegate.apply(this, arguments); }; + + // TODO: test this + // copy private helpers like $$addScopeInfo + for (var prop in $delegate) { + if ($delegate.hasOwnProperty(prop)) { + newCompile[prop] = $delegate[prop]; + } + } + return newCompile; }]); }]); - -angular.module('ngLocale').config(function($provide) { - var originalProvider = $provide.provider; - $provide.provider = function(token, provider) { - provider = originalProvider.apply($provide, arguments); - if (token === '$compile') { - var originalProviderDirective = provider.directive; - provider.directive = function(dirsObj) { - for(var prop in dirsObj){ - var propDashed = dasherize(prop); - if(isNaN(+propDashed) && - !defaultDirectives[propDashed] && - !htmlDirectives[propDashed]) { - var matchRestrict = dirsObj[prop].toString().match(RESTRICT_REGEXP); - ddLibData.directiveTypes['angular-default-directives'] - .directives[propDashed] = (matchRestrict && matchRestrict[1]) || 'ACME'; - } - } - return originalProviderDirective.apply(this, arguments); - }; +function supportObject(directiveObject) { + if(typeof directiveObject === 'object') { + var keys = Object.keys(directiveObject); + for(var i = keys.length - 1; i >= 0; i--) { + if(typeof directiveObject[keys[i]] === 'function') { + return directiveObject[keys[i]]; + } } - return provider; - }; -}); + } + return function() {}; +} var originalAngularModule = angular.module; angular.module = function() { var module = originalAngularModule.apply(this, arguments); var originalDirective = module.directive; module.directive = function(directiveName, directiveFactory) { + directiveFactory = directiveFactory || supportObject(directiveName); + directiveName = typeof directiveName === 'string' ? directiveName : Object.keys(directiveName)[0]; + var originalDirectiveFactory = typeof directiveFactory === 'function' ? directiveFactory : directiveFactory[directiveFactory.length - 1]; + var factoryStr = originalDirectiveFactory.toString(); - checkPrelimErrors(directiveName,factoryStr); + checkPrelimErrors(directiveName, factoryStr); var pairs = getKeysAndValues(factoryStr); - pairs.map(function(pair){customDirectives.push(pair);}); + pairs.map(function(pair) { + customDirectives.push(pair); + }); var matchRestrict = factoryStr.match(RESTRICT_REGEXP); var restrict = (matchRestrict && matchRestrict[1]) || 'A'; - var directive = {directiveName: directiveName, restrict: restrict, require:pairs}; + var directive = { + directiveName: directiveName, + restrict: restrict, + require: pairs + }; + customDirectives.push(directive); return originalDirective.apply(this, arguments); @@ -691,7 +866,7 @@ angular.module = function() { return module; }; -},{"./lib/checkPrelimErrors":15,"./lib/ddLib-data":16,"./lib/getKeysAndValues":23,"./lib/search":31,"angular-hint-log":33,"dasherize":34}],6:[function(require,module,exports){ +},{"./lib/checkPrelimErrors":19,"./lib/ddLib-data":20,"./lib/getKeysAndValues":27,"./lib/search":35}],7:[function(require,module,exports){ /** *@param s: first string to compare *@param t: second string to compare @@ -699,9 +874,9 @@ angular.module = function() { *@description: *Checks to see if two strings are similiar enough to even bother checking the Levenshtein Distance. */ -module.exports = function(s,t) { +module.exports = function(s, t) { var strMap = {}, similarities = 0, STRICTNESS = 0.66; - if(Math.abs(s.length-t.length) > 3) { + if(Math.abs(s.length - t.length) > 3) { return false; } s.split('').forEach(function(x){strMap[x] = x;}); @@ -711,7 +886,7 @@ module.exports = function(s,t) { return similarities >= t.length * STRICTNESS; }; -},{}],7:[function(require,module,exports){ +},{}],8:[function(require,module,exports){ var ddLibData = require('./ddLib-data'); /** @@ -754,82 +929,138 @@ module.exports = function(attribute, options) { } } }); - var typeError = wrongUse? 'wronguse':'' || !anyTrue ? 'nonexsisting' : '' || ''; + var typeError = wrongUse? 'wronguse' : '' || !anyTrue ? 'nonexsisting' : '' || ''; return {exsists: anyTrue, wrongUse: wrongUse, typeError: typeError}; }; -},{"./ddLib-data":16}],8:[function(require,module,exports){ +},{"./ddLib-data":20}],9:[function(require,module,exports){ +var ddLibData = require('./ddLib-data'), + SEVERITY_ERROR = 1; + +module.exports = function(info, id, type) { + var message = ddLibData.directiveTypes[info.directiveType].message + type + ' element' + id + '. '; + var error = info.error; + error = (error.charAt(0) === '*') ? error.substring(1): error; + message += 'Found deprecated directive "' + error + '". Use an alternative solution.'; + return [message, SEVERITY_ERROR]; +}; + +},{"./ddLib-data":20}],10:[function(require,module,exports){ +var SEVERITY_ERROR = 1; + module.exports = function(info, id, type) { - var s = info.missing.length === 1 ? ' ' : 's '; - var waswere = info.missing.length === 1 ? 'was ' : 'were '; + var missingLength = info.missing.length; + var s = missingLength === 1 ? ' ' : 's '; + var waswere = missingLength === 1 ? 'is ' : 'are '; var missing = ''; info.missing.forEach(function(str){ - missing += '"'+str+'",'; + missing += '"' + str + '",'; }); - missing = '['+missing.substring(0,missing.length-1)+'] '; - var message = 'Attribute'+s+missing+waswere+'found to be missing in '+type+ ' element'+id+'.'; - return message; + missing = '[' + missing.substring(0, missing.length-1) + '] '; + var message = 'Attribute' + s + missing + waswere + 'missing in ' + type + ' element' + id + '.'; + return [message, SEVERITY_ERROR]; }; -},{}],9:[function(require,module,exports){ -var isMutExclusiveDir = require('./isMutExclusiveDir'); +},{}],11:[function(require,module,exports){ +var isMutExclusiveDir = require('./isMutExclusiveDir'), + SEVERITY_ERROR = 1; module.exports = function(info, id, type) { var pair = isMutExclusiveDir(info.error); var message = 'Angular attributes "'+info.error+'" and "'+pair+'" in '+type+ ' element'+id+ ' should not be attributes together on the same HTML element'; - return message; + return [message, SEVERITY_ERROR]; }; -},{"./isMutExclusiveDir":28}],10:[function(require,module,exports){ -var hintLog = require('angular-hint-log'); +},{"./isMutExclusiveDir":32}],12:[function(require,module,exports){ +var hintLog = require('angular-hint-log'), + MODULE_NAME = 'Directives', + SEVERITY_SUGGESTION = 3; module.exports = function(directiveName) { var message = 'Directive "'+directiveName+'" should have proper namespace try adding a prefix'+ ' and/or using camelcase.'; var domElement = '<'+directiveName+'> '; - hintLog.logMessage('##Directives## ' + message); + hintLog.logMessage(MODULE_NAME, message, SEVERITY_SUGGESTION); }; -},{"angular-hint-log":33}],11:[function(require,module,exports){ +},{"angular-hint-log":37}],13:[function(require,module,exports){ +var SEVERITY_SUGGESTION = 3; + module.exports = function(info, id, type) { - var ngDir = 'ng-'+info.error.substring(2); - var message = 'Use Angular version of "'+info.error+'" in '+type+' element'+id+'. Try: "'+ngDir+'"'; - return message; + var ngDir = 'ng-' + info.error.substring(2), + message = 'Use Angular version of "' + info.error + '" in ' + type + ' element' + id + + '. Try: "' + ngDir + '"'; + return [message, SEVERITY_SUGGESTION]; }; -},{}],12:[function(require,module,exports){ -var ddLibData = require('./ddLib-data'); +},{}],14:[function(require,module,exports){ +var SEVERITY_ERROR = 1; +module.exports = function(info, id, type) { + var message = 'ngRepeat in '+type+' element'+id+' was used incorrectly. '+info.suggestion; + return [message, SEVERITY_ERROR]; +}; + +},{}],15:[function(require,module,exports){ +var ddLibData = require('./ddLib-data'), + SEVERITY_ERROR = 1; module.exports = function(info, id, type) { - var message = ddLibData.directiveTypes[info.directiveType].message+type+' element'+id+'. '; + var message = ddLibData.directiveTypes[info.directiveType].message + type + ' element' + id + '. '; var error = (info.error.charAt(0) === '*') ? info.error.substring(1): info.error; - message +='Found incorrect attribute "'+error+'" try "'+info.match+'".'; - return message; + message += 'Found incorrect attribute "' + error + '" try "' + info.match + '".'; + return [message, SEVERITY_ERROR]; }; -},{"./ddLib-data":16}],13:[function(require,module,exports){ -var hintLog = require('angular-hint-log'); +},{"./ddLib-data":20}],16:[function(require,module,exports){ +var hintLog = angular.hint = require('angular-hint-log'), + MODULE_NAME = 'Directives', + SEVERITY_ERROR = 1; module.exports = function(directiveName) { var message = 'The use of "replace" in directive factories is deprecated,'+ - ' and it was found in "'+directiveName+'".'; - var domElement = '<'+directiveName+'> '; - hintLog.logMessage('##Directives## ' + message); + ' and it was found in "' + directiveName + '".'; + var domElement = '<' + directiveName + '> '; + hintLog.logMessage(MODULE_NAME, message, SEVERITY_ERROR); }; -},{"angular-hint-log":33}],14:[function(require,module,exports){ -var ddLibData = require('./ddLib-data'); +},{"angular-hint-log":37}],17:[function(require,module,exports){ +var ddLibData = require('./ddLib-data'), + SEVERITY_ERROR = 1; module.exports = function(info, id, type) { - var message = ddLibData.directiveTypes[info.directiveType].message+type+' element'+id+'. '; - var error = (info.error.charAt(0) === '*') ? info.error.substring(1): info.error; - var aecmType = (info.wrongUse.indexOf('attribute') > -1)? 'Element' : 'Attribute'; - message += aecmType+' name "'+error+'" is reserved for '+info.wrongUse+' names only.'; - return message; -}; - -},{"./ddLib-data":16}],15:[function(require,module,exports){ + var message = ddLibData.directiveTypes[info.directiveType].message + type + ' element' + + id + '. ', + error = (info.error.charAt(0) === '*') ? info.error.substring(1): info.error, + aecmType = (info.wrongUse.indexOf('attribute') > -1)? 'Element' : 'Attribute'; + message += aecmType + ' name "' + error + '" is reserved for ' + info.wrongUse + ' names only.'; + return [message, SEVERITY_ERROR]; +}; + +},{"./ddLib-data":20}],18:[function(require,module,exports){ + +module.exports = function(attrVal){ + var suggestion, + error = false, + TRACK_REGEXP = /track\s+by\s+\S*/, + FILTER_REGEXP = /filter\s*:\s*\w+(?:\.\w+)*/; + var trackMatch = attrVal.match(TRACK_REGEXP); + var filterMatch = attrVal.match(FILTER_REGEXP); + var breakIndex = attrVal.indexOf('|') > -1 ? attrVal.indexOf('|') : Infinity; + + if(!trackMatch && filterMatch && breakIndex === Infinity) { + return 'Try: " | '+filterMatch[0]+'"'; + } + + if(trackMatch && filterMatch) { + var trackInd = attrVal.indexOf(trackMatch[0]); + var filterInd = attrVal.indexOf(filterMatch[0]); + if(!(breakIndex < filterInd && filterInd < trackInd)) { + return 'Try: " | '+filterMatch[0]+' '+trackMatch[0]+'"'; + } + } +} +},{}],19:[function(require,module,exports){ var hasNameSpace = require('./hasNameSpace'); var buildNameSpace = require('./buildNameSpace'); var hasReplaceOption = require('./hasReplaceOption'); @@ -844,91 +1075,94 @@ module.exports = function(dirName, dirFacStr) { } }; -},{"./buildNameSpace":10,"./buildReplaceOption":13,"./hasNameSpace":26,"./hasReplaceOption":27}],16:[function(require,module,exports){ +},{"./buildNameSpace":12,"./buildReplaceOption":16,"./hasNameSpace":30,"./hasReplaceOption":31}],20:[function(require,module,exports){ module.exports = { directiveTypes : { 'html-directives': { message: 'There was an HTML error in ', directives: { - 'abbr' : 'A', - 'accept': 'A', - 'accesskey': 'A', - 'action': 'A', - 'align': 'A', - 'alt': 'A', - 'background': 'A', - 'bgcolor': 'A', - 'border': 'A', - 'cellpadding': 'A', - 'char': 'A', - 'charoff': 'A', - 'charset': 'A', - 'checked': 'A', - 'cite': 'A', - 'class': 'A', - 'classid': 'A', - 'code': 'A', - 'codebase': 'A', - 'color': 'A', - 'cols': 'A', - 'colspan': 'A', - 'content': 'A', - 'data': 'A', - 'defer': 'A', - 'dir': 'A', - 'face': 'A', - 'for': 'A', - 'frame': 'A', - 'frameborder': 'A', - 'headers': 'A', - 'height': 'A', - 'http-equiv': 'A', - 'href': 'A', - 'id': 'A', - 'label': 'A', - 'lang': 'A', - 'language': 'A', - 'link': 'A', - 'marginheight': 'A', - 'marginwidth': 'A', - 'maxlength': 'A', - 'media': 'A', - 'multiple': 'A', - 'name': 'A', - 'object': '!A', - 'onblur': '!A', - 'onchange': '!A', - 'onclick': '!A', - 'onfocus': '!A', - 'onkeydown': '!A', - 'onkeypress': '!A', - 'onkeyup': '!A', - 'onload': '!A', - 'onmousedown': '!A', - 'onmousemove': '!A', - 'onmouseout': '!A', - 'onmouseover': '!A', - 'onmouseup': '!A', - 'onreset': '!A', - 'onselect': '!A', - 'onsubmit': '!A', - 'readonly': 'A', - 'rel': 'A', - 'rev': 'A', - 'role': 'A', - 'rows': 'A', - 'rowspan': 'A', - 'size': 'A', - 'span': 'EA', - 'src': 'A', - 'start': 'A', - 'style': 'A', - 'text': 'A', - 'target': 'A', - 'title': 'A', - 'type': 'A', - 'value': 'A', - 'width': 'A'} + 'abbr' : 'A', + 'accept': 'A', + 'accesskey': 'A', + 'action': 'A', + 'align': 'A', + 'alt': 'A', + 'async': 'A', + 'background': 'A', + 'bgcolor': 'A', + 'border': 'A', + 'cellpadding': 'A', + 'char': 'A', + 'charoff': 'A', + 'charset': 'A', + 'checked': 'A', + 'cite': 'A', + 'class': 'A', + 'classid': 'A', + 'code': 'A', + 'codebase': 'A', + 'color': 'A', + 'cols': 'A', + 'colspan': 'A', + 'content': 'A', + 'data': 'A', + 'defer': 'A', + 'dir': 'A', + 'face': 'A', + 'for': 'A', + 'frame': 'A', + 'frameborder': 'A', + 'headers': 'A', + 'height': 'A', + 'http-equiv': 'A', + 'href': 'A', + 'id': 'A', + 'label': 'A', + 'lang': 'A', + 'language': 'A', + 'link': 'A', + 'marginheight': 'A', + 'marginwidth': 'A', + 'maxlength': 'A', + 'media': 'A', + 'multiple': 'A', + 'name': 'A', + 'object': '!A', + 'onblur': '!A', + 'onchange': '!A', + 'onclick': '!A', + 'onfocus': '!A', + 'onkeydown': '!A', + 'onkeypress': '!A', + 'onkeyup': '!A', + 'onload': '!A', + 'onmousedown': '!A', + 'onmousemove': '!A', + 'onmouseout': '!A', + 'onmouseover': '!A', + 'onmouseup': '!A', + 'onreset': '!A', + 'onselect': '!A', + 'onsubmit': '!A', + 'property': 'A', + 'readonly': 'A', + 'rel': 'A', + 'rev': 'A', + 'role': 'A', + 'rows': 'A', + 'rowspan': 'A', + 'size': 'A', + 'span': 'EA', + 'src': 'A', + 'start': 'A', + 'style': 'A', + 'text': 'A', + 'target': 'A', + 'title': 'A', + 'type': 'A', + 'value': 'A', + 'width': 'A' + } }, 'angular-default-directives': { message: 'There was an AngularJS error in ', @@ -989,6 +1223,8 @@ module.exports = { 'ng-pristine': 'A', 'ng-readonly': 'A', 'ng-repeat': 'A', + 'ng-repeat-start': 'A', + 'ng-repeat-end': 'A', 'ng-required': 'A', 'ng-selected': 'A', 'ng-show': 'A', @@ -997,6 +1233,7 @@ module.exports = { 'ng-style': 'A', 'ng-submit': 'A', 'ng-switch': 'A', + 'ng-switch-when': 'A', 'ng-transclude': 'A', 'ng-true-value': 'A', 'ng-trim': 'A', @@ -1009,15 +1246,19 @@ module.exports = { } }, 'angular-custom-directives': { + message: 'There was an AngularJS error in ', + directives: {} + }, + 'angular-deprecated-directives': { message: 'There was an AngularJS error in ', directives: { - + 'ng-bind-html-unsafe': 'deprecated' } } } }; -},{}],17:[function(require,module,exports){ +},{}],21:[function(require,module,exports){ var areSimilarEnough = require('./areSimilarEnough'); var levenshteinDistance = require('./levenshtein'); @@ -1040,7 +1281,7 @@ module.exports = function(directiveTypeData, attribute) { closestMatch = ''; for(var directive in directiveTypeData){ - if(areSimilarEnough(attribute,directive)) { + if(min_levDist !== 1 && areSimilarEnough(attribute,directive)) { var currentlevDist = levenshteinDistance(attribute, directive); closestMatch = (currentlevDist < min_levDist)? directive : closestMatch; min_levDist = (currentlevDist < min_levDist)? currentlevDist : min_levDist; @@ -1049,7 +1290,7 @@ module.exports = function(directiveTypeData, attribute) { return {min_levDist: min_levDist, match: closestMatch}; }; -},{"./areSimilarEnough":6,"./levenshtein":29}],18:[function(require,module,exports){ +},{"./areSimilarEnough":7,"./levenshtein":33}],22:[function(require,module,exports){ var getFailedAttributesOfElement = require('./getFailedAttributesOfElement'); @@ -1058,7 +1299,7 @@ module.exports = function(scopeElements, options) { .filter(function(x) {return x;}); }; -},{"./getFailedAttributesOfElement":22}],19:[function(require,module,exports){ +},{"./getFailedAttributesOfElement":26}],23:[function(require,module,exports){ var ddLibData = require('./ddLib-data'); module.exports = function(dirName, attributes) { @@ -1066,24 +1307,28 @@ module.exports = function(dirName, attributes) { var directive = ddLibData.directiveTypes['angular-custom-directives'].directives[dirName]; var missing = []; if (directive && directive.require) { - for (var i = 0; i < directive.require.length; i++) { - if (attributes.indexOf(directive.require[i].directiveName) < 0) { - missing.push(directive.require[i].directiveName); + for (var i = 0, length = directive.require.length; i < length; i++) { + var dirName = directive.require[i].directiveName; + if (attributes.indexOf(dirName) < 0) { + missing.push(dirName); } } } return missing; }; -},{"./ddLib-data":16}],20:[function(require,module,exports){ -var hintLog = require('angular-hint-log'); +},{"./ddLib-data":20}],24:[function(require,module,exports){ +var hintLog = angular.hint = require('angular-hint-log'), + MODULE_NAME = 'Directives'; var build = { - wronguse: require('./buildWrongUse'), - nonexsisting: require('./buildNonExsisting'), + deprecated: require('./buildDeprecated'), missingrequired: require('./buildMissingRequired'), + mutuallyexclusive: require('./buildMutuallyExclusive'), ngevent: require('./buildNgEvent'), - mutuallyexclusive: require('./buildMutuallyExclusive') + ngrepeatformat: require('./buildNgRepeatFormat'), + nonexsisting: require('./buildNonExsisting'), + wronguse: require('./buildWrongUse') }; /** @@ -1095,21 +1340,22 @@ var build = { module.exports = function(failedElements) { failedElements.forEach(function(obj) { obj.data.forEach(function(info) { - var id = (obj.domElement.id) ? ' with id: #' + obj.domElement.id : ''; - var type = obj.domElement.nodeName; - var message = build[info.typeError](info, id, type); - hintLog.logMessage('##Directives## ' + message); + var id = (obj.domElement.id) ? ' with id: #' + obj.domElement.id : '', + type = obj.domElement.nodeName, + messageAndSeverity = build[info.typeError](info, id, type); + hintLog.logMessage(MODULE_NAME, messageAndSeverity[0], messageAndSeverity[1]); }); }); }; -},{"./buildMissingRequired":8,"./buildMutuallyExclusive":9,"./buildNgEvent":11,"./buildNonExsisting":12,"./buildWrongUse":14,"angular-hint-log":33}],21:[function(require,module,exports){ +},{"./buildDeprecated":9,"./buildMissingRequired":10,"./buildMutuallyExclusive":11,"./buildNgEvent":13,"./buildNgRepeatFormat":14,"./buildNonExsisting":15,"./buildWrongUse":17,"angular-hint-log":37}],25:[function(require,module,exports){ var normalizeAttribute = require('./normalizeAttribute'); var ddLibData = require('./ddLib-data'); var isMutExclusiveDir = require('./isMutExclusiveDir'); var hasMutExclusivePair = require('./hasMutExclusivePair'); var attributeExsistsInTypes = require('./attributeExsistsInTypes'); var getSuggestions = require('./getSuggestions'); +var checkNgRepeatFormat = require('./checkNgRepeatFormat'); /** *@param attributes: [] of attributes from element (includes tag name of element, e.g. DIV, P, etc.) @@ -1121,7 +1367,18 @@ module.exports = function(attributes, options) { var failedAttrs = [], mutExPairFound = false; for (var i = 0; i < attributes.length; i++) { var attr = normalizeAttribute(attributes[i].nodeName); - var dirVal = ddLibData.directiveTypes['html-directives'].directives[attr] || ''; + var dirVal = ddLibData.directiveTypes['html-directives'].directives[attr] || + ddLibData.directiveTypes['angular-deprecated-directives'].directives[attr] || ''; + + if(dirVal === 'deprecated') { + failedAttrs.push({ + error: attr, + directiveType: 'angular-deprecated-directives', + typeError: 'deprecated' + }); + } + + //if attr is a event attr. Html event directives are prefixed with ! in ddLibData if (dirVal.indexOf('!') > -1) { failedAttrs.push({ error: attr, @@ -1139,7 +1396,21 @@ module.exports = function(attributes, options) { mutExPairFound = true; continue; } + var attrVal = attributes[i].value || ''; + if(attr === 'ng-repeat') { + var result = checkNgRepeatFormat(attrVal); + if(result) { + failedAttrs.push({ + error: attr, + suggestion: result, + directiveType: 'angular-default-directives', + typeError: 'ngrepeatformat' + }); + } + } + var result = attributeExsistsInTypes(attr,options); + var suggestion = result.typeError === 'nonexsisting' ? getSuggestions(attr, options) : {match: ''}; @@ -1155,8 +1426,7 @@ module.exports = function(attributes, options) { } return failedAttrs; }; - -},{"./attributeExsistsInTypes":7,"./ddLib-data":16,"./getSuggestions":24,"./hasMutExclusivePair":25,"./isMutExclusiveDir":28,"./normalizeAttribute":30}],22:[function(require,module,exports){ +},{"./attributeExsistsInTypes":8,"./checkNgRepeatFormat":18,"./ddLib-data":20,"./getSuggestions":28,"./hasMutExclusivePair":29,"./isMutExclusiveDir":32,"./normalizeAttribute":34}],26:[function(require,module,exports){ var getFailedAttributes = require('./getFailedAttributes'); var findMissingAttrs = require('./findMissingAttrs'); @@ -1180,8 +1450,10 @@ module.exports = function(options, element) { }); var failedAttrs = getFailedAttributes(eleAttrs, options); var missingRequired = findMissingAttrs(eleName, eleAttrs); - if(failedAttrs.length || missingRequired.length) { - if(missingRequired.length) { + var missingLength = missingRequired.length; + + if(failedAttrs.length || missingLength) { + if(missingLength) { failedAttrs.push({ directiveType: 'angular-custom-directive', missing: missingRequired, @@ -1196,14 +1468,20 @@ module.exports = function(options, element) { } }; -},{"./findMissingAttrs":19,"./getFailedAttributes":21}],23:[function(require,module,exports){ +},{"./findMissingAttrs":23,"./getFailedAttributes":25}],27:[function(require,module,exports){ module.exports = function(str) { - var customDirectives = [], pairs = []; - var matchScope = str.replace(/\n/g,'').match(/scope\s*:\s*{\s*[^}]*['"]\s*}/); - if (matchScope) { - matchScope[0].match(/\w+\s*:\s*['"][a-zA-Z=@&]+['"]/g).map(function(str){ - var temp = str.match(/(\w+)\s*:\s*['"](.+)['"]/); - pairs.push({key:temp[1],value:temp[2]}); + var customDirectives = [], + pairs = [], + SCOPE_REGEXP = /scope\s*:\s*{\s*[^}]*['"]\s*}/, + PROPERTY_REGEXP = /\w+\s*:\s*['"][a-zA-Z=@&]+['"]/g, + KEYVAL_REGEXP = /(\w+)\s*:\s*['"](.+)['"]/; + var matchScope = str.replace(/\n/g,'').match(SCOPE_REGEXP); + var propertiesMatch = matchScope ? matchScope[0].match(PROPERTY_REGEXP) : undefined; + + if (matchScope && propertiesMatch) { + propertiesMatch.map(function(str){ + var temp = str.match(KEYVAL_REGEXP); + pairs.push({key: temp[1], value: temp[2]}); }); pairs.forEach(function(pair){ var name = (['=', '@', '&'].indexOf(pair.value) !== -1)? pair.key : pair.value.substring(1); @@ -1213,7 +1491,7 @@ module.exports = function(str) { return customDirectives; }; -},{}],24:[function(require,module,exports){ +},{}],28:[function(require,module,exports){ var ddLibData = require('./ddLib-data'); var findClosestMatchIn = require('./findClosestMatchIn'); @@ -1247,7 +1525,7 @@ module.exports = function(attribute, options) { }; }; -},{"./ddLib-data":16,"./findClosestMatchIn":17}],25:[function(require,module,exports){ +},{"./ddLib-data":20,"./findClosestMatchIn":21}],29:[function(require,module,exports){ var isMutExclusiveDir = require('./isMutExclusiveDir'); module.exports = function(attr, attributes) { @@ -1258,17 +1536,24 @@ module.exports = function(attr, attributes) { }); }; -},{"./isMutExclusiveDir":28}],26:[function(require,module,exports){ +},{"./isMutExclusiveDir":32}],30:[function(require,module,exports){ +var dasherize = require('dasherize'); +var validate = require('validate-element-name'); + module.exports = function(str) { - return str.toLowerCase() !== str; + var dashStr = dasherize(str); + var validated = !validate(dashStr).message ? true : false; + //Check for message definition because validate-element-name returns true for things starting + //with ng-, polymer-, and data- but message is defined for those and errors. + return validated && str.toLowerCase() !== str; }; -},{}],27:[function(require,module,exports){ +},{"dasherize":38,"validate-element-name":39}],31:[function(require,module,exports){ module.exports = function(facStr) { return facStr.match(/replace\s*:/); }; -},{}],28:[function(require,module,exports){ +},{}],32:[function(require,module,exports){ module.exports = function (dirName) { var exclusiveDirHash = { 'ng-show' : 'ng-hide', @@ -1279,7 +1564,7 @@ module.exports = function (dirName) { return exclusiveDirHash[dirName]; }; -},{}],29:[function(require,module,exports){ +},{}],33:[function(require,module,exports){ /** *@param s: first string to compare for Levenshtein Distance. *@param t: second string to compare for Levenshtein Distance. @@ -1325,7 +1610,7 @@ module.exports = function(s, t) { return d[n][m]; }; -},{}],30:[function(require,module,exports){ +},{}],34:[function(require,module,exports){ /** *@param attribute: attribute name before normalization as string * e.g. 'data-ng-click', 'width', 'x:ng:src', etc. @@ -1336,7 +1621,7 @@ module.exports = function(attribute) { return attribute.replace(/^(?:data|x)[-_:]/,'').replace(/[:_]/g,'-'); }; -},{}],31:[function(require,module,exports){ +},{}],35:[function(require,module,exports){ var formatResults = require('./formatResults'); var findFailedElements = require('./findFailedElements'); @@ -1344,7 +1629,8 @@ var setCustomDirectives = require('./setCustomDirectives'); var defaultTypes = [ 'html-directives', 'angular-default-directives', - 'angular-custom-directives' + 'angular-custom-directives', + 'angular-deprecated-directives' ]; @@ -1370,7 +1656,7 @@ module.exports = function(scopeElements, customDirectives, options) { formatResults(failedElements); }; -},{"./findFailedElements":18,"./formatResults":20,"./setCustomDirectives":32}],32:[function(require,module,exports){ +},{"./findFailedElements":22,"./formatResults":24,"./setCustomDirectives":36}],36:[function(require,module,exports){ var ddLibData = require('../lib/ddLib-data'); module.exports = function(customDirectives) { @@ -1381,36 +1667,56 @@ module.exports = function(customDirectives) { }); }; -},{"../lib/ddLib-data":16}],33:[function(require,module,exports){ -var queuedMessages = {}; -function logMessage(message) { - var nameAndValue = message.split(/##/); - if(nameAndValue[0] !== '') { - if(queuedMessages['No Name']) { - queuedMessages['No Name'][message] = message; - } else { - queuedMessages['No Name'] = {}; - queuedMessages['No Name'][message] = message; - } - } else if(queuedMessages[nameAndValue[1]]) { - queuedMessages[nameAndValue[1]][nameAndValue[2]] = nameAndValue[2]; - } else { - queuedMessages[nameAndValue[1]] = {}; - queuedMessages[nameAndValue[1]][nameAndValue[2]] = nameAndValue[2]; +},{"../lib/ddLib-data":20}],37:[function(require,module,exports){ +/** +* HintLog creates a queue of messages logged by ngHint modules. This object +* has a key for each ngHint module that corresponds to the messages +* from that module. +*/ +var queuedMessages = {}, + MESSAGE_TYPES = [ + 'error', + 'warning', + 'suggestion' + ]; + +/** +* Add a message to the HintLog message queue. Messages are organized into categories +* according to their module name and severity. +**/ +function logMessage(moduleName, message, severity, category) { + // If no severity was provided, categorize the message as a `suggestion` + severity = severity || 3; + var messageType = MESSAGE_TYPES[severity - 1]; + + // If no ModuleName was found, categorize the message under `General` + moduleName = moduleName || 'General'; + + // If the category does not exist, initialize a new object + queuedMessages[moduleName] = queuedMessages[moduleName] || {}; + queuedMessages[moduleName][messageType] = queuedMessages[moduleName][messageType] || []; + + if (queuedMessages[moduleName][messageType].indexOf(message) < 0) { + queuedMessages[moduleName][messageType].push(message); } - module.exports.onMessage(message); -}; + module.exports.onMessage(moduleName, message, messageType, category); +} + +/** +* Return and empty the current queue of messages. +**/ function flush() { var flushMessages = queuedMessages; queuedMessages = {}; return flushMessages; -}; +} module.exports.onMessage = function(message) {}; module.exports.logMessage = logMessage; module.exports.flush = flush; -},{}],34:[function(require,module,exports){ + +},{}],38:[function(require,module,exports){ 'use strict'; var isArray = Array.isArray || function (obj) { @@ -1487,411 +1793,225 @@ module.exports = function (obj) { return walk(obj); }; -},{}],35:[function(require,module,exports){ +},{}],39:[function(require,module,exports){ 'use strict'; +var ncname = require('ncname'); + +var reservedNames = [ + 'annotation-xml', + 'color-profile', + 'font-face', + 'font-face-src', + 'font-face-uri', + 'font-face-format', + 'font-face-name', + 'missing-glyph' +]; -/** -* Create an interceptor that will log a message when use of a DOM API is detected -*/ -var domInterceptor = require('dom-interceptor'); -domInterceptor.enableLineNumbers(3); -var hintLog = angular.hint = require('angular-hint-log'); -var INTERCEPTOR_FUNCTION = function(message) { - hintLog.logMessage(message); -}; +function hasError(name) { + if (!name) { + return 'Missing element name.'; + } -/** -* Decorates $controller with a patching function to -* throw an error if DOM APIs are manipulated from -* within an Angular controller -*/ -angular.module('ngHintDom', []). - config(function ($provide) { - $provide.decorator('$controller', function($delegate, $injector) { + if (/[A-Z]/.test(name)) { + return 'Custom element names must not contain uppercase ASCII characters.'; + } - var patchedServices = {}; + if (name.indexOf('-') === -1) { + return 'Custom element names must contain a hyphen. Example: unicorn-cake'; + } - return function(ctrl, locals) { + if (/^\d/i.test(name)) { + return 'Custom element names must not start with a digit.'; + } - if(typeof ctrl === 'string') { - ctrl = nameToConstructorMappings[ctrl]; - } + if (/^-/i.test(name)) { + return 'Custom element names must not start with a hyphen.'; + } - var dependencies = $injector.annotate(ctrl); + // http://www.w3.org/TR/custom-elements/#concepts + if (!ncname.test(name)) { + return 'Invalid element name.'; + } - // patch methods on $scope - locals = locals || {}; - dependencies.forEach(function (dep) { - if (typeof dep === 'string' && !locals[dep]) { - locals[dep] = patchedServices[dep] || - (patchedServices[dep] = patchService($injector.get('$timeout'))); - } - }); + if (reservedNames.indexOf(name) !== -1) { + return 'The supplied element name is reserved and can\'t be used.\nSee: http://www.w3.org/TR/custom-elements/#concepts'; + } +} - function disallowedContext(fn) { - return function () { - domInterceptor.addManipulationListener(INTERCEPTOR_FUNCTION); - var ret = fn.apply(this, arguments); - domInterceptor.removeManipulationListener(); - return ret; - }; - } +function hasWarning(name) { + if (/^polymer-/i.test(name)) { + return 'Custom element names should not start with `polymer-`.\nSee: http://webcomponents.github.io/articles/how-should-i-name-my-element'; + } - function patchArguments (fn) { - return function () { - for (var i = 0, ii = arguments.length; i < ii; i++) { - if (typeof arguments[i] === 'function') { - arguments[i] = disallowedContext(arguments[i]); - } - } - return fn.apply(this, arguments); - }; - } + if (/^x-/i.test(name)) { + return 'Custom element names should not start with `x-`.\nSee: http://webcomponents.github.io/articles/how-should-i-name-my-element/'; + } - function patchService (obj) { - if (typeof obj === 'function') { - return patchArguments(obj); - } else if (typeof obj === 'object') { - return Object.keys(obj).reduce(function (obj, prop) { - return obj[prop] = patchService(obj[prop]), obj; - }, obj); - } - return obj; - } + if (/^ng-/i.test(name)) { + return 'Custom element names should not start with `ng-`.\nSee: http://docs.angularjs.org/guide/directive#creating-directives'; + } - //Detect manipulation of DOM APIs from within the body of the controller - domInterceptor.addManipulationListener(INTERCEPTOR_FUNCTION); - var ctrlInstance = $delegate.apply(this, [ctrl, locals]); - domInterceptor.removeManipulationListener(); + if (/^xml/i.test(name)) { + return 'Custom element names should not start with `xml`.'; + } - //Detect manipulation of DOM APIs from properties on the controller - Object.keys(ctrlInstance).forEach(function (prop) { - if (prop[0] !== '$' && typeof ctrlInstance[prop] === 'function') { - ctrlInstance[prop] = disallowedContext(ctrlInstance[prop]); - } - }); + if (/^[^a-z]/i.test(name)) { + return 'This element name is only valid in XHTML, not in HTML. First character should be in the range a-z.'; + } - //Detect manipulation of DOM APIs from functions defined inside the controller - if(locals.$scope) { - Object.keys(locals.$scope).forEach(function (prop) { - if([prop][0] !== '$' && typeof locals.$scope[prop] === 'function') { - locals.$scope[prop] = disallowedContext(locals.$scope[prop]); - } - }); - } + if (/[^a-z0-9]$/i.test(name)) { + return 'Custom element names should not end with a non-alpha character.'; + } - return ctrlInstance; - }; - }); - }); + if (/[\.]/.test(name)) { + return 'Custom element names should not contain a dot character as it would need to be escaped in a CSS selector.'; + } -/** -* Keep a record of 'ControllerName': Controller pairs -* so that a controller can be retrieved via its name -*/ -var nameToConstructorMappings = {}; -var originalAngularModule = angular.module; -angular.module = function() { - var module = originalAngularModule.apply(this, arguments); - var originalController = module.controller; - module.controller = function(controllerName, controllerConstructor) { - nameToConstructorMappings[controllerName] = controllerConstructor; - return originalController.apply(this, arguments); - }; - return module; + if (/[^\x20-\x7E]/.test(name)) { + return 'Custom element names should not contain non-ASCII characters.'; + } + + if (/--/.test(name)) { + return 'Custom element names should not contain consecutive hyphens.'; + } + + if (/[^a-z0-9]{2}/i.test(name)) { + return 'Custom element names should not contain consecutive non-alpha characters.'; + } +} + +module.exports = function (name) { + var errMsg = hasError(name); + + return { + isValid: !errMsg, + message: errMsg || hasWarning(name) + }; }; -},{"angular-hint-log":59,"dom-interceptor":36}],36:[function(require,module,exports){ +},{"ncname":40}],40:[function(require,module,exports){ 'use strict'; +var xmlChars = require('xml-char-classes'); -/** -* The DOM-interceptor should not throw errors because -* of its own access to the DOM. Within the interceptor -* the listener should have no behavior. -*/ -var _listener = function() {}; -var listener = savedListener; -var savedListener = function(message) {}; - -/** -* Initializes the listener to a function that is provided. -* The Element, Node, and Document prototypes are then patched to call -* this listener when DOM APIs are accessed. -**/ -function addManipulationListener(newListener) { - listener = _listener; - savedListener = newListener; - patchOnePrototype(Element, 'Element'); - patchOnePrototype(Node, 'Node'); - patchOnePrototype(Document, 'Document'); - listener = savedListener; +function getRange(re) { + return re.source.slice(1, -1); } -/** -* The interceptor should give a helpful message when manipulation is detected. -*/ -var explanation = 'Detected Manipulation of DOM API: '; +// http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName +module.exports = new RegExp('^[' + getRange(xmlChars.letter) + '_][' + getRange(xmlChars.letter) + getRange(xmlChars.digit) + '\\.\\-_' + getRange(xmlChars.combiningChar) + getRange(xmlChars.extender) + ']*$'); +},{"xml-char-classes":41}],41:[function(require,module,exports){ +exports.baseChar = /[A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\u0131\u0134-\u013E\u0141-\u0148\u014A-\u017E\u0180-\u01C3\u01CD-\u01F0\u01F4\u01F5\u01FA-\u0217\u0250-\u02A8\u02BB-\u02C1\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03D6\u03DA\u03DC\u03DE\u03E0\u03E2-\u03F3\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E-\u0481\u0490-\u04C4\u04C7\u04C8\u04CB\u04CC\u04D0-\u04EB\u04EE-\u04F5\u04F8\u04F9\u0531-\u0556\u0559\u0561-\u0586\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0641-\u064A\u0671-\u06B7\u06BA-\u06BE\u06C0-\u06CE\u06D0-\u06D3\u06D5\u06E5\u06E6\u0905-\u0939\u093D\u0958-\u0961\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8B\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B36-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CDE\u0CE0\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60\u0D61\u0E01-\u0E2E\u0E30\u0E32\u0E33\u0E40-\u0E45\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD\u0EAE\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0F40-\u0F47\u0F49-\u0F69\u10A0-\u10C5\u10D0-\u10F6\u1100\u1102\u1103\u1105-\u1107\u1109\u110B\u110C\u110E-\u1112\u113C\u113E\u1140\u114C\u114E\u1150\u1154\u1155\u1159\u115F-\u1161\u1163\u1165\u1167\u1169\u116D\u116E\u1172\u1173\u1175\u119E\u11A8\u11AB\u11AE\u11AF\u11B7\u11B8\u11BA\u11BC-\u11C2\u11EB\u11F0\u11F9\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2126\u212A\u212B\u212E\u2180-\u2182\u3041-\u3094\u30A1-\u30FA\u3105-\u312C\uAC00-\uD7A3]/; -/** -* The listener should include the line where the users program gives an error -* if line numbers are enabled. Enabling line numbers requires giving a valid -* line of the stack trace in which the line number should appear. This is because -* using an arbitrary line of the stacktrace such as line might return the line within -* the interceptor where the listener was called. -*/ -var stackTraceLine; -function enableLineNumbers(stackTraceLocation) { - if(typeof stackTraceLocation === 'number' && !isNaN(stackTraceLocation)) { - stackTraceLine = stackTraceLocation; - } else { - throw new Error('Enabling line numbers requires an integer parameter of the stack trace line ' + - 'that should be given. Got: ' + stackTraceLocation); - } -} +exports.ideographic = /[\u3007\u3021-\u3029\u4E00-\u9FA5]/; -/** -* Finds the line number where access of a DOM API was detected -*/ -function findLineNumber() { - var e = new Error(); - var lineNum; - //Find the line in the user's program rather than in this service - if(e.stack) { - lineNum = e.stack.split('\n')[stackTraceLine]; - } else { - //In Safari, an error does not have a line number until it is thrown - try { - throw e; - } catch (e) { - lineNum = e.stack.split('\n')[stackTraceLine]; - } - } - lineNum = lineNum.split(' ')[1] || lineNum; - return lineNum; -} +exports.letter = /[A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\u0131\u0134-\u013E\u0141-\u0148\u014A-\u017E\u0180-\u01C3\u01CD-\u01F0\u01F4\u01F5\u01FA-\u0217\u0250-\u02A8\u02BB-\u02C1\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03D6\u03DA\u03DC\u03DE\u03E0\u03E2-\u03F3\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E-\u0481\u0490-\u04C4\u04C7\u04C8\u04CB\u04CC\u04D0-\u04EB\u04EE-\u04F5\u04F8\u04F9\u0531-\u0556\u0559\u0561-\u0586\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0641-\u064A\u0671-\u06B7\u06BA-\u06BE\u06C0-\u06CE\u06D0-\u06D3\u06D5\u06E5\u06E6\u0905-\u0939\u093D\u0958-\u0961\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8B\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B36-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CDE\u0CE0\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60\u0D61\u0E01-\u0E2E\u0E30\u0E32\u0E33\u0E40-\u0E45\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD\u0EAE\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0F40-\u0F47\u0F49-\u0F69\u10A0-\u10C5\u10D0-\u10F6\u1100\u1102\u1103\u1105-\u1107\u1109\u110B\u110C\u110E-\u1112\u113C\u113E\u1140\u114C\u114E\u1150\u1154\u1155\u1159\u115F-\u1161\u1163\u1165\u1167\u1169\u116D\u116E\u1172\u1173\u1175\u119E\u11A8\u11AB\u11AE\u11AF\u11B7\u11B8\u11BA\u11BC-\u11C2\u11EB\u11F0\u11F9\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2126\u212A\u212B\u212E\u2180-\u2182\u3007\u3021-\u3029\u3041-\u3094\u30A1-\u30FA\u3105-\u312C\u4E00-\u9FA5\uAC00-\uD7A3]/; -/** -* Object to preserve all the original properties -* that will be restored after patching. -**/ -var originalProperties = {}; +exports.combiningChar = /[\u0300-\u0345\u0360\u0361\u0483-\u0486\u0591-\u05A1\u05A3-\u05B9\u05BB-\u05BD\u05BF\u05C1\u05C2\u05C4\u064B-\u0652\u0670\u06D6-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0901-\u0903\u093C\u093E-\u094D\u0951-\u0954\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A02\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A70\u0A71\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0B01-\u0B03\u0B3C\u0B3E-\u0B43\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B82\u0B83\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C01-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C82\u0C83\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0D02\u0D03\u0D3E-\u0D43\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86-\u0F8B\u0F90-\u0F95\u0F97\u0F99-\u0FAD\u0FB1-\u0FB7\u0FB9\u20D0-\u20DC\u20E1\u302A-\u302F\u3099\u309A]/; + +exports.digit = /[0-9\u0660-\u0669\u06F0-\u06F9\u0966-\u096F\u09E6-\u09EF\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0BE7-\u0BEF\u0C66-\u0C6F\u0CE6-\u0CEF\u0D66-\u0D6F\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F29]/; + +exports.extender = /[\xB7\u02D0\u02D1\u0387\u0640\u0E46\u0EC6\u3005\u3031-\u3035\u309D\u309E\u30FC-\u30FE]/; +},{}],42:[function(require,module,exports){ +'use strict'; /** -* Helper function for patching one prototype. -* Saves the unaltered state of the prototype using collectUnalteredPrototypeProperties() -* and then patches the given prototype with a call to the listener. +* Load necessary functions from /lib into variables. */ -function patchOnePrototype(type, typeName) { - collectUnalteredPrototypeProperties(type, typeName); - listener = _listener; - if (!type || !type.prototype) { - throw new Error('collectPrototypeProperties() needs a .prototype to collect properties from. ' + - type + '.prototype is undefined.'); - } - var objectProperties = Object.getOwnPropertyNames(type.prototype); - objectProperties.forEach(function(prop) { - //Access of some prototype values may throw an error - var desc; - try { - desc = Object.getOwnPropertyDescriptor(type.prototype, prop); - } - catch(e) {} - if (desc) { - if (desc.configurable) { - if (desc.value) { - if (typeof desc.value === 'function') { - var originalValue = desc.value; - desc.value = function () { - listener(explanation + prop + (stackTraceLine ? ' ' + findLineNumber() : '')); - return originalValue.apply(this, arguments); - }; - } - } - Object.defineProperty(type.prototype, prop, desc); - } else if (desc.writable) { - try { - var original = type.prototype[prop]; - type.prototype[prop] = function () { - listener(explanation + prop + (stackTraceLine ? ' ' + findLineNumber() : '')); - return original.apply(this, arguments); - }; - } - catch (e) {} - } - } - }); - listener = savedListener; -} +var ngEventDirectives = require('./lib/getEventDirectives')(), + getEventAttribute = require('./lib/getEventAttribute'), + getFunctionNames = require('./lib/getFunctionNames'), + formatResults = require('./lib/formatResults'); /** -* Helper method to collect all properties of a given prototype. -* When patching is removed, all prototype properties -* are set back to these original values -**/ -function collectUnalteredPrototypeProperties(type, typeName) { - listener = _listener; - if(!type || !type.prototype) { - throw new Error('collectUnalteredPrototypeProperties() needs a .prototype to collect properties' + - ' from. ' + type + '.prototype is undefined.'); - } else if(!typeName) { - throw new Error('typeName is required to save properties, got: ' + typeName); - } - var objectProperties = {}; - var objectPropertyNames = Object.getOwnPropertyNames(type.prototype); - objectPropertyNames.forEach(function(prop) { - //Access of some prototype values may throw an error - try { - objectProperties[prop] = type.prototype[prop]; - } catch(e) {} - }); - listener = savedListener; - originalProperties[typeName] = objectProperties; - return objectProperties; -} - -/** -* Controls the unpatching process by unpatching the -* prototypes as well as disabling the patching of individual -* HTML elements and returning those patched elements to their -* original state. -**/ -function removeManipulationListener() { - listener = _listener; - unpatchOnePrototype(Element, 'Element'); - unpatchOnePrototype(Node, 'Node'); - unpatchOnePrototype(Document, 'Document'); - listener = savedListener; -} - -/** -* Helper function to unpatch one prototype. -* Sets all properties of the given type back to the -* original values that were collected. -**/ -function unpatchOnePrototype(type, typeName) { - listener = _listener; - if(!typeName) { - throw new Error('typeName must be the name used to save prototype properties. Got: ' + typeName); - } - var objectProperties = Object.getOwnPropertyNames(type.prototype); - objectProperties.forEach(function(prop) { - //Access of some prototype values may throw an error - try{ - var alteredElement = type.prototype[prop]; - if(typeof alteredElement === 'function') { - type.prototype[prop] = originalProperties[typeName][prop]; - } - } catch(e) {} - }); - listener = savedListener; -} - -module.exports.addManipulationListener = addManipulationListener; -module.exports.removeManipulationListener = removeManipulationListener; -module.exports.patchOnePrototype = patchOnePrototype; -module.exports.unpatchOnePrototype = unpatchOnePrototype; -module.exports.enableLineNumbers = enableLineNumbers; - - -},{}],37:[function(require,module,exports){ -'use strict'; - -var hintLog = angular.hint = require('angular-hint-log'); -var ngEventDirectives = require('./lib/getEventDirectives')(); - -var getEventAttribute = require('./lib/getEventAttribute'); -var getFunctionNames = require('./lib/getFunctionNames'); -var formatResults = require('./lib/formatResults'); - -angular.module('ngHintEvents',[]) - .config(['$provide',function($provide) { +* Decorate $provide in order to examine ng-event directives +* and hint about their effective use. +*/ +angular.module('ngHintEvents', []) + .config(['$provide', function($provide) { for(var directive in ngEventDirectives) { - var dirName = ngEventDirectives[directive]+'Directive'; - - $provide.decorator(dirName, ['$delegate', '$timeout', '$parse', - function($delegate, $timeout, $parse) { - - var original = $delegate[0].compile, falseBinds = [], messages = []; - - $delegate[0].compile = function(element, attrs, transclude) { - var eventAttrName = getEventAttribute(attrs.$attr); - var fn = $parse(attrs[eventAttrName]); - var messages = []; - return function ngEventHandler(scope, element, attrs) { - for(var attr in attrs.$attr) { - var boundFuncs = getFunctionNames(attrs[attr]); - boundFuncs.forEach(function(boundFn) { - if(ngEventDirectives[attr] && !(boundFn in scope)) { - messages.push({ - scope: scope, - element:element, - attrs: attrs, - boundFunc: boundFn - }); - } + try{ + $provide.decorator(dirName, ['$delegate', '$timeout', '$parse', + function($delegate, $timeout, $parse) { + + var original = $delegate[0].compile, falseBinds = [], messages = []; + + $delegate[0].compile = function(element, attrs, transclude) { + var angularAttrs = attrs.$attr; + var eventAttrName = getEventAttribute(angularAttrs); + var fn = $parse(attrs[eventAttrName]); + var messages = []; + return function ngEventHandler(scope, element, attrs) { + for(var attr in angularAttrs) { + var boundFuncs = getFunctionNames(attrs[attr]); + boundFuncs.forEach(function(boundFn) { + if(ngEventDirectives[attr] && !(boundFn in scope)) { + messages.push({ + scope: scope, + element:element, + attrs: attrs, + boundFunc: boundFn + }); + } + }); + } + element.on(eventAttrName.substring(2).toLowerCase(), function(event) { + scope.$apply(function() { + fn(scope, {$event:event}); + }); }); - } - element.on(eventAttrName.substring(2).toLowerCase(), function(event) { - scope.$apply(function() { - fn(scope, {$event:event}); - }); - }); - formatResults(messages); + formatResults(messages); + }; }; - }; - return $delegate; - }]); + return $delegate; + }]); + } catch(e) {} } }]); -},{"./lib/formatResults":40,"./lib/getEventAttribute":41,"./lib/getEventDirectives":42,"./lib/getFunctionNames":43,"angular-hint-log":47}],38:[function(require,module,exports){ -var getValidProps = require('./getValidProps'); -var getSuggestion = require('./getSuggestion'); +},{"./lib/formatResults":44,"./lib/getEventAttribute":45,"./lib/getEventDirectives":46,"./lib/getFunctionNames":47}],43:[function(require,module,exports){ +'use strict'; + +var getValidProps = require('./getValidProps'), + suggest = require('suggest-it'); module.exports = function addSuggestions(messages) { messages.forEach(function(messageObj) { - var props = getValidProps(messageObj.scope); - var suggestion = getSuggestion(messageObj.boundFunc, props); + var dictionary = getValidProps(messageObj.scope), + suggestion = suggest(dictionary)(messageObj.boundFunc); messageObj['match'] = suggestion; }); return messages; }; -},{"./getSuggestion":44,"./getValidProps":45}],39:[function(require,module,exports){ -module.exports = function areSimilarEnough(s,t) { - var strMap = {}, similarities = 0, STRICTNESS = .66; - if(Math.abs(s.length-t.length) > 3) { - return false; - } - s.split('').forEach(function(x){strMap[x] = x;}); - for (var i = t.length - 1; i >= 0; i--) { - similarities = strMap[t.charAt(i)] ? similarities + 1 : similarities; - } - return similarities >= t.length * STRICTNESS; -}; +},{"./getValidProps":48,"suggest-it":50}],44:[function(require,module,exports){ +'use strict'; -},{}],40:[function(require,module,exports){ -var hintLog = require('angular-hint-log'); -var addSuggestions = require('./addSuggestions'); +var hintLog = angular.hint = require('angular-hint-log'), + addSuggestions = require('./addSuggestions'), + MODULE_NAME = 'Events', + SEVERITY_ERROR = 1; module.exports = function formatResults(messages) { messages = addSuggestions(messages); if(messages.length) { messages.forEach(function(obj) { - var id = (obj.element[0].id) ? ' with id: #'+obj.element[0].id : ''; - var type = obj.element[0].nodeName; - var suggestion = obj.match ? ' (Try "'+obj.match+'")': ''; - var message = 'Variable "'+obj.boundFunc+'" called on '+type+' element'+id+' does not '+ - 'exist in that scope.'+suggestion+' Event directive found on ' + obj.element[0] + ' in ' + - obj.scope + ' scope.'; - hintLog.logMessage('##Events## ' + message); + var id = (obj.element[0].id) ? ' with id: #' + obj.element[0].id : '', + type = obj.element[0].nodeName, + suggestion = obj.match ? ' (Try "' + obj.match + '").': '.', + message = 'Variable "' + obj.boundFunc + '" called on ' + type + ' element' + id + + ' does not exist in that scope' + suggestion + ' Event directive found on "' + + obj.element[0].outerHTML + '".'; + hintLog.logMessage(MODULE_NAME, message, SEVERITY_ERROR); }); } }; -},{"./addSuggestions":38,"angular-hint-log":47}],41:[function(require,module,exports){ +},{"./addSuggestions":43,"angular-hint-log":51}],45:[function(require,module,exports){ +'use strict'; + var ngEventDirectives = require('./getEventDirectives')(); module.exports = function getEventAttribute(attrs) { @@ -1902,23 +2022,27 @@ module.exports = function getEventAttribute(attrs) { } }; -},{"./getEventDirectives":42}],42:[function(require,module,exports){ +},{"./getEventDirectives":46}],46:[function(require,module,exports){ +'use strict'; + module.exports = function getEventDirectives() { - var list = 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '); + var list = 'click submit mouseenter mouseleave mousemove mousedown mouseover mouseup dblclick keyup keydown keypress blur focus submit cut copy paste'.split(' '); var eventDirHash = {}; - list.map(function(x){ - var name = 'ng'+x.charAt(0).toUpperCase()+x.substring(1); - eventDirHash[name]=name; + list.forEach(function(dirName) { + dirName = 'ng'+dirName.charAt(0).toUpperCase()+dirName.substring(1); + eventDirHash[dirName] = dirName; }); return eventDirHash; }; -},{}],43:[function(require,module,exports){ +},{}],47:[function(require,module,exports){ +'use strict'; + module.exports = function getFunctionNames(str) { - var results = str.replace(/\s+/g,'').split(/[\+\-\/\|\<\>\^=&!%~]/g).map(function(x){ + var results = str.replace(/\s+/g, '').split(/[\+\-\/\|\<\>\^=&!%~]/g).map(function(x) { if(isNaN(+x)) { if(x.match(/\w+\(.*\)$/)){ - return x.substring(0,x.indexOf('(')); + return x.substring(0, x.indexOf('(')); } return x; } @@ -1926,322 +2050,100 @@ module.exports = function getFunctionNames(str) { return results; }; -},{}],44:[function(require,module,exports){ -var areSimilarEnough = require('./areSimilarEnough'); -var levenshteinDistance = require('./levenshtein'); - -module.exports = function getSuggestion(original, props) { - var min_levDist = Infinity, closestMatch = ''; - for(var i in props) { - var prop = props[i]; - if(areSimilarEnough(original, prop)) { - var currentlevDist = levenshteinDistance(original, prop); - var closestMatch = (currentlevDist < min_levDist)? prop : closestMatch; - var min_levDist = (currentlevDist < min_levDist)? currentlevDist : min_levDist; - } - } - return closestMatch; -}; +},{}],48:[function(require,module,exports){ +'use strict'; -},{"./areSimilarEnough":39,"./levenshtein":46}],45:[function(require,module,exports){ module.exports = function getValidProps(obj) { var props = []; for(var prop in obj) { - if (prop.charAt(0) != '$' && typeof obj[prop] == 'function') { + if (prop.charAt(0) !== '$' && typeof obj[prop] === 'function') { props.push(prop); } } return props; }; -},{}],46:[function(require,module,exports){ -module.exports = function levenshteinDistance(s, t) { - if(typeof s !== 'string' || typeof t !== 'string') { - throw new Error('Function must be passed two strings, given: '+typeof s+' and '+typeof t+'.'); - } - var d = []; - var n = s.length; - var m = t.length; - - if (n == 0) return m; - if (m == 0) return n; - - for (var i = n; i >= 0; i--) d[i] = []; - for (var i = n; i >= 0; i--) d[i][0] = i; - for (var j = m; j >= 0; j--) d[0][j] = j; - for (var i = 1; i <= n; i++) { - var s_i = s.charAt(i - 1); - - for (var j = 1; j <= m; j++) { - if (i == j && d[i][j] > 4) return n; - var t_j = t.charAt(j - 1); - var cost = (s_i == t_j) ? 0 : 1; - var mi = d[i - 1][j] + 1; - var b = d[i][j - 1] + 1; - var c = d[i - 1][j - 1] + cost; - if (b < mi) mi = b; - if (c < mi) mi = c; - d[i][j] = mi; - if (i > 1 && j > 1 && s_i == t.charAt(j - 2) && s.charAt(i - 2) == t_j) { - d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost); - } - } - } - return d[n][m]; -}; - -},{}],47:[function(require,module,exports){ -module.exports=require(33) -},{}],48:[function(require,module,exports){ -'use strict'; - -var getAllParts = require('./lib/getAllParts'); -var buildMessage = require('./lib/buildMessage'); - -angular.module('ngHintInterpolation', []) - .config(['$provide', function($provide) { - var ngHintInterpMessages = []; - $provide.decorator('$interpolate', ['$delegate', '$timeout', function($delegate, $timeout) { - var interpolateWrapper = function() { - var interpolationFn = $delegate.apply(this, arguments); - if(interpolationFn) { - var parts = getAllParts(arguments[0],$delegate.startSymbol(),$delegate.endSymbol()); - var temp = interpolationFnWrap(interpolationFn,arguments, parts); - return temp; - } - }; - var interpolationFnWrap = function(interpolationFn, interpolationArgs, allParts) { - return function(){ - var result = interpolationFn.apply(this, arguments); - buildMessage(allParts, interpolationArgs[0].trim(), arguments[0], $timeout); - return result; - }; - }; - angular.extend(interpolateWrapper,$delegate); - return interpolateWrapper; - }]); - }]); - -},{"./lib/buildMessage":50,"./lib/getAllParts":52}],49:[function(require,module,exports){ -module.exports = function(s,t) { - var strMap = {}, similarities = 0, STRICTNESS = 0.66; - if(Math.abs(s.length-t.length) > 3) { - return false; - } - s.split('').forEach(function(x){strMap[x] = x;}); - for (var i = t.length - 1; i >= 0; i--) { - similarities = strMap[t.charAt(i)] ? similarities + 1 : similarities; - } - return similarities >= t.length * STRICTNESS; -}; - -},{}],50:[function(require,module,exports){ -var hintLog = angular.hint = require('angular-hint-log'); - -var partsEvaluate = require('./partsEvaluate'); - -module.exports = function(allParts, originalInterpolation, scope, $timeout) { - var message = partsEvaluate(allParts, originalInterpolation, scope); - if(message) { - hintLog.logMessage('##Interpolation## ' + message); - } -}; - -},{"./partsEvaluate":57,"angular-hint-log":58}],51:[function(require,module,exports){ -module.exports = function(parts, concatLength) { - var total = ''; - for(var i = 0; i <= concatLength; i++) { - var period = (i===0) ? '' : '.'; - total+=period+parts[i].trim(); - } - return total; -}; - -},{}],52:[function(require,module,exports){ -var getInterpolation = require('./getInterpolation'); -var getOperands = require('./getOperands'); -var concatParts = require('./concatParts'); - -module.exports = function(text, startSym, endSym) { - if(text.indexOf(startSym) < 0 || text.indexOf(endSym) < 0) { - throw new Error('Missing start or end symbol in interpolation. Start symbol: "'+startSym+ - '" End symbol: "'+endSym+'"'); - } - var comboParts = []; - var interpolation = getInterpolation(text, startSym, endSym); - var operands = getOperands(interpolation); - operands.forEach(function(operand) { - var opParts = operand.split('.'); - for(var i = 0; i < opParts.length; i++) { - var result = concatParts(opParts,i); - if(result && comboParts.indexOf(result) < 0 && isNaN(+result)){ - comboParts.push(result); +},{}],49:[function(require,module,exports){ +module.exports = distance; + +function distance(a, b) { + var table = []; + if (a.length === 0 || b.length === 0) return Math.max(a.length, b.length); + for (var ii = 0, ilen = a.length + 1; ii !== ilen; ++ii) { + table[ii] = []; + for (var jj = 0, jlen = b.length + 1; jj !== jlen; ++jj) { + if (ii === 0 || jj === 0) table[ii][jj] = Math.max(ii, jj); + else { + var diagPenalty = Number(a[ii-1] !== b[jj-1]); + var diag = table[ii - 1][jj - 1] + diagPenalty; + var top = table[ii - 1][jj] + 1; + var left = table[ii][jj - 1] + 1; + table[ii][jj] = Math.min(left, top, diag); } } - }); - return comboParts; -}; - -},{"./concatParts":51,"./getInterpolation":53,"./getOperands":54}],53:[function(require,module,exports){ -module.exports = function(text, startSym, endSym) { - var startInd = text.indexOf(startSym) + startSym.length; - var endInd = text.indexOf(endSym); - return text.substring(startInd, endInd); -}; - -},{}],54:[function(require,module,exports){ -module.exports = function(str) { - return str.split(/[\+\-\/\|<\>\^=&!%~]/g); -}; - -},{}],55:[function(require,module,exports){ -var areSimilarEnough = require('./areSimilarEnough'); -var levenshtein = require('./levenshtein'); - -module.exports = function (part, scope) { - var min_levDist = Infinity, closestMatch = ''; - for(var i in scope) { - if(areSimilarEnough(part, i)) { - var currentlevDist = levenshtein(part, i); - closestMatch = (currentlevDist < min_levDist)? i : closestMatch; - min_levDist = (currentlevDist < min_levDist)? currentlevDist : min_levDist; - } } - return closestMatch; -}; - -},{"./areSimilarEnough":49,"./levenshtein":56}],56:[function(require,module,exports){ -module.exports = function(s, t) { - if(typeof s !== 'string' || typeof t !== 'string') { - throw new Error('Function must be passed two strings, given: '+typeof s+' and '+typeof t+'.'); - } - var d = []; - var n = s.length; - var m = t.length; - - if (n === 0) {return m;} - if (m === 0) {return n;} + return table[a.length][b.length]; +} - for (var ii = n; ii >= 0; ii--) { d[ii] = []; } - for (var ii = n; ii >= 0; ii--) { d[ii][0] = ii; } - for (var jj = m; jj >= 0; jj--) { d[0][jj] = jj; } - for (var i = 1; i <= n; i++) { - var s_i = s.charAt(i - 1); - for (var j = 1; j <= m; j++) { - if (i == j && d[i][j] > 4) return n; - var t_j = t.charAt(j - 1); - var cost = (s_i == t_j) ? 0 : 1; - var mi = d[i - 1][j] + 1; - var b = d[i][j - 1] + 1; - var c = d[i - 1][j - 1] + cost; - if (b < mi) mi = b; - if (c < mi) mi = c; - d[i][j] = mi; - if (i > 1 && j > 1 && s_i == t.charAt(j - 2) && s.charAt(i - 2) == t_j) { - d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost); +},{}],50:[function(require,module,exports){ +module.exports = suggestDictionary; + +var distance = require('./levenstein_distance'); + +function suggestDictionary(dict, opts) { + opts = opts || {}; + var threshold = opts.threshold || 0.5; + return function suggest(word) { + var length = word.length; + return dict.reduce(function (result, dictEntry) { + var score = distance(dictEntry, word); + if (result.score > score && score / length < threshold) { + result.score = score; + result.word = dictEntry; } - } - } - return d[n][m]; -}; - -},{}],57:[function(require,module,exports){ -var getSuggestion = require('./getSuggestion'); - -module.exports = function(allParts, originalInterpolation, scope) { - var message, found = false; - allParts.forEach(function(part) { - if(!scope.$eval(part) && !found){ - found = true; - var perInd = part.lastIndexOf('.'); - var tempScope = (perInd > -1) ? scope.$eval(part.substring(0, perInd)) : scope; - var tempPart = part.substring(part.lastIndexOf('.') + 1); - var suggestion = getSuggestion(tempPart, tempScope); - suggestion = (suggestion) ? ' Try: "'+suggestion+'"' : ''; - message = '"'+part+'" was found to be undefined in "'+originalInterpolation+'".'+ suggestion; - } - }); - return message; -}; - -},{"./getSuggestion":55}],58:[function(require,module,exports){ -module.exports=require(33) -},{}],59:[function(require,module,exports){ -/** -* HintLog creates a queue of messages logged by ngHint modules. This object -* has a key for each ngHint module that corresponds to the messages -* from that module. -*/ -var queuedMessages = {}; - -/** -* Add a message to the HintLog message queue. Messages are organized into categories -* according to their module name which is included in the message with ##ModuleName##. -* If a ##ModuleName## is not included, the message is added to a `General` category -* in the queue. -**/ -function logMessage(message) { - //HintLog messages are delimited by `##ModuleName## Module Message` - //Split the message into the name and message value - var nameThenValue = message.split(/##/); - //If no ##ModuleName## was found, categorize the message under `General` - if(nameThenValue[0] !== '') { - //If the category does not exist, initialize a new object - queuedMessages.General = queuedMessages.General || {}; - queuedMessages.General[message] = message; - } else { - //Strip leading spaces in message caused by splitting out ##ModuleName## - nameThenValue[2] = nameThenValue[2].charAt(0) === ' ' ? nameThenValue[2].substring(1) - : nameThenValue[2]; - //If the category does not exist, initialize a new object - queuedMessages[nameThenValue[1]] = queuedMessages[nameThenValue[1]] || {}; - queuedMessages[nameThenValue[1]][nameThenValue[2]] = nameThenValue[2]; - } - module.exports.onMessage(message); + return result; + }, { score: Infinity }).word; + }; } -/** -* Return and empty the current queue of messages. -**/ -function flush() { - var flushMessages = queuedMessages; - queuedMessages = {}; - return flushMessages; -} +suggestDictionary.distance = distance; -module.exports.onMessage = function(message) {}; -module.exports.logMessage = logMessage; -module.exports.flush = flush; -},{}],60:[function(require,module,exports){ +},{"./levenstein_distance":49}],51:[function(require,module,exports){ +module.exports=require(5) +},{"/Users/bford/Development/angularjs-batarang/node_modules/angular-hint/node_modules/angular-hint-controllers/node_modules/angular-hint-log/hint-log.js":5}],52:[function(require,module,exports){ 'use strict'; -var hintLog = angular.hint = require('angular-hint-log'); -var storeDependencies = require('./lib/storeDependencies'); -var getModule = require('./lib/getModule'); -var start = require('./lib/start'); -var storeNgAppAndView = require('./lib/storeNgAppAndView'); -var storeUsedModules = require('./lib/storeUsedModules'); -var modData = require('./lib/moduleData'); +var storeDependencies = require('./lib/storeDependencies'), + getModule = require('./lib/getModule'), + start = require('./lib/start'), + storeNgAppAndView = require('./lib/storeNgAppAndView'), + storeUsedModules = require('./lib/storeUsedModules'), + hasNameSpace = require('./lib/hasNameSpace'), + modData = require('./lib/moduleData'); -var doc = Array.prototype.slice.call(document.getElementsByTagName('*')); -var originalAngularModule = angular.module; -var modules = {}; +var doc = Array.prototype.slice.call(document.getElementsByTagName('*')), + originalAngularModule = angular.module, + modules = {}; storeNgAppAndView(doc); -angular.module = function() { - var module = originalAngularModule.apply(this,arguments); - var name = module.name; +angular.module = function(name, requiresOriginal) { + var module = originalAngularModule.apply(this, arguments), + name = module.name; + + module.requiresOriginal = requiresOriginal; modules[name] = module; - var modToCheck = getModule(module.name, true); - if(modToCheck && modToCheck.requires.length && module.requires.length) { - if(!modData.createdMulti[module.name]) { - modData.createdMulti[module.name] = [getModule(module.name,true)]; + hasNameSpace(name); + var modToCheck = getModule(name, true); + + if(modToCheck && modToCheck.requiresOriginal !== module.requiresOriginal) { + if(!modData.createdMulti[name]) { + modData.createdMulti[name] = [getModule(name,true)]; } - modData.createdMulti[module.name].push(module); + modData.createdMulti[name].push(module); } - modData.createdModules[module.name] = module; + modData.createdModules[name] = module; return module; }; @@ -2251,136 +2153,142 @@ angular.module('ngHintModules', []).config(function() { start(); }); -},{"./lib/getModule":64,"./lib/moduleData":71,"./lib/start":74,"./lib/storeDependencies":75,"./lib/storeNgAppAndView":76,"./lib/storeUsedModules":77,"angular-hint-log":59}],61:[function(require,module,exports){ -module.exports = function(s,t) { - var strMap = {}, - similarities = 0, - STRICTNESS = 0.66; - if(Math.abs(s.length-t.length) > 3) { - return false; - } - s.split('').forEach(function(x){strMap[x] = x;}); - for (var i = t.length - 1; i >= 0; i--) { - similarities = strMap[t.charAt(i)] ? similarities + 1 : similarities; - } - return similarities >= t.length * STRICTNESS; -}; - -},{}],62:[function(require,module,exports){ -var hintLog = angular.hint = require('angular-hint-log'); +},{"./lib/getModule":55,"./lib/hasNameSpace":59,"./lib/moduleData":61,"./lib/start":64,"./lib/storeDependencies":65,"./lib/storeNgAppAndView":66,"./lib/storeUsedModules":67}],53:[function(require,module,exports){ +var hintLog = angular.hint = require('angular-hint-log'), + MODULE_NAME = 'Modules'; -module.exports = function(unusedModules) { - unusedModules.forEach(function(module) { - hintLog.logMessage('##Modules## ' + module.message); +module.exports = function(modules) { + modules.forEach(function(module) { + hintLog.logMessage(MODULE_NAME, module.message, module.severity); }); }; -},{"angular-hint-log":59}],63:[function(require,module,exports){ +},{"angular-hint-log":51}],54:[function(require,module,exports){ var modData = require('./moduleData'); + MODULE_NAME = 'Modules', + SEVERITY_WARNING = 2; module.exports = function() { var multiLoaded = []; for(var modName in modData.createdMulti) { - var message = 'Multiple modules with name "'+modName+'" are being created and they will overwrite each other.'; + var message = 'Multiple modules with name "' + modName + '" are being created and they will ' + + 'overwrite each other.'; var multi = modData.createdMulti[modName]; + var multiLength = multi.length; var details = { - existingModule: multi[multi.length - 1], - overwrittenModules: multi.slice(0,multi.length-1) + existingModule: multi[multiLength - 1], + overwrittenModules: multi.slice(0, multiLength - 1) }; - multiLoaded.push({module:details, message:message}); + multiLoaded + .push({module: details, message: message, name: MODULE_NAME, severity: SEVERITY_WARNING}); } return multiLoaded; }; -},{"./moduleData":71}],64:[function(require,module,exports){ +},{"./moduleData":61}],55:[function(require,module,exports){ var modData = require('./moduleData'); module.exports = function(moduleName, getCreated) { - return (getCreated)? modData.createdModules[moduleName] : modData.loadedModules[moduleName]; -}; - -},{"./moduleData":71}],65:[function(require,module,exports){ -module.exports = function(attrs) { + return (getCreated)? modData.createdModules[moduleName] : modData.loadedModules[moduleName]; +}; + +},{"./moduleData":61}],56:[function(require,module,exports){ +var hintLog = angular.hint = require('angular-hint-log'), + MODULE_NAME = 'Modules', + SEVERITY_ERROR = 1; + module.exports = function(attrs, ngAppFound) { + if(attrs['ng-app'] && ngAppFound) { + hintLog.logMessage(MODULE_NAME, 'ng-app may only be included once. The module "' + + attrs['ng-app'].value + '" was not used to bootstrap because ng-app was already included.', + SEVERITY_ERROR); + } return attrs['ng-app'] ? attrs['ng-app'].value : undefined; -}; + }; -},{}],66:[function(require,module,exports){ -var levenshteinDistance = require('./levenshtein'); -var areSimilarEnough = require('./areSimilarEnough'); -var modData = require('./moduleData'); -module.exports = function(module){ - var min_levDist = Infinity, - closestMatch = ''; - for(var createdModule in modData.createdModules) { - if(areSimilarEnough(createdModule, module)) { - var currentlevDist = levenshteinDistance(module, createdModule); - if(currentlevDist < 5) { - closestMatch = (currentlevDist < min_levDist)? createdModule : closestMatch; - min_levDist = (currentlevDist < min_levDist)? currentlevDist : min_levDist; - } - } - } - return closestMatch; -}; -},{"./areSimilarEnough":61,"./levenshtein":70,"./moduleData":71}],67:[function(require,module,exports){ -var getModule = require('./getModule'); -var getSuggestion = require('./getSuggestion'); +},{"angular-hint-log":51}],57:[function(require,module,exports){ +var getModule = require('./getModule'), + dictionary = Object.keys(require('./moduleData').createdModules), + suggest = require('suggest-it')(dictionary), + SEVERITY_ERROR = 1; module.exports = function(loadedModules) { var undeclaredModules = []; - for( var module in loadedModules) { + for(var module in loadedModules) { var cModule = getModule(module, true); if(!cModule) { - var match = getSuggestion(module); - var suggestion = (match) ? '; Try: "'+match+'"' : ''; - var message = 'Module "'+module+'" was loaded but does not exist'+suggestion+'.'; - undeclaredModules.push({module:null, message:message}); + var match = suggest(module), + suggestion = (match) ? '; Try: "'+match+'"' : '', + message = 'Module "'+module+'" was loaded but does not exist'+suggestion+'.'; + + undeclaredModules.push({module: null, message: message, severity: SEVERITY_ERROR}); } } return undeclaredModules; }; -},{"./getModule":64,"./getSuggestion":66}],68:[function(require,module,exports){ +},{"./getModule":55,"./moduleData":61,"suggest-it":69}],58:[function(require,module,exports){ var getModule = require('./getModule'); +var IGNORED = ['ngHintControllers', 'ngHintDirectives', 'ngHintDom', 'ngHintEvents', + 'ngHintInterpolation', 'ngHintModules', 'ngHintScopes', 'ng', 'ngLocale', 'protractorBaseModule_'], + SEVERITY_WARNING = 2; + module.exports = function(createdModules) { var unusedModules = []; for(var module in createdModules) { if(!getModule(module)) { - var cModule = createdModules[module]; - var message = 'Module "'+cModule.name+'" was created but never loaded.'; - unusedModules.push({module:cModule, message:message}); + var cModule = createdModules[module], + message = 'Module "' + cModule.name + '" was created but never loaded.'; + if(IGNORED.indexOf(cModule.name) === -1) { + unusedModules.push({module: cModule, message: message, severity: SEVERITY_WARNING}); + } } } return unusedModules; }; -},{"./getModule":64}],69:[function(require,module,exports){ +},{"./getModule":55}],59:[function(require,module,exports){ +var hintLog = angular.hint = require('angular-hint-log'), + MODULE_NAME = 'Modules', + SEVERITY_SUGGESTION = 3; + +module.exports = function(str) { + if (str === 'ng') { + return true; + } + if(str.toLowerCase() === str || str.charAt(0).toUpperCase() === str.charAt(0)) { + hintLog.logMessage(MODULE_NAME, 'The best practice for' + + ' module names is to use lowerCamelCase. Check the name of "' + str + '".', + SEVERITY_SUGGESTION); + return false; + } + return true; +}; + +},{"angular-hint-log":51}],60:[function(require,module,exports){ var normalizeAttribute = require('./normalizeAttribute'); module.exports = function(attrs) { - for(var i = 0; i < attrs.length; i++) { - if(normalizeAttribute(attrs[i].nodeName) === 'ng-view' - || attrs[i].value.indexOf('ng-view') > -1) { - return true; + for(var i = 0, length = attrs.length; i < length; i++) { + if(normalizeAttribute(attrs[i].nodeName) === 'ng-view' || + attrs[i].value.indexOf('ng-view') > -1) { + return true; } } }; -},{"./normalizeAttribute":73}],70:[function(require,module,exports){ -module.exports=require(56) -},{}],71:[function(require,module,exports){ +},{"./normalizeAttribute":63}],61:[function(require,module,exports){ module.exports = { - createdModules: {}, - createdMulti: {}, - loadedModules: {} - }; + createdModules: {}, + createdMulti: {}, + loadedModules: {} +}; -},{}],72:[function(require,module,exports){ -var modData = require('./moduleData'); -var getModule = require('./getModule'); +},{}],62:[function(require,module,exports){ +var modData = require('./moduleData'), + getModule = require('./getModule'); module.exports = function() { if(modData.ngViewExists && !getModule('ngRoute')) { @@ -2388,24 +2296,24 @@ module.exports = function() { } }; -},{"./getModule":64,"./moduleData":71}],73:[function(require,module,exports){ +},{"./getModule":55,"./moduleData":61}],63:[function(require,module,exports){ module.exports = function(attribute) { - return attribute.replace(/^(?:data|x)[-_:]/,'').replace(/[:_]/g,'-'); + return attribute.replace(/^(?:data|x)[-_:]/, '').replace(/[:_]/g, '-'); }; -},{}],74:[function(require,module,exports){ -var display = require('./display'); -var formatMultiLoaded = require('./formatMultiLoaded'); -var getUnusedModules = require('./getUnusedModules'); -var getUndeclaredModules = require('./getUndeclaredModules'); -var modData = require('./moduleData'); -var ngViewNoNgRoute = require('./ngViewNoNgRoute'); +},{}],64:[function(require,module,exports){ +var display = require('./display'), + formatMultiLoaded = require('./formatMultiLoaded'), + getUnusedModules = require('./getUnusedModules'), + getUndeclaredModules = require('./getUndeclaredModules'), + modData = require('./moduleData'), + ngViewNoNgRoute = require('./ngViewNoNgRoute'); module.exports = function() { - var unusedModules = getUnusedModules(modData.createdModules); - var undeclaredModules = getUndeclaredModules(modData.loadedModules); - var multiLoaded = formatMultiLoaded(); - var noNgRoute = ngViewNoNgRoute(); + var unusedModules = getUnusedModules(modData.createdModules), + undeclaredModules = getUndeclaredModules(modData.loadedModules), + multiLoaded = formatMultiLoaded(), + noNgRoute = ngViewNoNgRoute(); if(unusedModules.length || undeclaredModules.length || multiLoaded.length || noNgRoute) { var toSend = unusedModules.concat(undeclaredModules) .concat(multiLoaded); @@ -2416,7 +2324,7 @@ module.exports = function() { } }; -},{"./display":62,"./formatMultiLoaded":63,"./getUndeclaredModules":67,"./getUnusedModules":68,"./moduleData":71,"./ngViewNoNgRoute":72}],75:[function(require,module,exports){ +},{"./display":53,"./formatMultiLoaded":54,"./getUndeclaredModules":57,"./getUnusedModules":58,"./moduleData":61,"./ngViewNoNgRoute":62}],65:[function(require,module,exports){ var modData = require('./moduleData'); module.exports = function(module, isNgAppMod) { @@ -2432,11 +2340,11 @@ module.exports = function(module, isNgAppMod) { } }; -},{"./moduleData":71}],76:[function(require,module,exports){ -var getNgAppMod = require('./getNgAppMod'); -var inAttrsOrClasses = require('./inAttrsOrClasses'); -var storeDependencies = require('./storeDependencies'); -var modData = require('./moduleData'); +},{"./moduleData":61}],66:[function(require,module,exports){ +var getNgAppMod = require('./getNgAppMod'), + inAttrsOrClasses = require('./inAttrsOrClasses'), + storeDependencies = require('./storeDependencies'), + modData = require('./moduleData'); module.exports = function(doms) { var bothFound, @@ -2448,11 +2356,14 @@ module.exports = function(doms) { for(var i = 0; i < doms.length; i++) { elem = doms[i]; + var attributes = elem.attributes; isElemName = elem.nodeName.toLowerCase() === 'ng-view'; - isInAttrsOrClasses = inAttrsOrClasses(elem.attributes); + isInAttrsOrClasses = inAttrsOrClasses(attributes); ngViewFound = isElemName || isInAttrsOrClasses; - ngAppMod = getNgAppMod(elem.attributes); + + ngAppMod = getNgAppMod(attributes, modData.ngAppFound); + modData.ngAppFound = modData.ngAppFound || ngAppMod; if(ngAppMod) { storeDependencies(ngAppMod, true); @@ -2465,7 +2376,7 @@ module.exports = function(doms) { } }; -},{"./getNgAppMod":65,"./inAttrsOrClasses":69,"./moduleData":71,"./storeDependencies":75}],77:[function(require,module,exports){ +},{"./getNgAppMod":56,"./inAttrsOrClasses":60,"./moduleData":61,"./storeDependencies":65}],67:[function(require,module,exports){ var storeDependencies = require('./storeDependencies'); var storeUsedModules = module.exports = function(module, modules){ @@ -2476,5 +2387,387 @@ var storeUsedModules = module.exports = function(module, modules){ storeUsedModules(mod, modules); }); } +}; +},{"./storeDependencies":65}],68:[function(require,module,exports){ +module.exports=require(49) +},{"/Users/bford/Development/angularjs-batarang/node_modules/angular-hint/node_modules/angular-hint-events/node_modules/suggest-it/lib/levenstein_distance.js":49}],69:[function(require,module,exports){ +module.exports=require(50) +},{"./levenstein_distance":68,"/Users/bford/Development/angularjs-batarang/node_modules/angular-hint/node_modules/angular-hint-events/node_modules/suggest-it/lib/suggest-it.js":50}],70:[function(require,module,exports){ +'use strict'; + +var summarize = require('./lib/summarize-model'); +var hint = angular.hint = require('angular-hint-log'); +var debounceOn = require('debounce-on'); + +hint.emit = function () {}; + +module.exports = angular.module('ngHintScopes', []).config(['$provide', function ($provide) { + $provide.decorator('$rootScope', ['$delegate', '$parse', decorateRootScope]); + $provide.decorator('$compile', ['$delegate', decorateDollaCompile]); +}]); + +function decorateRootScope($delegate, $parse) { + + var perf = window.performance || { now: function () { return 0; } }; + + var scopes = {}, + watching = {}; + + var debouncedEmitModelChange = debounceOn(emitModelChange, 10, byScopeId); + + hint.watch = function (scopeId, path) { + path = typeof path === 'string' ? path.split('.') : path; + + if (!watching[scopeId]) { + watching[scopeId] = {}; + } + + for (var i = 1, ii = path.length; i <= ii; i += 1) { + var partialPath = path.slice(0, i).join('.'); + if (watching[scopeId][partialPath]) { + continue; + } + var get = gettterer(scopeId, partialPath); + var value = summarize(get()); + watching[scopeId][partialPath] = { + get: get, + value: value + }; + hint.emit('model:change', { + id: scopeId, + path: partialPath, + value: value + }); + } + }; + + hint.assign = function (scopeId, path, value) { + var scope; + if (scope = scopes[scopeId]) { + scope.$apply(function () { + return $parse(path).assign(scope, value); + }); + } + }; + + hint.inspectScope = function (scopeId) { + var scope; + if (scope = scopes[scopeId]) { + window.$scope = scope; + } + }; + + hint.unwatch = function (scopeId, unwatchPath) { + Object.keys(watching[scopeId]). + forEach(function (path) { + if (path.indexOf(unwatchPath) === 0) { + delete watching[scopeId][path]; + } + }); + }; + + var debouncedEmit = debounceOn(hint.emit, 10, function (params) { + return params.id + params.path; + }); + + + var scopePrototype = ('getPrototypeOf' in Object) ? + Object.getPrototypeOf($delegate) : $delegate.__proto__; + + // var _watch = scopePrototype.$watch; + // scopePrototype.$watch = function (watchExpression, reactionFunction) { + // var watchStr = humanReadableWatchExpression(watchExpression); + // var scopeId = this.$id; + // if (typeof watchExpression === 'function') { + // arguments[0] = function () { + // var start = perf.now(); + // var ret = watchExpression.apply(this, arguments); + // var end = perf.now(); + // hint.emit('scope:watch', { + // id: scopeId, + // watch: watchStr, + // time: end - start + // }); + // return ret; + // }; + // } else { + // var thatScope = this; + // arguments[0] = function () { + // var start = perf.now(); + // var ret = thatScope.$eval(watchExpression); + // var end = perf.now(); + // hint.emit('scope:watch', { + // id: scopeId, + // watch: watchStr, + // time: end - start + // }); + // return ret; + // }; + // } + + // if (typeof reactionFunction === 'function') { + // var applyStr = reactionFunction.toString(); + // arguments[1] = function () { + // var start = perf.now(); + // var ret = reactionFunction.apply(this, arguments); + // var end = perf.now(); + // hint.emit('scope:reaction', { + // id: this.$id, + // watch: watchStr, + // time: end - start + // }); + // return ret; + // }; + // } + + // return _watch.apply(this, arguments); + // }; + + + var _destroy = scopePrototype.$destroy; + scopePrototype.$destroy = function () { + var id = this.id; + + hint.emit('scope:destroy', { id: id }); + + delete scopes[id]; + delete watching[id]; + + return _destroy.apply(this, arguments); + }; + + + var _new = scopePrototype.$new; + scopePrototype.$new = function () { + var child = _new.apply(this, arguments); + + scopes[child.$id] = child; + watching[child.$id] = {}; + + hint.emit('scope:new', { parent: this.$id, child: child.$id }); + setTimeout(function () { + emitScopeElt(child); + }, 0); + return child; + }; + + function emitScopeElt (scope) { + var scopeId = scope.$id; + var elt = findElt(scopeId); + var descriptor = scopeDescriptor(elt, scope); + hint.emit('scope:link', { + id: scopeId, + descriptor: descriptor + }); + } + + function findElt (scopeId) { + var elts = document.querySelectorAll('.ng-scope'); + var elt, scope; + + for (var i = 0; i < elts.length; i++) { + elt = angular.element(elts[i]); + scope = elt.scope(); + if (scope.$id === scopeId) { + return elt; + } + } + } + + + var _digest = scopePrototype.$digest; + scopePrototype.$digest = function (fn) { + var start = perf.now(); + var ret = _digest.apply(this, arguments); + var end = perf.now(); + hint.emit('scope:digest', { id: this.$id, time: end - start }); + return ret; + }; + + + var _apply = scopePrototype.$apply; + scopePrototype.$apply = function (fn) { + var start = perf.now(); + var ret = _apply.apply(this, arguments); + var end = perf.now(); + hint.emit('scope:apply', { id: this.$id, time: end - start }); + debouncedEmitModelChange(this); + return ret; + }; + + + function gettterer (scopeId, path) { + if (path === '') { + return function () { + return scopes[scopeId]; + }; + } + var getter = $parse(path); + return function () { + return getter(scopes[scopeId]); + }; + } + + function emitModelChange (scope) { + var scopeId = scope.$id; + if (watching[scopeId]) { + Object.keys(watching[scopeId]).forEach(function (path) { + var model = watching[scopeId][path]; + var value = summarize(model.get()); + if (value !== model.value) { + hint.emit('model:change', { + id: scope.$id, + path: path, + oldValue: model.value, + value: value + }); + model.value = value; + } + }); + } + } + + hint.emit('scope:new', { + parent: null, + child: $delegate.$id + }); + scopes[$delegate.$id] = $delegate; + watching[$delegate.$id] = {}; + + return $delegate; +} + +function decorateDollaCompile ($delegate) { + var newCompile = function () { + var link = $delegate.apply(this, arguments); + + return function (scope) { + var elt = link.apply(this, arguments); + var descriptor = scopeDescriptor(elt, scope); + hint.emit('scope:link', { + id: scope.$id, + descriptor: descriptor + }); + return elt; + } + }; + + // TODO: test this + // copy private helpers like $$addScopeInfo + for (var prop in $delegate) { + if ($delegate.hasOwnProperty(prop)) { + newCompile[prop] = $delegate[prop]; + } + } + return newCompile; } -},{"./storeDependencies":75}]},{},[1]); + +function scopeDescriptor (elt, scope) { + var val, + types = [ + 'ng-app', + 'ng-controller', + 'ng-repeat', + 'ng-include' + ], + theseTypes = [], + type; + + if (elt) { + for (var i = 0; i < types.length; i++) { + type = types[i]; + if (val = elt.attr(type)) { + theseTypes.push(type + '="' + val + '"'); + } + } + } + if (theseTypes.length === 0) { + return 'scope.$id=' + scope.$id; + } else { + return theseTypes.join(' '); + } +} + +function byScopeId (scope) { + return scope.$id; +} + +function humanReadableWatchExpression (fn) { + if (fn.exp) { + fn = fn.exp; + } else if (fn.name) { + fn = fn.name; + } + return fn.toString(); +} + +},{"./lib/summarize-model":71,"angular-hint-log":72,"debounce-on":73}],71:[function(require,module,exports){ + +module.exports = function summarizeModel (model) { + + if (model instanceof Array) { + return JSON.stringify(model.map(summarizeProperty)); + } else if (typeof model === 'object') { + return JSON.stringify(Object. + keys(model). + filter(isAngularPrivatePropertyName). + reduce(shallowSummary, {})); + } else { + return model; + } + + function shallowSummary (obj, prop) { + obj[prop] = summarizeProperty(model[prop]); + return obj; + } +}; + +function isAngularPrivatePropertyName (key) { + return !(key[0] === '$' && key[1] === '$') && key !== '$parent' && key !== '$root'; +} + +// TODO: handle DOM nodes, fns, etc better. +function summarizeProperty (obj) { + return obj instanceof Array ? + { '~array-length': obj.length } : + obj === null ? + null : + typeof obj === 'object' ? + { '~object': true } : + obj; +} + +},{}],72:[function(require,module,exports){ +module.exports=require(5) +},{"/Users/bford/Development/angularjs-batarang/node_modules/angular-hint/node_modules/angular-hint-controllers/node_modules/angular-hint-log/hint-log.js":5}],73:[function(require,module,exports){ +module.exports = function debounceOn (fn, timeout, hash) { + var timeouts = {}; + + timeout = typeof timeout === 'number' ? timeout : (hash = timeout, 100); + hash = typeof hash === 'function' ? hash : defaultHash; + + return function () { + var key = hash.apply(null, arguments); + var args = arguments; + if (typeof timeouts[key] === 'undefined') { + timeouts[key] = setTimeout(function () { + delete timeouts[key]; + fn.apply(null, args); + }, timeout); + } + return function cancel () { + if (timeouts[key]) { + clearTimeout(timeouts[key]); + delete timeouts[key]; + return true; + } + return false; + }; + }; +}; + +function defaultHash () { + return Array.prototype.join.call(arguments, '::'); +} + +},{}]},{},[1]);