Compare commits
No commits in common. 'master' and '0.2.0' have entirely different histories.
@ -1,3 +1,4 @@
|
||||
node_modules
|
||||
bower_components
|
||||
app/components
|
||||
components
|
||||
.idea
|
||||
|
@ -1,7 +0,0 @@
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- '0.10'
|
||||
|
||||
notifications:
|
||||
email: false
|
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Dmitri Akatov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -1,165 +0,0 @@
|
||||
|
||||
/**
|
||||
* we copied most of the 'element' scenario dsl so we can keep the old actions
|
||||
* and also add the 'enter' and modified 'html' actions.
|
||||
* @see https://github.com/angular/angular.js/blob/master/src/ngScenario/dsl.js
|
||||
* @see http://stackoverflow.com/questions/12575199/how-to-test-a-contenteditable-field-based-on-a-td-using-angularjs-e2e-testing
|
||||
*
|
||||
* Usage:
|
||||
* element(selector, label).count() get the number of elements that match selector
|
||||
* element(selector, label).click() clicks an element
|
||||
* element(selector, label).mouseover() mouseover an element
|
||||
* element(selector, label).mousedown() mousedown an element
|
||||
* element(selector, label).mouseup() mouseup an element
|
||||
* element(selector, label).query(fn) executes fn(selectedElements, done)
|
||||
* element(selector, label).{method}() gets the value (as defined by jQuery, ex. val)
|
||||
* element(selector, label).{method}(value) sets the value (as defined by jQuery, ex. val)
|
||||
* element(selector, label).{method}(key) gets the value (as defined by jQuery, ex. attr)
|
||||
* element(selector, label).{method}(key, value) sets the value (as defined by jQuery, ex. attr)
|
||||
* element(selector, label).enter(value) sets the text if the element is contenteditable
|
||||
*/
|
||||
angular.scenario.dsl('element', function() {
|
||||
var KEY_VALUE_METHODS = ['attr', 'css', 'prop'];
|
||||
var VALUE_METHODS = [
|
||||
'val', 'text', 'html', 'height', 'innerHeight', 'outerHeight', 'width',
|
||||
'innerWidth', 'outerWidth', 'position', 'scrollLeft', 'scrollTop', 'offset'
|
||||
];
|
||||
var chain = {};
|
||||
|
||||
chain.count = function() {
|
||||
return this.addFutureAction("element '" + this.label + "' count", function($window, $document, done) {
|
||||
try {
|
||||
done(null, $document.elements().length);
|
||||
} catch (e) {
|
||||
done(null, 0);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
chain.click = function() {
|
||||
return this.addFutureAction("element '" + this.label + "' click", function($window, $document, done) {
|
||||
var elements = $document.elements();
|
||||
var href = elements.attr('href');
|
||||
var eventProcessDefault = elements.trigger('click')[0];
|
||||
|
||||
if (href && elements[0].nodeName.toUpperCase() === 'A' && eventProcessDefault) {
|
||||
this.application.navigateTo(href, function() {
|
||||
done();
|
||||
}, done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
chain.dblclick = function() {
|
||||
return this.addFutureAction("element '" + this.label + "' dblclick", function($window, $document, done) {
|
||||
var elements = $document.elements();
|
||||
var href = elements.attr('href');
|
||||
var eventProcessDefault = elements.trigger('dblclick')[0];
|
||||
|
||||
if (href && elements[0].nodeName.toUpperCase() === 'A' && eventProcessDefault) {
|
||||
this.application.navigateTo(href, function() {
|
||||
done();
|
||||
}, done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
chain.mouseover = function() {
|
||||
return this.addFutureAction("element '" + this.label + "' mouseover", function($window, $document, done) {
|
||||
var elements = $document.elements();
|
||||
elements.trigger('mouseover');
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
chain.mousedown = function() {
|
||||
return this.addFutureAction("element '" + this.label + "' mousedown", function($window, $document, done) {
|
||||
var elements = $document.elements();
|
||||
elements.trigger('mousedown');
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
chain.mouseup = function() {
|
||||
return this.addFutureAction("element '" + this.label + "' mouseup", function($window, $document, done) {
|
||||
var elements = $document.elements();
|
||||
elements.trigger('mouseup');
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
chain.query = function(fn) {
|
||||
return this.addFutureAction('element ' + this.label + ' custom query', function($window, $document, done) {
|
||||
fn.call(this, $document.elements(), done);
|
||||
});
|
||||
};
|
||||
|
||||
angular.forEach(KEY_VALUE_METHODS, function(methodName) {
|
||||
chain[methodName] = function(name, value) {
|
||||
var args = arguments,
|
||||
futureName = (args.length == 1)
|
||||
? "element '" + this.label + "' get " + methodName + " '" + name + "'"
|
||||
: "element '" + this.label + "' set " + methodName + " '" + name + "' to " + "'" + value + "'";
|
||||
|
||||
return this.addFutureAction(futureName, function($window, $document, done) {
|
||||
var element = $document.elements();
|
||||
done(null, element[methodName].apply(element, args));
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
angular.forEach(VALUE_METHODS, function(methodName) {
|
||||
chain[methodName] = function(value) {
|
||||
var args = arguments,
|
||||
futureName = (args.length == 0)
|
||||
? "element '" + this.label + "' " + methodName
|
||||
: futureName = "element '" + this.label + "' set " + methodName + " to '" + value + "'";
|
||||
|
||||
return this.addFutureAction(futureName, function($window, $document, done) {
|
||||
var element = $document.elements();
|
||||
done(null, element[methodName].apply(element, args));
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
// =============== These are the methods ================ \\
|
||||
chain.enter = function(value) {
|
||||
return this.addFutureAction("element '" + this.label + "' enter '" + value + "'", function($window, $document, done) {
|
||||
var element = $document.elements()
|
||||
if (element.is('[contenteditable=""]')
|
||||
|| (element.attr('contenteditable')
|
||||
&& element.attr('contenteditable').match(/true/i))) {
|
||||
element.text(value)
|
||||
element.trigger('input')
|
||||
}
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
chain.html = function(value) {
|
||||
var args = arguments,
|
||||
futureName = (args.length == 0)
|
||||
? "element '" + this.label + "' html"
|
||||
: futureName = "element '" + this.label + "' set html to '" + value + "'";
|
||||
return this.addFutureAction(futureName, function($window, $document, done) {
|
||||
var element = $document.elements();
|
||||
element.html.apply(element, args)
|
||||
if (args.length > 0
|
||||
&& (element.is('[contenteditable=""]')
|
||||
|| (element.attr('contenteditable')
|
||||
&& element.attr('contenteditable').match(/true/i)))) {
|
||||
element.trigger('input')
|
||||
}
|
||||
done(null, element.html.apply(element, args));
|
||||
});
|
||||
};
|
||||
|
||||
return function(selector, label) {
|
||||
this.dsl.using(selector, label);
|
||||
return chain;
|
||||
};
|
||||
});
|
@ -1,98 +1,55 @@
|
||||
/**
|
||||
* @see http://docs.angularjs.org/guide/concepts
|
||||
* @see http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController
|
||||
* @see https://github.com/angular/angular.js/issues/528#issuecomment-7573166
|
||||
*/
|
||||
|
||||
angular.module('contenteditable', [])
|
||||
.directive('contenteditable', ['$timeout', function($timeout) { return {
|
||||
restrict: 'A',
|
||||
require: '?ngModel',
|
||||
link: function(scope, element, attrs, ngModel) {
|
||||
// don't do anything unless this is actually bound to a model
|
||||
if (!ngModel) {
|
||||
return
|
||||
}
|
||||
|
||||
// options
|
||||
var opts = {}
|
||||
angular.forEach([
|
||||
'stripBr',
|
||||
'noLineBreaks',
|
||||
'selectNonEditable',
|
||||
'moveCaretToEndOnChange',
|
||||
], function(opt) {
|
||||
var o = attrs[opt]
|
||||
opts[opt] = o && o !== 'false'
|
||||
})
|
||||
|
||||
// view -> model
|
||||
element.bind('input', function(e) {
|
||||
scope.$apply(function() {
|
||||
var html, html2, rerender
|
||||
html = element.text()
|
||||
rerender = false
|
||||
if (opts.stripBr) {
|
||||
html = html.replace(/<br>$/, '')
|
||||
}
|
||||
if (opts.noLineBreaks) {
|
||||
html2 = html.replace(/<div>/g, '').replace(/<br>/g, '').replace(/<\/div>/g, '')
|
||||
if (html2 !== html) {
|
||||
rerender = true
|
||||
html = html2
|
||||
(function() {
|
||||
angular.module('contenteditable', []).directive('contenteditable', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elmt, attrs, ngModel) {
|
||||
var old_render;
|
||||
elmt.bind('input', function(e) {
|
||||
return scope.$apply(function() {
|
||||
var html;
|
||||
html = elmt.html();
|
||||
if (attrs.stripBr && attrs.stripBr !== "false" && html === '<br>') {
|
||||
html = '';
|
||||
}
|
||||
return ngModel.$setViewValue(html);
|
||||
});
|
||||
});
|
||||
old_render = ngModel.$render;
|
||||
ngModel.$render = function() {
|
||||
var el, el2, range, sel;
|
||||
if (old_render != null) {
|
||||
old_render();
|
||||
}
|
||||
ngModel.$setViewValue(html)
|
||||
if (rerender) {
|
||||
ngModel.$render()
|
||||
}
|
||||
if (html === '') {
|
||||
// the cursor disappears if the contents is empty
|
||||
// so we need to refocus
|
||||
$timeout(function(){
|
||||
element[0].blur()
|
||||
element[0].focus()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// model -> view
|
||||
var oldRender = ngModel.$render
|
||||
ngModel.$render = function() {
|
||||
var el, el2, range, sel
|
||||
if (!!oldRender) {
|
||||
oldRender()
|
||||
}
|
||||
element.html(ngModel.$viewValue || '')
|
||||
if (opts.moveCaretToEndOnChange) {
|
||||
el = element[0]
|
||||
range = document.createRange()
|
||||
sel = window.getSelection()
|
||||
elmt.html(ngModel.$viewValue || '');
|
||||
el = elmt.get(0);
|
||||
range = document.createRange();
|
||||
sel = window.getSelection();
|
||||
if (el.childNodes.length > 0) {
|
||||
el2 = el.childNodes[el.childNodes.length - 1]
|
||||
range.setStartAfter(el2)
|
||||
el2 = el.childNodes[el.childNodes.length - 1];
|
||||
range.setStartAfter(el2);
|
||||
} else {
|
||||
range.setStartAfter(el)
|
||||
range.setStartAfter(el);
|
||||
}
|
||||
range.collapse(true)
|
||||
sel.removeAllRanges()
|
||||
sel.addRange(range)
|
||||
range.collapse(true);
|
||||
sel.removeAllRanges();
|
||||
return sel.addRange(range);
|
||||
};
|
||||
if (attrs.selectNonEditable && attrs.selectNonEditable !== "false") {
|
||||
return elmt.click(function(e) {
|
||||
var range, sel, target;
|
||||
target = e.toElement;
|
||||
if (target !== this && angular.element(target).attr('contenteditable') === 'false') {
|
||||
range = document.createRange();
|
||||
sel = window.getSelection();
|
||||
range.setStartBefore(target);
|
||||
range.setEndAfter(target);
|
||||
sel.removeAllRanges();
|
||||
return sel.addRange(range);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (opts.selectNonEditable) {
|
||||
element.bind('click', function(e) {
|
||||
var range, sel, target
|
||||
target = e.toElement
|
||||
if (target !== this && angular.element(target).attr('contenteditable') === 'false') {
|
||||
range = document.createRange()
|
||||
sel = window.getSelection()
|
||||
range.setStartBefore(target)
|
||||
range.setEndAfter(target)
|
||||
sel.removeAllRanges()
|
||||
sel.addRange(range)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}}]);
|
||||
};
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
|
@ -1,24 +1,25 @@
|
||||
{
|
||||
"name": "angular-contenteditable",
|
||||
"version": "0.3.7",
|
||||
"main": "angular-contenteditable.js",
|
||||
"ignore": [
|
||||
".*",
|
||||
"README.md",
|
||||
"Gruntfile.coffee",
|
||||
"package.json",
|
||||
"components",
|
||||
"node_modules",
|
||||
"src",
|
||||
"test"
|
||||
],
|
||||
"devDependencies": {
|
||||
"angular": "*",
|
||||
"angular-mocks": "~1.0.5",
|
||||
"angular-scenario": "~1.0.5",
|
||||
"expect": "~0.2.0",
|
||||
"jquery": "~2.0.2",
|
||||
"bootstrap-css": "~2.3.2",
|
||||
"angular-bootstrap": "~0.4.0"
|
||||
{ "name": "angular-contenteditable"
|
||||
, "version": "0.2.0"
|
||||
, "main": "angular-contenteditable.js"
|
||||
, "ignore":
|
||||
[ ".*"
|
||||
, "README.md"
|
||||
, "Gruntfile.coffee"
|
||||
, "package.json"
|
||||
, "components"
|
||||
, "node_modules"
|
||||
, "src"
|
||||
, "test"
|
||||
]
|
||||
, "dependencies":
|
||||
{ "angular-unstable": "~1.1.5"
|
||||
}
|
||||
, "devDependencies":
|
||||
{ "angular-mocks": "~1.0.5"
|
||||
, "angular-scenario": "~1.0.5"
|
||||
, "angular-bootstrap": "~0.3.0"
|
||||
, "expect": "~0.2.0"
|
||||
, "jquery": "~2.0.2"
|
||||
, "bootstrap-css": "~2.3.2"
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
module.exports = (karma) ->
|
||||
toServe = for file in [
|
||||
'bower_components/**/*.css'
|
||||
'bower_components/*/*.js'
|
||||
'test/fixtures/**/*'
|
||||
]
|
||||
pattern: file
|
||||
watched: false
|
||||
included: false
|
||||
served: true
|
||||
|
||||
karma.set
|
||||
frameworks: ['ng-scenario']
|
||||
|
||||
preprocessors: '**/*.coffee': 'coffee'
|
||||
|
||||
files: [
|
||||
'test/e2e/**/*.coffee'
|
||||
'angular-contenteditable.js'
|
||||
'angular-contenteditable-scenario.js'
|
||||
].concat toServe
|
||||
|
||||
exclude: []
|
||||
|
||||
reporters: ['progress']
|
||||
|
||||
port: 9876
|
||||
|
||||
runnerPort: 9100
|
||||
|
||||
colors: true
|
||||
|
||||
logLevel: karma.LOG_INFO
|
||||
|
||||
autoWatch: true
|
||||
|
||||
browsers: ['Chrome']
|
||||
|
||||
captureTimeout: 60000
|
||||
|
||||
singleRun: false
|
@ -0,0 +1,41 @@
|
||||
angular.module('contenteditable', [])
|
||||
.directive('contenteditable', ->
|
||||
require: 'ngModel',
|
||||
link: (scope, elmt, attrs, ngModel) ->
|
||||
# view -> model
|
||||
elmt.bind 'input', (e) ->
|
||||
scope.$apply ->
|
||||
html = elmt.html()
|
||||
html = '' if attrs.stripBr && attrs.stripBr != "false" && html == '<br>'
|
||||
ngModel.$setViewValue(html)
|
||||
|
||||
# model -> view
|
||||
old_render = ngModel.$render # save for later
|
||||
ngModel.$render = ->
|
||||
old_render() if old_render?
|
||||
elmt.html(ngModel.$viewValue || '')
|
||||
# move cursor to the end
|
||||
el = elmt.get(0)
|
||||
range = document.createRange()
|
||||
sel = window.getSelection()
|
||||
if el.childNodes.length > 0
|
||||
el2 = el.childNodes[el.childNodes.length - 1]
|
||||
range.setStartAfter(el2)
|
||||
else
|
||||
range.setStartAfter(el)
|
||||
range.collapse(true)
|
||||
sel.removeAllRanges()
|
||||
sel.addRange(range)
|
||||
|
||||
# select whole sub-span if it has contenteditable="false"
|
||||
if attrs.selectNonEditable && attrs.selectNonEditable != "false"
|
||||
elmt.click (e) ->
|
||||
target = e.toElement
|
||||
if target != @ && angular.element(target).attr('contenteditable') == 'false'
|
||||
range = document.createRange()
|
||||
sel = window.getSelection()
|
||||
range.setStartBefore(target)
|
||||
range.setEndAfter(target)
|
||||
sel.removeAllRanges()
|
||||
sel.addRange(range)
|
||||
)
|
@ -0,0 +1,52 @@
|
||||
basePath = '..'
|
||||
|
||||
toServe = [
|
||||
'components/bootstrap-css/css/bootstrap.css'
|
||||
'components/jquery/jquery.js'
|
||||
'components/angular-unstable/angular.js'
|
||||
'components/angular-bootstrap/ui-bootstrap.js'
|
||||
'components/angular-bootstrap/ui-bootstrap-tpls.js'
|
||||
'angular-contenteditable.js'
|
||||
'test/fixtures/simple.html'
|
||||
'test/fixtures/typeahead1.html'
|
||||
'test/fixtures/typeahead2.html'
|
||||
'test/fixtures/typeahead3.html'
|
||||
'test/fixtures/states.json'
|
||||
'test/fixtures/img/ru.gif'
|
||||
'test/fixtures/img/gb.gif'
|
||||
'test/fixtures/img/us.gif'
|
||||
]
|
||||
|
||||
toServe =
|
||||
for file in toServe
|
||||
pattern: file
|
||||
watched: false
|
||||
included: false
|
||||
served: true
|
||||
|
||||
files = [
|
||||
ANGULAR_SCENARIO
|
||||
ANGULAR_SCENARIO_ADAPTER
|
||||
'src/**/*.coffee'
|
||||
'test/e2e/**/*.coffee'
|
||||
].concat toServe
|
||||
|
||||
exclude = []
|
||||
|
||||
reporters = ['progress']
|
||||
|
||||
port = 9876
|
||||
|
||||
runnerPort = 9100
|
||||
|
||||
colors = true
|
||||
|
||||
logLevel = LOG_INFO
|
||||
|
||||
autoWatch = true
|
||||
|
||||
browsers = ['Chrome']
|
||||
|
||||
captureTimeout = 60000
|
||||
|
||||
singleRun = false
|
@ -0,0 +1,30 @@
|
||||
basePath = '..'
|
||||
|
||||
files =
|
||||
[ MOCHA
|
||||
, MOCHA_ADAPTER
|
||||
, 'components/expect/expect.js'
|
||||
, 'components/angular/angular.js'
|
||||
, 'src/**/*.coffee'
|
||||
, 'test/unit/*.coffee'
|
||||
]
|
||||
|
||||
exclude = []
|
||||
|
||||
reporters = ['progress']
|
||||
|
||||
port = 9876
|
||||
|
||||
runnerPort = 9100
|
||||
|
||||
colors = true
|
||||
|
||||
logLevel = LOG_INFO
|
||||
|
||||
autoWatch = true
|
||||
|
||||
browsers = ['Chrome']
|
||||
|
||||
captureTimeout = 60000
|
||||
|
||||
singleRun = false
|
@ -1,24 +1,78 @@
|
||||
describe 'module contenteditable', ->
|
||||
describe 'directive contenteditable', ->
|
||||
angular.scenario.dsl 'contenteditable', ->
|
||||
(name) ->
|
||||
@name = name
|
||||
enter: (value) ->
|
||||
@addFutureAction "contenteditable '#{@name}' enter '#{value}'"
|
||||
, ($window, $document, done) ->
|
||||
elmt = $document.elements @name
|
||||
elmt.text value
|
||||
elmt.trigger 'input'
|
||||
done()
|
||||
html: (args...) ->
|
||||
futureName =
|
||||
if args.length == 0
|
||||
"contenteditable '#{@name}' html"
|
||||
else
|
||||
"contenteditable '#{@name}' set html to '#{args[0]}'"
|
||||
@addFutureAction futureName
|
||||
, ($window, $document, done) ->
|
||||
elmt = $document.elements @name
|
||||
elmt.html args
|
||||
elmt.trigger 'input'
|
||||
done(null, elmt.html(args))
|
||||
|
||||
angular.scenario.dsl 'scope', ->
|
||||
(ctrl, arg = null) ->
|
||||
futureName =
|
||||
if !arg?
|
||||
"scope in Controller '#{ctrl}'"
|
||||
else if typeof arg == 'function'
|
||||
"executing 'scope.$apply(#{arg})' in Controller '#{ctrl}'"
|
||||
else
|
||||
"scope variable '#{arg}' in Controller '#{ctrl}'"
|
||||
@addFutureAction futureName
|
||||
, ($window, $document, done) ->
|
||||
elmt = $window.$ "[ng-controller='#{ctrl}']"
|
||||
return done("No Controller #{ctrl}") unless elmt?
|
||||
sc = elmt.scope()
|
||||
return done(null, sc) unless arg?
|
||||
if typeof arg == 'string'
|
||||
parts = arg.split '.'
|
||||
for p in parts
|
||||
sc = sc[p]
|
||||
done(null, sc)
|
||||
else if typeof arg == 'function'
|
||||
sc.$apply -> arg(sc)
|
||||
done()
|
||||
else
|
||||
done "don't understand argument #{arg}"
|
||||
|
||||
describe 'radians', ->
|
||||
describe 'contenteditable', ->
|
||||
describe 'simple application', ->
|
||||
beforeEach ->
|
||||
browser().navigateTo 'base/test/fixtures/simple.html'
|
||||
|
||||
it 'should update the model from the view (simple text)', ->
|
||||
element('#input').enter('abc')
|
||||
contenteditable('#input').enter('abc')
|
||||
expect(element('#input').html()).toBe 'abc'
|
||||
expect(scope('Ctrl', 'model')).toBe 'abc'
|
||||
expect(element('#output').html()).toBe 'abc'
|
||||
|
||||
it 'should update the model from the view (text with spans)', ->
|
||||
element('#input').html('abc <span style="color:red">red</span>')
|
||||
contenteditable('#input').html('abc <span style="color:red">red</span>')
|
||||
expect(scope('Ctrl', 'model')).toBe 'abc <span style="color:red">red</span>'
|
||||
expect(element('#input span').html()).toBe 'red'
|
||||
expect(element('#output').html()).toBe 'abc <span style="color:red">red</span>'
|
||||
|
||||
it 'should update the view from the model', ->
|
||||
input('model').enter('oops')
|
||||
expect(scope('Ctrl', 'model')).toBe 'Initial stuff <b>with bold</b> <em>and italic</em> yay'
|
||||
scope('Ctrl', ($scope) -> $scope.model = 'oops')
|
||||
expect(scope('Ctrl', 'model')).toBe 'oops'
|
||||
expect(element('#input').html()).toBe 'oops'
|
||||
expect(element('#output').html()).toBe 'oops'
|
||||
input('model').enter('a <span style="color:red">red</span> b')
|
||||
scope('Ctrl', ($scope) -> $scope.model = 'a <span style="color:red">red</span> b')
|
||||
expect(element('#input').html()).toBe 'a <span style="color:red">red</span> b'
|
||||
expect(element('#input span').html()).toBe 'red'
|
||||
expect(element('#output').html()).toBe 'a <span style="color:red">red</span> b'
|
||||
|
||||
## Why doesn't it work on this one??!!
|
||||
# expect(element('#output').html()).toBe 'oops'
|
||||
|
@ -1,34 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="simple">
|
||||
<head>
|
||||
<title>Simple</title>
|
||||
<!-- we need jquery for e2e testing -->
|
||||
<script src="../../bower_components/jquery/jquery.js"></script>
|
||||
<script src="../../bower_components/angular/angular.js"></script>
|
||||
<script src="../../angular-contenteditable.js"></script>
|
||||
<script>
|
||||
angular.module('simple', ['contenteditable'])
|
||||
.controller('Ctrl', function($scope) {
|
||||
// $scope.model2 = 'hello'
|
||||
$scope.model = "Initial stuff <b>with bold</b> <em>and italic</em> yay"
|
||||
window.scope = $scope
|
||||
})
|
||||
.controller('Ctrl2', function($scope) {})
|
||||
|
||||
angular.bootstrap(document, ['simple'])
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div ng-controller="Ctrl">
|
||||
<label>Contenteditable (View):</label>
|
||||
<span id="input" contenteditable="true" ng-model="model" no-line-breaks="true" style="display:block">
|
||||
something
|
||||
</span>
|
||||
<hr>
|
||||
<div>
|
||||
<label>Model:</label>
|
||||
<pre id="output" onclick="console.log('clicked the output')">{{ model }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,3 @@
|
||||
describe 'radians', ->
|
||||
describe 'contenteditable', ->
|
||||
it 'passes', ->
|
@ -1,97 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'tmpdir'
|
||||
require 'json'
|
||||
|
||||
puts 'running post-commit hook'
|
||||
|
||||
BOWER = JSON.parse File.read 'bower.json'
|
||||
|
||||
def commit
|
||||
@commit ||= `git log | head -1 | cut -d' ' -f2`
|
||||
end
|
||||
|
||||
# get version of library from bower.json
|
||||
def bower_version(library)
|
||||
BOWER["dependencies"][library] || BOWER["devDependencies"][library]
|
||||
end
|
||||
|
||||
# return the base url for a library from bower / github
|
||||
def library_base_url(library)
|
||||
# TODO: figure out how to clean the cache so we can use `bower --offline`
|
||||
(@library_base_url ||= {})[library] ||=
|
||||
JSON.parse(`bower lookup #{library} --json`)["url"]
|
||||
.sub(/^git:\/\/github.com/, 'https://rawgithub.com')
|
||||
.sub(/\.git$/, "/#{bower_version(library).sub(/~/, '')}/")
|
||||
end
|
||||
|
||||
# transform script ref to bower URL
|
||||
def script_url(src)
|
||||
if src =~ /\/bower_components\//
|
||||
parts = src.split('/bower_components/')[1].split('/')
|
||||
library_base_url(parts[0]) + parts.drop(1).join('/')
|
||||
else
|
||||
src.sub(/^\.\.\/\.\./,
|
||||
'https://rawgithub.com/akatov/angular-contenteditable/master')
|
||||
end
|
||||
end
|
||||
|
||||
# link href
|
||||
# script src
|
||||
def replace_script_and_link(contents)
|
||||
["script src", "link href"].reduce(contents) do |c, tag|
|
||||
c.gsub /#{tag}="([^"]*)"/ do
|
||||
"#{tag}=\"#{script_url($1)}\""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def index_header
|
||||
<<EOF
|
||||
<html>
|
||||
<head>
|
||||
<title>angular-contenteditable</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>angular contenteditable</h1>
|
||||
<h2>examples<h2>
|
||||
<ul>
|
||||
EOF
|
||||
end
|
||||
|
||||
def index_footer
|
||||
<<EOF
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
end
|
||||
|
||||
puts commit
|
||||
|
||||
def execute
|
||||
Dir.mktmpdir do |temp|
|
||||
FileUtils.cp_r 'test/fixtures/', temp
|
||||
FileUtils.mv "#{temp}/fixtures", "#{temp}/examples"
|
||||
File.open("#{temp}/index.html", File::CREAT | File::WRONLY) do |index_file|
|
||||
index_file.write index_header
|
||||
Dir.glob("#{temp}/examples/*.html").each do |file_name|
|
||||
bn = File.basename file_name
|
||||
puts "changing references in #{bn}"
|
||||
File.write file_name, replace_script_and_link(File.read file_name)
|
||||
index_file.write " <li><a href='examples/#{bn}'>#{bn}</a></li>\n"
|
||||
end
|
||||
index_file.write index_footer
|
||||
end
|
||||
`git checkout gh-pages`
|
||||
`git rm -r examples`
|
||||
['index.html', 'examples'].each do |f|
|
||||
FileUtils.cp_r "#{temp}/#{f}", '.'
|
||||
`git add #{f}`
|
||||
end
|
||||
`git commit --message "updating gh-pages for commit #{commit}"`
|
||||
`git checkout master`
|
||||
end
|
||||
end
|
||||
|
||||
execute
|
Reference in New Issue