Compare commits

..

No commits in common. 'master' and '0.2.0' have entirely different histories.

@ -1,3 +0,0 @@
{
"directory": "bower_components"
}

3
.gitignore vendored

@ -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

@ -5,20 +5,46 @@ module.exports = (grunt) ->
grunt.initConfig
pkg: grunt.file.readJSON 'package.json'
meta:
src: 'src'
test: 'test'
target: '<%= pkg.name %>.js'
banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
'<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
'* Copyright (c) <%= grunt.template.today("yyyy") %>' +
' <%= pkg.author.name %>;' +
' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n'
clean: target: src: ['<%= pkg.title %>.js']
coffeelint:
src:
files: src: ['<%= meta.src %>/**/*.coffee']
options: max_line_length: level: 'warn'
test:
files: src: ['<%= meta.src %>/**/*.coffee']
options: max_line_length: level: 'warn'
gruntfile:
files: src: ['Gruntfile.coffee']
coffee: src:
files:
'<%= meta.target %>': [
'<%= meta.src %>/**/*.coffee'
]
karma:
e2e: configFile: 'karma.coffee'
unit: configFile: 'test/config-unit.coffee'
e2e: configFile: 'test/config-e2e.coffee'
unit_ci:
configFile: 'test/config-unit.coffee'
singleRun: true
browsers: ['PhantomJS']
e2e_ci:
configFile: 'karma.coffee'
configFile: 'test/config-e2e.coffee'
singleRun: true
browsers: ['PhantomJS']
jshint:
src: ['angular-contenteditable.js']
options:
asi: true
jshint: target: ['<%= meta.target %>']
require('matchdep').filterDev('grunt-*').forEach grunt.loadNpmTasks
grunt.registerTask 'test', ['karma:e2e_ci']
grunt.registerTask 'lint', ['jshint']
grunt.registerTask 'default', ['lint' , 'test']
grunt.registerTask 'lint', ['coffeelint']
grunt.registerTask 'build', ['clean', 'coffee']
grunt.registerTask 'default', ['lint' , 'test', 'build', 'jshint']

@ -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,7 +1,4 @@
# angular-contenteditable
[![Build Status](https://img.shields.io/travis/akatov/angular-contenteditable.svg)](https://travis-ci.org/akatov/angular-contenteditable)
[![Dependency Status](https://img.shields.io/gemnasium/akatov/angular-contenteditable.svg)](https://gemnasium.com/akatov/angular-contenteditable)
[![endorse](https://api.coderwall.com/akatov/endorsecount.png)](https://coderwall.com/akatov)
An AngularJS directive to bind html tags with the `contenteditable` attribute to models.
@ -15,9 +12,8 @@ bower install angular-contenteditable
```javascript
angular.module('myapp', ['contenteditable'])
.controller('Ctrl', ['$scope', function($scope) {
$scope.model="<i>interesting</i> stuff"
}])
.controller('Ctrl', function($scope){
$scope.model="<i>interesting</i> stuff" })
```
```html
@ -25,29 +21,13 @@ angular.module('myapp', ['contenteditable'])
<span contenteditable="true"
ng-model="model"
strip-br="true"
select-non-editable="true">
</span>
</div>
select-non-editable="true"></span></div>
```
## Notice
The directive currently does not work in any version of Internet Explorer or Opera < 15.
Both browsers don't fire the `input` event for contenteditable fields.
In Chrome, when a contenteditable element X contains a non-contenteditable
element Y as the last element, then the behaviour of the caret is the following:
* When X has style `display` set to `block` or `inline-block`, then the caret
moves to the very far right edge of X when it is _immediately_ at the end of X
(inserting spaces moves the caret back).
* When X has style `display` set to `inline`, then the caret disappears instead.
## Development
```bash
npm install
bower install
grunt
grunt build
grunt karma:e2e
```

@ -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

@ -1,17 +1,12 @@
{
"name": "angular-contenteditable",
"version": "0.3.7",
"description": "angular model for the 'contenteditable' html attribute",
"repository": {
"type": "git",
"url": "https://github.com/akatov/angular-contenteditable.git"
},
"version": "0.2.0",
"description": "angular extensions",
"main": "angular-contenteditable.js",
"directories": {
"test": "test"
},
"scripts": {
"install": "bower install",
"test": "grunt test"
},
"repository": "",
@ -22,14 +17,19 @@
"author": "Dmitri Akatov",
"license": "BSD",
"devDependencies": {
"matchdep": "~0.3.0",
"matchdep": "~0.1.2",
"grunt": "~0.4.1",
"grunt-cli": "~0.1.13",
"grunt-contrib-jshint": "~0.7.1",
"grunt-karma": "~0.6.2",
"bower": "~1.3.5",
"karma": "~0.10.2",
"karma-ng-scenario": "0.1.0",
"karma-coffee-preprocessor": "0.1.0"
"grunt-contrib-concat": "~0.3.0",
"grunt-mocha-cli": "~1.0.6",
"should": "~1.2.2",
"grunt-coffee-redux": "~0.2.3",
"grunt-contrib-coffee": "~0.7.0",
"grunt-contrib-clean": "~0.4.1",
"grunt-contrib-watch": "~0.4.4",
"grunt-contrib-jshint": "~0.6.0",
"grunt-coffeelint": "0.0.6",
"coffee-script": "~1.6.3",
"karma": "~0.8.5",
"grunt-karma": "~0.4.4"
}
}

@ -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 &lt;span style="color:red"&gt;red&lt;/span&gt;'
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 &lt;span style="color:red"&gt;red&lt;/span&gt; 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>

@ -3,8 +3,8 @@
<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="../../components/jquery/jquery.js"></script>
<script src="../../components/angular-unstable/angular.js"></script>
<script src="../../angular-contenteditable.js"></script>
<script>
angular.module('simple', ['contenteditable'])

@ -2,38 +2,33 @@
<html ng-app="simple">
<head>
<title>Simple</title>
<script src="../../bower_components/angular/angular.js"></script>
<!-- we need jquery for e2e testing -->
<script src="../../components/jquery/jquery.js"></script>
<script src="../../components/angular-unstable/angular.js"></script>
<script src="../../angular-contenteditable.js"></script>
<script>
angular.module('simple', ['contenteditable'])
.controller('Ctrl', function($scope) {
$scope.model = "Initial stuff <b>with bold</b> <em>and italic</em> yay"
})
.controller('Ctrl', function($scope) {
$scope.model = "Initial stuff <b>with bold</b> <em>and italic</em> yay"
})
.controller('Ctrl2', function($scope) {})
angular.bootstrap(document, ['simple'])
</script>
</head>
<body>
<div ng-controller="Ctrl">
<label>Model (non-editable):</label>
<pre id="output">{{ model }}</pre>
<label>Contenteditable (View):</label>
<div id="input" contenteditable ng-model="model">
</div>
<hr>
<label>Contenteditable View (1):</label>
<div id="input" contenteditable ng-model="model"></div>
<hr>
<label>Contenteditable View (2):</label>
<div id="input2" contenteditable="true" ng-model="model"></div>
<hr>
<label>Contenteditable View (3):</label>
<div id="input3" contenteditable="TRUE" ng-model="model"></div>
<hr>
<label>non-contenteditable View:</label>
<div id="non-editable" contenteditable="false" ng-model="model"></div>
<hr>
<label>Input (edit verbatim)</label>
<input ng-model="model" style="width:100%">
<hr>
<label>Contenteditable without Model</label>
<div contenteditable>Edit me - I don't affect anything</div>
<div>
<label>Model:</label>
<pre id="output" onclick="console.log('clicked the output')">{{ model }}</pre>
</div>
</div>
<div ng-controller="Ctrl2">
The other controller
</div>
</body>
</html>

@ -3,8 +3,8 @@
<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="../../components/jquery/jquery.js"></script>
<script src="../../components/angular-unstable/angular.js"></script>
<script src="../../angular-contenteditable.js"></script>
<script>
angular.module('simple', ['contenteditable'])

@ -2,11 +2,11 @@
<html lang="en" ng-app="typeahead1">
<head>
<meta charset="utf-8">
<link href="../../bower_components/bootstrap-css/css/bootstrap.css" rel="stylesheet">
<script src="../../bower_components/jquery/jquery.js"></script>
<script src="../../bower_components/angular/angular.js"></script>
<script src="../../bower_components/angular-bootstrap/ui-bootstrap.js"></script>
<script src="../../bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<link href="../../components/bootstrap-css/css/bootstrap.css" rel="stylesheet">
<script src="../../components/jquery/jquery.js"></script>
<script src="../../components/angular-unstable/angular.js"></script>
<script src="../../components/angular-bootstrap/ui-bootstrap.js"></script>
<script src="../../components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<script src="../../angular-contenteditable.js"></script>
<script>
angular.module('typeahead1', ['ui.bootstrap', 'contenteditable'])

@ -2,11 +2,11 @@
<html lang="en" ng-app="typeahead2">
<head>
<meta charset="utf-8">
<link href="../../bower_components/bootstrap-css/css/bootstrap.css" rel="stylesheet">
<script src="../../bower_components/jquery/jquery.js"></script>
<script src="../../bower_components/angular/angular.js"></script>
<script src="../../bower_components/angular-bootstrap/ui-bootstrap.js"></script>
<script src="../../bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<link href="../../components/bootstrap-css/css/bootstrap.css" rel="stylesheet">
<script src="../../components/jquery/jquery.js"></script>
<script src="../../components/angular-unstable/angular.js"></script>
<script src="../../components/angular-bootstrap/ui-bootstrap.js"></script>
<script src="../../components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<script src="../../angular-contenteditable.js"></script>
<script>
angular.module('typeahead2', ['ui.bootstrap', 'contenteditable'])

@ -2,11 +2,11 @@
<html lang="en" ng-app="typeahead3">
<head>
<meta charset="utf-8">
<link href="../../bower_components/bootstrap-css/css/bootstrap.css" rel="stylesheet">
<script src="../../bower_components/jquery/jquery.js"></script>
<script src="../../bower_components/angular/angular.js"></script>
<script src="../../bower_components/angular-bootstrap/ui-bootstrap.js"></script>
<script src="../../bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<link href="../../components/bootstrap-css/css/bootstrap.css" rel="stylesheet">
<script src="../../components/jquery/jquery.js"></script>
<script src="../../components/angular-unstable/angular.js"></script>
<script src="../../components/angular-bootstrap/ui-bootstrap.js"></script>
<script src="../../components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<script src="../../angular-contenteditable.js"></script>
<script>
angular.module('typeahead3', ['ui.bootstrap', 'contenteditable'])

@ -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