';
- }
- throw e;
- }
- }
-
- /**
- * Options
- */
-
- marked.options =
- marked.setOptions = function(opt) {
- merge(marked.defaults, opt);
- return marked;
- };
-
- marked.defaults = {
- gfm: true,
- tables: true,
- breaks: false,
- pedantic: false,
- sanitize: false,
- sanitizer: null,
- mangle: true,
- smartLists: false,
- silent: false,
- highlight: null,
- langPrefix: 'lang-',
- smartypants: false,
- headerPrefix: '',
- renderer: new Renderer,
- xhtml: false
- };
-
- /**
- * Expose
- */
-
- marked.Parser = Parser;
- marked.parser = Parser.parse;
-
- marked.Renderer = Renderer;
-
- marked.Lexer = Lexer;
- marked.lexer = Lexer.lex;
-
- marked.InlineLexer = InlineLexer;
- marked.inlineLexer = InlineLexer.output;
-
- marked.parse = marked;
-
- if (true) {
- module.exports = marked;
- } else if (typeof define === 'function' && define.amd) {
- define(function() { return marked; });
- } else {
- this.marked = marked;
- }
-
- }).call(function() {
- return this || (typeof window !== 'undefined' ? window : global);
- }());
-
- /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
-
-/***/ },
-/* 3 */
-/***/ function(module, exports, __webpack_require__) {
-
- module.exports = __webpack_require__(4)
-
- /* webpack only
- if (DEBUG && global.console) {
- console.debug('debug mode')
- }
- */
-
-
-/***/ },
-/* 4 */
-/***/ function(module, exports, __webpack_require__) {
-
- var cou = __webpack_require__(5)
-
- module.exports = cou.extend(_, cou)
-
- __webpack_require__(7)
- __webpack_require__(8)
- __webpack_require__(9)
- __webpack_require__(11)
- __webpack_require__(12)
- __webpack_require__(13)
-
- _.mixin(_, _)
-
- function _(val) {
- if (!(this instanceof _)) return new _(val)
- this.__value = val
- this.__chain = false
- }
-
-
-
-/***/ },
-/* 5 */
-/***/ function(module, exports, __webpack_require__) {
-
- var is = __webpack_require__(6)
-
- var slice = [].slice
-
- var _ = exports
-
- _.is = is
-
- _.extend = _.assign = extend
-
- _.each = each
-
- _.map = function(arr, fn) {
- var ret = []
- each(arr, function(item, i, arr) {
- ret[i] = fn(item, i, arr)
- })
- return ret
- }
-
- _.filter = function(arr, fn) {
- var ret = []
- each(arr, function(item, i, arr) {
- var val = fn(item, i, arr)
- if (val) ret.push(item)
- })
- return ret
- }
-
- _.some = function(arr, fn) {
- return -1 != findIndex(arr, fn)
- }
-
- _.every = function(arr, fn) {
- return -1 == findIndex(arr, negate(fn))
- }
-
- _.reduce = reduce
-
- _.findIndex = findIndex
-
- _.find = function(arr, fn) {
- var index = _.findIndex(arr, fn)
- if (-1 != index) {
- return arr[index]
- }
- }
-
- _.indexOf = indexOf
-
- _.includes = function(val, sub) {
- return -1 != indexOf(val, sub)
- }
-
- _.toArray = toArray
-
- _.slice = function(arr, start, end) {
- // support array and string
- var ret = [] // default return array
- var len = getLength(arr)
- if (len >= 0) {
- start = start || 0
- end = end || len
- // raw array and string use self slice
- if (!is.fn(arr.slice)) {
- arr = toArray(arr)
- }
- ret = arr.slice(start, end)
- }
- return ret
- }
-
- _.negate = negate
-
- _.forIn = forIn
-
- _.keys = keys
-
- var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g
-
- _.trim = function(str) {
- if (null == str) return ''
- return ('' + str).replace(rtrim, '')
- }
-
- _.noop = function() {}
-
- _.len = getLength
-
- function getLength(arr) {
- if (null != arr) return arr.length
- }
-
- function each(arr, fn) {
- var len = getLength(arr)
- if (len && is.fn(fn)) {
- for (var i = 0; i < len; i++) {
- if (false === fn(arr[i], i, arr)) break
- }
- }
- return arr
- }
-
- function findIndex(arr, fn) {
- var ret = -1
- each(arr, function(item, i, arr) {
- if (fn(item, i, arr)) {
- ret = i
- return false
- }
- })
- return ret
- }
-
- function toArray(arr) {
- var ret = []
- each(arr, function(item) {
- ret.push(item)
- })
- return ret
- }
-
-
- function extend(target) {
- if (target) {
- var sources = slice.call(arguments, 1)
- each(sources, function(src) {
- forIn(src, function(val, key) {
- if (!is.undef(val)) {
- target[key] = val
- }
- })
- })
- }
- return target
- }
-
- function negate(fn) {
- return function() {
- return !fn.apply(this, arguments)
- }
- }
-
- function indexOf(val, sub) {
- if (is.string(val)) return val.indexOf(sub)
-
- return findIndex(val, function(item) {
- // important!
- return sub === item
- })
- }
-
- function reduce(arr, fn, prev) {
- each(arr, function(item, i) {
- prev = fn(prev, item, i, arr)
- })
- return prev
- }
-
- function forIn(hash, fn) {
- if (hash) {
- for (var key in hash) {
- if (is.owns(hash, key)) {
- if (false === fn(hash[key], key, hash)) break
- }
- }
- }
- return hash
- }
-
- function keys(hash) {
- var ret = []
- forIn(hash, function(val, key) {
- ret.push(key)
- })
- return ret
- }
-
-
-
-/***/ },
-/* 6 */
-/***/ function(module, exports) {
-
- /* WEBPACK VAR INJECTION */(function(global) {var is = exports
-
- var obj = Object.prototype
-
- var navigator = global.navigator
-
- // reserved words in es3: instanceof null undefined arguments boolean false true function int
- // only have is.string and is.object, not is.str and is.obj
- // instanceof null undefined arguments boolean false true function int
-
- is.browser = function() {
- if (!is.wechatApp()) {
- if (navigator && global.window == global) {
- return true
- }
- }
- return false
- }
-
- // simple modern browser detect
- is.h5 = function() {
- if (is.browser() && navigator.geolocation) {
- return true
- }
- return false
- }
-
- is.mobile = function() {
- if (is.browser() && /mobile/i.test(navigator.userAgent)) {
- return true
- }
- return false
- }
-
- is.wechatApp = function() {
- if ('object' == typeof wx) {
- if (wx && is.fn(wx.createVideoContext)) {
- // wechat js sdk has no createVideoContext
- return true
- }
- }
- return false
- }
-
- function _class(val) {
- var name = obj.toString.call(val)
- // [object Class]
- return name.substring(8, name.length - 1).toLowerCase()
- }
-
- function _type(val) {
- // undefined object boolean number string symbol function
- return typeof val
- }
-
- function owns(owner, key) {
- return obj.hasOwnProperty.call(owner, key)
- }
-
- is._class = _class
-
- is._type = _type
-
- is.owns = owns
-
- // not a number
- is.nan = function(val) {
- return val !== val
- }
-
- is.bool = function(val) {
- return 'boolean' == _class(val)
- }
-
- is.infinite = function(val) {
- return val == Infinity || val == -Infinity
- }
-
- is.number = function(num) {
- return !isNaN(num) && 'number' == _class(num)
- }
-
- // integer or decimal
- is.iod = function(val) {
- if (is.number(val) && !is.infinite(val)) {
- return true
- }
- return false
- }
-
- is.decimal = function(val) {
- if (is.iod(val)) {
- return 0 != val % 1
- }
- return false
- }
-
- is.integer = function(val) {
- if (is.iod(val)) {
- return 0 == val % 1
- }
- return false
- }
-
- // object or function
- is.oof = function(val) {
- if (val) {
- var tp = _type(val)
- return 'object' == tp || 'function' == tp
- }
- return false
- }
-
- // regexp should return object
- is.object = function(obj) {
- return is.oof(obj) && 'function' != _class(obj)
- }
-
- is.hash = is.plainObject = function(hash) {
- if (hash) {
- if ('object' == _class(hash)) {
- // old window is object
- if (hash.nodeType || hash.setInterval) {
- return false
- }
- return true
- }
- }
- return false
- }
-
- is.undef = function(val) {
- return 'undefined' == _type(val)
- }
-
- // host function should return function, e.g. alert
- is.fn = function(fn) {
- return 'function' == _class(fn)
- }
-
- is.string = function(str) {
- return 'string' == _class(str)
- }
-
- // number or string
- is.nos = function(val) {
- return is.iod(val) || is.string(val)
- }
-
- is.array = function(arr) {
- return 'array' == _class(arr)
- }
-
- is.arraylike = function(arr) {
- // window has length for iframe too, but it is not arraylike
- if (!is.window(arr) && is.object(arr)) {
- var len = arr.length
- if (is.integer(len) && len >= 0) {
- return true
- }
- }
- return false
- }
-
- is.window = function(val) {
- if (val && val.window == val) {
- return true
- }
- return false
- }
-
- is.empty = function(val) {
- if (is.string(val) || is.arraylike(val)) {
- return 0 === val.length
- }
- if (is.hash(val)) {
- for (var key in val) {
- if (owns(val, key)) {
- return false
- }
- }
- }
- return true
- }
-
- is.element = function(elem) {
- if (elem && 1 === elem.nodeType) {
- return true
- }
- return false
- }
-
- is.regexp = function(val) {
- return 'regexp' == _class(val)
- }
-
-
- /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
-
-/***/ },
-/* 7 */
-/***/ function(module, exports, __webpack_require__) {
-
- var _ = module.exports = __webpack_require__(4)
-
- var each = _.each
- var includes = _.includes
- var is = _.is
- var proto = Array.prototype
-
- _.reject = function(arr, fn) {
- return _.filter(arr, function(val, i, arr) {
- return !fn(val, i, arr)
- })
- }
-
- _.without = function(arr) {
- var other = _.slice(arguments, 1)
- return _.difference(arr, other)
- }
-
- _.difference = function(arr, other) {
- var ret = []
- _.each(arr, function(val) {
- if (!includes(other, val)) {
- ret.push(val)
- }
- })
- return ret
- }
-
- _.pluck = function(arr, key) {
- return _.map(arr, function(item) {
- if (item) return item[key]
- })
- }
-
- _.size = function(arr) {
- var len = _.len(arr)
- if (null == len) {
- len = _.keys(arr).length
- }
- return len
- }
-
- _.first = function(arr) {
- if (arr) return arr[0]
- }
-
- _.last = function(arr) {
- var len = _.len(arr)
- if (len) {
- return arr[len - 1]
- }
- }
-
- _.asyncMap = function(arr, fn, cb) {
- // desperate
- var ret = []
- var count = 0
- var hasDone, hasStart
-
- each(arr, function(arg, i) {
- hasStart = true
- fn(arg, function(err, val) {
- if (hasDone) return
- count++
- if (err) {
- hasDone = true
- return cb(err)
- }
- ret[i] = val
- if (count == arr.length) {
- hasDone = true
- cb(null, ret)
- }
- })
- })
-
- if (!hasStart) cb(null) // empty
- }
-
- _.uniq = function(arr) {
- return _.uniqBy(arr)
- }
-
- _.uniqBy = function(arr, fn) {
- var ret = []
- var pool = []
- if (!is.fn(fn)) {
- fn = null
- }
- each(arr, function(item) {
- var val = item
- if (fn) {
- val = fn(item)
- }
- if (!includes(pool, val)) {
- pool.push(val)
- ret.push(item)
- }
- })
- return ret
- }
-
- _.flatten = function(arrs) {
- var ret = []
- each(arrs, function(arr) {
- if (is.arraylike(arr)) {
- each(arr, function(item) {
- ret.push(item)
- })
- } else ret.push(arr)
- })
- return ret
- }
-
- _.union = function() {
- return _.uniq(_.flatten(arguments))
- }
-
- _.sample = function(arr, n) {
- var ret = _.toArray(arr)
- var len = ret.length
- var need = Math.min(n || 1, len)
- for (var i = 0; i < len; i++) {
- var rand = _.random(i, len - 1)
- var tmp = ret[rand]
- ret[rand] = ret[i]
- ret[i] = tmp
- }
- ret.length = need
- if (null == n) {
- return ret[0]
- }
- return ret
- }
-
- _.shuffle = function(arr) {
- return _.sample(arr, Infinity)
- }
-
- _.compact = function(arr) {
- return _.filter(arr, _.identity)
- }
-
- _.rest = function(arr) {
- return _.slice(arr, 1)
- }
-
- _.invoke = function() {
- var args = arguments
- var arr = args[0]
- var fn = args[1]
- var isFunc = is.fn(fn)
- args = _.slice(args, 2)
-
- return _.map(arr, function(item) {
- if (isFunc) {
- return fn.apply(item, args)
- }
- if (null != item) {
- var method = item[fn]
- if (is.fn(method)) {
- return method.apply(item, args)
- }
- }
- })
- }
-
- _.partition = function(arr, fn) {
- var hash = _.groupBy(arr, function(val, i, arr) {
- var ret = fn(val, i, arr)
- if (ret) return 1
- return 2
- })
- return [hash[1] || [], hash[2] || []]
- }
-
- _.groupBy = function(arr, fn) {
- var hash = {}
- _.each(arr, function(val, i, arr) {
- var ret = fn(val, i, arr)
- hash[ret] = hash[ret] || []
- hash[ret].push(val)
- })
- return hash
- }
-
- _.range = function() {
- var args = arguments
- if (args.length < 2) {
- return _.range(args[1], args[0])
- }
- var start = args[0] || 0
- var last = args[1] || 0
- var step = args[2]
- if (!is.number(step)) {
- step = 1
- }
- var count = last - start
- if (0 != step) {
- count = count / step
- }
- var ret = []
- var val = start
- for (var i = 0; i < count; i++) {
- ret.push(val)
- val += step
- }
- return ret
- }
-
- _.pullAt = function(arr) {
- // `_.at` but mutate
- var indexes = _.slice(arguments, 1)
- return mutateDifference(arr, indexes)
- }
-
- function mutateDifference(arr, indexes) {
- var ret = []
- var len = _.len(indexes)
- if (len) {
- indexes = indexes.sort(function(a, b) {
- return a - b
- })
- while (len--) {
- var index = indexes[len]
- ret.push(proto.splice.call(arr, index, 1)[0])
- }
- }
- ret.reverse()
- return ret
- }
-
- _.remove = function(arr, fn) {
- // `_.filter` but mutate
- var len = _.len(arr) || 0
- var indexes = []
- while (len--) {
- if (fn(arr[len], len, arr)) {
- indexes.push(len)
- }
- }
- return mutateDifference(arr, indexes)
- }
-
- _.fill = function(val, start, end) {
- // TODO
- }
-
-
-/***/ },
-/* 8 */
-/***/ function(module, exports, __webpack_require__) {
-
- var _ = module.exports = __webpack_require__(4)
-
- var is = _.is
- var each = _.each
- var forIn = _.forIn
-
- _.only = function(obj, keys) {
- obj = obj || {}
- if (is.string(keys)) keys = keys.split(/ +/)
- return _.reduce(keys, function(ret, key) {
- if (null != obj[key]) ret[key] = obj[key]
- return ret
- }, {})
- }
-
- _.values = function(obj) {
- return _.map(_.keys(obj), function(key) {
- return obj[key]
- })
- }
-
- _.pick = function(obj, fn) {
- if (!is.fn(fn)) {
- return _.pick(obj, function(val, key) {
- return key == fn
- })
- }
- var ret = {}
- forIn(obj, function(val, key, obj) {
- if (fn(val, key, obj)) {
- ret[key] = val
- }
- })
- return ret
- }
-
- _.functions = function(obj) {
- return _.keys(_.pick(obj, function(val) {
- return is.fn(val)
- }))
- }
-
- _.mapKeys = function(obj, fn) {
- var ret = {}
- forIn(obj, function(val, key, obj) {
- var newKey = fn(val, key, obj)
- ret[newKey] = val
- })
- return ret
- }
-
- _.mapObject = _.mapValues = function(obj, fn) {
- var ret = {}
- forIn(obj, function(val, key, obj) {
- ret[key] = fn(val, key, obj)
- })
- return ret
- }
-
- // return value when walk through path, otherwise return empty
- _.get = function(obj, path) {
- path = toPath(path)
- if (path.length) {
- var flag = _.every(path, function(key) {
- if (null != obj) { // obj can be indexed
- obj = obj[key]
- return true
- }
- })
- if (flag) return obj
- }
- }
-
- _.has = function(obj, path) {
- path = toPath(path)
- if (path.length) {
- var flag = _.every(path, function(key) {
- if (null != obj && is.owns(obj, key)) {
- obj = obj[key]
- return true
- }
- })
- if (flag) return true
- }
- return false
- }
-
- _.set = function(obj, path, val) {
- path = toPath(path)
- var cur = obj
- _.every(path, function(key, i) {
- if (is.oof(cur)) {
- if (i + 1 == path.length) {
- cur[key] = val
- } else {
- var item = cur[key]
- if (null == item) {
- // fill value with {} or []
- var item = {}
- if (~~key == key) {
- item = []
- }
- }
- cur = cur[key] = item
- return true
- }
- }
- })
- return obj
- }
-
- _.create = (function() {
- function Object() {} // so it seems like Object.create
- return function(proto, property) {
- // not same as Object.create, Object.create(proto, propertyDescription)
- if ('object' != typeof proto) {
- // null is ok
- proto = null
- }
- Object.prototype = proto
- return _.extend(new Object, property)
- }
- })()
-
- _.defaults = function() {
- var args = arguments
- var target = args[0]
- var sources = _.slice(args, 1)
- if (target) {
- _.each(sources, function(src) {
- _.mapObject(src, function(val, key) {
- if (is.undef(target[key])) {
- target[key] = val
- }
- })
- })
- }
- return target
- }
-
- _.isMatch = function(obj, src) {
- var ret = true
- obj = obj || {}
- forIn(src, function(val, key) {
- if (val !== obj[key]) {
- ret = false
- return false
- }
- })
- return ret
- }
-
- _.toPlainObject = function(val) {
- var ret = {}
- forIn(val, function(val, key) {
- ret[key] = val
- })
- return ret
- }
-
- _.invert = function(obj) {
- var ret = {}
- forIn(obj, function(val, key) {
- ret[val] = key
- })
- return ret
- }
-
- // topath, copy from lodash
-
- var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g
- var reEscapeChar = /\\(\\)?/g;
-
- function toPath(val) {
- if (is.array(val)) return val
- var ret = []
- _.tostr(val).replace(rePropName, function(match, number, quote, string) {
- var item = number || match
- if (quote) {
- item = string.replace(reEscapeChar, '$1')
- }
- ret.push(item)
- })
- return ret
- }
-
-
-/***/ },
-/* 9 */
-/***/ function(module, exports, __webpack_require__) {
-
- var _ = module.exports = __webpack_require__(4)
-
- var is = _.is
- var slice = _.slice
-
- _.bind = function(fn, ctx) {
- if (is.string(ctx)) {
- var obj = fn
- fn = obj[ctx]
- ctx = obj
- }
- if (!is.fn(fn)) return fn
- var args = slice(arguments, 2)
- ctx = ctx || this
- return function() {
- return fn.apply(ctx, _.flatten([args, arguments]))
- }
- }
-
- // from lang.js `Function.prototype.inherits`
- // so belong to function
- _.inherits = function(ctor, superCtor) {
- ctor.super_ = superCtor
- ctor.prototype = _.create(superCtor.prototype, {
- constructor: ctor
- })
- }
-
- _.delay = function(fn, wait) {
- var args = _.slice(arguments, 2)
- return setTimeout(function() {
- fn.apply(this, args)
- }, wait)
- }
-
- _.before = function(n, fn) {
- return function() {
- if (n > 1) {
- n--
- return fn.apply(this, arguments)
- }
- }
- }
-
- _.once = function(fn) {
- return _.before(2, fn)
- }
-
- _.after = function(n, fn) {
- return function() {
- if (n > 1) {
- n--
- } else {
- return fn.apply(this, arguments)
- }
- }
- }
-
- _.throttle = function(fn, wait, opt) {
- wait = wait || 0
- opt = _.extend({
- leading: true,
- trailing: true,
- maxWait: wait
- }, opt)
- return _.debounce(fn, wait, opt)
- }
-
- _.debounce = function(fn, wait, opt) {
- wait = wait || 0
- opt = _.extend({
- leading: false,
- trailing: true
- }, opt)
- var maxWait = opt.maxWait
- var lastExec = 0 // wait
- var lastCall = 0 // just for maxWait
- var now = _.now()
- var timer
-
- if (!opt.leading) {
- lastExec = now
- }
-
- function ifIsCD() {
- if (now - lastExec > wait) return false
- if (maxWait && now - lastCall > maxWait) return false
- return true
- }
-
- function exec(fn, ctx, args) {
- lastExec = _.now() // update last exec
- return fn.apply(ctx, args)
- }
-
- function cancel() {
- if (timer) {
- clearTimeout(timer)
- timer = null
- }
- }
-
- function debounced() {
- now = _.now() // update now
- var isCD = ifIsCD()
- lastCall = now // update last call
- var me = this
- var args = arguments
-
- cancel()
-
- if (!isCD) {
- exec(fn, me, args)
- } else {
- if (opt.trailing) {
- timer = _.delay(function() {
- exec(fn, me, args)
- }, wait)
- }
- }
- }
-
- debounced.cancel = cancel
-
- return debounced
- }
-
- function memoize(fn) {
- var cache = new memoize.Cache
- function memoized() {
- var args = arguments
- var key = args[0]
- if (!cache.has(key)) {
- var ret = fn.apply(this, args)
- cache.set(key, ret)
- }
- return cache.get(key)
- }
- memoized.cache = cache
- return memoized
- }
-
- memoize.Cache = __webpack_require__(10)
-
- _.memoize = memoize
-
- _.wrap = function(val, fn) {
- return function() {
- var args = [val]
- args.push.apply(args, arguments)
- return fn.apply(this, args)
- }
- }
-
- _.curry = function(fn) {
- var len = fn.length
- return setter([])
-
- function setter(args) {
- return function() {
- var arr = args.concat(_.slice(arguments))
- if (arr.length >= len) {
- arr.length = len
- return fn.apply(this, arr)
- }
- return setter(arr)
- }
- }
- }
-
-
-/***/ },
-/* 10 */
-/***/ function(module, exports, __webpack_require__) {
-
- var _ = __webpack_require__(4)
- var is = _.is
-
- module.exports = Cache
-
- function Cache() {
- this.data = {}
- }
-
- var proto = Cache.prototype
-
- proto.has = function(key) {
- return is.owns(this.data, key)
- }
-
- proto.get = function(key) {
- return this.data[key]
- }
-
- proto.set = function(key, val) {
- this.data[key] = val
- }
-
- proto['delete'] = function(key) {
- delete this.data[key]
- }
-
-
-/***/ },
-/* 11 */
-/***/ function(module, exports, __webpack_require__) {
-
- var _ = module.exports = __webpack_require__(4)
- var is = _.is
-
- _.now = function() {
- return +new Date
- }
-
- _.constant = function(val) {
- return function() {
- return val
- }
- }
-
- _.identity = function(val) {
- return val
- }
-
- _.random = function(min, max) {
- return min + Math.floor(Math.random() * (max - min + 1))
- }
-
- _.mixin = function(dst, src, opt) {
- var keys = _.functions(src)
- if (dst) {
- if (is.fn(dst)) {
- opt = opt || {}
- var isChain = !!opt.chain
- // add to prototype
- var proto = dst.prototype
- _.each(keys, function(key) {
- var fn = src[key]
- proto[key] = function() {
- var me = this
- var args = [me.__value]
- args.push.apply(args, arguments)
- var ret = fn.apply(me, args)
- if (me.__chain) {
- me.__value = ret
- return me
- }
- return ret
- }
- })
- } else {
- _.each(keys, function(key) {
- dst[key] = src[key]
- })
- }
- }
- return dst
- }
-
- _.chain = function(val) {
- var ret = _(val)
- ret.__chain = true
- return ret
- }
-
- _.value = function() {
- this.__chain = false
- return this.__value
- }
-
-
-/***/ },
-/* 12 */
-/***/ function(module, exports, __webpack_require__) {
-
- var _ = module.exports = __webpack_require__(4)
-
- _.tostr = tostr // lodash toString
-
- var indexOf = _.indexOf
-
- _.split = function(str, separator, limit) {
- str = tostr(str)
- return str.split(separator, limit)
- }
-
- _.capitalize = function(str) {
- str = tostr(str)
- return str.charAt(0).toUpperCase() + str.substr(1)
- }
-
- _.decapitalize = function(str) {
- str = tostr(str)
- return str.charAt(0).toLowerCase() + str.substr(1)
- }
-
- _.camelCase = function(str) {
- str = tostr(str)
- var arr = str.split(/[^\w]|_+/)
- arr = _.map(arr, function(val) {
- return _.capitalize(val)
- })
- return _.decapitalize(arr.join(''))
- }
-
- _.startsWith = function(str, val) {
- return 0 == indexOf(str, val)
- }
-
- _.endsWith = function(str, val) {
- val += '' // null => 'null'
- return val == _.slice(str, _.len(str) - _.len(val))
- }
-
- _.lower = function(str) {
- // lodash toLower
- return tostr(str).toLowerCase()
- }
-
- _.upper = function(str) {
- // lodash toUpper
- return tostr(str).toUpperCase()
- }
-
- _.repeat = function(str, count) {
- return _.map(_.range(count), function() {
- return str
- }).join('')
- }
-
- _.padStart = function(str, len, chars) {
- str = _.tostr(str)
- len = len || 0
- var delta = len - str.length
- return getPadStr(chars, delta) + str
- }
-
- _.padEnd = function(str, len, chars) {
- str = _.tostr(str)
- len = len || 0
- var delta = len - str.length
- return str + getPadStr(chars, delta)
- }
-
- function getPadStr(chars, len) {
- chars = _.tostr(chars) || ' ' // '' will never end
- var count = Math.floor(len / chars.length) + 1
- return _.repeat(chars, count).slice(0, len)
- }
-
- function tostr(str) {
- if (str || 0 == str) return str + ''
- return ''
- }
-
-
-/***/ },
-/* 13 */
-/***/ function(module, exports, __webpack_require__) {
-
- var _ = module.exports = __webpack_require__(4)
-
- _.sum = function(arr) {
- return _.reduce(arr, function(sum, val) {
- return sum + val
- }, 0)
- }
-
- _.max = function(arr, fn) {
- var index = -1
- var data = -Infinity
- fn = fn || _.identity
- _.each(arr, function(val, i) {
- val = fn(val)
- if (val > data) {
- data = val
- index = i
- }
- })
- if (index > -1) {
- return arr[index]
- }
- return data
- }
-
- _.min = function(arr, fn) {
- var index = -1
- var data = Infinity
- fn = fn || _.identity
- _.each(arr, function(val, i) {
- val = fn(val)
- if (val < data) {
- data = val
- index = i
- }
- })
- if (index > -1) {
- return arr[index]
- }
- return data
- }
-
-
-/***/ },
-/* 14 */
-/***/ function(module, exports, __webpack_require__) {
-
- var _ = __webpack_require__(3)
- var is = _.is
-
- var defaultOption = {
- sep: '&',
- eq: '=',
- encode: encodeURIComponent,
- decode: decodeURIComponent,
- keepRaw: false,
- sort: null,
- ignoreValues: [undefined]
- }
-
- exports.parse = function(qs, sep, eq, opt) {
- qs += ''
- opt = getOpt(sep, eq, opt)
- var decode = opt.decode
- // var ret = {}
- qs = qs.split(opt.sep)
-
- return _.reduce(qs, function(ret, arr) {
- arr = arr.split(opt.eq)
- if (2 == arr.length) {
- var k = arr[0]
- var v = arr[1]
- if (!opt.keepRaw) {
- try {
- k = decode(k)
- v = decode(v)
- } catch (ignore) {}
- }
- ret[k] = v
- }
- return ret
- }, {})
- }
-
- exports.stringify = function(obj, sep, eq, opt) {
- opt = getOpt(sep, eq, opt)
-
- var keys = _.keys(obj)
-
- var sort = opt.sort
- if (sort) {
- if (is.fn(sort)) {
- keys.sort(sort)
- } else {
- keys.sort()
- }
- }
-
- var encode = opt.encode
-
- var arr = []
- _.each(keys, function(key) {
- var val = obj[key]
- if (!_.includes(opt.ignoreValues, val)) {
- if (is.nan(val) || null == val) {
- val = ''
- }
- if (!opt.keepRaw) {
- key = encode(key)
- val = encode(val)
- }
- arr.push(key + opt.eq + val)
- }
- })
- return arr.join(opt.sep)
- }
-
- function getOpt(sep, eq, opt) {
- // can be
- // _
- // opt
- // sep, opt
- // sep, eq, opt
- opt = _.find(arguments, function(val) {
- return is.object(val)
- })
- sep = is.nos(sep) ? sep : undefined
- eq = is.nos(eq) ? eq : undefined
- opt = _.extend({}, defaultOption, opt, {sep: sep, eq: eq})
- return opt
- }
-
-
-
-/***/ },
-/* 15 */
-/***/ function(module, exports) {
-
- module.exports = "# h1\n\nhead1\n===\n\nhead2\n---\n\n### head3 ###\n\n- **strong**\n- *emphasis*\n- ~~del~~\n- `code inline`\n\n> block quote\n\n[github link address](https://github.com/chunpu/markdown2confluence)\n\n```javascript\nvar i = 1 // comment\nconsole.log(\"This is code block\")\n```\n\n![image](https://www.google.com.hk/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png)\n\n## GFM support\n\nFirst Header | Second Header\n------------- | -------------\nContent Cell | Content Cell\nContent Cell | Content Cell\n*inline style* | **inline style**\n\n:)\n"
-
-/***/ }
-/******/ ]);
\ No newline at end of file
diff --git a/browser/index.html b/browser/index.html
deleted file mode 100644
index 0d9a9cf..0000000
--- a/browser/index.html
+++ /dev/null
@@ -1,5 +0,0 @@
-markdown2confluence
put markdown here
generated confluence markup
\ No newline at end of file
diff --git a/browser/index.jade b/browser/index.jade
deleted file mode 100644
index e85cca9..0000000
--- a/browser/index.jade
+++ /dev/null
@@ -1,23 +0,0 @@
-block title
- title markdown2confluence
-
-extend layout/basic
-
-block style
- style.
- textarea {
- width: 600px;
- height: 400px;
- }
-
-block main
- h1 put markdown here
- textarea#markdown
- p
- button#convert convert
- h1 generated confluence markup
- textarea#markup
-
-block script
- script(src='//code.jquery.com/jquery-1.11.2.min.js')
- script(src='./bundle.js')
diff --git a/demo.md b/demo.md
index a09bb20..04f568c 100644
--- a/demo.md
+++ b/demo.md
@@ -1,35 +1,81 @@
-# h1
+Heading 1
+=========
-head1
-===
+This is a paragraph of text. So far pretty uninteresting.
+If you have GFM enabled, this should be on the next line.
-head2
----
-### head3 ###
+## Subheading (level 2)
+
+> Amazing things happen when people work together!
+
+The above is a quote.
+
-- **strong**
-- *emphasis*
-- ~~del~~
-- `code inline`
+## Formatting
-> block quote
+Strikethrough text looks ~~amazing~~ acceptable.
-[github link address](https://github.com/chunpu/markdown2confluence)
+Bolded text is really called **strong**. Italicized text shows *emphasis*.
+
+
+## Code Blocks
```javascript
-var i = 1 // comment
-console.log("This is code block")
+// This is JavaScript
+console.log("This is JavaScript");
```
-![image](https://www.google.com.hk/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png)
+And `code()` within normal text is ok too.
+
+ Code without a language should not have special formatting.
+
+
+## Lists
+
+Careful with lists. There needs to be two blank lines after the ordered list otherwise the ordered list thinks it continues with the unordered list.
+
+1. Ordered list
+2. Second item
+
+
+* Unordered list
+* Second item
+
+This list is a bit more complex.
+
+* Heading, unordered
+ 1. Subheading, ordered
+ * Third item, unordered
+ 2. Subheading 2
+ 1. Alternate third item, ordered
+
+
+## Tables
+
+| Heading 1 | Heading 2 |
+|-----------------|-----------------|
+| Row 1, Column 1 | Row 1, Column 2 |
+| Row 2, Column 1 | Row 2, Column 2 |
+
+
+## Other
+
+This is a horizontal rule.
+
+---
+
+Here is another.
+
+----------
+
+![broken image][IMG] <-- That is a broken image to [this url][IMG] and the "broken_image" alt text is unfortunately lost.
+
+[IMG]: http://example.com/broken-image.png
-## GFM support
-First Header | Second Header
-------------- | -------------
-Content Cell | Content Cell
-Content Cell | Content Cell
-*inline style* | **inline style**
+## Broken Features
-:)
+
+ HTML is copied directly to the text without conversion. HTML tags will be seen in the resulting page.
+
diff --git a/index.js b/index.js
deleted file mode 100644
index c1852c3..0000000
--- a/index.js
+++ /dev/null
@@ -1,114 +0,0 @@
-var marked = require('marked')
-var _ = require('min-util')
-var qs = require('min-qs')
-var inlineLexer = marked.inlineLexer
-
-module.exports = exports = markdown2confluence
-
-// https://roundcorner.atlassian.net/secure/WikiRendererHelpAction.jspa?section=all
-// https://confluence.atlassian.com/display/DOC/Confluence+Wiki+Markup
-// http://blogs.atlassian.com/2011/11/why-we-removed-wiki-markup-editor-in-confluence-4/
-
-var MAX_CODE_LINE = 20
-
-function Renderer() {}
-
-var rawRenderer = marked.Renderer
-
-var langArr = 'actionscript3 bash csharp coldfusion cpp css delphi diff erlang groovy java javafx javascript perl php none powershell python ruby scala sql vb html/xml'.split(/\s+/)
-var langMap = {
- shell: 'bash'
-}
-for (var i = 0, x; x = langArr[i++];) {
- langMap[x] = x
-}
-
-_.extend(Renderer.prototype, rawRenderer.prototype, {
- paragraph: function(text) {
- return text + '\n\n'
- }
- , html: function(html) {
- return html
- }
- , heading: function(text, level, raw) {
- return 'h' + level + '. ' + text + '\n\n'
- }
- , strong: function(text) {
- return '*' + text + '*'
- }
- , em: function(text) {
- return '_' + text + '_'
- }
- , del: function(text) {
- return '-' + text + '-'
- }
- , codespan: function(text) {
- return '{{' + text + '}}'
- }
- , blockquote: function(quote) {
- return '{quote}' + quote + '{quote}'
- }
- , br: function() {
- return '\n'
- }
- , hr: function() {
- return '----'
- }
- , link: function(href, title, text) {
- var arr = [href]
- if (title || text) {
- arr.unshift(title || text)
- }
- return '[' + arr.join('|') + ']'
- }
- , list: function(body, ordered) {
- var arr = _.filter(_.trim(body).split('\n'), function(line) {
- return line
- })
- var type = ordered ? '#' : '*'
- return _.map(arr, function(line) {
- return type + ' ' + line
- }).join('\n') + '\n\n'
-
- }
- , listitem: function(body, ordered) {
- return body + '\n'
- }
- , image: function(href, title, text) {
- return '!' + href + '!'
- }
- , table: function(header, body) {
- return header + body + '\n'
- }
- , tablerow: function(content, flags) {
- return content + '\n'
- }
- , tablecell: function(content, flags) {
- var type = flags.header ? '||' : '|'
- return type + content
- }
- , code: function(code, lang) {
- // {code:language=java|borderStyle=solid|theme=RDark|linenumbers=true|collapse=true}
- lang = langMap[lang] || ''
- var param = {
- language: lang,
- borderStyle: 'solid',
- theme: 'RDark', // dark is good
- linenumbers: true,
- collapse: false
- }
- var lineCount = _.split(code, '\n').length
- if (lineCount > MAX_CODE_LINE) {
- // code is too long
- param.collapse = true
- }
- param = qs.stringify(param, '|', '=')
- return '{code:' + param + '}\n' + code + '\n{code}\n\n'
- }
-})
-
-var renderer = new Renderer()
-
-function markdown2confluence(markdown) {
- return marked(markdown, {renderer: renderer})
-}
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000..72124bd
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,488 @@
+"use strict";
+
+var defaultLanguageMap, marked, querystring;
+
+marked = require("marked");
+querystring = require("querystring");
+
+// https://confluence.atlassian.com/doc/code-block-macro-139390.html
+defaultLanguageMap = {
+ "": "none",
+ actionscript3: "actionscript3",
+ bash: "bash",
+ csharp: "csharp",
+ coldfusion: "coldfusion",
+ cpp: "cpp",
+ css: "css",
+ delphi: "delphi",
+ diff: "diff",
+ erlang: "erlang",
+ groovy: "groovy",
+ html: "html",
+ java: "java",
+ javafx: "javafx",
+ javascript: "javascript",
+ js: "javascript",
+ perl: "perl",
+ php: "php",
+ powershell: "powershell",
+ python: "python",
+ ruby: "ruby",
+ scala: "scala",
+ shell: "bash",
+ sql: "sql",
+ vb: "vb",
+ xml: "xml"
+};
+
+
+/**
+ * This class is how the marked library translsates markdown into something
+ * else.
+ */
+class ConfluenceRenderer {
+ /**
+ * Creates a new instance. The `options` parameter control a few
+ * tweaks that can be applied by the user in order to render better
+ * markup.
+ *
+ * @param {Object} options
+ */
+ constructor(options) {
+ // Must not save it as `this.options` because marked overwrites
+ // that property.
+ this.renderOptions = options;
+ }
+
+
+ /**
+ * Blockquote.
+ *
+ * > This is a blockquote.
+ *
+ * is changed into
+ *
+ * {quote}
+ * This is a blockquote.
+ * {quote}
+ *
+ * @param {string} text
+ * @return {string}
+ */
+ blockquote(text) {
+ return `{quote}\n${text.trim()}\n{quote}\n\n`;
+ }
+
+
+ /**
+ * A line break. Supposedly if you have a line with 2 or more spaces
+ * followed by a line that doesn't have whitespace, then it turns into
+ * this element. I'm failing to reproduce that scenario.
+ *
+ * @return {string}
+ */
+ br() {
+ return "\n";
+ }
+
+
+ /**
+ * Code block.
+ *
+ * ```js
+ * // JavaScript code
+ * ```
+ *
+ * is changed into
+ *
+ * {code:language=javascript|borderStyle=solid|theme=RDark|linenumbers=true|collapse=false}
+ * // JavaScript code
+ * {code}
+ *
+ * @param {string} text
+ * @param {string} lang
+ * @return {string}
+ */
+ code(text, lang) {
+ var stylingOptions;
+
+ // Simple clone of the options.
+ stylingOptions = JSON.parse(JSON.stringify(this.renderOptions.codeStyling));
+ lang = lang || "";
+ lang = lang.toLowerCase();
+ lang = this.renderOptions.codeLanguageMap[lang] || this.renderOptions.codeLanguageMap[""];
+
+ if (lang) {
+ stylingOptions.language = lang;
+ }
+
+ // If too big, collapse.
+ if (text.split("\n").length > this.renderOptions.codeCollapseAt) {
+ stylingOptions.collapse = true;
+ }
+
+ // Convert to a string
+ stylingOptions = querystring.stringify(stylingOptions, "|");
+
+ if (stylingOptions) {
+ stylingOptions = `:${stylingOptions}`;
+ }
+
+ return `{code${stylingOptions}}\n${text}\n{code}\n\n`;
+ }
+
+
+ /**
+ * Inline code.
+ *
+ * Text that has statements, like `a = true` or similar.
+ *
+ * turns into
+ *
+ * Text that has statements, like {{a = true}} or similar.
+ *
+ * @param {string} text
+ * @return {string}
+ */
+ codespan(text) {
+ return `{{${text}}}`;
+ }
+
+
+ /**
+ * Strikethrough.
+ *
+ * Supported ~~everywhere~~ in GFM only.
+ *
+ * turns into
+ *
+ * Supported -everywhere- in GFM only.
+ *
+ * @param {string} text
+ * @return {string}
+ */
+ del(text) {
+ return `-${text}-`;
+ }
+
+
+ /**
+ * Emphasis.
+ *
+ * Typically this is *italicized* text.
+ *
+ * turns into
+ *
+ * Typically this is _italicized_ text.
+ *
+ * @param {string} text
+ * @return {string}
+ */
+ em(text) {
+ return `_${text}_`;
+ }
+
+
+ /**
+ * Headings 1 through 6.
+ *
+ * Heading 1
+ * =========
+ *
+ * # Heading 1 alternate
+ *
+ * ###### Heading 6
+ *
+ * turns into
+ *
+ * h1. Heading 1
+ *
+ * h1. Heading 1 alternate
+ *
+ * h6. Heading 6
+ *
+ * @param {string} text
+ * @param {number} level
+ * @return {string}
+ */
+ heading(text, level) {
+ return `h${level}. ${text}\n\n`;
+ }
+
+
+ /**
+ * Horizontal rule.
+ *
+ * ---
+ *
+ * turns into
+ *
+ * ----
+ *
+ * @return {string}
+ */
+ hr() {
+ return "----\n\n";
+ }
+
+
+ /**
+ * Embedded HTML.
+ *
+ *
+ *
+ * turns into
+ *
+ *
+ *
+ * @param {string} text
+ * @return {string}
+ */
+ html(text) {
+ return text;
+ }
+
+
+ /**
+ * An embedded image.
+ *
+ * ![alt-text](image-url)
+ *
+ * is changed into
+ *
+ * !image-url!
+ *
+ * Markdown supports alt text and titles. Confluence does not.
+ *
+ * @param {string} href
+ * @return {string}
+ */
+ image(href) {
+ href = this.renderOptions.imageRewrite(href);
+
+ return `!${href}!`;
+ }
+
+
+ /**
+ * Link to another resource.
+ *
+ * [Home](/)
+ * [Home](/ "some title")
+ *
+ * turns into
+ *
+ * [Home|/]
+ * [some title|/]
+ *
+ * @param {string} href
+ * @param {string} title
+ * @param {string} text
+ * @return {string}
+ */
+ link(href, title, text) {
+ // Sadly, one must choose if the link's title should be displayed
+ // or the linked text should be displayed. We picked the linked text.
+ text = text || title;
+
+ if (text) {
+ text += "|";
+ }
+
+ href = this.renderOptions.linkRewrite(href);
+
+ return `[${text}${href}]`;
+ }
+
+
+ /**
+ * Converts a list.
+ *
+ * # ordered
+ * * unordered
+ *
+ * becomes
+ *
+ * # ordered
+ * #* unordered
+ *
+ * Note: This adds an extra "\r" before the list in order to cope
+ * with nested lists better. When there's a "\r" in a nested list, it
+ * is translated into a "\n". When the "\r" is left in the converted
+ * result then it is removed.
+ *
+ * @param {string} text
+ * @param {boolean} ordered
+ * @return {string}
+ */
+ list(text, ordered) {
+ text = text.trim();
+
+ if (ordered) {
+ text = text.replace(/^\*/gm, "#");
+ }
+
+ return `\r${text}\n\n`;
+ }
+
+
+ /**
+ * Changes a list item. Always marks it as an unordered list, but
+ * list() will change it back.
+ *
+ * @param {string} text
+ * @return {string}
+ */
+ listitem(text) {
+ // If a list item has a nested list, it will have a "\r" in the
+ // text. Turn that "\r" into "\n" but trim out other whitespace
+ // from the list.
+ text = text.replace(/\s*$/, "").replace(/\r/g, "\n");
+
+ // Convert newlines followed by a # or a * into sub-list items
+ text = text.replace(/\n([*#])/g, "\n*$1");
+
+ return `* ${text}\n`;
+ }
+
+
+ /**
+ * A paragraph of text.
+ *
+ * @param {string} text
+ * @return {string}
+ */
+ paragraph(text) {
+ return `${text}\n\n`;
+ }
+
+
+ /**
+ * Creates strong text.
+ *
+ * This is typically **bolded**.
+ *
+ * becomes
+ *
+ * This is typically *bolded*.
+ *
+ * @param {string} text
+ * @return {string}
+ */
+ strong(text) {
+ return `*${text}*`;
+ }
+
+
+ /**
+ * Renders a table. Most of the work is done in tablecell.
+ *
+ * @param {string} header
+ * @param {string} body
+ * @return {string}
+ */
+ table(header, body) {
+ return `${header}${body}\n`;
+ }
+
+
+ /**
+ * Converts a table cell. When this is a header, the cell is prefixed
+ * with two bars instead of one.
+ *
+ * @param {string} text
+ * @param {Object} flags
+ * @return {string}
+ */
+ tablecell(text, flags) {
+ var boundary;
+
+ if (flags.header) {
+ boundary = "||";
+ } else {
+ boundary = "|";
+ }
+
+ return `${boundary}${text}`;
+ }
+
+
+ /**
+ * Converts a table row. Most of the work is done in tablecell, however
+ * that can't tell if the cell is at the end of a row or not. Get the
+ * first cell's leading boundary and remove the double-boundary marks.
+ *
+ * @param {string} text
+ * @return {string}
+ */
+ tablerow(text) {
+ var boundary;
+
+ boundary = text.match(/^\|*/);
+
+ if (boundary) {
+ boundary = boundary[0];
+ } else {
+ boundary = "|";
+ }
+
+ return `${text}${boundary}\n`;
+ }
+
+
+ /**
+ * Simple text.
+ *
+ * @param {string} text
+ * @return {string}
+ */
+ text(text) {
+ return text;
+ }
+}
+
+
+/**
+ * Set up a default URI rewriter
+ *
+ * @param {string} href
+ * @return {string}
+ */
+function defaultHrefRewrite(href) {
+ return href;
+}
+
+module.exports = (markdown, options) => {
+ var result;
+
+ // Set defaults.
+ options = options || {};
+ options.marked = options.marked || {};
+ options.codeLanguageMap = options.codeLanguageMap || defaultLanguageMap;
+ options.codeStyling = options.codeStyling || {
+ theme: "RDark",
+ linenumbers: true
+ };
+ options.codeCollapseAt = options.codeCollapseAt || 20;
+ options.linkRewrite = options.linkRewrite || defaultHrefRewrite;
+ options.imageRewrite = options.imageRewrite || defaultHrefRewrite;
+
+ // Always override this one property.
+ options.marked.renderer = new ConfluenceRenderer(options);
+
+ // Replace "\r\n" and "\r" with "\n".
+ markdown = markdown.replace(/\r\n?/g, "\n");
+
+ // Convert.
+ result = marked(markdown, options.marked).trim();
+
+ // Fix the \r placeholder for list beginnings. See list() for more info.
+ result = result.replace(/\r/g, "");
+
+ // Remove trailing whitespace.
+ result = result.trim();
+
+ return result;
+};
+
+module.exports.defaultLanguageMap = defaultLanguageMap;
diff --git a/package.json b/package.json
index 3ed5b00..a88a5c2 100644
--- a/package.json
+++ b/package.json
@@ -1,43 +1,43 @@
{
- "name": "markdown2confluence",
- "version": "1.2.0",
- "description": "convert markdown to confluence markup",
- "main": "index.js",
- "scripts": {
- "test": "node test.js",
- "build": "npm run build-html && npm run build-js",
- "build-html": "jade -p node_modules/jade-gist/. < browser/index.jade > browser/index.html",
- "build-js": "webpack browser/app.js --output-filename=browser/bundle.js",
- "readme": "pretty-readme > readme.md"
- },
- "author": "ft",
- "license": "ISC",
- "dependencies": {
- "marked": "^0.3.2",
- "min-qs": "^1.3.0",
- "min-util": "^2.3.0"
- },
- "bin": {
- "markdown2confluence": "bin/markdown2confluence.js"
- },
- "devDependencies": {
- "jade": "^1.11.0",
- "jade-gist": "^1.0.3",
- "raw-loader": "^0.5.1",
- "webpack": "^1.14.0"
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/chunpu/markdown2confluence.git"
- },
- "keywords": [
- "markdown",
- "confluence",
- "markup",
- "convert"
- ],
- "bugs": {
- "url": "https://github.com/chunpu/markdown2confluence/issues"
- },
- "homepage": "https://github.com/chunpu/markdown2confluence"
+ "name": "markdown2confluence-cws",
+ "version": "2.0.0",
+ "description": "Convert Markdown to Confluence Wiki Markup.",
+ "main": "lib/index.js",
+ "scripts": {
+ "test": "script/test",
+ "watch": "script/watch"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/connected-world-services/markdown2confluence-cws.git"
+ },
+ "keywords": [
+ "markdown",
+ "confluence",
+ "markup",
+ "convert"
+ ],
+ "author": "ft",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/connected-world-services/markdown2confluence-cws/issues"
+ },
+ "homepage": "https://github.com/connected-world-services/markdown2confluence-cws#readme",
+ "devDependencies": {
+ "codecov": "~2.0.2",
+ "eslint": "~3.17.1",
+ "istanbul": "~0.4.5",
+ "jasmine": "~2.5.3",
+ "nodemon": "~1.11.0"
+ },
+ "dependencies": {
+ "get-stdin": "~5.0.1",
+ "marked": "~0.3.6"
+ },
+ "engines": {
+ "node": ">= 4.0.0"
+ },
+ "bin": {
+ "markdown2confluence": "bin/markdown2confluence.js"
+ }
}
diff --git a/preview.png b/preview.png
deleted file mode 100644
index 57e6c9e..0000000
Binary files a/preview.png and /dev/null differ
diff --git a/readme.md b/readme.md
deleted file mode 100644
index 04f92b5..0000000
--- a/readme.md
+++ /dev/null
@@ -1,51 +0,0 @@
-markdown2confluence
-===
-
-[![Build status][travis-image]][travis-url]
-[![NPM version][npm-image]][npm-url]
-[![Downloads][downloads-image]][downloads-url]
-[![Dependency Status][david-image]][david-url]
-[npm-image]: https://img.shields.io/npm/v/markdown2confluence.svg?style=flat-square
-[npm-url]: https://npmjs.org/package/markdown2confluence
-[downloads-image]: http://img.shields.io/npm/dm/markdown2confluence.svg?style=flat-square
-[downloads-url]: https://npmjs.org/package/markdown2confluence
-[david-image]: http://img.shields.io/david/chunpu/markdown2confluence.svg?style=flat-square
-[david-url]: https://david-dm.org/chunpu/markdown2confluence
-
-
-convert markdown to confluence markup
-
-Installation
----
-
-```sh
-npm i markdown2confluence -g
-```
-
-Usage
----
-
-```sh
-markdown2confluence markdown.md
-```
-
-or
-
-Try in browser
-
-Document
----
-
-[Confluence Wiki Markup](https://confluence.atlassian.com/display/CONF42/Confluence+Wiki+Markup)
-
-![demo preview](./preview.png)
-
-License
----
-
-[![License][license-image]][license-url]
-
-[travis-image]: https://img.shields.io/travis/chunpu/markdown2confluence.svg?style=flat-square
-[travis-url]: https://travis-ci.org/chunpu/markdown2confluence
-[license-image]: http://img.shields.io/npm/l/markdown2confluence.svg?style=flat-square
-[license-url]: #
diff --git a/script/test b/script/test
new file mode 100755
index 0000000..d6a78a7
--- /dev/null
+++ b/script/test
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+
+# Turn on strict mode.
+set -eEo pipefail
+
+# Move to repo root
+cd "${0%/*}/.."
+
+# Add node_modules/.bin to PATH
+PATH="$(pwd)/node_modules/.bin:$PATH"
+export PATH
+
+# Run the tests.
+istanbul cover jasmine
+
+# Check the code coverage.
+if [[ "$CI" == "true" ]]; then
+ codecov
+elif [[ -d ".git" ]]; then
+ # Easiest way to generate coverage files and not upload the report
+ # is to use --dump.
+ echo "Executing codecov and generating coverage report."
+ codecov --dump > /dev/null
+else
+ # codecov runs git and hg. If the folder is not under source control,
+ # codecov breaks with some nasty error. This situation happens during
+ # deployment, so we just don't worry about coverage during deployment.
+ echo "Not running code coverage report."
+fi
+
+# Verify the coding standards are enforced.
+eslint .
+
+echo "All tests pass."
diff --git a/script/watch b/script/watch
new file mode 100755
index 0000000..ec5ef25
--- /dev/null
+++ b/script/watch
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+# Turn on strict mode.
+set -eEo pipefail
+
+# Move to repo root
+cd "${0%/*}/.."
+
+# Add node_modules/.bin to PATH
+PATH="$(pwd)/node_modules/.bin:$PATH"
+export PATH
+
+# Set up a watcher
+nodemon --exec jasmine
diff --git a/spec/lib/index.spec.js b/spec/lib/index.spec.js
new file mode 100644
index 0000000..92c300f
--- /dev/null
+++ b/spec/lib/index.spec.js
@@ -0,0 +1,267 @@
+"use strict";
+
+var convert;
+
+convert = require("../../");
+
+describe("markdown2confluence", () => {
+ describe("blockquote", () => {
+ it("converts a single line quote correctly", () => {
+ expect(convert(`Paragraph
+
+> one line quote
+
+Another paragraph`)).toEqual(`Paragraph
+
+{quote}
+one line quote
+{quote}
+
+Another paragraph`);
+ });
+ it("works on multi-line quotes and multiple quotes", () => {
+ expect(convert(`> line 1
+> line 2
+
+inner text
+
+> quote 2
+> More quote 2
+> and more`)).toEqual(`{quote}
+line 1
+line 2
+{quote}
+
+inner text
+
+{quote}
+quote 2
+More quote 2
+and more
+{quote}`);
+ });
+ });
+ describe("br", () => {
+ // Not really sure how to get the "br" code to trigger.
+ });
+ describe("code", () => {
+ it("formats with code fences", () => {
+ expect(convert("```js\nthis is code\n```")).toEqual(`{code:theme=RDark|linenumbers=true|language=javascript}
+this is code
+{code}`);
+ });
+ it("formats with indentation", () => {
+ expect(convert(`
+ // different code
+`)).toEqual(`{code:theme=RDark|linenumbers=true|language=none}
+// different code
+{code}`);
+ });
+ it("uses the language map (lowercased) and code styling options", () => {
+ expect(convert("```Moo\ncow()```", {
+ codeLanguageMap: {
+ moo: "cowspeak"
+ },
+ codeStyling: {
+ anything: "goes_here"
+ }
+ })).toEqual(`{code:anything=goes_here|language=cowspeak}
+cow()
+{code}`);
+ });
+ it("allows 20 lines before collapsing", () => {
+ expect(convert("```\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n```", {
+ codeStyling: {}
+ })).toEqual("{code:language=none}\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n{code}");
+ });
+ it("collapses when too big", () => {
+ expect(convert("```\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n```", {
+ codeStyling: {}
+ })).toEqual("{code:language=none|collapse=true}\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n{code}");
+ });
+ it("collapses at a set number", () => {
+ expect(convert("```\n1\n2\n3\n```", {
+ codeStyling: {},
+ codeCollapseAt: 2
+ })).toEqual("{code:language=none|collapse=true}\n1\n2\n3\n{code}");
+ });
+ });
+ describe("codespan", () => {
+ it("wraps things in braces", () => {
+ expect(convert("text `code` text text `code`")).toEqual("text {{code}} text text {{code}}");
+ });
+ });
+ describe("del / strikethrough", () => {
+ it("converts in GFM", () => {
+ expect(convert("~~thing~~")).toEqual("-thing-");
+ });
+ });
+ describe("em / italics", () => {
+ it("converts", () => {
+ expect(convert("*one* and _two_")).toEqual("_one_ and _two_");
+ });
+ });
+ describe("headings", () => {
+ it("works on multi-line headings", () => {
+ expect(convert("multi-line\n====")).toEqual("h1. multi-line");
+ });
+ it("works on single-line headings", () => {
+ expect(convert("###### single-line")).toEqual("h6. single-line");
+ });
+ });
+ describe("hr", () => {
+ it("provides a horizontal rule", () => {
+ expect(convert("---")).toEqual("----");
+ });
+ it("adds a blank line", () => {
+ expect(convert("--------------\nWords here")).toEqual("----\n\nWords here");
+ });
+ });
+ describe("html", () => {
+ it("embeds HTML", () => {
+ expect(convert("