From 8f6c80cee1bebc2f483babf0b66593dbd0649846 Mon Sep 17 00:00:00 2001 From: Julio Montoya Date: Wed, 29 May 2019 15:59:46 +0200 Subject: [PATCH] Update from 1.11.x --- .../public/assets/linkifyjs/.bower.json | 45 + .../public/assets/linkifyjs/README.md | 21 + .../public/assets/linkifyjs/bower.json | 36 + .../public/assets/linkifyjs/linkify-jquery.js | 293 ++++ .../assets/linkifyjs/linkify-jquery.min.js | 1 + .../public/assets/linkifyjs/linkify.js | 1286 +++++++++++++++++ .../public/assets/linkifyjs/linkify.min.js | 1 + main/inc/lib/javascript/chat/css/chat.css | 229 +-- main/inc/lib/javascript/chat/js/chat.js | 38 +- main/inc/lib/pdf.lib.php | 43 +- main/inc/lib/template.lib.php | 32 +- 11 files changed, 1867 insertions(+), 158 deletions(-) create mode 100644 app/Resources/public/assets/linkifyjs/.bower.json create mode 100644 app/Resources/public/assets/linkifyjs/README.md create mode 100644 app/Resources/public/assets/linkifyjs/bower.json create mode 100644 app/Resources/public/assets/linkifyjs/linkify-jquery.js create mode 100644 app/Resources/public/assets/linkifyjs/linkify-jquery.min.js create mode 100644 app/Resources/public/assets/linkifyjs/linkify.js create mode 100644 app/Resources/public/assets/linkifyjs/linkify.min.js diff --git a/app/Resources/public/assets/linkifyjs/.bower.json b/app/Resources/public/assets/linkifyjs/.bower.json new file mode 100644 index 0000000000..d5dcba2c7d --- /dev/null +++ b/app/Resources/public/assets/linkifyjs/.bower.json @@ -0,0 +1,45 @@ +{ + "name": "linkifyjs", + "main": "linkify.js", + "version": "2.1.8", + "authors": [ + "SoapBox Innovations Inc. " + ], + "description": "Intelligent link recognition, made easy", + "keywords": [ + "node", + "js", + "jquery", + "link", + "autolink", + "text", + "url", + "email", + "hashtag", + "hashtags", + "mention", + "mentions" + ], + "license": "MIT", + "homepage": "http://soapbox.github.io/linkifyjs/", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "jquery": ">=1.7.0" + }, + "_release": "2.1.8", + "_resolution": { + "type": "version", + "tag": "v2.1.8", + "commit": "e7fbf85e679af9ef786a6e5ab83de546a371cbac" + }, + "_source": "https://github.com/nfrasser/linkify-shim.git", + "_target": "^2.1.8", + "_originalSource": "linkifyjs", + "_direct": true +} \ No newline at end of file diff --git a/app/Resources/public/assets/linkifyjs/README.md b/app/Resources/public/assets/linkifyjs/README.md new file mode 100644 index 0000000000..3bdefcc234 --- /dev/null +++ b/app/Resources/public/assets/linkifyjs/README.md @@ -0,0 +1,21 @@ +Linkify +======= + +Shim repository for Linkify. All issues with this plugin should be directed to http://github.com/SoapBox/linkifyjs/ + +Package Managers +---------------- + +### [Bower](http://bower.io/) + +``` +bower install linkifyjs +``` + +### [NPM](https://npmjs.com/) + +See the [main repository](https://github.com/SoapBox/linkifyjs/blob/master/README.md) for usage with Node.js/Browserify. + +``` +npm install linkifyjs +``` diff --git a/app/Resources/public/assets/linkifyjs/bower.json b/app/Resources/public/assets/linkifyjs/bower.json new file mode 100644 index 0000000000..209997aa6d --- /dev/null +++ b/app/Resources/public/assets/linkifyjs/bower.json @@ -0,0 +1,36 @@ +{ + "name": "linkifyjs", + "main": "linkify.js", + + "version": "2.1.8", + "authors": [ + "SoapBox Innovations Inc. " + ], + "description": "Intelligent link recognition, made easy", + "keywords": [ + "node", + "js", + "jquery", + "link", + "autolink", + "text", + "url", + "email", + "hashtag", + "hashtags", + "mention", + "mentions" + ], + "license": "MIT", + "homepage": "http://soapbox.github.io/linkifyjs/", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "jquery": ">=1.7.0" + } +} diff --git a/app/Resources/public/assets/linkifyjs/linkify-jquery.js b/app/Resources/public/assets/linkifyjs/linkify-jquery.js new file mode 100644 index 0000000000..80cda46a61 --- /dev/null +++ b/app/Resources/public/assets/linkifyjs/linkify-jquery.js @@ -0,0 +1,293 @@ +'use strict'; + +;(function (window, linkify, $) { + var linkifyJquery = function (linkify) { + 'use strict'; + + /** + Linkify a HTML DOM node + */ + + var tokenize = linkify.tokenize, + options = linkify.options; + var Options = options.Options; + + + var TEXT_TOKEN = linkify.parser.TOKENS.TEXT; + + var HTML_NODE = 1; + var TXT_NODE = 3; + + /** + Given a parent element and child node that the parent contains, replaces + that child with the given array of new children + */ + function replaceChildWithChildren(parent, oldChild, newChildren) { + var lastNewChild = newChildren[newChildren.length - 1]; + parent.replaceChild(lastNewChild, oldChild); + for (var i = newChildren.length - 2; i >= 0; i--) { + parent.insertBefore(newChildren[i], lastNewChild); + lastNewChild = newChildren[i]; + } + } + + /** + Given an array of MultiTokens, return an array of Nodes that are either + (a) Plain Text nodes (node type 3) + (b) Anchor tag nodes (usually, unless tag name is overridden in the options) + + Takes the same options as linkifyElement and an optional doc element + (this should be passed in by linkifyElement) + */ + function tokensToNodes(tokens, opts, doc) { + var result = []; + + for (var _iterator = tokens, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } + + var token = _ref; + + if (token.type === 'nl' && opts.nl2br) { + result.push(doc.createElement('br')); + continue; + } else if (!token.isLink || !opts.check(token)) { + result.push(doc.createTextNode(token.toString())); + continue; + } + + var _opts$resolve = opts.resolve(token), + formatted = _opts$resolve.formatted, + formattedHref = _opts$resolve.formattedHref, + tagName = _opts$resolve.tagName, + className = _opts$resolve.className, + target = _opts$resolve.target, + events = _opts$resolve.events, + attributes = _opts$resolve.attributes; + + // Build the link + + + var link = doc.createElement(tagName); + link.setAttribute('href', formattedHref); + + if (className) { + link.setAttribute('class', className); + } + + if (target) { + link.setAttribute('target', target); + } + + // Build up additional attributes + if (attributes) { + for (var attr in attributes) { + link.setAttribute(attr, attributes[attr]); + } + } + + if (events) { + for (var event in events) { + if (link.addEventListener) { + link.addEventListener(event, events[event]); + } else if (link.attachEvent) { + link.attachEvent('on' + event, events[event]); + } + } + } + + link.appendChild(doc.createTextNode(formatted)); + result.push(link); + } + + return result; + } + + // Requires document.createElement + function linkifyElementHelper(element, opts, doc) { + + // Can the element be linkified? + if (!element || element.nodeType !== HTML_NODE) { + throw new Error('Cannot linkify ' + element + ' - Invalid DOM Node type'); + } + + var ignoreTags = opts.ignoreTags; + + // Is this element already a link? + if (element.tagName === 'A' || options.contains(ignoreTags, element.tagName)) { + // No need to linkify + return element; + } + + var childElement = element.firstChild; + + while (childElement) { + var str = void 0, + tokens = void 0, + nodes = void 0; + + switch (childElement.nodeType) { + case HTML_NODE: + linkifyElementHelper(childElement, opts, doc); + break; + case TXT_NODE: + { + str = childElement.nodeValue; + tokens = tokenize(str); + + if (tokens.length === 0 || tokens.length === 1 && tokens[0] instanceof TEXT_TOKEN) { + // No node replacement required + break; + } + + nodes = tokensToNodes(tokens, opts, doc); + + // Swap out the current child for the set of nodes + replaceChildWithChildren(element, childElement, nodes); + + // so that the correct sibling is selected next + childElement = nodes[nodes.length - 1]; + + break; + } + } + + childElement = childElement.nextSibling; + } + + return element; + } + + function linkifyElement(element, opts) { + var doc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + + try { + doc = doc || document || window && window.document || global && global.document; + } catch (e) {/* do nothing for now */} + + if (!doc) { + throw new Error('Cannot find document implementation. ' + 'If you are in a non-browser environment like Node.js, ' + 'pass the document implementation as the third argument to linkifyElement.'); + } + + opts = new Options(opts); + return linkifyElementHelper(element, opts, doc); + } + + // Maintain reference to the recursive helper to cache option-normalization + linkifyElement.helper = linkifyElementHelper; + linkifyElement.normalize = function (opts) { + return new Options(opts); + }; + + // Applies the plugin to jQuery + function apply($) { + var doc = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + + $.fn = $.fn || {}; + + try { + doc = doc || document || window && window.document || global && global.document; + } catch (e) {/* do nothing for now */} + + if (!doc) { + throw new Error('Cannot find document implementation. ' + 'If you are in a non-browser environment like Node.js, ' + 'pass the document implementation as the second argument to linkify/jquery'); + } + + if (typeof $.fn.linkify === 'function') { + // Already applied + return; + } + + function jqLinkify(opts) { + opts = linkifyElement.normalize(opts); + return this.each(function () { + linkifyElement.helper(this, opts, doc); + }); + } + + $.fn.linkify = jqLinkify; + + $(doc).ready(function () { + $('[data-linkify]').each(function () { + var $this = $(this); + var data = $this.data(); + var target = data.linkify; + var nl2br = data.linkifyNlbr; + + var options = { + nl2br: !!nl2br && nl2br !== 0 && nl2br !== 'false' + }; + + if ('linkifyAttributes' in data) { + options.attributes = data.linkifyAttributes; + } + + if ('linkifyDefaultProtocol' in data) { + options.defaultProtocol = data.linkifyDefaultProtocol; + } + + if ('linkifyEvents' in data) { + options.events = data.linkifyEvents; + } + + if ('linkifyFormat' in data) { + options.format = data.linkifyFormat; + } + + if ('linkifyFormatHref' in data) { + options.formatHref = data.linkifyFormatHref; + } + + if ('linkifyTagname' in data) { + options.tagName = data.linkifyTagname; + } + + if ('linkifyTarget' in data) { + options.target = data.linkifyTarget; + } + + if ('linkifyValidate' in data) { + options.validate = data.linkifyValidate; + } + + if ('linkifyIgnoreTags' in data) { + options.ignoreTags = data.linkifyIgnoreTags; + } + + if ('linkifyClassName' in data) { + options.className = data.linkifyClassName; + } else if ('linkifyLinkclass' in data) { + // linkClass is deprecated + options.className = data.linkifyLinkclass; + } + + options = linkifyElement.normalize(options); + + var $target = target === 'this' ? $this : $this.find(target); + $target.linkify(options); + }); + }); + } + + // Try assigning linkifyElement to the browser scope + try { + !undefined.define && (window.linkifyElement = linkifyElement); + } catch (e) {/**/} + + return apply; + }(linkify); + + if (typeof $.fn.linkify !== 'function') { + linkifyJquery($); + } +})(window, linkify, jQuery); \ No newline at end of file diff --git a/app/Resources/public/assets/linkifyjs/linkify-jquery.min.js b/app/Resources/public/assets/linkifyjs/linkify-jquery.min.js new file mode 100644 index 0000000000..beabe881c0 --- /dev/null +++ b/app/Resources/public/assets/linkifyjs/linkify-jquery.min.js @@ -0,0 +1 @@ +"use strict";!function(e,n,t){var i=function(n){function t(e,n,t){var i=t[t.length-1];e.replaceChild(i,n);for(var a=t.length-2;a>=0;a--)e.insertBefore(t[a],i),i=t[a]}function i(e,n,t){for(var i=[],a=e,r=Array.isArray(a),o=0,a=r?a:a[Symbol.iterator]();;){var l;if(r){if(o>=a.length)break;l=a[o++]}else{if(o=a.next(),o.done)break;l=o.value}var f=l;if("nl"===f.type&&n.nl2br)i.push(t.createElement("br"));else if(f.isLink&&n.check(f)){var s=n.resolve(f),c=s.formatted,u=s.formattedHref,y=s.tagName,d=s.className,m=s.target,k=s.events,h=s.attributes,v=t.createElement(y);if(v.setAttribute("href",u),d&&v.setAttribute("class",d),m&&v.setAttribute("target",m),h)for(var g in h)v.setAttribute(g,h[g]);if(k)for(var b in k)v.addEventListener?v.addEventListener(b,k[b]):v.attachEvent&&v.attachEvent("on"+b,k[b]);v.appendChild(t.createTextNode(c)),i.push(v)}else i.push(t.createTextNode(f.toString()))}return i}function a(e,n,r){if(!e||e.nodeType!==u)throw new Error("Cannot linkify "+e+" - Invalid DOM Node type");var o=n.ignoreTags;if("A"===e.tagName||f.contains(o,e.tagName))return e;for(var s=e.firstChild;s;){var d=void 0,m=void 0,k=void 0;switch(s.nodeType){case u:a(s,n,r);break;case y:if(d=s.nodeValue,m=l(d),0===m.length||1===m.length&&m[0]instanceof c)break;k=i(m,n,r),t(e,s,k),s=k[k.length-1]}s=s.nextSibling}return e}function r(n,t){var i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];try{i=i||document||e&&e.document||global&&global.document}catch(r){}if(!i)throw new Error("Cannot find document implementation. If you are in a non-browser environment like Node.js, pass the document implementation as the third argument to linkifyElement.");return t=new s(t),a(n,t,i)}function o(n){function t(e){return e=r.normalize(e),this.each(function(){r.helper(this,e,i)})}var i=arguments.length>1&&void 0!==arguments[1]&&arguments[1];n.fn=n.fn||{};try{i=i||document||e&&e.document||global&&global.document}catch(a){}if(!i)throw new Error("Cannot find document implementation. If you are in a non-browser environment like Node.js, pass the document implementation as the second argument to linkify/jquery");"function"!=typeof n.fn.linkify&&(n.fn.linkify=t,n(i).ready(function(){n("[data-linkify]").each(function(){var e=n(this),t=e.data(),i=t.linkify,a=t.linkifyNlbr,o={nl2br:!!a&&0!==a&&"false"!==a};"linkifyAttributes"in t&&(o.attributes=t.linkifyAttributes),"linkifyDefaultProtocol"in t&&(o.defaultProtocol=t.linkifyDefaultProtocol),"linkifyEvents"in t&&(o.events=t.linkifyEvents),"linkifyFormat"in t&&(o.format=t.linkifyFormat),"linkifyFormatHref"in t&&(o.formatHref=t.linkifyFormatHref),"linkifyTagname"in t&&(o.tagName=t.linkifyTagname),"linkifyTarget"in t&&(o.target=t.linkifyTarget),"linkifyValidate"in t&&(o.validate=t.linkifyValidate),"linkifyIgnoreTags"in t&&(o.ignoreTags=t.linkifyIgnoreTags),"linkifyClassName"in t?o.className=t.linkifyClassName:"linkifyLinkclass"in t&&(o.className=t.linkifyLinkclass),o=r.normalize(o);var l="this"===i?e:e.find(i);l.linkify(o)})}))}var l=n.tokenize,f=n.options,s=f.Options,c=n.parser.TOKENS.TEXT,u=1,y=3;r.helper=a,r.normalize=function(e){return new s(e)};try{!(void 0).define&&(e.linkifyElement=r)}catch(d){}return o}(n);"function"!=typeof t.fn.linkify&&i(t)}(window,linkify,jQuery); \ No newline at end of file diff --git a/app/Resources/public/assets/linkifyjs/linkify.js b/app/Resources/public/assets/linkifyjs/linkify.js new file mode 100644 index 0000000000..950a6ffe83 --- /dev/null +++ b/app/Resources/public/assets/linkifyjs/linkify.js @@ -0,0 +1,1286 @@ +;(function () { +'use strict'; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +(function (exports) { + 'use strict'; + + function inherits(parent, child) { + var props = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + var extended = Object.create(parent.prototype); + for (var p in props) { + extended[p] = props[p]; + } + extended.constructor = child; + child.prototype = extended; + return child; + } + + var defaults = { + defaultProtocol: 'http', + events: null, + format: noop, + formatHref: noop, + nl2br: false, + tagName: 'a', + target: typeToTarget, + validate: true, + ignoreTags: [], + attributes: null, + className: 'linkified' // Deprecated value - no default class will be provided in the future + }; + + function Options(opts) { + opts = opts || {}; + + this.defaultProtocol = opts.hasOwnProperty('defaultProtocol') ? opts.defaultProtocol : defaults.defaultProtocol; + this.events = opts.hasOwnProperty('events') ? opts.events : defaults.events; + this.format = opts.hasOwnProperty('format') ? opts.format : defaults.format; + this.formatHref = opts.hasOwnProperty('formatHref') ? opts.formatHref : defaults.formatHref; + this.nl2br = opts.hasOwnProperty('nl2br') ? opts.nl2br : defaults.nl2br; + this.tagName = opts.hasOwnProperty('tagName') ? opts.tagName : defaults.tagName; + this.target = opts.hasOwnProperty('target') ? opts.target : defaults.target; + this.validate = opts.hasOwnProperty('validate') ? opts.validate : defaults.validate; + this.ignoreTags = []; + + // linkAttributes and linkClass is deprecated + this.attributes = opts.attributes || opts.linkAttributes || defaults.attributes; + this.className = opts.hasOwnProperty('className') ? opts.className : opts.linkClass || defaults.className; + + // Make all tags names upper case + var ignoredTags = opts.hasOwnProperty('ignoreTags') ? opts.ignoreTags : defaults.ignoreTags; + for (var i = 0; i < ignoredTags.length; i++) { + this.ignoreTags.push(ignoredTags[i].toUpperCase()); + } + } + + Options.prototype = { + /** + * Given the token, return all options for how it should be displayed + */ + resolve: function resolve(token) { + var href = token.toHref(this.defaultProtocol); + return { + formatted: this.get('format', token.toString(), token), + formattedHref: this.get('formatHref', href, token), + tagName: this.get('tagName', href, token), + className: this.get('className', href, token), + target: this.get('target', href, token), + events: this.getObject('events', href, token), + attributes: this.getObject('attributes', href, token) + }; + }, + + + /** + * Returns true or false based on whether a token should be displayed as a + * link based on the user options. By default, + */ + check: function check(token) { + return this.get('validate', token.toString(), token); + }, + + + // Private methods + + /** + * Resolve an option's value based on the value of the option and the given + * params. + * @param {String} key Name of option to use + * @param operator will be passed to the target option if it's method + * @param {MultiToken} token The token from linkify.tokenize + */ + get: function get(key, operator, token) { + var optionValue = void 0, + option = this[key]; + if (!option) { + return option; + } + + switch (typeof option === 'undefined' ? 'undefined' : _typeof(option)) { + case 'function': + return option(operator, token.type); + case 'object': + optionValue = option.hasOwnProperty(token.type) ? option[token.type] : defaults[key]; + return typeof optionValue === 'function' ? optionValue(operator, token.type) : optionValue; + } + + return option; + }, + getObject: function getObject(key, operator, token) { + var option = this[key]; + return typeof option === 'function' ? option(operator, token.type) : option; + } + }; + + /** + * Quick indexOf replacement for checking the ignoreTags option + */ + function contains(arr, value) { + for (var i = 0; i < arr.length; i++) { + if (arr[i] === value) { + return true; + } + } + return false; + } + + function noop(val) { + return val; + } + + function typeToTarget(href, type) { + return type === 'url' ? '_blank' : null; + } + + var options = Object.freeze({ + defaults: defaults, + Options: Options, + contains: contains + }); + + function createStateClass() { + return function (tClass) { + this.j = []; + this.T = tClass || null; + }; + } + + /** + A simple state machine that can emit token classes + + The `j` property in this class refers to state jumps. It's a + multidimensional array where for each element: + + * index [0] is a symbol or class of symbols to transition to. + * index [1] is a State instance which matches + + The type of symbol will depend on the target implementation for this class. + In Linkify, we have a two-stage scanner. Each stage uses this state machine + but with a slighly different (polymorphic) implementation. + + The `T` property refers to the token class. + + TODO: Can the `on` and `next` methods be combined? + + @class BaseState + */ + var BaseState = createStateClass(); + BaseState.prototype = { + defaultTransition: false, + + /** + @method constructor + @param {Class} tClass Pass in the kind of token to emit if there are + no jumps after this state and the state is accepting. + */ + + /** + On the given symbol(s), this machine should go to the given state + @method on + @param {Array|Mixed} symbol + @param {BaseState} state Note that the type of this state should be the + same as the current instance (i.e., don't pass in a different + subclass) + */ + on: function on(symbol, state) { + if (symbol instanceof Array) { + for (var i = 0; i < symbol.length; i++) { + this.j.push([symbol[i], state]); + } + return this; + } + this.j.push([symbol, state]); + return this; + }, + + + /** + Given the next item, returns next state for that item + @method next + @param {Mixed} item Should be an instance of the symbols handled by + this particular machine. + @return {State} state Returns false if no jumps are available + */ + next: function next(item) { + for (var i = 0; i < this.j.length; i++) { + var jump = this.j[i]; + var symbol = jump[0]; // Next item to check for + var state = jump[1]; // State to jump to if items match + + // compare item with symbol + if (this.test(item, symbol)) { + return state; + } + } + + // Nowhere left to jump! + return this.defaultTransition; + }, + + + /** + Does this state accept? + `true` only of `this.T` exists + @method accepts + @return {Boolean} + */ + accepts: function accepts() { + return !!this.T; + }, + + + /** + Determine whether a given item "symbolizes" the symbol, where symbol is + a class of items handled by this state machine. + This method should be overriden in extended classes. + @method test + @param {Mixed} item Does this item match the given symbol? + @param {Mixed} symbol + @return {Boolean} + */ + test: function test(item, symbol) { + return item === symbol; + }, + + + /** + Emit the token for this State (just return it in this case) + If this emits a token, this instance is an accepting state + @method emit + @return {Class} T + */ + emit: function emit() { + return this.T; + } + }; + + /** + State machine for string-based input + + @class CharacterState + @extends BaseState + */ + var CharacterState = inherits(BaseState, createStateClass(), { + /** + Does the given character match the given character or regular + expression? + @method test + @param {String} char + @param {String|RegExp} charOrRegExp + @return {Boolean} + */ + test: function test(character, charOrRegExp) { + return character === charOrRegExp || charOrRegExp instanceof RegExp && charOrRegExp.test(character); + } + }); + + /** + State machine for input in the form of TextTokens + + @class TokenState + @extends BaseState + */ + var TokenState = inherits(BaseState, createStateClass(), { + + /** + * Similar to `on`, but returns the state the results in the transition from + * the given item + * @method jump + * @param {Mixed} item + * @param {Token} [token] + * @return state + */ + jump: function jump(token) { + var tClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + + var state = this.next(new token('')); // dummy temp token + if (state === this.defaultTransition) { + // Make a new state! + state = new this.constructor(tClass); + this.on(token, state); + } else if (tClass) { + state.T = tClass; + } + return state; + }, + + + /** + Is the given token an instance of the given token class? + @method test + @param {TextToken} token + @param {Class} tokenClass + @return {Boolean} + */ + test: function test(token, tokenClass) { + return token instanceof tokenClass; + } + }); + + /** + Given a non-empty target string, generates states (if required) for each + consecutive substring of characters in str starting from the beginning of + the string. The final state will have a special value, as specified in + options. All other "in between" substrings will have a default end state. + + This turns the state machine into a Trie-like data structure (rather than a + intelligently-designed DFA). + + Note that I haven't really tried these with any strings other than + DOMAIN. + + @param {String} str + @param {CharacterState} start State to jump from the first character + @param {Class} endToken Token class to emit when the given string has been + matched and no more jumps exist. + @param {Class} defaultToken "Filler token", or which token type to emit when + we don't have a full match + @return {Array} list of newly-created states + */ + function stateify(str, start, endToken, defaultToken) { + var i = 0, + len = str.length, + state = start, + newStates = [], + nextState = void 0; + + // Find the next state without a jump to the next character + while (i < len && (nextState = state.next(str[i]))) { + state = nextState; + i++; + } + + if (i >= len) { + return []; + } // no new tokens were added + + while (i < len - 1) { + nextState = new CharacterState(defaultToken); + newStates.push(nextState); + state.on(str[i], nextState); + state = nextState; + i++; + } + + nextState = new CharacterState(endToken); + newStates.push(nextState); + state.on(str[len - 1], nextState); + + return newStates; + } + + function createTokenClass() { + return function (value) { + if (value) { + this.v = value; + } + }; + } + + /****************************************************************************** + Text Tokens + Tokens composed of strings + ******************************************************************************/ + + /** + Abstract class used for manufacturing text tokens. + Pass in the value this token represents + + @class TextToken + @abstract + */ + var TextToken = createTokenClass(); + TextToken.prototype = { + toString: function toString() { + return this.v + ''; + } + }; + + function inheritsToken(value) { + var props = value ? { v: value } : {}; + return inherits(TextToken, createTokenClass(), props); + } + + /** + A valid domain token + @class DOMAIN + @extends TextToken + */ + var DOMAIN = inheritsToken(); + + /** + @class AT + @extends TextToken + */ + var AT = inheritsToken('@'); + + /** + Represents a single colon `:` character + + @class COLON + @extends TextToken + */ + var COLON = inheritsToken(':'); + + /** + @class DOT + @extends TextToken + */ + var DOT = inheritsToken('.'); + + /** + A character class that can surround the URL, but which the URL cannot begin + or end with. Does not include certain English punctuation like parentheses. + + @class PUNCTUATION + @extends TextToken + */ + var PUNCTUATION = inheritsToken(); + + /** + The word localhost (by itself) + @class LOCALHOST + @extends TextToken + */ + var LOCALHOST = inheritsToken(); + + /** + Newline token + @class NL + @extends TextToken + */ + var NL = inheritsToken('\n'); + + /** + @class NUM + @extends TextToken + */ + var NUM = inheritsToken(); + + /** + @class PLUS + @extends TextToken + */ + var PLUS = inheritsToken('+'); + + /** + @class POUND + @extends TextToken + */ + var POUND = inheritsToken('#'); + + /** + Represents a web URL protocol. Supported types include + + * `http:` + * `https:` + * `ftp:` + * `ftps:` + + @class PROTOCOL + @extends TextToken + */ + var PROTOCOL = inheritsToken(); + + /** + Represents the start of the email URI protocol + + @class MAILTO + @extends TextToken + */ + var MAILTO = inheritsToken('mailto:'); + + /** + @class QUERY + @extends TextToken + */ + var QUERY = inheritsToken('?'); + + /** + @class SLASH + @extends TextToken + */ + var SLASH = inheritsToken('/'); + + /** + @class UNDERSCORE + @extends TextToken + */ + var UNDERSCORE = inheritsToken('_'); + + /** + One ore more non-whitespace symbol. + @class SYM + @extends TextToken + */ + var SYM = inheritsToken(); + + /** + @class TLD + @extends TextToken + */ + var TLD = inheritsToken(); + + /** + Represents a string of consecutive whitespace characters + + @class WS + @extends TextToken + */ + var WS = inheritsToken(); + + /** + Opening/closing bracket classes + */ + + var OPENBRACE = inheritsToken('{'); + var OPENBRACKET = inheritsToken('['); + var OPENANGLEBRACKET = inheritsToken('<'); + var OPENPAREN = inheritsToken('('); + var CLOSEBRACE = inheritsToken('}'); + var CLOSEBRACKET = inheritsToken(']'); + var CLOSEANGLEBRACKET = inheritsToken('>'); + var CLOSEPAREN = inheritsToken(')'); + + var AMPERSAND = inheritsToken('&'); + + var text = Object.freeze({ + Base: TextToken, + DOMAIN: DOMAIN, + AT: AT, + COLON: COLON, + DOT: DOT, + PUNCTUATION: PUNCTUATION, + LOCALHOST: LOCALHOST, + NL: NL, + NUM: NUM, + PLUS: PLUS, + POUND: POUND, + QUERY: QUERY, + PROTOCOL: PROTOCOL, + MAILTO: MAILTO, + SLASH: SLASH, + UNDERSCORE: UNDERSCORE, + SYM: SYM, + TLD: TLD, + WS: WS, + OPENBRACE: OPENBRACE, + OPENBRACKET: OPENBRACKET, + OPENANGLEBRACKET: OPENANGLEBRACKET, + OPENPAREN: OPENPAREN, + CLOSEBRACE: CLOSEBRACE, + CLOSEBRACKET: CLOSEBRACKET, + CLOSEANGLEBRACKET: CLOSEANGLEBRACKET, + CLOSEPAREN: CLOSEPAREN, + AMPERSAND: AMPERSAND + }); + + /** + The scanner provides an interface that takes a string of text as input, and + outputs an array of tokens instances that can be used for easy URL parsing. + + @module linkify + @submodule scanner + @main scanner + */ + + var tlds = 'aaa|aarp|abarth|abb|abbott|abbvie|abc|able|abogado|abudhabi|ac|academy|accenture|accountant|accountants|aco|active|actor|ad|adac|ads|adult|ae|aeg|aero|aetna|af|afamilycompany|afl|africa|ag|agakhan|agency|ai|aig|aigo|airbus|airforce|airtel|akdn|al|alfaromeo|alibaba|alipay|allfinanz|allstate|ally|alsace|alstom|am|americanexpress|americanfamily|amex|amfam|amica|amsterdam|analytics|android|anquan|anz|ao|aol|apartments|app|apple|aq|aquarelle|ar|arab|aramco|archi|army|arpa|art|arte|as|asda|asia|associates|at|athleta|attorney|au|auction|audi|audible|audio|auspost|author|auto|autos|avianca|aw|aws|ax|axa|az|azure|ba|baby|baidu|banamex|bananarepublic|band|bank|bar|barcelona|barclaycard|barclays|barefoot|bargains|baseball|basketball|bauhaus|bayern|bb|bbc|bbt|bbva|bcg|bcn|bd|be|beats|beauty|beer|bentley|berlin|best|bestbuy|bet|bf|bg|bh|bharti|bi|bible|bid|bike|bing|bingo|bio|biz|bj|black|blackfriday|blanco|blockbuster|blog|bloomberg|blue|bm|bms|bmw|bn|bnl|bnpparibas|bo|boats|boehringer|bofa|bom|bond|boo|book|booking|boots|bosch|bostik|boston|bot|boutique|box|br|bradesco|bridgestone|broadway|broker|brother|brussels|bs|bt|budapest|bugatti|build|builders|business|buy|buzz|bv|bw|by|bz|bzh|ca|cab|cafe|cal|call|calvinklein|cam|camera|camp|cancerresearch|canon|capetown|capital|capitalone|car|caravan|cards|care|career|careers|cars|cartier|casa|case|caseih|cash|casino|cat|catering|catholic|cba|cbn|cbre|cbs|cc|cd|ceb|center|ceo|cern|cf|cfa|cfd|cg|ch|chanel|channel|chase|chat|cheap|chintai|chloe|christmas|chrome|chrysler|church|ci|cipriani|circle|cisco|citadel|citi|citic|city|cityeats|ck|cl|claims|cleaning|click|clinic|clinique|clothing|cloud|club|clubmed|cm|cn|co|coach|codes|coffee|college|cologne|com|comcast|commbank|community|company|compare|computer|comsec|condos|construction|consulting|contact|contractors|cooking|cookingchannel|cool|coop|corsica|country|coupon|coupons|courses|cr|credit|creditcard|creditunion|cricket|crown|crs|cruise|cruises|csc|cu|cuisinella|cv|cw|cx|cy|cymru|cyou|cz|dabur|dad|dance|data|date|dating|datsun|day|dclk|dds|de|deal|dealer|deals|degree|delivery|dell|deloitte|delta|democrat|dental|dentist|desi|design|dev|dhl|diamonds|diet|digital|direct|directory|discount|discover|dish|diy|dj|dk|dm|dnp|do|docs|doctor|dodge|dog|doha|domains|dot|download|drive|dtv|dubai|duck|dunlop|duns|dupont|durban|dvag|dvr|dz|earth|eat|ec|eco|edeka|edu|education|ee|eg|email|emerck|energy|engineer|engineering|enterprises|epost|epson|equipment|er|ericsson|erni|es|esq|estate|esurance|et|etisalat|eu|eurovision|eus|events|everbank|exchange|expert|exposed|express|extraspace|fage|fail|fairwinds|faith|family|fan|fans|farm|farmers|fashion|fast|fedex|feedback|ferrari|ferrero|fi|fiat|fidelity|fido|film|final|finance|financial|fire|firestone|firmdale|fish|fishing|fit|fitness|fj|fk|flickr|flights|flir|florist|flowers|fly|fm|fo|foo|food|foodnetwork|football|ford|forex|forsale|forum|foundation|fox|fr|free|fresenius|frl|frogans|frontdoor|frontier|ftr|fujitsu|fujixerox|fun|fund|furniture|futbol|fyi|ga|gal|gallery|gallo|gallup|game|games|gap|garden|gb|gbiz|gd|gdn|ge|gea|gent|genting|george|gf|gg|ggee|gh|gi|gift|gifts|gives|giving|gl|glade|glass|gle|global|globo|gm|gmail|gmbh|gmo|gmx|gn|godaddy|gold|goldpoint|golf|goo|goodhands|goodyear|goog|google|gop|got|gov|gp|gq|gr|grainger|graphics|gratis|green|gripe|grocery|group|gs|gt|gu|guardian|gucci|guge|guide|guitars|guru|gw|gy|hair|hamburg|hangout|haus|hbo|hdfc|hdfcbank|health|healthcare|help|helsinki|here|hermes|hgtv|hiphop|hisamitsu|hitachi|hiv|hk|hkt|hm|hn|hockey|holdings|holiday|homedepot|homegoods|homes|homesense|honda|honeywell|horse|hospital|host|hosting|hot|hoteles|hotels|hotmail|house|how|hr|hsbc|ht|htc|hu|hughes|hyatt|hyundai|ibm|icbc|ice|icu|id|ie|ieee|ifm|ikano|il|im|imamat|imdb|immo|immobilien|in|industries|infiniti|info|ing|ink|institute|insurance|insure|int|intel|international|intuit|investments|io|ipiranga|iq|ir|irish|is|iselect|ismaili|ist|istanbul|it|itau|itv|iveco|iwc|jaguar|java|jcb|jcp|je|jeep|jetzt|jewelry|jio|jlc|jll|jm|jmp|jnj|jo|jobs|joburg|jot|joy|jp|jpmorgan|jprs|juegos|juniper|kaufen|kddi|ke|kerryhotels|kerrylogistics|kerryproperties|kfh|kg|kh|ki|kia|kim|kinder|kindle|kitchen|kiwi|km|kn|koeln|komatsu|kosher|kp|kpmg|kpn|kr|krd|kred|kuokgroup|kw|ky|kyoto|kz|la|lacaixa|ladbrokes|lamborghini|lamer|lancaster|lancia|lancome|land|landrover|lanxess|lasalle|lat|latino|latrobe|law|lawyer|lb|lc|lds|lease|leclerc|lefrak|legal|lego|lexus|lgbt|li|liaison|lidl|life|lifeinsurance|lifestyle|lighting|like|lilly|limited|limo|lincoln|linde|link|lipsy|live|living|lixil|lk|loan|loans|locker|locus|loft|lol|london|lotte|lotto|love|lpl|lplfinancial|lr|ls|lt|ltd|ltda|lu|lundbeck|lupin|luxe|luxury|lv|ly|ma|macys|madrid|maif|maison|makeup|man|management|mango|map|market|marketing|markets|marriott|marshalls|maserati|mattel|mba|mc|mckinsey|md|me|med|media|meet|melbourne|meme|memorial|men|menu|meo|merckmsd|metlife|mg|mh|miami|microsoft|mil|mini|mint|mit|mitsubishi|mk|ml|mlb|mls|mm|mma|mn|mo|mobi|mobile|mobily|moda|moe|moi|mom|monash|money|monster|mopar|mormon|mortgage|moscow|moto|motorcycles|mov|movie|movistar|mp|mq|mr|ms|msd|mt|mtn|mtr|mu|museum|mutual|mv|mw|mx|my|mz|na|nab|nadex|nagoya|name|nationwide|natura|navy|nba|nc|ne|nec|net|netbank|netflix|network|neustar|new|newholland|news|next|nextdirect|nexus|nf|nfl|ng|ngo|nhk|ni|nico|nike|nikon|ninja|nissan|nissay|nl|no|nokia|northwesternmutual|norton|now|nowruz|nowtv|np|nr|nra|nrw|ntt|nu|nyc|nz|obi|observer|off|office|okinawa|olayan|olayangroup|oldnavy|ollo|om|omega|one|ong|onl|online|onyourside|ooo|open|oracle|orange|org|organic|origins|osaka|otsuka|ott|ovh|pa|page|panasonic|panerai|paris|pars|partners|parts|party|passagens|pay|pccw|pe|pet|pf|pfizer|pg|ph|pharmacy|phd|philips|phone|photo|photography|photos|physio|piaget|pics|pictet|pictures|pid|pin|ping|pink|pioneer|pizza|pk|pl|place|play|playstation|plumbing|plus|pm|pn|pnc|pohl|poker|politie|porn|post|pr|pramerica|praxi|press|prime|pro|prod|productions|prof|progressive|promo|properties|property|protection|pru|prudential|ps|pt|pub|pw|pwc|py|qa|qpon|quebec|quest|qvc|racing|radio|raid|re|read|realestate|realtor|realty|recipes|red|redstone|redumbrella|rehab|reise|reisen|reit|reliance|ren|rent|rentals|repair|report|republican|rest|restaurant|review|reviews|rexroth|rich|richardli|ricoh|rightathome|ril|rio|rip|rmit|ro|rocher|rocks|rodeo|rogers|room|rs|rsvp|ru|rugby|ruhr|run|rw|rwe|ryukyu|sa|saarland|safe|safety|sakura|sale|salon|samsclub|samsung|sandvik|sandvikcoromant|sanofi|sap|sapo|sarl|sas|save|saxo|sb|sbi|sbs|sc|sca|scb|schaeffler|schmidt|scholarships|school|schule|schwarz|science|scjohnson|scor|scot|sd|se|search|seat|secure|security|seek|select|sener|services|ses|seven|sew|sex|sexy|sfr|sg|sh|shangrila|sharp|shaw|shell|shia|shiksha|shoes|shop|shopping|shouji|show|showtime|shriram|si|silk|sina|singles|site|sj|sk|ski|skin|sky|skype|sl|sling|sm|smart|smile|sn|sncf|so|soccer|social|softbank|software|sohu|solar|solutions|song|sony|soy|space|spiegel|spot|spreadbetting|sr|srl|srt|st|stada|staples|star|starhub|statebank|statefarm|statoil|stc|stcgroup|stockholm|storage|store|stream|studio|study|style|su|sucks|supplies|supply|support|surf|surgery|suzuki|sv|swatch|swiftcover|swiss|sx|sy|sydney|symantec|systems|sz|tab|taipei|talk|taobao|target|tatamotors|tatar|tattoo|tax|taxi|tc|tci|td|tdk|team|tech|technology|tel|telecity|telefonica|temasek|tennis|teva|tf|tg|th|thd|theater|theatre|tiaa|tickets|tienda|tiffany|tips|tires|tirol|tj|tjmaxx|tjx|tk|tkmaxx|tl|tm|tmall|tn|to|today|tokyo|tools|top|toray|toshiba|total|tours|town|toyota|toys|tr|trade|trading|training|travel|travelchannel|travelers|travelersinsurance|trust|trv|tt|tube|tui|tunes|tushu|tv|tvs|tw|tz|ua|ubank|ubs|uconnect|ug|uk|unicom|university|uno|uol|ups|us|uy|uz|va|vacations|vana|vanguard|vc|ve|vegas|ventures|verisign|versicherung|vet|vg|vi|viajes|video|vig|viking|villas|vin|vip|virgin|visa|vision|vista|vistaprint|viva|vivo|vlaanderen|vn|vodka|volkswagen|volvo|vote|voting|voto|voyage|vu|vuelos|wales|walmart|walter|wang|wanggou|warman|watch|watches|weather|weatherchannel|webcam|weber|website|wed|wedding|weibo|weir|wf|whoswho|wien|wiki|williamhill|win|windows|wine|winners|wme|wolterskluwer|woodside|work|works|world|wow|ws|wtc|wtf|xbox|xerox|xfinity|xihuan|xin|xn--11b4c3d|xn--1ck2e1b|xn--1qqw23a|xn--2scrj9c|xn--30rr7y|xn--3bst00m|xn--3ds443g|xn--3e0b707e|xn--3hcrj9c|xn--3oq18vl8pn36a|xn--3pxu8k|xn--42c2d9a|xn--45br5cyl|xn--45brj9c|xn--45q11c|xn--4gbrim|xn--54b7fta0cc|xn--55qw42g|xn--55qx5d|xn--5su34j936bgsg|xn--5tzm5g|xn--6frz82g|xn--6qq986b3xl|xn--80adxhks|xn--80ao21a|xn--80aqecdr1a|xn--80asehdb|xn--80aswg|xn--8y0a063a|xn--90a3ac|xn--90ae|xn--90ais|xn--9dbq2a|xn--9et52u|xn--9krt00a|xn--b4w605ferd|xn--bck1b9a5dre4c|xn--c1avg|xn--c2br7g|xn--cck2b3b|xn--cg4bki|xn--clchc0ea0b2g2a9gcd|xn--czr694b|xn--czrs0t|xn--czru2d|xn--d1acj3b|xn--d1alf|xn--e1a4c|xn--eckvdtc9d|xn--efvy88h|xn--estv75g|xn--fct429k|xn--fhbei|xn--fiq228c5hs|xn--fiq64b|xn--fiqs8s|xn--fiqz9s|xn--fjq720a|xn--flw351e|xn--fpcrj9c3d|xn--fzc2c9e2c|xn--fzys8d69uvgm|xn--g2xx48c|xn--gckr3f0f|xn--gecrj9c|xn--gk3at1e|xn--h2breg3eve|xn--h2brj9c|xn--h2brj9c8c|xn--hxt814e|xn--i1b6b1a6a2e|xn--imr513n|xn--io0a7i|xn--j1aef|xn--j1amh|xn--j6w193g|xn--jlq61u9w7b|xn--jvr189m|xn--kcrx77d1x4a|xn--kprw13d|xn--kpry57d|xn--kpu716f|xn--kput3i|xn--l1acc|xn--lgbbat1ad8j|xn--mgb9awbf|xn--mgba3a3ejt|xn--mgba3a4f16a|xn--mgba7c0bbn0a|xn--mgbaakc7dvf|xn--mgbaam7a8h|xn--mgbab2bd|xn--mgbai9azgqp6j|xn--mgbayh7gpa|xn--mgbb9fbpob|xn--mgbbh1a|xn--mgbbh1a71e|xn--mgbc0a9azcg|xn--mgbca7dzdo|xn--mgberp4a5d4ar|xn--mgbgu82a|xn--mgbi4ecexp|xn--mgbpl2fh|xn--mgbt3dhd|xn--mgbtx2b|xn--mgbx4cd0ab|xn--mix891f|xn--mk1bu44c|xn--mxtq1m|xn--ngbc5azd|xn--ngbe9e0a|xn--ngbrx|xn--node|xn--nqv7f|xn--nqv7fs00ema|xn--nyqy26a|xn--o3cw4h|xn--ogbpf8fl|xn--p1acf|xn--p1ai|xn--pbt977c|xn--pgbs0dh|xn--pssy2u|xn--q9jyb4c|xn--qcka1pmc|xn--qxam|xn--rhqv96g|xn--rovu88b|xn--rvc1e0am3e|xn--s9brj9c|xn--ses554g|xn--t60b56a|xn--tckwe|xn--tiq49xqyj|xn--unup4y|xn--vermgensberater-ctb|xn--vermgensberatung-pwb|xn--vhquv|xn--vuq861b|xn--w4r85el8fhu5dnra|xn--w4rs40l|xn--wgbh1c|xn--wgbl6a|xn--xhq521b|xn--xkc2al3hye2a|xn--xkc2dl3a5ee0h|xn--y9a3aq|xn--yfro4i67o|xn--ygbi2ammx|xn--zfr164b|xperia|xxx|xyz|yachts|yahoo|yamaxun|yandex|ye|yodobashi|yoga|yokohama|you|youtube|yt|yun|za|zappos|zara|zero|zip|zippo|zm|zone|zuerich|zw'.split('|'); // macro, see gulpfile.js + + var NUMBERS = '0123456789'.split(''); + var ALPHANUM = '0123456789abcdefghijklmnopqrstuvwxyz'.split(''); + var WHITESPACE = [' ', '\f', '\r', '\t', '\v', '\xA0', '\u1680', '\u180E']; // excluding line breaks + + var domainStates = []; // states that jump to DOMAIN on /[a-z0-9]/ + var makeState = function makeState(tokenClass) { + return new CharacterState(tokenClass); + }; + + // Frequently used states + var S_START = makeState(); + var S_NUM = makeState(NUM); + var S_DOMAIN = makeState(DOMAIN); + var S_DOMAIN_HYPHEN = makeState(); // domain followed by 1 or more hyphen characters + var S_WS = makeState(WS); + + // States for special URL symbols + S_START.on('@', makeState(AT)).on('.', makeState(DOT)).on('+', makeState(PLUS)).on('#', makeState(POUND)).on('?', makeState(QUERY)).on('/', makeState(SLASH)).on('_', makeState(UNDERSCORE)).on(':', makeState(COLON)).on('{', makeState(OPENBRACE)).on('[', makeState(OPENBRACKET)).on('<', makeState(OPENANGLEBRACKET)).on('(', makeState(OPENPAREN)).on('}', makeState(CLOSEBRACE)).on(']', makeState(CLOSEBRACKET)).on('>', makeState(CLOSEANGLEBRACKET)).on(')', makeState(CLOSEPAREN)).on('&', makeState(AMPERSAND)).on([',', ';', '!', '"', '\''], makeState(PUNCTUATION)); + + // Whitespace jumps + // Tokens of only non-newline whitespace are arbitrarily long + S_START.on('\n', makeState(NL)).on(WHITESPACE, S_WS); + + // If any whitespace except newline, more whitespace! + S_WS.on(WHITESPACE, S_WS); + + // Generates states for top-level domains + // Note that this is most accurate when tlds are in alphabetical order + for (var i = 0; i < tlds.length; i++) { + var newStates = stateify(tlds[i], S_START, TLD, DOMAIN); + domainStates.push.apply(domainStates, newStates); + } + + // Collect the states generated by different protocls + var partialProtocolFileStates = stateify('file', S_START, DOMAIN, DOMAIN); + var partialProtocolFtpStates = stateify('ftp', S_START, DOMAIN, DOMAIN); + var partialProtocolHttpStates = stateify('http', S_START, DOMAIN, DOMAIN); + var partialProtocolMailtoStates = stateify('mailto', S_START, DOMAIN, DOMAIN); + + // Add the states to the array of DOMAINeric states + domainStates.push.apply(domainStates, partialProtocolFileStates); + domainStates.push.apply(domainStates, partialProtocolFtpStates); + domainStates.push.apply(domainStates, partialProtocolHttpStates); + domainStates.push.apply(domainStates, partialProtocolMailtoStates); + + // Protocol states + var S_PROTOCOL_FILE = partialProtocolFileStates.pop(); + var S_PROTOCOL_FTP = partialProtocolFtpStates.pop(); + var S_PROTOCOL_HTTP = partialProtocolHttpStates.pop(); + var S_MAILTO = partialProtocolMailtoStates.pop(); + var S_PROTOCOL_SECURE = makeState(DOMAIN); + var S_FULL_PROTOCOL = makeState(PROTOCOL); // Full protocol ends with COLON + var S_FULL_MAILTO = makeState(MAILTO); // Mailto ends with COLON + + // Secure protocols (end with 's') + S_PROTOCOL_FTP.on('s', S_PROTOCOL_SECURE).on(':', S_FULL_PROTOCOL); + + S_PROTOCOL_HTTP.on('s', S_PROTOCOL_SECURE).on(':', S_FULL_PROTOCOL); + + domainStates.push(S_PROTOCOL_SECURE); + + // Become protocol tokens after a COLON + S_PROTOCOL_FILE.on(':', S_FULL_PROTOCOL); + S_PROTOCOL_SECURE.on(':', S_FULL_PROTOCOL); + S_MAILTO.on(':', S_FULL_MAILTO); + + // Localhost + var partialLocalhostStates = stateify('localhost', S_START, LOCALHOST, DOMAIN); + domainStates.push.apply(domainStates, partialLocalhostStates); + + // Everything else + // DOMAINs make more DOMAINs + // Number and character transitions + S_START.on(NUMBERS, S_NUM); + S_NUM.on('-', S_DOMAIN_HYPHEN).on(NUMBERS, S_NUM).on(ALPHANUM, S_DOMAIN); // number becomes DOMAIN + + S_DOMAIN.on('-', S_DOMAIN_HYPHEN).on(ALPHANUM, S_DOMAIN); + + // All the generated states should have a jump to DOMAIN + for (var _i = 0; _i < domainStates.length; _i++) { + domainStates[_i].on('-', S_DOMAIN_HYPHEN).on(ALPHANUM, S_DOMAIN); + } + + S_DOMAIN_HYPHEN.on('-', S_DOMAIN_HYPHEN).on(NUMBERS, S_DOMAIN).on(ALPHANUM, S_DOMAIN); + + // Set default transition + S_START.defaultTransition = makeState(SYM); + + /** + Given a string, returns an array of TOKEN instances representing the + composition of that string. + + @method run + @param {String} str Input string to scan + @return {Array} Array of TOKEN instances + */ + var run = function run(str) { + + // The state machine only looks at lowercase strings. + // This selective `toLowerCase` is used because lowercasing the entire + // string causes the length and character position to vary in some in some + // non-English strings. This happens only on V8-based runtimes. + var lowerStr = str.replace(/[A-Z]/g, function (c) { + return c.toLowerCase(); + }); + var len = str.length; + var tokens = []; // return value + + var cursor = 0; + + // Tokenize the string + while (cursor < len) { + var state = S_START; + var nextState = null; + var tokenLength = 0; + var latestAccepting = null; + var sinceAccepts = -1; + + while (cursor < len && (nextState = state.next(lowerStr[cursor]))) { + state = nextState; + + // Keep track of the latest accepting state + if (state.accepts()) { + sinceAccepts = 0; + latestAccepting = state; + } else if (sinceAccepts >= 0) { + sinceAccepts++; + } + + tokenLength++; + cursor++; + } + + if (sinceAccepts < 0) { + continue; + } // Should never happen + + // Roll back to the latest accepting state + cursor -= sinceAccepts; + tokenLength -= sinceAccepts; + + // Get the class for the new token + var TOKEN = latestAccepting.emit(); // Current token class + + // No more jumps, just make a new token + tokens.push(new TOKEN(str.substr(cursor - tokenLength, tokenLength))); + } + + return tokens; + }; + + var start = S_START; + + var scanner = Object.freeze({ + State: CharacterState, + TOKENS: text, + run: run, + start: start + }); + + /****************************************************************************** + Multi-Tokens + Tokens composed of arrays of TextTokens + ******************************************************************************/ + + // Is the given token a valid domain token? + // Should nums be included here? + function isDomainToken(token) { + return token instanceof DOMAIN || token instanceof TLD; + } + + /** + Abstract class used for manufacturing tokens of text tokens. That is rather + than the value for a token being a small string of text, it's value an array + of text tokens. + + Used for grouping together URLs, emails, hashtags, and other potential + creations. + + @class MultiToken + @abstract + */ + var MultiToken = createTokenClass(); + + MultiToken.prototype = { + /** + String representing the type for this token + @property type + @default 'TOKEN' + */ + type: 'token', + + /** + Is this multitoken a link? + @property isLink + @default false + */ + isLink: false, + + /** + Return the string this token represents. + @method toString + @return {String} + */ + toString: function toString() { + var result = []; + for (var _i2 = 0; _i2 < this.v.length; _i2++) { + result.push(this.v[_i2].toString()); + } + return result.join(''); + }, + + + /** + What should the value for this token be in the `href` HTML attribute? + Returns the `.toString` value by default. + @method toHref + @return {String} + */ + toHref: function toHref() { + return this.toString(); + }, + + + /** + Returns a hash of relevant values for this token, which includes keys + * type - Kind of token ('url', 'email', etc.) + * value - Original text + * href - The value that should be added to the anchor tag's href + attribute + @method toObject + @param {String} [protocol] `'http'` by default + @return {Object} + */ + toObject: function toObject() { + var protocol = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'http'; + + return { + type: this.type, + value: this.toString(), + href: this.toHref(protocol) + }; + } + }; + + /** + Represents an arbitrarily mailto email address with the prefix included + @class MAILTO + @extends MultiToken + */ + var MAILTOEMAIL = inherits(MultiToken, createTokenClass(), { + type: 'email', + isLink: true + }); + + /** + Represents a list of tokens making up a valid email address + @class EMAIL + @extends MultiToken + */ + var EMAIL = inherits(MultiToken, createTokenClass(), { + type: 'email', + isLink: true, + toHref: function toHref() { + return 'mailto:' + this.toString(); + } + }); + + /** + Represents some plain text + @class TEXT + @extends MultiToken + */ + var TEXT = inherits(MultiToken, createTokenClass(), { type: 'text' }); + + /** + Multi-linebreak token - represents a line break + @class NL + @extends MultiToken + */ + var NL$1 = inherits(MultiToken, createTokenClass(), { type: 'nl' }); + + /** + Represents a list of tokens making up a valid URL + @class URL + @extends MultiToken + */ + var URL = inherits(MultiToken, createTokenClass(), { + type: 'url', + isLink: true, + + /** + Lowercases relevant parts of the domain and adds the protocol if + required. Note that this will not escape unsafe HTML characters in the + URL. + @method href + @param {String} protocol + @return {String} + */ + toHref: function toHref() { + var protocol = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'http'; + + var hasProtocol = false; + var hasSlashSlash = false; + var tokens = this.v; + var result = []; + var i = 0; + + // Make the first part of the domain lowercase + // Lowercase protocol + while (tokens[i] instanceof PROTOCOL) { + hasProtocol = true; + result.push(tokens[i].toString().toLowerCase()); + i++; + } + + // Skip slash-slash + while (tokens[i] instanceof SLASH) { + hasSlashSlash = true; + result.push(tokens[i].toString()); + i++; + } + + // Lowercase all other characters in the domain + while (isDomainToken(tokens[i])) { + result.push(tokens[i].toString().toLowerCase()); + i++; + } + + // Leave all other characters as they were written + for (; i < tokens.length; i++) { + result.push(tokens[i].toString()); + } + + result = result.join(''); + + if (!(hasProtocol || hasSlashSlash)) { + result = protocol + '://' + result; + } + + return result; + }, + hasProtocol: function hasProtocol() { + return this.v[0] instanceof PROTOCOL; + } + }); + + var multi = Object.freeze({ + Base: MultiToken, + MAILTOEMAIL: MAILTOEMAIL, + EMAIL: EMAIL, + NL: NL$1, + TEXT: TEXT, + URL: URL + }); + + /** + Not exactly parser, more like the second-stage scanner (although we can + theoretically hotswap the code here with a real parser in the future... but + for a little URL-finding utility abstract syntax trees may be a little + overkill). + + URL format: http://en.wikipedia.org/wiki/URI_scheme + Email format: http://en.wikipedia.org/wiki/Email_address (links to RFC in + reference) + + @module linkify + @submodule parser + @main parser + */ + + var makeState$1 = function makeState$1(tokenClass) { + return new TokenState(tokenClass); + }; + + // The universal starting state. + var S_START$1 = makeState$1(); + + // Intermediate states for URLs. Note that domains that begin with a protocol + // are treated slighly differently from those that don't. + var S_PROTOCOL = makeState$1(); // e.g., 'http:' + var S_MAILTO$1 = makeState$1(); // 'mailto:' + var S_PROTOCOL_SLASH = makeState$1(); // e.g., '/', 'http:/'' + var S_PROTOCOL_SLASH_SLASH = makeState$1(); // e.g., '//', 'http://' + var S_DOMAIN$1 = makeState$1(); // parsed string ends with a potential domain name (A) + var S_DOMAIN_DOT = makeState$1(); // (A) domain followed by DOT + var S_TLD = makeState$1(URL); // (A) Simplest possible URL with no query string + var S_TLD_COLON = makeState$1(); // (A) URL followed by colon (potential port number here) + var S_TLD_PORT = makeState$1(URL); // TLD followed by a port number + var S_URL = makeState$1(URL); // Long URL with optional port and maybe query string + var S_URL_NON_ACCEPTING = makeState$1(); // URL followed by some symbols (will not be part of the final URL) + var S_URL_OPENBRACE = makeState$1(); // URL followed by { + var S_URL_OPENBRACKET = makeState$1(); // URL followed by [ + var S_URL_OPENANGLEBRACKET = makeState$1(); // URL followed by < + var S_URL_OPENPAREN = makeState$1(); // URL followed by ( + var S_URL_OPENBRACE_Q = makeState$1(URL); // URL followed by { and some symbols that the URL can end it + var S_URL_OPENBRACKET_Q = makeState$1(URL); // URL followed by [ and some symbols that the URL can end it + var S_URL_OPENANGLEBRACKET_Q = makeState$1(URL); // URL followed by < and some symbols that the URL can end it + var S_URL_OPENPAREN_Q = makeState$1(URL); // URL followed by ( and some symbols that the URL can end it + var S_URL_OPENBRACE_SYMS = makeState$1(); // S_URL_OPENBRACE_Q followed by some symbols it cannot end it + var S_URL_OPENBRACKET_SYMS = makeState$1(); // S_URL_OPENBRACKET_Q followed by some symbols it cannot end it + var S_URL_OPENANGLEBRACKET_SYMS = makeState$1(); // S_URL_OPENANGLEBRACKET_Q followed by some symbols it cannot end it + var S_URL_OPENPAREN_SYMS = makeState$1(); // S_URL_OPENPAREN_Q followed by some symbols it cannot end it + var S_EMAIL_DOMAIN = makeState$1(); // parsed string starts with local email info + @ with a potential domain name (C) + var S_EMAIL_DOMAIN_DOT = makeState$1(); // (C) domain followed by DOT + var S_EMAIL = makeState$1(EMAIL); // (C) Possible email address (could have more tlds) + var S_EMAIL_COLON = makeState$1(); // (C) URL followed by colon (potential port number here) + var S_EMAIL_PORT = makeState$1(EMAIL); // (C) Email address with a port + var S_MAILTO_EMAIL = makeState$1(MAILTOEMAIL); // Email that begins with the mailto prefix (D) + var S_MAILTO_EMAIL_NON_ACCEPTING = makeState$1(); // (D) Followed by some non-query string chars + var S_LOCALPART = makeState$1(); // Local part of the email address + var S_LOCALPART_AT = makeState$1(); // Local part of the email address plus @ + var S_LOCALPART_DOT = makeState$1(); // Local part of the email address plus '.' (localpart cannot end in .) + var S_NL = makeState$1(NL$1); // single new line + + // Make path from start to protocol (with '//') + S_START$1.on(NL, S_NL).on(PROTOCOL, S_PROTOCOL).on(MAILTO, S_MAILTO$1).on(SLASH, S_PROTOCOL_SLASH); + + S_PROTOCOL.on(SLASH, S_PROTOCOL_SLASH); + S_PROTOCOL_SLASH.on(SLASH, S_PROTOCOL_SLASH_SLASH); + + // The very first potential domain name + S_START$1.on(TLD, S_DOMAIN$1).on(DOMAIN, S_DOMAIN$1).on(LOCALHOST, S_TLD).on(NUM, S_DOMAIN$1); + + // Force URL for protocol followed by anything sane + S_PROTOCOL_SLASH_SLASH.on(TLD, S_URL).on(DOMAIN, S_URL).on(NUM, S_URL).on(LOCALHOST, S_URL); + + // Account for dots and hyphens + // hyphens are usually parts of domain names + S_DOMAIN$1.on(DOT, S_DOMAIN_DOT); + S_EMAIL_DOMAIN.on(DOT, S_EMAIL_DOMAIN_DOT); + + // Hyphen can jump back to a domain name + + // After the first domain and a dot, we can find either a URL or another domain + S_DOMAIN_DOT.on(TLD, S_TLD).on(DOMAIN, S_DOMAIN$1).on(NUM, S_DOMAIN$1).on(LOCALHOST, S_DOMAIN$1); + + S_EMAIL_DOMAIN_DOT.on(TLD, S_EMAIL).on(DOMAIN, S_EMAIL_DOMAIN).on(NUM, S_EMAIL_DOMAIN).on(LOCALHOST, S_EMAIL_DOMAIN); + + // S_TLD accepts! But the URL could be longer, try to find a match greedily + // The `run` function should be able to "rollback" to the accepting state + S_TLD.on(DOT, S_DOMAIN_DOT); + S_EMAIL.on(DOT, S_EMAIL_DOMAIN_DOT); + + // Become real URLs after `SLASH` or `COLON NUM SLASH` + // Here PSS and non-PSS converge + S_TLD.on(COLON, S_TLD_COLON).on(SLASH, S_URL); + S_TLD_COLON.on(NUM, S_TLD_PORT); + S_TLD_PORT.on(SLASH, S_URL); + S_EMAIL.on(COLON, S_EMAIL_COLON); + S_EMAIL_COLON.on(NUM, S_EMAIL_PORT); + + // Types of characters the URL can definitely end in + var qsAccepting = [DOMAIN, AT, LOCALHOST, NUM, PLUS, POUND, PROTOCOL, SLASH, TLD, UNDERSCORE, SYM, AMPERSAND]; + + // Types of tokens that can follow a URL and be part of the query string + // but cannot be the very last characters + // Characters that cannot appear in the URL at all should be excluded + var qsNonAccepting = [COLON, DOT, QUERY, PUNCTUATION, CLOSEBRACE, CLOSEBRACKET, CLOSEANGLEBRACKET, CLOSEPAREN, OPENBRACE, OPENBRACKET, OPENANGLEBRACKET, OPENPAREN]; + + // These states are responsible primarily for determining whether or not to + // include the final round bracket. + + // URL, followed by an opening bracket + S_URL.on(OPENBRACE, S_URL_OPENBRACE).on(OPENBRACKET, S_URL_OPENBRACKET).on(OPENANGLEBRACKET, S_URL_OPENANGLEBRACKET).on(OPENPAREN, S_URL_OPENPAREN); + + // URL with extra symbols at the end, followed by an opening bracket + S_URL_NON_ACCEPTING.on(OPENBRACE, S_URL_OPENBRACE).on(OPENBRACKET, S_URL_OPENBRACKET).on(OPENANGLEBRACKET, S_URL_OPENANGLEBRACKET).on(OPENPAREN, S_URL_OPENPAREN); + + // Closing bracket component. This character WILL be included in the URL + S_URL_OPENBRACE.on(CLOSEBRACE, S_URL); + S_URL_OPENBRACKET.on(CLOSEBRACKET, S_URL); + S_URL_OPENANGLEBRACKET.on(CLOSEANGLEBRACKET, S_URL); + S_URL_OPENPAREN.on(CLOSEPAREN, S_URL); + S_URL_OPENBRACE_Q.on(CLOSEBRACE, S_URL); + S_URL_OPENBRACKET_Q.on(CLOSEBRACKET, S_URL); + S_URL_OPENANGLEBRACKET_Q.on(CLOSEANGLEBRACKET, S_URL); + S_URL_OPENPAREN_Q.on(CLOSEPAREN, S_URL); + S_URL_OPENBRACE_SYMS.on(CLOSEBRACE, S_URL); + S_URL_OPENBRACKET_SYMS.on(CLOSEBRACKET, S_URL); + S_URL_OPENANGLEBRACKET_SYMS.on(CLOSEANGLEBRACKET, S_URL); + S_URL_OPENPAREN_SYMS.on(CLOSEPAREN, S_URL); + + // URL that beings with an opening bracket, followed by a symbols. + // Note that the final state can still be `S_URL_OPENBRACE_Q` (if the URL only + // has a single opening bracket for some reason). + S_URL_OPENBRACE.on(qsAccepting, S_URL_OPENBRACE_Q); + S_URL_OPENBRACKET.on(qsAccepting, S_URL_OPENBRACKET_Q); + S_URL_OPENANGLEBRACKET.on(qsAccepting, S_URL_OPENANGLEBRACKET_Q); + S_URL_OPENPAREN.on(qsAccepting, S_URL_OPENPAREN_Q); + S_URL_OPENBRACE.on(qsNonAccepting, S_URL_OPENBRACE_SYMS); + S_URL_OPENBRACKET.on(qsNonAccepting, S_URL_OPENBRACKET_SYMS); + S_URL_OPENANGLEBRACKET.on(qsNonAccepting, S_URL_OPENANGLEBRACKET_SYMS); + S_URL_OPENPAREN.on(qsNonAccepting, S_URL_OPENPAREN_SYMS); + + // URL that begins with an opening bracket, followed by some symbols + S_URL_OPENBRACE_Q.on(qsAccepting, S_URL_OPENBRACE_Q); + S_URL_OPENBRACKET_Q.on(qsAccepting, S_URL_OPENBRACKET_Q); + S_URL_OPENANGLEBRACKET_Q.on(qsAccepting, S_URL_OPENANGLEBRACKET_Q); + S_URL_OPENPAREN_Q.on(qsAccepting, S_URL_OPENPAREN_Q); + S_URL_OPENBRACE_Q.on(qsNonAccepting, S_URL_OPENBRACE_Q); + S_URL_OPENBRACKET_Q.on(qsNonAccepting, S_URL_OPENBRACKET_Q); + S_URL_OPENANGLEBRACKET_Q.on(qsNonAccepting, S_URL_OPENANGLEBRACKET_Q); + S_URL_OPENPAREN_Q.on(qsNonAccepting, S_URL_OPENPAREN_Q); + + S_URL_OPENBRACE_SYMS.on(qsAccepting, S_URL_OPENBRACE_Q); + S_URL_OPENBRACKET_SYMS.on(qsAccepting, S_URL_OPENBRACKET_Q); + S_URL_OPENANGLEBRACKET_SYMS.on(qsAccepting, S_URL_OPENANGLEBRACKET_Q); + S_URL_OPENPAREN_SYMS.on(qsAccepting, S_URL_OPENPAREN_Q); + S_URL_OPENBRACE_SYMS.on(qsNonAccepting, S_URL_OPENBRACE_SYMS); + S_URL_OPENBRACKET_SYMS.on(qsNonAccepting, S_URL_OPENBRACKET_SYMS); + S_URL_OPENANGLEBRACKET_SYMS.on(qsNonAccepting, S_URL_OPENANGLEBRACKET_SYMS); + S_URL_OPENPAREN_SYMS.on(qsNonAccepting, S_URL_OPENPAREN_SYMS); + + // Account for the query string + S_URL.on(qsAccepting, S_URL); + S_URL_NON_ACCEPTING.on(qsAccepting, S_URL); + + S_URL.on(qsNonAccepting, S_URL_NON_ACCEPTING); + S_URL_NON_ACCEPTING.on(qsNonAccepting, S_URL_NON_ACCEPTING); + + // Email address-specific state definitions + // Note: We are not allowing '/' in email addresses since this would interfere + // with real URLs + + // For addresses with the mailto prefix + // 'mailto:' followed by anything sane is a valid email + S_MAILTO$1.on(TLD, S_MAILTO_EMAIL).on(DOMAIN, S_MAILTO_EMAIL).on(NUM, S_MAILTO_EMAIL).on(LOCALHOST, S_MAILTO_EMAIL); + + // Greedily get more potential valid email values + S_MAILTO_EMAIL.on(qsAccepting, S_MAILTO_EMAIL).on(qsNonAccepting, S_MAILTO_EMAIL_NON_ACCEPTING); + S_MAILTO_EMAIL_NON_ACCEPTING.on(qsAccepting, S_MAILTO_EMAIL).on(qsNonAccepting, S_MAILTO_EMAIL_NON_ACCEPTING); + + // For addresses without the mailto prefix + // Tokens allowed in the localpart of the email + var localpartAccepting = [DOMAIN, NUM, PLUS, POUND, QUERY, UNDERSCORE, SYM, AMPERSAND, TLD]; + + // Some of the tokens in `localpartAccepting` are already accounted for here and + // will not be overwritten (don't worry) + S_DOMAIN$1.on(localpartAccepting, S_LOCALPART).on(AT, S_LOCALPART_AT); + S_TLD.on(localpartAccepting, S_LOCALPART).on(AT, S_LOCALPART_AT); + S_DOMAIN_DOT.on(localpartAccepting, S_LOCALPART); + + // Okay we're on a localpart. Now what? + // TODO: IP addresses and what if the email starts with numbers? + S_LOCALPART.on(localpartAccepting, S_LOCALPART).on(AT, S_LOCALPART_AT) // close to an email address now + .on(DOT, S_LOCALPART_DOT); + S_LOCALPART_DOT.on(localpartAccepting, S_LOCALPART); + S_LOCALPART_AT.on(TLD, S_EMAIL_DOMAIN).on(DOMAIN, S_EMAIL_DOMAIN).on(LOCALHOST, S_EMAIL); + // States following `@` defined above + + var run$1 = function run$1(tokens) { + var len = tokens.length; + var cursor = 0; + var multis = []; + var textTokens = []; + + while (cursor < len) { + var state = S_START$1; + var secondState = null; + var nextState = null; + var multiLength = 0; + var latestAccepting = null; + var sinceAccepts = -1; + + while (cursor < len && !(secondState = state.next(tokens[cursor]))) { + // Starting tokens with nowhere to jump to. + // Consider these to be just plain text + textTokens.push(tokens[cursor++]); + } + + while (cursor < len && (nextState = secondState || state.next(tokens[cursor]))) { + + // Get the next state + secondState = null; + state = nextState; + + // Keep track of the latest accepting state + if (state.accepts()) { + sinceAccepts = 0; + latestAccepting = state; + } else if (sinceAccepts >= 0) { + sinceAccepts++; + } + + cursor++; + multiLength++; + } + + if (sinceAccepts < 0) { + + // No accepting state was found, part of a regular text token + // Add all the tokens we looked at to the text tokens array + for (var _i3 = cursor - multiLength; _i3 < cursor; _i3++) { + textTokens.push(tokens[_i3]); + } + } else { + + // Accepting state! + + // First close off the textTokens (if available) + if (textTokens.length > 0) { + multis.push(new TEXT(textTokens)); + textTokens = []; + } + + // Roll back to the latest accepting state + cursor -= sinceAccepts; + multiLength -= sinceAccepts; + + // Create a new multitoken + var MULTI = latestAccepting.emit(); + multis.push(new MULTI(tokens.slice(cursor - multiLength, cursor))); + } + } + + // Finally close off the textTokens (if available) + if (textTokens.length > 0) { + multis.push(new TEXT(textTokens)); + } + + return multis; + }; + + var parser = Object.freeze({ + State: TokenState, + TOKENS: multi, + run: run$1, + start: S_START$1 + }); + + if (!Array.isArray) { + Array.isArray = function (arg) { + return Object.prototype.toString.call(arg) === '[object Array]'; + }; + } + + /** + Converts a string into tokens that represent linkable and non-linkable bits + @method tokenize + @param {String} str + @return {Array} tokens + */ + var tokenize = function tokenize(str) { + return run$1(run(str)); + }; + + /** + Returns a list of linkable items in the given string. + */ + var find = function find(str) { + var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + + var tokens = tokenize(str); + var filtered = []; + + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + if (token.isLink && (!type || token.type === type)) { + filtered.push(token.toObject()); + } + } + + return filtered; + }; + + /** + Is the given string valid linkable text of some sort + Note that this does not trim the text for you. + + Optionally pass in a second `type` param, which is the type of link to test + for. + + For example, + + test(str, 'email'); + + Will return `true` if str is a valid email. + */ + var test = function test(str) { + var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + + var tokens = tokenize(str); + return tokens.length === 1 && tokens[0].isLink && (!type || tokens[0].type === type); + }; + + exports.find = find; + exports.inherits = inherits; + exports.options = options; + exports.parser = parser; + exports.scanner = scanner; + exports.test = test; + exports.tokenize = tokenize; +})(self.linkify = self.linkify || {}); +})(); \ No newline at end of file diff --git a/app/Resources/public/assets/linkifyjs/linkify.min.js b/app/Resources/public/assets/linkifyjs/linkify.min.js new file mode 100644 index 0000000000..94ef0a5d88 --- /dev/null +++ b/app/Resources/public/assets/linkifyjs/linkify.min.js @@ -0,0 +1 @@ +!function(){"use strict";var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol&&n!==Symbol.prototype?"symbol":typeof n};!function(e){function a(n,e){var a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},t=Object.create(n.prototype);for(var o in a)t[o]=a[o];return t.constructor=e,e.prototype=t,e}function t(n){n=n||{},this.defaultProtocol=n.hasOwnProperty("defaultProtocol")?n.defaultProtocol:h.defaultProtocol,this.events=n.hasOwnProperty("events")?n.events:h.events,this.format=n.hasOwnProperty("format")?n.format:h.format,this.formatHref=n.hasOwnProperty("formatHref")?n.formatHref:h.formatHref,this.nl2br=n.hasOwnProperty("nl2br")?n.nl2br:h.nl2br,this.tagName=n.hasOwnProperty("tagName")?n.tagName:h.tagName,this.target=n.hasOwnProperty("target")?n.target:h.target,this.validate=n.hasOwnProperty("validate")?n.validate:h.validate,this.ignoreTags=[],this.attributes=n.attributes||n.linkAttributes||h.attributes,this.className=n.hasOwnProperty("className")?n.className:n.linkClass||h.className;for(var e=n.hasOwnProperty("ignoreTags")?n.ignoreTags:h.ignoreTags,a=0;a=r)return[];for(;o1&&void 0!==arguments[1]?arguments[1]:null,a=this.next(new n(""));return a===this.defaultTransition?(a=new this.constructor(e),this.on(n,a)):e&&(a.T=e),a},test:function(n,e){return n instanceof e}}),d=l();d.prototype={toString:function(){return this.v+""}};var x=u(),y=u("@"),v=u(":"),k=u("."),w=u(),j=u(),z=u("\n"),O=u(),q=u("+"),N=u("#"),S=u(),T=u("mailto:"),A=u("?"),L=u("/"),P=u("_"),E=u(),C=u(),R=u(),H=u("{"),B=u("["),U=u("<"),M=u("("),D=u("}"),I=u("]"),K=u(">"),_=u(")"),G=u("&"),Y=Object.freeze({Base:d,DOMAIN:x,AT:y,COLON:v,DOT:k,PUNCTUATION:w,LOCALHOST:j,NL:z,NUM:O,PLUS:q,POUND:N,QUERY:A,PROTOCOL:S,MAILTO:T,SLASH:L,UNDERSCORE:P,SYM:E,TLD:C,WS:R,OPENBRACE:H,OPENBRACKET:B,OPENANGLEBRACKET:U,OPENPAREN:M,CLOSEBRACE:D,CLOSEBRACKET:I,CLOSEANGLEBRACKET:K,CLOSEPAREN:_,AMPERSAND:G}),Q="aaa|aarp|abarth|abb|abbott|abbvie|abc|able|abogado|abudhabi|ac|academy|accenture|accountant|accountants|aco|active|actor|ad|adac|ads|adult|ae|aeg|aero|aetna|af|afamilycompany|afl|africa|ag|agakhan|agency|ai|aig|aigo|airbus|airforce|airtel|akdn|al|alfaromeo|alibaba|alipay|allfinanz|allstate|ally|alsace|alstom|am|americanexpress|americanfamily|amex|amfam|amica|amsterdam|analytics|android|anquan|anz|ao|aol|apartments|app|apple|aq|aquarelle|ar|arab|aramco|archi|army|arpa|art|arte|as|asda|asia|associates|at|athleta|attorney|au|auction|audi|audible|audio|auspost|author|auto|autos|avianca|aw|aws|ax|axa|az|azure|ba|baby|baidu|banamex|bananarepublic|band|bank|bar|barcelona|barclaycard|barclays|barefoot|bargains|baseball|basketball|bauhaus|bayern|bb|bbc|bbt|bbva|bcg|bcn|bd|be|beats|beauty|beer|bentley|berlin|best|bestbuy|bet|bf|bg|bh|bharti|bi|bible|bid|bike|bing|bingo|bio|biz|bj|black|blackfriday|blanco|blockbuster|blog|bloomberg|blue|bm|bms|bmw|bn|bnl|bnpparibas|bo|boats|boehringer|bofa|bom|bond|boo|book|booking|boots|bosch|bostik|boston|bot|boutique|box|br|bradesco|bridgestone|broadway|broker|brother|brussels|bs|bt|budapest|bugatti|build|builders|business|buy|buzz|bv|bw|by|bz|bzh|ca|cab|cafe|cal|call|calvinklein|cam|camera|camp|cancerresearch|canon|capetown|capital|capitalone|car|caravan|cards|care|career|careers|cars|cartier|casa|case|caseih|cash|casino|cat|catering|catholic|cba|cbn|cbre|cbs|cc|cd|ceb|center|ceo|cern|cf|cfa|cfd|cg|ch|chanel|channel|chase|chat|cheap|chintai|chloe|christmas|chrome|chrysler|church|ci|cipriani|circle|cisco|citadel|citi|citic|city|cityeats|ck|cl|claims|cleaning|click|clinic|clinique|clothing|cloud|club|clubmed|cm|cn|co|coach|codes|coffee|college|cologne|com|comcast|commbank|community|company|compare|computer|comsec|condos|construction|consulting|contact|contractors|cooking|cookingchannel|cool|coop|corsica|country|coupon|coupons|courses|cr|credit|creditcard|creditunion|cricket|crown|crs|cruise|cruises|csc|cu|cuisinella|cv|cw|cx|cy|cymru|cyou|cz|dabur|dad|dance|data|date|dating|datsun|day|dclk|dds|de|deal|dealer|deals|degree|delivery|dell|deloitte|delta|democrat|dental|dentist|desi|design|dev|dhl|diamonds|diet|digital|direct|directory|discount|discover|dish|diy|dj|dk|dm|dnp|do|docs|doctor|dodge|dog|doha|domains|dot|download|drive|dtv|dubai|duck|dunlop|duns|dupont|durban|dvag|dvr|dz|earth|eat|ec|eco|edeka|edu|education|ee|eg|email|emerck|energy|engineer|engineering|enterprises|epost|epson|equipment|er|ericsson|erni|es|esq|estate|esurance|et|etisalat|eu|eurovision|eus|events|everbank|exchange|expert|exposed|express|extraspace|fage|fail|fairwinds|faith|family|fan|fans|farm|farmers|fashion|fast|fedex|feedback|ferrari|ferrero|fi|fiat|fidelity|fido|film|final|finance|financial|fire|firestone|firmdale|fish|fishing|fit|fitness|fj|fk|flickr|flights|flir|florist|flowers|fly|fm|fo|foo|food|foodnetwork|football|ford|forex|forsale|forum|foundation|fox|fr|free|fresenius|frl|frogans|frontdoor|frontier|ftr|fujitsu|fujixerox|fun|fund|furniture|futbol|fyi|ga|gal|gallery|gallo|gallup|game|games|gap|garden|gb|gbiz|gd|gdn|ge|gea|gent|genting|george|gf|gg|ggee|gh|gi|gift|gifts|gives|giving|gl|glade|glass|gle|global|globo|gm|gmail|gmbh|gmo|gmx|gn|godaddy|gold|goldpoint|golf|goo|goodhands|goodyear|goog|google|gop|got|gov|gp|gq|gr|grainger|graphics|gratis|green|gripe|grocery|group|gs|gt|gu|guardian|gucci|guge|guide|guitars|guru|gw|gy|hair|hamburg|hangout|haus|hbo|hdfc|hdfcbank|health|healthcare|help|helsinki|here|hermes|hgtv|hiphop|hisamitsu|hitachi|hiv|hk|hkt|hm|hn|hockey|holdings|holiday|homedepot|homegoods|homes|homesense|honda|honeywell|horse|hospital|host|hosting|hot|hoteles|hotels|hotmail|house|how|hr|hsbc|ht|htc|hu|hughes|hyatt|hyundai|ibm|icbc|ice|icu|id|ie|ieee|ifm|ikano|il|im|imamat|imdb|immo|immobilien|in|industries|infiniti|info|ing|ink|institute|insurance|insure|int|intel|international|intuit|investments|io|ipiranga|iq|ir|irish|is|iselect|ismaili|ist|istanbul|it|itau|itv|iveco|iwc|jaguar|java|jcb|jcp|je|jeep|jetzt|jewelry|jio|jlc|jll|jm|jmp|jnj|jo|jobs|joburg|jot|joy|jp|jpmorgan|jprs|juegos|juniper|kaufen|kddi|ke|kerryhotels|kerrylogistics|kerryproperties|kfh|kg|kh|ki|kia|kim|kinder|kindle|kitchen|kiwi|km|kn|koeln|komatsu|kosher|kp|kpmg|kpn|kr|krd|kred|kuokgroup|kw|ky|kyoto|kz|la|lacaixa|ladbrokes|lamborghini|lamer|lancaster|lancia|lancome|land|landrover|lanxess|lasalle|lat|latino|latrobe|law|lawyer|lb|lc|lds|lease|leclerc|lefrak|legal|lego|lexus|lgbt|li|liaison|lidl|life|lifeinsurance|lifestyle|lighting|like|lilly|limited|limo|lincoln|linde|link|lipsy|live|living|lixil|lk|loan|loans|locker|locus|loft|lol|london|lotte|lotto|love|lpl|lplfinancial|lr|ls|lt|ltd|ltda|lu|lundbeck|lupin|luxe|luxury|lv|ly|ma|macys|madrid|maif|maison|makeup|man|management|mango|map|market|marketing|markets|marriott|marshalls|maserati|mattel|mba|mc|mckinsey|md|me|med|media|meet|melbourne|meme|memorial|men|menu|meo|merckmsd|metlife|mg|mh|miami|microsoft|mil|mini|mint|mit|mitsubishi|mk|ml|mlb|mls|mm|mma|mn|mo|mobi|mobile|mobily|moda|moe|moi|mom|monash|money|monster|mopar|mormon|mortgage|moscow|moto|motorcycles|mov|movie|movistar|mp|mq|mr|ms|msd|mt|mtn|mtr|mu|museum|mutual|mv|mw|mx|my|mz|na|nab|nadex|nagoya|name|nationwide|natura|navy|nba|nc|ne|nec|net|netbank|netflix|network|neustar|new|newholland|news|next|nextdirect|nexus|nf|nfl|ng|ngo|nhk|ni|nico|nike|nikon|ninja|nissan|nissay|nl|no|nokia|northwesternmutual|norton|now|nowruz|nowtv|np|nr|nra|nrw|ntt|nu|nyc|nz|obi|observer|off|office|okinawa|olayan|olayangroup|oldnavy|ollo|om|omega|one|ong|onl|online|onyourside|ooo|open|oracle|orange|org|organic|origins|osaka|otsuka|ott|ovh|pa|page|panasonic|panerai|paris|pars|partners|parts|party|passagens|pay|pccw|pe|pet|pf|pfizer|pg|ph|pharmacy|phd|philips|phone|photo|photography|photos|physio|piaget|pics|pictet|pictures|pid|pin|ping|pink|pioneer|pizza|pk|pl|place|play|playstation|plumbing|plus|pm|pn|pnc|pohl|poker|politie|porn|post|pr|pramerica|praxi|press|prime|pro|prod|productions|prof|progressive|promo|properties|property|protection|pru|prudential|ps|pt|pub|pw|pwc|py|qa|qpon|quebec|quest|qvc|racing|radio|raid|re|read|realestate|realtor|realty|recipes|red|redstone|redumbrella|rehab|reise|reisen|reit|reliance|ren|rent|rentals|repair|report|republican|rest|restaurant|review|reviews|rexroth|rich|richardli|ricoh|rightathome|ril|rio|rip|rmit|ro|rocher|rocks|rodeo|rogers|room|rs|rsvp|ru|rugby|ruhr|run|rw|rwe|ryukyu|sa|saarland|safe|safety|sakura|sale|salon|samsclub|samsung|sandvik|sandvikcoromant|sanofi|sap|sapo|sarl|sas|save|saxo|sb|sbi|sbs|sc|sca|scb|schaeffler|schmidt|scholarships|school|schule|schwarz|science|scjohnson|scor|scot|sd|se|search|seat|secure|security|seek|select|sener|services|ses|seven|sew|sex|sexy|sfr|sg|sh|shangrila|sharp|shaw|shell|shia|shiksha|shoes|shop|shopping|shouji|show|showtime|shriram|si|silk|sina|singles|site|sj|sk|ski|skin|sky|skype|sl|sling|sm|smart|smile|sn|sncf|so|soccer|social|softbank|software|sohu|solar|solutions|song|sony|soy|space|spiegel|spot|spreadbetting|sr|srl|srt|st|stada|staples|star|starhub|statebank|statefarm|statoil|stc|stcgroup|stockholm|storage|store|stream|studio|study|style|su|sucks|supplies|supply|support|surf|surgery|suzuki|sv|swatch|swiftcover|swiss|sx|sy|sydney|symantec|systems|sz|tab|taipei|talk|taobao|target|tatamotors|tatar|tattoo|tax|taxi|tc|tci|td|tdk|team|tech|technology|tel|telecity|telefonica|temasek|tennis|teva|tf|tg|th|thd|theater|theatre|tiaa|tickets|tienda|tiffany|tips|tires|tirol|tj|tjmaxx|tjx|tk|tkmaxx|tl|tm|tmall|tn|to|today|tokyo|tools|top|toray|toshiba|total|tours|town|toyota|toys|tr|trade|trading|training|travel|travelchannel|travelers|travelersinsurance|trust|trv|tt|tube|tui|tunes|tushu|tv|tvs|tw|tz|ua|ubank|ubs|uconnect|ug|uk|unicom|university|uno|uol|ups|us|uy|uz|va|vacations|vana|vanguard|vc|ve|vegas|ventures|verisign|versicherung|vet|vg|vi|viajes|video|vig|viking|villas|vin|vip|virgin|visa|vision|vista|vistaprint|viva|vivo|vlaanderen|vn|vodka|volkswagen|volvo|vote|voting|voto|voyage|vu|vuelos|wales|walmart|walter|wang|wanggou|warman|watch|watches|weather|weatherchannel|webcam|weber|website|wed|wedding|weibo|weir|wf|whoswho|wien|wiki|williamhill|win|windows|wine|winners|wme|wolterskluwer|woodside|work|works|world|wow|ws|wtc|wtf|xbox|xerox|xfinity|xihuan|xin|xn--11b4c3d|xn--1ck2e1b|xn--1qqw23a|xn--2scrj9c|xn--30rr7y|xn--3bst00m|xn--3ds443g|xn--3e0b707e|xn--3hcrj9c|xn--3oq18vl8pn36a|xn--3pxu8k|xn--42c2d9a|xn--45br5cyl|xn--45brj9c|xn--45q11c|xn--4gbrim|xn--54b7fta0cc|xn--55qw42g|xn--55qx5d|xn--5su34j936bgsg|xn--5tzm5g|xn--6frz82g|xn--6qq986b3xl|xn--80adxhks|xn--80ao21a|xn--80aqecdr1a|xn--80asehdb|xn--80aswg|xn--8y0a063a|xn--90a3ac|xn--90ae|xn--90ais|xn--9dbq2a|xn--9et52u|xn--9krt00a|xn--b4w605ferd|xn--bck1b9a5dre4c|xn--c1avg|xn--c2br7g|xn--cck2b3b|xn--cg4bki|xn--clchc0ea0b2g2a9gcd|xn--czr694b|xn--czrs0t|xn--czru2d|xn--d1acj3b|xn--d1alf|xn--e1a4c|xn--eckvdtc9d|xn--efvy88h|xn--estv75g|xn--fct429k|xn--fhbei|xn--fiq228c5hs|xn--fiq64b|xn--fiqs8s|xn--fiqz9s|xn--fjq720a|xn--flw351e|xn--fpcrj9c3d|xn--fzc2c9e2c|xn--fzys8d69uvgm|xn--g2xx48c|xn--gckr3f0f|xn--gecrj9c|xn--gk3at1e|xn--h2breg3eve|xn--h2brj9c|xn--h2brj9c8c|xn--hxt814e|xn--i1b6b1a6a2e|xn--imr513n|xn--io0a7i|xn--j1aef|xn--j1amh|xn--j6w193g|xn--jlq61u9w7b|xn--jvr189m|xn--kcrx77d1x4a|xn--kprw13d|xn--kpry57d|xn--kpu716f|xn--kput3i|xn--l1acc|xn--lgbbat1ad8j|xn--mgb9awbf|xn--mgba3a3ejt|xn--mgba3a4f16a|xn--mgba7c0bbn0a|xn--mgbaakc7dvf|xn--mgbaam7a8h|xn--mgbab2bd|xn--mgbai9azgqp6j|xn--mgbayh7gpa|xn--mgbb9fbpob|xn--mgbbh1a|xn--mgbbh1a71e|xn--mgbc0a9azcg|xn--mgbca7dzdo|xn--mgberp4a5d4ar|xn--mgbgu82a|xn--mgbi4ecexp|xn--mgbpl2fh|xn--mgbt3dhd|xn--mgbtx2b|xn--mgbx4cd0ab|xn--mix891f|xn--mk1bu44c|xn--mxtq1m|xn--ngbc5azd|xn--ngbe9e0a|xn--ngbrx|xn--node|xn--nqv7f|xn--nqv7fs00ema|xn--nyqy26a|xn--o3cw4h|xn--ogbpf8fl|xn--p1acf|xn--p1ai|xn--pbt977c|xn--pgbs0dh|xn--pssy2u|xn--q9jyb4c|xn--qcka1pmc|xn--qxam|xn--rhqv96g|xn--rovu88b|xn--rvc1e0am3e|xn--s9brj9c|xn--ses554g|xn--t60b56a|xn--tckwe|xn--tiq49xqyj|xn--unup4y|xn--vermgensberater-ctb|xn--vermgensberatung-pwb|xn--vhquv|xn--vuq861b|xn--w4r85el8fhu5dnra|xn--w4rs40l|xn--wgbh1c|xn--wgbl6a|xn--xhq521b|xn--xkc2al3hye2a|xn--xkc2dl3a5ee0h|xn--y9a3aq|xn--yfro4i67o|xn--ygbi2ammx|xn--zfr164b|xperia|xxx|xyz|yachts|yahoo|yamaxun|yandex|ye|yodobashi|yoga|yokohama|you|youtube|yt|yun|za|zappos|zara|zero|zip|zippo|zm|zone|zuerich|zw".split("|"),W="0123456789".split(""),X="0123456789abcdefghijklmnopqrstuvwxyz".split(""),Z=[" ","\f","\r","\t","\x0B"," "," ","᠎"],F=[],J=function(n){return new m(n)},V=J(),$=J(O),nn=J(x),en=J(),an=J(R);V.on("@",J(y)).on(".",J(k)).on("+",J(q)).on("#",J(N)).on("?",J(A)).on("/",J(L)).on("_",J(P)).on(":",J(v)).on("{",J(H)).on("[",J(B)).on("<",J(U)).on("(",J(M)).on("}",J(D)).on("]",J(I)).on(">",J(K)).on(")",J(_)).on("&",J(G)).on([",",";","!",'"',"'"],J(w)),V.on("\n",J(z)).on(Z,an),an.on(Z,an);for(var tn=0;tn=0&&l++,s++,o++;if(!(l<0)){o-=l,s-=l;var u=c.emit();t.push(new u(n.substr(o-s,s)))}}return t},vn=V,kn=Object.freeze({State:m,TOKENS:Y,run:yn,start:vn}),wn=l();wn.prototype={type:"token",isLink:!1,toString:function(){for(var n=[],e=0;e0&&void 0!==arguments[0]?arguments[0]:"http";return{type:this.type,value:this.toString(),href:this.toHref(n)}}};var jn=a(wn,l(),{type:"email",isLink:!0}),zn=a(wn,l(),{type:"email",isLink:!0,toHref:function(){return"mailto:"+this.toString()}}),On=a(wn,l(),{type:"text"}),qn=a(wn,l(),{type:"nl"}),Nn=a(wn,l(),{type:"url",isLink:!0,toHref:function(){for(var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"http",e=!1,a=!1,t=this.v,o=[],r=0;t[r]instanceof S;)e=!0,o.push(t[r].toString().toLowerCase()),r++;for(;t[r]instanceof L;)a=!0,o.push(t[r].toString()),r++;for(;g(t[r]);)o.push(t[r].toString().toLowerCase()),r++;for(;r=0&&u++,a++,c++;if(u<0)for(var g=a-c;g0&&(t.push(new On(o)),o=[]),a-=u,c-=u;var h=l.emit();t.push(new h(n.slice(a-c,a)))}}return o.length>0&&t.push(new On(o)),t},me=Object.freeze({State:f,TOKENS:Sn,run:pe,start:An});Array.isArray||(Array.isArray=function(n){return"[object Array]"===Object.prototype.toString.call(n)});var fe=function(n){return pe(yn(n))},de=function(n){for(var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,a=fe(n),t=[],o=0;o1&&void 0!==arguments[1]?arguments[1]:null,a=fe(n);return 1===a.length&&a[0].isLink&&(!e||a[0].type===e)};e.find=de,e.inherits=a,e.options=b,e.parser=me,e.scanner=kn,e.test=xe,e.tokenize=fe}(self.linkify=self.linkify||{})}(); \ No newline at end of file diff --git a/main/inc/lib/javascript/chat/css/chat.css b/main/inc/lib/javascript/chat/css/chat.css index a95a53994a..b2b9183902 100755 --- a/main/inc/lib/javascript/chat/css/chat.css +++ b/main/inc/lib/javascript/chat/css/chat.css @@ -1,181 +1,198 @@ .chatboxmain { - position: fixed; - width: auto; - z-index: 9000; - bottom: 0px; - right: 20px; - display: block; + position: fixed; + width: auto; + z-index: 9000; + bottom: 0px; + right: 20px; + display: block; } + .chatboxheadmain { - color: #ffffff; - background-color: #000; - min-height: 34px; - padding: 0 10px; + color: #ffffff; + background-color: #000; + min-height: 34px; + padding: 0 10px; } #chatboxtitlemain { - font-weight: normal; - float: left; - font-size: 12px; - padding-top: 2px; - cursor:pointer; + font-weight: normal; + float: left; + font-size: 12px; + padding-top: 2px; + cursor: pointer; } + .user_status_main { - width:18px; - display:inline; - float:left; + width: 18px; + display: inline; + float: left; } + .chatbox { - position: fixed; - position:expression("absolute"); - width: 320px; - display:none; - z-index: 9000; + position: fixed; + position: expression("absolute"); + width: 320px; + display: none; + z-index: 9000; +} + +.chatbox-common { + word-wrap: break-word; } .chatboxmessage_me { background-color: #4080ff; color: #fff; width: 80%; - float:right !important; + float: right !important; border-radius: 12px; } .chatboxmessage { background-color: #ebedf2; color: #2a2f35; - margin-left:1em; + margin-left: 1em; width: 80%; - float:left; + float: left; border-radius: 12px; } .chatboxmessagecontent { } +.chatboxmessage_me .chatboxmessagecontent a { + color: #ffffff; +} + .chatboxcontent { - font-family: arial,sans-serif; - font-size: 13px; - color: #333333; - height:200px; - overflow-y:auto; - overflow-x:auto; - padding:7px; - border-left:1px solid #cccccc; - border-right:1px solid #cccccc; - border-bottom:1px solid #eeeeee; - background-color: #ffffff; - line-height: 1.3em; + font-family: arial, sans-serif; + font-size: 13px; + color: #333333; + max-height: 200px; + overflow-y: auto; + overflow-x: auto; + padding: 7px; + border-left: 1px solid #cccccc; + border-right: 1px solid #cccccc; + border-bottom: 1px solid #eeeeee; + background-color: #ffffff; + line-height: 1.3em; +} + +.chatboxcontent .alert { + margin-bottom: 0; } .user_status { - width:8px; - display:inline-block; - margin-right: 5px; + width: 8px; + display: inline-block; + margin-right: 5px; } + .chatimage { - display: inline-block; - margin-right: 5px; + display: inline-block; + margin-right: 5px; } .chatboxtitle { - font-weight: normal; - display: inline-block; - font-size: 10px; - width: auto; - cursor:pointer; - overflow: hidden; - padding-left: 2px; - text-overflow: ellipsis; - white-space: nowrap; - line-height: 10px; + font-weight: normal; + display: inline-block; + font-size: 10px; + width: auto; + cursor: pointer; + overflow: hidden; + padding-left: 2px; + text-overflow: ellipsis; + white-space: nowrap; + line-height: 10px; } .chatboxhead { - /* background-color: #222; */ - padding: 5px; - color: #ffffff; - border-right:1px solid #222; - border-left:1px solid #222; - background-color: #222; - background-repeat: repeat-x; - background-image: -khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222)); - background-image: -moz-linear-gradient(top, #333333, #222222); - background-image: -ms-linear-gradient(top, #333333, #222222); - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222)); - background-image: -webkit-linear-gradient(top, #333333, #222222); - background-image: -o-linear-gradient(top, #333333, #222222); - background-image: linear-gradient(top, #333333, #222222); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + /* background-color: #222; */ + padding: 5px; + color: #ffffff; + border-right: 1px solid #222; + border-left: 1px solid #222; + background-color: #222; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222)); + background-image: -moz-linear-gradient(top, #333333, #222222); + background-image: -ms-linear-gradient(top, #333333, #222222); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222)); + background-image: -webkit-linear-gradient(top, #333333, #222222); + background-image: -o-linear-gradient(top, #333333, #222222); + background-image: linear-gradient(top, #333333, #222222); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); } .chatboxblink { - background-color: #FF921F; - border-right:1px solid #EF7A00; - border-left:1px solid #EF7A00; + background-color: #FF921F; + border-right: 1px solid #EF7A00; + border-left: 1px solid #EF7A00; background-repeat: repeat-x; - background-image: -khtml-gradient(linear, left top, left bottom, from(#FF921F), to(#FF921F)); - background-image: -moz-linear-gradient(top, #FF921F, #FFAC55); - background-image: -ms-linear-gradient(top, #FF921F, #FF921F); - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #FF921F), color-stop(100%, #FF921F)); - background-image: -webkit-linear-gradient(top, #FF921F, #FF921F); - background-image: -o-linear-gradient(top, #FF921F, #FF921F); - background-image: linear-gradient(top, #FF921F, #FF921F); + background-image: -khtml-gradient(linear, left top, left bottom, from(#FF921F), to(#FF921F)); + background-image: -moz-linear-gradient(top, #FF921F, #FFAC55); + background-image: -ms-linear-gradient(top, #FF921F, #FF921F); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #FF921F), color-stop(100%, #FF921F)); + background-image: -webkit-linear-gradient(top, #FF921F, #FF921F); + background-image: -o-linear-gradient(top, #FF921F, #FF921F); + background-image: linear-gradient(top, #FF921F, #FF921F); } .chatboxinput { - padding: 5px; - background-color: #ffffff; - border-left:1px solid #cccccc; - border-right:1px solid #cccccc; - border-bottom:1px solid #cccccc; + padding: 5px; + background-color: #ffffff; + border-left: 1px solid #cccccc; + border-right: 1px solid #cccccc; + border-bottom: 1px solid #cccccc; } .chatboxtextarea { - width: 300px; - height:44px; - padding:3px 0pt 3px 3px; - border: 1px solid #eeeeee; - margin: 1px; - overflow:hidden; + width: 300px; + height: 44px; + padding: 3px 0pt 3px 3px; + border: 1px solid #eeeeee; + margin: 1px; + overflow: hidden; } .chatboxtextareaselected { - border: 2px solid #f99d39; - margin:0; + border: 2px solid #f99d39; + margin: 0; } + .chatboxinfo { - margin-left:-1em; - color:#666666; + margin-left: -1em; + color: #666666; } .chatboxmessagefrom { - margin-left:-1em; - font-weight: bold; + margin-left: -1em; + font-weight: bold; } .chatboxoptions { - float: right; + float: right; } .chatboxoptions a { - text-decoration: none; - color: white; - font-weight:bold; - font-family:Verdana,Arial,"Bitstream Vera Sans",sans-serif; + text-decoration: none; + color: white; + font-weight: bold; + font-family: Verdana, Arial, "Bitstream Vera Sans", sans-serif; } .chatboxoptions a:hover { - background-color: #aaa; + background-color: #aaa; } .chatbox_checks { - float:right; + float: right; } .chatbox_checked { - color: #13A7F0; + color: #13A7F0; } diff --git a/main/inc/lib/javascript/chat/js/chat.js b/main/inc/lib/javascript/chat/js/chat.js index 0e541ba6f2..ee7665875a 100755 --- a/main/inc/lib/javascript/chat/js/chat.js +++ b/main/inc/lib/javascript/chat/js/chat.js @@ -165,7 +165,6 @@ function startChatSession() window_user_info.avatar_small ); } - createChatBubble(my_user_id, item); } }); @@ -279,7 +278,6 @@ function chatHeartbeat() newMessagesWin[my_user_id]= {'status':true, 'username':item.username}; var chatBubble = createChatBubble(my_user_id, item); - //$("#chatbox_"+my_user_id+" .chatboxcontent").append(chatBubble); $("#chatbox_"+my_user_id+" .chatboxcontent").scrollTop( $("#chatbox_"+my_user_id+" .chatboxcontent")[0].scrollHeight @@ -338,18 +336,22 @@ function createChatBubble(my_user_id, item, appendType = 'append') var messageObject = $("#chatbox_"+my_user_id+" .chatboxcontent").find('#message_id_' + item.id); var exists = messageObject.length !== 0; - var messageHeader = '
'; + var messageHeader = '
'; var messageEnd = '
'; var message = ''; if (my_user_id == item.from_user_info.id) { message += ''+item.from_user_info.complete_name+':  '; } + message += '
'+item.message+'
'; message += '
'+check+'
'; if (exists) { messageObject.html(message); + $(messageObject).linkify({ + target: "_blank" + }); } else { message = messageHeader + message + messageEnd; if (appendType == 'append') { @@ -358,6 +360,9 @@ function createChatBubble(my_user_id, item, appendType = 'append') $("#chatbox_"+my_user_id+" .chatboxcontent").prepend(message); } + $("#chatbox_"+my_user_id+" .chatboxcontent").linkify({ + target: "_blank" + }); } return message; @@ -369,9 +374,7 @@ function createChatBubble(my_user_id, item, appendType = 'append') function closeChat() { $.post( - ajax_url + "?action=close", - { - }, + ajax_url + "?action=close", {}, function (data) { // Disconnects from chat set_user_status(0); @@ -730,7 +733,7 @@ function createChatBox(user_id, chatboxtitle, minimizeChatBox, online, userImage $("#chatbox_"+user_id).click(function() { if ($('#chatbox_'+user_id+' .chatboxcontent').css('display') != 'none') { - $("#chatbox_"+user_id+" .chatboxtextarea").focus(); + //$("#chatbox_"+user_id+" .chatboxtextarea").focus(); } }); @@ -756,36 +759,21 @@ function getMoreItems(userId, scrollType) cache: false, dataType: "json", success: function(items) { - console.log(items); items = items.reverse(); $.each(items, function(i, item) { - console.log(i); if (item) { if ($("#chatbox_"+userId).css('display') == 'none') { $("#chatbox_"+userId).css('display','block'); restructureChatBoxes(); } var chatBubble = createChatBubble(userId, item, 'prepend'); - //$("#chatbox_"+userId+" .chatboxcontent").prepend(chatBubble); - if ($('#chatbox_'+userId+' .chatboxcontent').css('display') == 'none') { $('#chatbox_'+userId+' .chatboxhead').toggleClass('chatboxblink'); } - - // When using scroll set the scroll window to the first - var scrollValue = 10; - if (scrollType === 'last') { - // When loading for the first time show the last msg - //scrollValue = $("#chatbox_"+userId+" .chatboxcontent").height(); - } - - /*$("#chatbox_"+userId+" .chatboxcontent").scrollTop( - scrollValue - );*/ } }); } - }); //ajax + }); } /** @@ -847,7 +835,6 @@ function toggleChatBoxGrowth(user_id) Cookies.set('chatbox_minimized', newCookie); $('#chatbox_'+user_id+' .chatboxcontent').css('display','block'); $('#chatbox_'+user_id+' .chatboxinput').css('display','block'); - //$("#chatbox_"+user_id+" .chatboxcontent").scrollTop($("#chatbox_"+user_id+" .chatboxcontent")[0].scrollHeight); $('.togglelink').html(''); } else { // hide box @@ -926,8 +913,7 @@ function checkChatBoxInputKey(event, chatboxtextarea, user_id) message: message, id: messageId }; - var bubble = createChatBubble(user_id, item); - //$("#chatbox_" + user_id + " .chatboxcontent").append(bubble); + createChatBubble(user_id, item); $("#chatbox_" + user_id + " .chatboxcontent").scrollTop( $("#chatbox_" + user_id + " .chatboxcontent")[0].scrollHeight ); diff --git a/main/inc/lib/pdf.lib.php b/main/inc/lib/pdf.lib.php index 13b9370ef9..67b35a97f8 100755 --- a/main/inc/lib/pdf.lib.php +++ b/main/inc/lib/pdf.lib.php @@ -43,6 +43,7 @@ class PDF $params['right'] = isset($params['right']) ? $params['right'] : 15; $params['top'] = isset($params['top']) ? $params['top'] : 30; $params['bottom'] = isset($params['bottom']) ? $params['bottom'] : 30; + $params['margin_footer'] = isset($params['margin_footer']) ? $params['margin_footer'] : 8; $this->params['filename'] = isset($params['filename']) ? $params['filename'] : api_get_local_time(); $this->params['pdf_title'] = isset($params['pdf_title']) ? $params['pdf_title'] : ''; @@ -72,6 +73,8 @@ class PDF $orientation ); + $this->pdf->margin_footer = $params['margin_footer']; + // Default value is 96 set in the mpdf library file config.php $value = api_get_configuration_value('pdf_img_dpi'); if (!empty($value)) { @@ -83,9 +86,8 @@ class PDF * Export the given HTML to PDF, using a global template. * * @uses \export/table_pdf.tpl - * - * @param $content + * @param string $content * @param bool|false $saveToFile * @param bool|false $returnHtml * @param bool $addDefaultCss (bootstrap/default/base.css) @@ -147,12 +149,17 @@ class PDF $html = $tpl->fetch($tableTemplate); $html = api_utf8_encode($html); + if ($returnHtml) { + return $html; + } + $css_file = api_get_path(SYS_CSS_PATH).'themes/'.$tpl->theme.'/print.css'; if (!file_exists($css_file)) { $css_file = api_get_path(SYS_CSS_PATH).'print.css'; } $css = file_get_contents($css_file); - $html = self::content_to_pdf( + + self::content_to_pdf( $html, $css, $this->params['filename'], @@ -163,10 +170,6 @@ class PDF $returnHtml, $addDefaultCss ); - - if ($returnHtml) { - return $html; - } } /** @@ -187,6 +190,7 @@ class PDF * @param bool $print_title add title * @param bool $complete_style show header and footer if true * @param bool $addStyle + * @param string $mainTitle * * @return false|null */ @@ -196,7 +200,8 @@ class PDF $course_code = null, $print_title = false, $complete_style = true, - $addStyle = true + $addStyle = true, + $mainTitle = '' ) { if (empty($html_file_array)) { return false; @@ -231,14 +236,13 @@ class PDF $counter = 1; foreach ($html_file_array as $file) { - //Add a page break per file + // Add a page break per file $page_break = ''; if ($counter == count($html_file_array)) { $page_break = ''; } - $counter++; - //if the array provided contained subarrays with 'title' entry, + // if the array provided contained subarrays with 'title' entry, // then print the title in the PDF if (is_array($file) && isset($file['title'])) { $html_title = $file['title']; @@ -248,14 +252,27 @@ class PDF $html_title = basename($file); } + $counter++; + if (empty($file) && !empty($html_title)) { - //this is a chapter, print title & skip the rest + // this is a chapter, print title & skip the rest + if ($counter === 2 && !empty($mainTitle)) { + $this->pdf->WriteHTML( + '

'.$mainTitle.'

' + ); + } if ($print_title) { $this->pdf->WriteHTML( '

'.$html_title.'

'.$page_break ); } continue; + } else { + if ($counter === 2 && !empty($mainTitle)) { + $this->pdf->WriteHTML( + '

'.$mainTitle.'

' + ); + } } if (!file_exists($file)) { @@ -382,7 +399,7 @@ class PDF return false; } - //clean styles and javascript document + // clean styles and javascript document $clean_search = [ '@]*?>.*?@si', '@]*?>.*?@siU', diff --git a/main/inc/lib/template.lib.php b/main/inc/lib/template.lib.php index 53565055fd..833e0e5136 100755 --- a/main/inc/lib/template.lib.php +++ b/main/inc/lib/template.lib.php @@ -706,18 +706,6 @@ class Template 'chosen/chosen.jquery.min.js', ]; - $viewBySession = api_get_setting('my_courses_view_by_session') === 'true'; - - if (api_is_global_chat_enabled() || $viewBySession) { - // Do not include the global chat in LP - if ($this->show_learnpath == false && - $this->show_footer == true && - $this->hide_global_chat == false - ) { - $js_files[] = 'chat/js/chat.js'; - } - } - if (api_get_setting('accessibility_font_resize') === 'true') { $js_files[] = 'fontresize.js'; } @@ -745,6 +733,20 @@ class Template 'js-cookie/src/js.cookie.js', ]; + $viewBySession = api_get_setting('my_courses_view_by_session') === 'true'; + + if ($viewBySession || api_is_global_chat_enabled()) { + // Do not include the global chat in LP + if ($this->show_learnpath == false && + $this->show_footer == true && + $this->hide_global_chat == false + ) { + $js_files[] = 'chat/js/chat.js'; + $bowerJsFiles[] = 'linkifyjs/linkify.js'; + $bowerJsFiles[] = 'linkifyjs/linkify-jquery.js'; + } + } + $features = api_get_configuration_value('video_features'); if (!empty($features) && isset($features['features'])) { foreach ($features['features'] as $feature) { @@ -838,10 +840,13 @@ class Template { global $disable_js_and_css_files; $js_files = []; + $bower = ''; if (api_is_global_chat_enabled()) { //Do not include the global chat in LP if ($this->show_learnpath == false && $this->show_footer == true && $this->hide_global_chat == false) { $js_files[] = 'chat/js/chat.js'; + $bower .= ''; + $bower .= ''; } } $js_file_to_string = ''; @@ -849,7 +854,7 @@ class Template $js_file_to_string .= api_get_js($js_file); } if (!$disable_js_and_css_files) { - $this->assign('js_file_to_string_post', $js_file_to_string); + $this->assign('js_file_to_string_post', $js_file_to_string.$bower); } } @@ -1296,6 +1301,7 @@ class Template $url = api_get_path(WEB_CODE_PATH).'ticket/tickets.php?project_id='.$defaultProjectId.'&'.$courseParams; $allow = TicketManager::userIsAllowInProject(api_get_user_info(), $defaultProjectId); + if ($allow) { $rightFloatMenu .= '