angular-content-editable
Advanced tools
Comparing version 1.1.3 to 1.2.1
@@ -10,5 +10,5 @@ angular.module('angular-content-editable', []); | ||
require: 'ngModel', | ||
scope: { editCallback: '=' }, | ||
scope: { editCallback: '&?', isEditing: '=?' }, | ||
link: _link | ||
} | ||
}; | ||
@@ -41,12 +41,23 @@ return directive; | ||
scope.$watch('isEditing', function(newValue, oldValue) { | ||
if (newValue !== oldValue) { | ||
if (newValue) { | ||
originalElement.click(); | ||
} else { | ||
originalElement.blur(); | ||
} | ||
} | ||
}); | ||
// render always with model value | ||
ngModel.$render = function() { | ||
elem.html( ngModel.$modelValue || elem.html() ); | ||
} | ||
}; | ||
// handle click on element | ||
function onClick(e){ | ||
function onClick(e) { | ||
e.preventDefault(); | ||
attrs.$set('contenteditable', 'true'); | ||
return originalElement.focus(); | ||
attrs.$addClass('active'); | ||
originalElement.focus(); | ||
} | ||
@@ -58,20 +69,27 @@ | ||
// turn on the flag | ||
noEscape = true; | ||
// evalAsync in case a digest is already in progress (e.g. changing isEditing to true) | ||
scope.$evalAsync(function() { | ||
// select all on focus | ||
if( options.focusSelect ) { | ||
var range = $window.document.createRange(); | ||
var selection = $window.getSelection(); | ||
range.selectNodeContents( originalElement ); | ||
selection.removeAllRanges(); | ||
selection.addRange(range); | ||
} | ||
scope.isEditing = true; | ||
// if render-html is enabled convert | ||
// all text content to plaintext | ||
// in order to modify html tags | ||
if( options.renderHtml ) { | ||
originalElement.textContent = elem.html(); | ||
} | ||
// turn on the flag | ||
noEscape = true; | ||
// select all on focus | ||
if( options.focusSelect ) { | ||
var range = $window.document.createRange(); | ||
var selection = $window.getSelection(); | ||
range.selectNodeContents( originalElement ); | ||
selection.removeAllRanges(); | ||
selection.addRange(range); | ||
} | ||
// if render-html is enabled convert | ||
// all text content to plaintext | ||
// in order to modify html tags | ||
if( options.renderHtml ) { | ||
originalElement.textContent = elem.html(); | ||
} | ||
}); | ||
@@ -82,41 +100,54 @@ } | ||
// the text | ||
var html; | ||
scope.$apply(function() { | ||
// disable editability | ||
attrs.$set('contenteditable', 'false'); | ||
// if text needs to be rendered as html | ||
if( options.renderHtml && noEscape ) { | ||
// get plain text html (with html tags) | ||
// replace all blank spaces | ||
html = originalElement.textContent.replace(/\u00a0/g, " "); | ||
// update elem html value | ||
elem.html(html); | ||
} else { | ||
// get element content replacing html tag | ||
html = elem.html().replace(/ /g, ' '); | ||
} | ||
// if element value is | ||
// different from model value | ||
if( html != ngModel.$modelValue ) { | ||
/** | ||
* This method should be called | ||
* when a controller wants to | ||
* change the view value | ||
*/ | ||
ngModel.$setViewValue(html) | ||
// if user passed a variable | ||
// and is a function | ||
if( scope.editCallback && angular.isFunction(scope.editCallback) ) { | ||
// apply the callback | ||
// with arguments: current text and element | ||
return scope.$apply( scope.editCallback(html, elem) ); | ||
// the text | ||
var html; | ||
scope.isEditing = false; | ||
// remove active class when editing is over | ||
attrs.$removeClass('active'); | ||
// disable editability | ||
attrs.$set('contenteditable', 'false'); | ||
// if text needs to be rendered as html | ||
if( options.renderHtml && noEscape ) { | ||
// get plain text html (with html tags) | ||
// replace all blank spaces | ||
html = originalElement.textContent.replace(/\u00a0/g, " "); | ||
// update elem html value | ||
elem.html(html); | ||
} else { | ||
// get element content replacing html tag | ||
html = elem.html().replace(/ /g, ' '); | ||
} | ||
// if element value is different from model value | ||
if( html != ngModel.$modelValue ) { | ||
/** | ||
* This method should be called | ||
* when a controller wants to | ||
* change the view value | ||
*/ | ||
ngModel.$setViewValue(html); | ||
// if user passed a variable | ||
// and is a function | ||
if( scope.editCallback && angular.isFunction(scope.editCallback) ) { | ||
// run the callback with arguments: current text and element | ||
return scope.editCallback({ | ||
text: html, | ||
elem: elem | ||
}); | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
@@ -186,3 +217,3 @@ | ||
}]) | ||
}]); | ||
@@ -189,0 +220,0 @@ angular.module('angular-content-editable') |
@@ -1,1 +0,1 @@ | ||
angular.module("angular-content-editable",[]),angular.module("angular-content-editable").directive("contentEditable",["$log","$sce","$parse","$window","contentEditable",function($log,$sce,$parse,$window,contentEditable){function _link(scope,elem,attrs,ngModel){function onClick(e){return e.preventDefault(),attrs.$set("contenteditable","true"),originalElement.focus()}function onFocus(e){if(noEscape=!0,options.focusSelect){var range=$window.document.createRange(),selection=$window.getSelection();range.selectNodeContents(originalElement),selection.removeAllRanges(),selection.addRange(range)}options.renderHtml&&(originalElement.textContent=elem.html())}function onBlur(e){var html;if(attrs.$set("contenteditable","false"),options.renderHtml&&noEscape?(html=originalElement.textContent.replace(/\u00a0/g," "),elem.html(html)):html=elem.html().replace(/ /g," "),html!=ngModel.$modelValue&&(ngModel.$setViewValue(html),scope.editCallback&&angular.isFunction(scope.editCallback)))return scope.$apply(scope.editCallback(html,elem))}function onKeyDown(e){return 9==e.which?void originalElement.blur():27==e.which?(ngModel.$rollbackViewValue(),noEscape=!1,originalElement.blur()):13==e.which&&(options.singleLine||e.ctrlKey)?originalElement.blur():void 0}if(!ngModel)return void $log.warn("Error: ngModel is required in elem: ",elem);var noEscape=!0,originalElement=elem[0],options=angular.copy(contentEditable);angular.forEach(options,function(val,key){key in attrs&&(options[key]=$parse(attrs[key])(scope))}),attrs.$addClass(options.editableClass),ngModel.$render=function(){elem.html(ngModel.$modelValue||elem.html())},elem.on("click",onClick),elem.on("focus",onFocus),elem.on("blur",onBlur),elem.on("keydown",onKeyDown),scope.$on("$destroy",function(){elem.off("click",onClick),elem.off("focus",onFocus),elem.off("blur",onBlur),elem.off("keydown",onKeyDown)})}return{restrict:"A",require:"ngModel",scope:{editCallback:"="},link:_link}}]),angular.module("angular-content-editable").provider("contentEditable",function(){var defaults={editableClass:"editable",keyBindings:!0,singleLine:!1,focusSelect:!0,renderHtml:!1,editCallback:!1};this.configure=function(options){return angular.extend(defaults,options)},this.$get=function(){return defaults}}); | ||
angular.module("angular-content-editable",[]),angular.module("angular-content-editable").directive("contentEditable",["$log","$sce","$parse","$window","contentEditable",function($log,$sce,$parse,$window,contentEditable){function _link(scope,elem,attrs,ngModel){function onClick(e){e.preventDefault(),attrs.$set("contenteditable","true"),attrs.$addClass("active"),originalElement.focus()}function onFocus(e){scope.$evalAsync(function(){if(scope.isEditing=!0,noEscape=!0,options.focusSelect){var range=$window.document.createRange(),selection=$window.getSelection();range.selectNodeContents(originalElement),selection.removeAllRanges(),selection.addRange(range)}options.renderHtml&&(originalElement.textContent=elem.html())})}function onBlur(e){scope.$apply(function(){var html;if(scope.isEditing=!1,attrs.$removeClass("active"),attrs.$set("contenteditable","false"),options.renderHtml&&noEscape?(html=originalElement.textContent.replace(/\u00a0/g," "),elem.html(html)):html=elem.html().replace(/ /g," "),html!=ngModel.$modelValue&&(ngModel.$setViewValue(html),scope.editCallback&&angular.isFunction(scope.editCallback)))return scope.editCallback({text:html,elem:elem})})}function onKeyDown(e){return 9==e.which?void originalElement.blur():27==e.which?(ngModel.$rollbackViewValue(),noEscape=!1,originalElement.blur()):13==e.which&&(options.singleLine||e.ctrlKey)?originalElement.blur():void 0}if(!ngModel)return void $log.warn("Error: ngModel is required in elem: ",elem);var noEscape=!0,originalElement=elem[0],options=angular.copy(contentEditable);angular.forEach(options,function(val,key){key in attrs&&(options[key]=$parse(attrs[key])(scope))}),attrs.$addClass(options.editableClass),scope.$watch("isEditing",function(newValue,oldValue){newValue!==oldValue&&(newValue?originalElement.click():originalElement.blur())}),ngModel.$render=function(){elem.html(ngModel.$modelValue||elem.html())},elem.on("click",onClick),elem.on("focus",onFocus),elem.on("blur",onBlur),elem.on("keydown",onKeyDown),scope.$on("$destroy",function(){elem.off("click",onClick),elem.off("focus",onFocus),elem.off("blur",onBlur),elem.off("keydown",onKeyDown)})}return{restrict:"A",require:"ngModel",scope:{editCallback:"&?",isEditing:"=?"},link:_link}}]),angular.module("angular-content-editable").provider("contentEditable",function(){var defaults={editableClass:"editable",keyBindings:!0,singleLine:!1,focusSelect:!0,renderHtml:!1,editCallback:!1};this.configure=function(options){return angular.extend(defaults,options)},this.$get=function(){return defaults}}); |
@@ -51,8 +51,2 @@ module.exports = function(grunt) { | ||
livereload: true, | ||
base: { | ||
path: '.', | ||
options: { | ||
index: 'example/index.html' | ||
} | ||
}, | ||
open: true | ||
@@ -59,0 +53,0 @@ } |
@@ -10,3 +10,2 @@ // Karma configuration | ||
// frameworks to use | ||
@@ -16,7 +15,6 @@ // available frameworks: https://npmjs.org/browse/keyword/karma-adapter | ||
// list of files / patterns to load in the browser | ||
files: [ | ||
'bower_components/angular/angular.js', | ||
'bower_components/angular-mocks/angular-mocks.js', | ||
'node_modules/angular/angular.js', | ||
'node_modules/angular-mocks/angular-mocks.js', | ||
'src/*.module.js', | ||
@@ -27,3 +25,2 @@ 'src/*.js', | ||
// list of files to exclude | ||
@@ -33,3 +30,2 @@ exclude: [ | ||
// preprocess matching files before serving them to the browser | ||
@@ -40,3 +36,2 @@ // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor | ||
// test results reporter to use | ||
@@ -47,11 +42,8 @@ // possible values: 'dots', 'progress' | ||
// web server port | ||
port: 9876, | ||
// enable / disable colors in the output (reporters and logs) | ||
colors: true, | ||
// level of logging | ||
@@ -61,7 +53,5 @@ // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG | ||
// enable / disable watching file and executing tests whenever any file changes | ||
autoWatch: true, | ||
// start these browsers | ||
@@ -71,3 +61,2 @@ // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher | ||
// Continuous Integration mode | ||
@@ -80,3 +69,3 @@ // if true, Karma captures browsers, runs the tests and exits | ||
concurrency: Infinity | ||
}) | ||
} | ||
}); | ||
}; |
{ | ||
"name": "angular-content-editable", | ||
"version": "1.1.3", | ||
"version": "1.2.1", | ||
"description": "modify in real time any html tag you want", | ||
@@ -10,3 +10,3 @@ "main": "dist/angular-content-editable.js", | ||
"scripts": { | ||
"test": "node node_modules/karma/bin/karma start" | ||
"test": "node node_modules/karma/bin/karma start --single-run" | ||
}, | ||
@@ -28,2 +28,4 @@ "repository": { | ||
"devDependencies": { | ||
"angular": "^1.6.5", | ||
"angular-mocks": "^1.6.5", | ||
"grunt": "^0.4.5", | ||
@@ -30,0 +32,0 @@ "grunt-contrib-concat": "^1.0.1", |
# angular-content-editable | ||
angular directive for modify in real time any html tag you want | ||
### [DEMO](http://www.codekraft.it/demos/angular-content-editable/) | ||
### [DEMO](http://codekraft-studio.github.io/angular-content-editable/) | ||
@@ -11,2 +11,8 @@ ## Getting started: | ||
``` | ||
Download the package using bower: | ||
```bash | ||
bower install angular-content-editable | ||
``` | ||
or directly from github. | ||
@@ -24,3 +30,3 @@ | ||
```html | ||
<a href="!#" ng-model="myModel" content-editable>edit my text</a> | ||
<a href="#!" ng-model="myModel" content-editable>edit my text</a> | ||
``` | ||
@@ -30,3 +36,3 @@ | ||
### Directive attributes: | ||
## Directive attributes: | ||
* __single-line__: if set to true makes the enter key save and blur | ||
@@ -36,10 +42,18 @@ * __focus-select__: if set to true when element goes to focus, all the text inside will be selected | ||
* __edit-callback__: a callback that is called wherever the model value is changed | ||
* __is-editing__: optional argument that can be used to programatically enable/disable the editor | ||
Note that, __edit-callback__ has two arguments: | ||
Note that, __edit-callback__ has two arguments, that you must specify in your template to use them: | ||
* __text__: the new text inside the element | ||
* __elem__: the element that has been modified | ||
#### Example: | ||
```html | ||
<div ng-model="myModel" edit-callback="myFunc(text, elem)" content-editable> | ||
Some content | ||
</div> | ||
``` | ||
--- | ||
### Customizations: | ||
## Customizations: | ||
You can use the __contentEditableProvider__ to set the default settings for the directive, but you can always pass directly to the directive as attributes to override the defaults for that element. | ||
@@ -59,3 +73,3 @@ ```javascript | ||
### Example basic: | ||
## Example basic: | ||
Simply adding the directive makes the element fully editable. | ||
@@ -74,5 +88,8 @@ ```html | ||
``` | ||
With __edit-callback__ attribute if you passed a valid function it will run every time the model value is changed. | ||
If you want to run a callback you must use __edit-callback__ attribute with a valid function and it will run every time the model value is __changed__. | ||
Since version __1.2.0__, after issue [#13](https://github.com/codekraft-studio/angular-content-editable/issues/13) you __MUST__ specify the arguments `text, elem` if you want to use them in your callback, like in this example. | ||
```html | ||
<span focus-select="true" edit-callback="myFunc" ng-model="myModel" content-editable>Change me!</span> | ||
<span focus-select="true" edit-callback="myFunc(text, elem)" ng-model="myModel" content-editable>Change me!</span> | ||
``` | ||
@@ -90,5 +107,9 @@ ```javascript | ||
### Development: | ||
--- | ||
This gives the ability to __pass additional arguments__ to the callback, because is executed with the parent scope. | ||
## Development: | ||
If you want to fork you copy of the project and modify it: | ||
```bash | ||
```text | ||
npm install angular-content-editable // install module files | ||
@@ -98,5 +119,19 @@ npm install // install dependencies | ||
Than a Gruntfile is ready with this actions: | ||
```bash | ||
grunt // build the package | ||
grunt watch // watch to /src folder and rebuild the package | ||
```text | ||
grunt // watch to /src folder and rebuild the package | ||
grunt build // build the package for distribution | ||
``` | ||
--- | ||
## Contributing | ||
1. Create an issue and describe your idea | ||
2. Fork the project (https://github.com/codekraft-studio/angular-content-editable/fork) | ||
3. Create your feature branch (`git checkout -b my-new-feature`) | ||
4. Get the development environment set up (`npm install`) | ||
5. Commit your changes (`git commit -am 'Add some feature'`) | ||
6. Add some test for your new feature (`npm test`) | ||
7. Build the directive with the new changes (`grunt build`) | ||
8. Publish the branch (`git push origin my-new-feature`) | ||
9. Create a new Pull Request |
@@ -8,5 +8,5 @@ angular.module('angular-content-editable') | ||
require: 'ngModel', | ||
scope: { editCallback: '=' }, | ||
scope: { editCallback: '&?', isEditing: '=?' }, | ||
link: _link | ||
} | ||
}; | ||
@@ -39,12 +39,23 @@ return directive; | ||
scope.$watch('isEditing', function(newValue, oldValue) { | ||
if (newValue !== oldValue) { | ||
if (newValue) { | ||
originalElement.click(); | ||
} else { | ||
originalElement.blur(); | ||
} | ||
} | ||
}); | ||
// render always with model value | ||
ngModel.$render = function() { | ||
elem.html( ngModel.$modelValue || elem.html() ); | ||
} | ||
}; | ||
// handle click on element | ||
function onClick(e){ | ||
function onClick(e) { | ||
e.preventDefault(); | ||
attrs.$set('contenteditable', 'true'); | ||
return originalElement.focus(); | ||
attrs.$addClass('active'); | ||
originalElement.focus(); | ||
} | ||
@@ -56,20 +67,27 @@ | ||
// turn on the flag | ||
noEscape = true; | ||
// evalAsync in case a digest is already in progress (e.g. changing isEditing to true) | ||
scope.$evalAsync(function() { | ||
// select all on focus | ||
if( options.focusSelect ) { | ||
var range = $window.document.createRange(); | ||
var selection = $window.getSelection(); | ||
range.selectNodeContents( originalElement ); | ||
selection.removeAllRanges(); | ||
selection.addRange(range); | ||
} | ||
scope.isEditing = true; | ||
// if render-html is enabled convert | ||
// all text content to plaintext | ||
// in order to modify html tags | ||
if( options.renderHtml ) { | ||
originalElement.textContent = elem.html(); | ||
} | ||
// turn on the flag | ||
noEscape = true; | ||
// select all on focus | ||
if( options.focusSelect ) { | ||
var range = $window.document.createRange(); | ||
var selection = $window.getSelection(); | ||
range.selectNodeContents( originalElement ); | ||
selection.removeAllRanges(); | ||
selection.addRange(range); | ||
} | ||
// if render-html is enabled convert | ||
// all text content to plaintext | ||
// in order to modify html tags | ||
if( options.renderHtml ) { | ||
originalElement.textContent = elem.html(); | ||
} | ||
}); | ||
@@ -80,41 +98,54 @@ } | ||
// the text | ||
var html; | ||
scope.$apply(function() { | ||
// disable editability | ||
attrs.$set('contenteditable', 'false'); | ||
// if text needs to be rendered as html | ||
if( options.renderHtml && noEscape ) { | ||
// get plain text html (with html tags) | ||
// replace all blank spaces | ||
html = originalElement.textContent.replace(/\u00a0/g, " "); | ||
// update elem html value | ||
elem.html(html); | ||
} else { | ||
// get element content replacing html tag | ||
html = elem.html().replace(/ /g, ' '); | ||
} | ||
// if element value is | ||
// different from model value | ||
if( html != ngModel.$modelValue ) { | ||
/** | ||
* This method should be called | ||
* when a controller wants to | ||
* change the view value | ||
*/ | ||
ngModel.$setViewValue(html) | ||
// if user passed a variable | ||
// and is a function | ||
if( scope.editCallback && angular.isFunction(scope.editCallback) ) { | ||
// apply the callback | ||
// with arguments: current text and element | ||
return scope.$apply( scope.editCallback(html, elem) ); | ||
// the text | ||
var html; | ||
scope.isEditing = false; | ||
// remove active class when editing is over | ||
attrs.$removeClass('active'); | ||
// disable editability | ||
attrs.$set('contenteditable', 'false'); | ||
// if text needs to be rendered as html | ||
if( options.renderHtml && noEscape ) { | ||
// get plain text html (with html tags) | ||
// replace all blank spaces | ||
html = originalElement.textContent.replace(/\u00a0/g, " "); | ||
// update elem html value | ||
elem.html(html); | ||
} else { | ||
// get element content replacing html tag | ||
html = elem.html().replace(/ /g, ' '); | ||
} | ||
// if element value is different from model value | ||
if( html != ngModel.$modelValue ) { | ||
/** | ||
* This method should be called | ||
* when a controller wants to | ||
* change the view value | ||
*/ | ||
ngModel.$setViewValue(html); | ||
// if user passed a variable | ||
// and is a function | ||
if( scope.editCallback && angular.isFunction(scope.editCallback) ) { | ||
// run the callback with arguments: current text and element | ||
return scope.editCallback({ | ||
text: html, | ||
elem: elem | ||
}); | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
@@ -184,2 +215,2 @@ | ||
}) | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
31124
554
130
12
13
1