diff options
| -rwxr-xr-x | lib/jsl/jsl.default.conf | 4 | ||||
| -rw-r--r-- | scenario/widgets.html | 8 | ||||
| -rw-r--r-- | src/Angular.js | 88 | ||||
| -rw-r--r-- | src/Browser.js | 46 | ||||
| -rw-r--r-- | src/Compiler.js | 11 | ||||
| -rw-r--r-- | src/Formatters.js | 2 | ||||
| -rw-r--r-- | src/JSON.js | 8 | ||||
| -rw-r--r-- | src/Parser.js | 4 | ||||
| -rw-r--r-- | src/Resource.js | 6 | ||||
| -rw-r--r-- | src/Scope.js | 53 | ||||
| -rw-r--r-- | src/Widgets.js | 23 | ||||
| -rw-r--r-- | src/angular-bootstrap.js | 1 | ||||
| -rw-r--r-- | src/directives.js | 11 | ||||
| -rw-r--r-- | src/jqLite.js | 4 | ||||
| -rw-r--r-- | src/markups.js | 6 | ||||
| -rw-r--r-- | src/services.js | 27 | ||||
| -rw-r--r-- | test/BinderTest.js | 5 | ||||
| -rw-r--r-- | test/BrowserTest.js (renamed from test/UrlWatcherTest.js) | 10 | ||||
| -rw-r--r-- | test/CompilerSpec.js | 62 | ||||
| -rw-r--r-- | test/FormattersTest.js | 22 | ||||
| -rw-r--r-- | test/ParserTest.js | 2 | ||||
| -rw-r--r-- | test/ScopeSpec.js | 57 | ||||
| -rw-r--r-- | test/servicesSpec.js | 17 | ||||
| -rw-r--r-- | test/testabilityPatch.js | 30 | 
24 files changed, 262 insertions, 245 deletions
diff --git a/lib/jsl/jsl.default.conf b/lib/jsl/jsl.default.conf index f494a35c..fe4d0ea4 100755 --- a/lib/jsl/jsl.default.conf +++ b/lib/jsl/jsl.default.conf @@ -23,7 +23,7 @@  -missing_break                # missing break statement  +missing_break_for_last_case  # missing break statement for last case in switch  +comparison_type_conv         # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) -+inc_dec_within_stmt          # increment (++) and decrement (--) operators used as part of greater statement +-inc_dec_within_stmt          # increment (++) and decrement (--) operators used as part of greater statement  +useless_void                 # use of the void type may be unnecessary (void is always undefined)  +multiple_plus_minus          # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs  +use_of_label                 # use of label @@ -41,7 +41,7 @@  +useless_assign               # useless assignment  +ambiguous_nested_stmt        # block statements containing block statements should use curly braces to resolve ambiguity  +ambiguous_else_stmt          # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent) -+missing_default_case         # missing default case in switch statement +-missing_default_case         # missing default case in switch statement  +duplicate_case_in_switch     # duplicate case in switch statements  +default_not_at_end           # the default case is not at the end of the switch statement  +legacy_cc_not_understood     # couldn't understand control comment using /*@keyword@*/ syntax diff --git a/scenario/widgets.html b/scenario/widgets.html index 64e48c54..73674be9 100644 --- a/scenario/widgets.html +++ b/scenario/widgets.html @@ -4,8 +4,8 @@      <link rel="stylesheet" type="text/css" href="style.css"></link>      <script type="text/javascript" src="../src/angular-bootstrap.js#autobind"></script>    </head> -  <body ng-init="$window.$scope = $root"> -    <table ng-repeat="i in [0, 1]"> +  <body ng-init="$window.$scope = this"> +    <table>        <tr>          <th>Description</th>          <th>Test</th> @@ -14,7 +14,7 @@        <tr><th colspan="3">Input text field</th></tr>        <tr>          <td>basic</td> -        <td><input type="text" name="text.basic" /></td> +        <td><input type="text" name="text.basic" ng-required /></td>          <td>text.basic={{text.basic}}</td>        </tr>        <tr> @@ -70,7 +70,7 @@        </tr>        <tr><th colspan="3">Buttons</th></tr>        <tr> -        <td>ng-action</td> +        <td>ng-change<br/>ng-click</td>          <td>            <form ng-init="button.count = 0">             <input type="button" value="button" ng-change="button.count = button.count + 1"/> <br/> diff --git a/src/Angular.js b/src/Angular.js index d97c2282..86fb5291 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -1,50 +1,3 @@ - -////////////////////////////// -//UrlWatcher -////////////////////////////// - -function UrlWatcher(location) { -  this.location = location; -  this.delay = 25; -  this.setTimeout = function(fn, delay) { -   window.setTimeout(fn, delay); -  }; -  this.expectedUrl = location.href; -  this.listeners = []; -} - -UrlWatcher.prototype = { -  watch: function(fn){ -   this.listeners.push(fn); -  }, - -  start: function() { -   var self = this; -   (function pull () { -     if (self.expectedUrl !== self.location.href) { -       foreach(self.listeners, function(listener){ -         listener(self.location.href); -       }); -       self.expectedUrl = self.location.href; -     } -     self.setTimeout(pull, self.delay); -   })(); -  }, - -  set: function(url) { -   var existingURL = this.location.href; -   if (!existingURL.match(/#/)) -     existingURL += '#'; -   if (existingURL != url) -     this.location.href = url; -   this.existingURL = url; -  }, - -  get: function() { -   return this.location.href; -  } -}; -  ////////////////////////////////////  if (typeof document.getAttribute == 'undefined') @@ -53,9 +6,9 @@ if (typeof document.getAttribute == 'undefined')  if (!window['console']) window['console']={'log':noop, 'error':noop};  var consoleNode, -    PRIORITY_FIRST    = -99999; -    PRIORITY_WATCH    = -1000; -    PRIORITY_LAST     =  99999; +    PRIORITY_FIRST    = -99999, +    PRIORITY_WATCH    = -1000, +    PRIORITY_LAST     =  99999,      NOOP              = 'noop',      NG_ERROR          = 'ng-error',      NG_EXCEPTION      = 'ng-exception', @@ -74,16 +27,14 @@ var consoleNode,      angularFilter     = extensionMap(angular, 'filter'),      angularFormatter  = extensionMap(angular, 'formatter'),      angularService    = extensionMap(angular, 'service'), -    angularCallbacks  = extensionMap(angular, 'callbacks'), -    urlWatcher        = new UrlWatcher(window.location); +    angularCallbacks  = extensionMap(angular, 'callbacks');  function angularAlert(){    log(arguments); window.alert.apply(window, arguments); -}; +}  extend(angular, {    'compile': compile, -  'startUrlWatch': bind(urlWatcher, urlWatcher.start),    'copy': copy,    'extend': extend,    'foreach': foreach, @@ -166,7 +117,7 @@ function isFunction(value){ return typeof value == 'function';}  function isTextNode(node) { return nodeName(node) == '#text'; }  function lowercase(value){ return isString(value) ? value.toLowerCase() : value; }  function uppercase(value){ return isString(value) ? value.toUpperCase() : value; } -function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; }; +function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; }  function nodeName(element) { return (element[0] || element || {}).nodeName; }  function map(obj, iterator, context) {    var results = []; @@ -174,7 +125,7 @@ function map(obj, iterator, context) {      results.push(iterator.call(context, value, index, list));    });    return results; -}; +}  function size(obj) {    var size = 0;    if (obj) { @@ -289,7 +240,7 @@ function copy(source, destination){      });      return destination;    } -}; +}  function setHtml(node, html) {    if (isLeafNode(node)) { @@ -367,22 +318,10 @@ function merge(src, dst) {    }  } -function compile(element, config) { +function compile(element, parentScope, overrides) {    var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget); -      $element = jqLite(element), -      rootScope = createScope({ -        $element: $element, -        $config: extend({ -          'onUpdateView': noop, -          'server': "", -          'location': { -            'get':bind(urlWatcher, urlWatcher.get), -            'set':bind(urlWatcher, urlWatcher.set), -            'watch':bind(urlWatcher, urlWatcher.watch) -          } -        }, config || {}) -      }, serviceAdapter(angularService)); -  return compiler.compile($element)($element, rootScope); +      $element = jqLite(element); +  return compiler.compile($element)($element, parentScope, overrides);  }  ///////////////////////////////////////////////// @@ -404,11 +343,10 @@ function toKeyValue(obj) {      parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));    });    return parts.length ? parts.join('&') : ''; -}; +}  function angularInit(config){    if (config.autobind) { -    compile(window.document, config).$init(); +    compile(window.document, null, {'$config':config}).$init();    }  } - diff --git a/src/Browser.js b/src/Browser.js new file mode 100644 index 00000000..bdf57386 --- /dev/null +++ b/src/Browser.js @@ -0,0 +1,46 @@ + +////////////////////////////// +// Browser +////////////////////////////// + +function Browser(location) { +  this.location = location; +  this.delay = 25; +  this.setTimeout = function(fn, delay) { +   window.setTimeout(fn, delay); +  }; +  this.expectedUrl = location.href; +  this.listeners = []; +} + +Browser.prototype = { +  watchUrl: function(fn){ +   this.listeners.push(fn); +  }, + +  startUrlWatcher: function() { +   var self = this; +   (function pull () { +     if (self.expectedUrl !== self.location.href) { +       foreach(self.listeners, function(listener){ +         listener(self.location.href); +       }); +       self.expectedUrl = self.location.href; +     } +     self.setTimeout(pull, self.delay); +   })(); +  }, + +  setUrl: function(url) { +   var existingURL = this.location.href; +   if (!existingURL.match(/#/)) +     existingURL += '#'; +   if (existingURL != url) +     this.location.href = url; +   this.existingURL = url; +  }, + +  getUrl: function() { +   return this.location.href; +  } +}; diff --git a/src/Compiler.js b/src/Compiler.js index c9039928..dac4931f 100644 --- a/src/Compiler.js +++ b/src/Compiler.js @@ -1,5 +1,5 @@  /** - * Template provides directions an how to bind to a given element. += * Template provides directions an how to bind to a given element.   * It contains a list of init functions which need to be called to   * bind to a new instance of elements. It also provides a list   * of child paths which contain child templates @@ -43,7 +43,7 @@ Template.prototype = {    },    empty: function() { -    return this.inits.length == 0 && this.paths.length == 0; +    return this.inits.length === 0 && this.paths.length === 0;    }  }; @@ -63,8 +63,9 @@ Compiler.prototype = {      var template = this.templatize(rawElement) || new Template();      return function(element, parentScope){        element = jqLite(element); -      parentScope = parentScope || {}; -      var scope = createScope(parentScope); +      var scope = parentScope && parentScope.$eval ? +          parentScope : +          createScope(parentScope || {}, angularService);        return extend(scope, {          $element:element,          $init: function() { @@ -161,7 +162,7 @@ function eachNode(element, fn){  function eachAttribute(element, fn){    var i, attrs = element[0].attributes || [], size = attrs.length, chld, attr, attrValue = {};    for (i = 0; i < size; i++) { -    var attr = attrs[i]; +    attr = attrs[i];      attrValue[attr.name] = attr.value;    }    foreach(attrValue, fn); diff --git a/src/Formatters.js b/src/Formatters.js index c1ff82c6..ee63c1a5 100644 --- a/src/Formatters.js +++ b/src/Formatters.js @@ -1,5 +1,5 @@  function formater(format, parse) {return {'format':format, 'parse':parse || format};} -function toString(obj) {return isDefined(obj) ? "" + obj : obj;}; +function toString(obj) {return isDefined(obj) ? "" + obj : obj;}  extend(angularFormatter, {    'noop':formater(identity, identity),    'boolean':formater(toString, toBoolean), diff --git a/src/JSON.js b/src/JSON.js index 69e1b4c0..5c3e1043 100644 --- a/src/JSON.js +++ b/src/JSON.js @@ -4,11 +4,11 @@ function toJson(obj, pretty){    var buf = [];    toJsonArray(buf, obj, pretty ? "\n  " : null, []);    return buf.join(''); -}; +}  function toPrettyJson(obj)  {    return toJson(obj, true); -}; +}  function fromJson(json) {    if (!json) return json; @@ -21,7 +21,7 @@ function fromJson(json) {      error("fromJson error: ", json, e);      throw e;    } -}; +}  angular['toJson'] = toJson;  angular['fromJson'] = fromJson; @@ -102,4 +102,4 @@ function toJsonArray(buf, obj, pretty, stack){    if (typeof obj == "object") {      stack.pop();    } -}; +} diff --git a/src/Parser.js b/src/Parser.js index ec58295a..8e42f18a 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -4,7 +4,7 @@ function Lexer(text, parsStrings){    this.dateParseLength = parsStrings ? 20 : -1;    this.tokens = [];    this.index = 0; -}; +}  Lexer.OPERATORS = {      'null':function(self){return null;}, @@ -244,7 +244,7 @@ function Parser(text, parseStrings){    this.text = text;    this.tokens = new Lexer(text, parseStrings).parse();    this.index = 0; -}; +}  Parser.ZERO = function(){    return 0; diff --git a/src/Resource.js b/src/Resource.js index 27ce8aa9..9fe60788 100644 --- a/src/Resource.js +++ b/src/Resource.js @@ -15,7 +15,7 @@ Route.prototype = {      var self = this;      var url = this.template;      params = params || {}; -    foreach(this.urlParams, function(value, urlParam){ +    foreach(this.urlParams, function(_, urlParam){        var value = params[urlParam] || self.defaults[urlParam] || "";        url = url.replace(new RegExp(":" + urlParam + "(\\W)"), value + "$1");      }); @@ -57,7 +57,7 @@ ResourceFactory.prototype = {      function Resource(value){        copy(value || {}, this); -    }; +    }      foreach(actions, function(action, name){        var isGet = action.method == 'GET'; @@ -105,7 +105,7 @@ ResourceFactory.prototype = {            var params = {};            var callback = noop;            switch(arguments.length) { -          case 2: params = a1, callback = a2; +          case 2: params = a1; callback = a2;            case 1: if (typeof a1 == 'function') callback = a1; else params = a1;            case 0: break;            default: diff --git a/src/Scope.js b/src/Scope.js index 26a3f85b..52ab3ed7 100644 --- a/src/Scope.js +++ b/src/Scope.js @@ -26,7 +26,7 @@ function getter(instance, path) {      return bind(lastInstance, instance);    }    return instance; -}; +}  function setter(instance, path, value){    var element = path.split('.'); @@ -41,7 +41,7 @@ function setter(instance, path, value){    }    instance[element.shift()] = value;    return value; -}; +}  var compileCache = {};  function expressionCompile(exp){ @@ -54,7 +54,7 @@ function expressionCompile(exp){      compileCache[exp] = expFn;    }    return parserNewScopeAdapter(expFn); -}; +}  // return expFn  // TODO(remove this hack) @@ -85,21 +85,16 @@ function errorHandlerFor(element, error) {  }  var scopeId = 0; -function createScope(parent, Class) { +function createScope(parent, services, existing) {    function Parent(){}    function API(){}    function Behavior(){} -  var instance, behavior, api, evalLists = {}; -  if (isFunction(parent)) { -    Class = parent; -    parent = {}; -  } +  var instance, behavior, api, evalLists = {}, servicesCache = extend({}, existing); -  Class = Class || noop; -  parent = Parent.prototype = parent || {}; +  parent = Parent.prototype = (parent || {});    api = API.prototype = new Parent(); -  behavior = Behavior.prototype = extend(new API(), Class.prototype); +  behavior = Behavior.prototype = new API();    instance = new Behavior();    extend(api, { @@ -161,22 +156,28 @@ function createScope(parent, Class) {      }    }); -  if (isUndefined(instance.$root)) { -    behavior.$root = instance; -    behavior.$parent = instance; +  if (!parent.$root) { +    api.$root = instance; +    api.$parent = instance;    } -  (parent.$onEval || noop)(instance.$eval); -  Class.apply(instance, slice.call(arguments, 2, arguments.length)); +  function inject(name){ +    var service = getter(servicesCache, name), factory, args = []; +    if (isUndefined(service)) { +      factory = services[name]; +      if (!isFunction(factory)) +        throw "Don't know how to inject '" + name + "'."; +      foreach(factory.inject, function(dependency){ +        args.push(inject(dependency)); +      }); +      setter(servicesCache, name, service = factory.apply(instance, args)); +    } +    return service; +  } + +  foreach(services, function(_, name){ +    instance[name] = inject(name); +  });    return instance;  } - -function serviceAdapter(services) { -  return function(){ -    var self = this; -    foreach(services, function(service, name){ -      self[name] = service.call(self); -    }); -  }; -}; diff --git a/src/Widgets.js b/src/Widgets.js index a05ea63c..1e703a56 100644 --- a/src/Widgets.js +++ b/src/Widgets.js @@ -23,7 +23,7 @@ function valueAccessor(scope, element) {        validator = compileValidator(validatorName),        required = element.attr('ng-required'),        lastError; -  required = required || required == ''; +  required = required || required === '';    if (!validator) throw "Validator named '" + validatorName + "' not found.";    function validate(value) {      var error = required && !trim(value) ? "Required" : validator({self:scope, scope:{get:scope.$get, set:scope.$set}}, value); @@ -115,7 +115,7 @@ function radioInit(model, view, element) {   var modelValue = model.get(), viewValue = view.get(), input = element[0];   input.name = this.$id + '@' + input.name;   if (isUndefined(modelValue)) model.set(null); - if (viewValue != null) model.set(viewValue); + if (viewValue !== null) model.set(viewValue);  }  function inputWidget(events, modelAccessor, viewAccessor, initFn) { @@ -126,14 +126,17 @@ function inputWidget(events, modelAccessor, viewAccessor, initFn) {          action = element.attr('ng-change') || '';      initFn.call(scope, model, view, element);      this.$eval(element.attr('ng-init')||''); -    element.bind(events, function(){ -      model.set(view.get()); -      scope.$tryEval(action, element); -      scope.$root.$eval(); -      // if we have noop initFn than we are just a button, -      // therefore we want to prevent default action -      return initFn != noop; -    }); +    // Don't register a handler if we are a button (noopAccessor) and there is no action +    if (action || modelAccessor !== noopAccessor) { +      element.bind(events, function(){ +        model.set(view.get()); +        scope.$tryEval(action, element); +        scope.$root.$eval(); +        // if we have noop initFn than we are just a button, +        // therefore we want to prevent default action +        return initFn != noop; +      }); +    }      view.set(model.get());      scope.$watch(model.get, view.set);    }; diff --git a/src/angular-bootstrap.js b/src/angular-bootstrap.js index b0a3aa4f..7484b0c3 100644 --- a/src/angular-bootstrap.js +++ b/src/angular-bootstrap.js @@ -46,6 +46,7 @@    addScript("/jqlite.js");    addScript("/Parser.js");    addScript("/Resource.js"); +  addScript("/Browser.js");    // Extension points    addScript("/apis.js"); diff --git a/src/directives.js b/src/directives.js index de68360e..4f2916da 100644 --- a/src/directives.js +++ b/src/directives.js @@ -52,7 +52,8 @@ function compileBindTemplate(template){      };    }    return fn; -}; +} +  angularDirective("ng-bind-template", function(expression){    var templateFn = compileBindTemplate(expression);    return function(element) { @@ -120,7 +121,7 @@ angularWidget("@ng-repeat", function(expression, element){            assign(childScope = children[index]);          } else {            // grow children -          assign(childScope = template(element.clone(), currentScope)); +          assign(childScope = template(element.clone(), createScope(currentScope)));            lastElement.after(childScope.$element);            childScope.$index = index;            childScope.$element.attr('ng-repeat-index', index); @@ -144,7 +145,7 @@ angularDirective("ng-click", function(expression, element){      var self = this;      element.click(function(){        self.$tryEval(expression, element); -      self.$eval(); +      self.$root.$eval();        return false;      });    }; @@ -180,8 +181,8 @@ function ngClass(selector) {  }  angularDirective("ng-class", ngClass(function(){return true;})); -angularDirective("ng-class-odd", ngClass(function(i){return i % 2 == 0;})); -angularDirective("ng-class-even", ngClass(function(i){return i % 2 == 1;})); +angularDirective("ng-class-odd", ngClass(function(i){return i % 2 === 0;})); +angularDirective("ng-class-even", ngClass(function(i){return i % 2 === 1;}));  angularDirective("ng-show", function(expression, element){    return function(element){ diff --git a/src/jqLite.js b/src/jqLite.js index 3baafd51..ec77a6fb 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -5,7 +5,7 @@  var jqCache = {};  var jqName = 'ng-' + new Date().getTime();  var jqId = 1; -function jqNextId() { return jqId++; } +function jqNextId() { return (jqId++); }  var addEventListener = window.document.attachEvent ?      function(element, type, fn) { @@ -31,7 +31,7 @@ function jqClearData(element) {      delete jqCache[cacheId];      delete element[jqName];    } -}; +}  function JQLite(element) {    this[0] = element; diff --git a/src/markups.js b/src/markups.js index 3ae713fb..5c069f49 100644 --- a/src/markups.js +++ b/src/markups.js @@ -16,16 +16,16 @@ function parseBindings(string) {    if (lastIndex != string.length)      results.push(string.substr(lastIndex, string.length - lastIndex));    return results.length === 0 ? [ string ] : results; -}; +}  function binding(string) {    var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/);    return binding ? binding[1] : null; -}; +}  function hasBindings(bindings) {    return bindings.length > 1 || binding(bindings[0]) !== null; -}; +}  angularTextMarkup('{{}}', function(text, textNode, parentElement) {    var bindings = parseBindings(text), diff --git a/src/services.js b/src/services.js index fc12b22b..59c21d36 100644 --- a/src/services.js +++ b/src/services.js @@ -1,8 +1,11 @@  angularService("$window", bind(window, identity, window)); +angularService("$document", function(window){ +  return jqLite(window.document); +}, {inject:['$window']});  var URL_MATCH = /^(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?([\w\.]*)(:([0-9]+))?([^\?#]+)(\?([^#]*))?((#([^\?]*))?(\?([^\?]*))?)$/;  var DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21}; -angularService("$location", function(){ +angularService("$location", function(browser){    var scope = this;    function location(url){      if (isDefined(url)) { @@ -24,17 +27,29 @@ angularService("$location", function(){      return location.href +        (location.hashPath ? location.hashPath : '') +        (hashKeyValue ? '?' + hashKeyValue : ''); -  }; -  this.$config.location.watch(function(url){ +  } +  browser.watchUrl(function(url){      location(url);    }); -  location(this.$config.location.get()); +  location(browser.getUrl());    this.$onEval(PRIORITY_LAST, function(){      var href = location();      if (href != location.href) { -      scope.$config.location.set(location()); +      browser.setUrl(href);        location.href = href;      }    });    return location; -}); +}, {inject: ['$browser']}); + +if (!angularService['$browser']) { +  var browserSingleton; +  angularService('$browser', function browserFactory(){ +    if (!browserSingleton) { +      browserSingleton = new Browser(window.location); +      browserSingleton.startUrlWatcher(); +    } +    return browserSingleton; +  }); +} + diff --git a/test/BinderTest.js b/test/BinderTest.js index 9c5c5dc6..fa3127d7 100644 --- a/test/BinderTest.js +++ b/test/BinderTest.js @@ -19,6 +19,7 @@ BinderTest.prototype.tearDown = function(){    if (this.element && this.element.dealoc) this.element.dealoc();  }; +  BinderTest.prototype.testChangingTextfieldUpdatesModel = function(){    var state = this.compile('<input type="text" name="model.price" value="abc">', {model:{}});    state.scope.$eval(); @@ -707,7 +708,7 @@ BinderTest.prototype.testItShouldDisplayErrorWhenActionIsSyntacticlyIncorect = f    var second = jqLite(c.node[0].childNodes[1]);    first.click(); -  assertEquals("ABC", c.scope.$get('greeting')); +  assertEquals("ABC", c.scope.greeting);    second.click();    assertTrue(second.hasClass("ng-exception")); @@ -821,5 +822,3 @@ BinderTest.prototype.XtestWriteAnchorAsPartOfTheUpdateView = function(){    binder.updateView();    assertEquals(binder.location.get(), "a#a=b");  }; - - diff --git a/test/UrlWatcherTest.js b/test/BrowserTest.js index 6080ca62..2e630172 100644 --- a/test/UrlWatcherTest.js +++ b/test/BrowserTest.js @@ -1,11 +1,11 @@ -UrlWatcherTest = TestCase('UrlWatcherTest'); +BrowserTest = TestCase('BrowserTest'); -UrlWatcherTest.prototype.testUrlWatcher = function () { +BrowserTest.prototype.testUrlWatcher = function () {    expectAsserts(2);    var location = {href:"http://server", hash:""}; -  var watcher = new UrlWatcher(location); +  var watcher = new Browser(location);    watcher.delay = 1; -  watcher.watch(function(url){ +  watcher.watchUrl(function(url){      assertEquals('http://getangular.test', url);    });    watcher.setTimeout = function(fn, delay){ @@ -15,7 +15,7 @@ UrlWatcherTest.prototype.testUrlWatcher = function () {      };      fn();    }; -  watcher.start(); +  watcher.startUrlWatcher();  };  FunctionTest = TestCase("FunctionTest"); diff --git a/test/CompilerSpec.js b/test/CompilerSpec.js index 59b7ab6b..9922070f 100644 --- a/test/CompilerSpec.js +++ b/test/CompilerSpec.js @@ -1,8 +1,4 @@ -xdescribe('compiler', function(){ -  function element(html) { -    return jQuery(html)[0]; -  } - +describe('compiler', function(){    var compiler, textMarkup, directives, widgets, compile, log;    beforeEach(function(){ @@ -29,25 +25,25 @@ xdescribe('compiler', function(){      widgets = {};      compiler = new Compiler(textMarkup, attrMarkup, directives, widgets);      compile = function(html){ -      var e = element("<div>" + html + "</div>"); -      var view = compiler.compile(e)(e); -      view.init(); -      return view.scope; +      var e = jqLite("<div>" + html + "</div>"); +      var scope = compiler.compile(e)(e); +      scope.$init(); +      return scope;      };    });    it('should recognize a directive', function(){ -    var e = element('<div directive="expr" ignore="me"></div>'); +    var e = jqLite('<div directive="expr" ignore="me"></div>');      directives.directive = function(expression, element){        log += "found";        expect(expression).toEqual("expr"); -      expect(element[0]).toEqual(e); +      expect(element).toEqual(e);        return function initFn() {          log += ":init";        };      };      var template = compiler.compile(e); -    var init = template(e).init; +    var init = template(e).$init;      expect(log).toEqual("found");      init();      expect(log).toEqual("found:init"); @@ -61,13 +57,13 @@ xdescribe('compiler', function(){    it('should watch scope', function(){      var scope = compile('<span watch="name"/>');      expect(log).toEqual(""); -    scope.updateView(); -    scope.set('name', 'misko'); -    scope.updateView(); -    scope.updateView(); -    scope.set('name', 'adam'); -    scope.updateView(); -    scope.updateView(); +    scope.$eval(); +    scope.$set('name', 'misko'); +    scope.$eval(); +    scope.$eval(); +    scope.$set('name', 'adam'); +    scope.$eval(); +    scope.$eval();      expect(log).toEqual(":misko:adam");    }); @@ -83,29 +79,17 @@ xdescribe('compiler', function(){        element.removeAttr("duplicate");        var template = this.compile(element);        return function(marker) { -        this.$addEval(function() { +        this.$onEval(function() {            marker.after(template(element.clone()).element);          });        };      };      var scope = compile('before<span duplicate="expr">x</span>after'); -    expect($(scope.element).html()).toEqual('before<!--marker-->after'); -    scope.updateView(); -    expect($(scope.element).html()).toEqual('before<!--marker--><span>x</span>after'); -    scope.updateView(); -    expect($(scope.element).html()).toEqual('before<!--marker--><span>x</span><span>x</span>after'); -  }); - -  it('should allow for exculsive tags which suppress others', function(){ -    directives.exclusive = function(){ -      return function() { -        log += ('exclusive'); -      }; -    }; -    directives.exclusive.exclusive = true; - -    compile('<span hello="misko", exclusive/>'); -    expect(log).toEqual('exclusive'); +    expect(sortedHtml(scope.$element)).toEqual('<div>before<#comment></#comment>after</div>'); +    scope.$eval(); +    expect(sortedHtml(scope.$element)).toEqual('<div>before<#comment></#comment>after</div>'); +    scope.$eval(); +    expect(sortedHtml(scope.$element)).toEqual('<div>before<#comment></#comment>after</div>');    });    it('should process markup before directives', function(){ @@ -117,7 +101,7 @@ xdescribe('compiler', function(){        }      });      var scope = compile('before<span>middle</span>after'); -    expect(scope.element.innerHTML).toEqual('before<span hello="middle">replaced</span>after'); +    expect(scope.$element[0].innerHTML).toEqual('before<span hello="middle">replaced</span>after');      expect(log).toEqual("hello middle");    }); @@ -129,7 +113,7 @@ xdescribe('compiler', function(){        };      };      var scope = compile('<ng:button>push me</ng:button>'); -    expect(scope.element.innerHTML).toEqual('<div>button</div>'); +    expect(scope.$element[0].innerHTML).toEqual('<div>button</div>');      expect(log).toEqual('init');    }); diff --git a/test/FormattersTest.js b/test/FormattersTest.js index 59f12c1f..b520faf9 100644 --- a/test/FormattersTest.js +++ b/test/FormattersTest.js @@ -4,7 +4,7 @@ TestCase("formatterTest", {      assertEquals("xyz", angular.formatter.noop.parse("xyz"));      assertEquals(null, angular.formatter.noop.parse(null));    }, -   +    testList: function() {      assertEquals('a, b', angular.formatter.list.format(['a', 'b']));      assertEquals('', angular.formatter.list.format([])); @@ -12,26 +12,26 @@ TestCase("formatterTest", {      assertEquals([], angular.formatter.list.parse(""));      assertEquals([], angular.formatter.list.parse(null));    }, -   +    testBoolean: function() { -    assertEquals('true', angular.formatter.boolean.format(true)); -    assertEquals('false', angular.formatter.boolean.format(false)); -    assertEquals(true, angular.formatter.boolean.parse("true")); -    assertEquals(false, angular.formatter.boolean.parse("")); -    assertEquals(false, angular.formatter.boolean.parse("false")); -    assertEquals(null, angular.formatter.boolean.parse(null)); +    assertEquals('true', angular.formatter['boolean'].format(true)); +    assertEquals('false', angular.formatter['boolean'].format(false)); +    assertEquals(true, angular.formatter['boolean'].parse("true")); +    assertEquals(false, angular.formatter['boolean'].parse("")); +    assertEquals(false, angular.formatter['boolean'].parse("false")); +    assertEquals(null, angular.formatter['boolean'].parse(null));    }, -   +    testNumber: function() {      assertEquals('1', angular.formatter.number.format(1));      assertEquals(1, angular.formatter.number.format('1'));    }, -   +    testTrim: function() {      assertEquals('', angular.formatter.trim.format(null));      assertEquals('', angular.formatter.trim.format(""));      assertEquals('a', angular.formatter.trim.format(" a "));      assertEquals('a', angular.formatter.trim.parse(' a '));    } -   +  }); diff --git a/test/ParserTest.js b/test/ParserTest.js index 639e919f..6170dd4a 100644 --- a/test/ParserTest.js +++ b/test/ParserTest.js @@ -171,7 +171,7 @@ ParserTest.prototype.testComparison = function(){    assertEquals(scope.$eval("1>2"), 1>2);    assertEquals(scope.$eval("2>=1"), 2>=1); -  assertEquals(true==2<3, scope.$eval("true==2<3")); +  assertEquals(true === 2<3, scope.$eval("true==2<3"));  }; diff --git a/test/ScopeSpec.js b/test/ScopeSpec.js index 8d2a0ed4..a7322cae 100644 --- a/test/ScopeSpec.js +++ b/test/ScopeSpec.js @@ -65,20 +65,6 @@ describe('scope/model', function(){      expect(model.$bind(function(){return this.name;})()).toEqual('misko');    }); -  //$behavior -  it('should behave as class', function(){ -    function Printer(brand){ -      this.brand = brand; -    }; -    Printer.prototype.print = function(){ -      this.printed = true; -    }; -    var model = createScope({ name: 'parent' }, Printer, 'hp'); -    expect(model.brand).toEqual('hp'); -    model.print(); -    expect(model.printed).toEqual(true); -  }); -    //$tryEval    it('should report error on element', function(){      var scope = createScope(); @@ -108,16 +94,6 @@ describe('scope/model', function(){      expect(scope.log).toEqual('first;middle;last;');    }); -  // Services are initialized -  it("should inject services", function(){ -    var scope = createScope(serviceAdapter({ -      $window: function(){ -      return window; -    } -    })); -    expect(scope.$window).toEqual(window); -  }); -    it("should have $root and $parent", function(){      var parent = createScope();      var scope = createScope(parent); @@ -125,4 +101,37 @@ describe('scope/model', function(){      expect(scope.$parent).toEqual(parent);    }); +  // Service injection +  it('should inject services', function(){ +    var scope = createScope(null, { +      service:function(){ +        return "ABC"; +      } +    }); +    expect(scope.service).toEqual("ABC"); +  }); + +  it('should inject arugments', function(){ +    var scope = createScope(null, { +      name:function(){ +        return "misko"; +      }, +      greet: extend(function(name) { +        return 'hello ' + name; +      }, {inject:['name']}) +    }); +    expect(scope.greet).toEqual("hello misko"); +  }); + +  it('should throw error on missing dependency', function(){ +    try { +      createScope(null, { +        greet: extend(function(name) { +        }, {inject:['name']}) +      }); +    } catch(e) { +      expect(e).toEqual("Don't know how to inject 'name'."); +    } +   }); +  }); diff --git a/test/servicesSpec.js b/test/servicesSpec.js index b92975d0..49000af4 100644 --- a/test/servicesSpec.js +++ b/test/servicesSpec.js @@ -2,11 +2,7 @@ describe("services", function(){    var scope;    beforeEach(function(){ -    scope = createScope({ -      $config: { -        'location': {'get':noop, 'set':noop, 'watch':noop} -      } -    }, serviceAdapter(angularService)); +    scope = createScope(null, angularService, {});    });    it("should inject $window", function(){ @@ -46,4 +42,15 @@ describe("services", function(){      expect(scope.$location()).toEqual('file:///Users/Shared/misko/work/angular.js/scenario/widgets.html');    }); +  xit('should add stylesheets', function(){ +    scope.$document = { +      getElementsByTagName: function(name){ +        expect(name).toEqual('LINK'); +        return []; +      } +    }; +    scope.$document.addStyleSheet('css/angular.css'); + +  }); +  }); diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index 86d139cf..752f8ef2 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -3,7 +3,7 @@ dump = bind(jstd.console, jstd.console.log);  function nakedExpect(obj) {    return expect(angular.fromJson(angular.toJson(obj))); -}; +}  swfobject = {    createSwf:function() { @@ -27,15 +27,27 @@ function report(reportTest){    });  } -MockLocation = function() { +function MockBrowser() {    this.url = "http://server"; +  this.watches = []; +} +MockBrowser.prototype = { +  getUrl: function(){ +    return this.url; +  }, + +  setUrl: function(url){ +    this.url = url; +  }, + +  watchUrl: function(fn) { +    this.watches.push(fn); +  }  }; -MockLocation.prototype.get = function(){ -  return this.url; -}; -MockLocation.prototype.set = function(url){ -  this.url = url; -}; + +angularService('$browser', function(){ +  return new MockBrowser(); +});  function childNode(element, index) {    return jqLite(element[0].childNodes[index]); @@ -80,7 +92,7 @@ function sortedHtml(element) {      }    })(element[0]);    return html; -}; +}  function isVisible(node) {    var display = node.css('display');  | 
