diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4ff040b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+bower_components
+npm-debug.log
diff --git a/README.md b/README.md
index 3d8ae99..8faa092 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# angular-pagedown
-A pagedown editor for AngularJS. View this [Plunker](http://plnkr.co/edit/2LZiw454g77k6aE3HTyd) for demo.
+A pagedown editor for AngularJS. View this [Plunker](http://plnkr.co/edit/m93lSn?p=preview) for demo.
## Instructions
@@ -35,34 +35,39 @@ This package comes with 2 directives:
## Editor
```html
-
+
```
Options:
-#### content
+#### ng-model
1. An expression.
1. *Expression*: Mandatory
-1. Example: ``
-1. Example: ``
+1. Example: ``
+1. Example: ``
#### show-preview
1. Should a live preview be displayed.
1. *Boolean*: Default to true
+#### show-button-bar
+
+1. Should the button bar be displayed.
+1. *Boolean*: Default to true
+
#### help
1. An expression to invoke upon clicking the help (?) button.
1. *Expression*: Default to open http://daringfireball.net/projects/markdown/syntax in new window
-1. Example: ``
+1. Example: ``
#### insert-image
1. An expression to invoke upon clicking the "Insert Image" button.
1. *Expression*: Default to `noop`
-1. Example: ``
+1. Example: ``
1. The parent scope function `promptImageUrl` must return either:
- A string of image URL.
- A promise resolved with a string of image URL.
@@ -71,15 +76,15 @@ Options:
1. An expression.
1. *Expression*: Default to empty string
-1. Example: ``
+1. Example: ``
#### editor-class
1. An expression to use as [ngClass](https://docs.angularjs.org/api/ng/directive/ngClass).
1. *Expression*: Default to `wmd-input`
-1. Example: ``
-1. Example: ``
-1. Example: ``
+1. Example: ``
+1. Example: ``
+1. Example: ``
#### editor-rows
@@ -104,5 +109,4 @@ Options:
```
## TODO
-1. Grunt setup to minify files.
1. Extend PageDown editor to allow override of hyper link insertion.
diff --git a/angular-pagedown.js b/angular-pagedown.js
index 9eb962e..582bcd5 100644
--- a/angular-pagedown.js
+++ b/angular-pagedown.js
@@ -1,171 +1,176 @@
-// Mardown Extra Options
-var mdExtraOptions = {
- extensions: "all",
- table_class: 'table'
-};
-
-// adapted from http://stackoverflow.com/a/20957476/940030
-angular.module("ui.pagedown", [])
- .directive("pagedownEditor", ['$compile', '$timeout', '$window', '$q', function ($compile, $timeout, $window, $q) {
- var nextId = 0;
- var converter = Markdown.getSanitizingConverter();
- Markdown.Extra.init(converter, mdExtraOptions);
-
- converter.hooks.chain("preBlockGamut", function (text, rbg) {
- return text.replace(/^ {0,3}""" *\n((?:.*?\n)+?) {0,3}""" *$/gm, function (whole, inner) {
- return "
" + rbg(inner) + "
\n";
- });
- });
-
- return {
- restrict: "E",
- require: 'ngModel',
- scope: {
- ngModel: "=",
- placeholder: "@",
- showPreview: "@",
- help: "&",
- insertImage: "&",
- editorClass: "=?",
- editorRows: "@",
- previewClass: "=?",
- previewContent: "=?"
- },
- link: function (scope, element, attrs, ngModel) {
-
- scope.changed = function () {
- ngModel.$setDirty();
- scope.$parent.$eval(attrs.ngChange);
- };
-
-
- var editorUniqueId;
-
- if (attrs.id == null) {
- editorUniqueId = nextId++;
- } else {
- editorUniqueId = attrs.id;
- }
-
- // just hide the preview, we still need it for "onPreviewRefresh" hook
- var previewHiddenStyle = scope.showPreview == "false" ? "display: none;" : "";
-
- var placeholder = attrs.placeholder || "";
- var editorRows = attrs.editorRows || "10";
-
- var newElement = $compile(
- '' +
- '
' +
- '
' +
- '
' +
- '
')(scope);
-
- // html() doesn't work
- element.append(newElement);
-
- var help = angular.isFunction(scope.help) ? scope.help : function () {
- // redirect to the guide by default
- $window.open("http://daringfireball.net/projects/markdown/syntax", "_blank");
- };
-
- var editor = new Markdown.Editor(converter, "-" + editorUniqueId, {
- handler: help
- });
-
-
- var editorElement = angular.element(document.getElementById("wmd-input-" + editorUniqueId));
-
- editorElement.val(scope.ngModel);
-
- converter.hooks.chain("postConversion", function (text) {
- ngModel.$setViewValue(editorElement.val());
-
- // update
- scope.previewContent = text;
- return text;
+(function() {
+ "use strict";
+ // Mardown Extra Options
+ var mdExtraOptions = {
+ extensions: 'all',
+ table_class: 'table'
+ };
+
+ // adapted from http://stackoverflow.com/a/20957476/940030
+ angular.module('ui.pagedown', [])
+ .directive('pagedownEditor', ['$compile', '$timeout', '$window', '$q', function ($compile, $timeout, $window, $q) {
+ var nextId = 0;
+ var converter = Markdown.getSanitizingConverter();
+ Markdown.Extra.init(converter, mdExtraOptions);
+
+ converter.hooks.chain('preBlockGamut', function (text, rbg) {
+ return text.replace(/^ {0,3}""" *\n((?:.*?\n)+?) {0,3}""" *$/gm, function (whole, inner) {
+ return '' + rbg(inner) + '
\n';
});
+ });
-
- // add watch for content
- if (scope.showPreview != "false") {
- scope.$watch('content', function () {
- editor.refreshPreview();
+ return {
+ restrict: 'E',
+ require: 'ngModel',
+ scope: {
+ ngModel: '=',
+ placeholder: '@',
+ showPreview: '@',
+ showButtonBar: '@',
+ help: '&',
+ insertImage: '&',
+ editorClass: '=?',
+ editorRows: '@',
+ previewClass: '=?',
+ previewContent: '=?'
+ },
+ link: function (scope, element, attrs, ngModel) {
+
+ scope.changed = function () {
+ ngModel.$setDirty();
+ scope.$parent.$eval(attrs.ngChange);
+ };
+
+
+ var editorUniqueId;
+
+ if (attrs.id === null) {
+ editorUniqueId = nextId++;
+ } else {
+ editorUniqueId = attrs.id;
+ }
+
+ // just hide the preview, we still need it for 'onPreviewRefresh' hook
+ var previewHiddenStyle = scope.showPreview == 'false' ? 'display: none;' : '';
+ var buttonBarHiddenStyle = scope.showButtonBar == 'false' ? 'display: none;' : '';
+
+ var placeholder = attrs.placeholder || '';
+ var editorRows = attrs.editorRows || '10';
+
+ var newElement = $compile(
+ '' +
+ '
' +
+ '
' +
+ '
' +
+ '
')(scope);
+
+ // html() doesn't work
+ element.append(newElement);
+
+ var help = angular.isFunction(scope.help) ? scope.help : function () {
+ // redirect to the guide by default
+ $window.open('http://daringfireball.net/projects/markdown/syntax', '_blank');
+ };
+
+ var editor = new Markdown.Editor(converter, '-' + editorUniqueId, {
+ handler: help
});
- }
- editor.hooks.chain("onPreviewRefresh", function () {
- // wire up changes caused by user interaction with the pagedown controls
- // and do within $apply
- $timeout(function () {
- scope.content = editorElement.val();
+
+
+ var editorElement = angular.element(document.getElementById('wmd-input-' + editorUniqueId));
+
+ editorElement.val(scope.ngModel);
+
+ converter.hooks.chain('postConversion', function (text) {
+ ngModel.$setViewValue(editorElement.val());
+
+ // update
+ scope.previewContent = text;
+ return text;
});
- });
-
- if (angular.isFunction(scope.insertImage)) {
- editor.hooks.set("insertImageDialog", function (callback) {
- // expect it to return a promise or a url string
- var result = scope.insertImage();
-
- // Note that you cannot call the callback directly from the hook; you have to wait for the current scope to be exited.
- // https://code.google.com/p/pagedown/wiki/PageDown#insertImageDialog
+
+
+ // add watch for content
+ if (scope.showPreview != 'false') {
+ scope.$watch('content', function () {
+ editor.refreshPreview();
+ });
+ }
+ editor.hooks.chain('onPreviewRefresh', function () {
+ // wire up changes caused by user interaction with the pagedown controls
+ // and do within $apply
$timeout(function () {
- if (!result) {
- // must be null to indicate failure
- callback(null);
- } else {
- // safe way to handle either string or promise
- $q.when(result).then(
- function success(imgUrl) {
- callback(imgUrl);
- },
- function error(reason) {
- callback(null);
- }
- );
- }
+ scope.content = editorElement.val();
});
-
- return true;
});
- }
-
- editor.run();
- }
- }
- }])
- .directive("pagedownViewer", ['$compile', '$sce', function ($compile, $sce) {
- var converter = Markdown.getSanitizingConverter();
- Markdown.Extra.init(converter, mdExtraOptions);
-
- return {
- restrict: "E",
- scope: {
- content: "="
- },
- link: function (scope, element, attrs) {
- var run = function run() {
- if (!scope.content) {
- scope.sanitizedContent = '';
- return;
+
+ if (angular.isFunction(scope.insertImage)) {
+ editor.hooks.set('insertImageDialog', function (callback) {
+ // expect it to return a promise or a url string
+ var result = scope.insertImage();
+
+ // Note that you cannot call the callback directly from the hook; you have to wait for the current scope to be exited.
+ // https://code.google.com/p/pagedown/wiki/PageDown#insertImageDialog
+ $timeout(function () {
+ if (!result) {
+ // must be null to indicate failure
+ callback(null);
+ } else {
+ // safe way to handle either string or promise
+ $q.when(result).then(
+ function success(imgUrl) {
+ callback(imgUrl);
+ },
+ function error(reason) {
+ callback(null);
+ }
+ );
+ }
+ });
+
+ return true;
+ });
}
-
- scope.sanitizedContent = $sce.trustAsHtml(converter.makeHtml(scope.content));
- };
-
- scope.$watch("content", run);
-
- run();
-
- var newElementHtml = "";
- var newElement = $compile(newElementHtml)(scope);
-
- element.append(newElement);
- }
- }
- }]);
+
+ editor.run();
+ }
+ };
+ }])
+ .directive('pagedownViewer', ['$compile', '$sce', function ($compile, $sce) {
+ var converter = Markdown.getSanitizingConverter();
+ Markdown.Extra.init(converter, mdExtraOptions);
+
+ return {
+ restrict: 'E',
+ scope: {
+ content: '='
+ },
+ link: function (scope, element, attrs) {
+ var run = function run() {
+ if (!scope.content) {
+ scope.sanitizedContent = '';
+ return;
+ }
+
+ scope.sanitizedContent = $sce.trustAsHtml(converter.makeHtml(scope.content));
+ };
+
+ scope.$watch('content', run);
+
+ run();
+
+ var newElementHtml = '';
+ var newElement = $compile(newElementHtml)(scope);
+
+ element.append(newElement);
+ }
+ };
+ }]);
+})();
diff --git a/angular-pagedown.min.css b/angular-pagedown.min.css
index 2fbdd30..2cee674 100644
--- a/angular-pagedown.min.css
+++ b/angular-pagedown.min.css
@@ -1 +1 @@
-.wmd-button,.wmd-spacer{position:absolute;list-style:none}.wmd-button,.wmd-button>span,.wmd-spacer{display:inline-block}blockquote{border-left:2px dotted #888;padding-left:5px;background:#d0f0ff}.wmd-panel{min-width:500px}.wmd-button-bar{width:100%;background-color:Silver}.wmd-input{height:300px;width:100%;background-color:#dcdcdc;border:1px solid #a9a9a9}.wmd-preview{background-color:#c0e0ff}.wmd-button-row{position:relative;padding:0;height:20px;margin:10px 5px 5px}.wmd-spacer{width:1px;height:20px;margin-left:14px;background-color:Silver}.wmd-button{width:20px;height:20px;padding-left:2px;padding-right:3px;cursor:pointer}.wmd-button>span{background-image:url(../pagedown/wmd-buttons.png);background-repeat:no-repeat;background-position:0 0;width:20px;height:20px}.wmd-spacer1{left:50px}.wmd-spacer2{left:175px}.wmd-spacer3{left:300px}.wmd-prompt-background{background-color:#000}.wmd-prompt-dialog{border:1px solid #999;background-color:#F5F5F5}.wmd-prompt-dialog>div{font-size:.8em;font-family:arial,helvetica,sans-serif}.wmd-prompt-dialog>form>input[type=text]{border:1px solid #999;color:#000}.wmd-prompt-dialog>form>input[type=button]{border:1px solid #888;font-family:trebuchet MS,helvetica,sans-serif;font-size:.8em;font-weight:700}
\ No newline at end of file
+blockquote{border-left:2px dotted #888;padding-left:5px;background:#d0f0ff}.wmd-panel{min-width:500px}.wmd-button-bar{width:100%;background-color:Silver}.wmd-input{height:300px;width:100%;background-color:#dcdcdc;border:1px solid #a9a9a9}.wmd-preview{background-color:#c0e0ff}.wmd-button-row{position:relative;padding:0;height:20px;margin:10px 5px 5px}.wmd-button,.wmd-spacer{position:absolute;list-style:none}.wmd-spacer{width:1px;height:20px;margin-left:14px;background-color:Silver;display:inline-block}.wmd-button,.wmd-button>span{width:20px;height:20px;display:inline-block}.wmd-button{padding-left:2px;padding-right:3px;cursor:pointer}.wmd-button>span{background-image:url(../pagedown/wmd-buttons.png);background-repeat:no-repeat;background-position:0 0}.wmd-spacer1{left:50px}.wmd-spacer2{left:175px}.wmd-spacer3{left:300px}.wmd-prompt-background{background-color:#000}.wmd-prompt-dialog{border:1px solid #999;background-color:#F5F5F5}.wmd-prompt-dialog>div{font-size:.8em;font-family:arial,helvetica,sans-serif}.wmd-prompt-dialog>form>input[type=text]{border:1px solid #999;color:#000}.wmd-prompt-dialog>form>input[type=button]{border:1px solid #888;font-family:trebuchet MS,helvetica,sans-serif;font-size:.8em;font-weight:700}
\ No newline at end of file
diff --git a/angular-pagedown.min.js b/angular-pagedown.min.js
index c6687fd..54641ee 100644
--- a/angular-pagedown.min.js
+++ b/angular-pagedown.min.js
@@ -1 +1 @@
-var mdExtraOptions={extensions:"all",table_class:"table"};angular.module("ui.pagedown",[]).directive("pagedownEditor",["$compile","$timeout","$window","$q",function(n,e,t,i){var o=0,r=Markdown.getSanitizingConverter();return Markdown.Extra.init(r,mdExtraOptions),r.hooks.chain("preBlockGamut",function(n,e){return n.replace(/^ {0,3}""" *\n((?:.*?\n)+?) {0,3}""" *$/gm,function(n,t){return""+e(t)+"
\n"})}),{restrict:"E",scope:{content:"=",placeholder:"@",showPreview:"@",help:"&",insertImage:"&",editorClass:"=?",editorRows:"@",previewClass:"=?",previewContent:"=?"},link:function(a,s,l){var d;d=null==l.id?o++:l.id;var c="false"==a.showPreview?"display: none;":"",u=l.placeholder||"",w=l.editorRows||"10",p=n('")(a);s.append(p);var v=angular.isFunction(a.help)?a.help:function(){t.open("http://daringfireball.net/projects/markdown/syntax","_blank")},m=new Markdown.Editor(r,"-"+d,{handler:v});r.hooks.chain("postConversion",function(n){return a.previewContent=n,n});var h=angular.element(document.getElementById("wmd-input-"+d));"false"!=a.showPreview&&a.$watch("content",function(){m.refreshPreview()}),m.hooks.chain("onPreviewRefresh",function(){e(function(){a.content=h.val()})}),angular.isFunction(a.insertImage)&&m.hooks.set("insertImageDialog",function(n){var t=a.insertImage();return e(function(){t?i.when(t).then(function(e){n(e)},function(){n(null)}):n(null)}),!0}),m.run()}}}]).directive("pagedownViewer",["$compile","$sce",function(n,e){var t=Markdown.getSanitizingConverter();return Markdown.Extra.init(t,mdExtraOptions),{restrict:"E",scope:{content:"="},link:function(i,o){var r=function(){return i.content?void(i.sanitizedContent=e.trustAsHtml(t.makeHtml(i.content))):void(i.sanitizedContent="")};i.$watch("content",r),r();var a="",s=n(a)(i);o.append(s)}}}]);
\ No newline at end of file
+!function(){"use strict";var n={extensions:"all",table_class:"table"};angular.module("ui.pagedown",[]).directive("pagedownEditor",["$compile","$timeout","$window","$q",function(e,t,i,o){var a=0,r=Markdown.getSanitizingConverter();return Markdown.Extra.init(r,n),r.hooks.chain("preBlockGamut",function(n,e){return n.replace(/^ {0,3}""" *\n((?:.*?\n)+?) {0,3}""" *$/gm,function(n,t){return""+e(t)+"
\n"})}),{restrict:"E",require:"ngModel",scope:{ngModel:"=",placeholder:"@",showPreview:"@",showButtonBar:"@",help:"&",insertImage:"&",editorClass:"=?",editorRows:"@",previewClass:"=?",previewContent:"=?"},link:function(n,l,s,d){n.changed=function(){d.$setDirty(),n.$parent.$eval(s.ngChange)};var c;c=null===s.id?a++:s.id;var u="false"==n.showPreview?"display: none;":"",w="false"==n.showButtonBar?"display: none;":"",p=s.placeholder||"",v=s.editorRows||"10",g=e('")(n);l.append(g);var h=angular.isFunction(n.help)?n.help:function(){i.open("http://daringfireball.net/projects/markdown/syntax","_blank")},m=new Markdown.Editor(r,"-"+c,{handler:h}),f=angular.element(document.getElementById("wmd-input-"+c));f.val(n.ngModel),r.hooks.chain("postConversion",function(e){return d.$setViewValue(f.val()),n.previewContent=e,e}),"false"!=n.showPreview&&n.$watch("content",function(){m.refreshPreview()}),m.hooks.chain("onPreviewRefresh",function(){t(function(){n.content=f.val()})}),angular.isFunction(n.insertImage)&&m.hooks.set("insertImageDialog",function(e){var i=n.insertImage();return t(function(){i?o.when(i).then(function(n){e(n)},function(n){e(null)}):e(null)}),!0}),m.run()}}}]).directive("pagedownViewer",["$compile","$sce",function(e,t){var i=Markdown.getSanitizingConverter();return Markdown.Extra.init(i,n),{restrict:"E",scope:{content:"="},link:function(n,o,a){var r=function(){return n.content?void(n.sanitizedContent=t.trustAsHtml(i.makeHtml(n.content))):void(n.sanitizedContent="")};n.$watch("content",r),r();var l='',s=e(l)(n);o.append(s)}}}])}();
\ No newline at end of file
diff --git a/bower.json b/bower.json
index 794b5d6..d4f55e5 100644
--- a/bower.json
+++ b/bower.json
@@ -1,12 +1,12 @@
{
"name": "angular-pagedown",
- "version": "0.4.4",
+ "version": "0.4.5",
"main": [
"angular-pagedown.css",
"angular-pagedown.js"
],
"dependencies": {
- "angular": "~1.2",
+ "angular": "~1.4",
"pagedown": "^1.1.0"
}
}
diff --git a/demo/index.html b/demo/index.html
new file mode 100644
index 0000000..eea2a79
--- /dev/null
+++ b/demo/index.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+ angular-pagedown demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
This is an independent viewer
+
+
+
+
\ No newline at end of file
diff --git a/demo/script.js b/demo/script.js
new file mode 100644
index 0000000..c906711
--- /dev/null
+++ b/demo/script.js
@@ -0,0 +1,28 @@
+angular.module("myApp", ["ui.pagedown"])
+
+.controller("myController", function($scope, $window, $q) {
+
+ $scope.data = {
+ content: "Wee *wang* **wang**!",
+ placeholder: "Enter something here.."
+ };
+
+ $scope.showSomeHelp = function showSomeHelp() {
+ // this is what the default will do
+ $window.open("http://daringfireball.net/projects/markdown/syntax", "_blank");
+ };
+
+ $scope.insertImage = function insertImage() {
+ var deferred = $q.defer();
+
+ // or you can return a string straightaway
+ deferred.resolve("http://www.discoposse.com/wp-content/uploads/2014/08/test-all-the-things.jpg");
+
+ return deferred.promise;
+ };
+
+ $scope.shout = function shout() {
+ // test update the content programatically
+ $scope.data.content += '\n\nTell Laura I love her';
+ };
+});
\ No newline at end of file
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000..971e274
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,47 @@
+var gulp = require('gulp');
+
+var jshint = require('gulp-jshint');
+var uglify = require('gulp-uglify');
+var rename = require('gulp-rename');
+var minifyCss = require('gulp-minify-css');
+var browserSync = require('browser-sync').create();
+
+gulp.task('lint', function() {
+ return gulp.src('angular-pagedown.js')
+ .pipe(jshint())
+ .pipe(jshint.reporter('default'));
+});
+
+gulp.task('scripts', function() {
+ return gulp.src('angular-pagedown.js')
+ .pipe(rename('angular-pagedown.min.js'))
+ .pipe(uglify())
+ .pipe(gulp.dest('./'));
+});
+
+gulp.task('styles', function() {
+ return gulp.src('angular-pagedown.css')
+ .pipe(rename('angular-pagedown.min.css'))
+ .pipe(minifyCss({compatibility: 'ie8'}))
+ .pipe(gulp.dest('./'));
+});
+
+gulp.task('browser-sync', function() {
+ browserSync.init({
+ server: {
+ baseDir: ["./", "demo"],
+ routes: {
+ "/bower_components": "bower_components"
+ }
+ }
+ });
+});
+
+gulp.task('watch', function() {
+ gulp.watch('angular-pagedown.js', ['lint', 'scripts']);
+ gulp.watch('angular-pagedown.css', ['styles']);
+ gulp.watch(['demo/*.html', 'angular-pagedown.min.js', 'angular-pagedown.min.css']).on("change", browserSync.reload);
+});
+
+gulp.task('default', ['lint', 'scripts', 'styles', 'watch']);
+gulp.task('serve', ['lint', 'scripts', 'styles', 'browser-sync', 'watch']);
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..5eae5e4
--- /dev/null
+++ b/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "angular-pagedown",
+ "private": true,
+ "devDependencies": {
+ "browser-sync": "^2.10.0",
+ "gulp": "^3.9.0",
+ "gulp-jshint": "^2.0.0",
+ "gulp-minify-css": "^1.2.1",
+ "gulp-rename": "^1.2.2",
+ "gulp-uglify": "^1.5.1",
+ "jshint": "^2.8.0"
+ }
+}