├── .gitignore ├── .travis.yml ├── bower.json ├── karma.conf.js ├── package.json ├── markdown.js ├── README.md └── markdown.spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | before_script: 5 | - npm install -g bower 6 | - bower install 7 | - export DISPLAY=:99.0 8 | - sh -e /etc/init.d/xvfb start 9 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-markdown-directive", 3 | "version": "0.3.1", 4 | "main": "markdown.js", 5 | "dependencies": { 6 | "angular": "^1.0.8", 7 | "angular-sanitize": "^1.0.8", 8 | "showdown": "~0.3.1" 9 | }, 10 | "devDependencies": { 11 | "angular-mocks": "^1.0.8" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | 3 | module.exports = function (config) { 4 | config.set({ 5 | basePath: '', 6 | files: [ 7 | 'bower_components/angular/angular.js', 8 | 'bower_components/angular-sanitize/angular-sanitize.js', 9 | 'bower_components/angular-mocks/angular-mocks.js', 10 | 'bower_components/showdown/src/showdown.js', 11 | 'bower_components/showdown/src/extensions/*.js', 12 | 'markdown.js', 13 | '*.spec.js' 14 | ], 15 | 16 | reporters: ['progress'], 17 | 18 | port: 9876, 19 | colors: true, 20 | 21 | logLevel: config.LOG_INFO, 22 | 23 | browsers: ['Chrome'], 24 | frameworks: ['jasmine'], 25 | 26 | captureTimeout: 60000, 27 | 28 | autoWatch: true, 29 | singleRun: false 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-markdown-directive", 3 | "version": "0.3.1", 4 | "description": "simple AngularJS markdown directive with showdown", 5 | "main": "markdown.js", 6 | "scripts": { 7 | "test": "./node_modules/karma/bin/karma start --browsers Firefox --single-run" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/btford/angular-markdown-directive.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "angularjs", 16 | "markdown", 17 | "directive" 18 | ], 19 | "author": "Brian Ford", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/btford/angular-markdown-directive/issues" 23 | }, 24 | "homepage": "https://github.com/btford/angular-markdown-directive", 25 | "devDependencies": { 26 | "karma": "^0.12.9", 27 | "karma-jasmine": "^0.1.5", 28 | "karma-firefox-launcher": "^0.1.3", 29 | "karma-chrome-launcher": "^0.1.3" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /markdown.js: -------------------------------------------------------------------------------- 1 | /* 2 | * angular-markdown-directive v0.3.1 3 | * (c) 2013-2014 Brian Ford http://briantford.com 4 | * License: MIT 5 | */ 6 | 7 | 'use strict'; 8 | 9 | angular.module('btford.markdown', ['ngSanitize']). 10 | provider('markdownConverter', function () { 11 | var opts = {}; 12 | return { 13 | config: function (newOpts) { 14 | opts = newOpts; 15 | }, 16 | $get: function () { 17 | return new Showdown.converter(opts); 18 | } 19 | }; 20 | }). 21 | directive('btfMarkdown', ['$sanitize', 'markdownConverter', function ($sanitize, markdownConverter) { 22 | return { 23 | restrict: 'AE', 24 | link: function (scope, element, attrs) { 25 | if (attrs.btfMarkdown) { 26 | scope.$watch(attrs.btfMarkdown, function (newVal) { 27 | var html = newVal ? $sanitize(markdownConverter.makeHtml(newVal)) : ''; 28 | element.html(html); 29 | }); 30 | } else { 31 | var html = $sanitize(markdownConverter.makeHtml(element.text())); 32 | element.html(html); 33 | } 34 | } 35 | }; 36 | }]); 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-markdown-directive [![Build Status](https://travis-ci.org/btford/angular-markdown-directive.png)](https://travis-ci.org/btford/angular-markdown-directive) 2 | 3 | Bower Component for a simple AngularJS Markdown directive using [Showdown](https://github.com/coreyti/showdown). Based on [this excellent tutorial](http://blog.angularjs.org/2012/05/custom-components-part-1.html) by [@johnlinquist](https://twitter.com/johnlindquist). 4 | 5 | 6 | ## Usage 7 | 1. `bower install angular-markdown-directive` 8 | 2. Include `angular-sanitize.js`. It should be located at `bower_components/angular-sanitize/`. 9 | 3. Include `showdown.js`. It should be located at `bower_components/showdown/`. 10 | 4. Include `markdown.js` provided by this component into your app. 11 | 5. Add `btford.markdown` as a module dependency to your app. 12 | 6. Insert the `btf-markdown` directive into your template: 13 | 14 | ```html 15 | #Markdown directive *It works!* 16 | ``` 17 | 18 | You can also bind the markdown input to a scope variable: 19 | 20 | ```html 21 |
22 |
23 | 24 | ``` 25 | 26 | Or include a markdown file: 27 | 28 | ```html 29 |
30 |
31 | 32 | ``` 33 | 34 | 35 | ## Options 36 | 37 | You can configure the `markdownConverterProvider`: 38 | 39 | ```javascript 40 | angular.module('myApp', [ 41 | 'ngSanitize', 42 | 'btford.markdown' 43 | ]). 44 | config(['markdownConverterProvider', function (markdownConverterProvider) { 45 | // options to be passed to Showdown 46 | // see: https://github.com/coreyti/showdown#extensions 47 | markdownConverterProvider.config({ 48 | extensions: ['twitter'] 49 | }); 50 | }]) 51 | ``` 52 | 53 | 54 | ## License 55 | MIT 56 | -------------------------------------------------------------------------------- /markdown.spec.js: -------------------------------------------------------------------------------- 1 | describe('btfMarkdown', function () { 2 | var $compile, 3 | $rootScope; 4 | 5 | beforeEach(module('ngSanitize')); 6 | beforeEach(module('btford.markdown')); 7 | 8 | beforeEach(inject(function (_$compile_, _$rootScope_) { 9 | $compile = _$compile_; 10 | $rootScope = _$rootScope_; 11 | })); 12 | 13 | it('should work as an element', function () { 14 | var elt = angular.element('*hi*'); 15 | $compile(elt)($rootScope); 16 | expect(elt.html()).toBe('

hi

'); 17 | }); 18 | 19 | it('should work as an attribute', function () { 20 | var elt = angular.element('
*hi*
'); 21 | $compile(elt)($rootScope); 22 | expect(elt.html()).toBe('

hi

'); 23 | }); 24 | 25 | it('should work as an attribute with property', function () { 26 | var elt = angular.element('
'); 27 | $compile(elt)($rootScope); 28 | expect(elt.html()).toBe(''); 29 | 30 | $rootScope.hey = "*hi*"; 31 | $rootScope.$apply(); 32 | expect(elt.html()).toBe('

hi

'); 33 | }); 34 | 35 | it('should sanitize input', function () { 36 | var elt = angular.element(''); 37 | $compile(elt)($rootScope); 38 | expect(elt.html()).toBe('

window.hacked = true;

'); 39 | expect(window.hacked).toBeUndefined(); 40 | }); 41 | 42 | }); 43 | 44 | describe('markdownConverterProvider', function () { 45 | var $compile, 46 | $rootScope; 47 | 48 | // module that adds config 49 | angular.module('testModule', []). 50 | config(function (markdownConverterProvider) { 51 | markdownConverterProvider.config({ 52 | extensions: ['twitter'] 53 | }); 54 | }); 55 | 56 | beforeEach(module('ngSanitize')); 57 | beforeEach(module('btford.markdown')); 58 | beforeEach(module('testModule')); 59 | 60 | beforeEach(inject(function (_$compile_, _$rootScope_) { 61 | $compile = _$compile_; 62 | $rootScope = _$rootScope_; 63 | })); 64 | 65 | it('should allow extensions', function () { 66 | var elt = angular.element('@briantford'); 67 | $compile(elt)($rootScope); 68 | expect(elt.html()).toBe('

@briantford

'); 69 | }) 70 | }) 71 | --------------------------------------------------------------------------------